1 | /**************************************************************************** |
2 | ** |
3 | ** Copyright (C) 2008-2010 Ralf Habacker <ralf.habacker@freenet.de> |
4 | ** Copyright (C) 2010 Patrick Spendrin <ps_ml@gmx.de> |
5 | ** All rights reserved. |
6 | ** |
7 | ** This file is part of the KDE installer for windows |
8 | ** |
9 | ** This file may be used under the terms of the GNU General Public |
10 | ** License version 2.0 as published by the Free Software Foundation |
11 | ** and appearing in the file LICENSE.GPL included in the packaging of |
12 | ** this file. Please review the following information to ensure GNU |
13 | ** General Public Licensing requirements will be met: |
14 | ** http://www.trolltech.com/products/qt/opensource.html |
15 | ** |
16 | ** If you are unsure which license is appropriate for your use, please |
17 | ** review the following information: |
18 | ** http://www.trolltech.com/products/qt/licensing.html or contact the |
19 | ** sales department at sales@trolltech.com. |
20 | ** |
21 | ** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE |
22 | ** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. |
23 | ** |
24 | ****************************************************************************/ |
25 | |
26 | #include "config.h" |
27 | #include "debug.h" |
28 | #include "downloader.h" |
29 | #include "installer.h" |
30 | #include "installerprogress.h" |
31 | #include "installerenginegui.h" |
32 | #include "installerdialogs.h" |
33 | #include "package.h" |
34 | #include "packageinfo.h" |
35 | #include "packagelist.h" |
36 | #include "mirrors.h" |
37 | #include "settings.h" |
38 | #include "uninstaller.h" |
39 | #include "unpacker.h" |
40 | #include "enduserpackageselectorpage.h" |
41 | #include "packagecategorycache.h" |
42 | |
43 | #include <QVector> |
44 | #include <QListWidget> |
45 | #include <QSplitter> |
46 | #include <QTextEdit> |
47 | #include <QTreeWidget> |
48 | #include <QRegExp> |
49 | |
50 | extern InstallerEngineGui *engine; |
51 | typedef enum { C_NAME, C_ACTION, C_AVAILABLE, C_INSTALLED, C_NOTES, /* always leave this item at the end */ C_COLUMNCOUNT } columnvalue; |
52 | |
53 | EndUserPackageSelectorPage::EndUserPackageSelectorPage() : InstallWizardPage(0) |
54 | { |
55 | ui.setupUi(this); |
56 | setTitle(windowTitle()); |
57 | if (0/* update*/) |
58 | setSubTitle(tr("This page shows all available updates for installed package and further available packages. " |
59 | "You may click into the action column to exclude a package from the update, to installl further packages " |
60 | "or to remove installed packages. If you are ready, press Next to start the update." ) |
61 | ); |
62 | else |
63 | setSubTitle(statusTip()); |
64 | } |
65 | |
66 | bool EndUserPackageSelectorPage::includePackage(const QString &name, PackageDisplayType displayType) |
67 | { |
68 | if (displayType == Language && !name.contains("-l10n" ) ) |
69 | return false; |
70 | else if (displayType == Spelling && !name.contains("aspell" )) |
71 | return false; |
72 | else if (displayType == Application |
73 | && ( name.contains("aspell" ) |
74 | || name.contains("-l10n" ) |
75 | || name.contains("lib" ) |
76 | || name.contains("runtime" ) |
77 | ) |
78 | ) |
79 | return false; |
80 | else |
81 | return true; |
82 | } |
83 | |
84 | void EndUserPackageSelectorPage::setWidgetData() |
85 | { |
86 | QTreeWidget *tree = ui.packageList; |
87 | tree->clear(); |
88 | QVector<QString> labels; |
89 | QList<QTreeWidgetItem *> items; |
90 | QString toolTip = "select this checkbox to install or update this package" ; |
91 | |
92 | labels.resize( C_COLUMNCOUNT ); |
93 | labels[C_NAME] = tr ( "Package" ); |
94 | labels[C_ACTION] = tr ( "Action" ); |
95 | labels[C_AVAILABLE] = tr ( "Available" ); |
96 | labels[C_INSTALLED] = tr ( "Installed" ); |
97 | labels[C_NOTES] = tr ( "Package notes" ); |
98 | |
99 | tree->setColumnCount ( C_COLUMNCOUNT ); |
100 | tree->setHeaderLabels ( QList<QString>::fromVector( labels ) ); |
101 | |
102 | tree->setIndentation(10); |
103 | |
104 | // see http://lists.trolltech.com/qt-interest/2006-06/thread00441-0.html |
105 | // and Task Tracker Entry 106731 |
106 | //tree->setAlignment(Center); |
107 | |
108 | // adding top level items |
109 | QList<QTreeWidgetItem *> categoryList; |
110 | QHash<QString, Package*> packageList; |
111 | |
112 | QStringList selectedCategories; |
113 | Q_FOREACH(QString category, categoryCache.categories()) |
114 | { |
115 | QStringList a = category.split(":" ); |
116 | if (activeCategories.contains(a[0])) |
117 | selectedCategories << a[0]; |
118 | } |
119 | |
120 | Q_FOREACH(QString categoryName, selectedCategories) |
121 | { |
122 | // add packages which are installed but for which no config entry is there |
123 | Q_FOREACH(Package *instPackage, categoryCache.packages(categoryName,*engine->database())) |
124 | { |
125 | QString name = instPackage->name(); |
126 | Package *p = engine->packageResources()->getPackage(name); |
127 | |
128 | if (!p) { |
129 | packageList[PackageInfo::baseName(name)] = instPackage; |
130 | } |
131 | } |
132 | } |
133 | |
134 | Settings &s = Settings::instance(); |
135 | Q_FOREACH(QString categoryName, selectedCategories) |
136 | { |
137 | Q_FOREACH(Package *availablePackage,categoryCache.packages(categoryName,*engine->packageResources())) |
138 | { |
139 | QString name = availablePackage->name(); |
140 | if (engine->includePackage(s.compilerType(),name,categoryName)) |
141 | packageList[PackageInfo::baseName(name)] = availablePackage; |
142 | } |
143 | } |
144 | |
145 | // go through all metaPackages now |
146 | Q_FOREACH(QString metaPackage, engine->globalConfig()->metaPackages().keys()) |
147 | { |
148 | Package *p = engine->packageResources()->find(metaPackage); |
149 | if(!p) p = packageList[PackageInfo::baseName(metaPackage)]; |
150 | |
151 | // in case p is 0, we couldn't find a predefined package - e.g. another package has been defined before |
152 | if(!p || p->hasType(FileTypes::BIN) || p->hasType(FileTypes::LIB)) continue; |
153 | |
154 | if (!includePackage(p->name(),m_displayType)) |
155 | continue; |
156 | |
157 | QTreeWidgetItem *item = addPackageToTree(p, 0); |
158 | |
159 | // if this is no metaPackage, simply ignore it |
160 | if(p->hasType(FileTypes::META)) { |
161 | int packageCount = 0; |
162 | |
163 | // now check whether we have packages for this metaPackage in our packageList and add them as child elements |
164 | Q_FOREACH(QString name, engine->globalConfig()->metaPackages()[metaPackage]) |
165 | { |
166 | // if this package is contained in the packageList and inside our metaPackage, we want to make it a child item |
167 | // of this QTreeWidgetItem |
168 | if(packageList.keys().contains(name)) { |
169 | if(addPackageToTree(packageList[name], item) != 0) ++packageCount; |
170 | packageList.remove( name ); |
171 | } |
172 | } |
173 | |
174 | // in case the packageCount is 0 (e.g. there are no childItems available for this metaPackage) simply delete the |
175 | // item again which we got from addPackageToTree |
176 | if(packageCount > 0) { |
177 | categoryList.append(item); |
178 | engine->setMetaPackageState(*item, C_ACTION); |
179 | } else { |
180 | delete item; |
181 | } |
182 | } |
183 | } |
184 | |
185 | Q_FOREACH(Package *availablePackage,packageList.values()) |
186 | { |
187 | if (!includePackage(availablePackage->name(),m_displayType)) |
188 | continue; |
189 | if(availablePackage->hasType(FileTypes::BIN)) { |
190 | QTreeWidgetItem *item = addPackageToTree(availablePackage, 0); |
191 | if (item) |
192 | categoryList.append(item); |
193 | } |
194 | } |
195 | tree->addTopLevelItems ( categoryList ); |
196 | tree->sortItems ( C_NAME, Qt::AscendingOrder ); |
197 | for ( int i = 0; i < tree->columnCount(); i++ ) |
198 | tree->resizeColumnToContents ( i ); |
199 | } |
200 | |
201 | QTreeWidgetItem *EndUserPackageSelectorPage::addPackageToTree(Package *availablePackage, QTreeWidgetItem *parent) |
202 | { |
203 | QVector<QString> data; |
204 | QString toolTip = "select this checkbox to install or update this package" ; |
205 | |
206 | Package *installedPackage = engine->database()->getPackage(availablePackage->name()); |
207 | Package::PackageVersion installedVersion = installedPackage ? installedPackage->installedVersion() : Package::PackageVersion(); |
208 | Package::PackageVersion availableVersion = availablePackage->version(); |
209 | availablePackage->setInstalledVersion(installedVersion); |
210 | |
211 | if (installedPackage && availableVersion == installedVersion) |
212 | return 0; |
213 | data.resize( C_COLUMNCOUNT ); |
214 | data[C_NAME] = PackageInfo::baseName(availablePackage->name()); |
215 | data[C_AVAILABLE] = (availableVersion != installedVersion ? availableVersion.toString() : "" ); |
216 | data[C_INSTALLED] = installedVersion.toString(); |
217 | QTreeWidgetItem *item = new QTreeWidgetItem ( parent, QList<QString>::fromVector( data ) ); |
218 | // save real package name for selection code |
219 | item->setData(C_ACTION, Qt::StatusTipRole, availablePackage->name()); |
220 | |
221 | // in case the package is a meta package, we need to check whether the subpackages are installed -> fix that after this function |
222 | engine->setEndUserInitialState(*item, availablePackage, installedPackage, C_ACTION); |
223 | item->setText(C_NOTES, availablePackage->notes()); |
224 | item->setToolTip(C_NAME, toolTip); |
225 | |
226 | return item; |
227 | } |
228 | |
229 | void EndUserPackageSelectorPage::initializePage() |
230 | { |
231 | Settings::instance().setFirstRun(false); |
232 | Settings::instance().setSkipBasicSettings(true); |
233 | setSettingsButtonVisible(true); |
234 | InstallerDialogs::instance().downloadProgressDialog(this,true,tr("Downloading Package Lists" )); |
235 | engine->init(); |
236 | InstallerDialogs::instance().downloadProgressDialog(this,false); |
237 | |
238 | // disabled for now, because it will not install debug packages for already selected packages, need to be fixed |
239 | ui.installDebugPackagesCheckBox->setVisible(false); |
240 | ui.installDebugPackagesCheckBox->setEnabled(engine->database()->size() == 0); |
241 | ui.installDebugPackagesCheckBox->setChecked(Settings::instance().installDebugPackages()); |
242 | engine->setInstallDebugPackages(Settings::instance().installDebugPackages()); |
243 | connect(ui.installDebugPackagesCheckBox,SIGNAL(clicked()),this,SLOT(slotInstallDebugPackages())); |
244 | |
245 | connect(ui.packageList,SIGNAL(itemClicked(QTreeWidgetItem *, int)),this,SLOT(itemClicked(QTreeWidgetItem *, int))); |
246 | connect(ui.selectAllCheckBox,SIGNAL(clicked()),this,SLOT(selectAllClicked())); |
247 | |
248 | connect(ui.applicationPackageButton,SIGNAL(clicked()),this,SLOT(slotApplicationPackageButton())); |
249 | connect(ui.languagePackageButton,SIGNAL(clicked()),this,SLOT(slotLanguagePackageButton())); |
250 | connect(ui.spellingPackageButton,SIGNAL(clicked()),this,SLOT(slotSpellingPackageButton())); |
251 | |
252 | connect(ui.filterEdit,SIGNAL(textChanged(const QString &)),this,SLOT(slotFilterTextChanged(const QString &))); |
253 | |
254 | activeCategories = engine->globalConfig()->endUserCategories().size() > 0 ? engine->globalConfig()->endUserCategories() : QStringList() << "KDE" ; |
255 | setPackageDisplayType(Application); |
256 | // @TODO remove |
257 | if (ui.packageList->topLevelItemCount() == 0) { |
258 | // no items skip page |
259 | } |
260 | } |
261 | |
262 | void EndUserPackageSelectorPage::preSelectPackages(const QString &package) |
263 | { |
264 | QTreeWidget *tree = ui.packageList; |
265 | QString systemCode = QLocale::system().name(); |
266 | QStringList b = systemCode.split('_'); |
267 | QString languageCode = b[0]; |
268 | QStringList searchCodes = QStringList() << systemCode << languageCode; |
269 | QString pattern = package + "-%1" ; |
270 | |
271 | bool found = false; |
272 | foreach(QString code, searchCodes) |
273 | { |
274 | QList<QTreeWidgetItem *> list = tree->findItems (QString(pattern).arg(code), Qt::MatchContains, C_NAME ); |
275 | foreach(QTreeWidgetItem *item, list) |
276 | { |
277 | QString name = item->data(C_ACTION, Qt::StatusTipRole).toString(); |
278 | QString availableVersion = item->text ( C_AVAILABLE ); |
279 | Package *availablePackage = engine->getPackageByName ( name, availableVersion ); |
280 | if (!engine->isPackageSelected(availablePackage,FileTypes::BIN)) |
281 | { |
282 | QString installedVersion = item->text ( C_INSTALLED ); |
283 | Package *installedPackage = engine->database()->getPackage( name,installedVersion.toLatin1() ); |
284 | engine->setNextState(*item, availablePackage, installedPackage, FileTypes::BIN, C_ACTION ); |
285 | qDebug() << "found" << QString(pattern).arg(code); |
286 | } |
287 | found = true; |
288 | } |
289 | if (found) |
290 | break; |
291 | } |
292 | } |
293 | |
294 | void EndUserPackageSelectorPage::setPackageDisplayType(PackageDisplayType type) |
295 | { |
296 | m_displayType = type; |
297 | setWidgetData(); |
298 | if (type == Application) |
299 | { |
300 | ui.applicationPackageButton->setEnabled(false); |
301 | ui.languagePackageButton->setEnabled(true); |
302 | ui.spellingPackageButton->setEnabled(true); |
303 | ui.selectAllCheckBox->setEnabled(true); |
304 | } |
305 | else if (type == Language) |
306 | { |
307 | ui.applicationPackageButton->setEnabled(true); |
308 | ui.languagePackageButton->setEnabled(false); |
309 | ui.spellingPackageButton->setEnabled(true); |
310 | ui.selectAllCheckBox->setEnabled(false); |
311 | if (!engine->isAnyPackageInstalled()) |
312 | preSelectPackages("kde-l10n" ); |
313 | } |
314 | else if (type == Spelling) |
315 | { |
316 | ui.applicationPackageButton->setEnabled(true); |
317 | ui.languagePackageButton->setEnabled(true); |
318 | ui.spellingPackageButton->setEnabled(false); |
319 | ui.selectAllCheckBox->setEnabled(false); |
320 | if (!engine->isAnyPackageInstalled()) |
321 | preSelectPackages("aspell" ); |
322 | } |
323 | } |
324 | |
325 | void EndUserPackageSelectorPage::itemClicked(QTreeWidgetItem *item, int column) |
326 | { |
327 | QString name = item->data( C_ACTION, Qt::StatusTipRole ).toString(); |
328 | QString installedVersion = item->text ( C_INSTALLED ); |
329 | QString availableVersion = item->text ( C_AVAILABLE ); |
330 | |
331 | Package *installedPackage = engine->database()->getPackage( name,installedVersion.toLatin1() ); |
332 | Package *availablePackage = engine->getPackageByName( name, availableVersion ); |
333 | |
334 | if ( !availablePackage && !installedPackage ) { |
335 | qWarning() << __FUNCTION__ << "neither available or installed package present for package" << name; |
336 | return; |
337 | } |
338 | |
339 | if ( engine->globalConfig()->metaPackages().keys().contains( PackageInfo::baseName(name) ) ) { |
340 | |
341 | // this is a metaPackage which has no representation in the database |
342 | // now go through all subpackages |
343 | Q_FOREACH(QString package, engine->globalConfig()->metaPackages()[PackageInfo::baseName(name)]) |
344 | { |
345 | int i = 0; |
346 | QString packageName; |
347 | |
348 | // try to find the childItems which are connected to this metaPackage |
349 | for(; i < item->childCount(); ++i) |
350 | { |
351 | packageName = item->child(i)->data(C_ACTION, Qt::StatusTipRole).toString(); |
352 | if(package == PackageInfo::baseName(item->child(i)->data(C_ACTION, Qt::StatusTipRole).toString())) |
353 | break; |
354 | } |
355 | if(i == item->childCount()) continue; |
356 | |
357 | QString _installedVersion = item->child(i)->text ( C_INSTALLED ); |
358 | QString _availableVersion = item->child(i)->text ( C_AVAILABLE ); |
359 | |
360 | Package *ip = engine->database()->getPackage( packageName, _installedVersion.toLatin1() ); |
361 | Package *ap = engine->getPackageByName( packageName, _availableVersion ); |
362 | |
363 | if(!ap && !ip) continue; |
364 | |
365 | // switch state for the childItem |
366 | if ( column == C_ACTION) engine->setNextState(*item->child(i), ap, ip, FileTypes::BIN, C_ACTION, true); |
367 | } |
368 | } |
369 | |
370 | if ( column == C_ACTION ) |
371 | { |
372 | engine->setNextState(*item, availablePackage, installedPackage, FileTypes::BIN, C_ACTION ); |
373 | if(availablePackage->hasType(FileTypes::META)) |
374 | engine->setMetaPackageState( *item, C_ACTION ); |
375 | |
376 | // now check that if we're no top level item, we need to handle also the category icon |
377 | if (ui.packageList->indexOfTopLevelItem(item) == -1) { |
378 | engine->setMetaPackageState( *item->parent(), C_ACTION ); |
379 | } |
380 | } |
381 | // dependencies are selected later |
382 | } |
383 | |
384 | void EndUserPackageSelectorPage::selectAllClicked() |
385 | { |
386 | QTreeWidget *tree = ui.packageList; |
387 | int count = tree->topLevelItemCount(); |
388 | for(int i = 0; i < count; i++) |
389 | { |
390 | QTreeWidgetItem *item = tree->topLevelItem(i); |
391 | QString name = item->data(C_ACTION, Qt::StatusTipRole).toString(); |
392 | if (name.startsWith("aspell-" ) || name.startsWith("kde-l10n" )) |
393 | continue; |
394 | itemClicked(item, C_ACTION); |
395 | } |
396 | } |
397 | |
398 | void EndUserPackageSelectorPage::slotApplicationPackageButton() |
399 | { |
400 | setPackageDisplayType(Application); |
401 | } |
402 | |
403 | void EndUserPackageSelectorPage::slotLanguagePackageButton() |
404 | { |
405 | setPackageDisplayType(Language); |
406 | } |
407 | |
408 | void EndUserPackageSelectorPage::slotSpellingPackageButton() |
409 | { |
410 | setPackageDisplayType(Spelling); |
411 | } |
412 | |
413 | void EndUserPackageSelectorPage::slotFilterTextChanged(const QString &text) |
414 | { |
415 | QTreeWidget *tree = ui.packageList; |
416 | if (text.isEmpty()) |
417 | { |
418 | for(int i = 0; i < tree->topLevelItemCount(); i++) |
419 | { |
420 | QTreeWidgetItem *item = tree->topLevelItem (i); |
421 | item->setHidden(false); |
422 | for(int j = 0; j < item->childCount(); j++) |
423 | { |
424 | item->child(j)->setHidden(false); |
425 | } |
426 | } |
427 | return; |
428 | } |
429 | |
430 | QList<QTreeWidgetItem *> list = tree->findItems (text, Qt::MatchContains|Qt::MatchRecursive, C_NAME ); |
431 | Q_FOREACH(QTreeWidgetItem *item, tree->findItems (text, Qt::MatchContains|Qt::MatchRecursive, C_NOTES )) |
432 | { |
433 | if (!list.contains(item)) |
434 | list.append(item); |
435 | } |
436 | |
437 | Q_FOREACH(QTreeWidgetItem *item, list) |
438 | { |
439 | QTreeWidgetItem *parent = item->parent(); |
440 | if (!list.contains(parent) && parent != tree->invisibleRootItem()) |
441 | list.append(parent); |
442 | } |
443 | |
444 | for(int i = 0; i < tree->topLevelItemCount(); i++) |
445 | { |
446 | QTreeWidgetItem *item = tree->topLevelItem (i); |
447 | item->setHidden(!list.contains(item)); |
448 | for(int j = 0; j < item->childCount(); j++) |
449 | { |
450 | QTreeWidgetItem *child = item->child(j); |
451 | child->setHidden(!list.contains(child)); |
452 | } |
453 | } |
454 | } |
455 | |
456 | void EndUserPackageSelectorPage::slotInstallDebugPackages() |
457 | { |
458 | Settings::instance().setInstallDebugPackages(ui.installDebugPackagesCheckBox->isChecked()); |
459 | engine->setInstallDebugPackages(ui.installDebugPackagesCheckBox->isChecked()); |
460 | } |
461 | |
462 | bool EndUserPackageSelectorPage::validatePage() |
463 | { |
464 | return true; |
465 | } |
466 | |
467 | void EndUserPackageSelectorPage::cleanupPage() |
468 | { |
469 | disconnect(ui.packageList,SIGNAL(itemClicked(QTreeWidgetItem *, int)),this,SLOT(itemClicked(QTreeWidgetItem *, int))); |
470 | disconnect(ui.selectAllCheckBox,SIGNAL(clicked()),this,SLOT(selectAllClicked())); |
471 | |
472 | disconnect(ui.filterEdit,SIGNAL(textChanged(const QString &)),this,SLOT(slotFilterTextChanged(const QString &))); |
473 | |
474 | disconnect(ui.applicationPackageButton,SIGNAL(clicked()),this,SLOT(slotApplicationPackageButton())); |
475 | disconnect(ui.languagePackageButton,SIGNAL(clicked()),this,SLOT(slotLanguagePackageButton())); |
476 | disconnect(ui.spellingPackageButton,SIGNAL(clicked()),this,SLOT(slotSpellingPackageButton())); |
477 | |
478 | engine->unselectAllPackages(); |
479 | } |
480 | |
481 | bool EndUserPackageSelectorPage::isComplete() |
482 | { |
483 | return true; |
484 | } |
485 | |