/* 
 * This file is part of DesktopBSD, see the README file.
 *
 * Author: Peter Hofer <ph@desktopbsd.net>
 *         (C) 2004, 2005
 *
 * Copyright: BSD, see the COPYING file included in this distribution
 *
 */

#include <dbsdglobal.h>

/* libdesktopbsd */
#include <pkgconstants.h>
#include <package.h>
#include <portauditengine.h>
#include <pkgupgradeengine.h>
#include <pkgdbupdateengine.h>
#include <portsdbupdateengine.h>
#include <portsupdateengine.h>
#include <pkgdeinstallengine.h>
#include <portsnapengine.h>
#include <unixprocess.h>
#include <variables.h>
#include <dbsdsettings.h>

/* libdesktopbsd-ui */
#include <dialogs.h>
#include <portauditformatter.h>
#include <porttoolsformatter.h>
#include <portsupdateformatter.h>
#include <frmcleanports.h>
#include <frmpkgsettings.h>
#include <frmdeinstallpkgs.h>
#include <frmtextbrowser.h>
#include <installedpkglistviewitem.h>
#include <translation.h>
#include <frmpkgupgradecontroller.h>
#include <pkgupgradetraynotifier.h>

/* Qt */
#include <qtoolbutton.h>
#include <qlocale.h>
#include <qtimer.h>
#include <qapplication.h>
#include <qstring.h>
#include <qstringlist.h>
#include <qeventloop.h>
#include <qprogressdialog.h>
#include <qsplashscreen.h>
#include <qmessagebox.h>

/* KDE */
#include <kapplication.h>

#define	SPLASH_IMAGE		"/usr/local/share/desktopbsd/images/pkgmgr-splash.png"
#define  AVAIL_EVLOOPRES	2048
#define  MAXCATEGORIES 		256
#define  LINEBUFSIZE			32768

/* This will store all ports, so use a reasonably large prime number. */
#define	PKGDICT_SIZE		32749

/* Warn after specifical amount of days */
#define	SECURITY_WARNING	8
#define	SECURITY_CRITICAL	14

/* wsPager pages */
#define  WSPAGER_PKG			0
#define  WSPAGER_CAT			1
#define  WSPAGER_HELP		2
#define  WSPAGER_SECURITY	3

/* lstInstalledPkgs columns */
#define  INSTPKGS_COL_NAME		0
#define  INSTPKGS_COL_STATUS	1	/* =, <, > */
#define  INSTPKGS_COL_REQBY	2
#define  INSTPKGS_COL_SIZE		3


enum PkgTask {
	Install = 0,
	Upgrade = 1,
	None = 2
};



void	wndMain::showWebsite() {
	if (shownPkg != NULL) {
		UnixProcess	process;

		/* Open browser as unprivileged user */
		QString user = Variables::getValue("pkgmgr.user");
		if(!user.isEmpty() && user != "root") {
			process.addArgument("/usr/local/bin/kdesu");
			process.addArgument("-u");
			process.addArgument(user);
		}

		process.addArgument("/usr/local/bin/kfmclient");
		process.addArgument("exec");
		process.addArgument(shownPkg->getWebsite());

		if (!process.start())
			Dialogs::warnDlg(tr("Error: Cannot launch browser.\n"
			                    "(kdesu failed or kfmclient couldn't be started)"));
	}
}



void	wndMain::findPkg() {
	/* Column; 0 = package name, 1 = short description */
	uint col = cmbSearchBy->currentItem();

	uint foundPkgs = 0;
	QString sstr = editSearch->text();

	if(!sstr.isEmpty()) {
		QListViewItemIterator it(lstPkgs);
		uint foundInCategory = 0;
		QListViewItem *category = NULL;
		for(; it.current() != NULL; ++it) {
			QListViewItem *i = it.current();
			
			if(i->childCount() > 0) {
				i->setVisible(true);
				i->setOpen(true);
				
				if(category != NULL && foundInCategory == 0)
					category->setVisible(false);
				
				category = i;
				foundInCategory = 0;
			} else if(i->text(col).contains(sstr, false)) {
				i->setVisible(true);
			
				++foundPkgs;
				++foundInCategory;
				
				if(foundPkgs == 1 || (col == 0 && sstr == i->text(col)))
					lstPkgs->setSelected(i, true);
			} else
				i->setVisible(false);
		}
		
		if(category != NULL && foundInCategory == 0)
			category->setVisible(false);
	}
	
	/* Display all items if the search was deleted
	 * or no packages could be found.
	 */
	if(sstr.isEmpty())
	{
		QListViewItemIterator it(lstPkgs);
		for(; it.current() != NULL; ++it)
			(*it)->setVisible(true);
	}
}



bool	wndMain::updatePkgDB() {
	PkgDBUpdateEngine  dbUpdateEngine;
	
	/* Note: No auto-checking on the pkgdb is performed because
	 *       the tools do that themselves when they upgrade,
	 *       delete or install packages.
	 */
	UnixProcess  *process = dbUpdateEngine.getProcess();
	process->setCommunication(UnixProcess::Stdout | UnixProcess::Stderr | UnixProcess::DupStderr);

	bool success = runProcess(process);
	if(!success)
		Dialogs::warnDlg(tr("Updating the package database failed:") + "\n\n" + QString(process->readStdout()));

	return success;
}


