release 0.6.6
[fapman] / packageselector.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 <QtNetwork>
21 #include <QDBusConnection>
22 #include <QDBusInterface>
23
24 #include "packageselector.h"
25 #include "ui_packageselector.h"
26 #include "package.h"
27 #include "packageview.h"
28 #include "repository.h"
29 #include "blacklistselect.h"
30 #include "aaptinterface.h"
31 #include "settings.h"
32
33 PackageSelector::PackageSelector(Package* pkg, AAptInterface* apt, Settings* set, QWidget *parent) :
34     QDialog(parent),
35     ui(new Ui::PackageSelector)
36 {
37     ui->setupUi(this);  
38         iPkg = pkg;
39         iAptInterface = apt;
40         iSettings = set;
41         iNetworkAccessManager = 0;
42         iChangelogFetched = false;
43         iFetchInProgress = false;
44         iChangelog = "";
45
46         ui->pushButton_website->setIcon(QPixmap("/usr/share/icons/hicolor/48x48/hildon/general_web.png"));
47         if( getMaemoOrgUrl(pkg).isEmpty() && getMaemoOrgUrl(pkg->availablePackage()).isEmpty() ) {
48                 ui->pushButton_website->setEnabled(false);
49         }
50         if( pkg->isMarkedForOperation() )
51                 ui->pushButton_blacklist->setEnabled(false);
52
53         if( pkg->icon() && !pkg->icon()->isNull() ) {
54                 ui->label_appicon->setPixmap( *pkg->icon() );
55         } else {
56                 ui->label_appicon->setPixmap( QPixmap(":/icons/icons/appdefault.png") );
57         }
58         this->setWindowTitle( pkg->name() );
59
60         if( pkg->maemoDisplayName() != "" )
61                 ui->label_header->setText("<b>" + pkg->maemoDisplayName() + "</b>");
62         else
63                 ui->label_header->setText("<b>" + pkg->name() + "</b>");
64
65         ui->radioTabBasic->setChecked(true);
66         updateInfo();
67
68         // switch to shorter button labels for portait mode
69         if( QApplication::desktop()->width() < QApplication::desktop()->height() )
70         {
71                 ui->radioTabBasic->setText("Info");
72                 ui->radioTabChanges->setText("Chgs");
73                 ui->radioTabDepends->setText("Deps");
74         }
75
76         Package* upg_pkg = iPkg->availablePackage();
77         if( !upg_pkg )
78                 upg_pkg = iPkg;
79         if( (!pkg->isUpgradeable() || (upg_pkg && upg_pkg->upgradeDescription().isEmpty())) && (getMaemoOrgUrl(upg_pkg).isEmpty()) )
80                 ui->radioTabChanges->hide();
81
82         if( pkg->isUpgradeable() )
83                 ui->radioInstall->setText("Upgrade");
84         else
85                 ui->radioInstall->setText("Install");
86
87         if( pkg->markedOperation() == Package::PkgOpNone )
88         {
89                 if( pkg->isInstalled() && pkg->isUpgradeable() )
90                         ui->label_statusicon->setPixmap(QPixmap(":/icons/icons/pkg_nop_instupgr.png"));
91                 else if( pkg->isInstalled() )
92                         ui->label_statusicon->setPixmap(QPixmap(":/icons/icons/pkg_nop_installed.png"));
93                 else if( !pkg->isInstalled() )
94                         ui->label_statusicon->setPixmap(QPixmap(":/icons/icons/pkg_nop_notinstalled.png"));
95
96                 ui->radioNothing->setChecked(true);
97         } else if( pkg->markedOperation() == Package::PkgOpInstallUpgrade ) {
98                 if( pkg->isUpgradeable() )
99                         ui->label_statusicon->setPixmap(QPixmap(":/icons/icons/pkg_upgrade.png"));
100                 else
101                         ui->label_statusicon->setPixmap(QPixmap(":/icons/icons/pkg_install.png"));
102                 ui->radioInstall->setChecked(true);
103         } else if( pkg->markedOperation() == Package::PkgOpRemove ) {
104                 ui->label_statusicon->setPixmap(QPixmap(":/icons/icons/pkg_remove.png"));
105                 ui->radioRemove->setChecked(true);
106         }
107
108         if( !getMaemoOrgUrl(upg_pkg).isEmpty() && !iChangelogFetched && !iFetchInProgress &&
109                 iSettings->qsettings()->value("always_fetch_changes",false).toBool() )
110         {
111                 queryChangelog();
112         }
113 }
114
115 PackageSelector::~PackageSelector()
116 {
117         if( iNetworkAccessManager ) {
118                 delete iNetworkAccessManager;
119                 iNetworkAccessManager = 0;
120         }
121     delete ui;
122 }
123
124 void PackageSelector::changeEvent(QEvent *e)
125 {
126     QDialog::changeEvent(e);
127     switch (e->type()) {
128     case QEvent::LanguageChange:
129         ui->retranslateUi(this);
130         break;
131     default:
132         break;
133     }
134 }
135
136 Package::operation PackageSelector::selectedOperation()
137 {
138         if( ui->radioInstall->isChecked() )
139                 return Package::PkgOpInstallUpgrade;
140         if( ui->radioRemove->isChecked() )
141                 return Package::PkgOpRemove;
142
143         return Package::PkgOpNone;
144 }
145
146 void PackageSelector::on_pushButton_blacklist_clicked()
147 {
148         Package* pkg = iPkg;
149         /*if( iPkg->isUpgradeable() && iPkg->availablePackage() )
150                 pkg = iPkg->availablePackage();*/
151
152         if( pkg->isInstalled() && pkg->isUpgradeable() )
153         {
154                 Package* upg = pkg->availablePackage();
155                 if( upg )
156                         pkg = upg;
157         }
158
159         BlacklistSelect s(pkg, this);
160         BlacklistSelect::blackList old = pkg->blacklisted();
161         if( s.exec() ) {                
162                 if( old != pkg->blacklisted() ) {
163                         Package* p1 = iAptInterface->packagesInstalled()->value(pkg->name(),0);
164                         Package* p2 = iAptInterface->packagesAvailable()->value(pkg->name(),0);
165                         if( p1 ) {
166                                 iAptInterface->removeFromBlacklist(p1,old);
167                                 if( pkg->blacklisted()==BlacklistSelect::BlacklistAll || pkg->blacklisted()==BlacklistSelect::BlacklistNone )
168                                         p1->setBlacklisted( pkg->blacklisted() );
169                                 else if( pkg->blacklisted()==BlacklistSelect::BlacklistThis && pkg->version()==p1->version() )
170                                         p1->setBlacklisted( pkg->blacklisted() );
171                                 else
172                                         p1->setBlacklisted( BlacklistSelect::BlacklistNone );
173                         }
174                         if( p2 ) {
175                                 iAptInterface->removeFromBlacklist(p2,old);
176                                 if( pkg->blacklisted()==BlacklistSelect::BlacklistAll || pkg->blacklisted()==BlacklistSelect::BlacklistNone )
177                                         p2->setBlacklisted( pkg->blacklisted() );
178                                 else if( pkg->blacklisted()==BlacklistSelect::BlacklistThis && pkg->version()==p2->version() )
179                                         p2->setBlacklisted( pkg->blacklisted() );
180                                 else
181                                         p2->setBlacklisted( BlacklistSelect::BlacklistNone );
182                         }
183                         iAptInterface->writeBlacklist();
184                 }
185                 updateInfo();
186         }
187 }
188
189 QString PackageSelector::getMaemoOrgUrl(Package* pkg)
190 {
191         QString url = "";
192
193         if( !pkg )
194                 return url;
195
196         if( pkg->repositories().count() == 0 )
197                 return url;
198
199         for( int i=0; i<pkg->repositories().count(); i++ )
200         {
201                 if( pkg->repositories().at(i) && pkg->repositories().at(i)->url().startsWith("http://repository.maemo.org") )
202                         url = "http://maemo.org/packages/view/" + pkg->name() + "/";
203         }
204         return url;
205 }
206
207 void PackageSelector::updateInfo()
208 {
209         if( ui->radioTabBasic->isChecked() )
210                 on_radioTabBasic_clicked();
211         else if( ui->radioTabChanges->isChecked() )
212                 on_radioTabChanges_clicked();
213         else if( ui->radioTabDepends->isChecked() )
214                 on_radioTabDepends_clicked();
215
216         ui->radioInstall->setEnabled(true);
217         ui->radioRemove->setEnabled(true);
218         ui->radioInstall->show();
219         ui->radioRemove->show();
220
221         Package* upg_pkg = iPkg->availablePackage();
222
223         if( iPkg->isInstalled() && !iPkg->isUpgradeable() )
224         {
225                 ui->radioInstall->setEnabled(false);
226                 ui->radioInstall->hide();
227         } else if( !iPkg->isInstalled() ) {
228                 ui->radioRemove->setEnabled(false);
229                 ui->radioRemove->hide();
230         }
231
232         if( iPkg->isBlacklisted() ) {
233                 ui->radioInstall->setEnabled(false);
234                 //ui->radioRemove->setEnabled(false);
235         }
236         if( iPkg->isInstalled() && upg_pkg && upg_pkg->isBlacklisted() ) {
237                 ui->radioInstall->setEnabled(false);
238         }
239 }
240
241 void PackageSelector::on_pushButton_website_clicked()
242 {
243         QString url = getMaemoOrgUrl(iPkg);
244         if( url == "" ) {
245                 url = getMaemoOrgUrl(iPkg->availablePackage());
246                 if( url == "")
247                         return;
248         }
249
250         QDBusConnection conn = QDBusConnection::connectToBus(QDBusConnection::SessionBus, "faster_application_manager");
251
252         QString service = "com.nokia.osso_browser";
253         QString path = "/com/nokia/osso_browser/request";
254         QString method = "open_new_window";
255
256         QDBusInterface browser(service, path, service, conn, this);
257         browser.call(method, url);
258 }
259
260 void PackageSelector::on_radioTabBasic_clicked()
261 {
262         ui->label_header2->show();
263         QString header2;
264
265         Package* upg_pkg = iPkg->availablePackage();
266
267         if( iPkg->isInstalled() ) {
268                 header2 += "Installed, version <b>" + iPkg->version() + "</b>";
269         } else {
270                 header2 += "Not installed";
271         }
272         header2 += "<br>";
273
274         if( iPkg->markedOperation() == Package::PkgOpInstallUpgrade )
275         {
276                 if( iPkg->isUpgradeable() )
277                         header2 += "Marked for <b>upgrade</b><br>";
278                 else
279                         header2 += "Marked for <b>installation</b><br>";
280         } else if(iPkg->markedOperation() == Package::PkgOpRemove)
281         {
282                 header2 += "Marked for <b>removal</b><br>";
283         }
284
285         if( iPkg->isBlacklisted() && !iPkg->isUpgradeable() ) {
286                 header2 += "BLACKLISTED";
287                 if( iPkg->blacklisted() == BlacklistSelect::BlacklistAll )
288                         header2 += " (all)";
289                 else if( iPkg->blacklisted() == BlacklistSelect::BlacklistThis )
290                         header2 += " (this)";
291                 iPkg->setMarkedForOperation(Package::PkgOpNone);
292                 header2 += "<br>";
293         } else if( upg_pkg && upg_pkg->isBlacklisted() ) {
294                 header2 += "BLACKLISTED";
295                 if( upg_pkg->blacklisted() == BlacklistSelect::BlacklistAll )
296                         header2 += " (all)";
297                 else if( upg_pkg->blacklisted() == BlacklistSelect::BlacklistThis )
298                         header2 += " (" + upg_pkg->version() + ")";
299                 iPkg->setMarkedForOperation(Package::PkgOpNone);
300                 upg_pkg->setMarkedForOperation(Package::PkgOpNone);
301                 header2 += "<br>";
302         }
303
304         ui->label_header2->setText(header2);
305
306
307         QString longtext;
308         longtext += "Category: " + iPkg->section() + "<br>";
309
310         if( iPkg->isInstalled() ) {
311                 longtext += "Installation date: ";
312                 if( iPkg->date().isValid() )
313                         longtext += iPkg->date().toString("yyyy-MM-dd hh:mm");
314                 else
315                         longtext += "Unknown";
316                 longtext += "<br>";
317         }
318
319         if( iPkg->isInstalled() && iPkg->installedSize() > 0 )
320                 longtext += QString("Size: %L1 kB<br>").arg(iPkg->installedSize() );
321
322
323         if( !iPkg->isInstalled() ){
324                 longtext += "Available version <b>" + iPkg->version() + "</b>";
325                 if( iPkg->size() > 0 )
326                         longtext += QString(" (%L1 kB)").arg(iPkg->size()/1024 );
327                 longtext += "<br>";
328         }
329         else if( iPkg->isUpgradeable() ){
330                 longtext += "Available version <b>" + iPkg->upgradeableVersion() + "</b>";
331                 if( iPkg->size() > 0 )
332                         longtext += QString(" (%L1 kB)").arg(iPkg->size()/1024 );
333                 longtext += "<br>";
334         }
335
336         if( !iPkg->isInstalled() ) {
337                 longtext += "Available package date: ";
338                 if( iPkg->date().isValid() )
339                         longtext += iPkg->date().toString("yyyy-MM-dd hh:mm");
340                 else
341                         longtext += "Unknown";
342                 longtext += "<br>";
343         } else if( iPkg->isUpgradeable() ) {
344                 longtext += "Available package date: ";
345                 if( iPkg->availablePackage() && iPkg->availablePackage()->date().isValid() )
346                         longtext += iPkg->availablePackage()->date().toString("yyyy-MM-dd hh:mm");
347                 else
348                         longtext += "Unknown";
349                 longtext += "<br>";
350         }
351
352         if( !iPkg->isInstalled() )
353         {
354                 longtext += "Repositories: ";
355                 if( iPkg->repositories().count()>0 ) {
356                         for(int i=0; i<iPkg->repositories().count(); i++ ) {
357                                 if( iPkg->repositories().at(i) )
358                                         longtext += iPkg->repositories().at(i)->name();
359                                 else
360                                         longtext += "unknown";
361                                 if( i<iPkg->repositories().count()-1 )
362                                         longtext += ", ";
363                         }
364                         longtext += "<br>";
365                 } else {
366                         longtext += "unknown<br>";
367                 }
368         }
369
370         if( iPkg->isUpgradeable() )
371         {
372                 longtext += "Repositories: ";
373                 if( upg_pkg && upg_pkg->repositories().count()>0 ) {
374                         for(int i=0; i<upg_pkg->repositories().count(); i++ ) {
375                                 if( upg_pkg->repositories().at(i) )
376                                         longtext += upg_pkg->repositories().at(i)->name();
377                                 else
378                                         longtext += "unknown";
379                                 if( i<upg_pkg->repositories().count()-1 )
380                                         longtext += ", ";
381                         }
382                         longtext += "<br>";
383                 } else {
384                         longtext += "unknown<br>";
385                 }
386         }
387
388         if( !iPkg->maintainerRichText().isEmpty() ) {
389                 longtext +="Maintainer: " + iPkg->maintainerRichText() + "<br>";
390         }
391
392         longtext += "<br>" + iPkg->descShort();
393         QString descLong = iPkg->descLong();
394         if( descLong.length()>0 ) {
395                 descLong.replace('\n',"<br>");
396                 longtext += "<font size=\"-1\"><br><br>" + descLong + "</font>";
397         }
398
399         ui->label_text->setText( longtext );
400 }
401
402 void PackageSelector::on_radioTabChanges_clicked()
403 {
404         ui->label_header2->hide();
405         ui->label_header2->setText("");
406
407         QString text;
408
409         Package* upg_pkg = iPkg->availablePackage();
410         if( !upg_pkg )
411                 upg_pkg = iPkg;
412
413         if( iPkg->isUpgradeable() && upg_pkg && !upg_pkg->upgradeDescription().isEmpty() )
414         {
415                 text += "<u><b>Upgrade description:</b></u>";
416                 text += "<font size=\"-1\"><br>";
417                 text += upg_pkg->upgradeDescription();
418                 text += "</font><br>";
419                 text.replace('\n',"<br>");
420         }
421
422         QString changelog;
423         if( !getMaemoOrgUrl(upg_pkg).isEmpty() && !iChangelogFetched && !iFetchInProgress ) {
424                 queryChangelog();
425                 changelog = "Fetching changelog...";
426         } else {
427                 changelog = iChangelog;
428                 if( iFetchInProgress )
429                         changelog = "Fetching changelog...";
430                 else if( changelog == "" )
431                         changelog = "Not available";
432         }
433
434         text += "<u><b>Debian changelog:</b></u><font size=\"-1\"><br>";
435         text += changelog;
436         text += "</font>";
437
438         ui->label_text->setText(text);
439 }
440
441 void PackageSelector::on_radioTabDepends_clicked()
442 {
443         ui->label_header2->hide();
444         ui->label_header2->setText("");
445         ui->label_text->setText("");
446
447         QStringList deps = Package::toTrimmedRichTextList( iPkg->depends() );
448         QStringList confl = Package::toTrimmedRichTextList( iPkg->conflicts() );
449         QStringList predeps = Package::toTrimmedRichTextList( iPkg->preDepends() );
450         QStringList repl = Package::toTrimmedRichTextList( iPkg->replaces() );
451         QStringList prov = Package::toTrimmedRichTextList( iPkg->provides() );
452         QStringList brks = Package::toTrimmedRichTextList( iPkg->breaks() );
453         QStringList recs = Package::toTrimmedRichTextList( iPkg->recommends() );
454         QStringList sugs = Package::toTrimmedRichTextList( iPkg->suggests() );
455
456         QString text;
457
458         if( deps.count()>0 )
459         {
460                 text += "<b><u>Depends:</u></b><br><font size=\"-1\">";
461                 text += deps.join(", ");
462                 text += "</font><br>";
463         }
464
465         if( predeps.count()>0 )
466         {
467                 text += "<b><u>Pre-Depends:</u></b><br><font size=\"-1\">";
468                 text += predeps.join(", ");
469                 text += "</font><br>";
470         }
471
472         if( confl.count()>0 )
473         {
474                 text += "<b><u>Conflicts:</u></b><br><font size=\"-1\">";
475                 text += confl.join(", ");
476                 text += "</font><br>";
477         }
478
479         if( prov.count()>0 )
480         {
481                 text += "<b><u>Provides:</u></b><br><font size=\"-1\">";
482                 text += prov.join(", ");
483                 text += "</font><br>";
484         }
485
486         if( repl.count()>0 )
487         {
488                 text += "<b><u>Replaces:</u></b><br><font size=\"-1\">";
489                 text += repl.join(", ");
490                 text += "</font><br>";
491         }
492
493         if( brks.count()>0 )
494         {
495                 text += "<b><u>Breaks:</u></b><br><font size=\"-1\">";
496                 text += brks.join(", ");
497                 text += "</font><br>";
498         }
499
500         if( recs.count()>0 )
501         {
502                 text += "<b><u>Recommends:</u></b><br><font size=\"-1\">";
503                 text += recs.join(", ");
504                 text += "</font><br>";
505         }
506
507         if( sugs.count()>0 )
508         {
509                 text += "<b><u>Suggests:</u></b><br><font size=\"-1\">";
510                 text += sugs.join(", ");
511                 text += "</font><br>";
512         }
513
514         if( deps.count()==0 && predeps.count()==0 && confl.count()==0 && prov.count()==0 &&
515                 repl.count()==0 && brks.count()==0 && recs.count()==0 && sugs.count()==0 )
516         {
517                 text = "Package has no dependencies listed";
518         }
519
520         ui->label_text->setText(text);
521 }
522
523 void PackageSelector::queryChangelog()
524 {
525         if( iChangelogFetched || iFetchInProgress )
526                 return;
527
528         iChangelogFetched = false;
529         iFetchInProgress = true;
530         iChangelog = "";
531
532         Package* pkg = iPkg->availablePackage();
533         if( !pkg )
534                 pkg = iPkg;
535
536         if( getMaemoOrgUrl(pkg) == "" ) {
537                 iChangelogFetched = true;
538                 return;
539         }
540
541         if( !iNetworkAccessManager ) {
542                 iNetworkAccessManager = new QNetworkAccessManager(this);
543                 connect(iNetworkAccessManager,SIGNAL(finished(QNetworkReply*)),this,SLOT(changelogFetchNetworkReply(QNetworkReply*)));
544
545                 if( iSettings->qsettings()->value("use_proxies").toBool() && !iSettings->qsettings()->value("http_proxy").toString().isEmpty() )
546                 {
547                          QNetworkProxy proxy = Settings::createProxyFromString( iSettings->qsettings()->value("http_proxy").toString() );
548                          iNetworkAccessManager->setProxy(proxy);
549                 }
550         }
551
552         QUrl url( getMaemoOrgUrl(pkg) );
553         QNetworkRequest req(url);
554
555         qDebug() << "fetching changelog";
556
557         iNetworkAccessManager->get(req);
558 }
559
560 void PackageSelector::changelogFetchNetworkReply(QNetworkReply* reply)
561 {
562         if( reply->error() == QNetworkReply::NoError ) {
563                 QByteArray data = reply->readAll();
564
565                 int pos = data.indexOf("<div class=\"changelog\">");
566                 int pos2 = data.indexOf("</div>", pos);
567
568                 if( pos!=-1 && pos2!=-1 ) {
569                         iChangelog = parseChangeTable( QString::fromUtf8( data.mid(pos,pos2-pos+6) ) );
570                 } else {
571                         iChangelog = "";
572                 }
573
574                 qDebug() << "changelog fetched";
575         } else {
576                 iChangelog = "Not available (Network error)";
577                 qDebug() << "changelog fetch error:" << reply->error() << reply->errorString();
578         }
579
580         reply->deleteLater();
581         iFetchInProgress = false;
582         iChangelogFetched = true;
583
584         if( this->isVisible() && ui->radioTabChanges->isChecked() )
585                 on_radioTabChanges_clicked();
586 }
587
588 QString PackageSelector::parseChangeTable(QString tabledata)
589 {
590         tabledata.remove("<strong>");
591         tabledata.remove("</strong>");
592         tabledata.remove('\n');
593
594         //qDebug() << tabledata;
595
596         QString newdata = "<table><tbody>";
597
598         int pos = tabledata.indexOf("<tr>", tabledata.indexOf("<tbody>") );
599         while( pos != -1 ) {
600                 int pos2 = tabledata.indexOf("</td>",pos);
601                 pos2 = tabledata.indexOf("</td>",pos2+5);
602
603                 if( pos2 != -1 )
604                         newdata += tabledata.mid(pos, pos2-pos+5) + "</tr>";
605
606                 pos = tabledata.indexOf("<tr>",pos+4);
607         }
608
609         newdata += "</tbody></table>";
610
611         //qDebug() << newdata;
612
613         return newdata;
614 }