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