void  wndMain::initSecurityInfo() {
	/* Initialize portaudit XML CSS stylesheet */
	QStyleSheetItem  *siVuln = new QStyleSheetItem(brwVuXML->styleSheet(), "vuln");
	siVuln->setDisplayMode(QStyleSheetItem::DisplayBlock);
	siVuln->setMargin(QStyleSheetItem::MarginBottom, 35);
	
	QStyleSheetItem	*siTopic = new QStyleSheetItem(brwVuXML->styleSheet(), "topic");
	siTopic->setFontWeight(QFont::Bold);
	siTopic->setFontSize(14);
	siTopic->setDisplayMode(QStyleSheetItem::DisplayBlock);
	siTopic->setMargin(QStyleSheetItem::MarginBottom, 4);

   QStyleSheetItem	*siH1 = new QStyleSheetItem(brwVuXML->styleSheet(), "h1");
	siH1->setFontUnderline(true);
	siH1->setFontSize(13);
	siH1->setDisplayMode(QStyleSheetItem::DisplayBlock);

	QStyleSheetItem	*siPackage = new QStyleSheetItem(brwVuXML->styleSheet(), "package");
	siPackage->setFontWeight(QFont::Bold);
	siPackage->setDisplayMode(QStyleSheetItem::DisplayBlock);

	QStyleSheetItem	*siDesc = new QStyleSheetItem(brwVuXML->styleSheet(), "description");
	siDesc->setDisplayMode(QStyleSheetItem::DisplayBlock);
	
	QStyleSheetItem	*siEntryDate = new QStyleSheetItem(brwVuXML->styleSheet(), "entry");
	siEntryDate->setColor("steelblue");
	siEntryDate->setFontItalic(true);

	QStyleSheetItem	*siRef = new QStyleSheetItem(brwVuXML->styleSheet(), "references"),
							*siDiscovery = new QStyleSheetItem(brwVuXML->styleSheet(), "discovery"),
							*siModDate = new QStyleSheetItem(brwVuXML->styleSheet(), "modified");
	siRef->setDisplayMode(QStyleSheetItem::DisplayNone);
	siDiscovery->setDisplayMode(QStyleSheetItem::DisplayNone);
	siModDate->setDisplayMode(QStyleSheetItem::DisplayNone);
	
    if(brwVuXML->source() != VUXML_DB_FILE)
      brwVuXML->setSource(VUXML_DB_FILE);
    else
		brwVuXML->reload();
	
	QFileInfo fi(VUXML_DB_FILE);
	if(!fi.exists()) {
		const QString msgtext = tr("DesktopBSD maintains a package list that contains available packages,\n"
											"their versions and dependencies between them. An up-to-date list is\n"
											"required to be able to install and upgrade software on this computer.\n"
											"\n"
											"This list doesn't exist on your system, so it has to be downloaded\n"
											"from the internet first.\n"
											"\n"
											"Please ensure you are connected to the Internet and click 'Proceed'\n"
											"to download the package list. If you don't want to do this now, click\n"
											"'Quit' to exit the package manager.");

		if(QMessageBox::question(this, tr("Package list required"), msgtext, tr("Proceed"), tr("Quit")) == 0) {
			PackageSettings().setPortsUpdateMethod(PackageSettings::Portsnap);

			if(updatePorts())
				/* Reinitialization of everything already done, don't proceed here */
				return;
		}

		/*
		 * Exit the program if the user answered no to the
		 * question above or didn't specify a package
		 * server after clicking "Proceed":
		 * 
		 */
		exit(0);
	}

	/* Determine date of last security check using the portaudit audit file's timestamp */
	bool forceaudit = false;
	int days = -1;
	QFileInfo fiaudit(PORTAUDIT_DB_FILE);
	if(!fiaudit.exists())
		forceaudit = true;
	else {
		days = fiaudit.lastModified().daysTo(QDateTime::currentDateTime());
		if(days >= SECURITY_CRITICAL)
			forceaudit = true;
		else
			checkLastSecurityCheck();
	}

	if(forceaudit)
	{
		Dialogs::infoDlg(tr("%1\n\n"
								  "Installed software will now be checked for security issues.\n"
								  "Please ensure that you are connected to the internet and press\n"
								  "OK to continue.\n\n"
								  "(No data about you or your system will be transmitted)")
								.arg(fiaudit.exists() ?
									  tr("The last security check was run %1 days ago.").arg(days)
									  : tr("No security check was performed on this system yet.")));

		performSecurityCheck();
	}
}

/* NOTE: This method is not only called when the module
 *       is initialized, but also when the ports tree
 *       was updated to reinitialize the module.
 */
void	wndMain::initModule() {
	pkgContextMenu = new QPopupMenu(this);

	PackageSettings *ps = new PackageSettings();
	if(ps->isFirstRun()) {
		showIntroduction(true);
		ps->setFirstRun(false);
	}
	delete ps;
	
	initSecurityInfo();
	setIncrementalFind();

	/* Clean up user interface */
	editSearch->setText("");
	lstInstalledPkgs->clear();
	lstPkgs->clear();
	lstPendingOperations->clear();
	radGrpByNothing->setChecked(true);

	/* Clean up */
	installPkgs.clear();
	upgradePkgs.clear();
	outdatedPkgs.clear();
	
	pkgdict.setAutoDelete(true);	
	pkgdict.clear();
	pkgdict.resize(PKGDICT_SIZE);
	
	wsPager->raiseWidget(WSPAGER_HELP);

	/* Hide the window - prevents unnecessary redraws
	 * and access to the user interface before loading
	 * is finished when re-initializing
	 */
	hide();
	
	QSplashScreen *splash = new QSplashScreen(QPixmap(SPLASH_IMAGE));
	splash->message(tr("Reading: Available Packages..."), Qt::AlignRight | Qt::AlignBottom, Qt::white);
	splash->show();
	
	bool success = createAvailList();
	if(success) {
		splash->message(tr("Reading: Installed Packages..."), Qt::AlignRight | Qt::AlignBottom, Qt::white);
		qApp->processEvents();
		success = createInstalledList();
	}

	delete splash;
		
	/* Show the window (again) */
	show();

	/* Disable whole window when an error occured */
	setEnabled(success);

	grpFilterInstalled->setButton(PackageSettings().getInstalledPkgsGroupBy());
	lstInstalledPkgs->setColumnAlignment(INSTPKGS_COL_STATUS, Qt::AlignHCenter);


	//the exit action should try to quit the application (qApp is not selectable in designer)
	QObject::connect( acExit, SIGNAL(activated()), qApp, SLOT(quit()) );
}


bool  wndMain::updatePortsDB() {
	PortsDBUpdateEngine  dbUpdateEngine;

	UnixProcess  *process = dbUpdateEngine.getProcess();
	process->setCommunication(UnixProcess::Stdout | UnixProcess::Stderr | UnixProcess::DupStderr);

	bool success = runProcess(process);
	if(!success)
		Dialogs::warnDlg(tr("Updating the ports database failed:") + "\n\n" + QString(process->readStdout()));

	return success;
}


