/***************************************************************************
 *	Copyright (C) 2005 by Karye												*
 *	karye@users.sourceforge.net												*
 *																			*
 *	This program is free software; you can redistribute it and/or modify	*
 *	it under the terms of the GNU General Public License as published by	*
 *	the Free Software Foundation; either version 2 of the License, or		*
 *	(at your option) any later version.										*
 *																			*
 *	This program is distributed in the hope that it will be useful,			*
 *	but WITHOUT ANY WARRANTY; without even the implied warranty of			*
 *	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the			*
 *	GNU General Public License for more details.							*
 *																			*
 *	You should have received a copy of the GNU General Public License		*
 *	along with this program; if not, write to the							*
 *	Free Software Foundation, Inc.,											*
 *	59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.				*
 ***************************************************************************/

#include <klocalizedstring.h>    // for i18n
#include <kmessagebox.h>         // for questionYesNoList, Yes, warningYesNo
#include <kstandardguiitem.h>    // for no, yes
#include <qabstractitemmodel.h>  // for QModelIndex
#include <qaction.h>             // for QAction
#include <qcheckbox.h>           // for QCheckBox
#include <qcursor.h>             // for QCursor
#include <qdebug.h>              // for QDebug, operator<<
#include <qglobal.h>             // for qDebug, foreach
#include <qicon.h>               // for QIcon
#include <qlist.h>               // for QList
#include <qmenu.h>               // for QMenu
#include <qnamespace.h>          // for CustomContextMenu
#include <qpushbutton.h>         // for QPushButton
#include <qstringlist.h>         // for QStringList
#include <qstringliteral.h>      // for QStaticStringData, QStringLiteral
#include <qtextbrowser.h>        // for QTextBrowser
#include <qwhatsthis.h>          // for QWhatsThis

#include "common.h"              // for QueueSingleton, EmergeSingleton, Sig...
#include "emerge.h"              // for Emerge
#include "global.h"              // for formatTime
#include "log.h"                 // for Log
#include "packageinspector.h"    // for PackageInspector
#include "packagelistitem.h"     // for PackageListItem
#include "portage.h"             // for Portage
#include "portagedb.h"           // for KurooDB
#include "queue.h"               // for Queue
#include "queuelistitem.h"       // for QueueListItem
#include "queuelistview.h"       // for QueueListView
#include "queuetab.h"
#include "settings.h"            // for KurooConfig
#include "signalist.h"           // for Signalist
#include "statusbar.h"           // for KurooStatusBar

class QPoint;

