release 0.6.7 (see the debian changelog for a proper list of changes)
[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         show();
455
456         if( !ui->searchBar->isVisible() ) {
457                 ui->listWidget->setFocusPolicy(Qt::StrongFocus);
458                 ui->listWidget->setFocus();
459         } else {
460                 ui->listWidget->setFocusPolicy(Qt::NoFocus);
461                 ui->lineEdit->setFocus();
462         }
463
464         if( ui->listWidget->count() == 0 )
465         {
466                 iListCoverLabel->setGeometry( ui->listWidget->rect() );
467                 iListCoverLabel->setText("No packages");
468                 iListCoverLabel->show();
469         }
470
471         if( ui->listWidget->count()>1 && iPackagesEmptySort == ui->listWidget->count() && !iSortNoticeShown ) {
472                 ConfirmDialog d(false, this);
473                 QString msg = "No shown packages currently have the required information for sorting by this criterion. The list is unsorted.";
474                 if( iSortOrder == SortDateDesc && !iSettings->qsettings()->value("fetch_dates",false).toBool() )
475                         msg += " You can enable date fetching in the options menu.";
476                 d.setText("Notice", msg);
477                 d.exec();
478                 iSortNoticeShown = true;
479         }
480 }
481
482 void PackageView::enableMenu()
483 {
484         ui->menuMenu->setEnabled(true);
485 }
486
487 void PackageView::disableMenu()
488 {
489         ui->menuMenu->setEnabled(false);
490 }
491
492 void PackageView::addListItem(Package* pkg_, QString listname_)
493 {
494         PackageListWidgetItem* p = new PackageListWidgetItem( pkg_, listname_ );
495
496         if( pkg_ != 0 )
497         {
498                 QString name = pkg_->name();
499                 if( !pkg_->maemoDisplayName().isEmpty() )
500                         name = pkg_->maemoDisplayName();
501                 p->setData(UserRoleName, name);
502         } else {
503                 p->setData(UserRoleName, listname_);
504         }
505
506         if( pkg_ != 0 )
507         {
508                 p->setData(UserRoleDescShort, pkg_->descShort());
509                 p->setData(UserRoleVersion, pkg_->version());
510                 p->setData(UserRoleMarked, static_cast<int>(pkg_->markedOperation()) );
511                 p->setData(UserRoleInstalled, pkg_->isInstalled());
512                 p->setData(UserRoleUpgradeable, pkg_->isUpgradeable());
513                 p->setData(UserRoleAvailVersion, pkg_->upgradeableVersion());
514                 p->setData(UserRoleCurrentStatFilter, iSelectedStatFilter);
515                 p->setData(UserRoleCurrentCatFilter, iSelectedCatFilter);
516
517                 if( pkg_->availablePackage() )
518                         p->setData(UserRoleBlacklisted, static_cast<int>(pkg_->availablePackage()->blacklisted()) );
519                 else
520                         p->setData(UserRoleBlacklisted, static_cast<int>(pkg_->blacklisted()) );
521
522                 //qDebug() << pkg_->name();
523
524                 pkg_->convertIcon();
525                 p->setData(Qt::DecorationRole, *pkg_->icon());
526         }
527         ui->listWidget->addItem( p );
528 }
529
530 void PackageView::closeEvent(QCloseEvent *event)
531 {
532         if( !iAptInterface ) {
533                 resetWindow();
534                 event->accept();
535         }
536
537         if( iDimmer->busy() )
538         {
539                 iAptInterface->cancel();
540                 event->ignore();
541                 return;
542         }
543
544         if( iAptInterface->numSelectedPackages() == 0 )
545         {               
546                 resetWindow();
547                 event->accept();
548         } else {
549                 QString c;
550                 c.setNum( iAptInterface->numSelectedPackages() );
551                 ConfirmDialog d(true, this);
552                 d.setText("Returning to main menu", QString("Clear %1 package selection(s) and lose all the pending changes?").arg(iAptInterface->numSelectedPackages()));
553                 if( d.exec() ) {
554                         resetWindow();
555                         event->accept();
556                 } else {
557                         event->ignore();
558                 }
559         }
560 }
561
562 void PackageView::changeEvent(QEvent *e)
563 {
564     QMainWindow::changeEvent(e);
565     switch (e->type()) {
566     case QEvent::LanguageChange:
567         ui->retranslateUi(this);
568         break;
569     default:
570         break;
571     }
572 }
573
574 void PackageView::on_btn_Commit_clicked()
575 {
576         QStringList pkgnames;
577
578         QHashIterator<QString, Package*> i( *iAptInterface->packagesAvailable() );
579         while (i.hasNext())
580         {
581                 i.next();
582
583                 if( i.value()->markedOperation() == Package::PkgOpInstallUpgrade )
584                         pkgnames << i.value()->name();
585                 if( i.value()->markedOperation() == Package::PkgOpRemove ) {
586                         qDebug() << "warning: trying to add package marked from the wrong list";
587                         //pkgnames << i.value()->name() + "-";
588                 }
589         }
590
591         QHashIterator<QString, Package*> r( *iAptInterface->packagesInstalled() );
592         while (r.hasNext())
593         {
594                 r.next();
595
596                 if( r.value()->markedOperation() == Package::PkgOpInstallUpgrade )
597                         pkgnames << r.value()->name();
598                 if( r.value()->markedOperation() == Package::PkgOpRemove )
599                         pkgnames << r.value()->name() + "-";
600         }
601
602         iMainWindow->busyDialog(true, "Operation in progress", "Reading dependencies");
603
604         iAptInterface->setProcessPackages(pkgnames);
605         iAptInterface->addQueuedOperation(AAptInterface::ModeAptGetSimulate);
606         iAptInterface->run(iDimmer);
607 }
608
609 void PackageView::on_actionClear_selections_triggered()
610 {
611         QString c;
612         c.setNum( iAptInterface->numSelectedPackages() );
613         ConfirmDialog d(true, this);
614         d.setText(tr("Confirmation"), tr("Clear ") + c + tr(" package selection(s) and lose all the pending changes?"));
615         if( d.exec() )
616         {
617                 clearSelections();
618                 openWin();
619         }
620 }
621
622 void PackageView::clearSelections()
623 {
624         QHashIterator<QString, Package*> i( *iAptInterface->packagesInstalled() );
625         while (i.hasNext())
626         {
627                 i.next();
628                 i.value()->setMarkedForOperation(Package::PkgOpNone);
629         }
630         QHashIterator<QString, Package*> a( *iAptInterface->packagesAvailable() );
631         while (a.hasNext())
632         {
633                 a.next();
634                 a.value()->setMarkedForOperation(Package::PkgOpNone);
635         }
636         iAptInterface->setNumSelectedPackages(0);
637 }
638
639 void PackageView::on_listWidget_itemClicked(QListWidgetItem* item)
640 {
641         Package* pkg = dynamic_cast<PackageListWidgetItem*>(item)->package();
642         if( !pkg )
643                 return;
644
645         bool bl = pkg->isBlacklisted();
646         bool bl_u = false;
647
648         Package* upg_pkg = pkg->availablePackage();
649         if( upg_pkg )
650                 bl_u = upg_pkg->isBlacklisted();
651
652         PackageSelector s(pkg, iAptInterface, iSettings, this);
653         s.exec();
654         Package::operation op = s.selectedOperation();
655
656         QStringList confl = pkg->checkConflicts_RichText();
657         if( confl.count() > 0 && op != Package::PkgOpNone ) {
658                 ConfirmDialog d(true, this);
659                 QString t = "Package " + pkg->name() + " conflicts with another installed or marked package. Mark anyway?";
660                 t += "<font size=\"-1\"><br><br>Conflicts: ";
661                 t += confl.join(", ");
662                 t += "</font>";
663                 d.setText("Conflicting packages", t);
664                 if( !d.exec() )
665                         op = Package::PkgOpNone;
666         }
667
668         if( pkg->isPinned() && op != Package::PkgOpNone ) {
669                 ConfirmDialog d(true, this);
670                 d.setText("Warning","Package has been pinned in apt preferences. Operation might not go as expected. Mark anyway?");
671                 if( !d.exec() )
672                         op = Package::PkgOpNone;
673         }
674
675         pkg->setMarkedForOperation( op );
676         item->setData( UserRoleMarked, (int)op );
677         updateLabel();
678
679         if( pkg->isBlacklisted() != bl ) {
680                 openWin();
681         }
682         else if( upg_pkg && upg_pkg->isBlacklisted() != bl_u ) {
683                 openWin();
684         }
685 }
686
687 void PackageView::updateLabel()
688 {
689         QString s;
690         s.setNum( iAptInterface->numSelectedPackages() );
691         QString s2;
692         s2.setNum( ui->listWidget->count() );
693         QString statlabel = iStatFilterLabels.at(iSelectedStatFilter);
694         if( iSelectedCatFilter == CatFilterAllMarked || iSelectedCatFilter == CatFilterBlacklisted )
695                 statlabel = "All";
696         ui->label->setText("<font size=\"-2\"><b>" + s + "</b> package(s) marked<br>"
697                                            + "Showing: <b>" + statlabel + "</b><br>"
698                                            + "Filter: " + iCatFilterLabels.at(iSelectedCatFilter) + " - " + s2 + " package(s)</font>");
699
700         if( iAptInterface->numSelectedPackages()==0 ) {
701                 ui->btn_Commit->setEnabled(false);
702                 ui->actionClear_selections->setVisible(false);
703                 ui->actionSave_selections->setVisible(false);
704         } else {
705                 ui->btn_Commit->setEnabled(true);
706                 ui->actionClear_selections->setVisible(true);
707                 ui->actionSave_selections->setVisible(true);
708         }
709 }
710
711 void PackageView::on_btn_CategoryFilter_clicked()
712 {
713         FilterSelect f("Category filter", this);
714         f.setList( iCatFilterLabels, iSelectedCatFilter );
715
716         bool s = f.exec();
717
718         if( s )
719                 iSelectedCatFilter = f.selection();
720
721         if( iSelectedCatFilter == CatFilterAllMarked || iSelectedCatFilter == CatFilterBlacklisted ) {
722                 ui->btn_StatusFilter->setEnabled(false);
723         } else {
724                 ui->btn_StatusFilter->setEnabled(true);
725         }
726
727         if( s ) {
728                 iListCoverLabel->setText("Loading...");
729                 iListCoverLabel->setAutoFillBackground(true);
730                 iListCoverLabel->show();
731                 QApplication::processEvents();
732                 iSortNoticeShown = false;
733
734                 openWin();
735
736                 if( iSelectedCatFilter==CatFilterBlacklisted && iAptInterface->needListOrDateRefresh() )
737                 {
738                         ConfirmDialog d(false, this);
739                         d.setText("Notice","Since you don't have all package lists currently loaded, not all blacklisted "
740                                           "packages are necessarily shown");
741                         d.exec();
742                 }
743         }
744 }
745
746 void PackageView::setStatFilter(Package::packageStatus f_)
747 {
748         iSelectedStatFilter = f_;
749 }
750
751 void PackageView::on_btn_StatusFilter_clicked()
752 {
753         FilterSelect f("Status filter", this);
754         f.setList( iStatFilterLabels, iSelectedStatFilter );
755
756         int oldfilter = iSelectedStatFilter;
757
758         bool s = f.exec();
759
760         if( s ) {
761                 iSelectedStatFilter = (Package::packageStatus)f.selection();
762
763                 iListCoverLabel->setText("Loading...");
764                 iListCoverLabel->setAutoFillBackground(true);
765                 iListCoverLabel->show();
766                 QApplication::processEvents();
767                 iSortNoticeShown = false;
768
769                 openWin();
770
771                 if( oldfilter==Package::PkgStatInstalled && iSelectedStatFilter!=Package::PkgStatInstalled &&
772                         iAptInterface->needListOrDateRefresh() )
773                 {
774                         iMainWindow->openNetworkConnection();
775
776                         iMainWindow->setNextOperation(MainWindow::OpOpenPkgView);
777                         iMainWindow->busyDialog(true,"Operation in progress","Reading the rest of the package lists");
778
779                         if( iAptInterface->needRepoRefresh() && !iSettings->qsettings()->value("no_catalogs_autoupdate",false).toBool() )
780                                 iAptInterface->addQueuedOperation(AAptInterface::ModeAptGetUpdate);
781
782                         iAptInterface->addQueuedOperation(AAptInterface::ModeReadPackages);
783                         iAptInterface->addQueuedOperation(AAptInterface::ModeFetchDates);
784                         iAptInterface->run(iDimmer);
785                 }
786         }
787 }
788
789 void PackageView::resetWindow()
790 {
791         iAptInterface->writeBlacklist();
792
793         ui->btn_StatusFilter->setEnabled(true);
794         iSelectedCatFilter = iDefaultCatFilter;
795         //iSortOrder = SortAlpha;
796         iSortNoticeShown = false;
797
798         clearSelections();
799         clearSearch();
800 }
801
802 void PackageView::on_actionHelp_triggered()
803 {
804         Help h(this);
805         h.exec();
806 }
807
808 void PackageView::on_btn_searchClose_clicked()
809 {
810         clearSearch();
811
812         iListCoverLabel->setText("Loading...");
813         iListCoverLabel->setAutoFillBackground(true);
814         iListCoverLabel->show();
815         QApplication::processEvents();
816
817         openWin();
818 }
819
820 void PackageView::clearSearch()
821 {
822         ui->lineEdit->clear();
823         ui->searchBar->hide();
824         ui->toolBar->show();
825         ui->listWidget->setFocusPolicy(Qt::StrongFocus);
826
827         iSearchResults.clear();
828 }
829
830 void PackageView::on_actionSearch_triggered()
831 {
832         if( ui->searchBar->isVisible() )
833                 return;
834
835         ui->listWidget->setFocusPolicy(Qt::NoFocus);
836         ui->searchLabel->setText( QString("%1 results").arg(ui->listWidget->count()) );
837         ui->toolBar->hide();
838         ui->searchBar->show();
839         ui->lineEdit->setFocus();
840         iPrevSearchText = "";
841 }
842
843 void PackageView::on_lineEdit_textEdited(QString text)
844 {
845         if( !ui->searchBar->isVisible() )
846                 return;
847
848         if( text.isEmpty() ) {
849                 on_btn_searchClose_clicked();
850                 return;
851         }
852
853         if( iPrevSearchText.length() > text.length() )
854         {
855                 iListCoverLabel->setText("Loading...");
856                 iListCoverLabel->setAutoFillBackground(true);
857                 iListCoverLabel->show();
858                 QApplication::processEvents();
859
860                 ui->searchBar->hide();
861                 openWin();
862                 ui->searchBar->show();
863                 ui->toolBar->hide(); // ensure it stays hidden
864                 ui->lineEdit->setFocus();
865         }
866
867         iPrevSearchText = text;
868
869         QList<Package*> packages;
870         iSearchResults.clear();
871
872         for( int i=0; i<(ui->listWidget->count()); i++ ) {
873                 packages.append( dynamic_cast<PackageListWidgetItem*>( ui->listWidget->item(i) )->package() );
874         }
875
876         if( text.startsWith(":") ) {
877                 for( int i=0; i<packages.count(); i++ ) {
878                         if( packages.at(i) ) {
879                                 if( (iSearchPkgName && packages.at(i)->name().startsWith(text.mid(1), Qt::CaseInsensitive)) ||
880                                         (iSearchDisplayName && packages.at(i)->displayName().startsWith(text.mid(1), Qt::CaseInsensitive)) ||
881                                         (iSearchDescShort && packages.at(i)->descShort().startsWith(text.mid(1), Qt::CaseInsensitive)) ||
882                                         (iSearchDescLong && packages.at(i)->descLong().startsWith(text.mid(1), Qt::CaseInsensitive)) )
883                                 {
884                                         iSearchResults.append( packages.at(i) );
885                                 }
886                         }
887                 }
888         } else {
889                 for( int i=0; i<packages.count(); i++ ) {
890                         if( packages.at(i) ) {
891                                 if( (iSearchPkgName && packages.at(i)->name().contains(text, Qt::CaseInsensitive)) ||
892                                         (iSearchDisplayName && packages.at(i)->displayName().contains(text, Qt::CaseInsensitive)) ||
893                                         (iSearchDescShort && packages.at(i)->descShort().contains(text, Qt::CaseInsensitive)) ||
894                                         (iSearchDescLong && packages.at(i)->descLong().contains(text, Qt::CaseInsensitive)) )
895                                 {
896                                         iSearchResults.append( packages.at(i) );
897                                 }
898                         }
899                 }
900         }
901
902         ui->searchLabel->setText( QString("%1 results").arg( iSearchResults.count()) );
903
904         openWin();
905 }
906
907 void PackageView::setSearchText(QString text)
908 {
909         ui->lineEdit->setText(text);
910         on_lineEdit_textEdited(text);
911 }
912
913 KeyEventGrabber::KeyEventGrabber(QObject* parent) : QObject(parent)
914 {
915 }
916
917 bool KeyEventGrabber::eventFilter(QObject *obj, QEvent *event)
918 {
919         if( event->type() == QEvent::KeyPress ) {
920                 QString text = dynamic_cast<QKeyEvent*>(event)->text();
921                 int key = dynamic_cast<QKeyEvent*>(event)->key();
922                 if( (text.trimmed() != "" || text==" ") && key!=Qt::Key_Backspace ) {
923                         dynamic_cast<PackageView*>(this->parent())->on_actionSearch_triggered();
924                         dynamic_cast<PackageView*>(this->parent())->setSearchText( text );
925                         return true;
926                 }
927         }
928         return QObject::eventFilter(obj, event);
929 }
930
931 void PackageView::on_pushButton_searchOptions_clicked()
932 {
933         SearchOptions s(this);
934         s.setSelections(iSearchPkgName, iSearchDisplayName, iSearchDescShort, iSearchDescLong);
935         if( s.exec() )
936         {
937                 iSearchPkgName = s.searchPkgName();
938                 iSearchDisplayName = s.searchDisplayName();
939                 iSearchDescShort = s.searchDescShort();
940                 iSearchDescLong = s.searchDescLong();
941
942                 iPrevSearchText += " ";
943                 on_lineEdit_textEdited( ui->lineEdit->text() );
944         }
945 }
946
947 void PackageView::setSearchOptions(bool pkgname, bool dispname, bool dshort, bool dlong)
948 {
949         iSearchPkgName = pkgname;
950         iSearchDisplayName = dispname;
951         iSearchDescShort = dshort;
952         iSearchDescLong = dlong;
953 }
954
955 void PackageView::on_actionUpgrade_all_triggered()
956 {
957         for( int i=0; i<ui->listWidget->count(); i++ )
958         {
959                 Package* pkg = dynamic_cast<PackageListWidgetItem*>(ui->listWidget->item(i))->package();
960                 if( pkg && pkg->isUpgradeable() ) {
961                         pkg->setMarkedForOperation( Package::PkgOpInstallUpgrade );
962                         ui->listWidget->item(i)->setData(UserRoleMarked, (int)Package::PkgOpInstallUpgrade);
963                 }
964         }
965         updateLabel();
966 }
967
968 void PackageView::on_actionView_log_triggered()
969 {
970         QByteArray log = iAptInterface->readLogFile();
971         LogView l(log, this);
972         l.exec();
973 }
974
975 void PackageView::on_btn_Sort_clicked()
976 {
977         SortSelector s(iSortOrder, this);
978         if( s.exec() ) {
979                 iSortOrder = s.selectedOperation();
980                 iSortNoticeShown = false;
981                 openWin();
982         }
983 }
984
985 void PackageView::on_actionLoad_selections_triggered()
986 {
987         if( iAptInterface->numSelectedPackages() > 0 ) {
988                 ConfirmDialog d(true, this);
989                 d.setText("Confirmation", "Proceed loading selections? All current selections will be cleared.");
990                 if( !d.exec() )
991                         return;
992         }
993
994         clearSelections();
995
996         QStringList unknownList;
997         QStringList wrongverList;
998         int success=0;
999         int errors=0;
1000
1001         QFile f("/root/.fapman/selections.list");
1002         if( f.open(QIODevice::ReadOnly | QIODevice::Text ) )
1003         {
1004                 while(!f.atEnd()) {
1005                         QString line = f.readLine().trimmed();
1006                         QStringList parts = line.split(' ');
1007                         if( parts.count()==3 ) {
1008                                 Package* pkgAv = iAptInterface->packagesAvailable()->value(parts.at(0),0);
1009                                 Package* pkgIn = iAptInterface->packagesInstalled()->value(parts.at(0),0);
1010                                 if( parts.at(2)=="install" ) {
1011                                         if( !pkgAv && !pkgIn ) {
1012                                                 unknownList << parts.at(0);
1013                                         }
1014                                         else if( pkgIn ) {
1015                                                 wrongverList << parts.at(0);
1016                                         }
1017                                         else if( pkgAv ) {
1018                                                 if( pkgAv->version() == parts.at(1) && !pkgAv->isInstalled() ) {
1019                                                         pkgAv->setMarkedForOperation(Package::PkgOpInstallUpgrade);
1020                                                         success++;
1021                                                 } else {
1022                                                         wrongverList << parts.at(0);
1023                                                 }
1024                                         }
1025
1026                                 } else if( parts.at(2)=="upgrade" ) {
1027                                         if( !pkgAv && !pkgIn ) {
1028                                                 unknownList << parts.at(0);
1029                                         }
1030                                         else if( (pkgAv && !pkgIn) || (pkgIn && !pkgAv) ) {
1031                                                 wrongverList << parts.at(0);
1032                                         }
1033                                         else if( pkgIn && pkgAv ) {
1034                                                 if( pkgIn->version() == parts.at(1) && pkgIn->isInstalled() && pkgIn->isUpgradeable() ) {
1035                                                         pkgIn->setMarkedForOperation(Package::PkgOpInstallUpgrade);
1036                                                         success++;
1037                                                 } else {
1038                                                         wrongverList << parts.at(0);
1039                                                 }
1040                                         }
1041
1042                                 } else if( parts.at(2)=="remove" ) {
1043                                         if( !pkgAv && !pkgIn ) {
1044                                                 unknownList << parts.at(0);
1045                                         }
1046                                         else if( pkgAv && !pkgIn ) {
1047                                                 wrongverList << parts.at(0);
1048                                         }
1049                                         else if( pkgIn ) {
1050                                                 if( pkgIn->version() == parts.at(1) && pkgIn->isInstalled() ) {
1051                                                         pkgIn->setMarkedForOperation(Package::PkgOpRemove);
1052                                                         success++;
1053                                                 } else {
1054                                                         wrongverList << parts.at(0);
1055                                                 }
1056                                         }
1057                                 } else {
1058                                         errors++;
1059                                 }
1060                         } else if( !line.isEmpty() ){
1061                                 errors++;
1062                         }
1063                 }
1064                 f.close();
1065         }
1066
1067         ConfirmDialog d(false, this);
1068         QString msg;
1069         msg += QString("<b>%1 successful</b><br>").arg(success);
1070         if( wrongverList.count() > 0 ) {
1071                 msg += QString("%1 wrong versions/changed statuses (not selected):<br>").arg(wrongverList.count());
1072                 msg += "<font size=\"-1\">";
1073                 for( int i=0; i<wrongverList.count(); i++ ) {
1074                         msg += wrongverList.at(i) + " ";
1075                 }
1076                 msg += "</font><br>";
1077         }
1078         if( unknownList.count() > 0 ) {
1079                 msg += QString("%1 unknown packages:<br>").arg(unknownList.count());
1080                 msg += "<font size=\"-1\">";
1081                 for( int i=0; i<unknownList.count(); i++ ) {
1082                         msg += unknownList.at(i) + " ";
1083                 }
1084                 msg += "</font><br>";
1085         }
1086         if( errors>0 || (wrongverList.count()==0 && unknownList.count()==0) ) {
1087                 msg += QString("%1 errors<br>").arg(errors);
1088         }
1089         if( success==0 && wrongverList.count()==0 && unknownList.count()==0 )
1090                 msg = "No stored selections";
1091         QString title;
1092         if( success > 0 )
1093                 title = "Selections loaded";
1094         else
1095                 title = "No selections loaded";
1096         d.setText(title, msg);
1097         d.exec();
1098
1099         if( success > 0 ) {
1100                 ui->btn_StatusFilter->setEnabled(false);
1101                 iSelectedCatFilter = CatFilterAllMarked;
1102         }
1103         openWin();
1104 }
1105
1106 void PackageView::on_actionSave_selections_triggered()
1107 {
1108         if( iAptInterface->numSelectedPackages() == 0 )
1109                 return;
1110
1111         QFile f("/root/.fapman/selections.list");
1112         bool fail = false;
1113         int count = 0;
1114         if( f.open(QIODevice::WriteOnly | QIODevice::Text) )
1115         {
1116                 QTextStream out(&f);
1117
1118                 QHashIterator<QString, Package*> i( *iAptInterface->packagesAvailable() );
1119                 while (i.hasNext())
1120                 {
1121                         i.next();
1122
1123                         if( i.value()->markedOperation() == Package::PkgOpInstallUpgrade ) {
1124                                 out << i.value()->name() << " " << i.value()->version() << " install\n";
1125                                 count++;
1126                         }
1127                         if( i.value()->markedOperation() == Package::PkgOpRemove )
1128                                 qDebug() << "Warning: package is marked for removal in the wrong list";
1129                 }
1130
1131                 QHashIterator<QString, Package*> r( *iAptInterface->packagesInstalled() );
1132                 while (r.hasNext())
1133                 {
1134                         r.next();
1135
1136                         if( r.value()->markedOperation() == Package::PkgOpInstallUpgrade ) {
1137                                 out << r.value()->name() << " " << r.value()->version() << " upgrade\n";
1138                                 count++;
1139                         }
1140                         if( r.value()->markedOperation() == Package::PkgOpRemove ) {
1141                                 out << r.value()->name() << " " << r.value()->version() << " remove\n";
1142                                 count++;
1143                         }
1144                 }
1145
1146                 f.close();
1147         } else {
1148                 fail = true;
1149         }
1150
1151         ConfirmDialog d(false, this);
1152         if( fail )
1153                 d.setText( "Error", "Failed to write package selections" );
1154         else
1155                 d.setText( "Selections stored", QString("Stored %1 selections").arg(count) );
1156         d.exec();
1157 }
1158
1159 void PackageView::on_actionRestore_all_triggered()
1160 {
1161         ConfirmDialog d(true, this);
1162         d.setText("Confirmation","Restore all shown blacklisted packages?");
1163         if( !d.exec() )
1164                 return;
1165
1166         for( int i=0; i<ui->listWidget->count(); i++ )
1167         {
1168                 Package* pkg = dynamic_cast<PackageListWidgetItem*>(ui->listWidget->item(i))->package();
1169                 Package* p1 = iAptInterface->packagesInstalled()->value(pkg->name(),0);
1170                 Package* p2 = iAptInterface->packagesAvailable()->value(pkg->name(),0);
1171                 if( p1 && p1->isBlacklisted() ) {
1172                         BlacklistSelect::blackList old = p1->blacklisted();
1173                         p1->setBlacklisted(BlacklistSelect::BlacklistNone);
1174                         iAptInterface->removeFromBlacklist(p1, old);
1175                 }
1176                 if( p2 && p2->isBlacklisted() ) {
1177                         BlacklistSelect::blackList old = p2->blacklisted();
1178                         p2->setBlacklisted(BlacklistSelect::BlacklistNone);
1179                         iAptInterface->removeFromBlacklist(p2, old);
1180                 }
1181         }
1182         iAptInterface->writeBlacklist();
1183         openWin();
1184 }