void	wndMain::showPkg(Package * pkg) {
	shownPkg = pkg;

	if (pkg == NULL) {
		wsPager->raiseWidget(WSPAGER_HELP);
		Dialogs::warnDlg(tr("Error: Cannot retrieve information."));
	} else {
		lblPkgName->setText(pkg->getName());
		lblPkgLatestVersion->setText(pkg->getLatestVersion());
		lblPkgDesc->setText(pkg->getShortDescr());

		QString  website = pkg->getWebsite();
		if (website.isEmpty())
			btnPkgWebsite->hide();
		else {
			QToolTip::add(btnPkgWebsite, website);
			btnPkgWebsite->show();
		}

		Package::InstallStatus	istatus = pkg->getInstallStatus();
		if (istatus != Package::Undefined) {
			if (istatus == Package::NotInstalled) {
				btnInstall->show();
				btnUpgrade->hide();
				
				lblInstalledVersion->hide();
				lblPkgInstalledVersion->hide();
				lblInstalledSize->hide();
				lblPkgInstalledSize->hide();
				btnCalculateInstalledSize->hide();
			} else {
				btnInstall->hide();

				
				lblInstalledVersion->show();
				lblPkgInstalledVersion->show();
				lblPkgInstalledVersion->setText(pkg->getInstalledVersion());
				
				lblInstalledSize->show();
				lblPkgInstalledSize->show();
				if(pkg->isInstalledSizeSet()) 
					calculateShownPkgSize();
				else {
					lblPkgInstalledSize->hide();
					btnCalculateInstalledSize->show();
				}

				if (istatus == Package::InstalledOutdated)
					btnUpgrade->show();
				else
					btnUpgrade->hide();
			}
		}

		wsPager->raiseWidget(WSPAGER_PKG);
	}
}



/** Performs the default action for the given package.
  *
  * For installed packages, this is upgrading the
  * package (or nothing if no newer version is available).
  * 
  * For other packages this is installing the package.
  *
  */
void  wndMain::defaultAction(QListViewItem * item) {
	if(item == NULL  ||  item->childCount() != 0)
		return;
	
	Package * pkg = pkgdict[toPkgID(item, 0)];
	if(pkg == NULL)
		return;
	
	if(pkg->getInstallStatus() == Package::InstalledOutdated)
		addTaskPkg(pkg,  Upgrade,  true);
	else if(pkg->getInstallStatus() == Package::NotInstalled)
		addTaskPkg(pkg,  Install,  true);
}



void  wndMain::showPkgContextMenu(QListViewItem *item,  const QPoint & pnt,  int index) {
	if(item == NULL  ||  item->childCount() != 0)
		return;
	
	pkgContextMenu->clear();
	
	DBSDIconLoader  *iloader = DBSDGlobal::getIconLoader();
	
	Package * pkg = pkgdict[toPkgID(item, 0)];
	showPkg(pkg);
	
	Package::InstallStatus  status = pkg->getInstallStatus();
	if(status == Package::Installed || status == Package::InstalledNewer || status == Package::InstalledOutdated)
	{
		if(status == Package::InstalledOutdated)
		{
			pkgContextMenu->insertItem(iloader->loadIcon("up",  DBSDIconLoader::Size16x16),
												tr("Upgrade: %1 -> %2").arg(pkg->getInstalledVersion())
																				.arg(pkg->getLatestVersion()),
												this,
												SLOT(upgradePkg()));
		}

		if(!pkg->isInstalledSizeSet()) {
			pkgContextMenu->insertItem(iloader->loadIcon("kcalc",  DBSDIconLoader::Size16x16),
												tr("Calculate size"), this, SLOT(calculateShownPkgSize()));
		}
   }
	else if(status == Package::NotInstalled)
	{
		pkgContextMenu->insertItem(iloader->loadIcon("editcopy",  DBSDIconLoader::Size16x16),
											tr("Install"), this, SLOT(installPkg()));
	}

	pkgContextMenu->insertItem(iloader->loadIcon("text_left",  DBSDIconLoader::Size16x16),
										tr("Long Description"), this, SLOT(showLongDescr()));

	if(!pkg->getWebsite().isEmpty())
		pkgContextMenu->insertItem(iloader->loadIcon("konqueror",  DBSDIconLoader::Size16x16),
										tr("Website"), this, SLOT(showWebsite()));

	pkgContextMenu->popup(pnt);
}



void	wndMain::showItem(QListViewItem * item) {
	if (item == NULL)
		return;

	if (item->childCount() > 0) {
		/* Category */
		wsPager->raiseWidget(WSPAGER_CAT);
		lblCategory->setText(item->text(0));
		lblPackageCount->setText(tr("%1 packages").arg(item->childCount()));
	} else {
		/* Package */

		showPkg(pkgdict[toPkgID(item, 0)]);
	}
}



bool	wndMain::createInstalledList() {
	DBSDIconLoader *iload = DBSDGlobal::getIconLoader();
	QPixmap pixInstalled = iload->loadIcon("package", DBSDIconLoader::SmallListItem),
	        pixOutdated = iload->loadIcon("package_old", DBSDIconLoader::SmallListItem);
	uint		pkgStatistics[3] = { /* Newer */ 0,  /* Latest */ 0,  /* Outdated */ 0 };
	
	installedPkgs.clear();

	QPtrList<Package> instPkgs = Package::getInstalledPackages();
	
	QPtrListIterator<Package>	i(instPkgs);
	while(*i != NULL) {
		QString	id = (*i)->getID();
		Package	*latestPkg = pkgdict[id];
		if(latestPkg != NULL)
			latestPkg->mergeInstalledPackage(**i);
		else
		{
			pkgdict.insert(id, *i);
			latestPkg = *i;
		}
		
		installedPkgs.append(latestPkg);
		
		Package::InstallStatus	status = latestPkg->getInstallStatus();
		const char *statStr[3] = { ">", "=", "<" };
		int	statCat = (status == Package::Installed) ? 1 : ((status == Package::InstalledOutdated) ? 2 : 0);
		++pkgStatistics[statCat];
		if(status == Package::InstalledOutdated)
			outdatedPkgs.append(latestPkg);

		QString	requiredBy;
		uint		reqByCnt = latestPkg->getRequiredByCount();
		if (reqByCnt == 1)
			requiredBy = tr("1 package");
		else if(reqByCnt > 1)
			requiredBy = tr("%1 packages").arg(reqByCnt);

		QListViewItem	*item = new InstalledPkgListViewItem(lstInstalledPkgs, id, statStr[statCat], requiredBy);
		item->setPixmap(0, (status == Package::InstalledOutdated) ? pixOutdated : pixInstalled);
			
		++i;
		qApp->processEvents();
	}

	if(outdatedPkgs.isEmpty())
		btnUpgradeAll->hide();
	else
		btnUpgradeAll->show();

	/* Summary */
	QString	statusstr = "\n___\n\n";
	statusstr += tr("%1 packages installed.\n"
	                "%2 packages have the latest version.\n"
	                "%3 packages are older than the current version.\n")
	             .arg(installedPkgs.count())
	             .arg(pkgStatistics[1])
	             .arg(pkgStatistics[2]);

	lblInfo->setText(lblInfo->text() + statusstr);
	lstInstalledPkgs->setColumnWidthMode(INSTPKGS_COL_NAME, QListView::Maximum);
	lstInstalledPkgs->setSortColumn(INSTPKGS_COL_STATUS);

	return true;
}



