/* $Id: UIFileManager.cpp $ */
/** @file
 * VBox Qt GUI - UIFileManager class implementation.
 */

/*
 * Copyright (C) 2016-2023 Oracle and/or its affiliates.
 *
 * This file is part of VirtualBox base platform packages, as
 * available from https://www.virtualbox.org.
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation, in version 3 of the
 * License.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, see <https://www.gnu.org/licenses>.
 *
 * SPDX-License-Identifier: GPL-3.0-only
 */

/* Qt includes: */
#include <QHBoxLayout>
#include <QPushButton>
#include <QSplitter>

/* GUI includes: */
#include "QITabWidget.h"
#include "QITreeWidget.h"
#include "QIToolBar.h"
#include "UIActionPool.h"
#include "UICommon.h"
#include "UIConverter.h"
#include "UIErrorString.h"
#include "UIExtraDataManager.h"
#include "UIIconPool.h"
#include "UIFileManager.h"
#include "UIFileManagerOptionsPanel.h"
#include "UIFileManagerLogPanel.h"
#include "UIFileManagerOperationsPanel.h"
#include "UIFileManagerGuestTable.h"
#include "UIFileManagerHostTable.h"
#include "UIGuestControlInterface.h"
#include "UIVirtualMachineItem.h"

/* COM includes: */
#include "CConsole.h"
#include "CFsObjInfo.h"
#include "CGuestDirectory.h"
#include "CGuestFsObjInfo.h"
#include "CGuestSession.h"


/*********************************************************************************************************************************
*   UIFileOperationsList definition.                                                                                   *
*********************************************************************************************************************************/

class UIFileOperationsList : public QITreeWidget
{
    Q_OBJECT;
public:

    UIFileOperationsList(QWidget *pParent = 0);
};


/*********************************************************************************************************************************
*   UIFileManagerOptions implementation.                                                                             *
*********************************************************************************************************************************/

UIFileManagerOptions *UIFileManagerOptions::m_pInstance = 0;

UIFileManagerOptions* UIFileManagerOptions::instance()
{
    if (!m_pInstance)
    m_pInstance = new UIFileManagerOptions;
    return m_pInstance;
}

void UIFileManagerOptions::create()
{
    if (m_pInstance)
        return;
    m_pInstance = new UIFileManagerOptions;
}

void UIFileManagerOptions::destroy()
{
    delete m_pInstance;
    m_pInstance = 0;
}

 UIFileManagerOptions::~UIFileManagerOptions()
{
}

UIFileManagerOptions::UIFileManagerOptions()
    : fListDirectoriesOnTop(true)
    , fAskDeleteConfirmation(false)
    , fShowHumanReadableSizes(true)
    , fShowHiddenObjects(true)
{
}

/*********************************************************************************************************************************
*   UIFileOperationsList implementation.                                                                                   *
*********************************************************************************************************************************/

UIFileOperationsList::UIFileOperationsList(QWidget *pParent)
    :QITreeWidget(pParent)
{}


/*********************************************************************************************************************************
*   UIFileManager implementation.                                                                                    *
*********************************************************************************************************************************/

UIFileManager::UIFileManager(EmbedTo enmEmbedding, UIActionPool *pActionPool,
                             const CMachine &comMachine, QWidget *pParent, bool fShowToolbar)
    : QIWithRetranslateUI<QWidget>(pParent)
    , m_pMainLayout(0)
    , m_pVerticalSplitter(0)
    , m_pFileTableSplitter(0)
    , m_pToolBar(0)
    , m_pVerticalToolBar(0)
    , m_pHostFileTable(0)
    , m_pGuestTablesContainer(0)
    , m_enmEmbedding(enmEmbedding)
    , m_pActionPool(pActionPool)
    , m_fShowToolbar(fShowToolbar)
    , m_pOptionsPanel(0)
    , m_pLogPanel(0)
    , m_pOperationsPanel(0)
    , m_fCommitDataSignalReceived(false)
{
    loadOptions();
    prepareObjects();
    prepareConnections();
    retranslateUi();
    restorePanelVisibility();
    UIFileManagerOptions::create();
    uiCommon().setHelpKeyword(this, "guestadd-gc-file-manager");

    if (!comMachine.isNull())
        setMachines( QVector<QUuid>() << comMachine.GetId());
}

