source: src-qt4/pc-pkgmanager/mainWin.cpp @ 2c1e079

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

Add ability to get pkg status from pkgng output, will display progress
bar of current / total to go.

  • Property mode set to 100644
File size: 37.3 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   int curItem, totItem;
269   bool ok;
270
271   while (uProc->canReadLine()) {
272     line = uProc->readLine().simplified();
273     qDebug() << line;
274
275     tmp = line;
276     tmp.truncate(50);
277
278     // Flags we can parse out and not show the user
279
280     // Check if we have crashed into a conflict and ask the user what to do
281     if ( line.indexOf("PKGCONFLICTS: ") == 0 ) {
282        tmp = line; 
283        tmp.replace("PKGCONFLICTS: ", "");
284        ConflictList = tmp;
285        continue;
286     }
287     if ( line.indexOf("PKGREPLY: ") == 0 ) {
288        QString ans;
289        tmp = line; 
290        tmp.replace("PKGREPLY: ", "");
291        QMessageBox msgBox;
292        msgBox.setText(tr("The following packages are causing conflicts with the selected changes and can be automatically removed. Continue?") + "\n" + ConflictList);
293        msgBox.setStandardButtons(QMessageBox::Yes|QMessageBox::No);
294        msgBox.setDetailedText(getConflictDetailText());
295        msgBox.setDefaultButton(QMessageBox::No);
296        if ( msgBox.exec() == QMessageBox::Yes) {
297          // We will try to fix conflicts
298          ans="yes";
299        } else {
300          // We will fail :(
301          QMessageBox::warning(this, tr("Package Conflicts"),
302          tr("You may need to manually fix the conflicts before trying again."),
303          QMessageBox::Ok,
304          QMessageBox::Ok);
305          ans="no";
306        }
307
308        QFile pkgTrig( tmp );
309        if ( pkgTrig.open( QIODevice::WriteOnly ) ) {
310           QTextStream streamTrig( &pkgTrig );
311           streamTrig << ans;
312           pkgTrig.close();
313        }
314        continue;
315     }
316
317     if ( line.indexOf("FETCH: ") == 0 ) { 
318        progressUpdate->setValue(progressUpdate->value() + 1); 
319        tmp = line; 
320        tmp = tmp.remove(0, tmp.lastIndexOf("/") + 1); 
321        progressUpdate->setRange(0, 0);
322        progressUpdate->setValue(0);
323        curFileText = tr("Downloading: %1").arg(tmp); 
324        textStatus->setText(tr("Downloading: %1").arg(tmp)); 
325        continue;
326     } 
327     if ( line.indexOf("FETCHDONE") == 0 )
328        continue;
329
330     if ( line.indexOf("SIZE: ") == 0 ) {
331          bool ok, ok2;
332          line.replace("SIZE: ", "");
333          line.replace("DOWNLOADED: ", "");
334          line.replace("SPEED: ", "");
335          line.section(" ", 0, 0).toInt(&ok);
336          line.section(" ", 1, 1).toInt(&ok2);
337   
338          if ( ok && ok2 ) {
339            QString unit;
340            int tot = line.section(" ", 0, 0).toInt(&ok);
341            int cur = line.section(" ", 1, 1).toInt(&ok2);
342            QString percent = QString::number( (float)(cur * 100)/tot );
343            QString speed = line.section(" ", 2, 3);
344
345            // Get the MB downloaded / total
346            if ( tot > 2048 ) {
347              unit="MB";
348              tot = tot / 1024;
349              cur = cur / 1024;
350            } else {
351              unit="KB";
352            }
353
354            QString ProgressString=QString("(%1" + unit + " of %2" + unit + " at %3)").arg(cur).arg(tot).arg(speed);
355            progressUpdate->setRange(0, tot);
356            progressUpdate->setValue(cur);
357            textStatus->setText(curFileText + " " + ProgressString); 
358         }
359         continue;
360     }
361
362
363     // Now show output on GUI
364     textDisplayOut->insertPlainText(line + "\n");
365     textDisplayOut->moveCursor(QTextCursor::End);
366
367
368     // Any other flags to look for?
369     /////////////////////////////////////////////////////
370     if ( line.indexOf("to be downloaded") != -1 ) {
371       textStatus->setText(tr("Downloading packages..."));
372       curUpdate = 0;
373       progressUpdate->setValue(0);
374       continue;
375     }
376     if ( line.indexOf("Checking integrity") == 0 ) {
377       textStatus->setText(line);
378       uPackages = true;
379       dPackages = false;
380       curUpdate = 0;
381       progressUpdate->setValue(0);
382       progressUpdate->setRange(0, 0);
383       progressUpdate->setValue(0);
384       continue;
385     }
386     
387     if ( uPackages ) {
388       if ( line.indexOf("[") == 0 ) {
389         tmp=line.section("]", 1, 1);
390         textStatus->setText(tmp);
391         tmp=line.section("/", 0, 0).replace("[", "");
392         tmp.toInt(&ok);
393         if (ok)  {
394           curItem=tmp.toInt(&ok);
395           tmp=line.section("/", 1, 1).section("]", 0, 0);
396           tmp.toInt(&ok);
397           if (ok)  {
398             totItem=tmp.toInt(&ok);
399             progressUpdate->setRange(0, totItem);
400             progressUpdate->setValue(curItem);
401           }
402
403         }
404       }
405       continue;
406     }
407
408   } // end of while
409}
410
411void mainWin::slotPkgDone() {
412
413  if ( uProc->exitCode() != 0 )
414    pkgHasFailed=true;
415
416  // Run the next command on the stack if necessary
417  if (  pkgCmdList.size() > 1 ) {
418        pkgCmdList.removeAt(0); 
419        startPkgProcess();     
420        return;
421  }
422
423  // Nothing left to run! Lets wrap up
424  QFile sysTrig( SYSTRIGGER );
425  if ( sysTrig.open( QIODevice::WriteOnly ) ) {
426    QTextStream streamTrig( &sysTrig );
427     streamTrig << "INSTALLFINISHED: ";
428  }
429
430  if ( pkgHasFailed ) {
431    QFile file( "/tmp/pkg-output.log" );
432    if ( file.open( QIODevice::WriteOnly ) ) {
433       QTextStream stream( &file );
434       stream << textDisplayOut->toPlainText();
435       file.close();
436    }
437    QMessageBox::warning(this, tr("Failed!"), tr("The package commands failed. A copy of the output was saved to /tmp/pkg-output.log"));
438  } else
439    QMessageBox::warning(this, tr("Finished!"), tr("Package changes complete!" ));
440
441  // Clear out the old commands
442  pkgCmdList.clear();
443
444  // Switch back to our main display
445  stackedTop->setCurrentIndex(0);
446 
447  // Re-init the meta-widget
448  initMetaWidget();
449
450}
451
452/*****************************************
453Code for package stuff
454******************************************/
455
456void mainWin::initMetaWidget()
457{
458  qDebug() << "Starting metaWidget...";
459  groupInfo->setVisible(false);
460
461  // Running in basic mode
462  if ( stackedPkgView->currentIndex() == 0 )
463  {
464    populateMetaPkgs();
465    // Connect our slots
466  } else {
467    // Running in advanced mode
468    populateNGPkgs();
469  }
470}
471
472void mainWin::populateNGPkgs()
473{
474  pushPkgApply->setEnabled(false);
475  treeNGPkgs->clear();
476  tmpPkgList.clear();
477  new QTreeWidgetItem(treeNGPkgs, QStringList() << tr("Loading... Please wait...") );
478
479  if ( ! pkgList.isEmpty() ) {
480        disconnect(treeNGPkgs, SIGNAL(itemChanged(QTreeWidgetItem *, int)), 0, 0);
481        disconnect(treeNGPkgs, SIGNAL(currentItemChanged(QTreeWidgetItem *, QTreeWidgetItem *)), 0, 0);
482  }
483  pkgList.clear();
484  selPkgList.clear();
485
486  // Start the process to get meta-pkg info
487  getNGProc = new QProcess();
488  qDebug() << "Searching for pkgs...";
489  connect( getNGProc, SIGNAL(readyReadStandardOutput()), this, SLOT(slotGetNGPackageDataOutput()) );
490  connect( getNGProc, SIGNAL(finished(int, QProcess::ExitStatus)), this, SLOT(slotGetNGInstalledPkgs()) );
491  getNGProc->setProcessChannelMode(QProcess::MergedChannels);
492  if ( wDir.isEmpty() )
493    getNGProc->start(QString("pkg"), QStringList() << "rquery" << "-a" << "%o:::%n-%v:::%c:::%sh:::%m:::%w");
494  else
495    getNGProc->start(QString("chroot"), QStringList() << wDir << "pkg" << "rquery" << "-a" << "%o:::%n-%v:::%c:::%sh:::%m:::%w");
496
497}
498
499void mainWin::slotGetNGPackageDataOutput()
500{
501   while (getNGProc->canReadLine())
502     tmpPkgList << getNGProc->readLine().simplified();
503}
504
505void mainWin::slotGetNGInstalledDataOutput()
506{
507   while (getNGProc->canReadLine())
508     pkgList << getNGProc->readLine().simplified();
509}
510
511void mainWin::slotGetNGInstalledPkgs() {
512
513  qDebug() << "Building dependancy lists...";
514  QProcess p;
515  pkgDepList.clear();
516  if ( wDir.isEmpty() )
517    p.start("pkg", QStringList() << "rquery" << "-a" << "%n-%v:::%dn-%dv");
518  else
519    p.start("chroot", QStringList() << wDir << "pkg" << "rquery" << "-a" << "%n-%v:::%dn-%dv" );
520  while(p.state() == QProcess::Starting || p.state() == QProcess::Running) {
521      p.waitForFinished(200);
522      QCoreApplication::processEvents();
523  }
524  while (p.canReadLine()) {
525    pkgDepList << p.readLine().simplified();
526  }
527
528  qDebug() << "Building reverse dependancy lists...";
529  pkgRDepList.clear();
530  if ( wDir.isEmpty() )
531    p.start("pkg", QStringList() << "rquery" << "-a" << "%n-%v:::%rn-%rv");
532  else
533    p.start("chroot", QStringList() << wDir << "pkg" << "rquery" << "-a" << "%n-%v:::%rn-%rv" );
534  while(p.state() == QProcess::Starting || p.state() == QProcess::Running) {
535      p.waitForFinished(200);
536      QCoreApplication::processEvents();
537  }
538  while (p.canReadLine()) {
539    pkgRDepList << p.readLine().simplified();
540  }
541
542  getNGProc = new QProcess();
543  qDebug() << "Searching for installed pkgs...";
544  connect( getNGProc, SIGNAL(readyReadStandardOutput()), this, SLOT(slotGetNGInstalledDataOutput()) );
545  connect( getNGProc, SIGNAL(finished(int, QProcess::ExitStatus)), this, SLOT(slotFinishLoadingNGPkgs()) );
546  getNGProc->setProcessChannelMode(QProcess::MergedChannels);
547  if ( wDir.isEmpty() )
548    getNGProc->start(QString("pkg"), QStringList() << "info" << "-aq" );
549  else
550    getNGProc->start(QString("chroot"), QStringList() << wDir << "pkg" << "info" << "-aq");
551
552}
553
554void mainWin::slotFinishLoadingNGPkgs()
555{
556  treeNGPkgs->clear();
557
558  addNGItems();
559
560  pushPkgApply->setEnabled(false);
561
562  connect(treeNGPkgs, SIGNAL(itemChanged(QTreeWidgetItem *, int)), this, SLOT(slotEnableApply()));
563  connect(treeNGPkgs, SIGNAL(currentItemChanged(QTreeWidgetItem *, QTreeWidgetItem *)), this, SLOT(slotNGItemChanged()));
564
565  // Now we can look for updates safely
566  slotRescanPkgsClicked();
567}
568
569void mainWin::slotNGItemChanged()
570{
571  if ( ! treeNGPkgs->currentItem() ) {
572     groupInfo->setVisible(false);
573     return;
574  }
575  QString desc, size, maint, weburl;
576
577  QTreeWidgetItem *cItem = treeNGPkgs->currentItem();
578  QString pName = cItem->text(0).section("(", 1, 1).section(")", 0, 0);
579  if ( pName.isEmpty() ) {
580     groupInfo->setVisible(false);
581     return;
582  }
583  qDebug() << "Checking: " + pName;
584
585  QRegExp rx("*:::" + pName + ":::*");
586  rx.setPatternSyntax(QRegExp::Wildcard);
587  int pAt = tmpPkgList.indexOf(rx);
588  if (pAt == -1 ) {
589     qDebug() << "Unable to find package: " + pName;
590     groupInfo->setVisible(false);
591     return;
592  }
593
594  desc = tmpPkgList.at(pAt).section(":::", 2,2);
595  size = tmpPkgList.at(pAt).section(":::", 3,3);
596  maint = tmpPkgList.at(pAt).section(":::", 4,4);
597  weburl = tmpPkgList.at(pAt).section(":::", 5,5);
598  labelPkgNameVer->setText(pName);
599  labelSize->setText(size);
600  labelWeb->setText(weburl);
601  textDesc->setText(desc);
602  textOptions->clear();
603
604  QCoreApplication::processEvents();
605
606  // Display the depends
607  QString depTxt;
608  QRegExp rxd( pName + ":::*");
609  rxd.setPatternSyntax(QRegExp::Wildcard);
610  QStringList aDeps = pkgDepList.filter(rxd);
611  for ( int r=0; r < aDeps.size(); ++r) {
612      QString dName = aDeps.at(r).section(":::", 1, 1);
613      // Is this package installed?
614      if ( pkgList.indexOf(dName) != -1 )
615         depTxt+= dName + " (Installed)\n";
616      else
617         depTxt+= dName + "\n";
618  }
619
620  textDeps->setText(depTxt);
621
622  groupInfo->setVisible(true);
623
624  getNGInfo = new QProcess();
625  qDebug() << "Getting Info for " + pName;
626  connect( getNGInfo, SIGNAL(readyReadStandardOutput()), this, SLOT(slotNGReadInfo()) );
627  getNGInfo->setProcessChannelMode(QProcess::MergedChannels);
628  if ( wDir.isEmpty() )
629    getNGInfo->start(QString("pkg"), QStringList() << "rquery" << "%Ok=%Ov" << pName );
630  else
631    getNGInfo->start(QString("chroot"), QStringList() << wDir << "pkg" << "rquery" << "%Ok=%Ov" << pName);
632}
633
634void mainWin::slotNGReadInfo()
635{
636  while (getNGInfo->canReadLine())
637     textOptions->append(getNGInfo->readLine().simplified() );
638
639  textOptions->moveCursor(QTextCursor::Start);
640}
641
642void mainWin::slotEnableApply()
643{
644  pushPkgApply->setEnabled(true);
645}
646
647void mainWin::addNGItems()
648{
649   QString curCat, cat, name, pkgname, desc, size, maint, weburl;
650
651   // We like to add alphabetically
652   tmpPkgList.sort();
653
654   QTreeWidgetItem *catItem = new QTreeWidgetItem;
655
656   // Lets start adding packages to the tree widget
657   for (int i = 0; i < tmpPkgList.size(); ++i) {
658        name = cat = tmpPkgList.at(i).section(":::", 0,0);
659        cat=cat.section("/", 0, 0);
660        name=name.section("/", 1, 1);
661        pkgname = tmpPkgList.at(i).section(":::", 1,1);
662        desc = tmpPkgList.at(i).section(":::", 2,2);
663        size = tmpPkgList.at(i).section(":::", 3,3);
664        maint = tmpPkgList.at(i).section(":::", 4,4);
665        weburl = tmpPkgList.at(i).section(":::", 5,5);
666
667        // Check if we need to add a top-level category
668        if ( cat != curCat )
669        {
670           qDebug() << "Adding cat: " + cat;
671           catItem = new QTreeWidgetItem(QStringList() << cat);
672           treeNGPkgs->addTopLevelItem(catItem);
673           curCat = cat;
674        }
675 
676        // Now lets create the item and attach to the category
677        QTreeWidgetItem *pkgItem = new QTreeWidgetItem();
678        pkgItem->setText(0, name + " (" + pkgname + ") - " + size );
679        pkgItem->setToolTip(0, desc);
680
681        if ( pkgList.indexOf(pkgname) != -1 ) {
682          pkgItem->setCheckState(0, Qt::Checked);
683          selPkgList << pkgname;
684        } else
685          pkgItem->setCheckState(0, Qt::Unchecked);
686 
687        catItem->addChild(pkgItem);
688   }
689
690}
691
692// Lets prompt user, and do it!
693void mainWin::applyNGChanges()
694{
695   QString tmp;
696   QStringList curPkgChecked;
697   QStringList newPkgs;
698   QStringList rmPkgs;
699
700   QTreeWidgetItemIterator it(treeNGPkgs);
701   while (*it) {
702         if ((*it)->checkState(0) == Qt::Checked) {
703           tmp = (*it)->text(0).section("(", 1, 1).section(")", 0, 0);
704           curPkgChecked << tmp;
705           if (pkgList.indexOf(tmp) == -1 ) 
706              newPkgs << tmp;
707         }
708         ++it;
709   }
710
711   for ( int i=0; i < pkgList.size(); ++i)
712      // Has this package been unchecked?
713      if (curPkgChecked.indexOf(pkgList.at(i)) == -1 )  {
714         // Make sure this is a package in the repo
715         // This filters out any custom packages the user may have loaded which may not exist in our repo
716         QRegExp rx("*" + pkgList.at(i) + "*");
717         rx.setPatternSyntax(QRegExp::Wildcard);
718         if ( tmpPkgList.indexOf(rx) != -1 )
719           rmPkgs << pkgList.at(i);
720      }
721
722   if ( rmPkgs.isEmpty() && newPkgs.isEmpty() ) {
723      QMessageBox::warning(this, tr("No changes"),
724        tr("No changes to make!"),
725        QMessageBox::Ok,
726        QMessageBox::Ok);
727      return;
728   }
729
730   qDebug() << "Added packages" << newPkgs;
731   qDebug() << "Removed packages" << rmPkgs;
732   pkgRemoveList = rmPkgs;
733   pkgAddList = newPkgs;
734
735   QString confirmText;
736
737   // Lets start creating our confirmation text
738   if ( ! rmPkgs.isEmpty() ) {
739      confirmText+=tr("The following packages will be removed:") + "\n"; 
740      confirmText+= "------------------------------------------\n";
741      confirmText+=rmPkgs.join("\n"); 
742      confirmText+= "\n\n" + tr("The following packages that require the above packages will also removed:") + "\n"; 
743      confirmText+= "------------------------------------------\n";
744      for ( int i=0; i < rmPkgs.size(); ++i) {
745         QRegExp rx(rmPkgs.at(i) + ":::*");
746         rx.setPatternSyntax(QRegExp::Wildcard);
747         QStringList rDeps = pkgRDepList.filter(rx);
748         for ( int r=0; r < rDeps.size(); ++r) {
749             QString pName = rDeps.at(r).section(":::", 1, 1); 
750             // Is this package installed?
751             if ( pkgList.indexOf(pName) != -1 )
752               confirmText+= pName + " ";
753         }
754      }
755   }
756
757   if ( ! newPkgs.isEmpty() ) {
758      if ( ! rmPkgs.isEmpty() )
759        confirmText+= "\n\n";
760      confirmText+=tr("The following packages will be installed:") + "\n"; 
761      confirmText+= "------------------------------------------\n";
762      confirmText+=newPkgs.join("\n"); 
763      confirmText+= "\n\n" + tr("The following dependances will also be installed:") + "\n"; 
764      confirmText+= "------------------------------------------\n";
765      for ( int i=0; i < newPkgs.size(); ++i) {
766         QRegExp rx(newPkgs.at(i) + ":::*");
767         rx.setPatternSyntax(QRegExp::Wildcard);
768         QStringList aDeps = pkgDepList.filter(rx);
769         for ( int r=0; r < aDeps.size(); ++r) {
770             QString pName = aDeps.at(r).section(":::", 1, 1); 
771             // Is this package installed?
772             if ( pkgList.indexOf(pName) == -1 )
773               confirmText+= pName + " ";
774         }
775      }
776   }
777
778   // Launch our AddPartitionDialog to add a new device
779   askUserConfirm = new dialogConfirm();
780   connect(askUserConfirm, SIGNAL(ok()),this, SLOT(slotStartNGChanges()) );
781   askUserConfirm->programInit(tr("Confirm package changes"));
782   askUserConfirm->setInfoText(QString(confirmText));
783   askUserConfirm->exec();
784
785}
786
787
788// Time to start doing our NG changes!
789void mainWin::slotStartNGChanges()
790{
791  // Init the pkg process
792  prepPkgProcess();
793
794  // Create our runlist of package commands
795  QStringList pCmds;
796 
797  if ( ! pkgRemoveList.isEmpty() ) {
798    if ( wDir.isEmpty() )
799      pCmds << "pkg" << "delete" << "-R" << "-y" << pkgRemoveList.join(" ");
800    else
801      pCmds << "chroot" << wDir << "pkg" << "delete" << "-R" << "-y" << pkgRemoveList.join(" ");
802    pkgCmdList << pCmds;
803  }
804         
805  pCmds.clear();
806
807  if ( ! pkgAddList.isEmpty() ) {
808    if ( wDir.isEmpty() )
809      pCmds << "pc-pkg" << "install" << "-y" << pkgAddList.join(" ");
810    else
811      pCmds << "chroot" << wDir << "pc-pkg" << "install" << "-y" << pkgAddList.join(" ");
812    pkgCmdList << pCmds;
813  }
814
815  // Lets kick it off now
816  startPkgProcess();
817}
818
819// Display found meta-pkg data
820void mainWin::populateMetaPkgs()
821{
822  pushPkgApply->setEnabled(false);
823  treeMetaPkgs->clear();
824  tmpMetaPkgList.clear();
825  new QTreeWidgetItem(treeMetaPkgs, QStringList() << tr("Loading... Please wait...") );
826
827  if ( ! metaPkgList.isEmpty() )
828        disconnect(treeMetaPkgs, SIGNAL(itemChanged(QTreeWidgetItem *, int)), 0, 0);
829  metaPkgList.clear();
830
831  // Start the process to get meta-pkg info
832  getMetaProc = new QProcess();
833  qDebug() << "Searching for meta-pkgs...";
834  connect( getMetaProc, SIGNAL(readyReadStandardOutput()), this, SLOT(slotGetPackageDataOutput()) );
835  connect( getMetaProc, SIGNAL(finished(int, QProcess::ExitStatus)), this, SLOT(slotFinishLoadingMetaPkgs()) );
836  getMetaProc->setProcessChannelMode(QProcess::MergedChannels);
837  getMetaProc->start(QString("pc-metapkgmanager"), QStringList() << "list");
838
839}
840
841// Display found meta-pkg data
842void mainWin::slotFinishLoadingMetaPkgs()
843{
844
845  // Populate the metaPkgList
846  parseTmpMetaList();
847
848  treeMetaPkgs->clear();
849
850  addTreeItems(QString()); 
851
852  pushPkgApply->setEnabled(false);
853
854  connect(treeMetaPkgs, SIGNAL(itemChanged(QTreeWidgetItem *, int)), this, SLOT(slotDeskPkgsChanged(QTreeWidgetItem *, int)));
855
856  // Now we can look for updates safely
857  slotRescanPkgsClicked();
858}
859
860void mainWin::addTreeItems(QString parent)
861{
862  for (int z=0; z < metaPkgList.count(); ++z) {
863    if ( metaPkgList.at(z).at(3) != parent )
864      continue;
865
866    // Hide the "base-system" package, since we are installing it anyway
867    if (metaPkgList.at(z).at(0) == "base-system" )
868      return;
869
870    QTreeWidgetItem *deskItem = new QTreeWidgetItem(QStringList() << metaPkgList.at(z).at(0) );
871    deskItem->setIcon(0, QIcon(metaPkgList.at(z).at(2)));
872    deskItem->setToolTip(0, metaPkgList.at(z).at(1));
873    deskItem->setCheckState(0, Qt::Unchecked);
874
875    if ( metaPkgList.at(z).at(5) == "YES" )
876      deskItem->setCheckState(0, Qt::Checked);
877
878    if ( parent.isEmpty() ) {
879      treeMetaPkgs->addTopLevelItem(deskItem);
880    } else {
881      // Locate the parent to attach to
882      QTreeWidgetItemIterator it(treeMetaPkgs);
883      while (*it) {
884        if ((*it)->text(0) == parent ) {
885          (*it)->addChild(deskItem);
886          if ( metaPkgList.at(z).at(5) == "YES" && (*it)->checkState(0) == Qt::Unchecked)
887            (*it)->setCheckState(0, Qt::PartiallyChecked);
888          if ( metaPkgList.at(z).at(5) == "NO" && (*it)->checkState(0) == Qt::Checked)
889            (*it)->setCheckState(0, Qt::PartiallyChecked);
890          break;
891        }
892        it++;
893      }
894    }
895
896    // Now look for any possible children
897    addTreeItems(metaPkgList.at(z).at(0));   
898  }
899}
900
901// Check if a meta-pkg is installed
902bool mainWin::isMetaPkgInstalled(QString mPkg)
903{
904  QString tmp;
905  QProcess pcmp;
906  pcmp.start(QString("pc-metapkgmanager"), QStringList() << chrootArg1 << chrootArg2 << "status" << mPkg);
907  while ( pcmp.state() != QProcess::NotRunning ) {
908     pcmp.waitForFinished(50);
909     QCoreApplication::processEvents();
910  }
911
912  while (pcmp.canReadLine()) {
913     tmp = pcmp.readLine().simplified();
914     if ( tmp.indexOf("is installed") != -1 )
915     return true;
916  }
917
918  return false;
919}
920
921// Function which checks for our GUI package schema data
922void mainWin::slotGetPackageDataOutput()
923{
924  while (getMetaProc->canReadLine())
925        tmpMetaPkgList << getMetaProc->readLine().simplified();
926}
927
928// Parse the pc-metapkg saved output
929void mainWin::parseTmpMetaList()
930{
931  QString tmp, mName, mDesc, mIcon, mParent, mDesktop, mInstalled, mPkgFileList;
932  QStringList package;
933
934  for ( int i = 0 ; i < tmpMetaPkgList.size(); i++ )
935  {
936        QApplication::processEvents();
937
938        tmp = tmpMetaPkgList.at(i);
939
940        if ( tmp.indexOf("Meta Package: ") == 0) {
941                mName = tmp.replace("Meta Package: ", "");
942                continue;
943        }
944        if ( tmp.indexOf("Description: ") == 0) {
945                mDesc = tmp.replace("Description: ", "");
946                continue;
947        }
948        if ( tmp.indexOf("Icon: ") == 0) {
949                mIcon = tmp.replace("Icon: ", "");
950                mPkgFileList = mIcon;
951                mPkgFileList.replace("pkg-icon.png", "pkg-list");
952                continue;
953        }
954        if ( tmp.indexOf("Parent: ") == 0) {
955                mParent = tmp.replace("Parent: ", "");
956                continue;
957        }
958        if ( tmp.indexOf("Desktop: ") == 0) {
959                mDesktop = tmp.replace("Desktop: ", "");
960                continue;
961        }
962
963        // This is an empty category
964        if ( tmp.indexOf("Category Entry") == 0) {
965                // Now add this category to the string list
966                package.clear();
967                qDebug() << "Found Package" << mName << mDesc << mIcon << mParent << mDesktop;
968                mInstalled = "CATEGORY";
969                package << mName << mDesc << mIcon << mParent << mDesktop << mInstalled;
970                metaPkgList.append(package);
971                mName=""; mDesc=""; mIcon=""; mParent=""; mDesktop=""; mInstalled=""; mPkgFileList="";
972        }
973
974        // We have a Meta-Pkg
975        if ( tmp.indexOf("Required Packages:") == 0) {
976                // Now add this meta-pkg to the string list
977                package.clear();
978                qDebug() << "Found Package" << mName << mDesc << mIcon << mParent << mDesktop << mPkgFileList;
979
980                if ( isMetaPkgInstalled(mName) )
981                        mInstalled = "YES";
982                else
983                        mInstalled = "NO";
984
985                package << mName << mDesc << mIcon << mParent << mDesktop << mInstalled << mPkgFileList;
986                metaPkgList.append(package);
987                mName=""; mDesc=""; mIcon=""; mParent=""; mDesktop=""; mInstalled=""; mPkgFileList="";
988        }
989
990    }
991
992}
993
994void mainWin::saveMetaPkgs()
995{
996        if ( ! haveMetaPkgChanges() )
997                return;
998
999        addPkgs = getAddPkgs();
1000        delPkgs = getDelPkgs(); 
1001
1002        startMetaChanges();
1003
1004}
1005
1006void mainWin::startMetaChanges()
1007{
1008
1009  // Init the pkg process
1010  prepPkgProcess();
1011  // Create our runlist of package commands
1012  QStringList pCmds;
1013
1014  if ( ! delPkgs.isEmpty() ) {
1015    if ( wDir.isEmpty() )
1016      pCmds << "pc-metapkgmanager" << "del" << delPkgs;
1017    else 
1018      pCmds << "chroot" << wDir << "pc-metapkgmanager" << "del" << delPkgs;
1019    pkgCmdList << pCmds;
1020  }
1021 
1022  pCmds.clear();
1023
1024  if ( ! addPkgs.isEmpty() ) {
1025    if ( wDir.isEmpty() )
1026      pCmds << "pc-metapkgmanager" << "add" << addPkgs;
1027    else 
1028      pCmds << "chroot" << wDir << "pc-metapkgmanager" << "add" << addPkgs;
1029    pkgCmdList << pCmds;
1030  }
1031
1032  // Lets kick it off now
1033  startPkgProcess();
1034}
1035
1036bool mainWin::haveAMetaDesktop()
1037{
1038        // If running in a chroot we can skip this check
1039        if ( ! chrootArg1.isEmpty() )
1040          return true;
1041       
1042        QTreeWidgetItemIterator it(treeMetaPkgs);
1043        while (*it) {
1044         if ( ((*it)->checkState(0) == Qt::Checked) || ((*it)->checkState(0) == Qt::PartiallyChecked) )
1045           for (int z=0; z < metaPkgList.count(); ++z)
1046             if ( (*it)->text(0) == metaPkgList.at(z).at(0) && metaPkgList.at(z).at(4) == "YES" )
1047                return true;
1048         ++it;
1049        }
1050
1051        QMessageBox::warning(this, tr("No Desktop"),
1052          tr("No desktops have been selected! Please choose at least one desktop before saving."),
1053          QMessageBox::Ok,
1054          QMessageBox::Ok);
1055
1056        return false;
1057}
1058
1059bool mainWin::haveMetaPkgChanges()
1060{
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) == "YES" && (*it)->checkState(0) == Qt::Unchecked ) \
1066              || ( (*it)->text(0) == metaPkgList.at(z).at(0) && metaPkgList.at(z).at(5) == "YES" && (*it)->checkState(0) == Qt::PartiallyChecked ) \
1067              || ( (*it)->text(0) == metaPkgList.at(z).at(0) && metaPkgList.at(z).at(5) == "NO" && (*it)->checkState(0) == Qt::Checked ) )
1068                return true;
1069         ++it;
1070        }
1071
1072        return false;
1073}
1074
1075QString mainWin::getAddPkgs()
1076{
1077        QString tmp;
1078        QTreeWidgetItemIterator it(treeMetaPkgs);
1079        while (*it) {
1080          for (int z=0; z < metaPkgList.count(); ++z)
1081            // See if any packages status have changed
1082            if ( ( (*it)->text(0) == metaPkgList.at(z).at(0) && metaPkgList.at(z).at(5) == "NO" && (*it)->checkState(0) == Qt::Checked ) || \
1083                 ( (*it)->text(0) == metaPkgList.at(z).at(0) && metaPkgList.at(z).at(5) == "NO" && (*it)->checkState(0) == Qt::PartiallyChecked ) )
1084                if ( tmp.isEmpty() )
1085                        tmp = (*it)->text(0);
1086                else
1087                        tmp = tmp + "," + (*it)->text(0);
1088         ++it;
1089        }
1090
1091        return tmp;
1092}
1093
1094QString mainWin::getDelPkgs()
1095{
1096        QString tmp;
1097        QTreeWidgetItemIterator it(treeMetaPkgs);
1098        while (*it) {
1099          for (int z=0; z < metaPkgList.count(); ++z)
1100            // See if any packages status have changed
1101            if ( (*it)->text(0) == metaPkgList.at(z).at(0) && metaPkgList.at(z).at(5) == "YES" && (*it)->checkState(0) == Qt::Unchecked )
1102                if ( tmp.isEmpty() )
1103                        tmp = (*it)->text(0);
1104                else
1105                        tmp = tmp + "," + (*it)->text(0);
1106         ++it;
1107        }
1108
1109        return tmp;
1110}
1111
1112
1113// Time to save meta-pkgs
1114void mainWin::slotApplyMetaChanges() {
1115    saveMetaPkgs();
1116}
1117
1118
1119
1120// The User changed the tree widget checked / unchecked stuff sanity check
1121void mainWin::slotDeskPkgsChanged(QTreeWidgetItem *aItem, int __unused)
1122{
1123        if (!aItem)
1124          return;
1125
1126        disconnect(treeMetaPkgs, SIGNAL(itemChanged(QTreeWidgetItem *, int)), 0, 0);
1127
1128        if (aItem->childCount() == 0) {
1129                if (aItem->checkState(0) == Qt::Checked && aItem->parent() )
1130                        if ( allChildrenPkgsChecked(aItem->parent()->text(0)))
1131                                aItem->parent()->setCheckState(0, Qt::Checked); 
1132                        else
1133                                aItem->parent()->setCheckState(0, Qt::PartiallyChecked);       
1134                if (aItem->checkState(0) == Qt::Unchecked && aItem->parent() )
1135                        if ( ! allChildrenPkgsUnchecked(aItem->parent()->text(0)))
1136                                aItem->parent()->setCheckState(0, Qt::PartiallyChecked);       
1137
1138
1139        } else {
1140                if (aItem->checkState(0) == Qt::Checked )
1141                        checkAllChildrenPkgs(aItem->text(0));
1142                else
1143                        uncheckAllChildrenPkgs(aItem->text(0));
1144        }
1145       
1146
1147    connect(treeMetaPkgs, SIGNAL(itemChanged(QTreeWidgetItem *, int)), this, SLOT(slotDeskPkgsChanged(QTreeWidgetItem *, int)));
1148
1149        if ( haveMetaPkgChanges() )
1150                pushPkgApply->setEnabled(true);
1151        else
1152                pushPkgApply->setEnabled(false);
1153}
1154
1155// Check the "parent" app to see if all its children are checked or not
1156bool mainWin::allChildrenPkgsChecked(QString parent)
1157{
1158        QTreeWidgetItemIterator it(treeMetaPkgs);
1159        while (*it) {
1160         if ((*it)->text(0) == parent ) {
1161           if ( (*it)->childCount() <= 0)
1162             return true;
1163
1164           for ( int i = 0; i < (*it)->childCount() ; ++i) {
1165             if ( ! allChildrenPkgsChecked((*it)->child(i)->text(0)))
1166               return false;
1167
1168             if ((*it)->child(i)->checkState(0) != Qt::Checked ) 
1169               return false;
1170           }
1171         }
1172         ++it;
1173        }
1174        return true;
1175}
1176
1177// Check the "parent" app to see if all its children are unchecked or not
1178bool mainWin::allChildrenPkgsUnchecked(QString parent)
1179{
1180        QTreeWidgetItemIterator it(treeMetaPkgs);
1181        while (*it) {
1182         if ((*it)->text(0) == parent ) {
1183           if ( (*it)->childCount() <= 0)
1184             return true;
1185
1186           for ( int i = 0; i < (*it)->childCount() ; ++i) {
1187             if ( ! allChildrenPkgsUnchecked((*it)->child(i)->text(0)))
1188               return false;
1189
1190             if ((*it)->child(i)->checkState(0) != Qt::Unchecked ) 
1191               return false;
1192           }
1193         }
1194         ++it;
1195        }
1196        return true;
1197}
1198
1199// Check all children of parent
1200void mainWin::checkAllChildrenPkgs(QString parent)
1201{
1202        QTreeWidgetItemIterator it(treeMetaPkgs);
1203        while (*it) {
1204         if (! (*it)->parent()) {
1205           ++it;
1206           continue;
1207         } 
1208
1209         // Lets walk the tree see what pops up
1210         bool pFound=false;
1211         QTreeWidgetItem *itP = (*it)->parent();
1212         do {
1213           pFound=false;
1214           if (itP->text(0) == parent) {
1215             (*it)->setCheckState(0, Qt::Checked);
1216             break;
1217           }
1218           if ( itP->parent() ) {
1219             itP = itP->parent();
1220             pFound=true;
1221           }
1222         } while (pFound);
1223
1224         ++it;
1225       }
1226}
1227
1228// UnCheck all children of parent
1229void mainWin::uncheckAllChildrenPkgs(QString parent)
1230{
1231        QTreeWidgetItemIterator it(treeMetaPkgs);
1232        while (*it) {
1233         if (! (*it)->parent()) {
1234           ++it;
1235           continue;
1236         } 
1237
1238         // Lets walk the tree see what pops up
1239         bool pFound=false;
1240         QTreeWidgetItem *itP = (*it)->parent();
1241         do {
1242           pFound=false;
1243           if (itP->text(0) == parent) {
1244             (*it)->setCheckState(0, Qt::Unchecked);
1245             break;
1246           }
1247           if ( itP->parent() ) {
1248             itP = itP->parent();
1249             pFound=true;
1250           }
1251         } while (pFound);
1252
1253         ++it;
1254       }
1255}
1256
1257void mainWin::slotMetaRightClick()
1258{
1259        QTreeWidgetItemIterator it(treeMetaPkgs);
1260        while (*it) {
1261          for (int z=0; z < metaPkgList.count(); ++z) {
1262            if ( (*it)->isSelected() && (*it)->text(0) == metaPkgList.at(z).at(0) ) {
1263              if (metaPkgList.at(z).at(5) == "CATEGORY")
1264                return;
1265              popup = new QMenu;
1266              popup->setTitle((*it)->text(0));
1267              popup->addAction(tr("View Packages"), this, SLOT(slotMetaViewPkgs()));
1268              popup->exec( QCursor::pos() );
1269            }
1270          }
1271         ++it;
1272        }
1273}
1274
1275void mainWin::slotMetaViewPkgs()
1276{
1277        QStringList packageList;
1278        QTreeWidgetItemIterator it(treeMetaPkgs);
1279        while (*it) {
1280          for (int z=0; z < metaPkgList.count(); ++z) {
1281            if ( (*it)->isSelected() && (*it)->text(0) == metaPkgList.at(z).at(0) ) {
1282 
1283                QFile pList(metaPkgList.at(z).at(6));
1284                if ( ! pList.exists() )
1285                  return;
1286               
1287                if ( ! pList.open(QIODevice::ReadOnly | QIODevice::Text))
1288                  return;
1289
1290                while ( !pList.atEnd() )
1291                  packageList << pList.readLine().simplified();
1292
1293                pList.close();
1294                packageList.sort();
1295                       
1296                dIB = new dialogInfo();
1297                dIB->programInit(tr("Package Listing for:") + " " + (*it)->text(0));
1298                dIB->setInfoText(packageList.join("\n"));
1299                dIB->show();
1300            }
1301          }
1302         ++it;
1303        }
1304}
1305
1306QString mainWin::getLineFromCommandOutput( QString cmd )
1307{
1308        FILE *file = popen(cmd.toLatin1(),"r");
1309 
1310        char buffer[100];
1311 
1312        QString line = "";
1313        char firstChar;
1314
1315        if ((firstChar = fgetc(file)) != -1){
1316                line += firstChar;
1317                line += fgets(buffer,100,file);
1318        }
1319        pclose(file);
1320        return line.simplified();
1321}
Note: See TracBrowser for help on using the repository browser.