#include "stdafx.h"
#include "SandMan.h"
#include "../MiscHelpers/Common/Common.h"
#include "../MiscHelpers/Common/ExitDialog.h"
#include "../MiscHelpers/Common/SortFilterProxyModel.h"
#include "Views/SbieView.h"
#include "../MiscHelpers/Common/CheckableMessageBox.h"
#include <QWinEventNotifier>
#include "./Dialogs/MultiErrorDialog.h"
#include "../QSbieAPI/SbieUtils.h"
#include "../QSbieAPI/Sandboxie/BoxBorder.h"
#include "../QSbieAPI/Sandboxie/SbieTemplates.h"
#include "Windows/SettingsWindow.h"
#include "Windows/RecoveryWindow.h"
#include <QtConcurrent>
#include "../MiscHelpers/Common/SettingsWidgets.h"
#include "Windows/OptionsWindow.h"
#include <QProxyStyle>
#include "../MiscHelpers/Common/TreeItemModel.h"
#include "../MiscHelpers/Common/ListItemModel.h"
#include "Views/TraceView.h"
#include "Windows/SelectBoxWindow.h"
#include "../UGlobalHotkey/uglobalhotkeys.h"
#include "Wizards/SetupWizard.h"
#include "Helpers/WinAdmin.h"
#include "../MiscHelpers/Common/OtherFunctions.h"
#include "../MiscHelpers/Common/Common.h"
#include "Windows/SupportDialog.h"
#include "../MiscHelpers/Archive/Archive.h"
#include "../MiscHelpers/Archive/ArchiveFS.h"
#include "Views/FileView.h"
#include "OnlineUpdater.h"
#include "../MiscHelpers/Common/NeonEffect.h"
#include <QVariantAnimation>
#include <QSessionManager>
#include "Helpers/FullScreen.h"
#include "Helpers/WinHelper.h"

CSbiePlusAPI* theAPI = NULL;

#include <wtypes.h>
#include <QAbstractNativeEventFilter>
#include <dbt.h>

class CNativeEventFilter : public QAbstractNativeEventFilter
{
public:
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
    virtual bool nativeEventFilter(const QByteArray &eventType, void *message, qintptr *result)
#else
    virtual bool nativeEventFilter(const QByteArray &eventType, void *message, long *result)
#endif
	{
		if (eventType == "windows_generic_MSG" || eventType == "windows_dispatcher_MSG")
		{
			MSG *msg = static_cast<MSG *>(message);

			//if(msg->message != 275 && msg->message != 1025)
			//	qDebug() << msg->message;

			if (msg->message == WM_NOTIFY)
			{
				//return true;
			}
			else if (msg->message == WM_DEVICECHANGE)
			{
				if (msg->wParam == DBT_DEVICEARRIVAL // Drive letter added
				 || msg->wParam == DBT_DEVICEREMOVECOMPLETE) // Drive letter removed
				{
					/*DEV_BROADCAST_HDR* deviceBroadcast = (DEV_BROADCAST_HDR*)msg->lParam;
					if (deviceBroadcast->dbch_devicetype == DBT_DEVTYP_VOLUME) {
					}*/
					if (theAPI)
						theAPI->UpdateDriveLetters();
				}
				/*else if ((msg->wParam & 0xFF80) == 0xAA00 && msg->lParam == 'xobs') 
				{
					UCHAR driveNumber = (UCHAR)(msg->wParam & 0x1F);
					if (driveNumber < 26) {		
					}
				}
				else if (msg->wParam == DBT_DEVNODES_CHANGED) // hardware changed
				{
				}*/
			}
			else if (msg->message == WM_SETTINGCHANGE)
			{
				if (theGUI && theConf->GetInt("Options/UseDarkTheme", 2) == 2)
					theGUI->UpdateTheme();
			}
			else if (msg->message == WM_SHOWWINDOW && msg->wParam)
			{
				QWidget* pWidget = QWidget::find((WId)msg->hwnd);
				if (theGUI && pWidget && (pWidget->windowType() | Qt::Dialog) == Qt::Dialog)
					theGUI->UpdateTitleTheme(msg->hwnd);
			}
		}
		return false;
	}
};

HWND MainWndHandle = NULL;

CSandMan* theGUI = NULL;

extern QString g_PendingMessage;


CSandMan::CSandMan(QWidget *parent)
	: QMainWindow(parent)
{
#if defined(Q_OS_WIN)
	MainWndHandle = (HWND)QWidget::winId();

	QApplication::instance()->installNativeEventFilter(new CNativeEventFilter);
#endif

	CArchive::Init();

	theGUI = this;

	m_DarkTheme = false;
	m_FusionTheme = false;

	QDesktopServices::setUrlHandler("http", this, "OpenUrl");
	QDesktopServices::setUrlHandler("https", this, "OpenUrl");
	QDesktopServices::setUrlHandler("sbie", this, "OpenUrl");

	m_StartMenuUpdatePending = false;

	m_ThemeUpdatePending = false;
	m_DefaultStyle = QApplication::style()->objectName();
	m_DefaultPalett = QApplication::palette();
	m_DefaultFontSize = QApplication::font().pointSizeF();

	m_DarkPalett.setColor(QPalette::Light, QColor(96, 96, 96));
	m_DarkPalett.setColor(QPalette::Midlight, QColor(64, 64, 64));
	m_DarkPalett.setColor(QPalette::Mid, QColor(48, 48, 48));
	m_DarkPalett.setColor(QPalette::Dark, QColor(53, 53, 53));
	m_DarkPalett.setColor(QPalette::Shadow, QColor(25, 25, 25));
	m_DarkPalett.setColor(QPalette::Window, QColor(53, 53, 53));
	m_DarkPalett.setColor(QPalette::WindowText, Qt::white);
	m_DarkPalett.setColor(QPalette::Base, QColor(25, 25, 25));
	m_DarkPalett.setColor(QPalette::AlternateBase, QColor(53, 53, 53));
	m_DarkPalett.setColor(QPalette::ToolTipBase, Qt::lightGray);
	m_DarkPalett.setColor(QPalette::ToolTipText, Qt::white);
	m_DarkPalett.setColor(QPalette::Text, Qt::white);
	m_DarkPalett.setColor(QPalette::Button, QColor(53, 53, 53));
	m_DarkPalett.setColor(QPalette::ButtonText, Qt::white);
	m_DarkPalett.setColor(QPalette::BrightText, Qt::red);
	m_DarkPalett.setColor(QPalette::Link, QColor(218, 130, 42));
	m_DarkPalett.setColor(QPalette::LinkVisited, QColor(218, 130, 42));
	m_DarkPalett.setColor(QPalette::Highlight, QColor(42, 130, 218));
	m_DarkPalett.setColor(QPalette::HighlightedText, Qt::black);
	m_DarkPalett.setColor(QPalette::PlaceholderText, QColor(96, 96, 96));
	m_DarkPalett.setColor(QPalette::Disabled, QPalette::WindowText, Qt::darkGray);
	m_DarkPalett.setColor(QPalette::Disabled, QPalette::Text, Qt::darkGray);
	m_DarkPalett.setColor(QPalette::Disabled, QPalette::Light, Qt::black);
	m_DarkPalett.setColor(QPalette::Disabled, QPalette::ButtonText, Qt::darkGray);

	LoadLanguage();

	if (!theConf->IsWritable()) {
		QMessageBox::critical(this, "Sandboxie-Plus", tr("WARNING: Sandboxie-Plus.ini in %1 cannot be written to, settings will not be saved.").arg(theConf->GetConfigDir()));
	}

	m_bExit = false;

	theAPI = new CSbiePlusAPI(this);
	connect(theAPI, SIGNAL(StatusChanged()), this, SLOT(OnStatusChanged()));

	connect(theAPI, SIGNAL(BoxAdded(const CSandBoxPtr&)), this, SLOT(OnBoxAdded(const CSandBoxPtr&)));
	connect(theAPI, SIGNAL(BoxClosed(const CSandBoxPtr&)), this, SLOT(OnBoxClosed(const CSandBoxPtr&)));
	connect(theAPI, SIGNAL(BoxCleaned(CSandBoxPlus*)), this, SLOT(OnBoxCleaned(CSandBoxPlus*)));

	QString appTitle = tr("Sandboxie-Plus v%1").arg(GetVersion());

	this->setWindowTitle(appTitle);

	setAcceptDrops(true);

	m_pBoxBorder = new CBoxBorder(theAPI, this);

	m_SbieTemplates = new CSbieTemplates(theAPI, this);


	m_bConnectPending = false;
	m_bStopPending = false;


	m_pUpdater = new COnlineUpdater(this);


	m_pMainWidget = new QWidget(this);

	m_pMenuBar = menuBar();
	connect(m_pMenuBar, SIGNAL(hovered(QAction*)), this, SLOT(OnMenuHover(QAction*)));

	QWidget* pMenuWidget = new QWidget(this);
	m_pMenuLayout = new QHBoxLayout(pMenuWidget);
	m_pMenuLayout->setContentsMargins(0, 0, 0, 0);
	//m_pMenuLayout->addWidget(m_pMenuBar);
	m_pMenuLayout->setMenuBar(m_pMenuBar);
	//m_pMenuLayout->addWidget(m_pLabel);
	//m_pMenuLayout->addStretch(10);
	setMenuWidget(pMenuWidget);

	CreateUI();
	setCentralWidget(m_pMainWidget);

	m_pTraceInfo = new QLabel();
	m_pDisabledForce = new QLabel();
	m_pDisabledRecovery = new QLabel();
	m_pDisabledMessages = new QLabel();
	statusBar()->addPermanentWidget(m_pTraceInfo);
	statusBar()->addPermanentWidget(m_pDisabledForce);
	statusBar()->addPermanentWidget(m_pDisabledRecovery);
	statusBar()->addPermanentWidget(m_pDisabledMessages);
	OnDisablePopUp(); // update statusbar



	m_pHotkeyManager = new UGlobalHotkeys(this);
	connect(m_pHotkeyManager, SIGNAL(activated(size_t)), SLOT(OnHotKey(size_t)));
	SetupHotKeys();

	//for (int i = 0; i < eMaxColor; i++) {
	//	m_BoxIcons[i].Empty = QIcon(QString(":/Boxes/Empty%1").arg(i));
	//	m_BoxIcons[i].InUse = QIcon(QString(":/Boxes/Full%1").arg(i));
	//}

	m_BoxColors[CSandBoxPlus::eHardenedPlus] = qRgb(238,35,4);
	m_BoxColors[CSandBoxPlus::eHardened] = qRgb(247,125,2);
	m_BoxColors[CSandBoxPlus::eDefaultPlus] = qRgb(1,133,248);
	m_BoxColors[CSandBoxPlus::eDefault] = qRgb(246,246,2);
	m_BoxColors[CSandBoxPlus::eAppBoxPlus] = qRgb(3,232,232);
	m_BoxColors[CSandBoxPlus::eAppBox] = qRgb(0,253,0);
	m_BoxColors[CSandBoxPlus::eInsecure] = qRgb(244,3,244);
	m_BoxColors[CSandBoxPlus::eOpen] = qRgb(255,255,255);

	CreateTrayIcon();

	LoadState();

	m_pProgressDialog = new CProgressDialog("");
	m_pProgressDialog->setWindowModality(Qt::ApplicationModal);
	connect(m_pProgressDialog, SIGNAL(Cancel()), this, SLOT(OnCancelAsync()));
	m_pProgressModal = false;

	m_pPopUpWindow = new CPopUpWindow();

	bool bAlwaysOnTop = theConf->GetBool("Options/AlwaysOnTop", false);
	this->setWindowFlag(Qt::WindowStaysOnTopHint, bAlwaysOnTop);
	m_pPopUpWindow->setWindowFlag(Qt::WindowStaysOnTopHint, bAlwaysOnTop);
	m_pProgressDialog->setWindowFlag(Qt::WindowStaysOnTopHint, bAlwaysOnTop);

	//connect(theAPI, SIGNAL(LogMessage(const QString&, bool)), this, SLOT(OnLogMessage(const QString&, bool)));
	connect(theAPI, SIGNAL(LogSbieMessage(quint32, const QStringList&, quint32)), this, SLOT(OnLogSbieMessage(quint32, const QStringList&, quint32)));
	connect(theAPI, SIGNAL(NotAuthorized(bool, bool&)), this, SLOT(OnNotAuthorized(bool, bool&)), Qt::DirectConnection);
	connect(theAPI, SIGNAL(QueuedRequest(quint32, quint32, quint32, const QVariantMap&)), this, SLOT(OnQueuedRequest(quint32, quint32, quint32, const QVariantMap&)), Qt::QueuedConnection);
	connect(theAPI, SIGNAL(FileToRecover(const QString&, const QString&, const QString&, quint32)), this, SLOT(OnFileToRecover(const QString&, const QString&, const QString&, quint32)), Qt::QueuedConnection);
	connect(theAPI, SIGNAL(ConfigReloaded()), this, SLOT(OnIniReloaded()));

    connect(qApp, &QGuiApplication::commitDataRequest, this, &CSandMan::commitData);

	m_uTimerID = startTimer(1000);

	bool bAutoRun = QApplication::arguments().contains("-autorun");
	if (!bAutoRun && g_PendingMessage.isEmpty())
		SafeShow(this);

	OnStatusChanged();
	if (CSbieUtils::IsRunning(CSbieUtils::eAll) || theConf->GetBool("Options/StartIfStopped", true))
	{
		SB_RESULT(void*) Status = ConnectSbie();
		HandleMaintenance(Status);
	}

	//qApp->setWindowIcon(GetIcon("IconEmptyDC", false));
}

CSandMan::~CSandMan()
{
	m_pPopUpWindow->close();
	delete m_pPopUpWindow;

	if(theAPI->IsMonitoring())
		theAPI->EnableMonitor(false);

	delete m_pBoxBorder;

	killTimer(m_uTimerID);

	m_pTrayIcon->show();
	m_pTrayIcon->hide();

	StoreState();

	theAPI = NULL;

	theGUI = NULL;
}

void CSandMan::LoadState(bool bFull)
{
	if (bFull) {
		setWindowState(Qt::WindowNoState);
		restoreGeometry(theConf->GetBlob("MainWindow/Window_Geometry"));
		restoreState(theConf->GetBlob("MainWindow/Window_State"));
	}

	//m_pBoxTree->restoreState(theConf->GetBlob("MainWindow/BoxTree_Columns"));
	if(m_pMessageLog) m_pMessageLog->GetView()->header()->restoreState(theConf->GetBlob("MainWindow/LogList_Columns"));
	if(m_pRecoveryLog) m_pRecoveryLog->GetView()->header()->restoreState(theConf->GetBlob("MainWindow/RecoveryLog_Columns"));
	if(m_pLogSplitter) m_pLogSplitter->restoreState(theConf->GetBlob("MainWindow/Log_Splitter"));
	if(m_pPanelSplitter) m_pPanelSplitter->restoreState(theConf->GetBlob("MainWindow/Panel_Splitter"));
	if(m_pLogTabs) m_pLogTabs->setCurrentIndex(theConf->GetInt("MainWindow/LogTab", 0));
}

void CSandMan::StoreState()
{
	theConf->SetBlob("MainWindow/Window_Geometry", saveGeometry());
	theConf->SetBlob("MainWindow/Window_State", saveState());

	//theConf->SetBlob("MainWindow/BoxTree_Columns", m_pBoxTree->saveState());
	if(m_pMessageLog) theConf->SetBlob("MainWindow/LogList_Columns", m_pMessageLog->GetView()->header()->saveState());
	if(m_pRecoveryLog) theConf->SetBlob("MainWindow/RecoveryLog_Columns", m_pRecoveryLog->GetView()->header()->saveState());
	if(m_pLogSplitter) theConf->SetBlob("MainWindow/Log_Splitter", m_pLogSplitter->saveState());
	if(m_pPanelSplitter) theConf->SetBlob("MainWindow/Panel_Splitter", m_pPanelSplitter->saveState());
	if(m_pLogTabs) theConf->SetValue("MainWindow/LogTab", m_pLogTabs->currentIndex());
}

QIcon CSandMan::GetIcon(const QString& Name, int iAction)
{
	int iNoIcons = theConf->GetInt("Options/NoIcons", 2);
	if (iNoIcons == 2)
		iNoIcons = theConf->GetInt("Options/ViewMode", 1) == 2 ? 1 : 0;
	if(iAction && iNoIcons)
		return QIcon();

	QString Path = QApplication::applicationDirPath() + "/Icons/" + Name + ".png";
	if(QFile::exists(Path))
		return QIcon(Path);
	return QIcon((iAction == 1 ? ":/Actions/" : ":/") + Name + ".png");
}

void CSandMan::CreateUI()
{
	SetUITheme();

	int iViewMode = theConf->GetInt("Options/ViewMode", 1);

	if(iViewMode == 2)
		CreateOldMenus();
	else
		CreateMenus(iViewMode == 1);

	m_pMainLayout = new QVBoxLayout(m_pMainWidget);
	m_pMainLayout->setContentsMargins(2,2,2,2);
	m_pMainLayout->setSpacing(0);

	if(iViewMode == 1)
		CreateToolBar();
	else {
		m_pSeparator = NULL;
		CreateLabel();
		m_pMenuLayout->addWidget(m_pLabel);
		UpdateLabel();
	}

	CreateView(iViewMode);

	if (iViewMode != 2) {
		m_pMenuBrowse->setChecked(theConf->GetBool("Options/ShowFilePanel", false));
		m_pFileView->setVisible(m_pMenuBrowse->isChecked());
	}

	foreach(QAction * pAction, m_pViewMode->actions())
		pAction->setChecked(pAction->data().toInt() == iViewMode);

	statusBar()->setVisible(iViewMode == 1);

	if(m_pKeepTerminated) m_pKeepTerminated->setChecked(theConf->GetBool("Options/KeepTerminated"));
	if(m_pShowAllSessions) m_pShowAllSessions->setChecked(theConf->GetBool("Options/ShowAllSessions"));

	m_pWndTopMost->setChecked(theConf->GetBool("Options/AlwaysOnTop", false));


	// pizza background
	int iUsePizza = theConf->GetInt("Options/UseBackground", 2);
	if (iUsePizza == 2)
		iUsePizza = theConf->GetInt("Options/ViewMode", 1) == 2 ? 1 : 0;
	if (iUsePizza) 
	{
		QPalette pizzaPalete = GetBoxView()->GetTree()->palette(); // QPalette pizzaPalete = QApplication::palette();
		SetPaleteTexture(pizzaPalete, QPalette::Base, QImage(":/Assets/background.png"));
		GetBoxView()->GetTree()->setPalette(pizzaPalete); // QApplication::setPalette(pizzaPalete);
		GetFileView()->GetTree()->setPalette(pizzaPalete); // QApplication::setPalette(pizzaPalete);
	}
}

