0.7.1
[fapman] / packageview.cpp
1 /*
2     This file is part of Faster Application Manager.
3
4     Faster Application Manager is free software: you can redistribute it and/or modify
5     it under the terms of the GNU General Public License as published by
6     the Free Software Foundation, either version 3 of the License, or
7     (at your option) any later version.
8
9     Faster Application Manager is distributed in the hope that it will be useful,
10     but WITHOUT ANY WARRANTY; without even the implied warranty of
11     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12     GNU General Public License for more details.
13
14     You should have received a copy of the GNU General Public License
15     along with Faster Application Manager.  If not, see <http://www.gnu.org/licenses/>.
16
17     (C) Heikki Holstila 2010
18 */
19
20 #include "packageview.h"
21 #include "ui_packageview.h"
22 #include "package.h"
23 #include "filterselect.h"
24 #include "confirmdialog.h"
25 #include "dimmer.h"
26 #include "packageselector.h"
27 #include "help.h"
28 #include "aaptinterface.h"
29 #include "logview.h"
30 #include "sortselector.h"
31 #include "settings.h"
32 #include "searchoptions.h"
33 #include "blacklistselect.h"
34
35 PackageListWidgetItem::PackageListWidgetItem(Package* p_, QString name_) : QListWidgetItem(name_)
36 {
37     iPackage = p_;
38 }
39
40
41 void ListItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
42 {
43     //QTime t;
44     //t.start();
45
46     QString name = index.data(UserRoleName).toString();
47     QString version = index.data(UserRoleVersion).toString();
48     QString desc = index.data(UserRoleDescShort).toString();
49     Package::operation marked = (Package::operation) index.data(UserRoleMarked).toInt();
50     bool installed = index.data(UserRoleInstalled).toBool();
51     bool upgradeable = index.data(UserRoleUpgradeable).toBool();
52     int statfilter = index.data(UserRoleCurrentStatFilter).toInt();
53     int catfilter = index.data(UserRoleCurrentCatFilter).toInt();
54     QString upg_version = index.data(UserRoleAvailVersion).toString();
55     BlacklistSelect::blackList blacklisted = static_cast<BlacklistSelect::blackList>( index.data(UserRoleBlacklisted).toInt() );
56
57     painter->save();
58     QRect r = option.rect;
59
60     /*
61      // does nothing at the moment because items aren't actually "selected" on the list in the qt sense...
62     if(option.state & QStyle::State_Selected)
63     {
64         r = option.rect;
65 #ifdef Q_WS_MAEMO_5
66         painter->drawImage(r, QImage("/etc/hildon/theme/images/TouchListBackgroundPressed.png"));
67 #else
68         painter->fillRect(r, option.palette.highlight().color());
69 #endif
70     }
71     */
72
73     QPixmap icon = index.data(Qt::DecorationRole).value<QPixmap>();
74     if( icon.isNull() ) {
75         // use default icon
76         painter->drawPixmap( r.left()+6, r.top()+10, 48, 48, iDefaultIcon );
77     } else {
78         painter->drawPixmap( r.left()+6, r.top()+10, 48, 48, icon );
79     }
80
81     QPixmap statusicon;
82     if( marked == Package::PkgOpNone )
83     {
84         if( installed && upgradeable )
85             statusicon = iIconPkgNoOpInstalledUpgradeable;
86         else if( installed )
87             statusicon = iIconPkgNoOpInstalled;
88         else if( !installed )
89             statusicon = iIconPkgNoOpNotInstalled;
90     } else if( marked == Package::PkgOpInstallUpgrade ) {
91         if( upgradeable )
92             statusicon = iIconPkgUpgrade;
93         else
94             statusicon = iIconPkgInstall;
95     } else if( marked == Package::PkgOpRemove ) {
96         statusicon = iIconPkgRemove;
97     }
98
99     QString showVer;
100     if( upgradeable && (statfilter==Package::PkgStatUpgradeable ||
101                         (statfilter==Package::PkgStatUnknown && marked==Package::PkgOpInstallUpgrade) ||
102                         (catfilter==PackageView::CatFilterAllMarked && marked==Package::PkgOpInstallUpgrade) ))
103     {
104         showVer = upg_version;
105     } else {
106         showVer = version;
107     }
108
109     int ver_w = 0;
110     if( QApplication::desktop()->width() > QApplication::desktop()->height() )
111     {
112         r = option.rect;
113         r.setTop( r.top()+6 );
114         r.setRight( r.right()-statusicon.width()-6 );
115
116         if( catfilter != PackageView::CatFilterBlacklisted ) {
117             painter->drawText(r, Qt::AlignTop|Qt::AlignRight, showVer, &r);
118             ver_w = r.width();
119         } else if( blacklisted==BlacklistSelect::BlacklistThis ) {
120             if( upgradeable && !upg_version.isEmpty() ) {
121                 showVer = upg_version;
122             } else {
123                 showVer = version;
124             }
125             painter->drawText(r, Qt::AlignTop|Qt::AlignRight, showVer, &r);
126             ver_w = r.width();
127         }
128     }
129
130     r = option.rect;
131     r.setRight( r.right()-statusicon.width()-4-ver_w );  //does not work as it should?
132     QFont f = painter->font();
133     painter->setFont(f);
134     painter->drawText(r.left()+iDefaultIcon.width()+2, r.top()+5, r.width(), r.height(), Qt::AlignTop|Qt::AlignLeft, name, &r);
135
136     f.setPointSize( f.pointSize()-4 );
137     painter->setFont(f);
138     r = option.rect;
139     QColor gray;
140     gray = QColor(156, 154, 156);
141     painter->save();
142     f.setPointSize(13);
143     painter->setFont(f);
144     painter->setPen(gray);
145     r.setBottom(r.bottom()-10);
146     painter->drawText(r.left()+iDefaultIcon.width()+2, r.top(), r.width(), r.height(), Qt::AlignBottom|Qt::AlignLeft, desc, &r);
147     painter->restore();
148
149     r = option.rect;
150     painter->drawPixmap(r.right()-statusicon.width()-2, r.top()+10, 24, 24, statusicon);
151
152     painter->restore();
153
154     //if( t.elapsed()>=100 )
155     //qDebug() << name << t.elapsed();
156 }
157
158 void ListItemDelegate::loadIcons()
159 {
160 #ifdef Q_WS_MAEMO_5
161     iDefaultIcon = QPixmap("/usr/share/icons/hicolor/64x64/hildon/tasklaunch_default_application.png");
162 #else
163     iDefaultIcon = QPixmap(":/icons/icons/appdefault.png");
164 #endif
165     iIconPkgInstall = QPixmap(":/icons/icons/pkg_install.png");
166     iIconPkgUpgrade = QPixmap(":/icons/icons/pkg_upgrade.png");
167     iIconPkgRemove = QPixmap(":/icons/icons/pkg_remove.png");
168     iIconPkgNoOpInstalled = QPixmap(":/icons/icons/pkg_nop_installed.png");
169     iIconPkgNoOpNotInstalled = QPixmap(":/icons/icons/pkg_nop_notinstalled.png");
170     iIconPkgNoOpInstalledUpgradeable = QPixmap(":/icons/icons/pkg_nop_instupgr.png");
171 }
172
173
174 QSize ListItemDelegate::sizeHint(const QStyleOptionViewItem&, const QModelIndex&) const
175 {
176     return QSize(400, 70);
177 }
178
179
180 PackageView::PackageView(QWidget *parent) : QMainWindow(parent), ui(new Ui::PackageView)
181 {
182     iMainWindow = dynamic_cast<MainWindow*>(parent);
183     ui->setupUi(this);
184 #ifdef Q_WS_MAEMO_5
185     this->setAttribute(Qt::WA_Maemo5StackedWindow);
186     this->setWindowFlags(Qt::Window);
187     this->setAttribute(Qt::WA_Maemo5AutoOrientation);
188 #endif
189     iSettings = 0;
190     iAptInterface = 0;
191
192     connect(QApplication::desktop(), SIGNAL(resized(int)), this, SLOT(orientationChanged()));
193
194     iCatFilterLabels
195             << tr("All marked packages")        // a special case
196             << tr("All user categories")
197             << tr("\tDesktop")
198             << tr("\tEducation")
199             << tr("\tGames")
200             << tr("\tGraphics")
201             << tr("\tInternet & Networking")
202             << tr("\tLocation & Navigation")
203             << tr("\tMultimedia")
204             << tr("\tOffice")
205             << tr("\tOther")
206             << tr("\tProgramming")
207             << tr("\tScience")
208             << tr("\tSystem")
209             << tr("\tUtilities")
210             << tr("All packages (ADVANCED)")
211             << tr("Blacklisted packages");      // a special case
212
213     iCatFilterStrings
214             << ""
215             << "user/"
216             << "user/desktop"
217             << "user/education"
218             << "user/games"
219             << "user/graphics"
220             << "user/network"
221             << "user/navigation"
222             << "user/multimedia"
223             << "user/office"
224             << "user/other"
225             << "user/development"
226             << "user/science"
227             << "user/system"
228             << "user/utilities"
229             << ""
230             << "";
231
232     iDefaultCatFilter = CatFilterAllUser;
233     iSelectedCatFilter = iDefaultCatFilter;
234
235     iStatFilterLabels
236             << tr("All")
237             << tr("Not installed")
238             << tr("Upgradeable")
239             << tr("Installed");
240
241     iSelectedStatFilter = Package::PkgStatUnknown;
242     iSortOrder = SortAlpha;
243
244     iDimmer = new dimmer(this);
245
246     ui->searchBar->hide();
247
248     iListCoverLabel = new QLabel(ui->listWidget);
249     iListCoverLabel->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
250     iListCoverLabel->setAlignment(Qt::AlignCenter);
251
252     iKeyFilter = new KeyEventGrabber(this);
253     ui->listWidget->installEventFilter(iKeyFilter);
254
255     iSortNoticeShown = false;
256
257     iSearchPkgName = true;
258     iSearchDisplayName = true;
259     iSearchDescShort = true;
260     iSearchDescLong = false;
261
262     // fine-tune kinetic scrolling parameters
263 #ifdef Q_WS_MAEMO_5
264     QAbstractKineticScroller* listscroller = ui->listWidget->property("kineticScroller").value<QAbstractKineticScroller*>();
265     if( listscroller )
266     {
267         //qDebug() << listscroller->dragInertia() << listscroller->decelerationFactor()
268         //              << listscroller->minimumVelocity() << listscroller->maximumVelocity();
269         listscroller->setDecelerationFactor(0.75); // default is 0.85
270         listscroller->setDragInertia(0.60);     // default is 0.85
271         listscroller->setMaximumVelocity(1800); // default is 3500
272
273         // not good because it alse sets horizontal overshoot:
274         //listscroller->setOvershootPolicy( QAbstractKineticScroller::OvershootAlwaysOn );
275     }
276 #endif
277 }
278
279 PackageView::~PackageView()
280 {
281     delete iListCoverLabel; iListCoverLabel=0;
282     delete iKeyFilter; iKeyFilter=0;
283     delete iDimmer; iDimmer=0;
284     delete ui; ui=0;
285 }
286
287 void PackageView::orientationChanged()
288 {
289     ui->listWidget->scroll(1,1);        // this causes all items to be repainted
290     iListCoverLabel->setGeometry( ui->listWidget->rect() );
291 }
292
293 void PackageView::resizeEvent(QResizeEvent* event)
294 {
295     if( iDimmer ) {
296         iDimmer->resize( this->size() );
297     }
298
299     iListCoverLabel->setGeometry( ui->listWidget->rect() );
300     QWidget::resizeEvent(event);
301 }
302
303 bool PackageView::doFilterCategory(Package* pkg)
304 {
305     if( pkg->section()=="user/hidden" && iSelectedStatFilter!=Package::PkgStatInstalled && iSelectedStatFilter!=Package::PkgStatUnknown )
306         return false;
307     if( pkg->section()=="user/hidden" && !pkg->isInstalled() )
308         return false;
309
310     Package* upg_pkg = pkg->availablePackage();
311
312     if( iSelectedStatFilter==Package::PkgStatUpgradeable && iSelectedCatFilter!=CatFilterBlacklisted &&
313         pkg->isUpgradeable() && upg_pkg && upg_pkg->isBlacklisted() )
314         return false;
315
316     if( pkg->isBlacklisted() || (upg_pkg && upg_pkg->isBlacklisted()) )
317     {
318         if( iSelectedCatFilter == CatFilterBlacklisted )
319             return true;
320         if( pkg->isInstalled() && (iSelectedStatFilter==Package::PkgStatInstalled || iSelectedStatFilter==Package::PkgStatUnknown) )
321             return true;
322         if( pkg->isBlacklisted() && iSelectedCatFilter != CatFilterBlacklisted )
323             return false;
324     } else if( !pkg->isBlacklisted() && iSelectedCatFilter == CatFilterBlacklisted )
325         return false;
326
327     if( iSelectedCatFilter==CatFilterAllMarked ) {
328         if( pkg->isMarkedForOperation() )
329             return true;
330         else
331             return false;
332     }
333     if( pkg->section().startsWith( iCatFilterStrings.at(iSelectedCatFilter) ) )
334         return true;
335
336     return false;
337 }
338
339 QString PackageView::generateSortString(Package* pkg)
340 {
341     QString sortstr;
342
343     if( iSortOrder==SortAlpha ) {
344         sortstr = pkg->displayName();
345     }
346     else if( iSortOrder==SortDateDesc ) {
347         Package* upg = 0;
348         if( pkg->isUpgradeable() )
349             upg = pkg->availablePackage();
350
351         if( (!upg && pkg->date().isValid()) || iSelectedStatFilter==Package::PkgStatInstalled )
352             sortstr = pkg->date().toString("yyyy-MM-dd hh:mm:ss");
353         else if( upg && upg->date().isValid() ) {
354             sortstr = upg->date().toString("yyyy-MM-dd hh:mm:ss");
355         } else {
356             sortstr = "";
357             iPackagesEmptySort++;
358         }
359     }
360     else if( iSortOrder==SortSizeDesc ) {
361         if( pkg->isInstalled() )
362             sortstr = QString("%1").arg(pkg->installedSize()*1024, 12 );
363         else
364             sortstr = QString("%1").arg(pkg->size(), 12 );
365     }
366
367     //qDebug() << sortstr;
368
369     return sortstr;
370 }
371
372 void PackageView::openWin()
373 {
374     ui->listWidget->clear();
375     ui->listWidget->setSortingEnabled(true);
376     iPackagesEmptySort = 0;
377
378     if( !isVisible() ) {
379         iMainWindow->busyDialog(false);
380         iListCoverLabel->setText("Loading...");
381         iListCoverLabel->setAutoFillBackground(true);
382         iListCoverLabel->show();
383         show();
384         QApplication::processEvents();
385     }
386
387     if( iSortOrder==SortDateDesc || iSortOrder==SortSizeDesc )
388         ui->listWidget->sortItems(Qt::DescendingOrder);
389     else
390         ui->listWidget->sortItems(Qt::AscendingOrder);
391
392     delete ui->listWidget->itemDelegate();
393     ListItemDelegate* delegate = new ListItemDelegate(ui->listWidget);
394     delegate->loadIcons();
395     ui->listWidget->setItemDelegate( delegate );
396
397     if( !ui->searchBar->isVisible() )
398     {
399         if( iSelectedStatFilter == Package::PkgStatNotInstalled || iSelectedStatFilter == Package::PkgStatUnknown ||
400             iSelectedCatFilter == CatFilterAllMarked || iSelectedCatFilter == CatFilterBlacklisted )
401         {
402             QHashIterator<QString, Package*> i( *iAptInterface->packagesAvailable() );
403             while (i.hasNext())
404             {
405                 i.next();
406                 Package* inst = iAptInterface->packagesInstalled()->value(i.value()->name(),0);
407                 if( doFilterCategory(i.value()) && !inst )
408                     addListItem(i.value(), generateSortString(i.value()));
409             }
410         }
411         if( iSelectedStatFilter == Package::PkgStatInstalled || iSelectedStatFilter == Package::PkgStatUpgradeable ||
412             iSelectedStatFilter == Package::PkgStatUnknown || iSelectedCatFilter == CatFilterAllMarked ||
413             iSelectedCatFilter == CatFilterBlacklisted )
414         {
415             QHashIterator<QString, Package*> i( *iAptInterface->packagesInstalled() );
416             while (i.hasNext())
417             {
418                 i.next();
419                 if( iSelectedStatFilter == Package::PkgStatUpgradeable && iSelectedCatFilter != CatFilterBlacklisted ) {
420                     if( i.value()->isUpgradeable() && doFilterCategory(i.value()) )
421                         addListItem(i.value(), generateSortString(i.value()));
422                 } else {
423                     if( doFilterCategory(i.value()) )
424                         addListItem(i.value(), generateSortString(i.value()));
425                 }
426             }
427         }
428     } else {
429         for( int j=0; j<iSearchResults.count(); j++ )
430         {
431             addListItem(iSearchResults.at(j), generateSortString( iSearchResults.at(j) ));
432         }
433     }
434
435     iListCoverLabel->hide();
436     iListCoverLabel->setAutoFillBackground(false);
437
438     updateLabel();
439
440     if( iSelectedStatFilter==Package::PkgStatUpgradeable && ui->listWidget->count()>0 &&
441         iSelectedCatFilter != CatFilterAllMarked && iSelectedCatFilter != CatFilterBlacklisted )
442     {
443         ui->actionUpgrade_all->setVisible(true);
444     } else {
445         ui->actionUpgrade_all->setVisible(false);
446     }
447
448     if( iSelectedCatFilter == CatFilterBlacklisted && ui->listWidget->count()>0 ) {
449         ui->actionRestore_all->setVisible(true);
450     } else {
451         ui->actionRestore_all->setVisible(false);
452     }
453
454     if( iSelectedStatFilter==Package::PkgStatInstalled ) {
455         ui->actionStore_list->setVisible(true);
456     } else {
457         ui->actionStore_list->setVisible(false);
458     }
459
460     show();
461
462     if( !ui->searchBar->isVisible() ) {
463         ui->listWidget->setFocusPolicy(Qt::StrongFocus);
464         ui->listWidget->setFocus();
465     } else {
466         ui->listWidget->setFocusPolicy(Qt::NoFocus);
467         ui->lineEdit->setFocus();
468     }
469
470     if( ui->listWidget->count() == 0 )
471     {
472         iListCoverLabel->setGeometry( ui->listWidget->rect() );
473         iListCoverLabel->setText("No packages");
474         iListCoverLabel->show();
475     }
476
477     if( ui->listWidget->count()>1 && iPackagesEmptySort == ui->listWidget->count() && !iSortNoticeShown ) {
478         ConfirmDialog d(false, this);
479         QString msg = "No shown packages currently have the required information for sorting by this criterion. The list is unsorted.";
480         if( iSortOrder == SortDateDesc && !iSettings->qsettings()->value("fetch_dates",false).toBool() )
481             msg += " You can enable date fetching in the options menu.";
482         d.setText("Notice", msg);
483         d.exec();
484         iSortNoticeShown = true;
485     }
486 }
487
488 void PackageView::enableMenu()
489 {
490     ui->menuMenu->setEnabled(true);
491 }
492
493 void PackageView::disableMenu()
494 {
495     ui->menuMenu->setEnabled(false);
496 }
497
498 void PackageView::addListItem(Package* pkg_, QString listname_)
499 {
500     PackageListWidgetItem* p = new PackageListWidgetItem( pkg_, listname_ );
501
502     if( pkg_ != 0 )
503     {
504         QString name = pkg_->name();
505         if( !pkg_->maemoDisplayName().isEmpty() )
506             name = pkg_->maemoDisplayName();
507         p->setData(UserRoleName, name);
508     } else {
509         p->setData(UserRoleName, listname_);
510     }
511
512     if( pkg_ != 0 )
513     {
514         p->setData(UserRoleDescShort, pkg_->descShort());
515         p->setData(UserRoleVersion, pkg_->version());
516         p->setData(UserRoleMarked, static_cast<int>(pkg_->markedOperation()) );
517         p->setData(UserRoleInstalled, pkg_->isInstalled());
518         p->setData(UserRoleUpgradeable, pkg_->isUpgradeable());
519         p->setData(UserRoleAvailVersion, pkg_->upgradeableVersion());
520         p->setData(UserRoleCurrentStatFilter, iSelectedStatFilter);
521         p->setData(UserRoleCurrentCatFilter, iSelectedCatFilter);
522
523         if( pkg_->availablePackage() )
524             p->setData(UserRoleBlacklisted, static_cast<int>(pkg_->availablePackage()->blacklisted()) );
525         else
526             p->setData(UserRoleBlacklisted, static_cast<int>(pkg_->blacklisted()) );
527
528         //qDebug() << pkg_->name();
529
530         pkg_->convertIcon();
531         p->setData(Qt::DecorationRole, *pkg_->icon());
532     }
533     ui->listWidget->addItem( p );
534 }
535
536 void PackageView::closeEvent(QCloseEvent *event)
537 {
538     if( !iAptInterface ) {
539         resetWindow();
540         event->accept();
541     }
542
543     if( iDimmer->busy() )
544     {
545         iAptInterface->cancel();
546         event->ignore();
547         return;
548     }
549
550     if( iAptInterface->numSelectedPackages() == 0 )
551     {
552         resetWindow();
553         event->accept();
554     } else {
555         QString c;
556         c.setNum( iAptInterface->numSelectedPackages() );
557         ConfirmDialog d(true, this);
558         d.setText("Returning to main menu", QString("Clear %1 package selection(s) and lose all the pending changes?").arg(iAptInterface->numSelectedPackages()));
559         if( d.exec() ) {
560             resetWindow();
561             event->accept();
562         } else {
563             event->ignore();
564         }
565     }
566 }
567
568 void PackageView::changeEvent(QEvent *e)
569 {
570     QMainWindow::changeEvent(e);
571     switch (e->type()) {
572     case QEvent::LanguageChange:
573         ui->retranslateUi(this);
574         break;
575     default:
576         break;
577     }
578 }
579
580 void PackageView::on_btn_Commit_clicked()
581 {
582     QStringList pkgnames;
583
584     QHashIterator<QString, Package*> i( *iAptInterface->packagesAvailable() );
585     while (i.hasNext())
586     {
587         i.next();
588
589         if( i.value()->markedOperation() == Package::PkgOpInstallUpgrade )
590             pkgnames << i.value()->name();
591         if( i.value()->markedOperation() == Package::PkgOpRemove ) {
592             qDebug() << "warning: trying to add package marked from the wrong list";
593             //pkgnames << i.value()->name() + "-";
594         }
595     }
596
597     QHashIterator<QString, Package*> r( *iAptInterface->packagesInstalled() );
598     while (r.hasNext())
599     {
600         r.next();
601
602         if( r.value()->markedOperation() == Package::PkgOpInstallUpgrade )
603             pkgnames << r.value()->name();
604         if( r.value()->markedOperation() == Package::PkgOpRemove )
605             pkgnames << r.value()->name() + "-";
606     }
607
608     iMainWindow->busyDialog(true, "Operation in progress", "Reading dependencies");
609
610     iAptInterface->setProcessPackages(pkgnames);
611     iAptInterface->addQueuedOperation(AAptInterface::ModeAptGetSimulate);
612     iAptInterface->run(iDimmer);
613 }
614
615 void PackageView::on_actionClear_selections_triggered()
616 {
617     QString c;
618     c.setNum( iAptInterface->numSelectedPackages() );
619     ConfirmDialog d(true, this);
620     d.setText(tr("Confirmation"), tr("Clear ") + c + tr(" package selection(s) and lose all the pending changes?"));
621     if( d.exec() )
622     {
623         clearSelections();
624         openWin();
625     }
626 }
627
628 void PackageView::clearSelections()
629 {
630     QHashIterator<QString, Package*> i( *iAptInterface->packagesInstalled() );
631     while (i.hasNext())
632     {
633         i.next();
634         i.value()->setMarkedForOperation(Package::PkgOpNone);
635     }
636     QHashIterator<QString, Package*> a( *iAptInterface->packagesAvailable() );
637     while (a.hasNext())
638     {
639         a.next();
640         a.value()->setMarkedForOperation(Package::PkgOpNone);
641     }
642     iAptInterface->setNumSelectedPackages(0);
643 }
644
645 void PackageView::on_listWidget_itemClicked(QListWidgetItem* item)
646 {
647     Package* pkg = dynamic_cast<PackageListWidgetItem*>(item)->package();
648     if( !pkg )
649         return;
650
651     bool bl = pkg->isBlacklisted();
652     bool bl_u = false;
653
654     Package* upg_pkg = pkg->availablePackage();
655     if( upg_pkg )
656         bl_u = upg_pkg->isBlacklisted();
657
658     PackageSelector s(pkg, iAptInterface, iSettings, iMainWindow, this);
659     s.exec();
660     Package::operation op = s.selectedOperation();
661
662     QStringList confl = pkg->checkConflicts_RichText();
663     if( confl.count() > 0 && op != Package::PkgOpNone ) {
664         ConfirmDialog d(true, this);
665         QString t = "Package " + pkg->name() + " conflicts with another installed or marked package. Mark anyway?";
666         t += "<font size=\"-1\"><br><br>Conflicts: ";
667         t += confl.join(", ");
668         t += "</font>";
669         d.setText("Conflicting packages", t);
670         if( !d.exec() )
671             op = Package::PkgOpNone;
672     }
673
674     if( pkg->isPinned() && op != Package::PkgOpNone ) {
675         ConfirmDialog d(true, this);
676         d.setText("Warning","Package has been pinned in apt preferences. Operation might not go as expected. Mark anyway?");
677         if( !d.exec() )
678             op = Package::PkgOpNone;
679     }
680
681     pkg->setMarkedForOperation( op );
682     item->setData( UserRoleMarked, (int)op );
683     updateLabel();
684
685     if( pkg->isBlacklisted() != bl ) {
686         openWin();
687     }
688     else if( upg_pkg && upg_pkg->isBlacklisted() != bl_u ) {
689         openWin();
690     }
691 }
692
693 void PackageView::updateLabel()
694 {
695     QString s;
696     s.setNum( iAptInterface->numSelectedPackages() );
697     QString s2;
698     s2.setNum( ui->listWidget->count() );
699     QString statlabel = iStatFilterLabels.at(iSelectedStatFilter);
700     if( iSelectedCatFilter == CatFilterAllMarked || iSelectedCatFilter == CatFilterBlacklisted )
701         statlabel = "All";
702     ui->label->setText("<font size=\"-2\"><b>" + s + "</b> package(s) marked<br>"
703                        + "Showing: <b>" + statlabel + "</b><br>"
704                        + "Filter: " + iCatFilterLabels.at(iSelectedCatFilter) + " - " + s2 + " package(s)</font>");
705
706     if( iAptInterface->numSelectedPackages()==0 ) {
707         ui->btn_Commit->setEnabled(false);
708         ui->actionClear_selections->setVisible(false);
709         ui->actionSave_selections->setVisible(false);
710     } else {
711         ui->btn_Commit->setEnabled(true);
712         ui->actionClear_selections->setVisible(true);
713         ui->actionSave_selections->setVisible(true);
714     }
715 }
716
717 void PackageView::on_btn_CategoryFilter_clicked()
718 {
719     FilterSelect f("Category filter", this);
720     f.setList( iCatFilterLabels, iSelectedCatFilter );
721
722     bool s = f.exec();
723
724     if( s )
725         iSelectedCatFilter = f.selection();
726
727     if( iSelectedCatFilter == CatFilterAllMarked || iSelectedCatFilter == CatFilterBlacklisted ) {
728         ui->btn_StatusFilter->setEnabled(false);
729     } else {
730         ui->btn_StatusFilter->setEnabled(true);
731     }
732
733     if( s ) {
734         iListCoverLabel->setText("Loading...");
735         iListCoverLabel->setAutoFillBackground(true);
736         iListCoverLabel->show();
737         QApplication::processEvents();
738         iSortNoticeShown = false;
739
740         openWin();
741
742         if( iSelectedCatFilter==CatFilterBlacklisted && iAptInterface->needListOrDateRefresh() )
743         {
744             ConfirmDialog d(false, this);
745             d.setText("Notice","Since you don't have all package lists currently loaded, not all blacklisted "
746                       "packages are necessarily shown");
747             d.exec();
748         }
749     }
750 }
751
752 void PackageView::setStatFilter(Package::packageStatus f_)
753 {
754     iSelectedStatFilter = f_;
755 }
756
757 void PackageView::on_btn_StatusFilter_clicked()
758 {
759     FilterSelect f("Status filter", this);
760     f.setList( iStatFilterLabels, iSelectedStatFilter );
761
762     int oldfilter = iSelectedStatFilter;
763
764     bool s = f.exec();
765
766     if( s ) {
767         iSelectedStatFilter = (Package::packageStatus)f.selection();
768
769         iListCoverLabel->setText("Loading...");
770         iListCoverLabel->setAutoFillBackground(true);
771         iListCoverLabel->show();
772         QApplication::processEvents();
773         iSortNoticeShown = false;
774
775         openWin();
776
777         if( oldfilter==Package::PkgStatInstalled && iSelectedStatFilter!=Package::PkgStatInstalled &&
778             iAptInterface->needListOrDateRefresh() )
779         {
780             iMainWindow->openNetworkConnection();
781
782             iMainWindow->setNextOperation(MainWindow::OpOpenPkgView);
783             iMainWindow->busyDialog(true,"Operation in progress","Reading the rest of the package lists");
784
785             if( iAptInterface->needRepoRefresh() && !iSettings->qsettings()->value("no_catalogs_autoupdate",false).toBool() )
786                 iAptInterface->addQueuedOperation(AAptInterface::ModeAptGetUpdate);
787
788             iAptInterface->addQueuedOperation(AAptInterface::ModeReadPackages);
789             iAptInterface->addQueuedOperation(AAptInterface::ModeFetchDates);
790             iAptInterface->run(iDimmer);
791         }
792     }
793 }
794
795 void PackageView::resetWindow()
796 {
797     iAptInterface->writeBlacklist();
798
799     ui->btn_StatusFilter->setEnabled(true);
800     iSelectedCatFilter = iDefaultCatFilter;
801     //iSortOrder = SortAlpha;
802     iSortNoticeShown = false;
803
804     clearSelections();
805     clearSearch();
806 }
807
808 void PackageView::on_actionHelp_triggered()
809 {
810     Help h(this);
811     h.exec();
812 }
813
814 void PackageView::on_btn_searchClose_clicked()
815 {
816     clearSearch();
817
818     iListCoverLabel->setText("Loading...");
819     iListCoverLabel->setAutoFillBackground(true);
820     iListCoverLabel->show();
821     QApplication::processEvents();
822
823     openWin();
824 }
825
826 void PackageView::clearSearch()
827 {
828     ui->lineEdit->clear();
829     ui->searchBar->hide();
830     ui->toolBar->show();
831     ui->listWidget->setFocusPolicy(Qt::StrongFocus);
832
833     iSearchResults.clear();
834 }
835
836 void PackageView::on_actionSearch_triggered()
837 {
838     if( ui->searchBar->isVisible() )
839         return;
840
841     ui->listWidget->setFocusPolicy(Qt::NoFocus);
842     ui->searchLabel->setText( QString("%1 results").arg(ui->listWidget->count()) );
843     ui->toolBar->hide();
844     ui->searchBar->show();
845     ui->lineEdit->setFocus();
846     iPrevSearchText = "";
847 }
848
849 void PackageView::on_lineEdit_textEdited(QString text)
850 {
851     if( !ui->searchBar->isVisible() )
852         return;
853
854     if( text.isEmpty() ) {
855         on_btn_searchClose_clicked();
856         return;
857     }
858
859     if( iPrevSearchText.length() > text.length() )
860     {
861         iListCoverLabel->setText("Loading...");
862         iListCoverLabel->setAutoFillBackground(true);
863         iListCoverLabel->show();
864         QApplication::processEvents();
865
866         ui->searchBar->hide();
867         openWin();
868         ui->searchBar->show();
869         ui->toolBar->hide(); // ensure it stays hidden
870         ui->lineEdit->setFocus();
871     }
872
873     iPrevSearchText = text;
874
875     QList<Package*> packages;
876     iSearchResults.clear();
877
878     for( int i=0; i<(ui->listWidget->count()); i++ ) {
879         packages.append( dynamic_cast<PackageListWidgetItem*>( ui->listWidget->item(i) )->package() );
880     }
881
882     if( text.startsWith(":") ) {
883         for( int i=0; i<packages.count(); i++ ) {
884             if( packages.at(i) ) {
885                 if( (iSearchPkgName && packages.at(i)->name().startsWith(text.mid(1), Qt::CaseInsensitive)) ||
886                     (iSearchDisplayName && packages.at(i)->displayName().startsWith(text.mid(1), Qt::CaseInsensitive)) ||
887                     (iSearchDescShort && packages.at(i)->descShort().startsWith(text.mid(1), Qt::CaseInsensitive)) ||
888                     (iSearchDescLong && packages.at(i)->descLong().startsWith(text.mid(1), Qt::CaseInsensitive)) )
889                 {
890                     iSearchResults.append( packages.at(i) );
891                 }
892             }
893         }
894     } else {
895         for( int i=0; i<packages.count(); i++ ) {
896             if( packages.at(i) ) {
897                 if( (iSearchPkgName && packages.at(i)->name().contains(text, Qt::CaseInsensitive)) ||
898                     (iSearchDisplayName && packages.at(i)->displayName().contains(text, Qt::CaseInsensitive)) ||
899                     (iSearchDescShort && packages.at(i)->descShort().contains(text, Qt::CaseInsensitive)) ||
900                     (iSearchDescLong && packages.at(i)->descLong().contains(text, Qt::CaseInsensitive)) )
901                 {
902                     iSearchResults.append( packages.at(i) );
903                 }
904             }
905         }
906     }
907
908     ui->searchLabel->setText( QString("%1 results").arg( iSearchResults.count()) );
909
910     openWin();
911 }
912
913 void PackageView::setSearchText(QString text)
914 {
915     ui->lineEdit->setText(text);
916     on_lineEdit_textEdited(text);
917 }
918
919 KeyEventGrabber::KeyEventGrabber(QObject* parent) : QObject(parent)
920 {
921 }
922
923 bool KeyEventGrabber::eventFilter(QObject *obj, QEvent *event)
924 {
925     if( event->type() == QEvent::KeyPress ) {
926         QString text = dynamic_cast<QKeyEvent*>(event)->text();
927         int key = dynamic_cast<QKeyEvent*>(event)->key();
928         if( (text.trimmed() != "" || text==" ") && key!=Qt::Key_Backspace ) {
929             dynamic_cast<PackageView*>(this->parent())->on_actionSearch_triggered();
930             dynamic_cast<PackageView*>(this->parent())->setSearchText( text );
931             return true;
932         }
933     }
934     return QObject::eventFilter(obj, event);
935 }
936
937 void PackageView::on_pushButton_searchOptions_clicked()
938 {
939     SearchOptions s(this);
940     s.setSelections(iSearchPkgName, iSearchDisplayName, iSearchDescShort, iSearchDescLong);
941     if( s.exec() )
942     {
943         iSearchPkgName = s.searchPkgName();
944         iSearchDisplayName = s.searchDisplayName();
945         iSearchDescShort = s.searchDescShort();
946         iSearchDescLong = s.searchDescLong();
947
948         iPrevSearchText += " ";
949         on_lineEdit_textEdited( ui->lineEdit->text() );
950     }
951 }
952
953 void PackageView::setSearchOptions(bool pkgname, bool dispname, bool dshort, bool dlong)
954 {
955     iSearchPkgName = pkgname;
956     iSearchDisplayName = dispname;
957     iSearchDescShort = dshort;
958     iSearchDescLong = dlong;
959 }
960
961 void PackageView::on_actionUpgrade_all_triggered()
962 {
963     for( int i=0; i<ui->listWidget->count(); i++ )
964     {
965         Package* pkg = dynamic_cast<PackageListWidgetItem*>(ui->listWidget->item(i))->package();
966         if( pkg && pkg->isUpgradeable() ) {
967             pkg->setMarkedForOperation( Package::PkgOpInstallUpgrade );
968             ui->listWidget->item(i)->setData(UserRoleMarked, (int)Package::PkgOpInstallUpgrade);
969         }
970     }
971     updateLabel();
972 }
973
974 void PackageView::on_actionView_log_triggered()
975 {
976     QByteArray log = iAptInterface->readLogFile();
977     LogView l(log, this);
978     l.exec();
979 }
980
981 void PackageView::on_btn_Sort_clicked()
982 {
983     SortSelector s(iSortOrder, this);
984     if( s.exec() ) {
985         iSortOrder = s.selectedOperation();
986         iSortNoticeShown = false;
987         openWin();
988     }
989 }
990
991 void PackageView::on_actionLoad_selections_triggered()
992 {
993     if( iAptInterface->numSelectedPackages() > 0 ) {
994         ConfirmDialog d(true, this);
995         d.setText("Confirmation", "Proceed loading selections? All current selections will be cleared.");
996         if( !d.exec() )
997             return;
998     }
999
1000     QString filename = QFileDialog::getOpenFileName(this, "Open selections list", KDefaultUserOpenSaveDir, "Selection lists (*.slist)");
1001     if( filename.isNull() || filename.isEmpty() )
1002         return;
1003
1004     clearSelections();
1005
1006     QStringList unknownList;
1007     QStringList wrongverList;
1008     QStringList wrongstatusList;
1009     int success=0;
1010     int errors=0;
1011
1012     QFile f( filename );
1013     if( f.open(QIODevice::ReadOnly | QIODevice::Text ) )
1014     {
1015         while(!f.atEnd()) {
1016             QString line = f.readLine().trimmed();
1017             QStringList parts = line.split(' ');
1018             if( parts.count()==3 ) {
1019                 Package* pkgAv = iAptInterface->packagesAvailable()->value(parts.at(0),0);
1020                 Package* pkgIn = iAptInterface->packagesInstalled()->value(parts.at(0),0);
1021                 if( parts.at(2)=="install" ) {
1022                     if( !pkgAv && !pkgIn ) {
1023                         unknownList << parts.at(0);
1024                     }
1025                     else if( pkgIn ) {
1026                         wrongstatusList << parts.at(0);
1027                     }
1028                     else if( pkgAv ) {
1029                         if( !pkgAv->isInstalled() )
1030                         {
1031                             pkgAv->setMarkedForOperation(Package::PkgOpInstallUpgrade);
1032                             success++;
1033                             if( pkgAv->version() != parts.at(1) ) {
1034                                 wrongverList << parts.at(0);
1035                             }
1036                         } else {
1037                             wrongstatusList << parts.at(0);
1038                         }
1039                     }
1040
1041                 } else if( parts.at(2)=="upgrade" ) {
1042                     if( !pkgAv && !pkgIn ) {
1043                         unknownList << parts.at(0);
1044                     }
1045                     else if( (pkgAv && !pkgIn) || (pkgIn && !pkgAv) ) {
1046                         wrongstatusList << parts.at(0);
1047                     }
1048                     else if( pkgIn && pkgAv ) {
1049                         if( pkgIn->isInstalled() && pkgIn->isUpgradeable() )
1050                         {
1051                             pkgIn->setMarkedForOperation(Package::PkgOpInstallUpgrade);
1052                             success++;
1053                             if( pkgIn->version() != parts.at(1) ) {
1054                                 wrongverList << parts.at(0);
1055                             }
1056                         } else {
1057                             wrongstatusList << parts.at(0);
1058                         }
1059                     }
1060
1061                 } else if( parts.at(2)=="remove" ) {
1062                     if( !pkgAv && !pkgIn ) {
1063                         unknownList << parts.at(0);
1064                     }
1065                     else if( pkgAv && !pkgIn ) {
1066                         wrongstatusList << parts.at(0);
1067                     }
1068                     else if( pkgIn ) {
1069                         if( pkgIn->isInstalled() )
1070                         {
1071                             pkgIn->setMarkedForOperation(Package::PkgOpRemove);
1072                             success++;
1073                             if( pkgIn->version() != parts.at(1) ) {
1074                                 wrongverList << parts.at(0);
1075                             }
1076                         } else {
1077                             wrongstatusList << parts.at(0);
1078                         }
1079                     }
1080                 } else {
1081                     errors++;
1082                 }
1083             } else if( !line.isEmpty() ){
1084                 errors++;
1085             }
1086         }
1087         f.close();
1088     }
1089
1090     ConfirmDialog d(false, this);
1091     QString msg;
1092     msg += QString("<b>%1 successful</b><br>").arg(success);
1093
1094     if( wrongverList.count() > 0 ) {
1095         msg += QString("%1 version mismatches (selected anyway):<br>").arg(wrongverList.count());
1096         msg += "<font size=\"-1\">";
1097         for( int i=0; i<wrongverList.count(); i++ ) {
1098             msg += wrongverList.at(i) + " ";
1099         }
1100         msg += "</font><br>";
1101     }
1102
1103     if( wrongstatusList.count() > 0 ) {
1104         msg += QString("%1 status mismatches (packages ignored):<br>").arg(wrongstatusList.count());
1105         msg += "<font size=\"-1\">";
1106         for( int i=0; i<wrongstatusList.count(); i++ ) {
1107             msg += wrongstatusList.at(i) + " ";
1108         }
1109         msg += "</font><br>";
1110     }
1111
1112     if( unknownList.count() > 0 ) {
1113         msg += QString("%1 unknown packages:<br>").arg(unknownList.count());
1114         msg += "<font size=\"-1\">";
1115         for( int i=0; i<unknownList.count(); i++ ) {
1116             msg += unknownList.at(i) + " ";
1117         }
1118         msg += "</font><br>";
1119     }
1120
1121     if( errors>0 || (wrongstatusList.count()==0 && unknownList.count()==0) ) {
1122         msg += QString("%1 errors<br>").arg(errors);
1123     }
1124     if( success==0 && wrongstatusList.count()==0 && unknownList.count()==0 )
1125         msg = "No stored selections";
1126     QString title;
1127     if( success > 0 )
1128         title = "Selections loaded";
1129     else
1130         title = "No selections loaded";
1131     d.setText(title, msg);
1132     d.exec();
1133
1134     if( unknownList.count()>0 && iAptInterface->needListOrDateRefresh() )
1135     {
1136         ConfirmDialog f(false, this);
1137         f.setText("About Unknown packages","You encountered unknown packages. You might want to try "
1138                   "loading the selections again after loading the full package lists (by selecting install or upgrade view)");
1139         f.exec();
1140     }
1141
1142     if( success > 0 ) {
1143         ui->btn_StatusFilter->setEnabled(false);
1144         iSelectedCatFilter = CatFilterAllMarked;
1145     }
1146     openWin();
1147 }
1148
1149 void PackageView::on_actionSave_selections_triggered()
1150 {
1151     if( iAptInterface->numSelectedPackages() == 0 )
1152         return;
1153
1154     QString filename = QFileDialog::getSaveFileName(this, "Save selections to...", KDefaultUserOpenSaveDir, "Selection lists (*.slist)");
1155     if( filename.isNull() || filename.isEmpty() )
1156         return;
1157
1158     if( !filename.endsWith(".slist") )
1159         filename.append(".slist");
1160
1161     QFile f( filename );
1162     bool fail = false;
1163     int count = 0;
1164     if( f.open(QIODevice::WriteOnly | QIODevice::Text) )
1165     {
1166         QTextStream out(&f);
1167
1168         QHashIterator<QString, Package*> i( *iAptInterface->packagesAvailable() );
1169         while (i.hasNext())
1170         {
1171             i.next();
1172
1173             if( i.value()->markedOperation() == Package::PkgOpInstallUpgrade ) {
1174                 out << i.value()->name() << " " << i.value()->version() << " install\n";
1175                 count++;
1176             }
1177             if( i.value()->markedOperation() == Package::PkgOpRemove )
1178                 qDebug() << "Warning: package is marked for removal in the wrong list";
1179         }
1180
1181         QHashIterator<QString, Package*> r( *iAptInterface->packagesInstalled() );
1182         while (r.hasNext())
1183         {
1184             r.next();
1185
1186             if( r.value()->markedOperation() == Package::PkgOpInstallUpgrade ) {
1187                 out << r.value()->name() << " " << r.value()->version() << " upgrade\n";
1188                 count++;
1189             }
1190             if( r.value()->markedOperation() == Package::PkgOpRemove ) {
1191                 out << r.value()->name() << " " << r.value()->version() << " remove\n";
1192                 count++;
1193             }
1194         }
1195
1196         f.close();
1197     } else {
1198         fail = true;
1199     }
1200
1201     ConfirmDialog d(false, this);
1202     if( fail )
1203         d.setText( "Error", "Failed to write package selections" );
1204     else
1205         d.setText( "Selections stored", QString("Stored %1 selections").arg(count) );
1206     d.exec();
1207 }
1208
1209 void PackageView::on_actionRestore_all_triggered()
1210 {
1211     ConfirmDialog d(true, this);
1212     d.setText("Confirmation","Restore all shown blacklisted packages?");
1213     if( !d.exec() )
1214         return;
1215
1216     for( int i=0; i<ui->listWidget->count(); i++ )
1217     {
1218         Package* pkg = dynamic_cast<PackageListWidgetItem*>(ui->listWidget->item(i))->package();
1219         Package* p1 = iAptInterface->packagesInstalled()->value(pkg->name(),0);
1220         Package* p2 = iAptInterface->packagesAvailable()->value(pkg->name(),0);
1221         if( p1 && p1->isBlacklisted() ) {
1222             BlacklistSelect::blackList old = p1->blacklisted();
1223             p1->setBlacklisted(BlacklistSelect::BlacklistNone);
1224             iAptInterface->removeFromBlacklist(p1, old);
1225         }
1226         if( p2 && p2->isBlacklisted() ) {
1227             BlacklistSelect::blackList old = p2->blacklisted();
1228             p2->setBlacklisted(BlacklistSelect::BlacklistNone);
1229             iAptInterface->removeFromBlacklist(p2, old);
1230         }
1231     }
1232     iAptInterface->writeBlacklist();
1233     openWin();
1234 }
1235
1236 void PackageView::on_actionStore_list_triggered()
1237 {
1238     QString filename = QFileDialog::getSaveFileName(this, "Save application list to...", KDefaultUserOpenSaveDir, "Selection lists (*.slist)");
1239     if( filename.isNull() || filename.isEmpty() )
1240         return;
1241
1242     if( !filename.endsWith(".slist") )
1243         filename.append(".slist");
1244
1245     QFile f( filename );
1246     bool fail = false;
1247     int count = 0;
1248     if( f.open(QIODevice::WriteOnly | QIODevice::Text) )
1249     {
1250         QTextStream out(&f);
1251
1252         QHashIterator<QString, Package*> r( *iAptInterface->packagesInstalled() );
1253         while (r.hasNext())
1254         {
1255             r.next();
1256
1257             if( r.value()->isInstalled() && r.value()->section().startsWith("user/") ) {
1258                 out << r.value()->name() << " " << r.value()->version() << " install\n";
1259                 count++;
1260             }
1261         }
1262
1263         f.close();
1264     } else {
1265         fail = true;
1266     }
1267
1268     ConfirmDialog d(false, this);
1269     if( fail )
1270         d.setText( "Error", "Failed to write application list" );
1271     else
1272         d.setText( "List stored", QString("Stored %1 selections.").arg(count) + "<br>"
1273                    "The list can be used to restore removed applications later." );
1274     d.exec();
1275 }