/**
* @class QueueTab
* @short Page for the installation queue.
*/
QueueTab::QueueTab( QWidget* parent, PackageInspector *packageInspector )
	: QWidget( parent ), m_hasCheckedQueue( false ), m_initialQueueTime( QString() ), m_packageInspector( packageInspector )
{
	setupUi( this );
	// Connect What's this button
	connect(pbWhatsThis, &QPushButton::clicked, this, &QueueTab::slotWhatsThis);

	// Rmb actions.
	queueView->setContextMenuPolicy(Qt::CustomContextMenu);
	connect(queueView, &QueueListView::customContextMenuRequested, this, &QueueTab::slotContextMenu);

	m_removeFromQueue = new QAction( QIcon::fromTheme(QStringLiteral("list-remove")), i18n( "&Remove"), this );
	connect(m_removeFromQueue, &QAction::triggered, this, &QueueTab::slotRemove);
	m_removeFromWorld = new QAction( QIcon::fromTheme(QStringLiteral("kuroo_world")), i18n( "Remove From &World"), this );
	connect(m_removeFromWorld, &QAction::triggered, this, &QueueTab::slotRemoveWorld);
	m_addToWorld = new QAction( QIcon::fromTheme(QStringLiteral("kuroo_world")), i18n( "Add To &World"), this );
	connect(m_addToWorld, &QAction::triggered, this, &QueueTab::slotAddWorld);
	m_clearQueue = new QAction( QIcon::fromTheme(QStringLiteral("edit-clear")), i18n( "Remove &All"), this );
	connect(m_clearQueue, &QAction::triggered, this, &QueueTab::slotClear);
	m_packageDetails = new QAction( i18n( "&Details"), this );
	connect(m_packageDetails, &QAction::triggered, this, &QueueTab::slotAdvanced);

	// Button actions.
	connect(pbCheck, &QPushButton::clicked, this, &QueueTab::slotCheck);
	connect(pbRemove, &QPushButton::clicked, this, &QueueTab::slotRemove);
	connect(pbClear, &QPushButton::clicked, this, &QueueTab::slotClear);
	connect(pbAdvanced, &QPushButton::clicked, this, &QueueTab::slotAdvanced);
	connect(queueView, &QueueListView::itemDoubleClicked, this, &QueueTab::slotAdvanced);

	connect(cbRemove, &QCheckBox::clicked, this, &QueueTab::slotRemoveInstalled);

	connect(queueView, &QueueListView::selectionChangedSignal, this, &QueueTab::slotPackage);
	connect(queueView, &QueueListView::selectionChangedSignal, this, &QueueTab::slotButtons);

	// Lock/unlock if kuroo is busy
	connect( SignalistSingleton::Instance(), &Signalist::signalKurooBusy, this, &QueueTab::slotBusy );

	// Reload view after changes in queue.
	connect( QueueSingleton::Instance(), &Queue::signalQueueChanged, this, &QueueTab::slotReload );
	connect( PortageSingleton::Instance(), &Portage::signalPortageChanged, this, &QueueTab::slotRefresh );

	// Forward emerge start/stop/completed to package progressbar.
	connect( QueueSingleton::Instance(), &Queue::signalPackageStart, queueView, &QueueListView::slotPackageStart );
	connect( QueueSingleton::Instance(), &Queue::signalPackageComplete, queueView, &QueueListView::slotPackageComplete );
	connect( QueueSingleton::Instance(), &Queue::signalPackageAdvance, queueView, &QueueListView::slotPackageProgress );

	// Update Queue summary timer
	connect( QueueSingleton::Instance(), &Queue::signalPackageAdvance, this, &QueueTab::slotQueueSummary );
	connect( QueueSingleton::Instance(), &Queue::signalPackageComplete, this, &QueueTab::slotQueueSummary );

	// Recalculate package when user change settings in Inspector
	connect(m_packageInspector, &PackageInspector::signalPackageChanged, this, &QueueTab::slotPackage);
	connect(m_packageInspector, &PackageInspector::signalNextPackage, this, &QueueTab::slotNextPackage);
	connect(m_packageInspector, &PackageInspector::finished, this, &QueueTab::slotButtons);

	slotInit();
}

/**
* Save listview geometry.
*/
QueueTab::~QueueTab()
{
	KurooConfig::setForceConf( cbForceConf->isChecked() );
	KurooConfig::setDownload( cbDownload->isChecked() );
	KurooConfig::setNoWorld( cbNoWorld->isChecked() );
	KurooConfig::setRemove( cbRemove->isChecked() );
	KurooConfig::setBackupPkg( cbBackupPkg->isChecked() );
	KurooConfig::setUpdateNewUse( cbUpdate->isChecked() );

	if ( KurooConfig::enableEclean() || KurooConfig::revdepEnabled() )
		cbSkipHousekeeping->setDisabled( false );
	else
		cbSkipHousekeeping->setDisabled( true );
}