UIFileManager::~UIFileManager()
{
    UIFileManagerOptions::destroy();
    if (m_pGuestTablesContainer)
    {
        for (int i = 0; i < m_pGuestTablesContainer->count(); ++i)
        {
            UIFileManagerGuestTable *pTable = qobject_cast<UIFileManagerGuestTable*>(m_pGuestTablesContainer->widget(i));
            if (pTable)
                pTable->disconnect();
        }
    }
}

QMenu *UIFileManager::menu() const
{
    if (!m_pActionPool)
        return 0;
    return m_pActionPool->action(UIActionIndex_M_FileManager)->menu();
}

void UIFileManager::retranslateUi()
{
}

void UIFileManager::prepareObjects()
{
    /* m_pMainLayout is the outer most layout containing the main toolbar and splitter widget: */
    m_pMainLayout = new QVBoxLayout(this);
    if (!m_pMainLayout)
        return;

    /* Configure layout: */
    m_pMainLayout->setContentsMargins(0, 0, 0, 0);
#ifdef VBOX_WS_MAC
    m_pMainLayout->setSpacing(10);
#else
    m_pMainLayout->setSpacing(qApp->style()->pixelMetric(QStyle::PM_LayoutVerticalSpacing) / 2);
#endif

    if (m_fShowToolbar)
        prepareToolBar();

    QWidget *pTopWidget = new QWidget;
    QVBoxLayout *pTopLayout = new QVBoxLayout;
    pTopLayout->setSpacing(0);
    pTopLayout->setContentsMargins(0, 0, 0, 0);
    pTopWidget->setLayout(pTopLayout);

    m_pFileTableSplitter = new QSplitter;

    if (m_pFileTableSplitter)
    {
        m_pFileTableSplitter->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Expanding);
        m_pFileTableSplitter->setContentsMargins(0, 0, 0, 0);

        /* This widget hosts host file table and vertical toolbar. */
        QWidget *pHostTableAndVerticalToolbarWidget = new QWidget;
        QHBoxLayout *pHostTableAndVerticalToolbarLayout = new QHBoxLayout(pHostTableAndVerticalToolbarWidget);
        pHostTableAndVerticalToolbarLayout->setSpacing(0);
        pHostTableAndVerticalToolbarLayout->setContentsMargins(0, 0, 0, 0);

        m_pHostFileTable = new UIFileManagerHostTable(m_pActionPool);
        if (m_pHostFileTable)
            pHostTableAndVerticalToolbarLayout->addWidget(m_pHostFileTable);

        m_pFileTableSplitter->addWidget(pHostTableAndVerticalToolbarWidget);
        prepareVerticalToolBar(pHostTableAndVerticalToolbarLayout);

        m_pGuestTablesContainer = new QITabWidget;
        if (m_pGuestTablesContainer)
        {
            m_pGuestTablesContainer->setTabPosition(QTabWidget::East);
            m_pGuestTablesContainer->setTabBarAutoHide(true);
            m_pFileTableSplitter->addWidget(m_pGuestTablesContainer);
        }
        m_pFileTableSplitter->setStretchFactor(0, 1);
        m_pFileTableSplitter->setStretchFactor(1, 1);
    }

    pTopLayout->addWidget(m_pFileTableSplitter);
    for (int i = 0; i < m_pFileTableSplitter->count(); ++i)
        m_pFileTableSplitter->setCollapsible(i, false);

    /* Create options and session panels and insert them into pTopLayout: */
    prepareOptionsAndSessionPanels(pTopLayout);

    /** Vertical splitter has 3 widgets. Log panel as bottom most one, operations panel on top of it,
     * and pTopWidget which contains everthing else: */
    m_pVerticalSplitter = new QSplitter;
    if (m_pVerticalSplitter)
    {
        m_pMainLayout->addWidget(m_pVerticalSplitter);
        m_pVerticalSplitter->setOrientation(Qt::Vertical);
        m_pVerticalSplitter->setHandleWidth(4);

        m_pVerticalSplitter->addWidget(pTopWidget);
        /* Prepare operations and log panels and insert them into splitter: */
        prepareOperationsAndLogPanels(m_pVerticalSplitter);

        for (int i = 0; i < m_pVerticalSplitter->count(); ++i)
            m_pVerticalSplitter->setCollapsible(i, false);
        m_pVerticalSplitter->setStretchFactor(0, 3);
        m_pVerticalSplitter->setStretchFactor(1, 1);
        m_pVerticalSplitter->setStretchFactor(2, 1);
    }
}

