Files
iDescriptor/src/fileexplorerwidget.cpp
T
2025-11-03 19:55:48 -08:00

258 lines
9.1 KiB
C++

/*
* iDescriptor: A free and open-source idevice management tool.
*
* Copyright (C) 2025 Uncore <https://github.com/uncor3>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include "fileexplorerwidget.h"
#include "afcexplorerwidget.h"
#include "iDescriptor-ui.h"
#include "iDescriptor.h"
#include "mediapreviewdialog.h"
#include "settingsmanager.h"
#include <QApplication>
#include <QDebug>
#include <QDesktopServices>
#include <QFileDialog>
#include <QHBoxLayout>
#include <QHeaderView>
#include <QIcon>
#include <QInputDialog>
#include <QMenu>
#include <QMessageBox>
#include <QPainter>
#include <QPalette>
#include <QPushButton>
#include <QSignalBlocker>
#include <QSplitter>
#include <QSplitterHandle>
#include <QTreeWidget>
#include <QVariant>
#include <libimobiledevice/afc.h>
#include <libimobiledevice/libimobiledevice.h>
FileExplorerWidget::FileExplorerWidget(iDescriptorDevice *device,
QWidget *parent)
: QWidget(parent), m_device(device)
{
m_mainSplitter = new ModernSplitter(Qt::Horizontal, this);
// Main layout
QHBoxLayout *mainLayout = new QHBoxLayout(this);
mainLayout->addWidget(m_mainSplitter);
mainLayout->setContentsMargins(0, 0, 0, 0);
setupSidebar();
// Create stacked widget with AFC explorers
m_stackedWidget = new QStackedWidget();
// Add normal AFC explorer (index 0)
AfcExplorerWidget *afcExplorer =
new AfcExplorerWidget(m_device, true, m_device->afcClient, "/", this);
connect(afcExplorer, &AfcExplorerWidget::favoritePlaceAdded, this,
&FileExplorerWidget::saveFavoritePlace);
m_stackedWidget->addWidget(afcExplorer);
// Add AFC2 explorer (index 1)
AfcExplorerWidget *afc2Explorer =
new AfcExplorerWidget(m_device, true, m_device->afc2Client, "/", this);
connect(afc2Explorer, &AfcExplorerWidget::favoritePlaceAdded, this,
&FileExplorerWidget::saveFavoritePlaceAfc2);
m_stackedWidget->addWidget(afc2Explorer);
// Start with normal AFC client
m_stackedWidget->setCurrentIndex(0);
// Add widgets to splitter
m_mainSplitter->addWidget(m_sidebarTree);
m_mainSplitter->addWidget(m_stackedWidget);
m_mainSplitter->setSizes({400, 800});
setLayout(mainLayout);
connect(SettingsManager::sharedInstance(),
&SettingsManager::favoritePlacesChanged, this,
&FileExplorerWidget::loadFavoritePlaces);
}
void FileExplorerWidget::setupSidebar()
{
m_sidebarTree = new QTreeWidget();
m_sidebarTree->setHeaderLabel("Files");
m_sidebarTree->setMinimumWidth(50);
m_sidebarTree->setMaximumWidth(250);
m_sidebarTree->setContextMenuPolicy(Qt::CustomContextMenu);
QTreeWidgetItem *explorersRoot = new QTreeWidgetItem(m_sidebarTree);
explorersRoot->setText(0, "Explorer");
explorersRoot->setIcon(0, QIcon::fromTheme("folder"));
explorersRoot->setExpanded(true);
m_defaultAfcItem = new QTreeWidgetItem(explorersRoot);
m_defaultAfcItem->setText(0, "Default");
m_defaultAfcItem->setIcon(0, QIcon::fromTheme("folder"));
m_jailbrokenAfcItem = new QTreeWidgetItem(explorersRoot);
m_jailbrokenAfcItem->setText(0, "Jailbroken (AFC2)");
m_jailbrokenAfcItem->setIcon(0, QIcon::fromTheme("folder"));
// Common Places section
QTreeWidgetItem *commonPlacesItem = new QTreeWidgetItem(m_sidebarTree);
commonPlacesItem->setText(0, "Common Places");
commonPlacesItem->setIcon(0, QIcon::fromTheme("places-bookmarks"));
commonPlacesItem->setExpanded(true);
QTreeWidgetItem *wallpapersItem = new QTreeWidgetItem(commonPlacesItem);
QVariantMap dataMap;
dataMap["path"] = "../../../var/mobile/Library/Wallpapers";
dataMap["alias"] = "Wallpapers";
dataMap["afc2"] = false;
wallpapersItem->setText(0, "Wallpapers");
wallpapersItem->setIcon(0, QIcon::fromTheme("image-x-generic"));
wallpapersItem->setData(0, Qt::UserRole, QVariant::fromValue(dataMap));
// Favorite Places section
m_favoritePlacesItem = new QTreeWidgetItem(m_sidebarTree);
m_favoritePlacesItem->setText(0, "Favorite Places");
m_favoritePlacesItem->setIcon(0, QIcon::fromTheme("user-bookmarks"));
m_favoritePlacesItem->setExpanded(true);
loadFavoritePlaces();
connect(m_sidebarTree, &QTreeWidget::itemClicked, this,
&FileExplorerWidget::onSidebarItemClicked);
connect(m_sidebarTree, &QTreeWidget::customContextMenuRequested, this,
&FileExplorerWidget::onSidebarContextMenuRequested);
}
void FileExplorerWidget::loadFavoritePlaces()
{
m_favoritePlacesItem->takeChildren();
SettingsManager *settings = SettingsManager::sharedInstance();
QList<QPair<QString, QString>> favorites =
settings->getFavoritePlaces("favorite_places/");
for (const auto &favorite : favorites) {
QString path = favorite.first;
QString alias = favorite.second;
QVariantMap dataMap;
dataMap["path"] = path;
dataMap["alias"] = alias;
dataMap["afc2"] = false;
QTreeWidgetItem *favoriteItem =
new QTreeWidgetItem(m_favoritePlacesItem);
favoriteItem->setText(0, alias);
favoriteItem->setIcon(0, QIcon::fromTheme("folder-favorites"));
favoriteItem->setData(0, Qt::UserRole, QVariant::fromValue(dataMap));
}
QList<QPair<QString, QString>> favorites_afc2 =
settings->getFavoritePlaces("favorite_places_afc2/");
for (const auto &favorite : favorites_afc2) {
QString path = favorite.first;
QString alias = favorite.second;
QVariantMap dataMap;
dataMap["path"] = path;
dataMap["alias"] = alias;
dataMap["afc2"] = true;
QTreeWidgetItem *favoriteItem =
new QTreeWidgetItem(m_favoritePlacesItem);
favoriteItem->setText(0, alias);
favoriteItem->setIcon(0, QIcon::fromTheme("folder-favorites"));
favoriteItem->setData(0, Qt::UserRole, QVariant::fromValue(dataMap));
}
}
void FileExplorerWidget::onSidebarItemClicked(QTreeWidgetItem *item, int column)
{
Q_UNUSED(column);
if (item == m_defaultAfcItem) {
static_cast<AfcExplorerWidget *>(m_stackedWidget->widget(0))->goHome();
m_stackedWidget->setCurrentIndex(0);
} else if (item == m_jailbrokenAfcItem) {
static_cast<AfcExplorerWidget *>(m_stackedWidget->widget(1))->goHome();
m_stackedWidget->setCurrentIndex(1);
}
QVariant data = item->data(0, Qt::UserRole);
if (data.isValid()) {
QVariantMap dataMap = data.toMap();
QString path = dataMap.value("path").toString();
bool afc2 = dataMap.value("afc2").toBool();
if (afc2) {
m_stackedWidget->setCurrentIndex(1);
} else {
m_stackedWidget->setCurrentIndex(0);
}
AfcExplorerWidget *currentExplorer =
qobject_cast<AfcExplorerWidget *>(m_stackedWidget->currentWidget());
if (currentExplorer) {
currentExplorer->navigateToPath(path);
}
}
}
void FileExplorerWidget::saveFavoritePlace(const QString &alias,
const QString &path)
{
qDebug() << "Saving favorite place:" << alias << "->" << path;
SettingsManager *settings = SettingsManager::sharedInstance();
settings->saveFavoritePlace(path, alias, "favorite_places/");
}
void FileExplorerWidget::saveFavoritePlaceAfc2(const QString &alias,
const QString &path)
{
SettingsManager *settings = SettingsManager::sharedInstance();
settings->saveFavoritePlace(path, alias, "favorite_places_afc2/");
}
void FileExplorerWidget::onSidebarContextMenuRequested(const QPoint &pos)
{
QTreeWidgetItem *item = m_sidebarTree->itemAt(pos);
// Only show a context menu for items that are direct children of the
// favorites list.
if (!item || item->parent() != m_favoritePlacesItem) {
return;
}
QVariant data = item->data(0, Qt::UserRole);
if (!data.isValid()) {
return;
}
QMenu contextMenu;
QAction *removeAction = contextMenu.addAction("Remove from Favorites");
QAction *selectedAction =
contextMenu.exec(m_sidebarTree->viewport()->mapToGlobal(pos));
if (selectedAction == removeAction) {
QVariantMap dataMap = data.toMap();
QString path = dataMap.value("path").toString();
bool afc2 = dataMap.value("afc2").toBool();
SettingsManager *settings = SettingsManager::sharedInstance();
if (afc2) {
settings->removeFavoritePlace("favorite_places_afc2/", path);
} else {
settings->removeFavoritePlace("favorite_places/", path);
}
}
}