/**
* Initialize Queue view.
*/
void QueueTab::slotInit()
{
	//queueFrame->setPaletteBackgroundColor( colorGroup().base() );
	cbForceConf->setChecked( KurooConfig::forceConf() );
	cbDownload->setChecked( KurooConfig::download() );
	cbNoWorld->setChecked( KurooConfig::noWorld() );
	cbRemove->setChecked( KurooConfig::remove() );
	cbBackupPkg->setChecked( KurooConfig::backupPkg() );
	cbUpdate->setChecked( KurooConfig::updateNewUse() );

	if ( KurooConfig::enableEclean() || KurooConfig::revdepEnabled() )
		cbSkipHousekeeping->setDisabled( false );
	else
		cbSkipHousekeeping->setDisabled( true );


	cbDownload->setToolTip( i18n(  "<qt><table width=300><tr><td>Instead of doing any package building, "
									"just perform fetches for all packages (the main package as well as all dependencies), "
									"grabbing all potential files.</td></tr></table></qt>" ) );

	cbNoWorld->setToolTip( i18n(   "<qt><table width=300><tr><td>Emerge as normal, "
									"but do not add the packages to the @world profile for later updating.</td></tr></table></qt>" ) );

	cbForceConf->setToolTip( i18n( "<qt><table width=300><tr><td>Causes portage to disregard merge records indicating that a config file"
									"inside of a CONFIG_PROTECT directory has been merged already. "
									"Portage will normally merge those files only once to prevent the user"
									"from dealing with the same config multiple times. "
									"This flag will cause the file to always be merged.</td></tr></table></qt>" ) );

	cbBackupPkg->setToolTip( i18n(   "<qt><table width=300><tr><td>Emerge as normal, "
									"but use quickpkg to make a backup of the installed ebuilds before merging.</td></tr></table></qt>" ) );

	//TODO: port to kde4
	// Keyboard shortcuts
	/*KAccel* pAccel = new KAccel( this );
	pAccel->insert( "View package details...", i18n("View package details..."), i18n("View package details..."),
					Qt::Key_Return, this, SLOT( slotAdvanced() ) );*/

	pbRemove->setIcon( QIcon::fromTheme(QStringLiteral("list-remove")) );
	pbClear->setIcon( QIcon::fromTheme(QStringLiteral("edit-clear")) );
	//pbAdvanced->setIcon( QIcon::fromTheme(QStringLiteral("options")) );
	pbCheck->setIcon( QIcon::fromTheme(QStringLiteral("run-build")) );
	pbGo->setIcon( QIcon::fromTheme(QStringLiteral("run-build-install")) );
	pbWhatsThis->setIcon( QIcon::fromTheme(QStringLiteral("help-about")) );
}

/**
* What's this info explaning this tabs functionality.
*/
void QueueTab::slotWhatsThis()
{
	QWhatsThis::showText( QCursor::pos(), i18n( "<qt>"
			"The emerge queue quickly shows which packages listed for installation.<br/>"
			"Since many applications depend on each other, any attempt to install a certain software package might result in the installation "
			"of several dependencies as well. Don't worry, Portage handles dependencies well.<br/><br/>"
			"If you want to find out what Portage would install when you ask it to install a certain package, press 'Check Installation'.<br/>"
			"When all dependencies are press 'Start Installation'.</qt>" ), this );
}

/**
* Forward signal from next-buttons.
* @param isNext
*/
void QueueTab::slotNextPackage( bool isNext )
{
	if ( !m_packageInspector->isParentView( VIEW_QUEUE ) )
		return;

	queueView->nextPackage( isNext );
}


//////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Queue view slots
//////////////////////////////////////////////////////////////////////////////////////////////////////////////

/**
* Reload queue when package view is changed, fex when package is removed.
*/
void QueueTab::slotRefresh()
{
	DEBUG_LINE_INFO;
	if ( !QueueSingleton::Instance()->isQueueBusy() )
	{
		slotRemoveInstalled();
		queueView->insertPackageList( m_hasCheckedQueue );
	}

	slotBusy();
}

/**
* Load Queue packages.
*/
void QueueTab::slotReload( bool hasCheckedQueue )
{
	DEBUG_LINE_INFO;
	// Reenable the inspector after queue changes
	//m_packageInspector->setDisabled( true );
	pbAdvanced->setDisabled( true );
	m_hasCheckedQueue = hasCheckedQueue;

	// Load all packages
	slotRefresh();
	if (hasCheckedQueue) {
		queueView->expandToDepth(2);
	}

	// Enable the gui
	slotBusy();

	m_initialQueueTime = formatTime( queueView->totalDuration() );
	KurooStatusBar::instance()->clearElapsedTime();
	slotQueueSummary();
}

/**
* View current queue summary.
*/
void QueueTab::slotQueueSummary()
{
	queueBrowser->clear();
	QString queueBrowserLines(   i18n( "<table width=100% border=0 cellpadding=0><tr><td colspan=2><b>Summary</b></td></tr>" ) );
			queueBrowserLines += i18n( "<tr><td>Number&nbsp;of&nbsp;packages:</td><td> %1</td></tr>", queueView->count() );
			queueBrowserLines += i18n( "<tr><td>Initial&nbsp;estimated&nbsp;time:</td><td> %1</td></tr>", m_initialQueueTime );
			queueBrowserLines += i18n( "<tr><td>Elapsed&nbsp;time:</td><td> %1</td></tr>", formatTime( KurooStatusBar::instance()->elapsedTime() ) );
			queueBrowserLines += i18n( "<tr><td>Estimated&nbsp;time&nbsp;remaining:</td><td> %1</td></tr></table>", formatTime( queueView->totalDuration() ) );
	queueBrowser->setText( queueBrowserLines );
}