bool	wndMain::createAvailList() {
	QString indexpath = QString(PORTS_DIRECTORY "INDEX-%1").arg(FREEBSD_MAJOR_VERSION);
	FILE	*f = fopen(indexpath, "r");
	if (f != NULL) {
		QPixmap pixCategory = DBSDGlobal::getIconLoader()->loadIcon("folder",
																DBSDIconLoader::SmallListItem);
		QPixmap pixPackage = DBSDGlobal::getIconLoader()->loadIcon("package",
																DBSDIconLoader::SmallListItem);
	
		char				 buffer[LINEBUFSIZE];
		QStringList		 catNames;
		QListViewItem  *catItems[MAXCATEGORIES];

		/* Clear existing list */
		lstPkgs->clear();

		uint	count = 0;
		for (; fgets(buffer, sizeof buffer, f) != NULL; ++count) {
			Package  *pkg = Package::fromINDEXLine(buffer);
			
			const QString mainCat = pkg->getMainCategory();
			QListViewItem *parent = NULL;

			int index = catNames.findIndex(mainCat);
			if(index < 0)
			{
				/* Not present */
				index = catNames.count();
				
				if(index < MAXCATEGORIES)
				{
					catNames.push_back(mainCat);
					catItems[index] = parent = new QListViewItem(lstPkgs, mainCat);
					parent->setPixmap(0, pixCategory);
				} else
					qWarning("%s (%d): No space for category '%s'",
								__FILE__, __LINE__, mainCat.latin1());
			} else
				parent = catItems[index];


			if (parent != NULL)
			{
				QListViewItem  *item = new  QListViewItem(parent, pkg->getName(),
																		pkg->getShortDescr());
				item->setPixmap(0, pixPackage);
			}

			pkgdict.insert(pkg->getID(), pkg);

			if (count % AVAIL_EVLOOPRES == 0)
				qApp->processEvents();
		}

		fclose(f);
		lblInfo->setText(tr("%1 Packages available.").arg(count));
	} else {
		/* INDEX couldn't be opened */
		Dialogs::warnDlg(tr("Unable to read from '%1'!").arg(indexpath));
		return false;
	}

	return true;
}


void	wndMain::addTaskPkg(const Package  *pkg,  int  task,  bool  interactive) {
	static const QString taskNames[] = { tr("installation"), tr("upgrading") };
	QPtrList<Package>	*lists[] = { &installPkgs, &upgradePkgs };

	if (pkg == NULL  ||  task < Install  ||  task > None)
		return;

	QString	pkgID = pkg->getID();
	for (int i = 0;  i < int(sizeof lists / sizeof lists[0]);  ++i) {
		if(lists[i]->containsRef(pkg)) {
			if(interactive) {
				if (i != task)	{
					if(!Dialogs::question(tr("\"%1\" is already marked for %2.\n"
													"Mark it for %3?").arg(pkgID)
																			.arg(taskNames[i])
																			.arg(taskNames[task])))
						return;
				} else {
					Dialogs::infoDlg(tr("\"%1\" is already marked for %2.\n")
											.arg(pkgID)
											.arg(taskNames[i]));
					return;
				}
			}
	
			lists[i]->remove(pkg);
			delete lstPendingOperations->findItem(pkgID, Qt::ExactMatch | Qt::CaseSensitive);
		}
	}
	
	if (task != None) {
		lists[task]->prepend(pkg);

		DBSDIconLoader *loader = DBSDGlobal::getIconLoader();
		QPixmap icon;
		if(task == Install)
			icon = loader->loadIcon("editcopy", DBSDIconLoader::SmallListItem);
		else if(task == Upgrade)
			icon = loader->loadIcon("up", DBSDIconLoader::SmallListItem);

		lstPendingOperations->insertItem(icon, pkgID, 0);
		lstPendingOperations->setSelected(0, true);
	}
}


void	wndMain::upgradePkg() {
	addTaskPkg(shownPkg, Upgrade, true);
}


void	wndMain::installPkg() {
	addTaskPkg(shownPkg, Install, true);
}

void  wndMain::removeSelectionFromListBox(QListBox * lb) {
	if(lb == NULL)
		return;

	QListBoxItem * item = lb->firstItem();
	while(item != NULL) {
		if(item->isSelected())
			addTaskPkg(pkgdict[item->text()], None, false);
	
		item = item->next();
	}
}

void	wndMain::removePendingOperation() {
	removeSelectionFromListBox(lstPendingOperations);
}


void	wndMain::tabChanged(QWidget  *w) {
	if (w == tabSecurity)
		wsPager->raiseWidget(WSPAGER_SECURITY);
	else if(w == tabInstalledPkgs  ||  w == tabAvailablePkgs) {
		QListView  *lv = (w == tabInstalledPkgs) ? lstInstalledPkgs : lstPkgs;
		QListViewItem  *item = lv->selectedItem();
		lv->clearSelection();
		
		if(item != NULL)
			lv->setSelected(item, true);
		else
			wsPager->raiseWidget(WSPAGER_HELP);
	} else
		wsPager->raiseWidget(WSPAGER_HELP);
}


void	wndMain::groupInstPkgsByCategory(bool toggled) {
	if (toggled) {
		groupInstPkgsByNothing(true);

		QDict < QListViewItem > categories;

		QListViewItem  *item = lstInstalledPkgs->firstChild();
		while (item != NULL) {
			QListViewItem  *next = item->nextSibling();

			QString	name = item->text(INSTPKGS_COL_NAME);
			int		pos = name.find('/');
			if (pos >= 0) {
				QString			 catName = name.left(pos);
				QListViewItem  *category = categories[catName];
				if (category == NULL) {
					/* Category doesn't exist, create */
					category = new QListViewItem(lstInstalledPkgs, catName);
					category->setPixmap(0, DBSDGlobal::getIconLoader()->loadIcon("folder", DBSDIconLoader::SmallListItem));
					categories.insert(catName, category);
				}
				lstInstalledPkgs->takeItem(item);
				item->setText(INSTPKGS_COL_NAME, name.mid(pos + 1));
				category->insertItem(item);
			}
			item = next;
		}
	}
}