void CSandMan::CreateMaintenanceMenu()
{
		m_pMaintenance = m_pMenuFile->addMenu(CSandMan::GetIcon("Maintenance"), tr("&Maintenance"));
			m_pConnect = m_pMaintenance->addAction(CSandMan::GetIcon("Connect"), tr("Connect"), this, SLOT(OnMaintenance()));
			m_pDisconnect = m_pMaintenance->addAction(CSandMan::GetIcon("Disconnect"), tr("Disconnect"), this, SLOT(OnMaintenance()));
			m_pMaintenance->addSeparator();
			m_pStopAll = m_pMaintenance->addAction(CSandMan::GetIcon("Stop"), tr("Stop All"), this, SLOT(OnMaintenance()));
			m_pMaintenanceItems = m_pMaintenance->addMenu(CSandMan::GetIcon("ManMaintenance"), tr("&Advanced"));
				m_pInstallDrv = m_pMaintenanceItems->addAction(tr("Install Driver"), this, SLOT(OnMaintenance()));
				m_pStartDrv = m_pMaintenanceItems->addAction(tr("Start Driver"), this, SLOT(OnMaintenance()));
				m_pStopDrv = m_pMaintenanceItems->addAction(tr("Stop Driver"), this, SLOT(OnMaintenance()));
				m_pUninstallDrv = m_pMaintenanceItems->addAction(tr("Uninstall Driver"), this, SLOT(OnMaintenance()));
				m_pMaintenanceItems->addSeparator();
				m_pInstallSvc = m_pMaintenanceItems->addAction(tr("Install Service"), this, SLOT(OnMaintenance()));
				m_pStartSvc = m_pMaintenanceItems->addAction(tr("Start Service"), this, SLOT(OnMaintenance()));
				m_pStopSvc = m_pMaintenanceItems->addAction(tr("Stop Service"), this, SLOT(OnMaintenance()));
				m_pUninstallSvc = m_pMaintenanceItems->addAction(tr("Uninstall Service"), this, SLOT(OnMaintenance()));
			m_pMaintenance->addSeparator();
			m_pSetupWizard = m_pMaintenance->addAction(CSandMan::GetIcon("Wizard"), tr("Setup Wizard"), this, SLOT(OnMaintenance()));
			//m_pUpdateCore = m_pMaintenance->addAction(CSandMan::GetIcon("Install"), tr("Update Core Files"), this, SLOT(OnMaintenance()));
			if(theGUI->IsFullyPortable())
				m_pUninstallAll = m_pMaintenance->addAction(CSandMan::GetIcon("Uninstall"), tr("Uninstall All"), this, SLOT(OnMaintenance()));
}

void CSandMan::CreateViewBaseMenu()
{
		m_pViewMode = new QActionGroup(m_pMenuView);
		MakeAction(m_pViewMode, m_pMenuView, tr("Simple View"), 0);
		MakeAction(m_pViewMode, m_pMenuView, tr("Advanced View"), 1);
		MakeAction(m_pViewMode, m_pMenuView, tr("Vintage View (like SbieCtrl)"), 2);
		connect(m_pViewMode, SIGNAL(triggered(QAction*)), this, SLOT(OnViewMode(QAction*)));

		m_pMenuView->addSeparator();
		m_pWndTopMost = m_pMenuView->addAction(tr("Always on Top"), this, SLOT(OnAlwaysTop()));
		m_pWndTopMost->setCheckable(true);
}

void CSandMan::CreateHelpMenu(bool bAdvanced)
{
	m_pMenuHelp = m_pMenuBar->addMenu(tr("&Help"));
		//m_pMenuHelp->addAction(tr("Support Sandboxie-Plus on Patreon"), this, SLOT(OnHelp()));
		//m_pSupport = m_pMenuHelp->addAction(tr("Support Sandboxie-Plus with Donations"), this, SLOT(OnHelp()));
		//if (!bAdvanced) {
		//	m_pMenuHelp->removeAction(m_pSupport);
		//	m_pMenuBar->addAction(m_pSupport);
		//}
		m_pContribution = m_pMenuHelp->addAction(CSandMan::GetIcon("Support"), tr("Contribute to Sandboxie-Plus"), this, SLOT(OnHelp()));
		m_pManual = m_pMenuHelp->addAction(tr("Online Documentation"), this, SLOT(OnHelp()));
		m_pForum = m_pMenuHelp->addAction(tr("Visit Support Forum"), this, SLOT(OnHelp()));
		m_pMenuHelp->addSeparator();
		m_pUpdate = m_pMenuHelp->addAction(CSandMan::GetIcon("Update"), tr("Check for Updates"), this, SLOT(CheckForUpdates()));
		m_pMenuHelp->addSeparator();
		m_pAboutQt = m_pMenuHelp->addAction(tr("About the Qt Framework"), this, SLOT(OnAbout()));
		m_pAbout = m_pMenuHelp->addAction(CSandMan::GetIcon("IconFull", 2), tr("About Sandboxie-Plus"), this, SLOT(OnAbout()));
}

void CSandMan::CreateMenus(bool bAdvanced)
{
	m_pMenuBar->clear();

	m_pMenuFile = m_pMenuBar->addMenu(tr("&Sandbox"));
		m_pNewBox = m_pMenuFile->addAction(CSandMan::GetIcon("NewBox"), tr("Create New Box"), this, SLOT(OnSandBoxAction()));
		m_pNewGroup = m_pMenuFile->addAction(CSandMan::GetIcon("Group"), tr("Create Box Group"), this, SLOT(OnSandBoxAction()));
		m_pImportBox = m_pMenuFile->addAction(CSandMan::GetIcon("UnPackBox"), tr("Import Box"), this, SLOT(OnSandBoxAction()));
		m_pMenuFile->addSeparator();
		m_pRunBoxed = m_pMenuFile->addAction(CSandMan::GetIcon("Run"), tr("Run Sandboxed"), this, SLOT(OnSandBoxAction()));
		m_pEmptyAll = m_pMenuFile->addAction(CSandMan::GetIcon("EmptyAll"), tr("Terminate All Processes"), this, SLOT(OnEmptyAll()));
		m_pMenuFile->addSeparator();
		m_pDisableForce = m_pMenuFile->addAction(tr("Pause Forcing Programs"), this, SLOT(OnDisableForce()));
		m_pDisableForce->setCheckable(true);
	if(bAdvanced) {
		m_pDisableRecovery = m_pMenuFile->addAction(tr("Disable File Recovery"), this, SLOT(OnDisablePopUp()));
		m_pDisableRecovery->setCheckable(true);
		m_pDisableRecovery->setChecked(theConf->GetBool("UIConfig/DisabledRecovery", false));
		m_pDisableMessages = m_pMenuFile->addAction(tr("Disable Message Popup"), this, SLOT(OnDisablePopUp()));
		m_pDisableMessages->setCheckable(true);
		m_pDisableMessages->setChecked(theConf->GetBool("UIConfig/DisableMessages", false));
	}
	else {
		m_pDisableRecovery = NULL;
		m_pDisableMessages = NULL;
	}
		m_pMenuFile->addSeparator();
		m_pWndFinder = m_pMenuFile->addAction(CSandMan::GetIcon("finder"), tr("Is Window Sandboxed?"), this, SLOT(OnWndFinder()));

	if(bAdvanced || theGUI->IsFullyPortable())
		CreateMaintenanceMenu();
	else {
		m_pMaintenance = NULL;
			m_pConnect = NULL;
			m_pDisconnect = NULL;
			m_pMaintenanceItems = NULL;
			m_pInstallDrv = NULL;
			m_pStartDrv = NULL;
			m_pStopDrv = NULL;
			m_pUninstallDrv = NULL;
			m_pInstallSvc = NULL;
			m_pStartSvc = NULL;
			m_pStopSvc = NULL;
			m_pUninstallSvc = NULL;
			m_pStopAll = NULL;
			m_pUninstallAll = NULL;
			m_pSetupWizard = NULL;
			//m_pUpdateCore = NULL;
	}
				
		m_pMenuFile->addSeparator();
		m_pExit = m_pMenuFile->addAction(CSandMan::GetIcon("Exit"), tr("Exit"), this, SLOT(OnExit()));


	m_pMenuView = m_pMenuBar->addMenu(tr("&View"));

		CreateViewBaseMenu();

	if(bAdvanced) {
		m_pMenuView->addSeparator();

		m_pShowHidden = m_pMenuView->addAction(tr("Show Hidden Boxes"));
		m_pShowHidden->setCheckable(true);
		m_pShowAllSessions = m_pMenuView->addAction(tr("Show All Sessions"), this, SLOT(OnProcView()));
		m_pShowAllSessions->setCheckable(true);
	}
	else {
		m_pShowHidden = NULL;
		m_pShowAllSessions = NULL;
	}

		m_pMenuView->addSeparator();
		m_pMenuBrowse = m_pMenuView->addAction(CSandMan::GetIcon("Explore"), tr("Show File Panel"), this, SLOT(OnProcView()));
		m_pMenuBrowse->setCheckable(true);
		m_pMenuBrowse->setShortcut(QKeySequence("Ctrl+D"));
		m_pMenuBrowse->setShortcutContext(Qt::WidgetWithChildrenShortcut);
		this->addAction(m_pMenuBrowse);

		m_pMenuView->addSeparator();

		m_pRefreshAll = m_pMenuView->addAction(CSandMan::GetIcon("Refresh"), tr("Refresh View"), this, SLOT(OnRefresh()));
		m_pRefreshAll->setShortcut(QKeySequence("F5"));
		m_pRefreshAll->setShortcutContext(Qt::WidgetWithChildrenShortcut);
		this->addAction(m_pRefreshAll);

	if(bAdvanced) {
		m_pCleanUpMenu = m_pMenuView->addMenu(CSandMan::GetIcon("Clean"), tr("Clean Up"));
			m_pCleanUpProcesses = m_pCleanUpMenu->addAction(tr("Cleanup Processes"), this, SLOT(OnCleanUp()));
			m_pCleanUpMenu->addSeparator();
			m_pCleanUpMsgLog = m_pCleanUpMenu->addAction(tr("Cleanup Message Log"), this, SLOT(OnCleanUp()));
			m_pCleanUpTrace = m_pCleanUpMenu->addAction(tr("Cleanup Trace Log"), this, SLOT(OnCleanUp()));
			m_pCleanUpRecovery = m_pCleanUpMenu->addAction(tr("Cleanup Recovery Log"), this, SLOT(OnCleanUp()));

		m_pKeepTerminated = m_pMenuView->addAction(CSandMan::GetIcon("Keep"), tr("Keep terminated"), this, SLOT(OnProcView()));
		m_pKeepTerminated->setCheckable(true);
	}
	else {
		m_pCleanUpMenu = NULL;
			m_pCleanUpProcesses = NULL;
			m_pCleanUpMsgLog = NULL;
			m_pCleanUpTrace = NULL;
			m_pCleanUpRecovery = NULL;

		m_pKeepTerminated = NULL;
	}
		m_pMenuView->addSeparator();
		m_pEnableMonitoring = m_pMenuView->addAction(CSandMan::GetIcon("SetLogging"), tr("Trace Logging"), this, SLOT(OnMonitoring()));
	if (bAdvanced)
		m_pEnableMonitoring->setCheckable(true);
	if (!bAdvanced)
		m_pMenuView->addAction(CSandMan::GetIcon("Recover"), tr("Recovery Log"), this, SLOT(OnRecoveryLog()));
	

	m_pMenuOptions = m_pMenuBar->addMenu(tr("&Options"));
		m_pMenuSettings = m_pMenuOptions->addAction(CSandMan::GetIcon("Settings"), tr("Global Settings"), this, SLOT(OnSettings()));
		m_pMenuResetMsgs = m_pMenuOptions->addAction(tr("Reset all hidden messages"), this, SLOT(OnResetMsgs()));
		m_pMenuResetGUI = m_pMenuOptions->addAction(tr("Reset all GUI options"), this, SLOT(OnResetGUI()));
		m_pMenuResetGUI->setShortcut(QKeySequence("Ctrl+Shift+R"));
		m_pMenuResetGUI->setShortcutContext(Qt::WidgetWithChildrenShortcut);
		this->addAction(m_pMenuResetGUI);
		m_pMenuOptions->addSeparator();
		m_pEditIni = m_pMenuOptions->addAction(CSandMan::GetIcon("Editor"), tr("Edit Sandboxie.ini"), this, SLOT(OnEditIni()));
		m_pEditIni->setProperty("ini", "sbie");
		if (bAdvanced) {
			m_pEditIni2 = m_pMenuOptions->addAction(CSandMan::GetIcon("Editor2"), tr("Edit Templates.ini"), this, SLOT(OnEditIni()));
			m_pEditIni2->setProperty("ini", "tmpl");
			m_pEditIni3 = m_pMenuOptions->addAction(CSandMan::GetIcon("Editor4"), tr("Edit Sandboxie-Plus.ini"), this, SLOT(OnEditIni()));
			m_pEditIni3->setProperty("ini", "plus");
		} else
			m_pEditIni2 = m_pEditIni3 = NULL;
		m_pReloadIni = m_pMenuOptions->addAction(CSandMan::GetIcon("ReloadIni"), tr("Reload configuration"), this, SLOT(OnReloadIni()));

	CreateHelpMenu(bAdvanced);

	// for old menu
	m_pSandbox = NULL;
}

void CSandMan::CreateOldMenus()
{
	m_pMenuBar->clear();

	m_pMenuFile = m_pMenuBar->addMenu(tr("&File"));
		m_pRunBoxed = m_pMenuFile->addAction(CSandMan::GetIcon("Run"), tr("Run Sandboxed"), this, SLOT(OnSandBoxAction()));
		m_pEmptyAll = m_pMenuFile->addAction(CSandMan::GetIcon("EmptyAll"), tr("Terminate All Processes"), this, SLOT(OnEmptyAll()));
		m_pDisableForce = m_pMenuFile->addAction(tr("Pause Forcing Programs"), this, SLOT(OnDisableForce()));
		m_pDisableForce->setCheckable(true);
		//m_pDisableRecovery = m_pMenuFile->addAction(tr("Disable File Recovery"));
		//m_pDisableRecovery->setCheckable(true);
		m_pDisableRecovery = NULL;
		//m_pDisableMessages = m_pMenuFile->addAction(tr("Disable Message Popup"));
		//m_pDisableMessages->setCheckable(true);	
		m_pDisableMessages = NULL;
		m_pMenuFile->addSeparator();
		m_pWndFinder = m_pMenuFile->addAction(CSandMan::GetIcon("finder"), tr("Is Window Sandboxed?"), this, SLOT(OnWndFinder()));
		m_pEnableMonitoring = m_pMenuFile->addAction(CSandMan::GetIcon("SetLogging"), tr("Resource Access Monitor"), this, SLOT(OnMonitoring()));

		m_pMenuFile->addSeparator();

		if(theGUI->IsFullyPortable())
			CreateMaintenanceMenu();
		else {
			m_pMaintenance = NULL;
				m_pConnect = NULL;
				m_pDisconnect = NULL;
				m_pMaintenanceItems = NULL;
				m_pInstallDrv = NULL;
				m_pStartDrv = NULL;
				m_pStopDrv = NULL;
				m_pUninstallDrv = NULL;
				m_pInstallSvc = NULL;
				m_pStartSvc = NULL;
				m_pStopSvc = NULL;
				m_pUninstallSvc = NULL;
				m_pStopAll = NULL;
				m_pUninstallAll = NULL;
				m_pSetupWizard = NULL;
				//m_pUpdateCore = NULL;
		}
			
		m_pExit = m_pMenuFile->addAction(CSandMan::GetIcon("Exit"), tr("Exit"), this, SLOT(OnExit()));

	m_pMenuView = m_pMenuBar->addMenu(tr("&View"));

		CreateViewBaseMenu();

		m_pMenuView->addSeparator();
		QActionGroup* m_pView = new QActionGroup(m_pMenuView);
		MakeAction(m_pView, m_pMenuView, tr("Programs"), 0);
		MakeAction(m_pView, m_pMenuView, tr("Files and Folders"), 1);
		m_pView->actions().first()->setChecked(true);
		connect(m_pView, SIGNAL(triggered(QAction*)), this, SLOT(OnView(QAction*)));
		m_pMenuView->addSeparator();
		m_pMenuView->addAction(CSandMan::GetIcon("Recover"), tr("Recovery Log"), this, SLOT(OnRecoveryLog()));

		m_pMenuBrowse = NULL;
		//m_pMenuView->addSeparator();
		//m_pRefreshAll = m_pMenuView->addAction(CSandMan::GetIcon("Refresh"), tr("Refresh View"), this, SLOT(OnRefresh()));
		//m_pRefreshAll->setShortcut(QKeySequence("F5"));
		//m_pRefreshAll->setShortcutContext(Qt::WidgetWithChildrenShortcut);
		//this->addAction(m_pRefreshAll);
		m_pRefreshAll = NULL;

		m_pCleanUpMenu = NULL;
			m_pCleanUpProcesses = NULL;
			m_pCleanUpMsgLog = NULL;
			m_pCleanUpTrace = NULL;
			m_pCleanUpRecovery = NULL;
		m_pKeepTerminated = NULL;

	m_pSandbox = m_pMenuBar->addMenu(tr("&Sandbox"));

		connect(m_pSandbox, SIGNAL(hovered(QAction*)), this, SLOT(OnBoxMenuHover(QAction*)));

		m_pSandbox->addSeparator();
		m_pNewBox = m_pSandbox->addAction(CSandMan::GetIcon("NewBox"), tr("Create New Sandbox"), this, SLOT(OnSandBoxAction()));
		m_pNewGroup = m_pSandbox->addAction(CSandMan::GetIcon("Group"), tr("Create New Group"), this, SLOT(OnSandBoxAction()));
		m_pImportBox = m_pSandbox->addAction(CSandMan::GetIcon("UnPackBox"), tr("Import Sandbox"), this, SLOT(OnSandBoxAction()));

		QAction* m_pSetContainer = m_pSandbox->addAction(CSandMan::GetIcon("Advanced"), tr("Set Container Folder"), this, SLOT(OnSettingsAction()));
		m_pSetContainer->setData(CSettingsWindow::eAdvanced);

		m_pArrangeGroups = m_pSandbox->addAction(tr("Set Layout and Groups"), this, SLOT(OnSettingsAction()));
		m_pArrangeGroups->setCheckable(true);

		m_pShowHidden = m_pSandbox->addAction(tr("Reveal Hidden Boxes"));
		m_pShowHidden->setCheckable(true);
		//m_pShowAllSessions = m_pSandbox->addAction(tr("Show Boxes From All Sessions"), this, SLOT(OnProcView()));
		//m_pShowAllSessions->setCheckable(true);
		m_pShowAllSessions = NULL;

	m_pMenuOptions = m_pMenuBar->addMenu(tr("&Configure"));
		m_pMenuSettings = m_pMenuOptions->addAction(CSandMan::GetIcon("Settings"), tr("Global Settings"), this, SLOT(OnSettings()));
		m_pMenuOptions->addSeparator();

		QAction* m_pProgramAlert = m_pMenuOptions->addAction(CSandMan::GetIcon("Alarm"), tr("Program Alerts"), this, SLOT(OnSettingsAction()));
		m_pProgramAlert->setData(CSettingsWindow::eProgCtrl);
		QAction* m_pWindowsShell = m_pMenuOptions->addAction(CSandMan::GetIcon("Shell"), tr("Windows Shell Integration"), this, SLOT(OnSettingsAction()));
		m_pWindowsShell->setData(CSettingsWindow::eShell);
		QAction* m_pCompatibility = m_pMenuOptions->addAction(CSandMan::GetIcon("Compatibility"), tr("Software Compatibility"), this, SLOT(OnSettingsAction()));
		m_pCompatibility->setData(CSettingsWindow::eSoftCompat);

		m_pMenuResetMsgs = m_pMenuOptions->addAction(tr("Reset all hidden messages"), this, SLOT(OnResetMsgs()));
		m_pMenuResetGUI = m_pMenuOptions->addAction(tr("Reset all GUI options"), this, SLOT(OnResetGUI()));
		m_pMenuResetGUI->setShortcut(QKeySequence("Ctrl+Shift+R"));
		m_pMenuResetGUI->setShortcutContext(Qt::WidgetWithChildrenShortcut);
		this->addAction(m_pMenuResetGUI);
		m_pMenuOptions->addSeparator();
		QAction* m_pConfigLock = m_pMenuOptions->addAction(CSandMan::GetIcon("Lock"), tr("Lock Configuration"), this, SLOT(OnSettingsAction()));
		m_pConfigLock->setData(CSettingsWindow::eConfigLock);
		m_pEditIni = m_pMenuOptions->addAction(CSandMan::GetIcon("Editor"), tr("Edit Sandboxie.ini"), this, SLOT(OnEditIni()));
		m_pEditIni->setProperty("ini", "sbie");
		m_pEditIni2 = m_pEditIni3 = NULL;
		m_pReloadIni = m_pMenuOptions->addAction(CSandMan::GetIcon("ReloadIni"), tr("Reload configuration"), this, SLOT(OnReloadIni()));

	CreateHelpMenu(false);

}