//////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Toggle button slots
//////////////////////////////////////////////////////////////////////////////////////////////////////////////

/**
* Disable/enable buttons when kuroo busy signal is received.
*/
void QueueTab::slotBusy()
{
	// No db or queue is empty - no fun!
	if ( !SignalistSingleton::Instance()->isKurooReady() || KurooDBSingleton::Instance()->isQueueEmpty() ) {
		if ( !SignalistSingleton::Instance()->isKurooReady() )
			qDebug() << "QueueTab.slotBusy kuroo is busy";
		pbRemove->setDisabled( true );
		pbAdvanced->setDisabled( true );
		pbClear->setDisabled( true );
		cbDownload->setDisabled( true );
		cbForceConf->setDisabled( true );
		cbNoWorld->setDisabled( true );
		pbCheck->setDisabled( true );
		pbGo->setDisabled( true );
		cbSkipHousekeeping->setDisabled( true );
	}
	else
		slotButtons();
}

/**
* Disable buttons if no package is selected or kuroo is busy emerging.
*/
void QueueTab::slotButtons()
{
	if ( m_packageInspector->isVisible() )
		return;

	qDebug() << "QueueTab.slotButtons, emerge is running" << EmergeSingleton::Instance()->isRunning() << "m_hasCheckedQueue" << m_hasCheckedQueue;
	// Kuroo is busy emerging toggle to "abort"
	if ( EmergeSingleton::Instance()->isRunning() ) {
		pbGo->setText( i18n( "Abort Installation" ) );
		disconnect(pbGo, &QPushButton::clicked, this, &QueueTab::slotGo);
		disconnect(pbGo, &QPushButton::clicked, this, &QueueTab::slotStop);
		connect(pbGo, &QPushButton::clicked, this, &QueueTab::slotStop);
	}
	else {
		pbGo->setText( i18n( "Step &2: Start Installation" ) );
		disconnect(pbGo, &QPushButton::clicked, this, &QueueTab::slotGo);
		disconnect(pbGo, &QPushButton::clicked, this, &QueueTab::slotStop);
		connect(pbGo, &QPushButton::clicked, this, &QueueTab::slotGo);
		if ( KurooConfig::enableEclean() || KurooConfig::revdepEnabled() )
			cbSkipHousekeeping->setDisabled( false );
		else
			cbSkipHousekeeping->setDisabled( true );
	}

	// No package selected, disable all buttons
	if ( queueView->selectedPackagesByIds().isEmpty() ) {
		pbRemove->setDisabled( true );
		pbAdvanced->setDisabled( true );
	}

	if (KurooDBSingleton::Instance()->isQueueEmpty())
		return;

	// Queue is not empty - enable button "Remove all" and "Check Installation"
	cbDownload->setDisabled( false );

	// When emerging packages do not allow user to change the queue
	if ( EmergeSingleton::Instance()->isRunning() || SignalistSingleton::Instance()->isKurooBusy() ) {
		pbRemove->setDisabled( true );
		pbClear->setDisabled( true );
		pbCheck->setDisabled( true );
		cbSkipHousekeeping->setDisabled( true );
	}
	else {
		pbRemove->setDisabled( false );
		pbClear->setDisabled( false );
		pbCheck->setDisabled( false );
		if ( KurooConfig::enableEclean() || KurooConfig::revdepEnabled() )
			cbSkipHousekeeping->setDisabled( false );
		else
			cbSkipHousekeeping->setDisabled( true );
	}

	// packages in queue are "checked" - enable checkboxes
	if ( m_hasCheckedQueue ) {
		pbGo->setDisabled( false );
		cbForceConf->setDisabled( false );
		cbNoWorld->setDisabled( false );
		cbRemove->setDisabled( false );
	}
	else {
		pbGo->setDisabled( true );
		cbForceConf->setDisabled( true );
		cbNoWorld->setDisabled( true );
		cbRemove->setDisabled( true );
	}

	m_packageInspector->setDisabled( false );
	pbAdvanced->setDisabled( false );
}