void UIFileManager::prepareVerticalToolBar(QHBoxLayout *layout)
{
    m_pVerticalToolBar = new QIToolBar;
    if (!m_pVerticalToolBar && !m_pActionPool)
        return;

    m_pVerticalToolBar->setOrientation(Qt::Vertical);

    /* Add to dummy QWidget to toolbar to center the action icons vertically: */
    QWidget *topSpacerWidget = new QWidget(this);
    topSpacerWidget->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Expanding);
    topSpacerWidget->setVisible(true);
    QWidget *bottomSpacerWidget = new QWidget(this);
    bottomSpacerWidget->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Expanding);
    bottomSpacerWidget->setVisible(true);

    m_pVerticalToolBar->addWidget(topSpacerWidget);
    if (m_pActionPool->action(UIActionIndex_M_FileManager_S_CopyToHost))
    {
        m_pVerticalToolBar->addAction(m_pActionPool->action(UIActionIndex_M_FileManager_S_CopyToHost));
        m_pActionPool->action(UIActionIndex_M_FileManager_S_CopyToHost)->setEnabled(false);
    }
    if (m_pActionPool->action(UIActionIndex_M_FileManager_S_CopyToGuest))
    {
        m_pVerticalToolBar->addAction(m_pActionPool->action(UIActionIndex_M_FileManager_S_CopyToGuest));
        m_pActionPool->action(UIActionIndex_M_FileManager_S_CopyToGuest)->setEnabled(false);
    }

    m_pVerticalToolBar->addWidget(bottomSpacerWidget);

    layout ->addWidget(m_pVerticalToolBar);
}

void UIFileManager::prepareConnections()
{
    if (m_pActionPool)
    {
        if (m_pActionPool->action(UIActionIndex_M_FileManager_T_Options))
            connect(m_pActionPool->action(UIActionIndex_M_FileManager_T_Options), &QAction::toggled,
                    this, &UIFileManager::sltPanelActionToggled);
        if (m_pActionPool->action(UIActionIndex_M_FileManager_T_Log))
            connect(m_pActionPool->action(UIActionIndex_M_FileManager_T_Log), &QAction::toggled,
                    this, &UIFileManager::sltPanelActionToggled);
        if (m_pActionPool->action(UIActionIndex_M_FileManager_T_Operations))
            connect(m_pActionPool->action(UIActionIndex_M_FileManager_T_Operations), &QAction::toggled,
                    this, &UIFileManager::sltPanelActionToggled);
        if (m_pActionPool->action(UIActionIndex_M_FileManager_S_CopyToHost))
            connect(m_pActionPool->action(UIActionIndex_M_FileManager_S_CopyToHost), &QAction::triggered,
                    this, &UIFileManager::sltCopyGuestToHost);
        if (m_pActionPool->action(UIActionIndex_M_FileManager_S_CopyToGuest))
            connect(m_pActionPool->action(UIActionIndex_M_FileManager_S_CopyToGuest), &QAction::triggered,
                    this, &UIFileManager::sltCopyHostToGuest);
    }
    if (m_pOptionsPanel)
    {
        connect(m_pOptionsPanel, &UIFileManagerOptionsPanel::sigHidePanel,
                this, &UIFileManager::sltHandleHidePanel);
        connect(m_pOptionsPanel, &UIFileManagerOptionsPanel::sigShowPanel,
                this, &UIFileManager::sltHandleShowPanel);
        connect(m_pOptionsPanel, &UIFileManagerOptionsPanel::sigOptionsChanged,
                this, &UIFileManager::sltHandleOptionsUpdated);
    }
    if (m_pLogPanel)
    {
        connect(m_pLogPanel, &UIFileManagerLogPanel::sigHidePanel,
                this, &UIFileManager::sltHandleHidePanel);
        connect(m_pLogPanel, &UIFileManagerLogPanel::sigShowPanel,
                this, &UIFileManager::sltHandleShowPanel);
    }

    if (m_pOperationsPanel)
    {
        connect(m_pOperationsPanel, &UIFileManagerOperationsPanel::sigHidePanel,
                this, &UIFileManager::sltHandleHidePanel);
        connect(m_pOperationsPanel, &UIFileManagerOperationsPanel::sigShowPanel,
                this, &UIFileManager::sltHandleShowPanel);
    }
    if (m_pHostFileTable)
    {
        connect(m_pHostFileTable, &UIFileManagerHostTable::sigLogOutput,
                this, &UIFileManager::sltReceieveLogOutput);
        connect(m_pHostFileTable, &UIFileManagerHostTable::sigDeleteConfirmationOptionChanged,
                this, &UIFileManager::sltHandleOptionsUpdated);
        connect(m_pHostFileTable, &UIFileManagerGuestTable::sigSelectionChanged,
                this, &UIFileManager::sltFileTableSelectionChanged);
    }
    if (m_pGuestTablesContainer)
        connect(m_pGuestTablesContainer, &QITabWidget::currentChanged, this,
                &UIFileManager::sltCurrentTabChanged);

    connect(&uiCommon(), &UICommon::sigAskToCommitData,
            this, &UIFileManager::sltCommitDataSignalReceived);
}

