summaryrefslogtreecommitdiff
path: root/src/frontend/bookshelfmanager/installpage
diff options
context:
space:
mode:
authorRoberto C. Sanchez <roberto@connexer.com>2014-10-21 22:48:19 -0400
committerRoberto C. Sanchez <roberto@connexer.com>2014-10-21 22:48:19 -0400
commit1af3b165c9377702ca62a64112bc089a6f575c30 (patch)
tree4df9cca5543b2cab5ca56dbb1214d7d3b1f291e3 /src/frontend/bookshelfmanager/installpage
parent5b5fd0dce407556f98ed8edee89dc830bf1437b1 (diff)
Imported Upstream version 2.0~beta2
Diffstat (limited to 'src/frontend/bookshelfmanager/installpage')
-rw-r--r--src/frontend/bookshelfmanager/installpage/btinstallmodulechooserdialog.cpp232
-rw-r--r--src/frontend/bookshelfmanager/installpage/btinstallmodulechooserdialog.h53
-rw-r--r--src/frontend/bookshelfmanager/installpage/btinstallpage.cpp207
-rw-r--r--src/frontend/bookshelfmanager/installpage/btinstallpage.h66
-rw-r--r--src/frontend/bookshelfmanager/installpage/btinstallpathdialog.cpp170
-rw-r--r--src/frontend/bookshelfmanager/installpage/btinstallpathdialog.h44
-rw-r--r--src/frontend/bookshelfmanager/installpage/btinstallprogressdialog.cpp261
-rw-r--r--src/frontend/bookshelfmanager/installpage/btinstallprogressdialog.h70
-rw-r--r--src/frontend/bookshelfmanager/installpage/btinstallthread.cpp199
-rw-r--r--src/frontend/bookshelfmanager/installpage/btinstallthread.h99
-rw-r--r--src/frontend/bookshelfmanager/installpage/btsourcearea.cpp298
-rw-r--r--src/frontend/bookshelfmanager/installpage/btsourcearea.h97
-rw-r--r--src/frontend/bookshelfmanager/installpage/btsourcewidget.cpp403
-rw-r--r--src/frontend/bookshelfmanager/installpage/btsourcewidget.h86
14 files changed, 2285 insertions, 0 deletions
diff --git a/src/frontend/bookshelfmanager/installpage/btinstallmodulechooserdialog.cpp b/src/frontend/bookshelfmanager/installpage/btinstallmodulechooserdialog.cpp
new file mode 100644
index 0000000..739d2ea
--- /dev/null
+++ b/src/frontend/bookshelfmanager/installpage/btinstallmodulechooserdialog.cpp
@@ -0,0 +1,232 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+/*********
+*
+* This file is part of BibleTime's source code, http://www.bibletime.info/.
+*
+* Copyright 1999-2008 by the BibleTime developers.
+* The BibleTime source code is licensed under the GNU General Public License version 2.0.
+*
+**********/
+
+#include "btinstallmodulechooserdialog.h"
+
+#include "frontend/cmodulechooserdialog.h"
+#include "backend/drivers/cswordmoduleinfo.h"
+#include "backend/btmoduletreeitem.h"
+
+#include <QString>
+#include <QList>
+#include <QWidget>
+#include <QString>
+#include <QTreeWidgetItem>
+#include <QBrush>
+#include <QPushButton>
+
+#include <QDebug>
+
+
+BtInstallModuleChooserDialog::BtInstallModuleChooserDialog(QWidget* parent, QString title, QString label, QList<CSwordModuleInfo*>* empty)
+ : CModuleChooserDialog(parent, title, label, empty)
+{
+ qDebug("BtInstallModuleChooserDialog::BtInstallModuleChooserDialog start");
+ init();
+ okButton()->setText(tr("Install"));
+ m_nameList = QStringList();
+}
+
+// Do nothing, the tree is initialized outside this class.
+void BtInstallModuleChooserDialog::initModuleItem(BTModuleTreeItem*, QTreeWidgetItem*)
+{}
+
+void BtInstallModuleChooserDialog::initModuleItem(QString name, QTreeWidgetItem* sourceItem)
+{
+ QTreeWidgetItem* moduleItem = new QTreeWidgetItem(sourceItem);
+ moduleItem->setText(0, name);
+ moduleItem->setFlags(Qt::ItemIsUserCheckable|Qt::ItemIsEnabled);
+ moduleItem->setCheckState(0, Qt::Checked);
+
+ // prevent double items
+ if (m_nameList.contains(name)) {
+ qDebug() << "item already in list:" << name;
+ //moduleItem->setCheckState(0, Qt::Unchecked);
+ QBrush bg(Qt::red);
+ moduleItem->setBackground(0, bg);
+ //find and change the other offending items
+ foreach (QTreeWidgetItem* doubleItem, treeWidget()->findItems(name, Qt::MatchFixedString|Qt::MatchCaseSensitive|Qt::MatchRecursive, 0)) {
+ //doubleItem->setCheckState(0, Qt::Unchecked);
+ //qDebug() << "CInstallModuleChooserDialog::initModuleItem" << doubleItem;
+ doubleItem->setBackground(0, bg);
+ }
+ m_doubleCheckedModules[name] = true;
+ enableOk(false);
+ }
+ m_nameList << name;
+}
+
+void BtInstallModuleChooserDialog::slotItemChecked(QTreeWidgetItem* item, int column)
+{
+ QString moduleName = item->text(0);
+ qDebug("BtInstallModuleChooserDialog::slotItemChecked start");
+ // handle only non-toplevel items which has duplicates and where the first column was changed
+ if (item->parent() && column == 0 && (findModuleItemsByName(moduleName).count() > 1)) {
+ //prevent handling when the color is changed
+ if (item->data(1, Qt::UserRole).toBool() == false) {
+ qDebug("was not updating");
+ item->setData(1, Qt::UserRole, true);
+ } else {
+ qDebug("was updating");
+ item->setData(1, Qt::UserRole, false);
+ return;
+ }
+
+ QList<QTreeWidgetItem*> doubleNameItems = findModuleItemsByName(moduleName);
+ QList<QTreeWidgetItem*> doubleCheckedItems;
+ foreach (QTreeWidgetItem* nItem, doubleNameItems) {
+ if (nItem->checkState(0) == Qt::Checked) {
+ doubleCheckedItems << nItem;
+ }
+ }
+
+ if (doubleCheckedItems.count() > 1) {
+ enableOk(false);
+ // color the items
+ qDebug() << "there were more than 1 item of the name" << moduleName;
+ foreach (QTreeWidgetItem* i, doubleNameItems) {
+ QBrush bg(Qt::red);
+ i->setBackground(0, bg);
+ }
+ m_doubleCheckedModules[moduleName] = true;
+ } else if (doubleCheckedItems.count() == 1) {
+ qDebug() << "there were 1 checked items of the name" << moduleName;
+ // uncolor the items
+ foreach (QTreeWidgetItem* i, doubleNameItems) {
+ i->setBackground(0, i->parent()->background(0));
+ }
+ m_doubleCheckedModules.remove(moduleName);
+ if (m_doubleCheckedModules.count() == 0) {
+ enableOk(true);
+ }
+ }
+ }
+}
+
+QList<QTreeWidgetItem*> BtInstallModuleChooserDialog::findModuleItemsByName(QString name)
+{
+ qDebug() << "BtInstallModuleChooserDialog::findModuleItemsByName:" << name << treeWidget()->topLevelItemCount();
+ QList<QTreeWidgetItem*> doubleNamedAllItems = treeWidget()->findItems(name, Qt::MatchFixedString|Qt::MatchCaseSensitive|Qt::MatchRecursive);
+ //qDebug() << "doubleNamedAllItems: " << doubleNamedAllItems.count();
+ QList<QTreeWidgetItem*> doubleNamedModuleItems;
+ foreach (QTreeWidgetItem* item, doubleNamedAllItems) {
+ //qDebug() << "item:" << item;
+ if (item->parent()) {
+ doubleNamedModuleItems << item;
+ }
+ }
+ //qDebug() << "module items:" << doubleNamedModuleItems.count();
+ return doubleNamedModuleItems;
+}
+
+void BtInstallModuleChooserDialog::enableOk(bool enabled)
+{
+ qDebug() << "BtInstallModuleChooserDialog::enableOk" << enabled;
+ okButton()->setEnabled(enabled);
+}
diff --git a/src/frontend/bookshelfmanager/installpage/btinstallmodulechooserdialog.h b/src/frontend/bookshelfmanager/installpage/btinstallmodulechooserdialog.h
new file mode 100644
index 0000000..39b8a96
--- /dev/null
+++ b/src/frontend/bookshelfmanager/installpage/btinstallmodulechooserdialog.h
@@ -0,0 +1,53 @@
+/*********
+*
+* This file is part of BibleTime's source code, http://www.bibletime.info/.
+*
+* Copyright 1999-2008 by the BibleTime developers.
+* The BibleTime source code is licensed under the GNU General Public License version 2.0.
+*
+**********/
+
+#ifndef BTINSTALLMODULECHOOSERDIALOG_H
+#define BTINSTALLMODULECHOOSERDIALOG_H
+
+#include "frontend/cmodulechooserdialog.h"
+
+#include <QObject>
+#include <QString>
+#include <QMap>
+#include <QList>
+
+class BTModuleTreeItem;
+
+class QWidget;
+class QTreeWidgetItem;
+
+
+/**
+* Confirmation dialog for installation. Lets the user
+* uncheck modules from the list.
+*/
+class BtInstallModuleChooserDialog : public CModuleChooserDialog
+{
+ Q_OBJECT
+
+public:
+ BtInstallModuleChooserDialog(QWidget* parent, QString title, QString label, QList<CSwordModuleInfo*>* empty);
+
+ void initModuleItem(QString name, QTreeWidgetItem* sourceItem);
+ void enableOk(bool enabled);
+
+public slots:
+ void slotItemChecked(QTreeWidgetItem* item, int column);
+
+protected:
+ virtual void initModuleItem(BTModuleTreeItem*, QTreeWidgetItem*);
+
+ QList<QTreeWidgetItem*> findModuleItemsByName(QString name);
+private:
+ QStringList m_nameList;
+ QMap<QString, bool> m_doubleCheckedModules;
+
+};
+
+#endif
diff --git a/src/frontend/bookshelfmanager/installpage/btinstallpage.cpp b/src/frontend/bookshelfmanager/installpage/btinstallpage.cpp
new file mode 100644
index 0000000..9f8e6b9
--- /dev/null
+++ b/src/frontend/bookshelfmanager/installpage/btinstallpage.cpp
@@ -0,0 +1,207 @@
+/*********
+*
+* This file is part of BibleTime's source code, http://www.bibletime.info/.
+*
+* Copyright 1999-2008 by the BibleTime developers.
+* The BibleTime source code is licensed under the GNU General Public License version 2.0.
+*
+**********/
+
+
+#include "btinstallpage.h"
+
+#include "btinstallpathdialog.h"
+#include "btinstallprogressdialog.h"
+#include "btsourcewidget.h"
+#include "btsourcearea.h"
+
+#include "frontend/bookshelfmanager/btinstallmgr.h"
+#include "frontend/bookshelfmanager/cswordsetupinstallsourcesdialog.h"
+#include "frontend/bookshelfmanager/btconfigdialog.h"
+#include "frontend/bookshelfmanager/instbackend.h"
+#include "frontend/bookshelfmanager/btmodulemanagerdialog.h"
+
+#include "frontend/cmodulechooserdialog.h"
+
+#include "backend/drivers/cswordmoduleinfo.h"
+#include "backend/managers/cswordbackend.h"
+#include "backend/config/cbtconfig.h"
+
+#include "util/cpointers.h"
+#include "util/ctoolclass.h"
+#include "util/cresmgr.h"
+#include "util/directoryutil.h"
+
+#include <boost/scoped_ptr.hpp>
+
+
+#include <QAction>
+#include <QApplication>
+#include <QWidget>
+#include <QButtonGroup>
+#include <QComboBox>
+#include <QDialog>
+#include <QHBoxLayout>
+#include <QLabel>
+#include <QPushButton>
+#include <QToolButton>
+#include <QSpacerItem>
+#include <QTabBar>
+#include <QStackedWidget>
+#include <QTreeWidget>
+#include <QTreeWidgetItem>
+#include <QVBoxLayout>
+#include <QFileInfo>
+#include <QMessageBox>
+#include <QProgressDialog>
+#include <QTimer>
+#include <QProgressBar>
+
+#include <QHeaderView>
+
+#include <swversion.h>
+
+
+// *********************************************************
+// *********** Config dialog page: Install/Update **********
+// *********************************************************
+
+BtInstallPage::BtInstallPage()
+ : BtConfigPage()
+{
+ qDebug("BtInstallPage::BtInstallPage() start");
+ initView();
+ initConnections();
+}
+
+void BtInstallPage::setInstallEnabled(bool b)
+{
+ qDebug("void BtInstallPage::setInstallEnabled(bool b) start");
+ m_installButton->setEnabled(b);
+}
+
+QString BtInstallPage::selectedInstallPath()
+{
+ return m_pathCombo->currentText();
+}
+
+void BtInstallPage::initView()
+{
+ qDebug("void BtInstallPage::initView() start");
+ QVBoxLayout *mainLayout = new QVBoxLayout(this);
+
+ // installation path chooser
+ QHBoxLayout* pathLayout = new QHBoxLayout();
+ // beautify the layout
+ int top; int bottom; int left; int right;
+ pathLayout->getContentsMargins(&left, &top, &right, &bottom);
+ pathLayout->setContentsMargins(left, top + 7, right, bottom + 7 );
+ QSpacerItem *pathSpacer= new QSpacerItem(40, 20, QSizePolicy::Expanding, QSizePolicy::Minimum);
+ QLabel* pathLabel = new QLabel(tr("Install path:"));
+ m_pathCombo = new QComboBox();
+ m_pathCombo->setToolTip(tr("The path where the new works will be installed"));
+ initPathCombo(); // set the paths and the current path
+ //m_configurePathButton = new QPushButton(tr("Configure...")); //TODO: icon only?
+ m_configurePathButton = new QToolButton(this);
+ m_configurePathButton->setToolTip(tr("Configure paths where works are installed"));
+ m_configurePathButton->setIcon(util::filesystem::DirectoryUtil::getIcon(CResMgr::bookshelfmgr::installpage::path_icon));
+
+ pathLayout->addItem(pathSpacer);
+ pathLayout->addWidget(pathLabel);
+ pathLayout->addWidget(m_pathCombo);
+ pathLayout->addWidget(m_configurePathButton);
+ mainLayout->addLayout(pathLayout);
+
+ // Source widget
+ //QTabWidget* m_sourcesTabWidget;
+ m_sourceWidget = new BtSourceWidget(this);
+ mainLayout->addWidget(m_sourceWidget);
+ // Install button
+ QHBoxLayout *installButtonLayout = new QHBoxLayout();
+ installButtonLayout->setContentsMargins(0,5,0,5);
+ QSpacerItem *installButtonSpacer = new QSpacerItem(371, 20, QSizePolicy::Expanding, QSizePolicy::Minimum);
+ installButtonLayout->addItem(installButtonSpacer);
+ m_installButton = new QPushButton(tr("Install..."), this);
+ m_installButton->setToolTip(tr("Install or update selected works"));
+ m_installButton->setIcon(util::filesystem::DirectoryUtil::getIcon(CResMgr::bookshelfmgr::installpage::install_icon));
+ m_installButton->setEnabled(false);
+ installButtonLayout->addWidget(m_installButton);
+
+ mainLayout->addLayout(installButtonLayout);
+}
+
+void BtInstallPage::initConnections()
+{
+ qDebug("void BtInstallPage::initConnections() start");
+ QObject::connect(m_pathCombo, SIGNAL(activated(const QString&)), this , SLOT(slotPathChanged(const QString&)));
+ QObject::connect(m_configurePathButton, SIGNAL(clicked()), this, SLOT(slotEditPaths()));
+ QObject::connect(m_installButton, SIGNAL(clicked()), m_sourceWidget, SLOT(slotInstall()) );
+
+ QObject::connect(CPointers::backend(), SIGNAL(sigSwordSetupChanged(CSwordBackend::SetupChangedReason)), this, SLOT(slotSwordSetupChanged()));
+ //source widget has its own connections, not here
+}
+
+void BtInstallPage::initPathCombo()
+{
+ qDebug("void BtInstallPage::initPathCombo() start");
+ //populate the combo list
+ m_pathCombo->clear();
+
+ QStringList targets = instbackend::targetList();
+ for (QStringList::iterator it = targets.begin(); it != targets.end(); ++it) {
+ if ((*it).isEmpty()) continue;
+ m_pathCombo->addItem(*it);
+ }
+
+ // choose the current value from config but check whether we have so many items
+ int configValue = CBTConfig::get(CBTConfig::installPathIndex);
+ int index = configValue > (m_pathCombo->count()-1) ? m_pathCombo->count()-1 : configValue;
+ m_pathCombo->setCurrentIndex(index);
+}
+
+void BtInstallPage::slotPathChanged(const QString& /*pathText*/)
+{
+ CBTConfig::set(CBTConfig::installPathIndex, m_pathCombo->currentIndex( ) );
+}
+
+void BtInstallPage::slotEditPaths()
+{
+ qDebug("void BtInstallPage::slotEditPaths() start");
+
+ BtInstallPathDialog* dlg = new BtInstallPathDialog();
+ int result = dlg->exec();
+ if (result == QDialog::Accepted) {
+ //dynamic_cast<BtModuleManagerDialog*>(parentDialog())->slotSwordSetupChanged();
+ CPointers::backend()->reloadModules(CSwordBackend::PathChanged);
+ }
+}
+
+// implement the BtConfigPage methods
+
+QString BtInstallPage::iconName()
+{
+ return CResMgr::bookshelfmgr::installpage::icon;
+}
+QString BtInstallPage::label()
+{
+ // TODO: move the warning to a dialog which is shown when adding a source.
+ return tr("Install and update works. Add remote or local sources, refresh them, select the works to be installed/updated and click Install.<br/><b>WARNING:</b> If you live in a persecuted country and don't want to risk detection don't use remote sources.");
+}
+QString BtInstallPage::header()
+{
+ return tr("Install/Update");
+}
+
+void BtInstallPage::slotSwordSetupChanged()
+{
+ qDebug() << "BtInstallPage::slotSwordSetupChanged";
+ initPathCombo();
+// for (int i = 0; i < m_sourceWidget->count(); i++ ) {
+// BtSourceArea* sourceArea = dynamic_cast<BtSourceArea*>(m_sourceWidget->widget(i));
+// Q_ASSERT(sourceArea);
+// sourceArea->createModuleTree();
+// }
+}
+
+
+
diff --git a/src/frontend/bookshelfmanager/installpage/btinstallpage.h b/src/frontend/bookshelfmanager/installpage/btinstallpage.h
new file mode 100644
index 0000000..4d05577
--- /dev/null
+++ b/src/frontend/bookshelfmanager/installpage/btinstallpage.h
@@ -0,0 +1,66 @@
+/*********
+*
+* This file is part of BibleTime's source code, http://www.bibletime.info/.
+*
+* Copyright 1999-2008 by the BibleTime developers.
+* The BibleTime source code is licensed under the GNU General Public License version 2.0.
+*
+**********/
+
+#ifndef BTINSTALLPAGE_H
+#define BTINSTALLPAGE_H
+
+
+#include "frontend/bookshelfmanager/btconfigdialog.h"
+
+#include <QString>
+
+#include <installmgr.h>
+
+
+class BtSourceWidget;
+
+class QComboBox;
+class QPushButton;
+class QToolButton;
+
+/**
+* The Install page includes module path chooser, source/module handler and install button.
+*/
+class BtInstallPage : public BtConfigPage
+{
+ Q_OBJECT
+public:
+ BtInstallPage();
+
+ // reimplemented from btinstallpage
+ QString iconName();
+ QString label();
+ QString header();
+
+ void setInstallEnabled(bool b);
+
+ QString selectedInstallPath();
+
+public slots:
+ void slotSwordSetupChanged();
+
+private:
+ void initView();
+ void initConnections();
+ void initPathCombo();
+
+private slots:
+ void slotPathChanged(const QString& pathText);
+ void slotEditPaths();
+
+private:
+
+ QComboBox* m_pathCombo;
+ //QPushButton* m_configurePathButton;
+ QToolButton* m_configurePathButton;
+ BtSourceWidget* m_sourceWidget;
+ QPushButton* m_installButton;
+};
+
+#endif
diff --git a/src/frontend/bookshelfmanager/installpage/btinstallpathdialog.cpp b/src/frontend/bookshelfmanager/installpage/btinstallpathdialog.cpp
new file mode 100644
index 0000000..82b8362
--- /dev/null
+++ b/src/frontend/bookshelfmanager/installpage/btinstallpathdialog.cpp
@@ -0,0 +1,170 @@
+/*********
+*
+* This file is part of BibleTime's source code, http://www.bibletime.info/.
+*
+* Copyright 1999-2008 by the BibleTime developers.
+* The BibleTime source code is licensed under the GNU General Public License version 2.0.
+*
+**********/
+
+#include "btinstallpathdialog.h"
+
+#include "frontend/bookshelfmanager/instbackend.h"
+
+#include "util/ctoolclass.h"
+#include "util/dialogutil.h"
+#include "util/directoryutil.h"
+#include "util/cresmgr.h"
+
+#include <QString>
+#include <QDialog>
+#include <QDir>
+#include <QGridLayout>
+#include <QLabel>
+#include <QTreeWidget>
+#include <QTreeWidgetItem>
+#include <QPushButton>
+#include <QFileDialog>
+#include <QMessageBox>
+#include <QHeaderView>
+#include <QDialogButtonBox>
+
+#include <QDebug>
+
+BtInstallPathDialog::BtInstallPathDialog()
+{
+ setWindowTitle(tr("Bookshelf Paths"));
+
+ QVBoxLayout *mainLayout;
+ QHBoxLayout *viewLayout;
+
+ mainLayout = new QVBoxLayout(this);
+ viewLayout = new QHBoxLayout();
+
+ QString l1 = tr("Works can be installed in one or more directories. After setting up directories here you can choose one of them in Install page.");
+ QString l2 = tr("BibleTime and the Sword library find the modules from all of these directories. If the directory is removed here it still exists in the system with all the works in it. \".sword\" directory in your home directory is always used automatically and can't be removed or added.");
+
+ QLabel* mainLabel = CToolClass::explanationLabel(this,
+ tr("Configure bookshelf paths"), l1 + QString("<small><br><br>") + l2 + QString("</small>"));
+ mainLayout->addWidget(mainLabel);
+
+ QString swordConfPath = instbackend::swordConfigFilename();
+ QLabel* confPathLabel = new QLabel(tr("Configuration file for the paths is: ").append("<b>%1</b>").arg(swordConfPath), this);
+ confPathLabel->setWordWrap(true);
+ mainLayout->addWidget(confPathLabel);
+
+
+ m_swordPathListBox = new QTreeWidget(this);
+ m_swordPathListBox->header()->hide();
+
+ QDir swordDir = instbackend::swordDir();
+ QStringList targets = instbackend::targetList();
+ foreach (QString pathname, targets) {
+ if (pathname.isEmpty() || QDir(pathname) == swordDir) continue;
+ new QTreeWidgetItem(m_swordPathListBox, QStringList(pathname) );
+ }
+
+ viewLayout->addWidget(m_swordPathListBox);
+
+ QVBoxLayout* buttonLayout = new QVBoxLayout();
+
+ m_addButton = new QPushButton(tr("Add..."), this);
+ m_addButton->setToolTip(tr("Add new path"));
+ m_addButton->setIcon(util::filesystem::DirectoryUtil::getIcon(CResMgr::bookshelfmgr::paths::add_icon));
+ connect(m_addButton, SIGNAL(clicked()), this, SLOT(slotAddClicked()));
+ buttonLayout->addWidget(m_addButton);
+
+ m_editButton = new QPushButton(tr("Edit..."), this);
+ m_editButton->setToolTip(tr("Edit the selected path"));
+ m_editButton->setIcon(util::filesystem::DirectoryUtil::getIcon(CResMgr::bookshelfmgr::paths::edit_icon));
+ connect(m_editButton, SIGNAL(clicked()), this, SLOT(slotEditClicked()));
+ buttonLayout->addWidget(m_editButton);
+
+ m_removeButton = new QPushButton(tr("Remove"), this);
+ m_removeButton->setToolTip(tr("Remove the selected path"));
+ m_removeButton->setIcon(util::filesystem::DirectoryUtil::getIcon(CResMgr::bookshelfmgr::paths::remove_icon));
+ connect(m_removeButton, SIGNAL(clicked()), this, SLOT(slotRemoveClicked()));
+ buttonLayout->addWidget(m_removeButton);
+
+ QSpacerItem* spacerItem = new QSpacerItem(20, 40, QSizePolicy::Minimum, QSizePolicy::Expanding);
+ buttonLayout->addItem(spacerItem);
+
+ viewLayout->addLayout(buttonLayout);
+ mainLayout->addLayout(viewLayout);
+
+ QDialogButtonBox* buttonBox = new QDialogButtonBox(this);
+ buttonBox->setOrientation(Qt::Horizontal);
+ buttonBox->setStandardButtons(QDialogButtonBox::Cancel|QDialogButtonBox::NoButton|QDialogButtonBox::Ok);
+ util::prepareDialogBox(buttonBox);
+ mainLayout->addWidget(buttonBox);
+ connect(buttonBox, SIGNAL(accepted()), this, SLOT(accept()));
+ connect(buttonBox, SIGNAL(rejected()), this, SLOT(reject()));
+
+}
+
+void BtInstallPathDialog::slotEditClicked() {
+ if (QTreeWidgetItem* i = m_swordPathListBox->currentItem()) {
+ QString dirname = QFileDialog::getExistingDirectory(this, tr("Choose directory"), i->text(0), QFileDialog::ShowDirsOnly|QFileDialog::DontResolveSymlinks);
+
+ if (dirname.isEmpty()) { // if user cancelled the dialog
+ return;
+ }
+ QDir dir = QDir(dirname);
+ if (dir.isReadable()) {
+ const QFileInfo fi( dir.canonicalPath() );
+ if (!fi.exists() || !fi.isWritable()) {
+ const int result = QMessageBox::warning(this, tr("Use Directory?"), tr("This directory is not writable, so works can not be installed here using BibleTime. Do you want to use this directory instead of the previous value?"), QMessageBox::Yes|QMessageBox::No, QMessageBox::No);
+ if (result != QMessageBox::Yes) return;
+ }
+ i->setText(0, dir.absolutePath()); // absolute, not canonical
+ }
+ }
+}
+
+void BtInstallPathDialog::slotAddClicked() {
+ QString dirname = QFileDialog::getExistingDirectory(this, tr("Choose directory"), "", QFileDialog::ShowDirsOnly|QFileDialog::DontResolveSymlinks);
+ if (dirname.isEmpty()) { // if user cancelled the dialog
+ return;
+ }
+ QDir dir = QDir(dirname);
+ if (dir.isReadable()) {
+ const QFileInfo fi( dir.canonicalPath() );
+ if (!fi.exists() || !fi.isWritable()) {
+ const int result = QMessageBox::warning(this, tr("Warning"), tr("This directory is not writable, so works can not be installed here using BibleTime. Do you still want to add it to the list of bookshelf directories?"), QMessageBox::Yes|QMessageBox::No, QMessageBox::No);
+ if (result != QMessageBox::Yes) {
+ return;
+ }
+ }
+ new QTreeWidgetItem(m_swordPathListBox, QStringList(dir.canonicalPath()) );
+ }
+}
+
+void BtInstallPathDialog::slotRemoveClicked() {
+ QTreeWidgetItem* i = m_swordPathListBox->currentItem();
+ if (i) {
+ delete i;
+ }
+}
+
+void BtInstallPathDialog::writeSwordConfig()
+{
+ qDebug("BtInstallPathDialog::writeSwordConfig");
+ if (m_swordPathListBox->topLevelItemCount() >= 0) {
+ QStringList targets;
+ QTreeWidgetItemIterator it(m_swordPathListBox);
+ while (*it) {
+ if (!(*it)->text(0).isEmpty()) {
+ targets << (*it)->text(0);
+ }
+ ++it;
+ }
+ qDebug() << "save the target list" << targets;
+ instbackend::setTargetList(targets); //creates new Sword config
+ }
+}
+
+void BtInstallPathDialog::accept()
+{
+ writeSwordConfig();
+ QDialog::accept();
+}
diff --git a/src/frontend/bookshelfmanager/installpage/btinstallpathdialog.h b/src/frontend/bookshelfmanager/installpage/btinstallpathdialog.h
new file mode 100644
index 0000000..c3b56ac
--- /dev/null
+++ b/src/frontend/bookshelfmanager/installpage/btinstallpathdialog.h
@@ -0,0 +1,44 @@
+/*********
+*
+* This file is part of BibleTime's source code, http://www.bibletime.info/.
+*
+* Copyright 1999-2008 by the BibleTime developers.
+* The BibleTime source code is licensed under the GNU General Public License version 2.0.
+*
+**********/
+
+#ifndef BTINSTALLPATHDIALOG_H
+#define BTINSTALLPATHDIALOG_H
+
+#include <QDialog>
+
+class QPushButton;
+class QTreeWidget;
+
+
+class BtInstallPathDialog : public QDialog
+{
+ Q_OBJECT
+public:
+ BtInstallPathDialog();
+
+public slots:
+ virtual void accept();
+
+private slots:
+ void slotAddClicked();
+ void slotRemoveClicked();
+ void slotEditClicked();
+
+private:
+ void writeSwordConfig();
+
+private:
+ QPushButton* m_editButton;
+ QPushButton* m_addButton;
+ QPushButton* m_removeButton;
+ QTreeWidget* m_swordPathListBox;
+
+};
+
+#endif
diff --git a/src/frontend/bookshelfmanager/installpage/btinstallprogressdialog.cpp b/src/frontend/bookshelfmanager/installpage/btinstallprogressdialog.cpp
new file mode 100644
index 0000000..2f60fc9
--- /dev/null
+++ b/src/frontend/bookshelfmanager/installpage/btinstallprogressdialog.cpp
@@ -0,0 +1,261 @@
+/*********
+*
+* This file is part of BibleTime's source code, http://www.bibletime.info/.
+*
+* Copyright 1999-2008 by the BibleTime developers.
+* The BibleTime source code is licensed under the GNU General Public License version 2.0.
+*
+**********/
+
+#include "btinstallprogressdialog.h"
+
+#include "btinstallthread.h"
+
+#include "util/ctoolclass.h"
+#include "util/cpointers.h"
+#include "backend/managers/cswordbackend.h"
+
+#include <QTreeWidget>
+#include <QTreeWidgetItem>
+#include <QDialog>
+#include <QHeaderView>
+#include <QProgressBar>
+#include <QPushButton>
+#include <QVBoxLayout>
+#include <QApplication>
+#include <QCloseEvent>
+#include <QMultiMap>
+
+#include <QDebug>
+
+
+BtInstallProgressDialog::BtInstallProgressDialog(QWidget* parent, QTreeWidget* selectedModulesTreeWidget, QString destination)
+ : QDialog(parent)
+{
+ // we want this dialog to be deleted when user closes it or the downloads are completed
+ setAttribute(Qt::WA_DeleteOnClose, true);
+ setWindowTitle(tr("Install Progress"));
+ //create the dialog which shows the status and lets the user stop installation
+ m_statusWidget = new QTreeWidget();
+ m_statusWidget->setRootIsDecorated(false);
+ m_statusWidget->setHeaderLabels(QStringList(tr("Work")) << tr("Progress") << QString::null);
+ m_statusWidget->header()->setStretchLastSection(false);
+ m_statusWidget->header()->setResizeMode(1, QHeaderView::Stretch);
+ m_statusWidget->header()->setMovable(false);
+ //m_statusWidget->setColumnWidth(1, CToolClass::mWidth(m_statusWidget, 2));
+
+ foreach (QTreeWidgetItem* sourceItem, selectedModulesTreeWidget->invisibleRootItem()->takeChildren()) {
+ // create items and threads for modules under this source
+ foreach (QTreeWidgetItem* moduleItem, sourceItem->takeChildren()) {
+ if (moduleItem->checkState(0) == Qt::Checked) {
+ // create a thread for this module
+ BtInstallThread* thread = new BtInstallThread(this, moduleItem->text(0), sourceItem->text(0), destination);
+ m_waitingThreads.insert(sourceItem->text(0), thread);
+ m_threadsByModule.insert(moduleItem->text(0), thread);
+ // progress widget/item
+ QPushButton* stopButton = new QPushButton(tr("Stop"), m_statusWidget);
+ stopButton->setFixedSize(stopButton->sizeHint());
+
+ // the item
+ QTreeWidgetItem* progressItem = new QTreeWidgetItem(m_statusWidget);
+ m_statusWidget->setColumnWidth(2, stopButton->sizeHint().width());
+ progressItem->setSizeHint(2, stopButton->sizeHint());
+ progressItem->setText(0, moduleItem->text(0));
+ progressItem->setFlags(Qt::ItemIsEnabled);
+ m_statusWidget->setItemWidget(progressItem, 2, stopButton);
+ progressItem->setText(1, tr("Waiting for turn..."));
+
+ //connect the signals between the dialog, items and threads
+ QObject::connect(stopButton, SIGNAL(clicked()), thread, SLOT(slotStopInstall()), Qt::QueuedConnection);
+ QObject::connect(thread, SIGNAL(installStopped(QString, QString)), this, SLOT(slotOneItemStopped(QString, QString)), Qt::QueuedConnection);
+ //is this needed or is statusUpdated enough?
+ QObject::connect(thread, SIGNAL(installCompleted(QString, QString, int)), this, SLOT(slotOneItemCompleted(QString, QString, int)), Qt::QueuedConnection);
+ QObject::connect(thread, SIGNAL(statusUpdated(QString, int)), this, SLOT(slotStatusUpdated(QString, int)), Qt::QueuedConnection);
+ QObject::connect(thread, SIGNAL(downloadStarted(QString)), this, SLOT(slotDownloadStarted(QString)), Qt::QueuedConnection);
+
+ QObject::connect(thread, SIGNAL(preparingInstall(QString, QString)), this, SLOT(slotInstallStarted(QString, QString)), Qt::QueuedConnection);
+
+ }
+ }
+ }
+
+ m_statusWidget->setMinimumWidth(m_statusWidget->size().width());
+ QPushButton* stopAllButton = new QPushButton(tr("Stop All"), this);
+
+ QVBoxLayout* layout = new QVBoxLayout(this);
+ layout->addWidget(m_statusWidget);
+ layout->addWidget(stopAllButton);
+
+ connect(stopAllButton, SIGNAL(clicked()), SLOT(slotStopInstall()) );
+
+ qApp->processEvents();
+
+ startThreads();
+
+}
+
+void BtInstallProgressDialog::startThreads()
+{
+ // remove all the updated modules from the backend module list at once
+ //foreach (QString mName, m_threadsByModule.keys()) {
+ //}
+ //QList<CSwordModuleInfo*> CPointers::backend()->takeModulesFromList(m_threadsByModule.keys());
+ qDebug() << "start threads...";
+ //loop through the multimap of the waiting threads, start at most 3 threads for each source
+ QMultiMap<QString, BtInstallThread*>::iterator threadIterator = m_waitingThreads.end();
+// concurrency is disabled for now
+// while (threadIterator != m_waitingThreads.end()) {
+// QString sourceName = threadIterator.key();
+// qDebug() << sourceName;
+// if (m_runningThreads.values(sourceName).count() < 3) {
+// BtInstallThread* t = threadIterator.value();
+// m_runningThreads.insert(sourceName, t);
+// threadIterator = m_waitingThreads.erase(threadIterator);
+// t->start();
+// }
+// else ++threadIterator;
+// }
+ //non-concurrent
+ if (threadIterator != m_waitingThreads.begin()) {
+ // go to the last item which is actually the first in the visible list
+ // because the iterator is reversed compared to insert order
+ threadIterator--;
+ QString sourceName = threadIterator.key();
+ BtInstallThread* t = threadIterator.value();
+ m_runningThreads.insert(sourceName, t);
+ threadIterator = m_waitingThreads.erase(threadIterator);
+ t->start();
+ }
+
+ qDebug("BtInstallProgressDialog::startThreads end");
+}
+
+BtInstallProgressDialog::~BtInstallProgressDialog()
+{}
+
+
+void BtInstallProgressDialog::slotOneItemCompleted(QString module, QString source, int status)
+{
+ QString message;
+ //status comes from the sword installer. TODO: Additionally we should check that there are files really installed.
+ if (status != 0) {
+ message = tr("Failed");
+ }
+ else {
+ message = tr("Completed");
+ }
+ oneItemStoppedOrCompleted(module, source, message);
+}
+
+void BtInstallProgressDialog::slotOneItemStopped(QString module, QString source)
+{
+ oneItemStoppedOrCompleted(module, source, tr("Cancelled"));
+}
+
+void BtInstallProgressDialog::oneItemStoppedOrCompleted(QString module, QString source, QString statusMessage)
+{
+ qDebug() << "\n**********************************\nBtInstallProgressDialog::oneItemStoppedOrCompleted" << module << statusMessage << "\n******************************************";
+ // update the list item
+ m_statusWidget->setItemWidget(getItem(module), 1, 0);
+ getItem(module)->setText(1, statusMessage);
+ m_statusWidget->itemWidget(getItem(module), 2)->setEnabled(false);
+ getItem(module)->setDisabled(true);
+
+ qDebug() << "remove from threads maps" << source << m_threadsByModule.value(module);
+ m_runningThreads.remove(source, m_threadsByModule.value(module));
+ m_waitingThreads.remove(source, m_threadsByModule.value(module));
+
+//concurrency is disabled for now
+// //start a waiting thread if there are any
+// QList<BtInstallThread*> threadsForSource = m_waitingThreads.values(source);
+// qDebug() << threadsForSource;
+// if (!threadsForSource.isEmpty()) {
+// qDebug() << "Threads are waiting for turn";
+// BtInstallThread* thread = threadsForSource.at(0);
+// m_waitingThreads.remove(source, thread);
+// m_runningThreads.insert(source, thread);
+// thread->start();
+// }
+
+ //non-concurrent
+ QMultiMap<QString, BtInstallThread*>::iterator threadIterator = m_waitingThreads.end();
+ if (m_runningThreads.size() == 0 && threadIterator != m_waitingThreads.begin()) {
+ threadIterator--; // the last item
+ QString sourceName = threadIterator.key();
+ BtInstallThread* t = threadIterator.value();
+ m_runningThreads.insert(sourceName, t);
+ threadIterator = m_waitingThreads.erase(threadIterator);
+ t->start();
+ }
+
+ if (threadsDone()) {
+ qDebug() << "close the dialog";
+ close();
+ }
+}
+
+void BtInstallProgressDialog::slotStopInstall()
+{
+ qDebug("BtInstallProgressDialog::slotStopInstall");
+
+ // Clear the waiting threads map, stop all running threads.
+
+ m_waitingThreads.clear();
+ if (m_runningThreads.count() > 0) {
+ foreach(BtInstallThread* thread, m_runningThreads) {
+ thread->slotStopInstall();
+ }
+ } else {
+ close();
+ }
+}
+
+void BtInstallProgressDialog::slotStatusUpdated(QString module, int status)
+{
+ //qDebug("BtInstallProgressDialog::slotStatusUpdated");
+ //qDebug() << "module:" << module << "status:" << status;
+ // find the progress bar for this module and update the value
+ QWidget* itemWidget = m_statusWidget->itemWidget(getItem(module) , 1);
+ QProgressBar* bar = dynamic_cast<QProgressBar*>(itemWidget);
+ if (bar) bar->setValue(status);
+}
+
+void BtInstallProgressDialog::slotInstallStarted(QString module, QString)
+{
+ getItem(module)->setText(1, tr("Preparing install..."));
+}
+
+void BtInstallProgressDialog::slotDownloadStarted(QString module)
+{
+ qDebug() << "BtInstallProgressDialog::slotDownloadStarted" << module;
+ getItem(module)->setText(1, QString::null);
+ //m_statusWidget->itemWidget(getItem(module), 1)->setVisible(true);
+
+ QProgressBar* bar = new QProgressBar(m_statusWidget);
+ bar->setSizePolicy(QSizePolicy::Expanding,QSizePolicy::Fixed);
+ bar->setValue(0);
+ m_statusWidget->setItemWidget(getItem(module), 1, bar);
+}
+
+QTreeWidgetItem* BtInstallProgressDialog::getItem(QString moduleName)
+{
+ //qDebug() << "BtInstallProgressDialog::getItem" << moduleName;
+ return m_statusWidget->findItems(moduleName, Qt::MatchExactly).at(0);
+}
+
+void BtInstallProgressDialog::closeEvent(QCloseEvent* event)
+{
+ qDebug("BtInstallProgressDialog::closeEvent");
+
+ if (event->spontaneous()) {
+ event->ignore();
+ return;
+ }
+ // other parts of the UI/engine must be updated
+ CPointers::backend()->reloadModules(CSwordBackend::AddedModules);
+}
+
+bool BtInstallProgressDialog::threadsDone()
+{
+ return (m_waitingThreads.count() == 0 && m_runningThreads.count() == 0);
+}
diff --git a/src/frontend/bookshelfmanager/installpage/btinstallprogressdialog.h b/src/frontend/bookshelfmanager/installpage/btinstallprogressdialog.h
new file mode 100644
index 0000000..67aa8fb
--- /dev/null
+++ b/src/frontend/bookshelfmanager/installpage/btinstallprogressdialog.h
@@ -0,0 +1,70 @@
+/*********
+*
+* This file is part of BibleTime's source code, http://www.bibletime.info/.
+*
+* Copyright 1999-2008 by the BibleTime developers.
+* The BibleTime source code is licensed under the GNU General Public License version 2.0.
+*
+**********/
+
+#ifndef BTINSTALLPROGRESSDIALOG_H
+#define BTINSTALLPROGRESSDIALOG_H
+
+#include <QDialog>
+#include <QString>
+#include <QMultiMap>
+
+class QTreeWidget;
+class QTreeWidgetItem;
+
+class BtInstallThread;
+
+
+/**
+
+*/
+class BtInstallProgressDialog : public QDialog
+{
+ Q_OBJECT
+public:
+ BtInstallProgressDialog(QWidget* parent, QTreeWidget* selectedModulesTreeWidget, QString destination);
+
+ ~BtInstallProgressDialog();
+
+public slots:
+ void slotOneItemCompleted(QString module, QString source, int status);
+ void slotOneItemStopped(QString module, QString source);
+ void slotStopInstall();
+ void slotStatusUpdated(QString module, int status);
+ void slotDownloadStarted(QString module);
+ void slotInstallStarted(QString module, QString);
+
+protected:
+ /**
+ * Handles closing by the window close button, Cancel (Stop) All button, or completing
+ * the downloads.
+ */
+ virtual void closeEvent(QCloseEvent* event);
+
+//signals:
+// void swordSetupChanged();
+
+private:
+
+ //TODO: using maps is tedious and error prone. Find better solution for handling the modules
+ // and their states.
+ QMultiMap<QString, BtInstallThread*> m_waitingThreads;
+ QMultiMap<QString, BtInstallThread*> m_runningThreads;
+ QMap<QString, BtInstallThread*> m_threadsByModule;
+ //QList<BtInstallThread*> m_doneThreads;
+
+ QTreeWidget* m_statusWidget;
+
+private:
+ QTreeWidgetItem* getItem(QString moduleName);
+ bool threadsDone();
+ void startThreads();
+ void oneItemStoppedOrCompleted(QString module, QString source, QString message);
+};
+
+#endif
diff --git a/src/frontend/bookshelfmanager/installpage/btinstallthread.cpp b/src/frontend/bookshelfmanager/installpage/btinstallthread.cpp
new file mode 100644
index 0000000..adab4fa
--- /dev/null
+++ b/src/frontend/bookshelfmanager/installpage/btinstallthread.cpp
@@ -0,0 +1,199 @@
+/*********
+*
+* This file is part of BibleTime's source code, http://www.bibletime.info/.
+*
+* Copyright 1999-2008 by the BibleTime developers.
+* The BibleTime source code is licensed under the GNU General Public License version 2.0.
+*
+**********/
+
+#include "btinstallthread.h"
+
+#include "frontend/bookshelfmanager/btinstallmgr.h"
+#include "frontend/bookshelfmanager/instbackend.h"
+#include "util/cpointers.h"
+#include "backend/managers/cswordbackend.h"
+
+#include <QApplication>
+#include <QString>
+#include <QThread>
+#include <QDir>
+
+#include <QDebug>
+
+// sword
+#include <filemgr.h>
+
+BtInstallThread::BtInstallThread(QObject* parent, QString moduleName, QString sourceName, QString destinationName)
+ : QThread(parent),
+ done(false),
+ m_module(moduleName),
+ m_destination(destinationName),
+ m_source(sourceName),
+ m_cancelled(false),
+ m_installSource(instbackend::source(sourceName)),
+ m_backendForSource(instbackend::backend(m_installSource))
+{
+ m_iMgr = new BtInstallMgr();
+}
+
+
+BtInstallThread::~BtInstallThread()
+{
+ delete m_iMgr;
+}
+
+void BtInstallThread::run()
+{
+ qDebug() << "****************************************\nBtInstallThread::run, mod:" << m_module << "\n************************************";
+
+
+ emit preparingInstall(m_module, m_source);
+
+ //make sure target/mods.d and target/modules exist
+ //TODO: move this to some common precondition
+ QDir dir(m_destination);
+ if (!dir.exists()) {
+ dir.mkdir(m_destination);
+ qDebug() << "made directory" << m_destination;
+ }
+ if (!dir.exists("modules")) {
+ dir.mkdir("modules");
+ qDebug() << "made directory" << m_destination << "/modules";
+ }
+ if (!dir.exists("mods.d")) {
+ dir.mkdir("mods.d");
+ qDebug() << "made directory" << m_destination << "/mods.d";
+ }
+
+ QObject::connect(m_iMgr, SIGNAL(percentCompleted(int, int)), this, SLOT(slotManagerStatusUpdated(int, int)), Qt::QueuedConnection);
+ QObject::connect(m_iMgr, SIGNAL(downloadStarted()), this, SLOT(slotDownloadStarted()), Qt::QueuedConnection);
+
+ //check whether it's an update. If yes, remove existing module first
+ //TODO: silently removing without undo if the user cancels the update is WRONG!!!
+ removeModule();
+
+ // manager for the destination path
+ sword::SWMgr lMgr( m_destination.toLatin1() );
+
+ if (instbackend::isRemote(m_installSource)) {
+ qDebug() << "calling install";
+ int status = m_iMgr->installModule(&lMgr, 0, m_module.toLatin1(), &m_installSource);
+ if (status != 0) {
+ qWarning() << "Error with install: " << status << "module:" << m_module;
+ }
+ else {
+ done = true;
+ emit installCompleted(m_module, m_source, status);
+ }
+ }
+ else { //local source
+ emit statusUpdated(m_module, 0);
+ int status = m_iMgr->installModule(&lMgr, m_installSource.directory.c_str(), m_module.toLatin1());
+ if (status > 0) {
+ qWarning() << "Error with install: " << status << "module:" << m_module;
+ }
+ else if (status == -1) {
+ // it was terminated, do nothing
+ }
+ else {
+ emit statusUpdated(m_module, 100);
+ done = true;
+ emit installCompleted(m_module, m_source, status);
+ }
+ }
+}
+
+void BtInstallThread::slotStopInstall()
+{
+ qDebug() << "*************************************\nBtInstallThread::slotStopInstall" << m_module << "\n********************************";
+ if (!done) {
+ done = true;
+ qDebug() << "*********************************\nBtInstallThread::slotStopInstall, installing" << m_module << "was cancelled\n**************************************";
+ m_iMgr->terminate();
+ //this->terminate(); // It's dangerous to forcibly stop, but we will clean up the files
+ qDebug() << "BtInstallThread::slotStopInstall 2";
+ //qApp->processEvents();
+ // wait to terminate for some secs. We rather let the execution go on and cleaning up to fail than the app to freeze
+ int notRun = this->wait(200);
+ if (notRun) {
+ this->terminate();
+ this->wait(2);
+ qDebug() << "installthread ("<< m_module << ") terminated, delete m_iMgr";
+ delete m_iMgr; // this makes sure the ftp library will be cleaned up in the destroyer
+ m_iMgr = 0;
+ }
+ qDebug() << "BtInstallThread::slotStopInstall 3";
+ // cleanup: remove the module, remove the temp files
+ if (true) {
+ qDebug() << "BtInstallThread::slotStopInstall 4";
+ // remove the installed module, just to be sure because mgr may
+ // have been terminated when copying files
+ removeModule();
+ removeTempFiles();
+ qDebug() << "BtInstallThread::slotStopInstall will emit installStopped...";
+ emit installStopped(m_module, m_source);
+ }
+ }
+}
+
+void BtInstallThread::slotManagerStatusUpdated(int totalProgress, int /*fileProgress*/)
+{
+ //qDebug("BtInstallThread::slotManagerStatusUpdated");
+ emit statusUpdated(m_module, totalProgress);
+}
+
+void BtInstallThread::slotDownloadStarted()
+{
+ qDebug("BtInstallThread::slotDownloadStarted");
+ emit downloadStarted(m_module);
+}
+
+void BtInstallThread::removeModule()
+{
+ qDebug() << "BtInstallThread::removeModule start";
+ CSwordModuleInfo* m;
+ m = CPointers::backend()->findModuleByName(m_module);
+ if (!m) {
+ m = instbackend::backend(instbackend::source(m_destination.toLatin1()))->findModuleByName(m_module);
+ }
+ if (m) { //module found?
+ qDebug() << "BtInstallThread::removeModule, module" << m_module << "found";
+ QString prefixPath = m->config(CSwordModuleInfo::AbsoluteDataPath) + "/";
+ QString dataPath = m->config(CSwordModuleInfo::DataPath);
+ if (dataPath.left(2) == "./") {
+ dataPath = dataPath.mid(2);
+ }
+
+ if (prefixPath.contains(dataPath)) {
+ prefixPath.remove( prefixPath.indexOf(dataPath), dataPath.length() );
+ }
+ else {
+ prefixPath = QString::fromLatin1(CPointers::backend()->prefixPath);
+ }
+
+ sword::SWMgr mgr(prefixPath.toLatin1());
+ BtInstallMgr iMgr;
+ iMgr.removeModule(&mgr, m->name().toLatin1());
+ } else {
+ qDebug() << "BtInstallThread::removeModule, module" << m_module << "not found";
+ }
+}
+
+void BtInstallThread::removeTempFiles()
+{
+ qDebug("BtInstallThread::removeTempFiles start");
+
+ // (take the remote conf file for this module, take DataPath,
+ // take the absolute path of the InstallMgr)
+
+ //sword::InstallSource is = instbackend::source(m_source);
+ if (instbackend::isRemote(m_installSource)) {
+ // get the path for the module temp files
+ CSwordModuleInfo* mInfo = m_backendForSource->findModuleByName(m_module);
+ QString dataPath = mInfo->config(CSwordModuleInfo::AbsoluteDataPath);
+ qDebug() << "Delete path:" << dataPath;
+ // it's easier to use sword than qt
+ sword::FileMgr::removeDir(dataPath.toLatin1().data());
+ }
+}
diff --git a/src/frontend/bookshelfmanager/installpage/btinstallthread.h b/src/frontend/bookshelfmanager/installpage/btinstallthread.h
new file mode 100644
index 0000000..d10db95
--- /dev/null
+++ b/src/frontend/bookshelfmanager/installpage/btinstallthread.h
@@ -0,0 +1,99 @@
+/*********
+*
+* This file is part of BibleTime's source code, http://www.bibletime.info/.
+*
+* Copyright 1999-2008 by the BibleTime developers.
+* The BibleTime source code is licensed under the GNU General Public License version 2.0.
+*
+**********/
+
+#ifndef BTINSTALLTHREAD_H
+#define BTINSTALLTHREAD_H
+
+#include "frontend/bookshelfmanager/btinstallmgr.h"
+
+#include <QThread>
+
+#include <boost/scoped_ptr.hpp>
+
+class BtInstallProgressDialog;
+
+/**
+* Thread for installing a module.
+*
+* See the Qt thread documents. We have to be careful with signals and slots,
+* with other things.
+
+The main thread creates and owns the BtInstallThread object.
+When the install thread (the run() method) has been started
+the install thread object receives status update signals
+from the installmgr object. This works because these signals are sent
+from the running install thread but received in the thread object which
+lives in the main thread (note the difference between a thread object and a
+running thread). The slot of the thread object is executed in the main thread
+event loop, not in the install thread.
+
+The running thread sends update signals to the progress dialog.
+(This works because the signals are queued, i.e. the dialog is running
+in the main app event loop which queues the signals. ???)
+
+When the user cancels installing the main thread sends signal to a slot
+in the thread object. The slot is then run in the main thread, not
+in the install thread (note again the difference between a thread object and a
+running thread). That slot terminates the running install thread immediately.
+That is not the cleanest way to do it, but the Sword library has no good
+support for threads. Terminating a Sword InstallMgr takes time and leads to
+slow response. Therefore we stop installing by force and clean up the
+temporary files manually.
+
+* Terminating a thread forcibly is "dangerous and discouraged" but we have to do it,
+* I couldn't get cancelling work reliably otherwise. The Sword library is bad for threads.
+* We use ftp connection and file resources; the connection can be ignored but the files
+* have to be cleaned up after termination.
+*/
+class BtInstallThread : public QThread
+{
+ Q_OBJECT
+public:
+ BtInstallThread(QObject* parent, QString moduleName, QString sourceName, QString destinationName);
+
+ ~BtInstallThread();
+
+public slots:
+ void slotStopInstall();
+ void slotManagerStatusUpdated(int totalProgress, int fileProgress);
+ void slotDownloadStarted();
+
+public: // data member
+ bool done;
+
+protected:
+ virtual void run();
+ void removeModule();
+ void removeTempFiles();
+
+ QString m_module;
+ QString m_destination;
+ QString m_source;
+ bool m_cancelled;
+ BtInstallMgr* m_iMgr;
+ //sword::InstallSource m_installSource;
+ //BtInstallMgr m_iMgr;
+ sword::InstallSource m_installSource;
+ //TODO: it would be best to get the backend from the bookshelf manager install page
+ // where it has already been created. Could fasten the progress dialog startup.
+ boost::scoped_ptr<CSwordBackend> m_backendForSource;
+
+signals:
+ /** Emitted when the install progress status is updated. */
+ void statusUpdated(QString module, int progressPercent);
+ /** Emitted when installing has been stopped/cancelled. */
+ void installStopped(QString module, QString source);
+ /** Emitted when installing is complete. */
+ void installCompleted(QString module, QString source, int errorStatus);
+ /** Emitted when the first file download has been started. */
+ void downloadStarted(QString module);
+ void preparingInstall(QString module, QString source);
+};
+
+#endif
diff --git a/src/frontend/bookshelfmanager/installpage/btsourcearea.cpp b/src/frontend/bookshelfmanager/installpage/btsourcearea.cpp
new file mode 100644
index 0000000..2f8de1a
--- /dev/null
+++ b/src/frontend/bookshelfmanager/installpage/btsourcearea.cpp
@@ -0,0 +1,298 @@
+/*********
+*
+* This file is part of BibleTime's source code, http://www.bibletime.info/.
+*
+* Copyright 1999-2008 by the BibleTime developers.
+* The BibleTime source code is licensed under the GNU General Public License version 2.0.
+*
+**********/
+
+#include "btsourcearea.h"
+
+#include "frontend/bookshelfmanager/instbackend.h"
+
+#include "util/ctoolclass.h"
+#include "util/cpointers.h"
+#include "util/cresmgr.h"
+#include "util/directoryutil.h"
+
+#include "backend/managers/cswordbackend.h"
+#include "frontend/btaboutmoduledialog.h"
+
+#include <installmgr.h>
+
+#include <QString>
+#include <QWidget>
+#include <QMap>
+#include <QVBoxLayout>
+#include <QHBoxLayout>
+#include <QSpacerItem>
+#include <QLabel>
+#include <QPushButton>
+#include <QTreeWidget>
+#include <QTreeWidgetItem>
+#include <QHeaderView>
+
+#include <QDebug>
+#include <QTime>
+
+// ****************************************************************
+// ******** Installation source and module list widget ************
+// ****************************************************************
+
+BtSourceArea::BtSourceArea(const QString& sourceName)
+ : QWidget(),
+ m_sourceName(sourceName),
+ m_treeAlreadyInitialized(false),
+ m_remoteBackend(0) //important!
+{
+ m_checkedModules = QMap<QString, bool>();
+ qDebug() << "BtSourceArea::BtSourceArea, " << m_sourceName;
+ initView();
+}
+
+BtSourceArea::~BtSourceArea()
+{
+ delete m_remoteBackend;
+}
+
+void BtSourceArea::initView()
+{
+ qDebug("BtSourceArea::initView");
+ QVBoxLayout *mainLayout = new QVBoxLayout(this);
+ //QHBoxLayout *refreshLabelLayout = new QHBoxLayout();
+ //QLabel *refreshLabel = new QLabel(tr("Last refreshed:"));
+ //m_refreshTimeLabel = new QLabel();
+ //QSpacerItem *refreshLabelSpacer = new QSpacerItem(201, 20, QSizePolicy::Expanding, QSizePolicy::Minimum);
+
+ //refreshLabelLayout->addWidget(refreshLabel);
+ //refreshLabelLayout->addWidget(m_refreshTimeLabel);
+ //refreshLabelLayout->addItem(refreshLabelSpacer);
+ // TODO: or would it be better to integrate this information into the tooltip
+ // of the source tab?
+ //mainLayout->addLayout(refreshLabelLayout);
+
+ // source related button row
+ QHBoxLayout *sourceLayout = new QHBoxLayout();
+ m_refreshButton = new QPushButton(tr("Refresh..."));
+ m_refreshButton->setToolTip(tr("Refresh the list of works from this source"));
+ m_refreshButton->setIcon(util::filesystem::DirectoryUtil::getIcon(CResMgr::bookshelfmgr::installpage::refresh_icon));
+ //m_refreshButton->setEnabled(false);
+ QSpacerItem *sourceSpacer = new QSpacerItem(40, 20, QSizePolicy::Expanding, QSizePolicy::Minimum);
+ //m_editButton = new QPushButton(tr("Edit..."));
+ //m_editButton->setEnabled(false); // TODO after writing the edit widget
+ m_deleteButton = new QPushButton(tr("Delete..."));
+ m_deleteButton->setToolTip(tr("Delete this source"));
+ m_deleteButton->setIcon(util::filesystem::DirectoryUtil::getIcon(CResMgr::bookshelfmgr::installpage::delete_icon));
+ //m_deleteButton->setEnabled(false);
+ m_addButton = new QPushButton(tr("Add..."));
+ m_addButton->setToolTip(tr("Add new source"));
+ m_addButton->setIcon(util::filesystem::DirectoryUtil::getIcon(CResMgr::bookshelfmgr::installpage::add_icon));
+
+ sourceLayout->addWidget(m_refreshButton);
+ sourceLayout->addItem(sourceSpacer);
+ //sourceLayout->addWidget(m_editButton);
+ sourceLayout->addWidget(m_deleteButton);
+ sourceLayout->addWidget(m_addButton);
+
+ mainLayout->addLayout(sourceLayout);
+ // There are no views for the stack yet, see initSources
+ m_view = new QTreeWidget(this);
+ m_view->setHeaderLabels(QStringList() << tr("Work") << tr("Description"));
+ m_view->setColumnWidth(0, CToolClass::mWidth(m_view, 20));
+ mainLayout->addWidget(m_view);
+
+ connect(m_view, SIGNAL(itemDoubleClicked(QTreeWidgetItem*, int)), SLOT(slotItemDoubleClicked(QTreeWidgetItem*, int)));
+ connect(CPointers::backend(), SIGNAL(sigSwordSetupChanged(CSwordBackend::SetupChangedReason)), SLOT(slotSwordSetupChanged()));
+ connect(this, SIGNAL(signalCreateTree()), SLOT(slotCreateTree()), Qt::QueuedConnection);
+}
+
+QSize BtSourceArea::sizeHint() const
+{
+ return QSize(100, m_refreshButton->height() + (m_view->header()->height() * 5));
+}
+
+void BtSourceArea::initTreeFirstTime()
+{
+ if (!m_treeAlreadyInitialized) {
+ createModuleTree();
+ m_treeAlreadyInitialized = true;
+ }
+}
+
+void BtSourceArea::createModuleTree()
+{
+ qDebug("BtSourceArea::createModuleTree start");
+ // Start creating tree with a queued connection.
+ // This makes showing the dialog possible even before the tree is initialized.
+ emit signalCreateTree();
+}
+void BtSourceArea::slotCreateTree()
+{
+ qDebug()<<"BtSourceArea::slotCreateTree" << QTime::currentTime ();
+ //let the dialog become visible
+ QCoreApplication::processEvents();
+ // Creating the view and populating list may take time
+ QApplication::setOverrideCursor( QCursor(Qt::WaitCursor) );
+
+ // disconnect the signal so that we don't have to run functions for every module
+ // (note: what to do if we want to restore the item selection when rebuilding?
+ disconnect(m_view, SIGNAL(itemChanged(QTreeWidgetItem*, int)), this, SLOT(slotSelectionChanged(QTreeWidgetItem*, int)) );
+ m_view->clear();
+
+ // TODO: if the tree already exists for this source,
+ // maybe the selections should be preserved
+ m_checkedModules.clear();
+
+ sword::InstallSource is = instbackend::source(m_sourceName);
+ delete m_remoteBackend; // the old one can be deleted
+ m_remoteBackend = instbackend::backend(is);
+ Q_ASSERT(m_remoteBackend);
+ m_moduleList = m_remoteBackend->moduleList();
+
+ // give the list to BTModuleTreeItem, create filter to remove
+ // those modules which are installed already
+ InstalledFilter alreadyInstalledFilter(m_sourceName);
+ QList<BTModuleTreeItem::Filter*> filterList;
+ filterList.append(&alreadyInstalledFilter);
+ BTModuleTreeItem rootItem(filterList, BTModuleTreeItem::CatLangMod, &m_moduleList);
+
+ addToTree(&rootItem, m_view->invisibleRootItem());
+ QCoreApplication::processEvents();
+ // receive signal when user checks modules
+ connect(m_view, SIGNAL(itemChanged(QTreeWidgetItem*, int)), this, SLOT(slotSelectionChanged(QTreeWidgetItem*, int)) );
+ QApplication::restoreOverrideCursor();
+ qDebug()<< "BtSourceArea::createModuleTree end"<< QTime::currentTime ();
+}
+
+void BtSourceArea::addToTree(BTModuleTreeItem* item, QTreeWidgetItem* widgetItem)
+{
+ //qDebug()<<"BtSourceArea::addToTree "<<item->text();
+ //qDebug() << "BTMTItem type: " << item->type();
+
+ foreach (BTModuleTreeItem* i, item->children()) {
+ addToTree(i, new QTreeWidgetItem(widgetItem));
+ }
+ if (item->type() != BTModuleTreeItem::Root) {
+ CSwordModuleInfo* mInfo = item->moduleInfo();
+ widgetItem->setText(0, item->text());
+ if (item->type() == BTModuleTreeItem::Category || item->type() == BTModuleTreeItem::Language) {
+ //qDebug() << "item"<<item->text()<< "was cat or lang";
+ widgetItem->setFlags(Qt::ItemIsUserCheckable | Qt::ItemIsEnabled | Qt::ItemIsTristate);
+ }
+ if (item->type() == BTModuleTreeItem::Module) {
+ //qDebug() << "item"<<item->text()<< "was a module";
+ widgetItem->setFlags(Qt::ItemIsUserCheckable | Qt::ItemIsEnabled);
+ widgetItem->setCheckState(0, Qt::Unchecked);
+
+ CSwordModuleInfo* const installedModule = CPointers::backend()->findModuleByName(mInfo->name());
+ QString installedV;
+
+ if (!installedModule) {
+ // possible TODO: save the module list of a source before refreshing,
+ // compare after refreshing, mark the newly added modules
+ //if not newly added:
+ //state: installable (no indicator)
+ //else: status: newly added, color yellow
+ } else { // the module is already installed
+ QBrush bg(QColor(255,153,153));
+ widgetItem->setBackground(0, bg);
+ widgetItem->setBackground(1, bg);
+ installedV = QString(installedModule->config(CSwordModuleInfo::ModuleVersion).toLatin1());
+ // set the color for the parent items
+ QTreeWidgetItem* parent1 = widgetItem->parent();
+ if (parent1) {
+ parent1->setBackground(0,bg);
+ parent1->setBackground(1,bg);
+ QTreeWidgetItem* parent2 = parent1->parent();
+ if (parent2) {
+ parent2->setBackground(0,bg);
+ parent2->setBackground(1,bg);
+ }
+ }
+ }
+
+
+ QString descr(mInfo->config(CSwordModuleInfo::Description));
+ QString toolTipText = CToolClass::remoteModuleToolTip(mInfo, installedV);
+
+ widgetItem->setText(1, descr);
+ widgetItem->setToolTip(0, toolTipText);
+ widgetItem->setToolTip(1, toolTipText);
+ }
+ }
+}
+
+QTreeWidget* BtSourceArea::treeWidget()
+{
+ return m_view;
+}
+
+// return the selected modules
+QMap<QString, bool>* BtSourceArea::selectedModules()
+{
+ return &m_checkedModules;
+}
+
+// when a module is checked/unchecked
+void BtSourceArea::slotSelectionChanged(QTreeWidgetItem* item, int column)
+{
+ //qDebug("BtSourceArea::slotSelectionChanged");
+ // modify the internal list of selected (actually checked) modules
+ // if() leaves groups away
+ if (!item->childCount() && column == 0) {
+ foreach (CSwordModuleInfo* module, m_moduleList) {
+ if (module->name() == item->text(0)) {
+ if (item->checkState(0) == Qt::Checked) {
+ qDebug() << module->name() << "was checked";
+ m_checkedModules.insert(module->name(), true);
+ } else {
+ qDebug() << module->name() << "was unchecked";
+ m_checkedModules.remove(module->name());
+ }
+ emit signalSelectionChanged(m_sourceName, m_checkedModules.count());
+ break;
+ }
+ }
+ }
+}
+
+void BtSourceArea::slotItemDoubleClicked(QTreeWidgetItem* item, int /*column*/)
+{
+ CSwordModuleInfo* mInfo = m_remoteBackend->findModuleByName(item->text(0));
+ if (mInfo) {
+ BTAboutModuleDialog* dialog = new BTAboutModuleDialog(this, mInfo);
+ dialog->show();
+ dialog->raise();
+ }
+}
+
+BtSourceArea::InstalledFilter::InstalledFilter(QString sourceName)
+ : BTModuleTreeItem::Filter(),
+ m_source(instbackend::source(sourceName)),
+ m_swordBackend(instbackend::backend(m_source))
+{
+ // these are set once to optimize away repeated calls
+ // m_source, m_swordBackend
+
+}
+//filter out already installed, not updateable modules
+bool BtSourceArea::InstalledFilter::filter(CSwordModuleInfo* mInfo)
+{
+ //qDebug() << "BtSourceArea::InstalledFilter::filter, module " << mInfo->name();
+ CSwordModuleInfo* const installedModule = CPointers::backend()->findModuleByName(mInfo->name());
+ if (installedModule) {
+ //qDebug("already installed, check if it's an update...");
+ const sword::SWVersion installedVersion(installedModule->config(CSwordModuleInfo::ModuleVersion).toLatin1());
+ const sword::SWVersion newVersion(mInfo->config(CSwordModuleInfo::ModuleVersion).toLatin1());
+ if (installedVersion >= newVersion) {
+ return false;
+ }
+ }
+ return true;
+}
+
+void BtSourceArea::slotSwordSetupChanged()
+{
+ createModuleTree();
+}
diff --git a/src/frontend/bookshelfmanager/installpage/btsourcearea.h b/src/frontend/bookshelfmanager/installpage/btsourcearea.h
new file mode 100644
index 0000000..814bde8
--- /dev/null
+++ b/src/frontend/bookshelfmanager/installpage/btsourcearea.h
@@ -0,0 +1,97 @@
+/*********
+*
+* This file is part of BibleTime's source code, http://www.bibletime.info/.
+*
+* Copyright 1999-2008 by the BibleTime developers.
+* The BibleTime source code is licensed under the GNU General Public License version 2.0.
+*
+**********/
+
+#ifndef BTSOURCEAREA_H
+#define BTSOURCEAREA_H
+
+#include "backend/btmoduletreeitem.h"
+
+#include <boost/scoped_ptr.hpp>
+
+#include <installmgr.h>
+
+#include <QWidget>
+#include <QMap>
+#include <QApplication>
+
+class QTreeWidget;
+class QTreeWidgetItem;
+class QLabel;
+class QPushButton;
+
+/**
+* Area for one install source.
+*
+* - Tree widget for modules
+* - Buttons for handling the source(s): refresh, edit, remove, add
+*
+* Each source has
+* QTreeWidget, populated with the module tree if the source
+* module list is in a local cache. Refreshing the source refreshes
+* the cache and rebuilds the module tree. Sources are not refreshed
+* automatically, only by the user action, one source at a time.
+*/
+class BtSourceArea : public QWidget
+{
+ Q_OBJECT
+
+ friend class BtSourceWidget;
+public:
+
+ struct InstalledFilter : BTModuleTreeItem::Filter {
+ InstalledFilter(QString sourceName);
+ bool filter(CSwordModuleInfo*);
+ sword::InstallSource m_source;
+ boost::scoped_ptr<CSwordBackend> m_swordBackend;
+ };
+
+ BtSourceArea(const QString& sourceName);
+ ~BtSourceArea();
+
+ void initView();
+ /** Reimplemented from QWidget. */
+ virtual QSize sizeHint() const;
+ void initTreeFirstTime();
+ QTreeWidget* treeWidget();
+
+ QMap<QString, bool>* selectedModules();
+
+public slots:
+ void slotSwordSetupChanged();
+ /** Create a module tree for a tree widget */
+ void createModuleTree();
+
+signals:
+ void signalSelectionChanged(QString sourceName, int selectedCount);
+ void signalCreateTree();
+
+private slots:
+ void slotCreateTree();
+ void slotSelectionChanged(QTreeWidgetItem* item, int column);
+ void slotItemDoubleClicked(QTreeWidgetItem* item, int column);
+private:
+ void addToTree(BTModuleTreeItem* item, QTreeWidgetItem* widgetItem);
+
+ QString m_sourceName;
+ bool m_treeAlreadyInitialized;
+ QMap<QString, bool> m_checkedModules;
+ CSwordBackend* m_remoteBackend; // needed for the module list
+ QList<CSwordModuleInfo*> m_moduleList;
+
+ QTreeWidget* m_view;
+ QLabel* m_refreshTimeLabel;
+ QPushButton* m_refreshButton;
+ QPushButton* m_editButton;
+ QPushButton* m_deleteButton;
+ QPushButton* m_addButton;
+
+
+};
+
+#endif
diff --git a/src/frontend/bookshelfmanager/installpage/btsourcewidget.cpp b/src/frontend/bookshelfmanager/installpage/btsourcewidget.cpp
new file mode 100644
index 0000000..586a680
--- /dev/null
+++ b/src/frontend/bookshelfmanager/installpage/btsourcewidget.cpp
@@ -0,0 +1,403 @@
+/*********
+*
+* This file is part of BibleTime's source code, http://www.bibletime.info/.
+*
+* Copyright 1999-2008 by the BibleTime developers.
+* The BibleTime source code is licensed under the GNU General Public License version 2.0.
+*
+**********/
+
+#include "btsourcewidget.h"
+
+#include "btinstallpage.h"
+#include "btsourcearea.h"
+#include "btinstallprogressdialog.h"
+#include "btinstallmodulechooserdialog.h"
+
+#include "frontend/bookshelfmanager/btmodulemanagerdialog.h"
+#include "frontend/bookshelfmanager/cswordsetupinstallsourcesdialog.h"
+#include "frontend/bookshelfmanager/btinstallmgr.h"
+#include "frontend/bookshelfmanager/instbackend.h"
+
+
+#include <QString>
+#include <QWidget>
+#include <QTabWidget>
+#include <QMessageBox>
+#include <QProgressDialog>
+#include <QPushButton>
+#include <QApplication>
+#include <QFileInfo>
+#include <QTabBar>
+#include <QTreeWidget>
+#include <QTreeWidgetItem>
+#include <QHBoxLayout>
+#include <QLabel>
+
+// ****************************************************************
+// ******** Tab Widget that holds source widgets ******************
+// ****************************************************************
+
+BtSourceWidget::BtSourceWidget(BtInstallPage* parent)
+ : QTabWidget(parent),
+ m_page(parent)
+{
+ qDebug("BtSourceWidget::BtSourceWidget start");
+ initSources();
+
+ // TODO: choose the page from config
+
+}
+
+BtSourceArea* BtSourceWidget::area()
+{
+ return dynamic_cast<BtSourceArea*>(currentWidget());
+}
+
+QString BtSourceWidget::currentSourceName()
+{
+ qDebug() << "BtSourceWidget::currentSourceName: " << m_sourceNameList.at(currentIndex());
+ return m_sourceNameList.at(currentIndex());
+}
+
+void BtSourceWidget::initSourceConnections()
+{
+ qDebug("void BtSourceWidget::initSourceConnections() start");
+ if (area()) {
+ connect(area()->m_refreshButton, SIGNAL(clicked()), SLOT(slotRefresh()));
+ //connect(area()->m_editButton, SIGNAL(clicked()), SLOT(slotEdit()));
+ connect(area()->m_deleteButton, SIGNAL(clicked()), SLOT(slotDelete()));
+ connect(area()->m_addButton, SIGNAL(clicked()), SLOT(slotAdd()));
+ connect(area(), SIGNAL(signalSelectionChanged(QString, int)), SLOT(slotModuleSelectionChanged(QString, int)) );
+ }
+ qDebug("void BtSourceWidget::initSourceConnections() end");
+}
+
+void BtSourceWidget::slotEdit()
+{
+ qDebug("BtSourceWidget::slotEdit");
+ // open the source editor dialog
+
+ // if the source was changed, init the sources
+
+}
+
+void BtSourceWidget::slotDelete()
+{
+ qDebug("void BtSourceWidget::slotDelete() start");
+ // ask for confirmation
+ int ret = QMessageBox::warning(this, tr("Delete Source?"),
+ tr("Do you really want to delete this source?"),
+ QMessageBox::Yes | QMessageBox::No);
+
+ if (ret == QMessageBox::Yes) {
+ instbackend::deleteSource(currentSourceName());
+
+ // remove the UI elements
+ m_sourceNameList.removeAt(currentIndex());
+ QWidget* w = currentWidget();
+ removeTab(currentIndex());
+ delete w;
+ }
+}
+
+void BtSourceWidget::slotAdd()
+{
+ qDebug("void BtSourceWidget::slotAdd() start");
+ qDebug("open the old dialog, TODO: write new one");
+ sword::InstallSource newSource = CSwordSetupInstallSourcesDialog::getSource();
+ if ( !((QString)newSource.type.c_str()).isEmpty() ) { // we have a valid source to add
+ instbackend::addSource(newSource);
+ addSource(QString(newSource.caption.c_str()));
+ }
+}
+
+
+void BtSourceWidget::slotRefresh()
+{
+ qDebug("void BtSourceWidget::slotRefresh() start");
+ // (re)build the module cache for the source
+
+ QString sourceName = currentSourceName();
+
+ // quick enough, make it modal so that we don't need to take care of anything else
+ m_progressDialog = new QProgressDialog("", tr("Cancel"), 0 ,100, this);
+ m_progressDialog->setWindowTitle(tr("Refreshing Source"));
+ m_progressDialog->setMinimumDuration(0);
+
+ // TODO: get rid of the backend code, BtInstallMgr and progressdialog could handle this
+ //write method BtInstallMgr::slotRefreshCanceled()
+ connect(m_progressDialog, SIGNAL(canceled()), SLOT(slotRefreshCanceled()));
+
+ // BACKEND CODE **********************************************************
+ // would this be possible: instbackend::refreshSource( arguments );
+ qDebug("void BtSourceWidget::slotRefresh 1");
+ BtInstallMgr iMgr;
+ m_currentInstallMgr = &iMgr; //for the progress dialog
+ sword::InstallSource is = instbackend::source(sourceName);
+ bool success = false;
+ qDebug("void BtSourceWidget::slotRefresh 2");
+ // connect this directly to the dialog setValue(int) if possible
+ connect(&iMgr, SIGNAL(percentCompleted(const int, const int)), SLOT(slotRefreshCompleted(const int, const int)));
+
+ if (instbackend::isRemote(is)) {
+ m_progressDialog->show();
+ qApp->processEvents();
+ this->slotRefreshCompleted(0,0);
+ m_progressDialog->setLabelText(tr("Connecting..."));
+ m_progressDialog->setValue(0);
+ qApp->processEvents();
+ //qApp->flush();
+ //qApp->processEvents();
+ //m_progressDialog->repaint();
+ //qApp->processEvents();
+ qDebug("void BtSourceWidget::slotRefresh 3");
+ bool successful = iMgr.refreshRemoteSource( &is );
+ if (!successful ) { //make sure the sources were updated sucessfully
+ success = true;
+ m_progressDialog->setValue(100); //make sure the dialog closes
+ }
+ else {
+ qWarning("InstallMgr: refreshRemoteSources returned an error.");
+ success = false;
+ }
+ }
+ else {
+ // Local source, update the list
+ success = true;
+ }
+
+ delete m_progressDialog;
+ m_progressDialog = 0;
+
+ // rebuild the view tree and refresh the view
+ if (success) {
+ qDebug("void BtSourceWidget::slotRefresh 4");
+ area()->createModuleTree();
+ }
+}
+
+//TODO: try to move this to BtInstallMgr
+void BtSourceWidget::slotRefreshCanceled()
+{
+ qDebug("BtSourceWidget::slotRefreshCanceled");
+ Q_ASSERT(m_currentInstallMgr);
+ if (m_currentInstallMgr) {
+ m_currentInstallMgr->terminate();
+ }
+ qApp->processEvents();
+}
+
+//TODO: try to move this to progress dialog
+void BtSourceWidget::slotRefreshCompleted(const int, const int current)
+{
+ qDebug("BtSourceWidget::slotRefreshCompleted");
+ if (m_progressDialog) {
+ if (m_progressDialog->labelText() != tr("Refreshing...")) {
+ m_progressDialog->setLabelText(tr("Refreshing..."));
+ }
+ m_progressDialog->setValue(current);
+ }
+ qApp->processEvents();
+}
+
+// init the tabbar, setup the module tree for the current source
+void BtSourceWidget::initSources()
+{
+ qDebug("void BtSourceWidget::initSources() start");
+
+ // ***** Use the backend to get the list of sources *****
+ instbackend::initPassiveFtpMode();
+ QStringList sourceList = instbackend::sourceList();
+
+ // Add a default entry, the Crosswire main repository
+ // TODO: this is easy for the user, but should the edit dialog
+ // open automatically?
+ if (!sourceList.count()) {
+ sword::InstallSource is("FTP"); //default return value
+ is.caption = "Crosswire";
+ is.source = "ftp.crosswire.org";
+ is.directory = "/pub/sword/raw";
+ // passive ftp is not needed here, it's the default
+
+ instbackend::addSource(is);
+
+ sourceList = instbackend::sourceList();
+ //Q_ASSERT( sourceList.count() > 0 );
+ }
+ qDebug("void BtSourceWidget::initSources 1");
+ // Add the sources to the widget
+ foreach (QString sourceName, sourceList) {
+ addSource(sourceName);
+ }
+ // connect this after the tabs have been created,
+ // otherwise the signal is caught too early.
+ QObject::connect(this, SIGNAL(currentChanged(int)), this, SLOT(slotTabSelected(int)));
+ qDebug("void BtSourceWidget::initSources end");
+ // TODO: select the current source from the config
+ // It's important to choose something because the tree is not initialized until now
+ setCurrentIndex(0);
+ slotTabSelected(0); // setting the index wasn't enough if there were only 1 tab
+
+ if (sourceList.count() == 0) {
+ QHBoxLayout* l = new QHBoxLayout(this);
+ QLabel* message = new QLabel(QString("<i>") + tr("No sources were found in the SWORD configuration and BibleTime couldn't create a default source. Check your SWORD configuration and that the configuration path is writable. Then restart the Bookshelf Manager.") + QString("</i>"), this);
+ message->setWordWrap(true);
+ l->addWidget(message);
+ }
+}
+
+void BtSourceWidget::addSource(const QString& sourceName)
+{
+ qDebug("void BtSourceWidget::addSource(const QString& sourceName) start");
+ // The source has already been added to the backend.
+
+ QString type;
+ QString server;
+ QString path;
+ sword::InstallSource is = instbackend::source(sourceName);
+ if (instbackend::isRemote(is)) {
+ type = tr("Remote:");
+ server = is.source.c_str();
+ path = is.directory.c_str();
+ }
+ else { // local source
+ type = tr("Local:");
+ QFileInfo fi( is.directory.c_str() );
+ path = is.directory.c_str();
+ if (!(fi.isDir() )) {
+ path = path + QString(" ") + tr("Not a directory!"); //TODO: change this
+ }
+ if (!fi.isReadable()) {
+ path = path + QString(" ") + tr("Not readable!"); //TODO: change this
+ }
+ }
+
+ // Here the tab UI is created and added to the tab widget
+ BtSourceArea* area = new BtSourceArea(sourceName);
+ int tabNumber = this->addTab(area, sourceName);
+
+ // TODO: add "remote/local", server, path etc.
+ QString toolTip(QString("<p style='white-space:pre'>") + sourceName + QString("<br/><b>") + type + QString("</b> ") + server + path + QString("</p>"));
+ tabBar()->setTabToolTip(tabNumber, toolTip);
+
+ //select the new tab
+ setCurrentIndex(tabNumber);
+ m_sourceNameList.append(sourceName);
+ initSourceConnections();
+ qDebug("BtSourceWidget::addSource end");
+}
+
+//
+void BtSourceWidget::slotModuleSelectionChanged(QString sourceName, int selectedCount)
+{
+ //TODO: editing sources should update the map also
+ qDebug("BtSourceWidget::slotModuleSelectionChanged start");
+
+ int overallCount = 0;
+ m_selectedModulesCountMap.insert(sourceName, selectedCount);
+ foreach (int count, m_selectedModulesCountMap) {
+ qDebug() << "add" << count << "to overall count of selected modules";
+ overallCount += count;
+ }
+
+ if (overallCount > 0) {
+ m_page->setInstallEnabled(true);
+ } else {
+ m_page->setInstallEnabled(false);
+ }
+}
+
+void BtSourceWidget::slotTabSelected(int /*index*/)
+{
+ BtSourceArea* area = dynamic_cast<BtSourceArea*>(currentWidget());
+ if (area) area->initTreeFirstTime();
+}
+
+void BtSourceWidget::slotInstall()
+{
+ qDebug("void BtInstallPage::slotInstall start");
+
+ // check that the destination path is writable, do nothing if not and user doesn't want to continue
+ QDir dir = QDir(dynamic_cast<BtInstallPage*>(parent())->selectedInstallPath());
+ bool canWrite = true;
+ if (dir.isReadable()) {
+ const QFileInfo fi( dir.canonicalPath() );
+ if (!fi.exists() || !fi.isWritable()) {
+ canWrite = false;
+ }
+ } else {
+ canWrite = false;
+ }
+ if (!canWrite) {
+ const int result = QMessageBox::warning(this, tr("Warning"), tr("The destination directory is not writable or does not exist. Installation will fail unless this has first been fixed."), QMessageBox::Ignore|QMessageBox::Cancel, QMessageBox::Cancel);
+ if (result != QMessageBox::Ignore) {
+ return;
+ }
+ }
+
+ // create the confirmation dialog
+ // (module tree dialog, modules taken from all sources)
+ QString dlgTitle(tr("Install/Update works?"));
+ QString dlgLabel(tr("Do you really want to install these works?") +
+ QString("<br/><br/><small>") +
+ tr("Only one version of a work can be installed at the same time. Select only one if there are items marked with red.") +
+ QString("</small>"));
+
+ // with empty list we avoid creating the module tree inside the dialog code
+ QList<CSwordModuleInfo*> emptyList;
+ BtInstallModuleChooserDialog* dlg = new BtInstallModuleChooserDialog(this, dlgTitle, dlgLabel, &emptyList);
+ //dlg->setGrouping(BTModuleTreeItem::Mod);
+ QTreeWidget* treeWidget = dlg->treeWidget();
+ QTreeWidgetItem* rootItem = treeWidget->invisibleRootItem();
+
+ QStringList nameList;
+
+ // loop through each tab
+ for (int tab = 0; tab < count(); ++tab) {
+ BtSourceArea* sArea = dynamic_cast<BtSourceArea*>(widget(tab));
+ if (sArea && sArea->selectedModules()->count() > 0) {
+ // there are selected modules in the source, create items for these
+ QTreeWidgetItem* sourceItem = new QTreeWidgetItem(rootItem);
+ sourceItem->setText(0, m_sourceNameList.at(tab));
+ sourceItem->setFlags(Qt::ItemIsUserCheckable|Qt::ItemIsTristate|Qt::ItemIsEnabled);
+ foreach (QString mName, sArea->selectedModules()->keys()) {
+ dlg->initModuleItem(mName, sourceItem);
+ }
+ sourceItem->setExpanded(true);
+ }
+ }
+
+ //user accepts the dialog
+ connect(dlg, SIGNAL(modulesChanged(QList<CSwordModuleInfo*>, QTreeWidget*)), SLOT(slotInstallAccepted(QList<CSwordModuleInfo*>, QTreeWidget*)) );
+ // user checks/unchecks an item, needed for preventing double items
+ QObject::connect(treeWidget, SIGNAL(itemChanged(QTreeWidgetItem*, int)), dlg, SLOT(slotItemChecked(QTreeWidgetItem*, int)));
+ dlg->exec();
+ // The OK signal sent by the dialog is catched with slotInstallAccepted.
+}
+
+void BtSourceWidget::slotStopInstall(QTreeWidget* /*treeWidget*/)
+{
+ qDebug() << "BtSourceWidget::slotStopInstall";
+ // not needed?
+}
+
+void BtSourceWidget::slotInstallAccepted(QList<CSwordModuleInfo*> /*modules*/, QTreeWidget* treeWidget)
+{
+ qDebug() << "BtSourceWidget::slotInstallAccepted";
+
+ //TODO: first remove all modules which will be updated from the module list
+ // but what modules? all with the same real name? (there may be _n modules...)
+
+ BtModuleManagerDialog* parentDialog = dynamic_cast<BtModuleManagerDialog*>(dynamic_cast<BtInstallPage*>(parent())->parentDialog());
+
+ BtInstallProgressDialog* dlg = new BtInstallProgressDialog(parentDialog, treeWidget, dynamic_cast<BtInstallPage*>(parent())->selectedInstallPath());
+
+ if (!parentDialog) qDebug("error, wrong parent!");
+
+ m_page->setInstallEnabled(false);
+ // the progress dialog is now modal, it can be made modeless later.
+ dlg->exec();
+
+ qDebug("BtSourceWidget::slotInstallAccepted end");
+}
diff --git a/src/frontend/bookshelfmanager/installpage/btsourcewidget.h b/src/frontend/bookshelfmanager/installpage/btsourcewidget.h
new file mode 100644
index 0000000..812c6ac
--- /dev/null
+++ b/src/frontend/bookshelfmanager/installpage/btsourcewidget.h
@@ -0,0 +1,86 @@
+/*********
+*
+* This file is part of BibleTime's source code, http://www.bibletime.info/.
+*
+* Copyright 1999-2008 by the BibleTime developers.
+* The BibleTime source code is licensed under the GNU General Public License version 2.0.
+*
+**********/
+#ifndef BTSOURCEWIDGET_H
+#define BTSOURCEWIDGET_H
+
+class CSwordModuleInfo;
+
+#include <QTabWidget>
+#include <QString>
+#include <QMap>
+
+class BtInstallMgr;
+class BtInstallPage;
+class BtSourceArea;
+
+class QProgressDialog;
+class QTreeWidget;
+
+/**
+* Tabwidget which holds the source widgets.
+* This widget implements the slots for the source action buttons and
+* applies the actions to the proper source(s).
+*/
+class BtSourceWidget : public QTabWidget
+{
+ Q_OBJECT
+public:
+ friend class BtInstallPage;
+
+ BtSourceWidget(BtInstallPage* parent);
+ virtual ~BtSourceWidget() {}
+
+ BtSourceArea* area();
+ QString currentSourceName();
+
+public slots:
+ /** Install button has been clicked. */
+ void slotInstall();
+ /** "Stop All" button clicked */
+ void slotStopInstall(QTreeWidget* treeWidget);
+
+private:
+ void initSourceConnections();
+ /** Add tabs/views for each source. */
+ void initSources();
+
+ /** Add one source to tabs/stack. */
+ void addSource(const QString& sourceName);
+
+private slots:
+
+ void slotRefresh();
+
+ void slotRefreshCanceled();
+
+ void slotRefreshCompleted(int, int);
+
+ /** Edit button clicked. */
+ void slotEdit();
+ /** Delete button clicked. */
+ void slotDelete();
+ /** Add button clicked. */
+ void slotAdd();
+ /** Modules have been checked/unchecked in the view. */
+ void slotModuleSelectionChanged(QString sourceName, int selectedCount);
+
+ void slotTabSelected(int index);
+ void slotInstallAccepted(QList<CSwordModuleInfo*> mi, QTreeWidget* treeWidget);
+
+
+
+private:
+ QStringList m_sourceNameList;
+ BtInstallPage* m_page;
+ QProgressDialog* m_progressDialog; // for refreshing
+ BtInstallMgr* m_currentInstallMgr; // for refreshing
+ QMap<QString, int> m_selectedModulesCountMap;
+};
+
+#endif