//////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Package slots
//////////////////////////////////////////////////////////////////////////////////////////////////////////////

/**
* Emerge all packages in the installation queue.
*/
void QueueTab::slotCheck()
{
	// Only user-end packages not the dependencies
	QStringList packageList = queueView->allPackagesNoChildren();
	qDebug() << packageList;
	if( cbUpdate->isChecked() )
		packageList.prepend( QStringLiteral("-uN") );
	EmergeSingleton::Instance()->pretend( packageList );
}

/**
* Emerge all packages in the installation queue.
*/
void QueueTab::slotGo()
{
	DEBUG_LINE_INFO;
	// If emerge is running I'm the abort function
	if ( EmergeSingleton::Instance()->isRunning() )
		slotStop();

	// Only user-end packages not the dependencies
	QStringList packageList = queueView->allPackagesNoChildren();

	if ( cbSkipHousekeeping->isChecked() )
		EmergeSingleton::Instance()->setSkipHousekeeping( true );
	else
		EmergeSingleton::Instance()->setSkipHousekeeping( false );

	// Only download? prepend --fetch-all-uri
	// Else, let's install the user-end packages
	if ( cbDownload->isChecked() ) {
		switch( KMessageBox::questionTwoActionsList( this,
			i18n("Do you want to Download following packages?"), packageList, i18n("Installation queue"),
			KGuiItem( i18n("Download"), QStringLiteral("download") ), KStandardGuiItem::cancel(), QStringLiteral("dontAskAgainDownload"), KMessageBox::Dangerous ) ) {

				case KMessageBox::PrimaryAction:
					packageList.prepend( QStringLiteral("--fetch-all-uri") );
					KurooStatusBar::instance()->setTotalSteps( queueView->totalDuration() );
					QueueSingleton::Instance()->installQueue( packageList );
				default:
					break;
			}
	}
	else {
		switch( KMessageBox::questionTwoActionsList( this,
			i18n("Do you want to install following packages?"), packageList, i18n("Installation queue"),
			KGuiItem( i18n("Install"), QStringLiteral("kuroo_view_queue")  ), KStandardGuiItem::cancel(), QStringLiteral("dontAskAgainInstall"), KMessageBox::Dangerous ) ) {

				case KMessageBox::PrimaryAction: {
					if( cbUpdate->isChecked() )
						packageList.prepend( QStringLiteral("-uN") );

					// Force portage to reinstall files protected in CONFIG_PROTECT
					if ( cbForceConf->isChecked() )
						packageList.prepend( QStringLiteral("--noconfmem") );

					// Emerge as normal, but do not add the packages to the @world profile for later updating.
					if ( cbNoWorld->isChecked() )
						packageList.prepend( QStringLiteral("--oneshot") );

					// Doing 'setTotalSteps' before 'installQueue' will allow the 'stopTimer' on error inside of emerge.cpp
					// to stop the status bar, but it will also make the status bar move while the auth dialog is waiting
					KurooStatusBar::instance()->setTotalSteps( queueView->totalDuration() );
					QueueSingleton::Instance()->installQueue( packageList );
				}
				default:
					break;
			}
	}
}

/**
* Kill the running emerge process.
*/
void QueueTab::slotStop()
{
	qDebug() << "QueueTab::slotStop emerge is running" << EmergeSingleton::Instance()->isRunning();
	if ( EmergeSingleton::Instance()->isRunning() ) {
		switch ( KMessageBox::warningTwoActions( this,
			i18n( "Do you want to abort the running installation?" ), i18n("Abort"), KGuiItem( i18n("Abort"), QStringLiteral("kuroo_warning") ), KStandardGuiItem::cont() ) ) {

				case KMessageBox::PrimaryAction:
					EmergeSingleton::Instance()->stop();
					KurooStatusBar::instance()->setProgressStatus( QString(), i18n("Done.") );
					// added an explicit busy switch to allow someone to continue once
					// an abort has happened. 20070223 - aga
					SignalistSingleton::Instance()->setKurooBusy(false);
					// added a log entry for the abort
					LogSingleton::Instance()->writeLog( i18n("Emerge aborted by user."), KUROO );
					SignalistSingleton::Instance()->emergeAborted();
				default:
					break;
			}
	} else
		slotButtons();
}