void UIFileManager::prepareToolBar()
{
    /* Create toolbar: */
    m_pToolBar = new QIToolBar(parentWidget());
    if (m_pToolBar)
    {
        /* Configure toolbar: */
        const int iIconMetric = (int)(QApplication::style()->pixelMetric(QStyle::PM_LargeIconSize));
        m_pToolBar->setIconSize(QSize(iIconMetric, iIconMetric));
        m_pToolBar->setToolButtonStyle(Qt::ToolButtonTextUnderIcon);

        m_pToolBar->addAction(m_pActionPool->action(UIActionIndex_M_FileManager_T_Options));
        m_pToolBar->addAction(m_pActionPool->action(UIActionIndex_M_FileManager_T_Operations));
        m_pToolBar->addAction(m_pActionPool->action(UIActionIndex_M_FileManager_T_Log));

#ifdef VBOX_WS_MAC
        /* Check whether we are embedded into a stack: */
        if (m_enmEmbedding == EmbedTo_Stack)
        {
            /* Add into layout: */
            m_pMainLayout->addWidget(m_pToolBar);
        }
#else
        /* Add into layout: */
        m_pMainLayout->addWidget(m_pToolBar);
#endif
    }
}

void UIFileManager::sltReceieveLogOutput(QString strOutput, const QString &strMachineName, FileManagerLogType eLogType)
{
    appendLog(strOutput, strMachineName, eLogType);
}

void UIFileManager::sltCopyGuestToHost()
{
    copyToHost();
}

void UIFileManager::sltCopyHostToGuest()
{
    copyToGuest();
}

void UIFileManager::sltPanelActionToggled(bool fChecked)
{
    QAction *pSenderAction = qobject_cast<QAction*>(sender());
    if (!pSenderAction)
        return;
    UIDialogPanel* pPanel = 0;
    /* Look for the sender() within the m_panelActionMap's values: */
    for (QMap<UIDialogPanel*, QAction*>::const_iterator iterator = m_panelActionMap.begin();
        iterator != m_panelActionMap.end(); ++iterator)
    {
        if (iterator.value() == pSenderAction)
            pPanel = iterator.key();
    }
    if (!pPanel)
        return;
    if (fChecked)
        showPanel(pPanel);
    else
        hidePanel(pPanel);
}

void UIFileManager::sltReceieveNewFileOperation(const CProgress &comProgress, const QString &strTableName)
{
    if (m_pOperationsPanel)
        m_pOperationsPanel->addNewProgress(comProgress, strTableName);
}

void UIFileManager::sltFileOperationComplete(QUuid progressId)
{
    Q_UNUSED(progressId);
    if (m_pHostFileTable)
        m_pHostFileTable->refresh();
    /// @todo we need to refresh only the table from which the completed file operation has originated
    for (int i = 0; i < m_pGuestTablesContainer->count(); ++i)
    {
        UIFileManagerGuestTable *pTable = qobject_cast<UIFileManagerGuestTable*>(m_pGuestTablesContainer->widget(i));
        if (pTable)
            pTable->refresh();
    }
}