void	wndMain::groupInstPkgsByVersion(bool toggled) {
	if (toggled) {
		groupInstPkgsByNothing(true);

		QListViewItem  *newerParent = new QListViewItem(lstInstalledPkgs,
		                              			tr("Newer than available"));

		QListViewItem  *olderParent = new QListViewItem(lstInstalledPkgs,
															tr("Older than available"));

		QListViewItem  *equalParent = new QListViewItem(lstInstalledPkgs,
		                              					tr("Latest version"));

		QListViewItem  *item = lstInstalledPkgs->firstChild();
		while (item != NULL) {
			QListViewItem  *next = item->nextSibling();

			if (item != newerParent && item != olderParent && item != equalParent) {
				QListViewItem  *parent;

				if (item->text(INSTPKGS_COL_STATUS) == "=")
					parent = equalParent;
				else if (item->text(INSTPKGS_COL_STATUS) == "<")
					parent = olderParent;
				else
					parent = newerParent;

				lstInstalledPkgs->takeItem(item);
				parent->insertItem(item);
			}

			item = next;
		}

		if (newerParent->childCount() == 0)
			delete  newerParent;
		else
			newerParent->setPixmap(0, DBSDGlobal::getIconLoader()->loadIcon("folder", DBSDIconLoader::SmallListItem));

		if (olderParent->childCount() == 0)
			delete  olderParent;
		else
			olderParent->setPixmap(0, DBSDGlobal::getIconLoader()->loadIcon("folder", DBSDIconLoader::SmallListItem));

		if (equalParent->childCount() == 0)
			delete  equalParent;
		else
			equalParent->setPixmap(0, DBSDGlobal::getIconLoader()->loadIcon("folder", DBSDIconLoader::SmallListItem));
	}
}


void	wndMain::groupInstPkgsByReferences(bool toggled) {
	if (toggled) {
		groupInstPkgsByNothing(true);

		QListViewItem	*unrefParent = new QListViewItem(lstInstalledPkgs, tr("Unreferenced Packages")),
							*refParent = new QListViewItem(lstInstalledPkgs, tr("Referenced Packages"));
							
		unrefParent->setPixmap(0, DBSDGlobal::getIconLoader()->loadIcon("folder", DBSDIconLoader::SmallListItem));
		refParent->setPixmap(0, DBSDGlobal::getIconLoader()->loadIcon("folder", DBSDIconLoader::SmallListItem));

		QListViewItem  *item = lstInstalledPkgs->firstChild();
		while (item != NULL) {
			QListViewItem  *next = item->nextSibling();

			if (item != unrefParent && item != refParent) {
				QListViewItem  *parent = (item->text(INSTPKGS_COL_REQBY).isEmpty())
				                         ? unrefParent : refParent;

				lstInstalledPkgs->takeItem(item);
				parent->insertItem(item);
			}
			item = next;
		}
	}
}


/** Moves each child to the parent's level and
  * deletes all previous parents afterwards.
  *
  * @attention This function assumes that the maximum
  *            depth in lstInstalledPkgs is 1.
  *
  */
void	wndMain::groupInstPkgsByNothing(bool toggled) {
	/*
	 * Does not continue if all items are top-level because all items
	 * would be deleted then.
	 */
	if (toggled  &&  (int) installedPkgs.count() != lstInstalledPkgs->childCount()) {
		QListViewItem  *item = lstInstalledPkgs->firstChild();
		while (item != NULL) {
			QListViewItem  *parent = NULL;
			QListViewItem  *nextItem = NULL;

			if (item->childCount() > 0) {
				parent = item;
				item = item->firstChild();
			} else
				parent = item->parent();

			nextItem = item->nextSibling();

			if (parent != NULL) {
				item->setText(INSTPKGS_COL_NAME,  toPkgID(item, INSTPKGS_COL_NAME));
				parent->takeItem(item);
				lstInstalledPkgs->insertItem(item);

				if (nextItem == NULL) {
					nextItem = parent->nextSibling();
					delete  parent;
				}
			} else
				delete  item;

			item = nextItem;
		}
	}
}



/** Reads the pkgID (e.g. "accessibility/atk") by
  * using hierarchy and text information of a
  * QListViewItem.
  */
QString	wndMain::toPkgID(QListViewItem  *item, int nameCol) {
	if (item != NULL) {
		QListViewItem  *parent = item->parent();
		QString			 name = item->text(nameCol);
		if (parent == NULL  ||  name.contains('/'))
			return  name;
		else
			return  parent->text(nameCol) + '/' + name;
	}

	return  "";
}


void	wndMain::start() {
	bool	installEmpty =  installPkgs.isEmpty(),
			upgradeEmpty =  upgradePkgs.isEmpty();

	if(installEmpty && upgradeEmpty) {
		Dialogs::infoDlg(tr("No packages were selected."));
		return;
	}
	
	if(!upgradeEmpty  &&  Dialogs::question(tr("You selected packages to upgrade.\n\n"
															 "Do you want to read the update notes first?")))
	{
		showUpdating();
		
		if(!Dialogs::question(tr("Now that you've read the update notes, do you\n"
										 "want to continue installing and upgrading packages?")))
			return;
	}

	FrmPkgUpgradeController fpum(this);
	
	PkgUpgradeEngine installEngine;
	if(!installEmpty)
	{
		installEngine.loadSettings();
		installEngine.setInstallNewPkgs(true);
		installEngine.setPackages(installPkgs);
		
		fpum.setInstallEngine(&installEngine);
	}
	
	PkgUpgradeEngine upgradeEngine;
	if(!upgradeEmpty)
	{
		upgradeEngine.loadSettings();
		upgradeEngine.setInstallNewPkgs(false);
		upgradeEngine.setPackages(upgradePkgs);
		
		fpum.setUpgradeEngine(&upgradeEngine);
	}

	PackageMgrTray upgradeTray(&fpum, false);
	new PkgUpgradeTrayNotifier(fpum.getController(), &upgradeTray);

	upgradeTray.show();
	fpum.exec();
	upgradeTray.hide();

	QString user = Variables::getValue("pkgmgr.user");
	if(!installEmpty && !user.isEmpty()) {
		bool menuUpdate = Dialogs::question(tr("Add menu entries for new applications?"));
	
		if(menuUpdate)
		{
			QProcess p(QString("/usr/local/bin/kdesu"));
			p.addArgument("-u"); p.addArgument(user);
			p.addArgument("/usr/local/bin/kappfinder");
			
			if(!p.start()) {
				Dialogs::warnDlg(tr("Error: Unable to launch the Menu Updating Tool.\n"
										  "\n"
										  "(su/kappfinder couldn't be started)"));
			} else {
				/* Wait until program exits, ignore user interaction (modal behaviour) */
				QEventLoop *el = qApp->eventLoop();
				while(p.isRunning())
					el->processEvents(QEventLoop::WaitForMore | QEventLoop::ExcludeUserInput);
			}
		}
	}
	
	initModule();
}