void CSandMan::OnView(QAction* pAction)
{
	int iView = pAction->data().toInt();
	if(m_pViewStack) m_pViewStack->setCurrentIndex(iView);

	if (iView == 1) { // files
		m_pBoxCombo->clear();
		foreach(const CSandBoxPtr & pBox, theAPI->GetAllBoxes())
			m_pBoxCombo->addItem(tr("Sandbox %1").arg(pBox->GetName().replace("_", "")), pBox->GetName());
		m_pBoxCombo->setCurrentIndex(m_pBoxCombo->findData(theAPI->GetGlobalSettings()->GetText("DefaultBox", "DefaultBox")));
	}
}

void CSandMan::CreateToolBar()
{
	m_pToolBar = new QToolBar();
	m_pMainLayout->insertWidget(0, m_pToolBar);

	m_pToolBar->addAction(m_pMenuSettings);
	m_pToolBar->addSeparator();

	//m_pToolBar->addAction(m_pMenuNew);
	//m_pToolBar->addAction(m_pMenuEmptyAll);
	//m_pToolBar->addSeparator();
	m_pToolBar->addAction(m_pKeepTerminated);
	//m_pToolBar->addSeparator();
	//m_pToolBar->addAction(m_pCleanUp);

	m_pCleanUpButton = new QToolButton();
	m_pCleanUpButton->setIcon(CSandMan::GetIcon("Clean"));
	m_pCleanUpButton->setToolTip(tr("Cleanup"));
	m_pCleanUpButton->setText(tr("Cleanup"));
	m_pCleanUpButton->setPopupMode(QToolButton::MenuButtonPopup);
	m_pCleanUpButton->setMenu(m_pCleanUpMenu);
	//QObject::connect(m_pCleanUpButton, SIGNAL(triggered(QAction*)), , SLOT());
	QObject::connect(m_pCleanUpButton, SIGNAL(clicked(bool)), this, SLOT(OnCleanUp()));
	m_pToolBar->addWidget(m_pCleanUpButton);

	
	m_pToolBar->addSeparator();
	m_pToolBar->addAction(m_pMenuBrowse);
	m_pToolBar->addSeparator();
	
	/*m_pEditButton = new QToolButton();
	m_pEditButton->setIcon(m_pEditIni->icon());
	m_pEditButton->setText(m_pEditIni->text());
	m_pEditButton->setPopupMode(QToolButton::MenuButtonPopup);
	QMenu* pEditBtnMenu = new QMenu(m_pEditButton);
	pEditBtnMenu->addAction(m_pEditIni2->icon(), m_pEditIni2->text(), this, SLOT(OnEditIni2()));
	pEditBtnMenu->addAction(m_pEditIni3->icon(), m_pEditIni3->text(), this, SLOT(OnEditIni3()));
	m_pEditButton->setMenu(pEditBtnMenu);
	//QObject::connect(m_pEditButton, SIGNAL(triggered(QAction*)), , SLOT());
	QObject::connect(m_pEditButton, SIGNAL(clicked(bool)), this, SLOT(OnEditIni()));
	m_pToolBar->addWidget(m_pEditButton);*/
	m_pToolBar->addAction(m_pEditIni);

	m_pToolBar->addSeparator();
	m_pToolBar->addAction(m_pEnableMonitoring);
	//m_pToolBar->addSeparator();
	
	// Label

	QWidget* pSpacer = new QWidget();
	pSpacer->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
	m_pToolBar->addWidget(pSpacer);

	//m_pToolBar->addAction(m_pMenuElevate);

	m_pSeparator = m_pToolBar->addSeparator();
	
	CreateLabel();
	m_pToolBar->addWidget(m_pLabel);
	UpdateLabel();
}

void CSandMan::CreateLabel()
{
	m_pLabel = new QLabel(m_pMainWidget);
	m_pLabel->setTextInteractionFlags(Qt::TextBrowserInteraction);
	connect(m_pLabel, SIGNAL(linkActivated(const QString&)), this, SLOT(OpenUrl(const QString&)));

	m_pLabel->setAlignment(Qt::AlignCenter);
	m_pLabel->setContentsMargins(24, 0, 24, 0);

	QFont fnt = m_pLabel->font();
	fnt.setBold(true);
	//fnt.setWeight(QFont::DemiBold);
	m_pLabel->setFont(fnt);
}

void CSandMan::UpdateLabel()
{
	QString LabelText;
	QString LabelTip;

	if (!theConf->GetString("Updater/PendingUpdate").isEmpty())
	{
		QString FilePath = theConf->GetString("Updater/InstallerPath");
		if (!FilePath.isEmpty() && QFile::exists(FilePath)) {
			LabelText = tr("<a href=\"sbie://update/installer\" style=\"color: red;\">There is a new Sandboxie-Plus release %1 ready</a>").arg(theConf->GetString("Updater/InstallerVersion"));
			LabelTip = tr("Click to run installer");
		}
		else if (!theConf->GetString("Updater/UpdateVersion").isEmpty()){
			LabelText = tr("<a href=\"sbie://update/apply\" style=\"color: red;\">There is a new Sandboxie-Plus update %1 ready</a>").arg(theConf->GetString("Updater/UpdateVersion"));
			LabelTip = tr("Click to apply update");
		}
		else {
			LabelText = tr("<a href=\"sbie://update/check\" style=\"color: red;\">There is a new Sandboxie-Plus update v%1 available</a>").arg(theConf->GetString("Updater/PendingUpdate"));
			LabelTip = tr("Click to download update");
		}

		//auto neon = new CNeonEffect(10, 4, 180); // 140
		//m_pLabel->setGraphicsEffect(NULL);
	}
	else if (g_Certificate.isEmpty()) 
	{
		LabelText = tr("<a href=\"https://sandboxie-plus.com/go.php?to=patreon\">Support Sandboxie-Plus on Patreon</a>");
		LabelTip = tr("Click to open web browser");

		//auto neon = new CNeonEffect(10, 4, 240);
		auto neon = new CNeonEffect(10, 4);
		//neon->setGlowColor(Qt::green);
		neon->setHue(240);
		/*if(m_DarkTheme)
			neon->setColor(QColor(218, 130, 42));
		else
			neon->setColor(Qt::blue);*/
		m_pLabel->setGraphicsEffect(neon);

		/*auto glowAni = new QVariantAnimation(neon);
		glowAni->setDuration(10000);
		glowAni->setLoopCount(-1);
		glowAni->setStartValue(0);
		glowAni->setEndValue(360);
		glowAni->setEasingCurve(QEasingCurve::InQuad);
			connect(glowAni, &QVariantAnimation::valueChanged, [neon](const QVariant &value) {
				neon->setHue(value.toInt());
				qDebug() << value.toInt();
		});
		glowAni->start();*/

		/*auto glowAni = new QVariantAnimation(neon);
		glowAni->setDuration(3000);
		glowAni->setLoopCount(-1);
		glowAni->setStartValue(5);
		glowAni->setEndValue(20);
		glowAni->setEasingCurve(QEasingCurve::InQuad);
			connect(glowAni, &QVariantAnimation::valueChanged, [neon](const QVariant &value) {
				neon->setBlurRadius(value.toInt());
				qDebug() << value.toInt();
		});
		glowAni->start();*/

		/*auto glowAni = new QVariantAnimation(neon);
		glowAni->setDuration(3000);
		glowAni->setLoopCount(-1);
		glowAni->setStartValue(1);
		glowAni->setEndValue(20);
		glowAni->setEasingCurve(QEasingCurve::InQuad);
			connect(glowAni, &QVariantAnimation::valueChanged, [neon](const QVariant &value) {
				neon->setGlow(value.toInt());
				qDebug() << value.toInt();
		});
		glowAni->start();*/

		/*auto glowAni = new QVariantAnimation(neon);
		glowAni->setDuration(3000);
		glowAni->setLoopCount(-1);
		glowAni->setStartValue(5);
		glowAni->setEndValue(25);
		glowAni->setEasingCurve(QEasingCurve::InQuad);
			connect(glowAni, &QVariantAnimation::valueChanged, [neon](const QVariant &value) {
				int iValue = value.toInt();
				if (iValue >= 15)
					iValue = 30 - iValue;
				neon->setGlow(iValue);
				neon->setBlurRadius(iValue);
		});
		glowAni->start();*/

	}

	if(m_pSeparator) m_pSeparator->setVisible(!LabelText.isEmpty());
	m_pLabel->setVisible(!LabelText.isEmpty());
	m_pLabel->setText(LabelText);
	m_pLabel->setToolTip(LabelTip);
}

void CSandMan::CreateView(int iViewMode)
{
	m_pBoxView = new CSbieView();
	connect(m_pBoxView, SIGNAL(BoxSelected()), this, SLOT(OnBoxSelected()));
	m_pFileView = new CFileView();

	if (iViewMode != 1) {
		m_pRecoveryLogWnd = new CRecoveryLogWnd(m_pMainWidget);
		connect(this, SIGNAL(Closed()), m_pRecoveryLogWnd, SLOT(close()));
	}
	else
		m_pRecoveryLogWnd = NULL;

	if (iViewMode == 2) 
	{
		m_pViewStack = new QStackedLayout();
		m_pViewStack->addWidget(m_pBoxView);

		QWidget* pFileView = new QWidget();
		QGridLayout* pFileLayout = new QGridLayout(pFileView);
		pFileLayout->setContentsMargins(0,0,0,0);

		pFileLayout->addItem(new QSpacerItem(40, 20, QSizePolicy::Expanding, QSizePolicy::Minimum), 0, 0, 1, 1);

		m_pBoxCombo = new QComboBox();
		connect(m_pBoxCombo, SIGNAL(currentIndexChanged(int)), this, SLOT(OnBoxSelected()));
		pFileLayout->addWidget(m_pBoxCombo, 0, 1);

		pFileLayout->addWidget(m_pFileView, 1, 0, 1, 2);

		m_pViewStack->addWidget(pFileView);


		m_pMainLayout->addLayout(m_pViewStack);

		m_pPanelSplitter = NULL;
		m_pLogSplitter = NULL;

		m_pLogTabs = NULL;

		m_pMessageLog = NULL;
		m_pTraceView = NULL;
		m_pRecoveryLog = NULL;

		return;
	}

	m_pViewStack = NULL;
	m_pBoxCombo = NULL;

	if (iViewMode == 1) 
	{
		m_pLogSplitter = new QSplitter();
		m_pLogSplitter->setOrientation(Qt::Vertical);
		m_pMainLayout->addWidget(m_pLogSplitter);
	}

	m_pPanelSplitter = new QSplitter();
	m_pPanelSplitter->setOrientation(Qt::Horizontal);
	if (iViewMode == 1)
		m_pLogSplitter->addWidget(m_pPanelSplitter);
	else 
		m_pMainLayout->addWidget(m_pPanelSplitter);

	m_pPanelSplitter->addWidget(m_pBoxView);
	m_pPanelSplitter->addWidget(m_pFileView);

	m_pPanelSplitter->setCollapsible(0, false);
	//m_pPanelSplitter->setCollapsible(1, false);

	if (iViewMode == 1)
	{
		m_pLogTabs = new QTabWidget();
		m_pLogSplitter->addWidget(m_pLogTabs);

		// Message Log
		m_pMessageLog = new CPanelWidgetEx();
		m_pMessageLog->GetTree()->setItemDelegate(new CTreeItemDelegate());

		m_pMessageLog->GetTree()->setAlternatingRowColors(theConf->GetBool("Options/AltRowColors", false));
		
		//m_pMessageLog->GetView()->setItemDelegate(theGUI->GetItemDelegate());
		((QTreeWidgetEx*)m_pMessageLog->GetView())->setHeaderLabels(tr("Time|Message").split("|"));

		m_pMessageLog->GetMenu()->insertAction(m_pMessageLog->GetMenu()->actions()[0], m_pCleanUpMsgLog);
		m_pMessageLog->GetMenu()->insertSeparator(m_pMessageLog->GetMenu()->actions()[0]);

		m_pMessageLog->GetView()->setSelectionMode(QAbstractItemView::ExtendedSelection);
		m_pMessageLog->GetView()->setSortingEnabled(false);

		m_pLogTabs->addTab(m_pMessageLog, tr("Sbie Messages"));
		//

		m_pTraceView = new CTraceView(false, this);

		m_pTraceView->AddAction(m_pCleanUpTrace);

		m_pLogTabs->addTab(m_pTraceView, tr("Trace Log"));


		// Recovery Log
		m_pRecoveryLog = new CPanelWidgetEx();
		m_pRecoveryLog->GetTree()->setItemDelegate(new CTreeItemDelegate());

		m_pRecoveryLog->GetTree()->setAlternatingRowColors(theConf->GetBool("Options/AltRowColors", false));

		//m_pRecoveryLog->GetView()->setItemDelegate(theGUI->GetItemDelegate());
		((QTreeWidgetEx*)m_pRecoveryLog->GetView())->setHeaderLabels(tr("Time|Box Name|File Path").split("|"));

		m_pRecoveryLog->GetMenu()->insertAction(m_pRecoveryLog->GetMenu()->actions()[0], m_pCleanUpRecovery);
		m_pRecoveryLog->GetMenu()->insertSeparator(m_pRecoveryLog->GetMenu()->actions()[0]);

		m_pRecoveryLog->GetView()->setSelectionMode(QAbstractItemView::ExtendedSelection);
		m_pRecoveryLog->GetView()->setSortingEnabled(false);

		m_pLogTabs->addTab(m_pRecoveryLog, tr("Recovery Log"));
		//
	}
	else {
		m_pLogSplitter = NULL;

		m_pLogTabs = NULL;

		m_pMessageLog = NULL;
		m_pTraceView = NULL;
		m_pRecoveryLog = NULL;
	}
}

void CSandMan::CheckForUpdates(bool bManual)
{
	m_pUpdater->CheckForUpdates(bManual);
}

#include "SandManTray.cpp"

void CSandMan::OnExit()
{
	m_bExit = true;
	close();
}