void UIFileManager::sltHandleOptionsUpdated()
{
    if (m_pOptionsPanel)
        m_pOptionsPanel->update();

    for (int i = 0; i < m_pGuestTablesContainer->count(); ++i)
    {
        UIFileManagerGuestTable *pTable = qobject_cast<UIFileManagerGuestTable*>(m_pGuestTablesContainer->widget(i));
        if (pTable)
            pTable->optionsUpdated();
    }
    if (m_pHostFileTable)
        m_pHostFileTable->optionsUpdated();
    saveOptions();
}

void UIFileManager::sltHandleHidePanel(UIDialogPanel *pPanel)
{
    hidePanel(pPanel);
}

void UIFileManager::sltHandleShowPanel(UIDialogPanel *pPanel)
{
    showPanel(pPanel);
}

void UIFileManager::sltCommitDataSignalReceived()
{
    m_fCommitDataSignalReceived = true;
}

void UIFileManager::sltFileTableSelectionChanged(bool fHasSelection)
{
    /* If we dont have a guest session running that actions should stay disabled: */
    if (!currentGuestTable() || !currentGuestTable()->isGuestSessionRunning())
    {
        m_pActionPool->action(UIActionIndex_M_FileManager_S_CopyToGuest)->setEnabled(false);
        m_pActionPool->action(UIActionIndex_M_FileManager_S_CopyToHost)->setEnabled(false);
        return;
    }

    /* Enable/disable vertical toolbar actions: */
    UIFileManagerGuestTable *pGuestTable = qobject_cast<UIFileManagerGuestTable*>(sender());

    /* If the signal is coming from a guest table which is not the current one just dont do anything: */
    if (pGuestTable && pGuestTable != currentGuestTable())
        return;

    if (pGuestTable)
    {
        if (m_pActionPool->action(UIActionIndex_M_FileManager_S_CopyToHost))
            m_pActionPool->action(UIActionIndex_M_FileManager_S_CopyToHost)->setEnabled(fHasSelection);
        return;
    }

    if (sender() == m_pHostFileTable && m_pActionPool->action(UIActionIndex_M_FileManager_S_CopyToGuest))
        m_pActionPool->action(UIActionIndex_M_FileManager_S_CopyToGuest)->setEnabled(fHasSelection);
}

void UIFileManager::sltCurrentTabChanged(int iIndex)
{
    Q_UNUSED(iIndex);
    setVerticalToolBarActionsEnabled();

    /* Mark the current guest table: */
    UIFileManagerGuestTable *pCurrentGuestTable = currentGuestTable();
    if (!pCurrentGuestTable)
        return;
    for (int i = 0; i < m_pGuestTablesContainer->count(); ++i)
    {
        UIFileManagerGuestTable *pTable = qobject_cast<UIFileManagerGuestTable*>(m_pGuestTablesContainer->widget(i));
        if (!pTable)
            continue;
        pTable->setIsCurrent(pTable == pCurrentGuestTable);
    }
    /* Disable host file table if guest session is not running: */
    if (m_pHostFileTable)
        m_pHostFileTable->setEnabled(pCurrentGuestTable->isGuestSessionRunning());
    /* Disable/enable file table submenus of the menu: */
    UIMenu *pGuestSubmenu = m_pActionPool->action(UIActionIndex_M_FileManager_M_GuestSubmenu)->menu();
    if (pGuestSubmenu)
        pGuestSubmenu->setEnabled(pCurrentGuestTable->isGuestSessionRunning());
    UIMenu *pHostSubmenu = m_pActionPool->action(UIActionIndex_M_FileManager_M_HostSubmenu)->menu();
    if (pHostSubmenu)
        pHostSubmenu->setEnabled(pCurrentGuestTable->isGuestSessionRunning());
}

void UIFileManager::sltGuestFileTableStateChanged(bool fIsRunning)
{
    if (m_pHostFileTable)
        m_pHostFileTable->setEnabled(fIsRunning);
}