/* Starts an update of the ports tree. */
bool wndMain::updatePorts() {
	if((!installPkgs.isEmpty()  ||  !upgradePkgs.isEmpty())
	   && !Dialogs::question(tr("This dismisses your current selection of packages\n"
										 "that are marked for upgrading or installation."
										 "\n\n"
	                         	 "Continue?")))
			return false;

	if(PackageSettings().getPortsUpdateMethod() == PackageSettings::Portsnap)
	{
		/* portsnap */
		if(!checkPortsnap())
			return false;			
	}
	else if(PackageSettings().getUpdateServer().isEmpty()) {
		/* cvsup, no server set */
		int dlgresult;
	
		Dialogs::infoDlg(tr("Please specify an update server."));
		
		/* Settings are changed when the object is deleted,
		 * so we need to do that before checking if a server
		 * was entered below.
		 */
		frmPkgSettings *fs = new frmPkgSettings(this);
		fs->tabWidget->showPage(fs->pgUpdateServer);
		fs->tabWidget->setTabEnabled(fs->pgInstMethod, false);
		fs->tabWidget->setTabEnabled(fs->pgAdvanced, false);
		dlgresult = fs->exec();
		delete fs;

		if(dlgresult == QDialog::Rejected
			|| PackageSettings().getUpdateServer().isEmpty())
		{
			Dialogs::warnDlg("You have to specify a server to proceed.");
			return false;
		}
	}

	frmTerminalDisplay  *terminalDlg = new frmTerminalDisplay(this, NULL, true);
	new PortsUpdateFormatter(terminalDlg->getTerminalDisplay());
	terminalDlg->show();

	PortsUpdateEngine  engine;
	engine.readSettings();
	
	UnixProcess  *process = engine.getProcess();
	if(!displayProcess(terminalDlg,  process)) {
		terminalDlg->setFinished(true);
		Dialogs::warnDlg(tr("Error: Updating the list of available packages failed."
								  "\n\n"
								  "Make sure you specified a server under \"Settings\"."));
		
		/* Display the dialog until the user clicked "Cancel",
		 * giving him a chance to read what failed.
		 */
		terminalDlg->exec();
	}

	delete  terminalDlg;

	initModule();
	return true;
}


void  wndMain::upgradeOutdatedPkgs() {
	if(Dialogs::question(tr("<p>Upgrading all installed packages at once is a rather "
		  							"complex process and as such <b>might cause major breakage</b>.</p>"
		  							""
		  							"<p>We highly recommend to <b>only upgrade single packages</b> for "
		  							"security reasons or because of bugfixes or important new features.</p>"
		  							""
								   "<p>If you really want to continue, we strongly advise you to "
									"<b>read the upgrade notes</b> to be informed of major changes.</p>"
								   ""
								   "<p>Do you want to proceed?</p>")))
	{
		Package *pkg = outdatedPkgs.first();
		while(pkg != NULL)
		{
			addTaskPkg(pkg, Upgrade, false);

			pkg = outdatedPkgs.next();
		}
	}
}



/** Displays the output of a process in a terminal dialog.
  *
  * Returns FALSE when the terminalDlg is NULL or the
  * process fails, TRUE when the process is NULL or it's
  * execution succeeds.
  *
  * @see runProcess()
  */
bool	wndMain::displayProcess(frmTerminalDisplay  *terminalDlg,  UnixProcess  *process) {
	if(terminalDlg == NULL)
		return false;

	if(process == NULL)
		return false;

	TerminalDisplay *dsp = terminalDlg->getTerminalDisplay();
	dsp->setProcess(process);

	if(process->start()) {
		/* Keep processing events until invoked process exits */
		QEventLoop *appEvLoop = QApplication::eventLoop();
		bool cancelNoticed = false;
	
		while (process->isRunning()  &&  !cancelNoticed) {
			appEvLoop->processEvents(QEventLoop::WaitForMore);
	
			if(!cancelNoticed  &&  terminalDlg->wasCanceled()) {
				/* User canceled the operation, try to terminate process. */
				process->tryTerminateGroup();
				cancelNoticed = true;
			}
		}
	
		return  (process->normalExit()  &&  (process->exitStatus() == 0));
	};
	
	return false;
}


