Merge branch 'fixes0.6'
[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         QLinearGradient gradientBase(r.topLeft(), r.bottomLeft());
61         QColor base = option.palette.color(QPalette::Window);
62         QColor base2 = base;
63         int r1=base.red()+15;
64         int g1=base.green()+15;
65         int b1=base.blue()+15;
66         if( r1>255 ) r1=255;
67         if( g1>255 ) g1=255;
68         if( b1>255 ) b1=255;
69         int r2=base2.red()-20;
70         int g2=base2.green()-20;
71         int b2=base2.blue()-20;
72         if( r2<0 ) r2=0;
73         if( g2<0 ) g2=0;
74         if( b2<0 ) b2=0;
75         base.setRgb( r1, g1, b1 );
76         base2.setRgb( r2, g2, b2 );
77         gradientBase.setColorAt(0, base);
78         gradientBase.setColorAt(1, base2);
79
80         painter->fillRect(r, gradientBase);
81         painter->drawLine(QPoint(r.left(),r.bottom()), QPoint(r.right(),r.bottom()));
82
83         QPixmap icon = index.data(Qt::DecorationRole).value<QPixmap>();
84         if( icon.isNull() ) {
85                 // use default icon
86                 painter->drawPixmap( r.left(), r.top()+4, 48, 48, iDefaultIcon );
87         } else {
88                 painter->drawPixmap( r.left(), r.top()+4, 48, 48, icon );
89         }
90
91         QPixmap statusicon;
92         if( marked == Package::PkgOpNone )
93         {
94                 if( installed && upgradeable )
95                         statusicon = iIconPkgNoOpInstalledUpgradeable;
96                 else if( installed )
97                         statusicon = iIconPkgNoOpInstalled;
98                 else if( !installed )
99                         statusicon = iIconPkgNoOpNotInstalled;
100         } else if( marked == Package::PkgOpInstallUpgrade ) {
101                 if( upgradeable )
102                         statusicon = iIconPkgUpgrade;
103                 else
104                         statusicon = iIconPkgInstall;
105         } else if( marked == Package::PkgOpRemove ) {
106                 statusicon = iIconPkgRemove;
107         }
108
109         QString showVer;
110         if( upgradeable && (statfilter==Package::PkgStatUpgradeable ||
111                                                 (statfilter==Package::PkgStatUnknown && marked==Package::PkgOpInstallUpgrade) ||
112                                                 (catfilter==PackageView::CatFilterAllMarked && marked==Package::PkgOpInstallUpgrade) ))
113         {
114                 showVer = upg_version;
115         } else {
116                 showVer = version;
117         }
118
119         int ver_w = 0;
120         if( QApplication::desktop()->width() > QApplication::desktop()->height() )
121         {
122                 r = option.rect;
123                 r.setRight( r.right()-statusicon.width()-4 );
124
125                 if( catfilter != PackageView::CatFilterBlacklisted ) {
126                         painter->drawText(r, Qt::AlignTop|Qt::AlignRight, showVer, &r);
127                         ver_w = r.width();
128                 } else if( blacklisted==BlacklistSelect::BlacklistThis ) {
129                         if( upgradeable && !upg_version.isEmpty() ) {
130                                 showVer = upg_version;
131                         } else {
132                                 showVer = version;
133                         }
134                         painter->drawText(r, Qt::AlignTop|Qt::AlignRight, showVer, &r);
135                         ver_w = r.width();
136                 }
137         }
138
139         r = option.rect;
140         r.setRight( r.right()-statusicon.width()-4-ver_w );  //does not work as it should?
141         QFont f = painter->font();
142         f.setBold(true);
143         painter->setFont(f);
144         painter->drawText(r.left()+iDefaultIcon.width()+2, r.top(), r.width(), r.height(), Qt::AlignTop|Qt::AlignLeft, name, &r);
145         f.setBold(false);
146         painter->setFont(f);
147
148         f.setPointSize( f.pointSize()-4 );
149         painter->setFont(f);
150         r = option.rect;
151         painter->drawText(r.left()+iDefaultIcon.width()+2, r.top(), r.width(), r.height(), Qt::AlignBottom|Qt::AlignLeft, desc, &r);
152
153         r = option.rect;
154         painter->drawPixmap(r.right()-statusicon.width()-2, r.top()+4, 24, 24, statusicon);
155
156         painter->restore();
157
158         //if( t.elapsed()>=100 )
159         //qDebug() << name << t.elapsed();
160 }
161
162 void ListItemDelegate::loadIcons()
163 {
164         iDefaultIcon = QPixmap(":/icons/icons/appdefault.png");
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, 58);
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 }