void UIFileManager::setVerticalToolBarActionsEnabled()
{
    if (!m_pGuestTablesContainer)
        return;
    UIFileManagerGuestTable *pTable = currentGuestTable();
    if (!pTable)
        return;

    bool fRunning = pTable->isGuestSessionRunning();
    if (m_pActionPool->action(UIActionIndex_M_FileManager_S_CopyToHost))
        m_pActionPool->action(UIActionIndex_M_FileManager_S_CopyToHost)->setEnabled(fRunning && pTable->hasSelection());

    if (m_pActionPool->action(UIActionIndex_M_FileManager_S_CopyToGuest))
    {
        bool fHostHasSelection = m_pHostFileTable ? m_pHostFileTable->hasSelection() : false;
        m_pActionPool->action(UIActionIndex_M_FileManager_S_CopyToGuest)->setEnabled(fRunning && fHostHasSelection);
    }
}

void UIFileManager::copyToHost()
{
    if (m_pGuestTablesContainer && m_pHostFileTable)
    {
        UIFileManagerGuestTable *pGuestFileTable = currentGuestTable();
        if (pGuestFileTable)
            pGuestFileTable->copyGuestToHost(m_pHostFileTable->currentDirectoryPath());
    }
}

void UIFileManager::copyToGuest()
{
    if (m_pGuestTablesContainer && m_pHostFileTable)
    {
        UIFileManagerGuestTable *pGuestFileTable = currentGuestTable();
        if (pGuestFileTable)
            pGuestFileTable->copyHostToGuest(m_pHostFileTable->selectedItemPathList());
    }
}

void UIFileManager::prepareOptionsAndSessionPanels(QVBoxLayout *pLayout)
{
    if (!pLayout)
        return;

    m_pOptionsPanel = new UIFileManagerOptionsPanel(0 /*parent */, UIFileManagerOptions::instance());
    if (m_pOptionsPanel)
    {
        m_pOptionsPanel->hide();
        m_panelActionMap.insert(m_pOptionsPanel, m_pActionPool->action(UIActionIndex_M_FileManager_T_Options));
        pLayout->addWidget(m_pOptionsPanel);
    }
}

void UIFileManager::prepareOperationsAndLogPanels(QSplitter *pSplitter)
{
    if (!pSplitter)
        return;
    m_pOperationsPanel = new UIFileManagerOperationsPanel;
    if (m_pOperationsPanel)
    {
        m_pOperationsPanel->hide();
        connect(m_pOperationsPanel, &UIFileManagerOperationsPanel::sigFileOperationComplete,
                this, &UIFileManager::sltFileOperationComplete);
        connect(m_pOperationsPanel, &UIFileManagerOperationsPanel::sigFileOperationFail,
                this, &UIFileManager::sltReceieveLogOutput);
        m_panelActionMap.insert(m_pOperationsPanel, m_pActionPool->action(UIActionIndex_M_FileManager_T_Operations));
    }
    pSplitter->addWidget(m_pOperationsPanel);
    m_pLogPanel = new UIFileManagerLogPanel;
    if (m_pLogPanel)
    {
        m_pLogPanel->hide();
        m_panelActionMap.insert(m_pLogPanel, m_pActionPool->action(UIActionIndex_M_FileManager_T_Log));
    }
    pSplitter->addWidget(m_pLogPanel);
}


template<typename T>
QStringList UIFileManager::getFsObjInfoStringList(const T &fsObjectInfo) const
{
    QStringList objectInfo;
    if (!fsObjectInfo.isOk())
        return objectInfo;
    objectInfo << fsObjectInfo.GetName();
    return objectInfo;
}

void UIFileManager::saveOptions()
{
    if (m_fCommitDataSignalReceived)
        return;
    /* Save the options: */
    UIFileManagerOptions *pOptions = UIFileManagerOptions::instance();
    if (pOptions)
    {
        gEDataManager->setFileManagerOptions(pOptions->fListDirectoriesOnTop,
                                             pOptions->fAskDeleteConfirmation,
                                             pOptions->fShowHumanReadableSizes,
                                             pOptions->fShowHiddenObjects);
    }
}

