source: src-qt4/pc-pkgmanager/mainWin.cpp @ 584b6ff

9.2-releasereleng/10.0releng/10.0.1
Last change on this file since 584b6ff was 584b6ff, checked in by Kris Moore <kris@…>, 10 months ago

Avoid pkgng locking issues, by only looking for updates once the initial
package reading is finished. Otherwise you end up with cases where some
of the commands will fail, due to the sqlite DB being locked

  • Property mode set to 100644
File size: 36.9 KB
Line 
1/****************************************************************************
2** ui.h extension file, included from the uic-generated form implementation.
3**
4** If you want to add, delete, or rename functions or slots, use
5** Qt Designer to update this file, preserving your code.
6**
7** You should not define a constructor or destructor in this file.
8** Instead, write your code in functions called init() and destroy().
9** These will automatically be called by the form's constructor and
10** destructor.
11*****************************************************************************/
12#include <fcntl.h>
13#include <QDateTime>
14#include <QDebug>
15#include <QDir>
16#include <QProcess>
17#include <QProgressDialog>
18#include <QSocketNotifier>
19#include <QString>
20#include <QTextStream>
21#include <pcbsd-utils.h>
22#include <pcbsd-ui.h>
23#include <QSettings>
24#include "mainWin.h"
25#include "../config.h"
26
27void mainWin::ProgramInit(QString ch)
28{
29  // Set any warden directories
30  lastError="";
31  wDir = ch;
32
33  //Grab the username
34  //username = QString::fromLocal8Bit(getenv("LOGNAME"));
35  connect(pushUpdatePkgs, SIGNAL(clicked()), this, SLOT(slotUpdatePkgsClicked()));
36  connect(pushClose, SIGNAL(clicked()), this, SLOT(slotCloseClicked()));
37  connect(buttonRescanPkgs, SIGNAL(clicked()), this, SLOT(slotRescanPkgsClicked()));
38  connect(pushPkgApply, SIGNAL( clicked() ), this, SLOT( slotApplyClicked() ) );
39  connect(action_Quit, SIGNAL( triggered(bool) ), this, SLOT( slotCloseClicked() ) );
40  connect(action_Basic, SIGNAL( triggered(bool) ), this, SLOT( slotViewChanged() ) );
41  connect(action_Advanced, SIGNAL( triggered(bool) ), this, SLOT( slotViewChanged() ) );
42
43  // Setup the action group
44  viewGroup = new QActionGroup(this);
45  viewGroup->addAction(action_Basic);
46  viewGroup->addAction(action_Advanced);
47
48  treeMetaPkgs->setContextMenuPolicy(Qt::CustomContextMenu);
49  connect(treeMetaPkgs, SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(slotMetaRightClick()) );
50
51  QSettings settings("PC-BSD", "PackageManager");
52  QString curMode = settings.value("view/mode").toString();
53  if ( curMode == "Advanced" )
54  {
55     stackedPkgView->setCurrentIndex(1);
56     action_Basic->setChecked(false);
57     action_Advanced->setChecked(true);
58  }
59
60  // If we are running on a chroot, only do advanced mode
61  if ( !wDir.isEmpty() )
62  {
63     stackedPkgView->setCurrentIndex(1);
64     action_Advanced->setChecked(true);
65     menu_View->setEnabled(false);
66     menu_View->setVisible(false);
67  }
68
69  initMetaWidget();
70}
71
72void mainWin::slotViewChanged()
73{
74  QString mode;
75  if ( action_Basic->isChecked() ) {
76    stackedPkgView->setCurrentIndex(0);
77    mode="Basic";
78  } else {
79    mode="Advanced";
80    stackedPkgView->setCurrentIndex(1);
81  }
82
83  // Save the mode as the default at next open
84  QSettings settings("PC-BSD", "PackageManager");
85  settings.setValue("view/mode", mode);
86
87  // Changed view, lets refresh
88  initMetaWidget();
89}
90
91void mainWin::slotRescanPkgsClicked()
92{
93  // Check for pkg updates
94  checkMPKGUpdates();
95}
96
97void mainWin::slotApplyClicked() {
98  // Running in basic mode
99  if ( stackedPkgView->currentIndex() == 0 )
100  {
101     saveMetaPkgs();   
102  } else {
103     // Running in advanced mode
104     applyNGChanges();
105  }
106
107}
108
109void mainWin::checkMPKGUpdates() {
110
111  QString line, tmp, name, pkgname, pkgover, pkgnver;
112  QStringList up, listPkgs;
113  bool haveUpdates = false;
114  int totPkgs=0;
115  buttonRescanPkgs->setEnabled(false);
116  pushUpdatePkgs->setEnabled(false);
117  listViewUpdatesPkgs->clear();
118  groupUpdatesPkgs->setTitle(tr("Checking for updates"));
119
120  QProcess p;
121  if ( wDir.isEmpty() )
122     p.start(QString("pc-updatemanager"), QStringList() << "pkgcheck");
123  else
124     p.start(QString("chroot"), QStringList() << wDir << "pc-updatemanager" << "pkgcheck");
125  while(p.state() == QProcess::Starting || p.state() == QProcess::Running)
126     QCoreApplication::processEvents();
127
128  while (p.canReadLine()) {
129    line = p.readLine().simplified();
130    qDebug() << line;
131    if ( line.indexOf("Upgrading") != 0) {
132       continue;
133    }
134    tmp = line;
135    pkgname = tmp.section(" ", 1, 1);
136    pkgname.replace(":", "");
137    pkgover = tmp.section(" ", 2, 2);
138    pkgnver = tmp.section(" ", 4, 4);
139    QTreeWidgetItem *myItem = new QTreeWidgetItem(QStringList() << pkgname << pkgover << pkgnver);
140    listViewUpdatesPkgs->addTopLevelItem(myItem);
141    haveUpdates = true;
142    totPkgs++;
143  }
144
145  buttonRescanPkgs->setEnabled(true);
146  pushUpdatePkgs->setEnabled(haveUpdates);
147  if ( totPkgs > 0 ) {
148    tabUpdates->setTabText(1, tr("Package Updates (%1)").arg(totPkgs));
149    groupUpdatesPkgs->setTitle(tr("Available updates"));
150  } else {
151    tabUpdates->setTabText(1, tr("Package Updates"));
152    groupUpdatesPkgs->setTitle(tr("No available updates"));
153  }
154 
155}
156
157void mainWin::slotSingleInstance() {
158   this->hide();
159   this->showNormal();
160   this->activateWindow();
161   this->raise();
162}
163
164void mainWin::slotCloseClicked() {
165   close();
166}
167
168void mainWin::slotUpdatePkgsClicked() {
169  dPackages = false;
170  uPackages = false;
171
172  // Init the pkg process
173  prepPkgProcess();
174
175  // Create our runlist of package commands
176  QStringList pCmds;
177
178  if ( wDir.isEmpty() )
179    pCmds << "pc-updatemanager" << "pkgupdate";
180  else
181    pCmds << "chroot" << wDir << "pc-updatemanager" << "pkgupdate";
182
183  // Setup our runList
184  pkgCmdList << pCmds;
185
186  // Start the updating now
187  startPkgProcess();
188
189  textStatus->setText(tr("Starting package updates..."));
190
191}
192
193QString mainWin::getConflictDetailText() {
194
195  QStringList ConList = ConflictList.split(" ");
196  QStringList tmpDeps;
197  QString retText;
198
199  for (int i = 0; i < ConList.size(); ++i) {
200    QProcess p;
201    tmpDeps.clear();
202
203    if ( wDir.isEmpty() )
204      p.start("pkg", QStringList() << "rquery" << "%rn-%rv" << ConList.at(i));
205    else
206      p.start("chroot", QStringList() << wDir << "pkg" "rquery" << "%rn-%rv" << ConList.at(i) );
207
208    if(p.waitForFinished()) {
209      while (p.canReadLine()) {
210        tmpDeps << p.readLine().simplified();
211      }
212    }
213    retText+= ConList.at(i) + " " + tr("required by:") + "\n" + tmpDeps.join(" ");
214  }
215
216  return retText;
217}
218
219void mainWin::prepPkgProcess() {
220  pkgCmdList.clear();
221  textDisplayOut->clear();
222  pkgHasFailed=false;
223}
224
225void mainWin::startPkgProcess() {
226
227  if ( pkgCmdList.isEmpty() )
228    return;
229  if ( pkgCmdList.at(0).at(0).isEmpty() )
230     return; 
231
232  // Get the command name
233  QString cmd;
234  cmd = pkgCmdList.at(0).at(0);
235
236  // Get any optional flags
237  QStringList flags;
238  for (int i = 0; i < pkgCmdList.at(0).size(); ++i) {
239     if ( i == 0 )
240       continue;
241     flags << pkgCmdList.at(0).at(i);
242  } 
243
244  qDebug() << cmd + " " + flags.join(" ");
245 
246  // Setup the first process
247  uProc = new QProcess();
248  QProcessEnvironment env = QProcessEnvironment::systemEnvironment();
249  env.insert("PCFETCHGUI", "YES");
250  uProc->setProcessEnvironment(env);
251  uProc->setProcessChannelMode(QProcess::MergedChannels);
252
253  // Connect the slots
254  connect( uProc, SIGNAL(readyReadStandardOutput()), this, SLOT(slotReadPkgOutput()) );
255  connect( uProc, SIGNAL(finished(int, QProcess::ExitStatus)), this, SLOT(slotPkgDone()) );
256
257  uProc->start(cmd, flags);
258
259  stackedTop->setCurrentIndex(1);
260
261  progressUpdate->setRange(0, 0 );
262  progressUpdate->setValue(0);
263
264}
265
266void mainWin::slotReadPkgOutput() {
267   QString line, tmp, cur, tot, fname;
268
269   while (uProc->canReadLine()) {
270     line = uProc->readLine().simplified();
271     qDebug() << line;
272
273     tmp = line;
274     tmp.truncate(50);
275
276     // Flags we can parse out and not show the user
277
278     // Check if we have crashed into a conflict and ask the user what to do
279     if ( line.indexOf("PKGCONFLICTS: ") == 0 ) {
280        tmp = line; 
281        tmp.replace("PKGCONFLICTS: ", "");
282        ConflictList = tmp;
283        continue;
284     }
285     if ( line.indexOf("PKGREPLY: ") == 0 ) {
286        QString ans;
287        tmp = line; 
288        tmp.replace("PKGREPLY: ", "");
289        QMessageBox msgBox;
290        msgBox.setText(tr("The following packages are causing conflicts with the selected changes and can be automatically removed. Continue?") + "\n" + ConflictList);
291        msgBox.setStandardButtons(QMessageBox::Yes|QMessageBox::No);
292        msgBox.setDetailedText(getConflictDetailText());
293        msgBox.setDefaultButton(QMessageBox::No);
294        if ( msgBox.exec() == QMessageBox::Yes) {
295          // We will try to fix conflicts
296          ans="yes";
297        } else {
298          // We will fail :(
299          QMessageBox::warning(this, tr("Package Conflicts"),
300          tr("You may need to manually fix the conflicts before trying again."),
301          QMessageBox::Ok,
302          QMessageBox::Ok);
303          ans="no";
304        }
305
306        QFile pkgTrig( tmp );
307        if ( pkgTrig.open( QIODevice::WriteOnly ) ) {
308           QTextStream streamTrig( &pkgTrig );
309           streamTrig << ans;
310           pkgTrig.close();
311        }
312        continue;
313     }
314
315     if ( line.indexOf("FETCH: ") == 0 ) { 
316        progressUpdate->setValue(progressUpdate->value() + 1); 
317        tmp = line; 
318        tmp = tmp.remove(0, tmp.lastIndexOf("/") + 1); 
319        progressUpdate->setRange(0, 0);
320        progressUpdate->setValue(0);
321        curFileText = tr("Downloading: %1").arg(tmp); 
322        textStatus->setText(tr("Downloading: %1").arg(tmp)); 
323        continue;
324     } 
325     if ( line.indexOf("FETCHDONE") == 0 )
326        continue;
327
328     if ( line.indexOf("SIZE: ") == 0 ) {
329          bool ok, ok2;
330          line.replace("SIZE: ", "");
331          line.replace("DOWNLOADED: ", "");
332          line.replace("SPEED: ", "");
333          line.section(" ", 0, 0).toInt(&ok);
334          line.section(" ", 1, 1).toInt(&ok2);
335   
336          if ( ok && ok2 ) {
337            QString unit;
338            int tot = line.section(" ", 0, 0).toInt(&ok);
339            int cur = line.section(" ", 1, 1).toInt(&ok2);
340            QString percent = QString::number( (float)(cur * 100)/tot );
341            QString speed = line.section(" ", 2, 3);
342
343            // Get the MB downloaded / total
344            if ( tot > 2048 ) {
345              unit="MB";
346              tot = tot / 1024;
347              cur = cur / 1024;
348            } else {
349              unit="KB";
350            }
351
352            QString ProgressString=QString("(%1" + unit + " of %2" + unit + " at %3)").arg(cur).arg(tot).arg(speed);
353            progressUpdate->setRange(0, tot);
354            progressUpdate->setValue(cur);
355            textStatus->setText(curFileText + " " + ProgressString); 
356         }
357         continue;
358     }
359
360
361     // Now show output on GUI
362     textDisplayOut->insertPlainText(line + "\n");
363     textDisplayOut->moveCursor(QTextCursor::End);
364
365
366     // Any other flags to look for?
367     /////////////////////////////////////////////////////
368     if ( line.indexOf("to be downloaded") != -1 ) {
369       textStatus->setText(tr("Downloading packages..."));
370       curUpdate = 0;
371       progressUpdate->setValue(0);
372       continue;
373     }
374     if ( line.indexOf("Checking integrity") == 0 ) {
375       textStatus->setText(line);
376       uPackages = true;
377       dPackages = false;
378       curUpdate = 0;
379       progressUpdate->setValue(0);
380     }
381     
382     if ( uPackages ) {
383       if ( line.indexOf("Upgrading") == 0 || line.indexOf("Reinstalling") == 0 ) {
384         textStatus->setText(line);
385         curUpdate++;
386         progressUpdate->setValue(curUpdate);
387       }
388       continue;
389     }
390
391   } // end of while
392}
393
394void mainWin::slotPkgDone() {
395
396  if ( uProc->exitCode() != 0 )
397    pkgHasFailed=true;
398
399  // Run the next command on the stack if necessary
400  if (  pkgCmdList.size() > 1 ) {
401        pkgCmdList.removeAt(0); 
402        startPkgProcess();     
403        return;
404  }
405
406  // Nothing left to run! Lets wrap up
407  QFile sysTrig( SYSTRIGGER );
408  if ( sysTrig.open( QIODevice::WriteOnly ) ) {
409    QTextStream streamTrig( &sysTrig );
410     streamTrig << "INSTALLFINISHED: ";
411  }
412
413  if ( pkgHasFailed ) {
414    QFile file( "/tmp/pkg-output.log" );
415    if ( file.open( QIODevice::WriteOnly ) ) {
416       QTextStream stream( &file );
417       stream << textDisplayOut->toPlainText();
418       file.close();
419    }
420    QMessageBox::warning(this, tr("Failed!"), tr("The package commands failed. A copy of the output was saved to /tmp/pkg-output.log"));
421  } else
422    QMessageBox::warning(this, tr("Finished!"), tr("Package changes complete!" ));
423
424  // Clear out the old commands
425  pkgCmdList.clear();
426
427  // Switch back to our main display
428  stackedTop->setCurrentIndex(0);
429 
430  // Re-init the meta-widget
431  initMetaWidget();
432
433}
434
435/*****************************************
436Code for package stuff
437******************************************/
438
439void mainWin::initMetaWidget()
440{
441  qDebug() << "Starting metaWidget...";
442  groupInfo->setVisible(false);
443
444  // Running in basic mode
445  if ( stackedPkgView->currentIndex() == 0 )
446  {
447    populateMetaPkgs();
448    // Connect our slots
449  } else {
450    // Running in advanced mode
451    populateNGPkgs();
452  }
453}
454
455void mainWin::populateNGPkgs()
456{
457  pushPkgApply->setEnabled(false);
458  treeNGPkgs->clear();
459  tmpPkgList.clear();
460  new QTreeWidgetItem(treeNGPkgs, QStringList() << tr("Loading... Please wait...") );
461
462  if ( ! pkgList.isEmpty() ) {
463        disconnect(treeNGPkgs, SIGNAL(itemChanged(QTreeWidgetItem *, int)), 0, 0);
464        disconnect(treeNGPkgs, SIGNAL(currentItemChanged(QTreeWidgetItem *, QTreeWidgetItem *)), 0, 0);
465  }
466  pkgList.clear();
467  selPkgList.clear();
468
469  // Start the process to get meta-pkg info
470  getNGProc = new QProcess();
471  qDebug() << "Searching for pkgs...";
472  connect( getNGProc, SIGNAL(readyReadStandardOutput()), this, SLOT(slotGetNGPackageDataOutput()) );
473  connect( getNGProc, SIGNAL(finished(int, QProcess::ExitStatus)), this, SLOT(slotGetNGInstalledPkgs()) );
474  getNGProc->setProcessChannelMode(QProcess::MergedChannels);
475  if ( wDir.isEmpty() )
476    getNGProc->start(QString("pkg"), QStringList() << "rquery" << "-a" << "%o:::%n-%v:::%c:::%sh:::%m:::%w");
477  else
478    getNGProc->start(QString("chroot"), QStringList() << wDir << "pkg" << "rquery" << "-a" << "%o:::%n-%v:::%c:::%sh:::%m:::%w");
479
480}
481
482void mainWin::slotGetNGPackageDataOutput()
483{
484   while (getNGProc->canReadLine())
485     tmpPkgList << getNGProc->readLine().simplified();
486}
487
488void mainWin::slotGetNGInstalledDataOutput()
489{
490   while (getNGProc->canReadLine())
491     pkgList << getNGProc->readLine().simplified();
492}
493
494void mainWin::slotGetNGInstalledPkgs() {
495
496  qDebug() << "Building dependancy lists...";
497  QProcess p;
498  pkgDepList.clear();
499  if ( wDir.isEmpty() )
500    p.start("pkg", QStringList() << "rquery" << "-a" << "%n-%v:::%dn-%dv");
501  else
502    p.start("chroot", QStringList() << wDir << "pkg" << "rquery" << "-a" << "%n-%v:::%dn-%dv" );
503  while(p.state() == QProcess::Starting || p.state() == QProcess::Running) {
504      p.waitForFinished(200);
505      QCoreApplication::processEvents();
506  }
507  while (p.canReadLine()) {
508    pkgDepList << p.readLine().simplified();
509  }
510
511  qDebug() << "Building reverse dependancy lists...";
512  pkgRDepList.clear();
513  if ( wDir.isEmpty() )
514    p.start("pkg", QStringList() << "rquery" << "-a" << "%n-%v:::%rn-%rv");
515  else
516    p.start("chroot", QStringList() << wDir << "pkg" << "rquery" << "-a" << "%n-%v:::%rn-%rv" );
517  while(p.state() == QProcess::Starting || p.state() == QProcess::Running) {
518      p.waitForFinished(200);
519      QCoreApplication::processEvents();
520  }
521  while (p.canReadLine()) {
522    pkgRDepList << p.readLine().simplified();
523  }
524
525  getNGProc = new QProcess();
526  qDebug() << "Searching for installed pkgs...";
527  connect( getNGProc, SIGNAL(readyReadStandardOutput()), this, SLOT(slotGetNGInstalledDataOutput()) );
528  connect( getNGProc, SIGNAL(finished(int, QProcess::ExitStatus)), this, SLOT(slotFinishLoadingNGPkgs()) );
529  getNGProc->setProcessChannelMode(QProcess::MergedChannels);
530  if ( wDir.isEmpty() )
531    getNGProc->start(QString("pkg"), QStringList() << "info" << "-aq" );
532  else
533    getNGProc->start(QString("chroot"), QStringList() << wDir << "pkg" << "info" << "-aq");
534
535}
536
537void mainWin::slotFinishLoadingNGPkgs()
538{
539  treeNGPkgs->clear();
540
541  addNGItems();
542
543  pushPkgApply->setEnabled(false);
544
545  connect(treeNGPkgs, SIGNAL(itemChanged(QTreeWidgetItem *, int)), this, SLOT(slotEnableApply()));
546  connect(treeNGPkgs, SIGNAL(currentItemChanged(QTreeWidgetItem *, QTreeWidgetItem *)), this, SLOT(slotNGItemChanged()));
547
548  // Now we can look for updates safely
549  slotRescanPkgsClicked();
550}
551
552void mainWin::slotNGItemChanged()
553{
554  if ( ! treeNGPkgs->currentItem() ) {
555     groupInfo->setVisible(false);
556     return;
557  }
558  QString desc, size, maint, weburl;
559
560  QTreeWidgetItem *cItem = treeNGPkgs->currentItem();
561  QString pName = cItem->text(0).section("(", 1, 1).section(")", 0, 0);
562  if ( pName.isEmpty() ) {
563     groupInfo->setVisible(false);
564     return;
565  }
566  qDebug() << "Checking: " + pName;
567
568  QRegExp rx("*:::" + pName + ":::*");
569  rx.setPatternSyntax(QRegExp::Wildcard);
570  int pAt = tmpPkgList.indexOf(rx);
571  if (pAt == -1 ) {
572     qDebug() << "Unable to find package: " + pName;
573     groupInfo->setVisible(false);
574     return;
575  }
576
577  desc = tmpPkgList.at(pAt).section(":::", 2,2);
578  size = tmpPkgList.at(pAt).section(":::", 3,3);
579  maint = tmpPkgList.at(pAt).section(":::", 4,4);
580  weburl = tmpPkgList.at(pAt).section(":::", 5,5);
581  labelPkgNameVer->setText(pName);
582  labelSize->setText(size);
583  labelWeb->setText(weburl);
584  textDesc->setText(desc);
585  textOptions->clear();
586
587  QCoreApplication::processEvents();
588
589  // Display the depends
590  QString depTxt;
591  QRegExp rxd( pName + ":::*");
592  rxd.setPatternSyntax(QRegExp::Wildcard);
593  QStringList aDeps = pkgDepList.filter(rxd);
594  for ( int r=0; r < aDeps.size(); ++r) {
595      QString dName = aDeps.at(r).section(":::", 1, 1);
596      // Is this package installed?
597      if ( pkgList.indexOf(dName) != -1 )
598         depTxt+= dName + " (Installed)\n";
599      else
600         depTxt+= dName + "\n";
601  }
602
603  textDeps->setText(depTxt);
604
605  groupInfo->setVisible(true);
606
607  getNGInfo = new QProcess();
608  qDebug() << "Getting Info for " + pName;
609  connect( getNGInfo, SIGNAL(readyReadStandardOutput()), this, SLOT(slotNGReadInfo()) );
610  getNGInfo->setProcessChannelMode(QProcess::MergedChannels);
611  if ( wDir.isEmpty() )
612    getNGInfo->start(QString("pkg"), QStringList() << "rquery" << "%Ok=%Ov" << pName );
613  else
614    getNGInfo->start(QString("chroot"), QStringList() << wDir << "pkg" << "rquery" << "%Ok=%Ov" << pName);
615}
616
617void mainWin::slotNGReadInfo()
618{
619  while (getNGInfo->canReadLine())
620     textOptions->append(getNGInfo->readLine().simplified() );
621
622  textOptions->moveCursor(QTextCursor::Start);
623}
624
625void mainWin::slotEnableApply()
626{
627  pushPkgApply->setEnabled(true);
628}
629
630void mainWin::addNGItems()
631{
632   QString curCat, cat, name, pkgname, desc, size, maint, weburl;
633
634   // We like to add alphabetically
635   tmpPkgList.sort();
636
637   QTreeWidgetItem *catItem = new QTreeWidgetItem;
638
639   // Lets start adding packages to the tree widget
640   for (int i = 0; i < tmpPkgList.size(); ++i) {
641        name = cat = tmpPkgList.at(i).section(":::", 0,0);
642        cat=cat.section("/", 0, 0);
643        name=name.section("/", 1, 1);
644        pkgname = tmpPkgList.at(i).section(":::", 1,1);
645        desc = tmpPkgList.at(i).section(":::", 2,2);
646        size = tmpPkgList.at(i).section(":::", 3,3);
647        maint = tmpPkgList.at(i).section(":::", 4,4);
648        weburl = tmpPkgList.at(i).section(":::", 5,5);
649
650        // Check if we need to add a top-level category
651        if ( cat != curCat )
652        {
653           qDebug() << "Adding cat: " + cat;
654           catItem = new QTreeWidgetItem(QStringList() << cat);
655           treeNGPkgs->addTopLevelItem(catItem);
656           curCat = cat;
657        }
658 
659        // Now lets create the item and attach to the category
660        QTreeWidgetItem *pkgItem = new QTreeWidgetItem();
661        pkgItem->setText(0, name + " (" + pkgname + ") - " + size );
662        pkgItem->setToolTip(0, desc);
663
664        if ( pkgList.indexOf(pkgname) != -1 ) {
665          pkgItem->setCheckState(0, Qt::Checked);
666          selPkgList << pkgname;
667        } else
668          pkgItem->setCheckState(0, Qt::Unchecked);
669 
670        catItem->addChild(pkgItem);
671   }
672
673}
674
675// Lets prompt user, and do it!
676void mainWin::applyNGChanges()
677{
678   QString tmp;
679   QStringList curPkgChecked;
680   QStringList newPkgs;
681   QStringList rmPkgs;
682
683   QTreeWidgetItemIterator it(treeNGPkgs);
684   while (*it) {
685         if ((*it)->checkState(0) == Qt::Checked) {
686           tmp = (*it)->text(0).section("(", 1, 1).section(")", 0, 0);
687           curPkgChecked << tmp;
688           if (pkgList.indexOf(tmp) == -1 ) 
689              newPkgs << tmp;
690         }
691         ++it;
692   }
693
694   for ( int i=0; i < pkgList.size(); ++i)
695      // Has this package been unchecked?
696      if (curPkgChecked.indexOf(pkgList.at(i)) == -1 )  {
697         // Make sure this is a package in the repo
698         // This filters out any custom packages the user may have loaded which may not exist in our repo
699         QRegExp rx("*" + pkgList.at(i) + "*");
700         rx.setPatternSyntax(QRegExp::Wildcard);
701         if ( tmpPkgList.indexOf(rx) != -1 )
702           rmPkgs << pkgList.at(i);
703      }
704
705   if ( rmPkgs.isEmpty() && newPkgs.isEmpty() ) {
706      QMessageBox::warning(this, tr("No changes"),
707        tr("No changes to make!"),
708        QMessageBox::Ok,
709        QMessageBox::Ok);
710      return;
711   }
712
713   qDebug() << "Added packages" << newPkgs;
714   qDebug() << "Removed packages" << rmPkgs;
715   pkgRemoveList = rmPkgs;
716   pkgAddList = newPkgs;
717
718   QString confirmText;
719
720   // Lets start creating our confirmation text
721   if ( ! rmPkgs.isEmpty() ) {
722      confirmText+=tr("The following packages will be removed:") + "\n"; 
723      confirmText+= "------------------------------------------\n";
724      confirmText+=rmPkgs.join("\n"); 
725      confirmText+= "\n\n" + tr("The following packages that require the above packages will also removed:") + "\n"; 
726      confirmText+= "------------------------------------------\n";
727      for ( int i=0; i < rmPkgs.size(); ++i) {
728         QRegExp rx(rmPkgs.at(i) + ":::*");
729         rx.setPatternSyntax(QRegExp::Wildcard);
730         QStringList rDeps = pkgRDepList.filter(rx);
731         for ( int r=0; r < rDeps.size(); ++r) {
732             QString pName = rDeps.at(r).section(":::", 1, 1); 
733             // Is this package installed?
734             if ( pkgList.indexOf(pName) != -1 )
735               confirmText+= pName + " ";
736         }
737      }
738   }
739
740   if ( ! newPkgs.isEmpty() ) {
741      if ( ! rmPkgs.isEmpty() )
742        confirmText+= "\n\n";
743      confirmText+=tr("The following packages will be installed:") + "\n"; 
744      confirmText+= "------------------------------------------\n";
745      confirmText+=newPkgs.join("\n"); 
746      confirmText+= "\n\n" + tr("The following dependances will also be installed:") + "\n"; 
747      confirmText+= "------------------------------------------\n";
748      for ( int i=0; i < newPkgs.size(); ++i) {
749         QRegExp rx(newPkgs.at(i) + ":::*");
750         rx.setPatternSyntax(QRegExp::Wildcard);
751         QStringList aDeps = pkgDepList.filter(rx);
752         for ( int r=0; r < aDeps.size(); ++r) {
753             QString pName = aDeps.at(r).section(":::", 1, 1); 
754             // Is this package installed?
755             if ( pkgList.indexOf(pName) == -1 )
756               confirmText+= pName + " ";
757         }
758      }
759   }
760
761   // Launch our AddPartitionDialog to add a new device
762   askUserConfirm = new dialogConfirm();
763   connect(askUserConfirm, SIGNAL(ok()),this, SLOT(slotStartNGChanges()) );
764   askUserConfirm->programInit(tr("Confirm package changes"));
765   askUserConfirm->setInfoText(QString(confirmText));
766   askUserConfirm->exec();
767
768}
769
770
771// Time to start doing our NG changes!
772void mainWin::slotStartNGChanges()
773{
774  // Init the pkg process
775  prepPkgProcess();
776
777  // Create our runlist of package commands
778  QStringList pCmds;
779 
780  if ( ! pkgRemoveList.isEmpty() ) {
781    if ( wDir.isEmpty() )
782      pCmds << "pkg" << "delete" << "-R" << "-y" << pkgRemoveList.join(" ");
783    else
784      pCmds << "chroot" << wDir << "pkg" << "delete" << "-R" << "-y" << pkgRemoveList.join(" ");
785    pkgCmdList << pCmds;
786  }
787         
788  pCmds.clear();
789
790  if ( ! pkgAddList.isEmpty() ) {
791    if ( wDir.isEmpty() )
792      pCmds << "pc-pkg" << "install" << "-y" << pkgAddList.join(" ");
793    else
794      pCmds << "chroot" << wDir << "pc-pkg" << "install" << "-y" << pkgAddList.join(" ");
795    pkgCmdList << pCmds;
796  }
797
798  // Lets kick it off now
799  startPkgProcess();
800}
801
802// Display found meta-pkg data
803void mainWin::populateMetaPkgs()
804{
805  pushPkgApply->setEnabled(false);
806  treeMetaPkgs->clear();
807  tmpMetaPkgList.clear();
808  new QTreeWidgetItem(treeMetaPkgs, QStringList() << tr("Loading... Please wait...") );
809
810  if ( ! metaPkgList.isEmpty() )
811        disconnect(treeMetaPkgs, SIGNAL(itemChanged(QTreeWidgetItem *, int)), 0, 0);
812  metaPkgList.clear();
813
814  // Start the process to get meta-pkg info
815  getMetaProc = new QProcess();
816  qDebug() << "Searching for meta-pkgs...";
817  connect( getMetaProc, SIGNAL(readyReadStandardOutput()), this, SLOT(slotGetPackageDataOutput()) );
818  connect( getMetaProc, SIGNAL(finished(int, QProcess::ExitStatus)), this, SLOT(slotFinishLoadingMetaPkgs()) );
819  getMetaProc->setProcessChannelMode(QProcess::MergedChannels);
820  getMetaProc->start(QString("pc-metapkgmanager"), QStringList() << "list");
821
822}
823
824// Display found meta-pkg data
825void mainWin::slotFinishLoadingMetaPkgs()
826{
827
828  // Populate the metaPkgList
829  parseTmpMetaList();
830
831  treeMetaPkgs->clear();
832
833  addTreeItems(QString()); 
834
835  pushPkgApply->setEnabled(false);
836
837  connect(treeMetaPkgs, SIGNAL(itemChanged(QTreeWidgetItem *, int)), this, SLOT(slotDeskPkgsChanged(QTreeWidgetItem *, int)));
838
839  // Now we can look for updates safely
840  slotRescanPkgsClicked();
841}
842
843void mainWin::addTreeItems(QString parent)
844{
845  for (int z=0; z < metaPkgList.count(); ++z) {
846    if ( metaPkgList.at(z).at(3) != parent )
847      continue;
848
849    // Hide the "base-system" package, since we are installing it anyway
850    if (metaPkgList.at(z).at(0) == "base-system" )
851      return;
852
853    QTreeWidgetItem *deskItem = new QTreeWidgetItem(QStringList() << metaPkgList.at(z).at(0) );
854    deskItem->setIcon(0, QIcon(metaPkgList.at(z).at(2)));
855    deskItem->setToolTip(0, metaPkgList.at(z).at(1));
856    deskItem->setCheckState(0, Qt::Unchecked);
857
858    if ( metaPkgList.at(z).at(5) == "YES" )
859      deskItem->setCheckState(0, Qt::Checked);
860
861    if ( parent.isEmpty() ) {
862      treeMetaPkgs->addTopLevelItem(deskItem);
863    } else {
864      // Locate the parent to attach to
865      QTreeWidgetItemIterator it(treeMetaPkgs);
866      while (*it) {
867        if ((*it)->text(0) == parent ) {
868          (*it)->addChild(deskItem);
869          if ( metaPkgList.at(z).at(5) == "YES" && (*it)->checkState(0) == Qt::Unchecked)
870            (*it)->setCheckState(0, Qt::PartiallyChecked);
871          if ( metaPkgList.at(z).at(5) == "NO" && (*it)->checkState(0) == Qt::Checked)
872            (*it)->setCheckState(0, Qt::PartiallyChecked);
873          break;
874        }
875        it++;
876      }
877    }
878
879    // Now look for any possible children
880    addTreeItems(metaPkgList.at(z).at(0));   
881  }
882}
883
884// Check if a meta-pkg is installed
885bool mainWin::isMetaPkgInstalled(QString mPkg)
886{
887  QString tmp;
888  QProcess pcmp;
889  pcmp.start(QString("pc-metapkgmanager"), QStringList() << chrootArg1 << chrootArg2 << "status" << mPkg);
890  while ( pcmp.state() != QProcess::NotRunning ) {
891     pcmp.waitForFinished(50);
892     QCoreApplication::processEvents();
893  }
894
895  while (pcmp.canReadLine()) {
896     tmp = pcmp.readLine().simplified();
897     if ( tmp.indexOf("is installed") != -1 )
898     return true;
899  }
900
901  return false;
902}
903
904// Function which checks for our GUI package schema data
905void mainWin::slotGetPackageDataOutput()
906{
907  while (getMetaProc->canReadLine())
908        tmpMetaPkgList << getMetaProc->readLine().simplified();
909}
910
911// Parse the pc-metapkg saved output
912void mainWin::parseTmpMetaList()
913{
914  QString tmp, mName, mDesc, mIcon, mParent, mDesktop, mInstalled, mPkgFileList;
915  QStringList package;
916
917  for ( int i = 0 ; i < tmpMetaPkgList.size(); i++ )
918  {
919        QApplication::processEvents();
920
921        tmp = tmpMetaPkgList.at(i);
922
923        if ( tmp.indexOf("Meta Package: ") == 0) {
924                mName = tmp.replace("Meta Package: ", "");
925                continue;
926        }
927        if ( tmp.indexOf("Description: ") == 0) {
928                mDesc = tmp.replace("Description: ", "");
929                continue;
930        }
931        if ( tmp.indexOf("Icon: ") == 0) {
932                mIcon = tmp.replace("Icon: ", "");
933                mPkgFileList = mIcon;
934                mPkgFileList.replace("pkg-icon.png", "pkg-list");
935                continue;
936        }
937        if ( tmp.indexOf("Parent: ") == 0) {
938                mParent = tmp.replace("Parent: ", "");
939                continue;
940        }
941        if ( tmp.indexOf("Desktop: ") == 0) {
942                mDesktop = tmp.replace("Desktop: ", "");
943                continue;
944        }
945
946        // This is an empty category
947        if ( tmp.indexOf("Category Entry") == 0) {
948                // Now add this category to the string list
949                package.clear();
950                qDebug() << "Found Package" << mName << mDesc << mIcon << mParent << mDesktop;
951                mInstalled = "CATEGORY";
952                package << mName << mDesc << mIcon << mParent << mDesktop << mInstalled;
953                metaPkgList.append(package);
954                mName=""; mDesc=""; mIcon=""; mParent=""; mDesktop=""; mInstalled=""; mPkgFileList="";
955        }
956
957        // We have a Meta-Pkg
958        if ( tmp.indexOf("Required Packages:") == 0) {
959                // Now add this meta-pkg to the string list
960                package.clear();
961                qDebug() << "Found Package" << mName << mDesc << mIcon << mParent << mDesktop << mPkgFileList;
962
963                if ( isMetaPkgInstalled(mName) )
964                        mInstalled = "YES";
965                else
966                        mInstalled = "NO";
967
968                package << mName << mDesc << mIcon << mParent << mDesktop << mInstalled << mPkgFileList;
969                metaPkgList.append(package);
970                mName=""; mDesc=""; mIcon=""; mParent=""; mDesktop=""; mInstalled=""; mPkgFileList="";
971        }
972
973    }
974
975}
976
977void mainWin::saveMetaPkgs()
978{
979        if ( ! haveMetaPkgChanges() )
980                return;
981
982        addPkgs = getAddPkgs();
983        delPkgs = getDelPkgs(); 
984
985        startMetaChanges();
986
987}
988
989void mainWin::startMetaChanges()
990{
991
992  // Init the pkg process
993  prepPkgProcess();
994  // Create our runlist of package commands
995  QStringList pCmds;
996
997  if ( ! delPkgs.isEmpty() ) {
998    if ( wDir.isEmpty() )
999      pCmds << "pc-metapkgmanager" << "del" << delPkgs;
1000    else 
1001      pCmds << "chroot" << wDir << "pc-metapkgmanager" << "del" << delPkgs;
1002    pkgCmdList << pCmds;
1003  }
1004 
1005  pCmds.clear();
1006
1007  if ( ! addPkgs.isEmpty() ) {
1008    if ( wDir.isEmpty() )
1009      pCmds << "pc-metapkgmanager" << "add" << addPkgs;
1010    else 
1011      pCmds << "chroot" << wDir << "pc-metapkgmanager" << "add" << addPkgs;
1012    pkgCmdList << pCmds;
1013  }
1014
1015  // Lets kick it off now
1016  startPkgProcess();
1017}
1018
1019bool mainWin::haveAMetaDesktop()
1020{
1021        // If running in a chroot we can skip this check
1022        if ( ! chrootArg1.isEmpty() )
1023          return true;
1024       
1025        QTreeWidgetItemIterator it(treeMetaPkgs);
1026        while (*it) {
1027         if ( ((*it)->checkState(0) == Qt::Checked) || ((*it)->checkState(0) == Qt::PartiallyChecked) )
1028           for (int z=0; z < metaPkgList.count(); ++z)
1029             if ( (*it)->text(0) == metaPkgList.at(z).at(0) && metaPkgList.at(z).at(4) == "YES" )
1030                return true;
1031         ++it;
1032        }
1033
1034        QMessageBox::warning(this, tr("No Desktop"),
1035          tr("No desktops have been selected! Please choose at least one desktop before saving."),
1036          QMessageBox::Ok,
1037          QMessageBox::Ok);
1038
1039        return false;
1040}
1041
1042bool mainWin::haveMetaPkgChanges()
1043{
1044        QTreeWidgetItemIterator it(treeMetaPkgs);
1045        while (*it) {
1046          for (int z=0; z < metaPkgList.count(); ++z)
1047            // See if any packages status have changed
1048            if ( ( (*it)->text(0) == metaPkgList.at(z).at(0) && metaPkgList.at(z).at(5) == "YES" && (*it)->checkState(0) == Qt::Unchecked ) \
1049              || ( (*it)->text(0) == metaPkgList.at(z).at(0) && metaPkgList.at(z).at(5) == "YES" && (*it)->checkState(0) == Qt::PartiallyChecked ) \
1050              || ( (*it)->text(0) == metaPkgList.at(z).at(0) && metaPkgList.at(z).at(5) == "NO" && (*it)->checkState(0) == Qt::Checked ) )
1051                return true;
1052         ++it;
1053        }
1054
1055        return false;
1056}
1057
1058QString mainWin::getAddPkgs()
1059{
1060        QString tmp;
1061        QTreeWidgetItemIterator it(treeMetaPkgs);
1062        while (*it) {
1063          for (int z=0; z < metaPkgList.count(); ++z)
1064            // See if any packages status have changed
1065            if ( ( (*it)->text(0) == metaPkgList.at(z).at(0) && metaPkgList.at(z).at(5) == "NO" && (*it)->checkState(0) == Qt::Checked ) || \
1066                 ( (*it)->text(0) == metaPkgList.at(z).at(0) && metaPkgList.at(z).at(5) == "NO" && (*it)->checkState(0) == Qt::PartiallyChecked ) )
1067                if ( tmp.isEmpty() )
1068                        tmp = (*it)->text(0);
1069                else
1070                        tmp = tmp + "," + (*it)->text(0);
1071         ++it;
1072        }
1073
1074        return tmp;
1075}
1076
1077QString mainWin::getDelPkgs()
1078{
1079        QString tmp;
1080        QTreeWidgetItemIterator it(treeMetaPkgs);
1081        while (*it) {
1082          for (int z=0; z < metaPkgList.count(); ++z)
1083            // See if any packages status have changed
1084            if ( (*it)->text(0) == metaPkgList.at(z).at(0) && metaPkgList.at(z).at(5) == "YES" && (*it)->checkState(0) == Qt::Unchecked )
1085                if ( tmp.isEmpty() )
1086                        tmp = (*it)->text(0);
1087                else
1088                        tmp = tmp + "," + (*it)->text(0);
1089         ++it;
1090        }
1091
1092        return tmp;
1093}
1094
1095
1096// Time to save meta-pkgs
1097void mainWin::slotApplyMetaChanges() {
1098    saveMetaPkgs();
1099}
1100
1101
1102
1103// The User changed the tree widget checked / unchecked stuff sanity check
1104void mainWin::slotDeskPkgsChanged(QTreeWidgetItem *aItem, int __unused)
1105{
1106        if (!aItem)
1107          return;
1108
1109        disconnect(treeMetaPkgs, SIGNAL(itemChanged(QTreeWidgetItem *, int)), 0, 0);
1110
1111        if (aItem->childCount() == 0) {
1112                if (aItem->checkState(0) == Qt::Checked && aItem->parent() )
1113                        if ( allChildrenPkgsChecked(aItem->parent()->text(0)))
1114                                aItem->parent()->setCheckState(0, Qt::Checked); 
1115                        else
1116                                aItem->parent()->setCheckState(0, Qt::PartiallyChecked);       
1117                if (aItem->checkState(0) == Qt::Unchecked && aItem->parent() )
1118                        if ( ! allChildrenPkgsUnchecked(aItem->parent()->text(0)))
1119                                aItem->parent()->setCheckState(0, Qt::PartiallyChecked);       
1120
1121
1122        } else {
1123                if (aItem->checkState(0) == Qt::Checked )
1124                        checkAllChildrenPkgs(aItem->text(0));
1125                else
1126                        uncheckAllChildrenPkgs(aItem->text(0));
1127        }
1128       
1129
1130    connect(treeMetaPkgs, SIGNAL(itemChanged(QTreeWidgetItem *, int)), this, SLOT(slotDeskPkgsChanged(QTreeWidgetItem *, int)));
1131
1132        if ( haveMetaPkgChanges() )
1133                pushPkgApply->setEnabled(true);
1134        else
1135                pushPkgApply->setEnabled(false);
1136}
1137
1138// Check the "parent" app to see if all its children are checked or not
1139bool mainWin::allChildrenPkgsChecked(QString parent)
1140{
1141        QTreeWidgetItemIterator it(treeMetaPkgs);
1142        while (*it) {
1143         if ((*it)->text(0) == parent ) {
1144           if ( (*it)->childCount() <= 0)
1145             return true;
1146
1147           for ( int i = 0; i < (*it)->childCount() ; ++i) {
1148             if ( ! allChildrenPkgsChecked((*it)->child(i)->text(0)))
1149               return false;
1150
1151             if ((*it)->child(i)->checkState(0) != Qt::Checked ) 
1152               return false;
1153           }
1154         }
1155         ++it;
1156        }
1157        return true;
1158}
1159
1160// Check the "parent" app to see if all its children are unchecked or not
1161bool mainWin::allChildrenPkgsUnchecked(QString parent)
1162{
1163        QTreeWidgetItemIterator it(treeMetaPkgs);
1164        while (*it) {
1165         if ((*it)->text(0) == parent ) {
1166           if ( (*it)->childCount() <= 0)
1167             return true;
1168
1169           for ( int i = 0; i < (*it)->childCount() ; ++i) {
1170             if ( ! allChildrenPkgsUnchecked((*it)->child(i)->text(0)))
1171               return false;
1172
1173             if ((*it)->child(i)->checkState(0) != Qt::Unchecked ) 
1174               return false;
1175           }
1176         }
1177         ++it;
1178        }
1179        return true;
1180}
1181
1182// Check all children of parent
1183void mainWin::checkAllChildrenPkgs(QString parent)
1184{
1185        QTreeWidgetItemIterator it(treeMetaPkgs);
1186        while (*it) {
1187         if (! (*it)->parent()) {
1188           ++it;
1189           continue;
1190         } 
1191
1192         // Lets walk the tree see what pops up
1193         bool pFound=false;
1194         QTreeWidgetItem *itP = (*it)->parent();
1195         do {
1196           pFound=false;
1197           if (itP->text(0) == parent) {
1198             (*it)->setCheckState(0, Qt::Checked);
1199             break;
1200           }
1201           if ( itP->parent() ) {
1202             itP = itP->parent();
1203             pFound=true;
1204           }
1205         } while (pFound);
1206
1207         ++it;
1208       }
1209}
1210
1211// UnCheck all children of parent
1212void mainWin::uncheckAllChildrenPkgs(QString parent)
1213{
1214        QTreeWidgetItemIterator it(treeMetaPkgs);
1215        while (*it) {
1216         if (! (*it)->parent()) {
1217           ++it;
1218           continue;
1219         } 
1220
1221         // Lets walk the tree see what pops up
1222         bool pFound=false;
1223         QTreeWidgetItem *itP = (*it)->parent();
1224         do {
1225           pFound=false;
1226           if (itP->text(0) == parent) {
1227             (*it)->setCheckState(0, Qt::Unchecked);
1228             break;
1229           }
1230           if ( itP->parent() ) {
1231             itP = itP->parent();
1232             pFound=true;
1233           }
1234         } while (pFound);
1235
1236         ++it;
1237       }
1238}
1239
1240void mainWin::slotMetaRightClick()
1241{
1242        QTreeWidgetItemIterator it(treeMetaPkgs);
1243        while (*it) {
1244          for (int z=0; z < metaPkgList.count(); ++z) {
1245            if ( (*it)->isSelected() && (*it)->text(0) == metaPkgList.at(z).at(0) ) {
1246              if (metaPkgList.at(z).at(5) == "CATEGORY")
1247                return;
1248              popup = new QMenu;
1249              popup->setTitle((*it)->text(0));
1250              popup->addAction(tr("View Packages"), this, SLOT(slotMetaViewPkgs()));
1251              popup->exec( QCursor::pos() );
1252            }
1253          }
1254         ++it;
1255        }
1256}
1257
1258void mainWin::slotMetaViewPkgs()
1259{
1260        QStringList packageList;
1261        QTreeWidgetItemIterator it(treeMetaPkgs);
1262        while (*it) {
1263          for (int z=0; z < metaPkgList.count(); ++z) {
1264            if ( (*it)->isSelected() && (*it)->text(0) == metaPkgList.at(z).at(0) ) {
1265 
1266                QFile pList(metaPkgList.at(z).at(6));
1267                if ( ! pList.exists() )
1268                  return;
1269               
1270                if ( ! pList.open(QIODevice::ReadOnly | QIODevice::Text))
1271                  return;
1272
1273                while ( !pList.atEnd() )
1274                  packageList << pList.readLine().simplified();
1275
1276                pList.close();
1277                packageList.sort();
1278                       
1279                dIB = new dialogInfo();
1280                dIB->programInit(tr("Package Listing for:") + " " + (*it)->text(0));
1281                dIB->setInfoText(packageList.join("\n"));
1282                dIB->show();
1283            }
1284          }
1285         ++it;
1286        }
1287}
1288
1289QString mainWin::getLineFromCommandOutput( QString cmd )
1290{
1291        FILE *file = popen(cmd.toLatin1(),"r");
1292 
1293        char buffer[100];
1294 
1295        QString line = "";
1296        char firstChar;
1297
1298        if ((firstChar = fgetc(file)) != -1){
1299                line += firstChar;
1300                line += fgets(buffer,100,file);
1301        }
1302        pclose(file);
1303        return line.simplified();
1304}
Note: See TracBrowser for help on using the repository browser.