void CSandMan::closeEvent(QCloseEvent *e)
{
	if (!m_bExit)// && !theAPI->IsConnected())
	{
		if (m_pTrayIcon->isVisible())
		{
			StoreState();
			hide();

			if (theAPI->GetGlobalSettings()->GetBool("ForgetPassword", false))
				theAPI->ClearPassword();

			e->ignore();
			return;
		}
		else
		{
			CExitDialog ExitDialog(tr("Do you want to close Sandboxie Manager?"));
			if (!ExitDialog.exec())
			{
				e->ignore();
				return;
			}
		}
	}

	emit Closed();

	//if(theAPI->IsConnected())
	//	m_pBoxView->SaveUserConfig();

	if (IsFullyPortable() && theAPI->IsConnected())
	{
		int PortableStop = theConf->GetInt("Options/PortableStop", -1);
		if (PortableStop == -1)
		{
			bool State = false;
			auto Ret = CCheckableMessageBox::question(this, "Sandboxie-Plus", tr("Sandboxie-Plus was running in portable mode, now it has to clean up the created services. This will prompt for administrative privileges.\n\nDo you want to do the clean up?")
				, tr("Don't show this message again."), &State, QDialogButtonBox::Yes | QDialogButtonBox::No | QDialogButtonBox::Cancel, QDialogButtonBox::Yes, QMessageBox::Question);

			if (Ret == QDialogButtonBox::Cancel)
			{
				e->ignore();
				return;
			}

			PortableStop = (Ret == QDialogButtonBox::Yes) ? 1 : 0;

			if (State)
				theConf->SetValue("Options/PortableStop", PortableStop);
		}

		if (PortableStop == 1) {
			SB_RESULT(void*) Status = StopSbie(true);
			// don't care for Status we quit here anyway
		}
	}

	QApplication::quit();
}

void CSandMan::commitData(QSessionManager& manager)
{
    //if (manager.allowsInteraction()) 
	//{
    //	manager.cancel();
	//	return;
    //} 

	m_pBoxView->SaveState();
	m_pFileView->SaveState();
	StoreState();
	theConf->Sync();
}

QIcon CSandMan::GetBoxIcon(int boxType, bool inUse)// , int iBusy)
{
	//EBoxColors color = eYellow;
	int iViewMode = theConf->GetInt("Options/ViewMode", 1);
	if (iViewMode != 2) {
		return GetColorIcon(m_BoxColors[boxType], inUse);
		/*switch (boxType) {
		case CSandBoxPlus::eHardenedPlus:		color = eRed; break;
		case CSandBoxPlus::eHardened:			color = eOrang; break;
		case CSandBoxPlus::eDefaultPlus:		color = eBlue; break;
		case CSandBoxPlus::eDefault:			color = eYellow; break;
		case CSandBoxPlus::eAppBoxPlus:			color = eCyan; break;
		case CSandBoxPlus::eAppBox:				color = eGreen; break;
		case CSandBoxPlus::eInsecure:			color = eMagenta; break;
		case CSandBoxPlus::eOpen:				color = eWhite; break;
		}*/
	}
	//if (inBusy)
	//	return m_BoxIcons[color].Busy;
	/*if (inUse)
		return m_BoxIcons[color].InUse;
	return m_BoxIcons[color].Empty;*/
	return GetColorIcon(m_BoxColors[CSandBoxPlus::eDefault], inUse);
}

QIcon CSandMan::GetColorIcon(QColor boxColor, bool inUse/*, bool bOut*/)
{
	static QPixmap Sand;
	if(Sand.isNull())
		Sand = QPixmap(":/Boxes/Sand");

	static QPixmap Frame;
	if(Frame.isNull())
		Frame = QPixmap(":/Boxes/Frame");

	static QPixmap Items;
	if(Items.isNull())
		Items = QPixmap(":/Boxes/Items");

	static QPixmap Out;
	if(Out.isNull())
		Out = QPixmap(":/Boxes/Out");

	QRgb rgb = boxColor.rgba();
	QImage MySand = Sand.toImage();
	//if (!bOut) {
	for (QRgb* c = (QRgb*)MySand.bits(); c != (QRgb*)(MySand.bits() + MySand.sizeInBytes()); c++) {
		if (*c == 0xFFFFFFFF)
			*c = rgb;
	}
	//}
	
	QPixmap result(32, 32);
	result.fill(Qt::transparent); // force alpha channel
	QPainter painter(&result);
	//if (bOut) {
	//	QImage MyOut = Out.toImage();
	//	for (QRgb* c = (QRgb*)MyOut.bits(); c != (QRgb*)(MyOut.bits() + MyOut.sizeInBytes()); c++) {
	//		*c = rgb;
	//	}
	//	painter.drawPixmap(0, 0, QPixmap::fromImage(MyOut));
	//}
	painter.drawPixmap(0, 0, QPixmap::fromImage(MySand));
	painter.drawPixmap(0, 0, Frame);
	if (inUse) 
	{	
		//rgb = change_hsv_c(rgb, -60, 2, 1); // yellow -> red

		my_rgb rgb1 = { (double)qRed(rgb), (double)qGreen(rgb), (double)qBlue(rgb) };
		my_hsv hsv = rgb2hsv(rgb1);

		if((hsv.h >= 30 && hsv.h < 150) || (hsv.h >= 210 && hsv.h < 330))		hsv.h -= 60;
		else if(hsv.h >= 150 && hsv.h < 210)									hsv.h += 120;
		else if((hsv.h >= 330 && hsv.h < 360) || (hsv.h >= 0 && hsv.h < 30))	hsv.h -= 240;

		if (hsv.h < 0) hsv.h += 360;
		else if (hsv.h >= 360) hsv.h -= 360;
		hsv.s = 1; // make the content always fully saturated

		my_rgb rgb2 = hsv2rgb(hsv);
		rgb = qRgb(rgb2.r, rgb2.g, rgb2.b);

		QImage MyItems = Items.toImage();
		for (QRgb* c = (QRgb*)MyItems.bits(); c != (QRgb*)(MyItems.bits() + MyItems.sizeInBytes()); c++) {
			if (*c == 0xFF000000)
				*c = rgb;
		}
		painter.drawPixmap(0, 0, QPixmap::fromImage(MyItems));
	}

	return QIcon(result);
}

QIcon CSandMan::MakeIconBusy(const QIcon& Icon, int Index)
{
	static QPixmap overlay;
	if(overlay.isNull())
		overlay = QPixmap(":/Boxes/Busy");

	QPixmap base = Icon.pixmap(32, 32);
	QPixmap result(base.width(), base.height());
	result.fill(Qt::transparent); // force alpha channel
	QPainter painter(&result);
	painter.drawPixmap(0, 0, base);

	QTransform  rm;
	rm.rotate(90 * (Index % 4));
	painter.drawPixmap(8, 8, overlay.transformed(rm));
	return QIcon(result);
}

QIcon CSandMan::IconAddOverlay(const QIcon& Icon, const QString& Name, int Size)
{
	QPixmap overlay = QPixmap(Name).scaled(Size, Size, Qt::IgnoreAspectRatio, Qt::SmoothTransformation);

	QPixmap base = Icon.pixmap(32, 32);
	QPixmap result(base.width(), base.height());
	result.fill(Qt::transparent); // force alpha channel
	QPainter painter(&result);
	painter.drawPixmap(0, 0, base);

	painter.drawPixmap(32 - Size, 32 - Size, overlay);
	return QIcon(result);
}

QString CSandMan::GetBoxDescription(int boxType)
{
	QString Info;

	switch (boxType) {
	case CSandBoxPlus::eHardenedPlus:
	case CSandBoxPlus::eHardened:
		Info = tr("This box provides enhanced security isolation, it is suitable to test untrusted software.");
		break;
	case CSandBoxPlus::eDefaultPlus:
	case CSandBoxPlus::eDefault:
		Info = tr("This box provides standard isolation, it is suitable to run your software to enhance security.");	
		break;
	case CSandBoxPlus::eAppBoxPlus:
	case CSandBoxPlus::eAppBox:
		Info = tr("This box does not enforce isolation, it is intended to be used as an application compartment for software virtualization only.");
		break;
	}
	
	if(boxType == CSandBoxPlus::eHardenedPlus || boxType == CSandBoxPlus::eDefaultPlus || boxType == CSandBoxPlus::eAppBoxPlus)
		Info.append(tr("\n\nThis box prevents access to all user data locations, except explicitly granted in the Resource Access options."));

	return Info;
}

bool CSandMan::IsFullyPortable()
{
	QString SbiePath = theAPI->GetSbiePath();
	QString IniPath = theAPI->GetIniPath();
	if (IniPath.indexOf(SbiePath, 0, Qt::CaseInsensitive) == 0)
		return true;
	return false;
}

bool CSandMan::IsSilentMode()
{
	if (!theConf->GetBool("Options/CheckSilentMode", true))
		return false;
	return IsFullScreenMode();
}

void CSandMan::OnMessage(const QString& MsgData)
{
	QStringList Messages = MsgData.split("\n");
	QString Message = Messages[0];
	if (Message == "ShowWnd")
	{
		if (!isVisible())
			show();
		setWindowState(Qt::WindowActive);
		SetForegroundWindow(MainWndHandle);
	}
	else if (Message.left(4) == "Run:")
	{
		QString BoxName;
		QString CmdLine = Message.mid(4);

		if (CmdLine.contains("\\start.exe", Qt::CaseInsensitive)) {
			int pos = CmdLine.indexOf("/box:", 0, Qt::CaseInsensitive);
			int pos2 = CmdLine.indexOf(" ", pos);
			if (pos != -1 && pos2 != -1) {
				BoxName = CmdLine.mid(pos + 5, pos2 - (pos + 5));
				CmdLine = CmdLine.mid(pos2 + 1);
			}
		}

		QString WrkDir;
		for (int i = 1; i < Messages.length(); i++) {
			if (Messages[i].left(5) == "From:") {
				WrkDir = Messages[i].mid(5);
				break;
			}
		}

		CSupportDialog::CheckSupport(true);

		if (theConf->GetBool("Options/RunInDefaultBox", false) && (QGuiApplication::queryKeyboardModifiers() & Qt::ControlModifier) == 0) {
			theAPI->RunStart(theAPI->GetGlobalSettings()->GetText("DefaultBox", "DefaultBox"), CmdLine, false, WrkDir);
		}
		else
			RunSandboxed(QStringList(CmdLine), BoxName, WrkDir);
	}
	else if (Message.left(3) == "Op:")
	{
		QString Op = Message.mid(3);

		SB_RESULT(void*) Status;
		if (Op == "Connect")
			Status = ConnectSbie();
		else if (Op == "Disconnect")
			Status = DisconnectSbie();
		else if (Op == "Shutdown")
			Status = StopSbie();
		else if (Op == "EmptyAll")
			Status = theAPI->TerminateAll();
		else
			Status = SB_ERR(SB_Message, QVariantList () << (tr("Unknown operation '%1' requested via command line").arg(Op)));

		HandleMaintenance(Status);
	}
}

void CSandMan::dragEnterEvent(QDragEnterEvent* e)
{
	if (e->mimeData()->hasUrls()) {
		e->acceptProposedAction();
	}
}

bool CSandMan::RunSandboxed(const QStringList& Commands, QString BoxName, const QString& WrkDir)
{
	if (BoxName.isEmpty())
		BoxName = theAPI->GetGlobalSettings()->GetText("DefaultBox", "DefaultBox");
	CSelectBoxWindow* pSelectBoxWindow = new CSelectBoxWindow(Commands, BoxName, WrkDir);
	connect(this, SIGNAL(Closed()), pSelectBoxWindow, SLOT(close()));
	//pSelectBoxWindow->show();
	return SafeExec(pSelectBoxWindow) == 1;
}

void CSandMan::dropEvent(QDropEvent* e)
{
	QStringList Commands;
	foreach(const QUrl & url, e->mimeData()->urls()) {
		if (url.isLocalFile())
			Commands.append(url.toLocalFile().replace("/", "\\"));
	}

	QString BoxName;
	QList<CSandBoxPtr> Boxes = m_pBoxView->GetSelectedBoxes();
	if (Boxes.count() == 1)
		BoxName = Boxes.first()->GetName();

	QTimer::singleShot(0, this, [Commands, BoxName, this]() { RunSandboxed(Commands, BoxName); });
}

void CSandMan::timerEvent(QTimerEvent* pEvent)
{
	if (pEvent->timerId() != m_uTimerID)
		return;

	bool bForceProcessDisabled = false;
	bool bIconBusy = false;
	bool bConnected = false;

	if (theAPI->IsConnected())
	{
		SB_STATUS Status = theAPI->ReloadBoxes();

		theAPI->UpdateProcesses(KeepTerminated(), ShowAllSessions());

		bForceProcessDisabled = theAPI->AreForceProcessDisabled();
		m_pDisableForce->setChecked(bForceProcessDisabled);
		m_pDisableForce2->setChecked(bForceProcessDisabled);

		if (m_pTraceView) 
		{
			bool bIsMonitoring = theAPI->IsMonitoring();
			m_pEnableMonitoring->setChecked(bIsMonitoring);
			int iTraceCount = theAPI->GetTraceCount();
			if (!bIsMonitoring && iTraceCount > 0)
				bIsMonitoring = true; // don't disable the view as long as there are entries shown
			if (bIsMonitoring && m_pTraceView)
				m_pTraceInfo->setText(QString::number(iTraceCount));
			m_pTraceView->setEnabled(bIsMonitoring);
		}

		QMap<quint32, CBoxedProcessPtr> Processes = theAPI->GetAllProcesses();
		int ActiveProcesses = 0;
		if (KeepTerminated()) {
			foreach(const CBoxedProcessPtr & Process, Processes) {
				if (!Process->IsTerminated())
					ActiveProcesses++;
			}
		}
		else 
			ActiveProcesses = Processes.count();


		if (theAPI->IsBusy() || m_iDeletingContent > 0)
			bIconBusy = true;

		if (m_iIconDisabled != (bForceProcessDisabled ? 1 : 0)) {
			QString Str1 = tr("No Force Process");
			m_pDisabledForce->setText(m_pDisableForce->isChecked() ? Str1 : QString(Str1.length(), ' '));
		}

		bool bUpdatePending = !theConf->GetString("Updater/PendingUpdate").isEmpty();

		if (m_bIconEmpty != (ActiveProcesses == 0) || m_bIconBusy != bIconBusy || m_iIconDisabled != (bForceProcessDisabled ? 1 : 0) || bUpdatePending)
		{
			m_bIconEmpty = (ActiveProcesses == 0);
			m_bIconBusy = bIconBusy;
			m_iIconDisabled = (bForceProcessDisabled ? 1 : 0);

			m_bIconSun = bUpdatePending ? !m_bIconSun : false;
			m_pTrayIcon->setIcon(GetTrayIcon(true, m_bIconSun));
			m_pTrayIcon->setToolTip(GetTrayText());
		}
	}

	if (!isVisible() || windowState().testFlag(Qt::WindowMinimized))
		return;

	//QUERY_USER_NOTIFICATION_STATE NState; // todo
	//if (SHQueryUserNotificationState(&NState) == S_OK)
	//	;

	theAPI->UpdateWindowMap();

	m_pBoxView->Refresh();

	if(!IsSilentMode()) // do not check for updates when in presentation/game mode
		m_pUpdater->Process();

	if (!m_MissingTemplates.isEmpty())
	{
		if (m_MissingTemplates[0] == "") {
			m_MissingTemplates.clear();
			return;
		}

		int CleanupTemplates = theConf->GetInt("Options/AutoCleanupTemplates", -1);
		if (CleanupTemplates == -1)
		{
			bool State = false;
			CleanupTemplates = CCheckableMessageBox::question(this, "Sandboxie-Plus", tr("Some compatibility templates (%1) are missing, probably deleted, do you want to remove them from all boxes?")
				.arg(m_MissingTemplates.join(", "))
				, tr("Don't show this message again."), &State, QDialogButtonBox::Yes | QDialogButtonBox::No, QDialogButtonBox::Yes, QMessageBox::Information) == QDialogButtonBox::Yes ? 1 : 0;

			if (State)
				theConf->SetValue("Options/AutoCleanupTemplates", CleanupTemplates);
		}

		if (CleanupTemplates)
		{
			foreach(const QString& Template, m_MissingTemplates)
			{
				theAPI->GetGlobalSettings()->DelValue("Template", Template);
				foreach(const CSandBoxPtr& pBox, theAPI->GetAllBoxes())
					pBox->DelValue("Template", Template);
			}

			OnLogMessage(tr("Cleaned up removed templates..."));
		}
		m_MissingTemplates.clear();

		m_MissingTemplates.append("");
	}
}

void CSandMan::OnBoxSelected()
{
	CSandBoxPtr pBox;
	if (m_pPanelSplitter) {
		QList<CSandBoxPtr> boxes = m_pBoxView->GetSelectedBoxes();
		if (m_pPanelSplitter->sizes().at(1) > 0 && m_pFileView->isVisible() && boxes.count() == 1)
			pBox = boxes.first();
	}

	// for vintage mode
	if (m_pBoxCombo && m_pViewStack->currentIndex() == 1) {
		QString Name = m_pBoxCombo->currentData().toString();
		if (Name.isEmpty())
			Name = theAPI->GetGlobalSettings()->GetText("DefaultBox", "DefaultBox");
		pBox = theAPI->GetBoxByName(Name);
	}

	if (!pBox.isNull()) {
		if (!m_pFileView->isEnabled()) m_pFileView->setEnabled(true);
		if (pBox != m_pFileView->GetBox()) m_pFileView->SetBox(pBox);
	}
	else if (m_pFileView->isEnabled()) {
		m_pFileView->setEnabled(false);
		m_pFileView->SetBox(CSandBoxPtr());
	}
}

SB_STATUS CSandMan::DeleteBoxContent(const CSandBoxPtr& pBox, EDelMode Mode, bool DeleteSnapshots)
{
	SB_STATUS Ret = SB_OK;
	m_iDeletingContent++;

	if (Mode != eAuto) {
		Ret = pBox->TerminateAll();
		theAPI->UpdateProcesses(KeepTerminated(), ShowAllSessions());
		if (Ret.IsError())
			goto finish;
	}

	if (Mode != eForDelete) {
		foreach(const QString & Value, pBox->GetTextList("OnBoxDelete", true, false, true)) {
			QString Value2 = pBox->Expand(Value);
			CSbieProgressPtr pProgress = CSbieUtils::RunCommand(Value2, true);
			if (!pProgress.isNull()) {
				AddAsyncOp(pProgress, true, tr("Executing OnBoxDelete: %1").arg(Value2));
				if (pProgress->IsCanceled()) {
					Ret = CSbieStatus(SB_Canceled);
					goto finish;
				}
			}
		}
	}
	
	{
		SB_PROGRESS Status;
		if (Mode != eForDelete && !DeleteSnapshots && pBox->HasSnapshots()) { // in auto delete mode always return to last snapshot
			QString Current;
			QString Default = pBox->GetDefaultSnapshot(&Current);
			Status = pBox->SelectSnapshot(Mode == eAuto ? Current : Default);
		}
		else // if there are no snapshots just use the normal cleaning procedure
			Status = pBox->CleanBox();

		Ret = Status;
		if (Status.GetStatus() == OP_ASYNC) {
			Ret = AddAsyncOp(Status.GetValue(), true, tr("Auto Deleting %1 Content").arg(pBox->GetName()));
			OnBoxCleaned(qobject_cast<CSandBoxPlus*>(pBox.data()));
		}
	}

finish:
	m_iDeletingContent--;
	return Ret;
}