void UIFileManager::restorePanelVisibility()
{
    /** Make sure the actions are set to not-checked. this prevents an unlikely
     *  bug when the extrakey for the visible panels are manually modified: */
    foreach(QAction* pAction, m_panelActionMap.values())
    {
        pAction->blockSignals(true);
        pAction->setChecked(false);
        pAction->blockSignals(false);
    }

    /* Load the visible panel list and show them: */
    QStringList strNameList = gEDataManager->fileManagerVisiblePanels();
    foreach(const QString strName, strNameList)
    {
        foreach(UIDialogPanel* pPanel, m_panelActionMap.keys())
        {
            if (strName == pPanel->panelName())
            {
                showPanel(pPanel);
                break;
            }
        }
    }
}

void UIFileManager::loadOptions()
{
    /* Load options: */
    UIFileManagerOptions *pOptions = UIFileManagerOptions::instance();
    if (pOptions)
    {
        pOptions->fListDirectoriesOnTop = gEDataManager->fileManagerListDirectoriesFirst();
        pOptions->fAskDeleteConfirmation = gEDataManager->fileManagerShowDeleteConfirmation();
        pOptions->fShowHumanReadableSizes = gEDataManager->fileManagerShowHumanReadableSizes();
        pOptions->fShowHiddenObjects = gEDataManager->fileManagerShowHiddenObjects();
    }
}

void UIFileManager::hidePanel(UIDialogPanel* panel)
{
    if (!m_pActionPool)
        return;
    if (panel && panel->isVisible())
        panel->setVisible(false);
    QMap<UIDialogPanel*, QAction*>::iterator iterator = m_panelActionMap.find(panel);
    if (iterator != m_panelActionMap.end())
    {
        if (iterator.value() && iterator.value()->isChecked())
            iterator.value()->setChecked(false);
    }
    m_visiblePanelsList.removeAll(panel);
    manageEscapeShortCut();
    savePanelVisibility();
}

void UIFileManager::showPanel(UIDialogPanel* panel)
{
    if (panel && panel->isHidden())
        panel->setVisible(true);
    QMap<UIDialogPanel*, QAction*>::iterator iterator = m_panelActionMap.find(panel);
    if (iterator != m_panelActionMap.end())
    {
        if (!iterator.value()->isChecked())
            iterator.value()->setChecked(true);
    }
    if (!m_visiblePanelsList.contains(panel))
        m_visiblePanelsList.push_back(panel);
    manageEscapeShortCut();
    savePanelVisibility();
}

void UIFileManager::manageEscapeShortCut()
{
    /* if there is no visible panels give the escape shortcut to parent dialog: */
    if (m_visiblePanelsList.isEmpty())
    {
        emit sigSetCloseButtonShortCut(QKeySequence(Qt::Key_Escape));
        return;
    }
    /* Take the escape shortcut from the dialog: */
    emit sigSetCloseButtonShortCut(QKeySequence());
    /* Just loop thru the visible panel list and set the esc key to the
       panel which made visible latest */
    for (int i = 0; i < m_visiblePanelsList.size() - 1; ++i)
        m_visiblePanelsList[i]->setCloseButtonShortCut(QKeySequence());

    m_visiblePanelsList.back()->setCloseButtonShortCut(QKeySequence(Qt::Key_Escape));
}

void UIFileManager::appendLog(const QString &strLog, const QString &strMachineName, FileManagerLogType eLogType)
{
    if (!m_pLogPanel)
        return;
    m_pLogPanel->appendLog(strLog, strMachineName, eLogType);
}

void UIFileManager::savePanelVisibility()
{
    if (m_fCommitDataSignalReceived)
        return;
    /* Save a list of currently visible panels: */
    QStringList strNameList;
    foreach(UIDialogPanel* pPanel, m_visiblePanelsList)
        strNameList.append(pPanel->panelName());
    gEDataManager->setFileManagerVisiblePanels(strNameList);
}

void UIFileManager::setSelectedVMListItems(const QList<UIVirtualMachineItem*> &items)
{
    AssertReturnVoid(m_pGuestTablesContainer);
    QVector<QUuid> selectedMachines;

    foreach (const UIVirtualMachineItem *item, items)
    {
        if (!item)
            continue;
        selectedMachines << item->id();
    }
    QUuid lastSelection = selectedMachines.isEmpty() ? QUuid() : selectedMachines.last();
    /** Iterate through the current tabs and add any machine id for which we have a running guest session to the
      * list of machine ids we want to have a tab for: */
    for (int i = 0; i < m_pGuestTablesContainer->count(); ++i)
    {
        UIFileManagerGuestTable *pTable = qobject_cast<UIFileManagerGuestTable*>(m_pGuestTablesContainer->widget(i));
        if (!pTable || !pTable->isGuestSessionRunning())
            continue;
        if (!selectedMachines.contains(pTable->machineId()))
            selectedMachines << pTable->machineId();
    }

    setMachines(selectedMachines, lastSelection);
}