void wndMain::init() {
   DBSDIconLoader  *iloader = DBSDGlobal::getIconLoader();

	setIcon(iloader->loadIcon("package", DBSDIconLoader::Size32x32));
	acAbout->setIconSet(iloader->loadIcon("package", DBSDIconLoader::Button));
	
	QPixmap showHideDetails = iloader->loadIcon("view_detailed", DBSDIconLoader::Button);
	acShowHideDetails->setIconSet(showHideDetails);
	btnShowHideDetails->setIconSet(showHideDetails);

   /* Add toolbar buttons and actions */
   QToolButton *tbbCvsUp, *tbbDeinstall, *tbbClean, *tbbShowHidePendingOperations;
   
   acCvsUp->setIconSet(iloader->loadIcon("compfile",  DBSDIconLoader::Button));
   tbbCvsUp = new QToolButton(acCvsUp->iconSet(),
                              tr("Update List"),
                              tr("Downloads the latest package list from the server selected in \"Settings\""),
                              acCvsUp, SLOT(activate()),
                              tbMain);
   tbbCvsUp->setUsesTextLabel(true);
   tbbCvsUp->setTextPosition(QToolButton::BesideIcon);
                              
   acDeinstall->setIconSet(iloader->loadIcon("editdelete",  DBSDIconLoader::Button));
   tbbDeinstall = new QToolButton(acDeinstall->iconSet(),
                                  tr("Deinstall..."),
                                  tr("Wizard that lets you select packages to deinstall"),
                                  acDeinstall, SLOT(activate()),
                                  tbMain);
   tbbDeinstall->setUsesTextLabel(true);
   tbbDeinstall->setTextPosition(QToolButton::BesideIcon);
   
   acClean->setIconSet(iloader->loadIcon("recycled",  DBSDIconLoader::Button));
   tbbClean = new QToolButton(acClean->iconSet(),
                              tr("Clean..."),
                              tr("Remove obsolete and installation files from the system"),
                              acClean, SLOT(activate()),
                              tbMain);
   tbbClean->setUsesTextLabel(true);
   tbbClean->setTextPosition(QToolButton::BesideIcon);

   acShowHidePendingOperations->setIconSet(iloader->loadIcon("exec",  DBSDIconLoader::Button));
   tbbShowHidePendingOperations = new QToolButton(tbMain);
	tbbShowHidePendingOperations->setIconSet(acShowHidePendingOperations->iconSet());
	tbbShowHidePendingOperations->setTextLabel(acShowHidePendingOperations->menuText());
	tbbShowHidePendingOperations->setToggleButton(true);
	tbbShowHidePendingOperations->setOn(acShowHidePendingOperations->isOn());
   tbbShowHidePendingOperations->setUsesTextLabel(true);
   tbbShowHidePendingOperations->setTextPosition(QToolButton::BesideIcon);
	connect(tbbShowHidePendingOperations, SIGNAL(toggled(bool)), acShowHidePendingOperations, SLOT(setOn(bool)));
	connect(acShowHidePendingOperations, SIGNAL(toggled(bool)), tbbShowHidePendingOperations, SLOT(setOn(bool)));

	acSettings->setIconSet(iloader->loadIcon("configure",  DBSDIconLoader::Button));
   acExit->setIconSet(iloader->loadIcon("exit",  DBSDIconLoader::Button));
	pixPkg->setPixmap(iloader->loadIcon("package",  DBSDIconLoader::SmallLogo));
	pixCategory->setPixmap(iloader->loadIcon("folder",  DBSDIconLoader::SmallLogo));
	btnUpgradeAll->setIconSet(iloader->loadIcon("2rightarrow",  DBSDIconLoader::Button));
	btnSearch->setIconSet(iloader->loadIcon("find",  DBSDIconLoader::Button));
	btnStart->setIconSet(iloader->loadIcon("button_ok",  DBSDIconLoader::Button));
	btnPkgWebsite->setIconSet(iloader->loadIcon("konqueror",  DBSDIconLoader::Button));
	btnInstall->setIconSet(iloader->loadIcon("editcopy",  DBSDIconLoader::Button));
	btnUpgrade->setIconSet(iloader->loadIcon("up",  DBSDIconLoader::Button));
	btnCalculateInstalledSize->setIconSet(iloader->loadIcon("kcalc",  DBSDIconLoader::Button));
	btnCalculateAllSizes->setIconSet(iloader->loadIcon("kcalc",  DBSDIconLoader::Button));
	btnLongDescription->setIconSet(iloader->loadIcon("text_left",  DBSDIconLoader::Button));
	btnRemovePendingOp->setIconSet(iloader->loadIcon("cancel",  DBSDIconLoader::Size16x16));
	btnShowUpdating->setIconSet(iloader->loadIcon("info",  DBSDIconLoader::Size16x16));
	btnClearSearch->setPixmap(iloader->loadIcon("locationbar_erase",  DBSDIconLoader::Size16x16));
	pixPendingOps->setPixmap(iloader->loadIcon("exec",  DBSDIconLoader::Size16x16));
	pixMainHint->setPixmap(iloader->loadIcon("messagebox_info",  DBSDIconLoader::HintIcon));
	pixSecurityHint->setPixmap(iloader->loadIcon("halfencrypted",  DBSDIconLoader::HintIcon));
	pixWelcome->setPixmap(iloader->loadIcon("desktopbsd", DBSDIconLoader::Size64x64));
	tbtnInstall->setPixmap(iloader->loadIcon("editcopy", DBSDIconLoader::Size32x32));
	tbtnUpgrade->setPixmap(iloader->loadIcon("up", DBSDIconLoader::Size32x32));
	tbtnUninstall->setPixmap(iloader->loadIcon("editdelete", DBSDIconLoader::Size32x32));
	tbtnSettings->setPixmap(iloader->loadIcon("configure", DBSDIconLoader::Size32x32));
}


void wndMain::launchSettings() {
	frmPkgSettings *fs = new frmPkgSettings(this);
	fs->exec();
	delete fs;

	setIncrementalFind();
}


/** @see runProcess(UnixProcess*, frmTerminalDisplay*) */
bool wndMain::runProcess(UnixProcess  *process) {
	return runProcess(process, NULL);
}


/** Waits until a process finished or the user clicked "Cancel".
  *
  * @attention Deletes the given process itself after execution.
  */
bool wndMain::runProcess(UnixProcess  *process, frmTerminalDisplay  *terminalDlg) {
	if(process == NULL)
		return true;

	if(process->start()) {
		/* Keep processing events until invoked process exits */
		QEventLoop *appEvLoop = QApplication::eventLoop();
	
		while (process->isRunning())
			appEvLoop->processEvents(QEventLoop::WaitForMore);
	
		return  (process->normalExit()  &&  (process->exitStatus() == 0));
	};
	
	return false;
}


void wndMain::calculatePkgSize(Package * pkg) {
	if(pkg != NULL) {
		QString	size = pkg->getInstalledSize().toString(Size::MB);
		
		if(shownPkg == pkg) {
			lblPkgInstalledSize->setText(size);
			btnCalculateInstalledSize->hide();
			lblPkgInstalledSize->show();
		}
		
		QListViewItem  *item = findPkgInListView(lstInstalledPkgs, INSTPKGS_COL_NAME, pkg);
		if(item != NULL)
			item->setText(INSTPKGS_COL_SIZE, size);
	}
}


void wndMain::clearListBox(QListBox * lb) {
	if(lb == NULL)
		return;

	for(int i = 0;  i < (int) lb->count();  ++i)
		lb->setSelected(i, true);
		
	removeSelectionFromListBox(lb);
}


/** Tries to identify a package in a QListView using toPkgID()
  * to compare the resulting package ID of the items to the
  * one of the given package (uses the given column as name column).
  * 
  * Returns a pointer to a package or NULL if one of the given
  * arguments is NULL or no matching package was found.
  */
QListViewItem  *wndMain::findPkgInListView(QListView *lv,  int column,  Package *pkg) {
	if(lv == NULL || pkg == NULL)
		return  NULL;

	QString	id = pkg->getID();
	QListViewItemIterator  i(lv);
	for(;  *i != NULL;  ++i)
		if(toPkgID(*i, column) == id)
			return  *i;
			
	return  NULL;
}


void wndMain::showDeinstallationWizard() {
	frmDeinstallPkgs* dlg = new frmDeinstallPkgs(this);
	dlg->setInstalledPackages(installedPkgs);
	dlg->exec();
		
	if(dlg->result() == QDialog::Accepted) {
		frmTerminalDisplay  *terminalDlg = new frmTerminalDisplay(this, NULL, true);
		new PortToolsFormatter(terminalDlg->getTerminalDisplay());
		terminalDlg->show();
			
		PkgDeinstallEngine  deinstallEngine;
		deinstallEngine.setPackages(dlg->getSelectedPackages());
		deinstallEngine.setRemoveEmptyDirs(true);
		
		/* The correct dependencies should be handled by the deinstallation
		 * wizard anyways, so this should cause no problems, except for
		 * failing deinstallation scripts and such stuff.
		 */
		deinstallEngine.setForce(true);
	
		UnixProcess  *process = deinstallEngine.getProcess();
		
		if(process == NULL  ||  !displayProcess(terminalDlg,  process))
			Dialogs::warnDlg(tr("An error occured. Probably not all packages were deinstalled.\n"));
		
			
		delete  terminalDlg;
		initModule();
	}
}