/**
* Launch emerge pretend of packages in queue.
*/
void QueueTab::slotPretend()
{
	PortageSingleton::Instance()->pretendPackageList( queueView->allId() );
}

/**
* Remove package from Queue.
*/
void QueueTab::slotRemove()
{
	if ( isVisible() )
		m_packageInspector->hide();

	QueueSingleton::Instance()->removePackageIdList(queueView->selectedPackagesByIds());
}

/**
* Remove all packages from Queue.
*/
void QueueTab::slotClear()
{
	if ( isVisible() )
		m_packageInspector->hide();

	QueueSingleton::Instance()->reset();
}

/**
* Remove package from Queue.
*/
void QueueTab::slotRemoveInstalled()
{
	QueueSingleton::Instance()->setRemoveInstalled( cbRemove->isChecked() );
}

/**
* Open advanced dialog with: ebuild, versions, use flags...
*/
void QueueTab::slotAdvanced()
{
	DEBUG_LINE_INFO;
	pbRemove->setDisabled( true );
	pbClear->setDisabled( true );
	pbCheck->setDisabled( true );
	pbAdvanced->setDisabled( true );
	pbGo->setDisabled( true );

	if ( queueView->currentPackage() )
		processPackage( true );
}

void QueueTab::slotPackage()
{
	if (m_packageInspector->isVisible())
		processPackage( true );
	else
		processPackage( false );
}

void QueueTab::slotAddWorld()
{
	if ( isVisible() )
		m_packageInspector->hide();

	const QStringList selectedIdsList = queueView->selectedPackagesByIds();

	QStringList packageList;
	for ( const QString& id : selectedIdsList )
		packageList += queueView->packageItemById( id )->category() + u'/' + queueView->packageItemById( id )->name();
	PortageSingleton::Instance()->appendWorld( packageList );
}

void QueueTab::slotRemoveWorld()
{
	if ( isVisible() )
		m_packageInspector->hide();

	const QStringList selectedIdsList = queueView->selectedPackagesByIds();

	QStringList packageList;
	for ( const QString& id : selectedIdsList )
		packageList += queueView->packageItemById( id )->category() + u'/' + queueView->packageItemById( id )->name();
	PortageSingleton::Instance()->removeFromWorld( packageList );
}

/**
* Process package and view in Inspector.
*/
void QueueTab::processPackage( bool viewInspector )
{
	// Queue view is hidden don't update
	if ( m_packageInspector->isVisible() && !m_packageInspector->isParentView( VIEW_QUEUE ) )
		return;

	//TODO: This test wasn't needed before, it would be nice to get rid of it again
	if (queueView->currentPackage())
	{
		// Initialize the portage package object with package and it's versions data
		queueView->currentPackage()->parsePackageVersions();

		// Refresh inspector if visible
		if ( viewInspector )
			m_packageInspector->edit( queueView->currentPackage(), VIEW_QUEUE );
	}
}

/**
* Popup menu for current package.
* @param point
*/
void QueueTab::slotContextMenu(QPoint point)
{
	QModelIndex item = queueView->indexAt(point);

	if ( !item.isValid() )
		return;

	QMenu menu( this );

	menu.addAction( m_removeFromQueue );
	menu.addAction( m_packageDetails );

	// Allow editing of World when superuser
	auto* internalItem = static_cast<PackageListItem*>( item.internalPointer() );
	if ( !internalItem->isInWorld() ) {
		menu.addAction( m_addToWorld );
	} else {
		menu.addAction( m_removeFromWorld );
	}

	// No change to Queue when busy
	if ( EmergeSingleton::Instance()->isRunning() || SignalistSingleton::Instance()->isKurooBusy() )
		m_removeFromQueue->setDisabled(true);

	if ( m_packageInspector->isVisible() ) {
		m_removeFromQueue->setDisabled(true);
		m_packageDetails->setDisabled(true);
		m_addToWorld->setDisabled(true);
		m_removeFromWorld->setDisabled(true);
	}

	menu.exec(queueView->viewport()->mapToGlobal(point));
}