void CSandMan::OnBoxAdded(const CSandBoxPtr& pBox)
{
	connect(pBox.data(), SIGNAL(StartMenuChanged()), this, SLOT(OnStartMenuChanged()));
}

void CSandMan::EnumBoxLinks(QMap<QString, QMap<QString,SBoxLink> > &BoxLinks, const QString& Prefix, const QString& Folder, bool bWithSubDirs)
{
	QRegularExpression exp("/\\[[0-9Sa-zA-Z_]+\\] ");

	QStringList	Files = ListDir(Folder, QStringList() << "*.lnk" << "*.url" << "*.pif", bWithSubDirs);
	foreach(QString File, Files)
	{
		auto result = exp.match(File);
		if(!result.hasMatch())
			continue;

		int pos = result.capturedStart() + 1;
		int len = result.capturedLength() - 1;
		QString BoxName = File.mid(pos + 1, len - 3);

		SBoxLink BoxLink;
		BoxLink.RelPath = (Prefix + "/" + QString(File).remove(pos, len));
		BoxLink.FullPath = Folder + "/" + File;

		QVariantMap Link = ResolveShortcut(BoxLink.FullPath);
		BoxLink.Target = Link["Arguments"].toString().trimmed();
		if (BoxLink.Target.left(4) == "/box") {
			if(int pos = BoxLink.Target.indexOf(" ") + 1)
				BoxLink.Target = BoxLink.Target.mid(pos).trimmed();
		}

		BoxLinks[BoxName.toLower()].insert(BoxLink.RelPath.toLower(), BoxLink);
	}
}

void CSandMan::CleanupShortcutPath(const QString& Path)
{
	QRegularExpression exp("\\[[0-9Sa-zA-Z_]+\\] ");

	StrPair PathName = Split2(Path, "/", true);
	if (PathName.first.indexOf(exp) != -1) {
		if (ListDir(PathName.first).isEmpty())
		{
			QDir().rmdir(PathName.first);
			//qDebug() << "delete dir" << PathName.first;
			CleanupShortcutPath(PathName.first);
		}
	}
}

void CSandMan::DeleteShortcut(const QString& Path)
{
	QFile::remove(Path);
	//qDebug() << "delete link" << Path;
	CleanupShortcutPath(Path);
}

void CSandMan::CleanUpStartMenu(QMap<QString, QMap<QString, SBoxLink> >& BoxLinks)
{
	for (auto I = BoxLinks.begin(); I != BoxLinks.end(); ++I) {
		for (auto J = I->begin(); J != I->end(); ++J) {
			//qDebug() << "Delete Shortcut" << J.value();
			OnLogMessage(tr("Removed Shortcut: %1").arg(J.key()));
			DeleteShortcut(J->FullPath);
		}
	}
}

void CSandMan::ClearStartMenu()
{
	QMap<QString, QMap<QString, SBoxLink> > BoxLinks;
	EnumBoxLinks(BoxLinks, "Programs", QStandardPaths::writableLocation(QStandardPaths::ApplicationsLocation));
	EnumBoxLinks(BoxLinks, "Desktop", QStandardPaths::writableLocation(QStandardPaths::DesktopLocation), false);

	CleanUpStartMenu(BoxLinks);
}

bool CSandMan__MatchLinkTarget(QString L, QString R)
{
	if (L == R)
		return true;
	if (L.left(1) == "\"" &&  L == "\"" + R + "\"")
		return true;
	return false;
}

void CSandMan::SyncStartMenu()
{
	m_StartMenuUpdatePending = false;

	int Mode = theConf->GetInt("Options/IntegrateStartMenu", 0);
	if (Mode == 0)
		return;

	QMap<QString, QMap<QString, SBoxLink> > BoxLinks;
	EnumBoxLinks(BoxLinks, "Programs", QStandardPaths::writableLocation(QStandardPaths::ApplicationsLocation));
	EnumBoxLinks(BoxLinks, "Desktop", QStandardPaths::writableLocation(QStandardPaths::DesktopLocation), false);

	QMap<QString, CSandBoxPtr> Boxes = theAPI->GetAllBoxes();
	foreach(const CSandBoxPtr & pBox, Boxes) 
	{
		CSandBoxPlus* pBoxEx = (CSandBoxPlus*)pBox.data();

		QMap<QString, SBoxLink>& CurLinks = BoxLinks[pBoxEx->GetName().toLower()];

		foreach(const CSandBoxPlus::SLink & Link, pBoxEx->GetStartMenu())
		{
			QString Location;
			QString Prefix;
			StrPair LocPath = Split2(Link.Folder, "/");
			if (Mode == 2) // deep integration
			{
				if (LocPath.first == "Programs")
					Location = QStandardPaths::writableLocation(QStandardPaths::ApplicationsLocation);
				else if (LocPath.first == "Desktop")
					Location = QStandardPaths::writableLocation(QStandardPaths::DesktopLocation);
				else
					continue;
			}
			else //if(Mode == 1) // contained integration
			{
				Prefix = "Programs\\Sandboxie-Plus\\";
				Location = QStandardPaths::writableLocation(QStandardPaths::ApplicationsLocation) + "\\Sandboxie-Plus";
				Location += "\\" + LocPath.first;
			}

			QString Folder;
			if (LocPath.second.isEmpty()) {
				Folder = Location;
				Location += "\\[" + pBoxEx->GetName() + "] ";
			} else {
				Location += "\\[" + pBoxEx->GetName() + "] " + LocPath.second + "\\";
				Folder = Location;
			}

			QString Key = QString(Prefix + Link.Folder + "\\" + Link.Name + ".lnk").replace("\\", "/").toLower();
			SBoxLink BoxLink = CurLinks.take(Key);
			if (BoxLink.FullPath.isEmpty() || !CSandMan__MatchLinkTarget(BoxLink.Target, Link.Target)) {
				//qDebug() << "CreateShortcut" << Location + Link.Name;
				if (!BoxLink.FullPath.isEmpty()) {
					QFile::remove(BoxLink.FullPath);
					OnLogMessage(tr("Updated Shortcut to: %1").arg(Key));
				} else
					OnLogMessage(tr("Added Shortcut to: %1").arg(Key));
				QDir().mkpath(Folder);
				CSbieUtils::CreateShortcut(theAPI, Location + Link.Name,
						Link.Name, pBoxEx->GetName(), Link.Target, Link.Icon.isEmpty() ? Link.Target : Link.Icon, Link.IconIndex);
			}
		}
	}

	CleanUpStartMenu(BoxLinks);
}

void CSandMan::OnStartMenuChanged()
{
	if (!m_StartMenuUpdatePending)
	{
		m_StartMenuUpdatePending = true;
		QTimer::singleShot(1000, this, SLOT(SyncStartMenu()));
	}
}

void CSandMan::OnBoxClosed(const CSandBoxPtr& pBox)
{
	if (!pBox->GetBool("NeverDelete", false))
	{
		if (pBox->GetBool("AutoDelete", false))
		{
			bool DeleteSnapshots = false;
			// if this box auto deletes first show the recovry dialog with the option to abort deletion
			if (!theGUI->OpenRecovery(pBox, DeleteSnapshots, true)) // unless no files are found than continue silently
				return;

			if (theConf->GetBool("Options/AutoBoxOpsNotify", false))
				OnLogMessage(tr("Auto deleting content of %1").arg(pBox->GetName()), true);

			if (theConf->GetBool("Options/UseAsyncBoxOps", false) || IsSilentMode())
			{
				auto pBoxEx = pBox.objectCast<CSandBoxPlus>();
				SB_STATUS Status = pBoxEx->DeleteContentAsync(DeleteSnapshots);
				CheckResults(QList<SB_STATUS>() << Status);
			}
			else
				DeleteBoxContent(pBox, eAuto, DeleteSnapshots);
		}
	}
}

void CSandMan::OnBoxCleaned(CSandBoxPlus* pBoxEx)
{
	if (pBoxEx->GetBool("AutoRemove", false)) 
	{
		if (theConf->GetBool("Options/AutoBoxOpsNotify", false))
			OnLogMessage(tr("Auto removing sandbox %1").arg(pBoxEx->GetName()), true);

		pBoxEx->RemoveBox();
		return;
	}
}

void CSandMan::OnStatusChanged()
{
	QString appTitle = tr("Sandboxie-Plus v%1").arg(GetVersion());
	if (theAPI->IsConnected())
	{
		bool bPortable = IsFullyPortable();

		QString SbiePath = theAPI->GetSbiePath();
		OnLogMessage(tr("%1 Directory: %2").arg(bPortable ? tr("Application") : tr("Installation")).arg(SbiePath));
		OnLogMessage(tr("Sandboxie-Plus Version: %1 (%2)").arg(GetVersion()).arg(theAPI->GetVersion()));
		OnLogMessage(tr("Current Config: %1").arg(theAPI->GetIniPath()));
		OnLogMessage(tr("Data Directory: %1").arg(QString(theConf->GetConfigDir()).replace("/","\\")));

		//statusBar()->showMessage(tr("Driver version: %1").arg(theAPI->GetVersion()));
		
		//appTitle.append(tr("   -   Driver: v%1").arg(theAPI->GetVersion()));
		if (bPortable)
		{
			//appTitle.append(tr("   -   Portable"));

			QString BoxPath = QDir::cleanPath(QApplication::applicationDirPath() + "/../Sandbox").replace("/", "\\");

			int PortableRootDir = theConf->GetInt("Options/PortableRootDir", 2);
			if (PortableRootDir == 2)
			{
				QString NtBoxRoot = theAPI->GetGlobalSettings()->GetText("FileRootPath", "\\??\\%SystemDrive%\\Sandbox\\%USER%\\%SANDBOX%", false, false).replace("GlobalSettings", "[BoxName]");

				bool State = false;
				PortableRootDir = CCheckableMessageBox::question(this, "Sandboxie-Plus", 
					tr("Sandboxie-Plus was started in portable mode, do you want to put the Sandbox folder into its parent directory?\nYes will choose: %1\nNo will choose: %2")
					.arg(BoxPath + "\\[BoxName]")
					.arg(theAPI->Nt2DosPath(NtBoxRoot))
					, tr("Don't show this message again."), &State, QDialogButtonBox::Yes | QDialogButtonBox::No, QDialogButtonBox::Yes, QMessageBox::Information) == QDialogButtonBox::Yes ? 1 : 0;

				if (State)
					theConf->SetValue("Options/PortableRootDir", PortableRootDir);
			}

			if (PortableRootDir)
				theAPI->GetGlobalSettings()->SetText("FileRootPath", BoxPath + "\\%SANDBOX%");
		}

		if (SbiePath.compare(QApplication::applicationDirPath().replace("/", "\\"), Qt::CaseInsensitive) == 0)
		{
			if (theAPI->GetUserSettings()->GetText("SbieCtrl_AutoStartAgent").isEmpty())
				theAPI->GetUserSettings()->SetText("SbieCtrl_AutoStartAgent", "SandMan.exe");

			QString cmd = CSbieUtils::GetContextMenuStartCmd();
			if (!cmd.isEmpty() && !cmd.contains("SandMan.exe", Qt::CaseInsensitive))
				CSettingsWindow__AddContextMenu();
		}

		m_pBoxView->Clear();

		OnIniReloaded();

		theAPI->WatchIni(true, theConf->GetBool("Options/WatchIni", true));

		if (!theAPI->ReloadCert().IsError())
			CSettingsWindow::LoadCertificate();
		else {
			g_Certificate.clear();

			QString CertPath = QCoreApplication::applicationDirPath() + "\\Certificate.dat";
			if(QFile::exists(CertPath)) // always delete invalid certificates
				WindowsMoveFile(CertPath.replace("/", "\\"), "");
		}
		UpdateCertState();

		uchar UsageFlags = 0;
		if (theAPI->GetSecureParam("UsageFlags", &UsageFlags, sizeof(UsageFlags))) {
			if (!g_CertInfo.business) {
				if ((UsageFlags & (2 | 1)) != 0) {
					if(g_CertInfo.valid)
						appTitle.append(tr(" for Personal use"));
					else
						appTitle.append(tr("   -   for Non-Commercial use ONLY"));
				}
			}
		}
		else { // migrate value form ini to registry // todo remove in later builds
			int BusinessUse = theConf->GetInt("Options/BusinessUse", 2);
			if (BusinessUse == 1) {
				UsageFlags = 1;
				theAPI->SetSecureParam("UsageFlags", &UsageFlags, sizeof(UsageFlags));
			}
		}
		
		g_FeatureFlags = theAPI->GetFeatureFlags();

		SB_STATUS Status = theAPI->ReloadBoxes(true);

		if (!Status.IsError()) {

			auto AllBoxes = theAPI->GetAllBoxes();

			m_pBoxView->ClearUserUIConfig(AllBoxes);

			foreach(const QString & Key, theConf->ListKeys("SizeCache")) {
				if (!AllBoxes.contains(Key.toLower()) || !theConf->GetBool("Options/WatchBoxSize", false))
					theConf->DelValue("SizeCache/" + Key);
			}

			QString DefaultBox = theAPI->GetGlobalSettings()->GetText("DefaultBox", "DefaultBox");
			if (!AllBoxes.contains(DefaultBox.toLower())) {
				OnLogMessage(tr("Default sandbox not found; creating: %1").arg(DefaultBox));
				theAPI->CreateBox(DefaultBox);
			}

			if (theConf->GetBool("Options/CleanUpOnStart", false)) {

				//
				// clean up Auto Delete boxes after reboot
				//

				theAPI->UpdateProcesses(false, ShowAllSessions());

				foreach(const CSandBoxPtr & pBox, AllBoxes) {
					if (pBox->GetActiveProcessCount() == 0)
						OnBoxClosed(pBox);
				}
			}
		}

		if (isVisible())
			CheckSupport();

		int WizardLevel = theConf->GetBool("Options/WizardLevel", 0);
		if (WizardLevel == 0) {
			if (!CSetupWizard::ShowWizard()) // if user canceled mark that and not show again
				theConf->SetValue("Options/WizardLevel", -1);
		}

		if (theConf->GetBool("Options/AutoRunSoftCompat", true))
		{
			if (m_SbieTemplates->RunCheck())
			{
				CSettingsWindow* pSettingsWindow = new CSettingsWindow(this);
				connect(pSettingsWindow, SIGNAL(OptionsChanged(bool)), this, SLOT(UpdateSettings(bool)));
				pSettingsWindow->showTab(CSettingsWindow::eSoftCompat);
			}
		}
	}
	else
	{
		appTitle.append(tr("   -   NOT connected"));

		m_pBoxView->Clear();

		theAPI->WatchIni(false);

		theAPI->StopMonitor();
	}

	this->setWindowTitle(appTitle);

	UpdateState();
}

void CSandMan::UpdateState()
{
	bool isConnected = theAPI->IsConnected();

	//m_pSupport->setVisible(g_Certificate.isEmpty());

	m_pTrayIcon->setIcon(GetTrayIcon(isConnected));
	m_pTrayIcon->setToolTip(GetTrayText(isConnected));
	m_bIconEmpty = true;
	m_iIconDisabled = -1;
	m_bIconBusy = false;
	m_bIconSun = false;

	m_pRunBoxed->setEnabled(isConnected);
	m_pNewBox->setEnabled(isConnected);
	m_pNewGroup->setEnabled(isConnected);
	m_pImportBox->setEnabled(isConnected);
	m_pEmptyAll->setEnabled(isConnected);
	m_pDisableForce->setEnabled(isConnected);
	m_pDisableForce2->setEnabled(isConnected);

	//m_pCleanUpMenu->setEnabled(isConnected);
	//m_pCleanUpButton->setEnabled(isConnected);
	//m_pKeepTerminated->setEnabled(isConnected);

	m_pEditIni->setEnabled(isConnected);
	if(m_pEditIni2) m_pEditIni2->setEnabled(isConnected);
	m_pReloadIni->setEnabled(isConnected);
	if(m_pEnableMonitoring) m_pEnableMonitoring->setEnabled(isConnected);
}

void CSandMan::OnMenuHover(QAction* action)
{
	//if (!m_pMenuBar->actions().contains(action))
	//	return; // ignore sub menus


	if (m_pMenuBar->actions().at(0) == action && m_pMaintenance)
	{
		bool bConnected = theAPI->IsConnected();
		m_pConnect->setEnabled(!bConnected);
		m_pDisconnect->setEnabled(bConnected);

		m_pMaintenanceItems->setEnabled(!bConnected);

		bool DrvInstalled = CSbieUtils::IsInstalled(CSbieUtils::eDriver);
		bool DrvLoaded = CSbieUtils::IsRunning(CSbieUtils::eDriver);
		m_pInstallDrv->setEnabled(!DrvInstalled);
		m_pStartDrv->setEnabled(!DrvLoaded);
		m_pStopDrv->setEnabled(DrvLoaded);
		m_pUninstallDrv->setEnabled(DrvInstalled);

		bool SvcInstalled = CSbieUtils::IsInstalled(CSbieUtils::eService);
		bool SvcStarted = CSbieUtils::IsRunning(CSbieUtils::eService);
		m_pInstallSvc->setEnabled(!SvcInstalled);
		m_pStartSvc->setEnabled(!SvcStarted && DrvInstalled);
		m_pStopSvc->setEnabled(SvcStarted);
		m_pUninstallSvc->setEnabled(SvcInstalled);

		//m_pMenuStopAll - always enabled
	}

	if (m_pMenuBar->actions().at(2) == action && m_pSandbox)
		CreateBoxMenu(m_pSandbox);
}