void UIFileManager::setMachines(const QVector<QUuid> &machineIds, const QUuid &lastSelectedMachineId /* = QUuid() */)
{
    AssertReturnVoid(m_pGuestTablesContainer);

    /* List of machines that are newly added to selected machine list: */
    QVector<QUuid> newSelections;
    QVector<QUuid> unselectedMachines(m_machineIds);

    foreach (const QUuid &id, machineIds)
    {
        unselectedMachines.removeAll(id);
        if (!m_machineIds.contains(id))
            newSelections << id;
    }
    m_machineIds = machineIds;

    addTabs(newSelections);
    removeTabs(unselectedMachines);
    if (!lastSelectedMachineId.isNull())
    {
        int iIndexToSelect = -1;
        for (int i = 0; i < m_pGuestTablesContainer->count() && iIndexToSelect == -1; ++i)
        {
            UIFileManagerGuestTable *pTable = qobject_cast<UIFileManagerGuestTable*>(m_pGuestTablesContainer->widget(i));
            if (!pTable)
                continue;
            if (lastSelectedMachineId == pTable->machineId())
                iIndexToSelect = i;
        }
        if (iIndexToSelect != -1)
            m_pGuestTablesContainer->setCurrentIndex(iIndexToSelect);
    }
}

void UIFileManager::removeTabs(const QVector<QUuid> &machineIdsToRemove)
{
    if (!m_pGuestTablesContainer)
        return;
    QVector<UIFileManagerGuestTable*> removeList;

    for (int i = m_pGuestTablesContainer->count() - 1; i >= 0; --i)
    {
        UIFileManagerGuestTable *pTable = qobject_cast<UIFileManagerGuestTable*>(m_pGuestTablesContainer->widget(i));
        if (!pTable)
            continue;
        if (machineIdsToRemove.contains(pTable->machineId()))
        {
            removeList << pTable;
            m_pGuestTablesContainer->removeTab(i);
        }
    }
    qDeleteAll(removeList.begin(), removeList.end());
}

void UIFileManager::addTabs(const QVector<QUuid> &machineIdsToAdd)
{
    if (!m_pGuestTablesContainer)
        return;

    foreach (const QUuid &id, machineIdsToAdd)
    {
        CMachine comMachine = uiCommon().virtualBox().FindMachine(id.toString());
        if (comMachine.isNull())
            continue;
        UIFileManagerGuestTable *pGuestFileTable = new UIFileManagerGuestTable(m_pActionPool, comMachine, m_pGuestTablesContainer);
        m_pGuestTablesContainer->addTab(pGuestFileTable, comMachine.GetName());
        if (pGuestFileTable)
        {
            connect(pGuestFileTable, &UIFileManagerGuestTable::sigLogOutput,
                    this, &UIFileManager::sltReceieveLogOutput);
            connect(pGuestFileTable, &UIFileManagerGuestTable::sigSelectionChanged,
                    this, &UIFileManager::sltFileTableSelectionChanged);
            connect(pGuestFileTable, &UIFileManagerGuestTable::sigNewFileOperation,
                    this, &UIFileManager::sltReceieveNewFileOperation);
            connect(pGuestFileTable, &UIFileManagerGuestTable::sigDeleteConfirmationOptionChanged,
                    this, &UIFileManager::sltHandleOptionsUpdated);
            connect(pGuestFileTable, &UIFileManagerGuestTable::sigStateChanged,
                    this, &UIFileManager::sltGuestFileTableStateChanged);
        }
    }
}

UIFileManagerGuestTable *UIFileManager::currentGuestTable()
{
    if (!m_pGuestTablesContainer)
        return 0;
    return qobject_cast<UIFileManagerGuestTable*>(m_pGuestTablesContainer->currentWidget());
}
#include "UIFileManager.moc"