void wndMain::calculateAllSizes() {
	QProgressDialog  dlg(tr("Calculating sizes..."), tr("Cancel"), installedPkgs.count(), this, NULL, true);
	dlg.setCaption(tr("DesktopBSD"));
	
	QPtrListIterator<Package>  i(installedPkgs);
	for(int n = 0;  *i != NULL;  ++i, ++n) {
		if(dlg.wasCanceled())
			return;
		
		dlg.setProgress(n);
		qApp->processEvents();
		
		calculatePkgSize(*i);
	}
}


void wndMain::calculateShownPkgSize() {
	calculatePkgSize(shownPkg);
}


void wndMain::showLongDescr() {
	QString desc = shownPkg->getLongDescr();
	Dialogs::infoDlg(desc.isEmpty() ? tr("No description found.") : desc);
}

/** Displays the /usr/ports/UPDATING file, which contains important
  * information for upgrading ports. This method returns not immediately,
  * but after the user closed the dialog.
  */
void wndMain::showUpdating() {
	frmTextBrowser *dlg = new frmTextBrowser(this);
	dlg->setDisplaySource(UPDATING_FILE);
	dlg->setCaption(tr("Updating Hints"));
	dlg->exec();
		
	delete dlg;
}


void wndMain::showCleaningWizard() {
	frmCleanPorts *dlg = new frmCleanPorts(this);
	dlg->exec();
}


void wndMain::about() {
	QMessageBox::about(this,
							 tr("About"),
							 tr("<qt><b>DesktopBSD Package Manager</b>"
							 	 "<p>Package management based on FreeBSD ports and packages.</p>"
							 	 "<p>Licensed under the terms of the 2-clause BSD license.</p></qt>"));
}



void wndMain::showIntroduction() {
	showIntroduction(false);
}


void wndMain::showIntroduction(bool firstRun) {
	const QString info = tr("In DesktopBSD, software is bundled in so-called packages. These\n"
									"packages can be applications or components shared between different\n"
									"applications. Most applications reuse functionality of other\n"
									"software to avoid duplications, so they depend on other packages.\n"
									"\n"
									"The package list contains available packages. You should update it\n"
									"regularly to know about security upgrades and new program versions.\n"
									"\n"
									"%1");

	Dialogs::infoDlg(info.arg(firstRun ? tr("You can open this introduction from the \"Help\" menu later.") : ""));
}


void wndMain::performSecurityCheck()
{
	frmTerminalDisplay  *terminalDlg = new frmTerminalDisplay(this, NULL, true);
 	new PortAuditFormatter(terminalDlg->getTerminalDisplay());
	terminalDlg->show();
		
	UnixProcess  *process = PortAuditEngine::getProcess(PortAuditEngine::FetchDatabase | PortAuditEngine::CheckAll);

	if(process == NULL)
		Dialogs::warnDlg(tr("Error: Unable to perform security check."));
	else {
		/* Ignore result, portaudit usually exits <> 0 */
		displayProcess(terminalDlg,  process);
		terminalDlg->setFinished(true);
		terminalDlg->exec(); /* Keep open until user clicks 'Close' */
	}

	delete process;
	delete terminalDlg;

	checkLastSecurityCheck();
}


void wndMain::checkLastSecurityCheck()
{
	int days = QFileInfo(PORTAUDIT_DB_FILE).lastModified().daysTo(QDateTime::currentDateTime());
	if(days == 0)
		lblLastSecurityCheck->setText(tr("Last security check performed today."));
	else if(days == 1)
		lblLastSecurityCheck->setText(tr("Last security check performed yesterday."));
	else
		lblLastSecurityCheck->setText(tr("Last security check performed %1 days ago.").arg(days));

	if(days < SECURITY_WARNING)
		lblLastSecurityCheck->setPaletteForegroundColor(Qt::darkGreen);
	else if(days >= SECURITY_WARNING && days < SECURITY_CRITICAL)
		lblLastSecurityCheck->setPaletteForegroundColor(Qt::darkYellow);
	else
		lblLastSecurityCheck->setPaletteForegroundColor(Qt::red);
}


bool wndMain::checkPortsnap()
{
	if(PackageSettings().getPortsUpdateMethod() == PackageSettings::Portsnap)
	{
		PortsnapEngine pe;
		pe.readSettings();
		
		if(!QDir(PORTS_DIRECTORY).exists() || (!pe.portsnapUsed()
			&& Dialogs::question(tr("portsnap hasn't been used on this system before.\n"
											"\n"
											"Do you want to automatically configure portsnap\n"
											"and download the package list now?"))))
		{
			if(pe.preparePortsnap() < 0)
			{
				Dialogs::warnDlg("Error: Couldn't create portsnap's working directory");
				return false;
			}

			UnixProcess *p = pe.getExtractProcess(true);

			frmTerminalDisplay  *terminalDlg = new frmTerminalDisplay(this, NULL, true);
			new PortsUpdateFormatter(terminalDlg->getTerminalDisplay());
			terminalDlg->show();

			bool success = displayProcess(terminalDlg, p);
			if(!success)
			{
				terminalDlg->setFinished(true);
				Dialogs::warnDlg(tr("Error: Downloading and installing the package list failed."));

				/* Display the dialog until the user clicked "Cancel",
				 * giving him a chance to read what failed.
				 */
				terminalDlg->exec();
			} else
				Dialogs::infoDlg(tr("portsnap successfully configured."));

			delete terminalDlg;
			delete p;

			return success;
		}
	}

	return true;
}


void wndMain::installedPkgsGroupByChanged(int selected)
{
	PackageSettings().setInstalledPkgsGroupBy(selected);
}


void wndMain::closeEvent(QCloseEvent *e)
{
	//Exit on close
	qApp->quit();
}


void wndMain::setIncrementalFind()
{
	if(PackageSettings().getIncrementalPackageFind())
	{
		btnSearch->hide();
		connect(editSearch, SIGNAL(textChanged(const QString &)), this, SLOT(findPkg()));
	} else {
		btnSearch->show();
		disconnect(editSearch, SIGNAL(textChanged(const QString &)), this, SLOT(findPkg())); 
	}
}


void wndMain::showAvailableTab()
{
	tabPkgLists->showPage(tabAvailablePkgs);
}


void wndMain::showInstalledTab()
{
	tabPkgLists->showPage(tabInstalledPkgs);
}
