summaryrefslogtreecommitdiff
path: root/src/frontend/bookshelfmanager/installpage/btsourcearea.cpp
blob: 45bfe0f3422fa8e684755d6353a2fae1f514f3da (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
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)); /// \bug Possible color conflict
				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();
}