Sandboxie/SandboxiePlus/SandMan/Views/FileView.cpp

414 lines
14 KiB
C++
Raw Normal View History

2021-10-16 16:19:51 +01:00
#include "stdafx.h"
2022-07-11 18:30:09 +01:00
#include "FileView.h"
2021-10-16 16:19:51 +01:00
#include "SandMan.h"
2023-05-21 18:23:43 +01:00
#include "../MiscHelpers/Common/Common.h"
2021-10-16 16:19:51 +01:00
#include "../MiscHelpers/Common/Settings.h"
#include "../MiscHelpers/Common/TreeItemModel.h"
#include "../MiscHelpers/Common/OtherFunctions.h"
#include "../QSbieAPI/SbieUtils.h"
2022-07-11 18:30:09 +01:00
CFileView::CFileView(QWidget *parent)
: QWidget(parent)
2021-10-16 16:19:51 +01:00
{
2022-07-11 18:30:09 +01:00
m_pMainLayout = new QGridLayout();
2022-09-29 17:28:48 +01:00
m_pMainLayout->setContentsMargins(0,0,0,0);
2022-07-11 18:30:09 +01:00
this->setLayout(m_pMainLayout);
2021-10-16 16:19:51 +01:00
2023-05-21 15:37:28 +01:00
m_pTreeView = new QTreeViewEx();
m_pTreeView->setColumnFixed(0, true);
2022-07-29 09:24:32 +01:00
m_pTreeView->setAlternatingRowColors(theConf->GetBool("Options/AltRowColors", false));
2022-07-11 18:30:09 +01:00
m_pMainLayout->addWidget(m_pTreeView, 0, 0);
2021-10-16 16:19:51 +01:00
2022-11-05 13:42:23 +00:00
m_pFileModel = NULL;
2022-08-16 17:35:16 +01:00
m_pTreeView->setSortingEnabled(true);
2022-07-11 18:30:09 +01:00
m_pTreeView->setSelectionMode(QAbstractItemView::ExtendedSelection);
2021-10-16 16:19:51 +01:00
QStyle* pStyle = QStyleFactory::create("windows");
2022-07-11 18:30:09 +01:00
m_pTreeView->setStyle(pStyle);
2022-07-20 08:35:07 +01:00
m_pTreeView->setItemDelegate(new CTreeItemDelegate());
2022-07-13 14:42:33 +01:00
2022-07-11 18:30:09 +01:00
m_pTreeView->setExpandsOnDoubleClick(false);
2021-10-16 16:19:51 +01:00
2022-07-11 18:30:09 +01:00
m_pTreeView->setContextMenuPolicy(Qt::CustomContextMenu);
connect(m_pTreeView, SIGNAL(customContextMenuRequested( const QPoint& )), this, SLOT(OnFileMenu(const QPoint &)));
connect(m_pTreeView, SIGNAL(doubleClicked(const QModelIndex &)), this, SLOT(OnFileDblClick(const QModelIndex &)));
2021-10-16 16:19:51 +01:00
}
2022-07-11 18:30:09 +01:00
CFileView::~CFileView()
2022-10-07 11:53:45 +01:00
{
SaveState();
}
void CFileView::SaveState()
2021-10-16 16:19:51 +01:00
{
2023-05-21 12:54:05 +01:00
if(m_pFileModel)
theConf->SetBlob("MainWindow/FileTree_Columns", m_pTreeView->header()->saveState());
2021-10-16 16:19:51 +01:00
}
2022-07-11 18:30:09 +01:00
void CFileView::SetBox(const CSandBoxPtr& pBox)
2021-10-16 16:19:51 +01:00
{
2023-01-29 16:10:11 +00:00
if (!m_pBox.isNull()) disconnect(m_pBox.data(), SIGNAL(AboutToBeModified()), this, SLOT(OnAboutToBeModified()));
2022-07-14 09:46:13 +01:00
2022-07-11 18:30:09 +01:00
m_pBox = pBox;
2022-07-14 09:46:13 +01:00
2023-01-29 16:10:11 +00:00
if (!m_pBox.isNull()) connect(m_pBox.data(), SIGNAL(AboutToBeModified()), this, SLOT(OnAboutToBeModified()));
2022-07-14 09:46:13 +01:00
2022-07-11 18:30:09 +01:00
QString Root;
2023-05-17 18:26:54 +01:00
if (!pBox.isNull() && QFile::exists(pBox->GetFileRoot()))
2022-07-11 18:30:09 +01:00
Root = pBox->GetFileRoot();
2022-11-05 13:42:23 +00:00
//if (Root.isEmpty()) {
// //Root = theAPI->GetSbiePath();
// m_pTreeView->setEnabled(false);
//}
//else
// m_pTreeView->setEnabled(true);
if (m_pFileModel) {
2023-05-21 12:54:05 +01:00
SaveState();
2023-05-17 18:26:54 +01:00
delete m_pFileModel;
2022-11-05 13:42:23 +00:00
m_pFileModel = NULL;
}
if (!Root.isEmpty()) {
m_pFileModel = new QFileSystemModel(this);
m_pFileModel->setFilter(QDir::NoDotAndDotDot | QDir::AllDirs | QDir::Files | QDir::Hidden | QDir::System);
2023-05-21 12:54:05 +01:00
QByteArray Columns = theConf->GetBlob("MainWindow/FileTree_Columns");
if (!Columns.isEmpty())
m_pTreeView->header()->restoreState(Columns);
2022-07-11 18:30:09 +01:00
}
2022-09-29 17:28:48 +01:00
m_pTreeView->setModel(m_pFileModel);
2022-11-05 13:42:23 +00:00
if (!Root.isEmpty())
{
m_pTreeView->setRootIndex(m_pFileModel->setRootPath(Root));
2022-07-14 09:46:13 +01:00
m_pTreeView->expand(m_pFileModel->index(Root + "/drive"));
m_pTreeView->expand(m_pFileModel->index(Root + "/share"));
m_pTreeView->expand(m_pFileModel->index(Root + "/user"));
//m_pTreeView->expand(m_pFileModel->index(Root + "/user/all"));
//m_pTreeView->expand(m_pFileModel->index(Root + "/user/current"));
}
}
2023-01-29 16:10:11 +00:00
void CFileView::OnAboutToBeModified()
2022-07-14 09:46:13 +01:00
{
if (sender() == m_pBox.data())
SetBox(CSandBoxPtr());
2021-10-16 16:19:51 +01:00
}
#include <windows.h>
#include <Shlobj.h>
#include <atlbase.h>
#define MENU_RECOVER 1
#define MENU_RECOVER_TO_ANY 2
#define MENU_CREATE_SHORTCUT 3
2023-01-29 09:49:41 +00:00
#define MENU_CHECK_FILE 4
2023-05-21 18:23:43 +01:00
#define MENU_PIN_FILE 5
2021-10-16 16:19:51 +01:00
void addSeparatorToShellContextMenu(HMENU hMenu)
{
MENUITEMINFO menu_item_info;
memset(&menu_item_info, 0, sizeof(MENUITEMINFO));
menu_item_info.cbSize = sizeof(MENUITEMINFO);
menu_item_info.fType = MFT_SEPARATOR;
menu_item_info.wID = 0;
InsertMenuItem(hMenu, 0, TRUE, &menu_item_info);
}
2023-05-21 18:23:43 +01:00
void addItemToShellContextMenu(HMENU hMenu, const wchar_t *name, int ID, bool bChecked = false)
2021-10-16 16:19:51 +01:00
{
MENUITEMINFO menu_item_info;
memset(&menu_item_info, 0, sizeof(MENUITEMINFO));
menu_item_info.cbSize = sizeof(MENUITEMINFO);
menu_item_info.fMask = MIIM_ID | MIIM_STRING | MIIM_DATA;
2023-05-21 18:23:43 +01:00
if (bChecked) {
menu_item_info.fMask |= MIIM_STATE;
menu_item_info.fState |= MFS_CHECKED;
}
2021-10-16 16:19:51 +01:00
menu_item_info.wID = 0xF000 + ID;
menu_item_info.dwTypeData = (wchar_t*)name;
InsertMenuItem(hMenu, 0, TRUE, &menu_item_info);
}
2023-05-21 18:23:43 +01:00
int openShellContextMenu(const QStringList& Files, void* parentWindow, const CSandBoxPtr& pBox, QString* pPin = NULL)
2021-10-16 16:19:51 +01:00
{
2023-04-13 18:46:51 +01:00
CComPtr<IShellFolder> pDesktop;
if (!SUCCEEDED(SHGetDesktopFolder(&pDesktop)))
return 0;
2021-10-16 16:19:51 +01:00
std::list<CComHeapPtr<ITEMIDLIST_ABSOLUTE>> items;
items.resize(Files.count());
auto IT = items.begin();
foreach(QString File, Files) {
2023-04-13 18:46:51 +01:00
//CComPtr<IShellItem> item;
//SHCreateItemFromParsingName(File.toStdWString().c_str(), NULL, IID_PPV_ARGS(&item));
//CComQIPtr<IPersistIDList> idl(item);
//idl->GetIDList(&*IT++);
pDesktop->ParseDisplayName((HWND)parentWindow, NULL, (wchar_t*)File.toStdWString().c_str(), NULL, &*IT++, NULL);
2021-10-16 16:19:51 +01:00
}
std::vector<LPCITEMIDLIST> list;
list.resize(items.size());
LPCITEMIDLIST* listPtr = &list.front();
for (auto I = items.begin(); I != items.end(); I++)
*listPtr++ = *I;
2023-04-13 18:46:51 +01:00
CComPtr<IContextMenu> pContextMenu;
//CComPtr<IShellItemArray> array;
//SHCreateShellItemArrayFromIDLists(list.size(), (LPCITEMIDLIST*)&list.front(), &array);
//array->BindToHandler(NULL, BHID_SFUIObject, IID_PPV_ARGS(&pContextMenu)); // note: this fails when the files have different parent folders
//if (!pContextMenu) // note: with the below approach properties and delete does not work, so a custom handler would be needed
2023-04-13 18:46:51 +01:00
// pDesktop->GetUIObjectOf((HWND)parentWindow, list.size(), (LPCITEMIDLIST*)&list.front(), IID_IContextMenu, NULL, (void**)&pContextMenu);
{
DEFCONTEXTMENU details = { 0 };
details.hwnd = (HWND)parentWindow;
//details.pcmcb = pContextMenuCB;
//details.pidlFolder = NULL;
details.psf = pDesktop;
details.cidl = list.size();
details.apidl = reinterpret_cast<PCUITEMID_CHILD_ARRAY>(&list.front());
//details.punkAssociationInfo = NULL;
//details.cKeys = 0;
//details.aKeys = NULL;
SHCreateDefaultContextMenu(&details, IID_IContextMenu, reinterpret_cast<LPVOID*>(&pContextMenu));
}
if (!SUCCEEDED(!pContextMenu))
2021-10-16 16:19:51 +01:00
return 0;
HMENU hMenu = CreatePopupMenu();
if (!hMenu)
return 0;
2023-04-13 18:46:51 +01:00
if (SUCCEEDED(pContextMenu->QueryContextMenu(hMenu, 0, 1, 0x7FFF, CMF_NORMAL)))
2021-10-16 16:19:51 +01:00
{
addSeparatorToShellContextMenu(hMenu);
2022-07-11 18:30:09 +01:00
std::wstring Str1 = CFileView::tr("Create Shortcut").toStdWString();
2021-10-16 16:19:51 +01:00
if (Files.count() == 1) {
addItemToShellContextMenu(hMenu, Str1.c_str(), MENU_CREATE_SHORTCUT);
addSeparatorToShellContextMenu(hMenu);
}
2022-07-11 18:30:09 +01:00
std::wstring Str2 = CFileView::tr("Recover to Any Folder").toStdWString();
2021-10-16 16:19:51 +01:00
addItemToShellContextMenu(hMenu, Str2.c_str(), MENU_RECOVER_TO_ANY);
2022-07-11 18:30:09 +01:00
std::wstring Str3 = CFileView::tr("Recover to Same Folder").toStdWString();
2021-10-16 16:19:51 +01:00
addItemToShellContextMenu(hMenu, Str3.c_str(), MENU_RECOVER);
2023-01-29 09:49:41 +00:00
if (!pBox->GetTextList("OnFileRecovery", true, false, true).isEmpty()) {
std::wstring Str4 = CFileView::tr("Run Recovery Checks").toStdWString();
addItemToShellContextMenu(hMenu, Str4.c_str(), MENU_CHECK_FILE);
}
2023-05-21 18:23:43 +01:00
if (pPin && Files.count() == 1)
{
auto pBoxPlus = pBox.objectCast<CSandBoxPlus>();
QStringList RunOptions = pBox->GetTextList("RunCommand", true);
QString FoundPin;
QString FileName = Files.first();
foreach(const QString & RunOption, RunOptions) {
QString CmdFile = pBoxPlus->GetCommandFile(Split2(RunOption, "|").second);
if(CmdFile.compare(FileName, Qt::CaseInsensitive) == 0) {
FoundPin = RunOption;
break;
}
}
*pPin = FoundPin;
std::wstring Str5 = CFileView::tr("Pin to Box Run Menu").toStdWString();
addItemToShellContextMenu(hMenu, Str5.c_str(), MENU_PIN_FILE, !FoundPin.isEmpty());
}
2021-10-16 16:19:51 +01:00
POINT point;
GetCursorPos(&point);
int iCmd = TrackPopupMenuEx(hMenu, TPM_RETURNCMD, point.x, point.y, (HWND)parentWindow, NULL);
if (iCmd > 0)
{
if ((iCmd & 0xF000) == 0xF000) {
DestroyMenu(hMenu);
return iCmd & 0x0FFF;
}
CMINVOKECOMMANDINFOEX info = { 0 };
info.cbSize = sizeof(info);
info.fMask = CMIC_MASK_UNICODE;
info.hwnd = (HWND)parentWindow;
info.lpVerb = MAKEINTRESOURCEA(iCmd - 1);
info.lpVerbW = MAKEINTRESOURCEW(iCmd - 1);
info.nShow = SW_SHOWNORMAL;
2023-04-13 18:46:51 +01:00
pContextMenu->InvokeCommand((LPCMINVOKECOMMANDINFO)&info);
2021-10-16 16:19:51 +01:00
}
}
DestroyMenu(hMenu);
return 0;
}
2022-07-11 18:30:09 +01:00
void CFileView::OnFileMenu(const QPoint&)
2021-10-16 16:19:51 +01:00
{
2022-09-29 17:28:48 +01:00
if (!m_pFileModel) return;
2021-10-16 16:19:51 +01:00
QStringList Files;
2022-07-11 18:30:09 +01:00
foreach(const QModelIndex & Index, m_pTreeView->selectionModel()->selectedIndexes()) {
2021-10-16 16:19:51 +01:00
QString BoxedPath = m_pFileModel->fileInfo(Index).absoluteFilePath().replace("/", "\\");
if (m_pFileModel->fileInfo(Index).isDir())
BoxedPath += "\\";
bool bFound = false;
foreach(const QString & File, Files) {
if (BoxedPath.contains(File))
bFound = true;
else if (File.contains(BoxedPath))
Files.removeOne(File);
}
if(!bFound)
Files.append(BoxedPath);
}
2021-10-19 23:40:29 +01:00
if (Files.isEmpty())
return;
2023-05-21 18:23:43 +01:00
QString FoundPin;
int iCmd = openShellContextMenu(Files, (void*)this->winId(), m_pBox, &FoundPin);
2021-10-16 16:19:51 +01:00
if (iCmd == 0)
return;
QString RecoveryFolder;
switch (iCmd)
{
case MENU_RECOVER_TO_ANY:
2022-07-11 18:30:09 +01:00
RecoveryFolder = QFileDialog::getExistingDirectory(this, CFileView::tr("Select Directory")).replace("/", "\\");
2021-10-16 16:19:51 +01:00
if (RecoveryFolder.isEmpty())
break;
case MENU_RECOVER:
{
QStringList AllFiles;
foreach(const QString& File, Files)
{
if (File.right(1) == "\\") {
foreach(QString SubFile, ListDir(File))
AllFiles.append(File + SubFile.replace("/", "\\"));
}
else
AllFiles.append(File);
}
QList<QPair<QString, QString>> FileList;
foreach(QString BoxedPath, AllFiles)
{
if (!RecoveryFolder.isEmpty()) {
QString FileName = BoxedPath.mid(BoxedPath.lastIndexOf("\\") + 1);
FileList.append(qMakePair(BoxedPath, RecoveryFolder + "\\" + FileName));
}
else {
2022-05-19 18:53:51 +01:00
QString RealPath = theAPI->GetRealPath(m_pBox.data(), BoxedPath);
2021-10-16 16:19:51 +01:00
FileList.append(qMakePair(BoxedPath, RealPath));
}
}
2023-05-08 20:33:50 +01:00
SB_PROGRESS Status = theGUI->RecoverFiles(m_pBox->GetName(), FileList, theGUI, 0);
2021-10-16 16:19:51 +01:00
if (Status.GetStatus() == OP_ASYNC)
theGUI->AddAsyncOp(Status.GetValue());
break;
}
2023-01-29 09:49:41 +00:00
case MENU_CHECK_FILE:
{
SB_PROGRESS Status = theGUI->CheckFiles(m_pBox->GetName(), Files);
if (Status.GetStatus() == OP_ASYNC)
theGUI->AddAsyncOp(Status.GetValue());
break;
}
2023-05-21 18:23:43 +01:00
case MENU_PIN_FILE:
{
auto pBoxPlus = m_pBox.objectCast<CSandBoxPlus>();
if (FoundPin.isEmpty())
pBoxPlus->InsertText("RunCommand", Split2(Files.first(), "\\", true).second + "|\"" + pBoxPlus->MakeBoxCommand(Files.first()) + "\"");
else
pBoxPlus->DelValue("RunCommand", FoundPin);
break;
}
2021-10-16 16:19:51 +01:00
case MENU_CREATE_SHORTCUT:
{
QString BoxName = m_pBox->GetName();
QString LinkPath = Files.first();
QString LinkName = LinkPath.mid(LinkPath.lastIndexOf("\\") + 1);
QString Path = QStandardPaths::writableLocation(QStandardPaths::DesktopLocation).replace("/", "\\");
2022-12-07 16:32:40 +00:00
//Path = QFileDialog::getExistingDirectory(this, tr("Select Directory to create Shortcut in"), Path).replace("/", "\\");
2021-10-16 16:19:51 +01:00
//if (Path.isEmpty())
// return;
if (Path.right(1) != "\\")
Path.append("\\");
Path += "[" + BoxName + "] " + LinkName;
Path = QFileDialog::getSaveFileName(this, tr("Create Shortcut to sandbox %1").arg(BoxName), Path, QString("Shortcut files (*.lnk)")).replace("/", "\\");
if (Path.isEmpty())
return;
CSbieUtils::CreateShortcut(theAPI, Path, LinkName, BoxName, LinkPath, LinkPath);
break;
}
}
}
2022-07-11 18:30:09 +01:00
void CFileView::OnFileDblClick(const QModelIndex &)
2021-10-16 16:19:51 +01:00
{
2022-09-29 17:28:48 +01:00
if (!m_pFileModel) return;
2022-07-11 18:30:09 +01:00
QString BoxedPath = m_pFileModel->fileInfo(m_pTreeView->currentIndex()).absoluteFilePath();
2021-10-16 16:19:51 +01:00
ShellExecute(NULL, NULL, BoxedPath.toStdWString().c_str(), NULL, m_pBox->GetFileRoot().toStdWString().c_str(), SW_SHOWNORMAL);
}
2022-07-11 18:30:09 +01:00
////////////////////////////////////////////////////////////////////////////////////////
// CFileBrowserWindow
CFileBrowserWindow::CFileBrowserWindow(const CSandBoxPtr& pBox, QWidget *parent)
: QDialog(parent)
{
Qt::WindowFlags flags = windowFlags();
flags |= Qt::CustomizeWindowHint;
//flags &= ~Qt::WindowContextHelpButtonHint;
//flags &= ~Qt::WindowSystemMenuHint;
//flags &= ~Qt::WindowMinMaxButtonsHint;
flags |= Qt::WindowMinimizeButtonHint;
//flags &= ~Qt::WindowCloseButtonHint;
setWindowFlags(flags);
bool bAlwaysOnTop = theConf->GetBool("Options/AlwaysOnTop", false);
this->setWindowFlag(Qt::WindowStaysOnTopHint, bAlwaysOnTop);
m_pMainLayout = new QGridLayout(this);
m_FileView = new CFileView();
m_FileView->SetBox(pBox);
m_pMainLayout->addWidget(m_FileView, 0, 0);
this->setWindowTitle(tr("%1 - Files").arg(pBox->GetName()));
//statusBar();
restoreGeometry(theConf->GetBlob("FileBrowserWindow/Window_Geometry"));
}
CFileBrowserWindow::~CFileBrowserWindow()
{
theConf->SetBlob("FileBrowserWindow/Window_Geometry",saveGeometry());
}
void CFileBrowserWindow::closeEvent(QCloseEvent *e)
{
emit Closed();
this->deleteLater();
}