void CSandMan::CheckSupport()
{
	if (CSupportDialog::CheckSupport())
		return;

	static bool ReminderShown = false;
	if (!ReminderShown && (g_CertInfo.expired || g_CertInfo.about_to_expire) && !theConf->GetBool("Options/NoSupportCheck", false)) 
	{
		ReminderShown = true;
		CSettingsWindow* pSettingsWindow = new CSettingsWindow(this);
		connect(pSettingsWindow, SIGNAL(OptionsChanged(bool)), this, SLOT(UpdateSettings(bool)));
		pSettingsWindow->showTab(CSettingsWindow::eSupport);
	}
}

#define HK_PANIC 1

void CSandMan::SetupHotKeys()
{
	m_pHotkeyManager->unregisterAllHotkeys();

	if (theConf->GetBool("Options/EnablePanicKey", false))
		m_pHotkeyManager->registerHotkey(theConf->GetString("Options/PanicKeySequence", "Shift+Pause"), HK_PANIC);
}

void CSandMan::OnHotKey(size_t id)
{
	switch (id)
	{
	case HK_PANIC: 
		theAPI->TerminateAll();
		break;
	}
}

void CSandMan::OnLogMessage(const QString& Message, bool bNotify)
{
	AddLogMessage(Message);

	if (bNotify) {
		statusBar()->showMessage(Message);
		m_pTrayIcon->showMessage("Sandboxie-Plus", Message);
	}
}

void CSandMan::AddLogMessage(const QString& Message)
{
	if (!m_pMessageLog)
		return;

	QTreeWidgetItem* pItem = new QTreeWidgetItem(); // Time|Message
	pItem->setText(0, QDateTime::currentDateTime().toString("hh:mm:ss.zzz"));
	pItem->setText(1, Message);
	m_pMessageLog->GetTree()->addTopLevelItem(pItem);

	m_pMessageLog->GetView()->verticalScrollBar()->setValue(m_pMessageLog->GetView()->verticalScrollBar()->maximum());
}

void CSandMan::OnLogSbieMessage(quint32 MsgCode, const QStringList& MsgData, quint32 ProcessId)
{
	if ((MsgCode & 0xFFFF) == 2198 ) // file migration progress
	{
		if (!IsDisableMessages() && theConf->GetBool("Options/ShowMigrationProgress", true))
			m_pPopUpWindow->ShowProgress(MsgCode, MsgData, ProcessId);
		return;
	}

	if ((MsgCode & 0xFFFF) == 1411) // removed/missing template
	{
		if(MsgData.size() >= 3 && !m_MissingTemplates.contains(MsgData[2]))
			m_MissingTemplates.append(MsgData[2]);
	}

	if ((MsgCode & 0xFFFF) == 6004) // certificate error
	{
		static quint64 iLastCertWarning = 0;
		if (iLastCertWarning + 60 < QDateTime::currentDateTime().toSecsSinceEpoch()) { // reset after 60 seconds
			iLastCertWarning = QDateTime::currentDateTime().toSecsSinceEpoch();
			
			QString Message;
			if (!MsgData[2].isEmpty())
				Message = tr("The program %1 started in box %2 will be terminated in 5 minutes because the box was configured to use features exclusively available to project supporters.").arg(MsgData[2]).arg(MsgData[1]);
			else 
				Message = tr("The box %1 is configured to use features exclusively available to project supporters, these presets will be ignored.").arg(MsgData[1]);
			Message.append(tr("<br /><a href=\"https://sandboxie-plus.com/go.php?to=sbie-get-cert\">Become a project supporter</a>, and receive a <a href=\"https://sandboxie-plus.com/go.php?to=sbie-cert\">supporter certificate</a>"));

			QMessageBox msgBox(this);
			msgBox.setTextFormat(Qt::RichText);
			msgBox.setIcon(QMessageBox::Critical);
			msgBox.setWindowTitle("Sandboxie-Plus");
			msgBox.setText(Message);
			msgBox.setStandardButtons(QMessageBox::Ok);
			msgBox.exec();
			/*msgBox.setStandardButtons(QMessageBox::Yes | QMessageBox::No);
			if (msgBox.exec() == QDialogButtonBox::Yes) {
				OpenUrl(QUrl("https://sandboxie-plus.com/go.php?to=sbie-get-cert"));
			}*/
			
			//bCertWarning = false;
		}
		// return;
	}

	QString Message = MsgCode != 0 ? theAPI->GetSbieMsgStr(MsgCode, m_LanguageId) : (MsgData.size() > 0 ? MsgData[0] : QString());

	for (int i = 1; i < MsgData.size(); i++)
		Message = Message.arg(MsgData[i]);

	if (ProcessId != 4) // if it's not from the driver, add the pid
	{
		CBoxedProcessPtr pProcess = theAPI->GetProcessById(ProcessId);
		if(pProcess.isNull())
			Message.prepend(tr("PID %1: ").arg(ProcessId));
		else
			Message.prepend(tr("%1 (%2): ").arg(pProcess->GetProcessName()).arg(ProcessId));
	}

	OnLogMessage(Message);

	if ((MsgCode & 0xFFFF) == 6004) // certificate error
		return; // don't pop that one up

	if ((MsgCode & 0xFFFF) == 2111) // process open denided
		return; // don't pop that one up

	if(MsgCode != 0 && theConf->GetBool("Options/ShowNotifications", true) && !IsDisableMessages())
		m_pPopUpWindow->AddLogMessage(Message, MsgCode, MsgData, ProcessId);
}

bool CSandMan::CheckCertificate(QWidget* pWidget) 
{
	if (g_CertInfo.valid)
		return true;

	//if ((g_FeatureFlags & CSbieAPI::eSbieFeatureCert) == 0) {
	//	OnLogMessage(tr("The supporter certificate is expired"));
	//	return false;
	//}

	QMessageBox msgBox(pWidget);
	msgBox.setTextFormat(Qt::RichText);
	msgBox.setIcon(QMessageBox::Information);
	msgBox.setWindowTitle("Sandboxie-Plus");
	msgBox.setText(tr("The selected feature set is only available to project supporters. Processes started in a box with this feature set enabled without a supporter certificate will be terminated after 5 minutes.<br />"
		"<a href=\"https://sandboxie-plus.com/go.php?to=sbie-get-cert\">Become a project supporter</a>, and receive a <a href=\"https://sandboxie-plus.com/go.php?to=sbie-cert\">supporter certificate</a>"));
	msgBox.setStandardButtons(QMessageBox::Ok);
	msgBox.exec();
	/*msgBox.setStandardButtons(QMessageBox::Yes | QMessageBox::No);
	if (msgBox.exec() == QDialogButtonBox::Yes) {
		OpenUrl(QUrl("https://sandboxie-plus.com/go.php?to=sbie-get-cert"));
	}*/

	return false;
}

void CSandMan::UpdateCertState()
{
	g_CertInfo.State = theAPI->GetCertState();

#ifdef _DEBUG
	int CertificateStatus = theConf->GetInt("Debug/CertificateStatus", -1);
	switch (CertificateStatus)
	{
	case 0: // no certificate
		g_CertInfo.State = 0; 
		break;
	case 1: // evaluation/subscription/business cert expired
		g_CertInfo.valid = 0;
		g_CertInfo.expired = 1;
		break;
	case 2: // version bound cert expired but valid for this build
		g_CertInfo.expired = 1;
		break;
	case 3: // version bound cert expired and not valid for this build
		g_CertInfo.valid = 0;
		g_CertInfo.expired = 1;
		g_CertInfo.outdated = 1;
		break;
	}
#endif

	if (g_CertInfo.evaluation)
	{
		if (g_CertInfo.expired)
			OnLogMessage(tr("The evaluation period has expired!!!"));
	}
	else
	{
		g_CertInfo.about_to_expire = g_CertInfo.expirers_in_sec > 0 && g_CertInfo.expirers_in_sec < (60 * 60 * 24 * 30);
		if (g_CertInfo.outdated)
			OnLogMessage(tr("The supporter certificate is not valid for this build, please get an updated certificate"));
		// outdated always implicates it is no longer valid
		else if (g_CertInfo.expired) // may be still valid for the current and older builds
			OnLogMessage(tr("The supporter certificate has expired%1, please get an updated certificate")
				.arg(!g_CertInfo.outdated ? tr(", but it remains valid for the current build") : ""));
		else if (g_CertInfo.about_to_expire)
			OnLogMessage(tr("The supporter certificate will expire in %1 days, please get an updated certificate").arg(g_CertInfo.expirers_in_sec / (60 * 60 * 24)));
	}

	emit CertUpdated();
}

void CSandMan::OnQueuedRequest(quint32 ClientPid, quint32 ClientTid, quint32 RequestId, const QVariantMap& Data)
{
	if (Data["id"].toInt() == 0) 
	{
		QVariantMap Ret;
		Ret["retval"] = (theAPI->IsStarting(ClientPid) || CSupportDialog::ShowDialog()) ? 1 : 0;
		theAPI->SendReplyData(RequestId, Ret);
		return;
	}
	m_pPopUpWindow->AddUserPrompt(RequestId, Data, ClientPid);
}

#include "SandManRecovery.cpp"

int CSandMan::ShowQuestion(const QString& question, const QString& checkBoxText, bool* checkBoxSetting, int buttons, int defaultButton, int type)
{
	int ret =  CCheckableMessageBox::question(this, "Sandboxie-Plus", question, checkBoxText, checkBoxSetting, (QDialogButtonBox::StandardButtons)buttons, (QDialogButtonBox::StandardButton)defaultButton, (QMessageBox::Icon)type);
	QTimer::singleShot(10, [this]() {
		this->raise();
	});
	return ret;
}

void CSandMan::ShowMessage(const QString& message, int type)
{
    QMessageBox mb(this);
    mb.setWindowTitle("Sandboxie-Plus");
    mb.setIconPixmap(QMessageBox::standardIcon((QMessageBox::Icon)type));
    mb.setText(message);
    mb.exec();
	QTimer::singleShot(10, [this]() {
		this->raise();
	});
}

void CSandMan::OnNotAuthorized(bool bLoginRequired, bool& bRetry)
{
	if (!bLoginRequired)
	{
		QMessageBox::warning(this, "Sandboxie-Plus", tr("Only Administrators can change the config."));
		return;
	}

	static bool LoginOpen = false;
	if (LoginOpen)
		return;
	LoginOpen = true;
	for (;;)
	{
		QString Value = QInputDialog::getText(this, "Sandboxie-Plus", tr("Please enter the configuration password."), QLineEdit::Password);
		if (Value.isEmpty())
			break;
		SB_STATUS Status = theAPI->UnlockConfig(Value);
		if (!Status.IsError()) {
			bRetry = true;
			break;
		}
		QMessageBox::warning(this, "Sandboxie-Plus", tr("Login Failed: %1").arg(FormatError(Status)));
	}
	LoginOpen = false;
}

void CSandMan::OnBoxDblClick(QTreeWidgetItem* pItem)
{
	m_pBoxView->ShowOptions(pItem->data(0, Qt::UserRole).toString());
}

void CSandMan::OnSandBoxAction()
{
	QAction* pAction = qobject_cast<QAction*>(sender());

	if (pAction == m_pNewBox)
		GetBoxView()->AddNewBox();
	else if (pAction == m_pNewGroup)
		GetBoxView()->AddNewGroup();
	else if (pAction == m_pImportBox)
	{
		QString Value = QFileDialog::getOpenFileName(this, tr("Select file name"), "", tr("7-zip Archive (*.7z)"));
		if (Value.isEmpty())
			return;

		StrPair PathName = Split2(Value, "/", true);
		StrPair NameEx = Split2(PathName.second, ".", true);
		QString Name = NameEx.first;

		CSandBoxPtr pBox;
		for (;;) {
			pBox = theAPI->GetBoxByName(Name);
			if (pBox.isNull())
				break;
			Name = QInputDialog::getText(this, "Sandboxie-Plus", tr("This name is already in use, please select an alternative box name"), QLineEdit::Normal, Name);
			if (Name.isEmpty())
				return;
		}

		SB_PROGRESS Status = theAPI->CreateBox(Name);
		if (!Status.IsError()) {
			pBox = theAPI->GetBoxByName(Name);
			if (pBox) {
				auto pBoxEx = pBox.objectCast<CSandBoxPlus>();
				Status = pBoxEx->ImportBox(Value);
			}
		}
		if (Status.GetStatus() == OP_ASYNC) {
			Status = theGUI->AddAsyncOp(Status.GetValue(), true, tr("Importing: %1").arg(Value));
			if (Status.IsError()) {
				theGUI->DeleteBoxContent(pBox, CSandMan::eForDelete);
				pBox->RemoveBox();
			}
		}
		else
			CSandMan::CheckResults(QList<SB_STATUS>() << Status);
	}
	else if (pAction == m_pRunBoxed)
		RunSandboxed(QStringList() << "run_dialog");
}

void CSandMan::OnEmptyAll()
{
 	if (theConf->GetInt("Options/WarnTerminateAll", -1) == -1)
	{
		bool State = false;
		if(CCheckableMessageBox::question(this, "Sandboxie-Plus", tr("Do you want to terminate all processes in all sandboxes?")
			, tr("Terminate all without asking"), &State, QDialogButtonBox::Yes | QDialogButtonBox::No, QDialogButtonBox::Yes, QMessageBox::Information) != QDialogButtonBox::Yes)
			return;

		if (State)
			theConf->SetValue("Options/WarnTerminateAll", 1);
	}

	theAPI->TerminateAll();
}

void CSandMan::OnDisableForce()
{
	bool Status = m_pDisableForce->isChecked();
	int Seconds = 0;
	if (Status)
	{
		int LastValue = theAPI->GetGlobalSettings()->GetNum("ForceDisableSeconds", 60);

		bool bOK = false;
		Seconds = QInputDialog::getInt(this, "Sandboxie-Plus", tr("Please enter the duration, in seconds, for disabling Forced Programs rules."), LastValue, 0, INT_MAX, 1, &bOK);
		if (!bOK)
			return;
	}
	theAPI->DisableForceProcess(Status, Seconds);
}

void CSandMan::OnDisableForce2()
{
	bool Status = m_pDisableForce2->isChecked();
	theAPI->DisableForceProcess(Status);
}

void CSandMan::OnDisablePopUp()
{
	QString Str2 = tr("No Recovery");
	if (m_pDisableRecovery) {
		m_pDisabledRecovery->setText(m_pDisableRecovery->isChecked() ? Str2 : QString(Str2.length(), ' '));
		theConf->SetValue("UIConfig/DisabledRecovery", m_pDisableRecovery->isChecked());
	}

	QString Str3 = tr("No Messages");
	if (m_pDisableMessages) {
		m_pDisabledMessages->setText(m_pDisableMessages->isChecked() ? Str3 : QString(Str3.length(), ' '));
		theConf->SetValue("UIConfig/DisableMessages", m_pDisableMessages->isChecked());
	}
}

SB_RESULT(void*) CSandMan::ConnectSbie()
{
	SB_RESULT(void*) Status;
	if (!CSbieUtils::IsRunning(CSbieUtils::eAll)) 
	{
		if (!CSbieUtils::IsInstalled(CSbieUtils::eAll))
		{
			int PortableStart = theConf->GetInt("Options/PortableStart", -1);
			if (PortableStart == -1)
			{
				bool State = false;
				PortableStart = CCheckableMessageBox::question(this, "Sandboxie-Plus", tr("Sandboxie-Plus was started in portable mode and it needs to create necessary services. This will prompt for administrative privileges.")
					, tr("Don't show this message again."), &State, QDialogButtonBox::Ok | QDialogButtonBox::Cancel, QDialogButtonBox::Ok, QMessageBox::Information) == QDialogButtonBox::Ok ? 1 : 0;

				if (State)
					theConf->SetValue("Options/PortableStart", PortableStart);
			}

			if(!PortableStart)
				return SB_OK;
		}

		Status = CSbieUtils::Start(CSbieUtils::eAll);
	}

	if (Status.GetStatus() == OP_ASYNC) {
		m_bConnectPending = true;
		return Status;
	}
	if (Status.IsError())
		return Status;

	return ConnectSbieImpl();
}

SB_STATUS CSandMan::ConnectSbieImpl()
{
	SB_STATUS Status = theAPI->Connect(g_PendingMessage.isEmpty(), theConf->GetBool("Options/UseInteractiveQueue", true));

	if (Status.GetStatus() == 0xC0000038L /*STATUS_DEVICE_ALREADY_ATTACHED*/) {
		OnLogMessage(tr("CAUTION: Another agent (probably SbieCtrl.exe) is already managing this Sandboxie session, please close it first and reconnect to take over."));
		return SB_OK;
	}

	if (!g_PendingMessage.isEmpty()) {
		OnMessage(g_PendingMessage);
		PostQuitMessage(0);
	}

	return Status;
}

SB_STATUS CSandMan::DisconnectSbie()
{
	return theAPI->Disconnect();
}

SB_RESULT(void*) CSandMan::StopSbie(bool andRemove)
{
	SB_RESULT(void*) Status;

	if (theAPI->IsConnected()) {
		Status = theAPI->TerminateAll();
		theAPI->Disconnect();
	}
	if (!Status.IsError()) {
		if(andRemove)
			Status = CSbieUtils::Uninstall(CSbieUtils::eAll); // it stops it first of course
		else
			Status = CSbieUtils::Stop(CSbieUtils::eAll);
		if (Status.GetStatus() == OP_ASYNC)
			m_bStopPending = true;
	}

	return Status;
}

void CSandMan::OnMaintenance()
{
	SB_RESULT(void*) Status;
	if (sender() == m_pConnect)
		Status = ConnectSbie();
	else if (sender() == m_pDisconnect)
		Status = DisconnectSbie();
	else if (sender() == m_pStopAll)
		Status = StopSbie();

	// advanced
	else if (sender() == m_pInstallDrv)
		Status = CSbieUtils::Install(CSbieUtils::eDriver);
	else if (sender() == m_pStartDrv)
		Status = CSbieUtils::Start(CSbieUtils::eDriver);
	else if (sender() == m_pStopDrv)
		Status = CSbieUtils::Stop(CSbieUtils::eDriver);
	else if (sender() == m_pUninstallDrv)
		Status = CSbieUtils::Uninstall(CSbieUtils::eDriver);

	else if (sender() == m_pInstallSvc)
		Status = CSbieUtils::Install(CSbieUtils::eService);
	else if (sender() == m_pStartSvc)
		Status = CSbieUtils::Start(CSbieUtils::eService);
	else if (sender() == m_pStopSvc)
		Status = CSbieUtils::Stop(CSbieUtils::eService);
	else if (sender() == m_pUninstallSvc)
		Status = CSbieUtils::Uninstall(CSbieUtils::eService);

	else if (sender() == m_pSetupWizard) {
		CSetupWizard::ShowWizard();
		return;
	}

	//else if (sender() == m_pUpdateCore) {
	//	// todo	
	//	return;
	//}

	// uninstall	
	else if (sender() == m_pUninstallAll) {

		Status = StopSbie(true);

		CSetupWizard::ShellUninstall();
	}

	HandleMaintenance(Status);
}

void CSandMan::HandleMaintenance(SB_RESULT(void*) Status)
{
	if (Status.GetStatus() == OP_ASYNC) {

		HANDLE hProcess = Status.GetValue();
		QWinEventNotifier* processFinishedNotifier = new QWinEventNotifier(hProcess);
		processFinishedNotifier->setEnabled(true);
		connect(processFinishedNotifier, &QWinEventNotifier::activated, this, [processFinishedNotifier, this, hProcess]() {
			processFinishedNotifier->setEnabled(false);
			processFinishedNotifier->deleteLater();
			
			DWORD dwStatus = 0;
			GetExitCodeProcess(hProcess, & dwStatus);

			if (dwStatus != 0)
			{
				if(m_bStopPending)
					QMessageBox::warning(this, tr("Sandboxie-Plus - Error"), tr("Failed to stop all Sandboxie components"));
				else if(m_bConnectPending)
					QMessageBox::warning(this, tr("Sandboxie-Plus - Error"), tr("Failed to start required Sandboxie components"));

				OnLogMessage(tr("Maintenance operation failed (%1)").arg((quint32)dwStatus));
				CheckResults(QList<SB_STATUS>() << SB_ERR(dwStatus));
			}
			else
			{
				OnLogMessage(tr("Maintenance operation completed"));
				if (m_bConnectPending) {

					QTimer::singleShot(1000, [this]() {
						SB_STATUS Status = this->ConnectSbieImpl();
						CheckResults(QList<SB_STATUS>() << Status);
						if (Status.IsError())
							theAPI->LoadEventLog();
					});
				}
			}
			m_pProgressDialog->hide();
			//statusBar()->showMessage(tr("Maintenance operation completed"), 3000);
			m_bConnectPending = false;
			m_bStopPending = false;

			CloseHandle(hProcess);
		});

		//statusBar()->showMessage(tr("Executing maintenance operation, please wait..."));
		m_pProgressDialog->OnStatusMessage(tr("Executing maintenance operation, please wait..."));
		SafeShow(m_pProgressDialog);

		return;
	}

	CheckResults(QList<SB_STATUS>() << Status);
}

void CSandMan::OnViewMode(QAction* pAction)
{
	int iViewMode = pAction->data().toInt();
	theConf->SetValue("Options/ViewMode", iViewMode);
	UpdateSettings(true);
}

void CSandMan::OnAlwaysTop()
{
	StoreState();
	bool bAlwaysOnTop = m_pWndTopMost->isChecked();
	theConf->SetValue("Options/AlwaysOnTop", bAlwaysOnTop);
	this->setWindowFlag(Qt::WindowStaysOnTopHint, bAlwaysOnTop);
	LoadState();
	SafeShow(this); // why is this needed?

	m_pPopUpWindow->setWindowFlag(Qt::WindowStaysOnTopHint, bAlwaysOnTop);
	m_pProgressDialog->setWindowFlag(Qt::WindowStaysOnTopHint, bAlwaysOnTop);
}

void CSandMan::OnRefresh()
{
	if (!theAPI->IsConnected())
		return;

	theAPI->ReloadBoxes(true);

	QMap<QString, CSandBoxPtr> Boxes = theAPI->GetAllBoxes();
	foreach(const CSandBoxPtr & pBox, Boxes) {
		pBox.objectCast<CSandBoxPlus>()->UpdateSize();
		if (theConf->GetBool("Options/ScanStartMenu", true))
			pBox.objectCast<CSandBoxPlus>()->ScanStartMenu();
	}
}

void CSandMan::OnCleanUp()
{
	if (sender() == m_pCleanUpMsgLog || sender() == m_pCleanUpButton)
		if(m_pMessageLog) m_pMessageLog->GetTree()->clear();
	
	if (sender() == m_pCleanUpTrace || sender() == m_pCleanUpButton)
		if (m_pTraceView) { 
			m_pTraceView->Clear(); 
			m_pTraceInfo->clear();
		}

	if (sender() == m_pCleanUpRecovery || sender() == m_pCleanUpButton)
		if(m_pRecoveryLog) m_pRecoveryLog->GetTree()->clear();
	
	if (sender() == m_pCleanUpProcesses || sender() == m_pCleanUpButton)
		theAPI->UpdateProcesses(false, ShowAllSessions());
}

void CSandMan::OnProcView()
{
	if(m_pKeepTerminated) theConf->SetValue("Options/KeepTerminated", m_pKeepTerminated->isChecked());
	if(m_pShowAllSessions) theConf->SetValue("Options/ShowAllSessions", m_pShowAllSessions->isChecked());

	if (m_pMenuBrowse) {
		theConf->SetValue("Options/ShowFilePanel", m_pMenuBrowse->isChecked());
		m_pFileView->setVisible(m_pMenuBrowse->isChecked());

		if (m_pMenuBrowse->isChecked()) {
			QTimer::singleShot(10, [&] {
				auto Sizes = m_pPanelSplitter->sizes();
				if (Sizes.at(1) == 0) {
					Sizes[1] = 100;
					m_pPanelSplitter->setSizes(Sizes);
				}
			});
		}
	}
}

void CSandMan::OnSettings()
{
	static CSettingsWindow* pSettingsWindow = NULL;
	if (pSettingsWindow == NULL) {
		pSettingsWindow = new CSettingsWindow();
		connect(this, SIGNAL(Closed()), pSettingsWindow, SLOT(close()));
		connect(pSettingsWindow, SIGNAL(OptionsChanged(bool)), this, SLOT(UpdateSettings(bool)));
		connect(pSettingsWindow, &CSettingsWindow::Closed, [this]() {
			pSettingsWindow = NULL;
			});
		SafeShow(pSettingsWindow);
	}
	else {
		pSettingsWindow->setWindowState((pSettingsWindow->windowState() & ~Qt::WindowMinimized) | Qt::WindowActive);
		SetForegroundWindow((HWND)pSettingsWindow->winId());
	}
}

// for old menu
void CSandMan::OnSettingsAction()
{
	QAction* pAction = qobject_cast<QAction*>(sender());

	if (pAction == m_pArrangeGroups)
	{
		QMessageBox *msgBox = new QMessageBox(this);
		msgBox->setAttribute(Qt::WA_DeleteOnClose);
		msgBox->setWindowTitle("Sandboxie-Plus");
		msgBox->setText(tr("In the Plus UI, this functionality has been integrated into the main sandbox list view."));
		msgBox->setInformativeText(tr("Using the box/group context menu, you can move boxes and groups to other groups. You can also use drag and drop to move the items around. "
			"Alternatively, you can also use the arrow keys while holding ALT down to move items up and down within their group.<br />"
			"You can create new boxes and groups from the Sandbox menu."));
		QPixmap pic(":/Assets/LayoutAndGroups.png");
		msgBox->setIconPixmap(pic.scaled(pic.width() * 3/4, pic.height() * 3/4, Qt::IgnoreAspectRatio, Qt::SmoothTransformation));
		SafeExec(msgBox);
	}
	else
	{
		CSettingsWindow* pSettingsWindow = new CSettingsWindow(this);
		connect(pSettingsWindow, SIGNAL(OptionsChanged(bool)), this, SLOT(UpdateSettings(bool)));
		int Tab = pAction->data().toInt();
		pSettingsWindow->showTab(Tab, true);
	}
}

void CSandMan::UpdateSettings(bool bRebuildUI)
{
	if(m_pTrayBoxes) m_pTrayBoxes->clear(); // force refresh

	//GetBoxView()->UpdateRunMenu();

	SetupHotKeys();

	if (theConf->GetInt("Options/SysTrayIcon", 1))
		m_pTrayIcon->show();
	else
		m_pTrayIcon->hide();


	if (bRebuildUI) 
	{
		StoreState();

		RebuildUI();
	}
}

void CSandMan::RebuildUI()
{
	LoadLanguage();

	if(m_pRefreshAll) this->removeAction(m_pRefreshAll);
	if(m_pMenuBrowse) this->removeAction(m_pMenuBrowse);
	if(m_pMenuResetGUI) this->removeAction(m_pMenuResetGUI);

	m_pMainWidget->deleteLater();
	m_pMainWidget = new QWidget(this);
	setCentralWidget(m_pMainWidget);

	m_pLabel->deleteLater();

	CreateUI();

	m_pTrayMenu->deleteLater();
	CreateTrayMenu();

	LoadState(false);

	GetBoxView()->ReloadUserConfig();

	UpdateState();

	if(m_pTrayBoxes) m_pTrayBoxes->setStyle(QStyleFactory::create(m_DefaultStyle));
}

void CSandMan::OnResetMsgs()
{
	auto Ret = QMessageBox("Sandboxie-Plus", tr("Do you also want to reset hidden message boxes (yes), or only all log messages (no)?"),
		QMessageBox::Question, QMessageBox::Yes | QMessageBox::Default, QMessageBox::No, QMessageBox::Cancel | QMessageBox::Escape, this).exec();
	if (Ret == QMessageBox::Cancel)
		return;

	if (Ret == QMessageBox::Yes)
	{
		theConf->DelValue("Options/WarnDeleteV2");

		theConf->DelValue("Options/PortableStop");
		theConf->DelValue("Options/PortableStart");
		theConf->DelValue("Options/PortableRootDir");

		theConf->DelValue("Options/CheckForUpdates");

		theConf->DelValue("Options/NoEditInfo");
		theConf->DelValue("Options/NoEditWarn");

		theConf->DelValue("Options/BoxedExplorerInfo");
		theConf->DelValue("Options/ExplorerInfo");

		theConf->DelValue("Options/OpenUrlsSandboxed");

		theConf->DelValue("Options/AutoCleanupTemplates");
		theConf->DelValue("Options/WarnTerminateAll");
		theConf->DelValue("Options/WarnTerminate");

		theConf->DelValue("Options/InfoMkLink");

		theConf->DelValue("Options/WarnOpenCOM");
	}

	theAPI->GetUserSettings()->UpdateTextList("SbieCtrl_HideMessage", QStringList(), true);
	m_pPopUpWindow->ReloadHiddenMessages();
}

void CSandMan::OnResetGUI()
{
	theConf->DelValue("ErrorWindow/Window_Geometry");
	theConf->DelValue("MainWindow/Window_Geometry");
	theConf->DelValue("MainWindow/Window_State");
	theConf->DelValue("MainWindow/BoxTree_Columns");
	theConf->DelValue("MainWindow/LogList_Columns");
	theConf->DelValue("MainWindow/Log_Splitter");
	theConf->DelValue("MainWindow/Panel_Splitter");
	theConf->DelValue("MainWindow/BoxTree_Columns");
	theConf->DelValue("MainWindow/TraceLog_Columns");
	theConf->DelValue("FileBrowserWindow/Window_Geometry");
	theConf->DelValue("MainWindow/FileTree_Columns");
	theConf->DelValue("NewBoxWindow/Window_Geometry");
	theConf->DelValue("PopUpWindow/Window_Geometry");
	theConf->DelValue("RecoveryWindow/Window_Geometry");
	theConf->DelValue("RecoveryWindow/TreeView_Columns");
	theConf->DelValue("SelectBoxWindow/Window_Geometry");
	theConf->DelValue("SettingsWindow/Window_Geometry");
	theConf->DelValue("SnapshotsWindow/Window_Geometry");
	QStringList Options = theConf->ListKeys("OptionsWindow");
	foreach(const QString& Option, Options)
		theConf->DelValue("OptionsWindow/" + Option);

//	theConf->SetValue("Options/DPIScaling", 1);
	theConf->SetValue("Options/FontScaling", 100);

	RebuildUI();
}

void CSandMan::OnEditIni()
{
	QString ini = ((QAction*)sender())->property("ini").toString();

	std::wstring Editor = theConf->GetString("Options/Editor", "notepad.exe").toStdWString();
	std::wstring IniPath;

	bool bPlus;
	if (bPlus = (ini == "plus"))
	{
		IniPath = L"\"" + QString(theConf->GetConfigDir() + "/Sandboxie-Plus.ini").replace("/", "\\").toStdWString() + L"\"";
	}
	else if (ini == "tmpl")
	{
		IniPath = L"\"" + (theAPI->GetSbiePath() + "\\Templates.ini").toStdWString() + L"\"";

		if (theConf->GetBool("Options/NoEditWarn", true)) {
			bool State = false;
			CCheckableMessageBox::question(this, "Sandboxie-Plus",
				tr("You are about to edit the Templates.ini, this is generally not recommended.\n"
					"This file is part of Sandboxie and all change done to it will be reverted next time Sandboxie is updated.")
				, tr("Don't show this message again."), &State, QDialogButtonBox::Ok, QDialogButtonBox::Ok, QMessageBox::Warning);

			if (State)
				theConf->SetValue("Options/NoEditWarn", false);
		}
	}
	else //if (ini == "sbie")
	{
		IniPath = L"\"" + theAPI->GetIniPath().toStdWString() + L"\"";

		if (theConf->GetBool("Options/NoEditInfo", true)) {
			bool State = false;
			CCheckableMessageBox::question(this, "Sandboxie-Plus",
				theConf->GetBool("Options/WatchIni", true)
				? tr("The changes will be applied automatically whenever the file gets saved.")
				: tr("The changes will be applied automatically as soon as the editor is closed.")
				, tr("Don't show this message again."), &State, QDialogButtonBox::Ok, QDialogButtonBox::Ok, QMessageBox::Information);

			if (State)
				theConf->SetValue("Options/NoEditInfo", false);
		}
	}

	SHELLEXECUTEINFO si = { 0 };
	si.cbSize = sizeof(SHELLEXECUTEINFO);
	si.fMask = SEE_MASK_NOCLOSEPROCESS;
	si.hwnd = NULL;
	si.lpVerb = bPlus ? NULL : L"runas"; // plus ini does not require admin privileges
	si.lpFile = Editor.c_str();
	si.lpParameters = IniPath.c_str();
	si.lpDirectory = NULL;
	si.nShow = SW_SHOW;
	si.hInstApp = NULL;
	ShellExecuteEx(&si);
	//WaitForSingleObject(si.hProcess, INFINITE);
	//CloseHandle(si.hProcess);

	if (!bPlus && theConf->GetBool("Options/WatchIni", true))
		return; // if the ini is watched don't double reload
	
	QWinEventNotifier* processFinishedNotifier = new QWinEventNotifier(si.hProcess);
	processFinishedNotifier->setEnabled(true);
	connect(processFinishedNotifier, &QWinEventNotifier::activated, this, [processFinishedNotifier, this, si, bPlus]() {
		processFinishedNotifier->setEnabled(false);
		processFinishedNotifier->deleteLater();
		if (bPlus)
			theConf->Sync();
		else
			this->OnReloadIni();
		CloseHandle(si.hProcess);
	});
}

void CSandMan::OnReloadIni()
{
	theAPI->ReloadConfig(true);
}

void CSandMan::OnIniReloaded()
{
	OnLogSbieMessage(0, QStringList() << tr("Sandboxie config has been reloaded") << "" << "", 4);

	m_pBoxView->ReloadUserConfig();
	m_pPopUpWindow->ReloadHiddenMessages();
}

void CSandMan::OnMonitoring()
{
	if (m_pTraceView)
	{
		theAPI->EnableMonitor(m_pEnableMonitoring->isChecked());

		if(m_pEnableMonitoring->isChecked() && !m_pToolBar->isVisible())
			m_pLogTabs->show();

		//m_pTraceView->setEnabled(m_pEnableMonitoring->isChecked());
	}
	else
	{
		theAPI->EnableMonitor(true);

		static CTraceWindow* pTraceWindow = NULL;
		if (!pTraceWindow) {
			pTraceWindow = new CTraceWindow();
			connect(this, SIGNAL(Closed()), pTraceWindow, SLOT(close()));
			//pTraceWindow->setAttribute(Qt::WA_DeleteOnClose);
			connect(pTraceWindow, &CTraceWindow::Closed, [&]() {
				pTraceWindow = NULL;
			});
			SafeShow(pTraceWindow);
		}
	}
}

SB_STATUS CSandMan::AddAsyncOp(const CSbieProgressPtr& pProgress, bool bWait, const QString& InitialMsg)
{
	m_pAsyncProgress.insert(pProgress.data(), pProgress);
	connect(pProgress.data(), SIGNAL(Message(const QString&)), this, SLOT(OnAsyncMessage(const QString&)));
	connect(pProgress.data(), SIGNAL(Progress(int)), this, SLOT(OnAsyncProgress(int)));
	connect(pProgress.data(), SIGNAL(Finished()), this, SLOT(OnAsyncFinished()));

	m_pProgressDialog->OnStatusMessage(InitialMsg);
	if (bWait) {
		m_pProgressModal = true;
		m_pProgressDialog->exec(); // safe exec breaks the closing
		m_pProgressModal = false;
	}
	else
		SafeShow(m_pProgressDialog);

	if (pProgress->IsFinished()) // Note: since the operation runs asynchronously, it may have already finished, so we need to test for that
		OnAsyncFinished(pProgress.data());

	if (pProgress->IsCanceled())
		return CSbieStatus(SB_Canceled);
	return SB_OK;
}

void CSandMan::OnAsyncFinished()
{
	OnAsyncFinished(qobject_cast<CSbieProgress*>(sender()));
}

void CSandMan::OnAsyncFinished(CSbieProgress* pSender)
{
	CSbieProgressPtr pProgress = m_pAsyncProgress.take(pSender);
	if (pProgress.isNull())
		return;
	disconnect(pProgress.data() , SIGNAL(Finished()), this, SLOT(OnAsyncFinished()));

	SB_STATUS Status = pProgress->GetStatus();
	if(Status.IsError())
		CSandMan::CheckResults(QList<SB_STATUS>() << Status);

	if (m_pAsyncProgress.isEmpty()) {
		if(m_pProgressModal)
			m_pProgressDialog->close();
		else
			m_pProgressDialog->hide();
	}
}

void CSandMan::OnAsyncMessage(const QString& Text)
{
	m_pProgressDialog->OnStatusMessage(Text);
}

void CSandMan::OnAsyncProgress(int Progress)
{
	m_pProgressDialog->OnProgressMessage("", Progress);
}

void CSandMan::OnCancelAsync()
{
	foreach(const CSbieProgressPtr& pProgress, m_pAsyncProgress)
		pProgress->Cancel();
}

QString CSandMan::FormatError(const SB_STATUS& Error)
{
	//QString Text = Error.GetText();
	//if (!Text.isEmpty())
	//	return Text;

	QString Message;
	switch (Error.GetMsgCode())
	{
	case SB_Generic:		return tr("Error Status: 0x%1 (%2)").arg((quint32)Error.GetStatus(), 8, 16, QChar('0')).arg(
		(Error.GetArgs().isEmpty() || Error.GetArgs().first().toString().isEmpty()) ? tr("Unknown") : Error.GetArgs().first().toString().trimmed());
	case SB_Message:		Message = "%1"; break;
	case SB_NeedAdmin:		Message = tr("Administrator rights are required for this operation."); break;
	case SB_ExecFail:		Message = tr("Failed to execute: %1"); break;
	case SB_DriverFail:		Message = tr("Failed to connect to the driver"); break;
	case SB_ServiceFail:	Message = tr("Failed to communicate with Sandboxie Service: %1"); break;
	case SB_Incompatible:	Message = tr("An incompatible Sandboxie %1 was found. Compatible versions: %2"); break;
	case SB_PathFail:		Message = tr("Can't find Sandboxie installation path."); break;
	case SB_FailedCopyConf:	Message = tr("Failed to copy configuration from sandbox %1: %2"); break;
	case SB_AlreadyExists:  Message = tr("A sandbox of the name %1 already exists"); break;
	case SB_DeleteFailed:	Message = tr("Failed to delete sandbox %1: %2"); break;
	case SB_NameLenLimit:	Message = tr("The sandbox name can not be longer than 32 characters."); break;
	case SB_BadNameDev:		Message = tr("The sandbox name can not be a device name."); break;
	case SB_BadNameChar:	Message = tr("The sandbox name can contain only letters, digits and underscores which are displayed as spaces."); break;
	case SB_FailedKillAll:	Message = tr("Failed to terminate all processes"); break;
	case SB_DeleteProtect:	Message = tr("Delete protection is enabled for the sandbox"); break;
	case SB_DeleteNotEmpty:	Message = tr("All sandbox processes must be stopped before the box content can be deleted"); break;
	case SB_DeleteError:	Message = tr("Error deleting sandbox folder: %1"); break;
	//case SB_RemNotEmpty:	Message = tr("A sandbox must be emptied before it can be renamed."); break;
	case SB_DelNotEmpty:	Message = tr("A sandbox must be emptied before it can be deleted."); break;
	case SB_FailedMoveDir:	Message = tr("Failed to move directory '%1' to '%2'"); break;
	case SB_SnapIsRunning:	Message = tr("This Snapshot operation can not be performed while processes are still running in the box."); break;
	case SB_SnapMkDirFail:	Message = tr("Failed to create directory for new snapshot"); break;
	case SB_SnapCopyDatFail:Message = tr("Failed to copy box data files"); break;
	case SB_SnapNotFound:	Message = tr("Snapshot not found"); break;
	case SB_SnapMergeFail:	Message = tr("Error merging snapshot directories '%1' with '%2', the snapshot has not been fully merged."); break;
	case SB_SnapRmDirFail:	Message = tr("Failed to remove old snapshot directory '%1'"); break;
	case SB_SnapIsShared:	Message = tr("Can't remove a snapshot that is shared by multiple later snapshots"); break;
	case SB_SnapDelDatFail:	Message = tr("Failed to remove old box data files"); break;
	case SB_NotAuthorized:	Message = tr("You are not authorized to update configuration in section '%1'"); break;
	case SB_ConfigFailed:	Message = tr("Failed to set configuration setting %1 in section %2: %3"); break;
	case SB_SnapIsEmpty:	Message = tr("Can not create snapshot of an empty sandbox"); break;
	case SB_NameExists:		Message = tr("A sandbox with that name already exists"); break;
	case SB_PasswordBad:	Message = tr("The config password must not be longer than 64 characters"); break;
	case SB_Canceled:		Message = tr("The operation was canceled by the user"); break;

	case SBX_7zNotReady:	Message = tr("Import/Export not available, 7z.dll could not be loaded"); break;
	case SBX_7zCreateFailed: Message = tr("Failed to create the box archive"); break;
	case SBX_7zOpenFailed:	Message = tr("Failed to open the 7z archive"); break;
	case SBX_7zExtractFailed: Message = tr("Failed to unpack the box archive"); break;
	case SBX_NotBoxArchive:	Message = tr("The selected 7z file is NOT a box archive"); break;

	default:				return tr("Unknown Error Status: 0x%1").arg((quint32)Error.GetStatus(), 8, 16, QChar('0'));
	}

	foreach(const QVariant& Arg, Error.GetArgs())
		Message = Message.arg(Arg.toString()); // todo: make quint32 hex and so on

	return Message;
}

void CSandMan::CheckResults(QList<SB_STATUS> Results, bool bAsync)
{
	QStringList Errors;
	for (QList<SB_STATUS>::iterator I = Results.begin(); I != Results.end(); ++I) {
		if (I->IsError() && I->GetStatus() != OP_CANCELED)
			Errors.append(FormatError(*I));
	}

	if (bAsync) {
		foreach(const QString &Error, Errors)
			theGUI->OnLogMessage(Error, true);
	}
	else if (Errors.count() == 1)
		QMessageBox::warning(theGUI, tr("Sandboxie-Plus - Error"), Errors.first());
	else if (Errors.count() > 1) {
		CMultiErrorDialog Dialog(tr("Operation failed for %1 item(s).").arg(Errors.size()), Errors, theGUI);
		Dialog.exec();
	}
}

void CSandMan::OpenUrl(const QUrl& url)
{
	QString scheme = url.scheme();
	QString host = url.host();
	QString path = url.path();	
	QString query = url.query();

	if (scheme == "sbie") {
		if (path == "/check")
			m_pUpdater->CheckForUpdates(true);
		else if (path == "/installer")
			m_pUpdater->RunInstaller(false);
		else if (path == "/apply")
			m_pUpdater->ApplyUpdate(false);
		else if (path == "/cert")
			m_pUpdater->UpdateCert();
		else
			OpenUrl("https://sandboxie-plus.com/sandboxie" + path);
		return;
	}

	int iSandboxed = theConf->GetInt("Options/OpenUrlsSandboxed", 2);

	if (iSandboxed == 2)
	{
		bool bCheck = false;
		QString Message = tr("Do you want to open %1 in a sandboxed (yes) or unsandboxed (no) Web browser?").arg(url.toString());
		QDialogButtonBox::StandardButton Ret = CCheckableMessageBox::question(this, "Sandboxie-Plus", Message , tr("Remember choice for later."), 
			&bCheck, QDialogButtonBox::Yes | QDialogButtonBox::No | QDialogButtonBox::Cancel, QDialogButtonBox::Yes, QMessageBox::Question);
		if (Ret == QDialogButtonBox::Cancel) return;
		iSandboxed = Ret == QDialogButtonBox::Yes ? 1 : 0;
		if(bCheck) theConf->SetValue("Options/OpenUrlsSandboxed", iSandboxed);
	}

	if (iSandboxed) RunSandboxed(QStringList(url.toString()));
	else ShellExecute(MainWndHandle, NULL, url.toString().toStdWString().c_str(), NULL, NULL, SW_SHOWNORMAL);
}

QString CSandMan::GetVersion()
{
	QString Version = QString::number(VERSION_MJR) + "." + QString::number(VERSION_MIN) //.rightJustified(2, '0')
//#if VERSION_REV > 0 || VERSION_MJR == 0
		+ "." + QString::number(VERSION_REV)
//#endif
#if VERSION_UPD > 0
		+ QChar('a' + VERSION_UPD - 1)
#endif
		;
	return Version;
}

void CSandMan::SetUITheme()
{
	m_ThemeUpdatePending = false;


	bool bDark;
	int iDark = theConf->GetInt("Options/UseDarkTheme", 2);
	if (iDark == 2) {
		QSettings settings("HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\CurrentVersion\\Themes\\Personalize", QSettings::NativeFormat);
		bDark = (settings.value("AppsUseLightTheme") == 0);
	} else
		bDark = (iDark == 1);
	
	if (bDark)
		QApplication::setPalette(m_DarkPalett);
	else
		QApplication::setPalette(m_DefaultPalett);
	m_DarkTheme = bDark;


	bool bFusion;
	int iFusion = theConf->GetInt("Options/UseFusionTheme", 2);
	if (iFusion == 2)
		bFusion = bDark;
	else 
		bFusion = (iFusion == 1);

	if (bFusion)
		QApplication::setStyle(QStyleFactory::create("Fusion"));
	else {
		int iViewMode = theConf->GetInt("Options/ViewMode", 1);
		QApplication::setStyle(QStyleFactory::create((bDark || iViewMode == 2) ? "Windows" : m_DefaultStyle));
	}
	m_FusionTheme = bFusion;


	CTreeItemModel::SetDarkMode(bDark);
	//CListItemModel::SetDarkMode(bDark); // not used
	CPopUpWindow::SetDarkMode(bDark);
	CPanelView::SetDarkMode(bDark);
	CFinder::SetDarkMode(bDark);


	QFont font = QApplication::font();
	double newFontSize = m_DefaultFontSize * theConf->GetInt("Options/FontScaling", 100) / 100.0;
	if (newFontSize != font.pointSizeF()) {
		font.setPointSizeF(newFontSize);
		QApplication::setFont(font);
	}

#if defined(Q_OS_WIN)
	foreach(QWidget * pWidget, QApplication::topLevelWidgets())
	{
		if (pWidget->isVisible())
			SetTitleTheme((HWND)pWidget->winId());
	}
#endif
}

void CSandMan::SetTitleTheme(const HWND& hwnd)
{
	static const int CurrentVersion = QSettings("HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion",
												QSettings::NativeFormat).value("CurrentBuild").toInt();
	if (CurrentVersion < 17763) // Windows 10 1809 -
		return;
	
	HMODULE dwmapi = GetModuleHandle(L"dwmapi.dll");
	if (dwmapi)
	{
		typedef HRESULT(WINAPI* P_DwmSetWindowAttribute)(HWND, DWORD, LPCVOID, DWORD);
		P_DwmSetWindowAttribute pDwmSetWindowAttribute = reinterpret_cast<P_DwmSetWindowAttribute>(GetProcAddress(dwmapi, "DwmSetWindowAttribute"));
		if (pDwmSetWindowAttribute)
		{
#ifndef DWMWA_USE_IMMERSIVE_DARK_MODE_BEFORE_20H1
#define DWMWA_USE_IMMERSIVE_DARK_MODE_BEFORE_20H1 19
#endif
#ifndef DWMWA_USE_IMMERSIVE_DARK_MODE
#define DWMWA_USE_IMMERSIVE_DARK_MODE 20
#endif
			BOOL bDark = m_DarkTheme;
			pDwmSetWindowAttribute(hwnd,
				CurrentVersion >= 18985 ? DWMWA_USE_IMMERSIVE_DARK_MODE : DWMWA_USE_IMMERSIVE_DARK_MODE_BEFORE_20H1,
				&bDark,
				sizeof(bDark));
		}
	}
}

void CSandMan::UpdateTheme()
{
	if (!m_ThemeUpdatePending)
	{
		m_ThemeUpdatePending = true;
		QTimer::singleShot(500, this, SLOT(SetUITheme()));
	}
}

void CSandMan::UpdateTitleTheme(const HWND& hwnd)
{
	SetTitleTheme(hwnd);
}

void CSandMan::LoadLanguage()
{
	QString Lang = theConf->GetString("Options/UiLanguage");
	if(Lang.isEmpty())
		Lang = QLocale::system().name();

	if (Lang.compare("native", Qt::CaseInsensitive) == 0)
		Lang.clear();

	m_LanguageId = LocaleNameToLCID(Lang.toStdWString().c_str(), 0);
	if (!m_LanguageId) 
		m_LanguageId = 1033; // default to English

	LoadLanguage(Lang, "sandman", 0);
	LoadLanguage(Lang, "qt", 1);

	QTreeViewEx::m_ResetColumns = tr("Reset Columns");
	CPanelView::m_CopyCell = tr("Copy Cell");
	CPanelView::m_CopyRow = tr("Copy Row");
	CPanelView::m_CopyPanel = tr("Copy Panel");
}

void CSandMan::LoadLanguage(const QString& Lang, const QString& Module, int Index)
{
	qApp->removeTranslator(&m_Translator[Index]);

	if (Lang.isEmpty())
		return;
	
	QString LangAux = Lang; // Short version as fallback
	LangAux.truncate(LangAux.lastIndexOf('_'));

	C7zFileEngineHandler LangFS(QApplication::applicationDirPath() + "/translations.7z", "lang", this);

	QString LangDir;
	if (LangFS.IsOpen())
		LangDir = LangFS.Prefix() + "/";
	else
		LangDir = QApplication::applicationDirPath() + "/translations/";

	QString LangPath = LangDir + Module + "_";
	bool bAux = false;
	if (QFile::exists(LangPath + Lang + ".qm") || (bAux = QFile::exists(LangPath + LangAux + ".qm")))
	{
		if(m_Translator[Index].load(LangPath + (bAux ? LangAux : Lang) + ".qm", LangDir))
			qApp->installTranslator(&m_Translator[Index]);
	}
}

void CSandMan::OnHelp()
{
	//if (sender() == m_pSupport)
	//	QDesktopServices::openUrl(QUrl("https://sandboxie-plus.com/go.php?to=donate"));
	//else 
	if (sender() == m_pContribution)
		QDesktopServices::openUrl(QUrl("https://sandboxie-plus.com/go.php?to=sbie-contribute"));
	else if (sender() == m_pManual)
		QDesktopServices::openUrl(QUrl("https://sandboxie-plus.com/go.php?to=sbie-docs"));
	else if (sender() == m_pForum)
		QDesktopServices::openUrl(QUrl("https://sandboxie-plus.com/go.php?to=sbie-forum"));
	else
		QDesktopServices::openUrl(QUrl("https://sandboxie-plus.com/go.php?to=patreon"));
}

void CSandMan::OnAbout()
{
	if (sender() == m_pAbout)
	{
		QString AboutCaption = tr(
			"<h3>About Sandboxie-Plus</h3>"
			"<p>Version %1</p>"
			"<p>Copyright (c) 2020-2023 by DavidXanatos</p>"
		).arg(theGUI->GetVersion());

		QString CertInfo;
		if (!g_Certificate.isEmpty()) {
			CertInfo = tr("This copy of Sandboxie+ is certified for: %1").arg(GetArguments(g_Certificate, L'\n', L':').value("NAME"));
		} else {
			CertInfo = tr("Sandboxie+ is free for personal and non-commercial use.");
		}

		QString AboutText = tr(
			"Sandboxie-Plus is an open source continuation of Sandboxie.<br />"
			"Visit <a href=\"https://sandboxie-plus.com\">sandboxie-plus.com</a> for more information.<br />"
			"<br />"
			"%3<br />"
			"<br />"
			"Driver version: %1<br />"
			"Features: %2<br />"
			"<br />"
			"Icons from <a href=\"https://icons8.com\">icons8.com</a>"
		).arg(theAPI->GetVersion()).arg(theAPI->GetFeatureStr()).arg(CertInfo);

		QMessageBox *msgBox = new QMessageBox(this);
		msgBox->setAttribute(Qt::WA_DeleteOnClose);
		msgBox->setWindowTitle(tr("About Sandboxie-Plus"));
		msgBox->setText(AboutCaption);
		msgBox->setInformativeText(AboutText);

		QIcon ico(QLatin1String(":/SandMan.png"));
		msgBox->setIconPixmap(ico.pixmap(128, 128));

		SafeExec(msgBox);
	}
	else if (sender() == m_pAboutQt)
		QMessageBox::aboutQt(this);
}

// Make sure that QPlatformTheme strings won't be marked as vanished in all .ts files, even after running lupdate

static const char* platform_strings[] = {
QT_TRANSLATE_NOOP("QPlatformTheme", "OK"),
QT_TRANSLATE_NOOP("QPlatformTheme", "Apply"),
QT_TRANSLATE_NOOP("QPlatformTheme", "Cancel"),
QT_TRANSLATE_NOOP("QPlatformTheme", "&Yes"),
QT_TRANSLATE_NOOP("QPlatformTheme", "&No"),
};

// Make sure that CSandBox strings won't be marked as vanished in all .ts files, even after running lupdate

static const char* CSandBox_strings[] = {
QT_TRANSLATE_NOOP("CSandBox", "Waiting for folder: %1"),
QT_TRANSLATE_NOOP("CSandBox", "Deleting folder: %1"),
QT_TRANSLATE_NOOP("CSandBox", "Merging folders: %1 &gt;&gt; %2"),
QT_TRANSLATE_NOOP("CSandBox", "Finishing Snapshot Merge..."),
};


#include "SbieFindWnd.cpp"
