diff --git a/CMakeLists.txt b/CMakeLists.txt
index b6d33cb..0eaa5d9 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -203,11 +203,31 @@ find_package(Qt6 REQUIRED COMPONENTS Multimedia)
find_package(Qt6 REQUIRED COMPONENTS MultimediaWidgets)
-qt_add_executable(iDescriptor
+if (WIN32)
+# todo
+ set(app_icon_resource_windows "${CMAKE_CURRENT_SOURCE_DIR}/resources/todo.rc")
+ qt_add_executable(iDescriptor
+ MANUAL_FINALIZATION
+ ${PROJECT_SOURCES}
+ ${app_icon_resource_windows}
+)
+elseif (APPLE)
+ set(MACOSX_BUNDLE_ICON_FILE icon.icns)
+ set(app_icon_macos "${CMAKE_CURRENT_SOURCE_DIR}/resources/icons/app-icon/icon.icns")
+ set_source_files_properties(${app_icon_macos} PROPERTIES
+ MACOSX_PACKAGE_LOCATION "Resources")
+
+ qt_add_executable(iDescriptor
+ MANUAL_FINALIZATION
+ ${PROJECT_SOURCES}
+ ${app_icon_macos}
+)
+else()
+ qt_add_executable(iDescriptor
MANUAL_FINALIZATION
${PROJECT_SOURCES}
)
-
+endif()
target_link_libraries(iDescriptor PRIVATE
Qt${QT_VERSION_MAJOR}::Widgets
diff --git a/resources.qrc b/resources.qrc
index 7c562b0..a418045 100644
--- a/resources.qrc
+++ b/resources.qrc
@@ -7,6 +7,14 @@
resources/icons/IcOutlinePowerSettingsNew.png
resources/icons/HugeiconsWrench01.png
resources/icons/IcTwotoneRestartAlt.png
+ resources/icons/MaterialSymbolsArrowUpwardAltRounded.png
+ resources/icons/LetsIconsImport.png
+ resources/icons/MaterialSymbolsArrowLeftAlt.png
+ resources/icons/MaterialSymbolsArrowRightAlt.png
+ resources/icons/PhExport.png
+ resources/icons/MaterialSymbolsLightKeyboardReturn.png
+ resources/icons/MaterialSymbolsFavorite.png
+ resources/icons/MdiLightMagnify.png
qml/MapView.qml
diff --git a/resources/icons/LetsIconsImport.png b/resources/icons/LetsIconsImport.png
new file mode 100644
index 0000000..42c77ec
Binary files /dev/null and b/resources/icons/LetsIconsImport.png differ
diff --git a/resources/icons/MaterialSymbolsArrowLeftAlt.png b/resources/icons/MaterialSymbolsArrowLeftAlt.png
new file mode 100644
index 0000000..d7a4333
Binary files /dev/null and b/resources/icons/MaterialSymbolsArrowLeftAlt.png differ
diff --git a/resources/icons/MaterialSymbolsArrowRightAlt.png b/resources/icons/MaterialSymbolsArrowRightAlt.png
new file mode 100644
index 0000000..ccb9868
Binary files /dev/null and b/resources/icons/MaterialSymbolsArrowRightAlt.png differ
diff --git a/resources/icons/MaterialSymbolsArrowUpwardAltRounded.png b/resources/icons/MaterialSymbolsArrowUpwardAltRounded.png
new file mode 100644
index 0000000..40ba23f
Binary files /dev/null and b/resources/icons/MaterialSymbolsArrowUpwardAltRounded.png differ
diff --git a/resources/icons/MaterialSymbolsFavorite.png b/resources/icons/MaterialSymbolsFavorite.png
new file mode 100644
index 0000000..33e0fa3
Binary files /dev/null and b/resources/icons/MaterialSymbolsFavorite.png differ
diff --git a/resources/icons/MaterialSymbolsLightKeyboardReturn.png b/resources/icons/MaterialSymbolsLightKeyboardReturn.png
new file mode 100644
index 0000000..df5a40a
Binary files /dev/null and b/resources/icons/MaterialSymbolsLightKeyboardReturn.png differ
diff --git a/resources/icons/MdiLightMagnify.png b/resources/icons/MdiLightMagnify.png
new file mode 100644
index 0000000..367f43a
Binary files /dev/null and b/resources/icons/MdiLightMagnify.png differ
diff --git a/resources/icons/PhExport.png b/resources/icons/PhExport.png
new file mode 100644
index 0000000..8358f61
Binary files /dev/null and b/resources/icons/PhExport.png differ
diff --git a/resources/icons/app-icon/icon.icns b/resources/icons/app-icon/icon.icns
new file mode 100644
index 0000000..eed2f3d
Binary files /dev/null and b/resources/icons/app-icon/icon.icns differ
diff --git a/src/afcexplorerwidget.cpp b/src/afcexplorerwidget.cpp
index 2a7123f..de9f08b 100644
--- a/src/afcexplorerwidget.cpp
+++ b/src/afcexplorerwidget.cpp
@@ -218,7 +218,8 @@ void AfcExplorerWidget::onFileListContextMenu(const QPoint &pos)
bool isDir = item->data(Qt::UserRole).toBool();
if (isDir)
- return; // Only export files
+ return; // TODO: Implement directory export later - Only export files
+ // for now
QMenu menu;
QAction *exportAction = menu.addAction("Export");
@@ -253,7 +254,7 @@ void AfcExplorerWidget::onExportClicked()
if (selectedItems.isEmpty())
return;
- // Only files (not directories)
+ // Only files (not directories) - TODO: Implement directory export later
QList filesToExport;
for (QListWidgetItem *item : selectedItems) {
if (!item->data(Qt::UserRole).toBool())
@@ -320,7 +321,11 @@ void AfcExplorerWidget::exportSelectedFile(QListWidgetItem *item,
}
}
-// TODO : abstract to services
+/*
+ FIXME : abstract to services
+ even though we are using safe wrappers,
+ we better move this to services
+*/
int AfcExplorerWidget::export_file_to_path(afc_client_t afc,
const char *device_path,
const char *local_path)
@@ -348,32 +353,23 @@ int AfcExplorerWidget::export_file_to_path(afc_client_t afc,
}
fclose(out);
- afc_file_close(afc, handle);
+ ServiceManager::safeAfcFileClose(m_device, handle);
return 0;
}
+// should be disabled if there is an error loading afc
void AfcExplorerWidget::onImportClicked()
{
- // TODO: check devices
-
- // Select one or more files to import
QStringList fileNames = QFileDialog::getOpenFileNames(this, "Import Files");
if (fileNames.isEmpty())
return;
- // Use current breadcrumb directory as target
QString currPath = "/";
if (!m_history.isEmpty())
currPath = m_history.top();
if (!currPath.endsWith("/"))
currPath += "/";
- // if (!device || !client || !serviceDesc)
- // {
- // qDebug() << "Failed to connect to device or lockdown service";
- // return;
- // }
-
// Import each file
for (const QString &localPath : fileNames) {
QFileInfo fi(localPath);
@@ -437,16 +433,13 @@ void AfcExplorerWidget::setupFileExplorer()
m_explorer->setStyleSheet("border : none;");
// Export/Import buttons layout
- QHBoxLayout *exportLayout = new QHBoxLayout();
- m_exportBtn = new QPushButton("Export");
- m_importBtn = new QPushButton("Import");
- m_addToFavoritesBtn = new QPushButton("Add to Favorites");
- exportLayout->addWidget(m_exportBtn);
- exportLayout->addWidget(m_importBtn);
- exportLayout->addWidget(m_addToFavoritesBtn);
- exportLayout->setContentsMargins(0, 0, 0, 0);
- exportLayout->addStretch();
- explorerLayout->addLayout(exportLayout);
+ m_exportBtn =
+ new ZIconWidget(QIcon(":/resources/icons/PhExport.png"), "Export");
+ m_importBtn = new ZIconWidget(
+ QIcon(":/resources/icons/LetsIconsImport.png"), "Import");
+ m_addToFavoritesBtn =
+ new ZIconWidget(QIcon(":/resources/icons/MaterialSymbolsFavorite.png"),
+ "Add to Favorites");
// Navigation layout (Address Bar with embedded icons)
m_navWidget = new QWidget();
@@ -474,17 +467,19 @@ void AfcExplorerWidget::setupFileExplorer()
// red;");
leftNavLayout->setContentsMargins(0, 0, 0, 0);
leftNavLayout->setSpacing(1);
-
- m_backButton = new ClickableIconWidget(
- QIcon::fromTheme("go-previous", QIcon("←")), "Go Back");
+ // rename to ziconwidget
+ m_backButton = new ZIconWidget(
+ QIcon(":/resources/icons/MaterialSymbolsArrowLeftAlt.png"), "Go Back");
m_backButton->setEnabled(false);
- m_forwardButton = new ClickableIconWidget(
- QIcon::fromTheme("go-next", QIcon("→")), "Go Forward");
+ m_forwardButton = new ZIconWidget(
+ QIcon(":/resources/icons/MaterialSymbolsArrowRightAlt.png"),
+ "Go Forward");
m_forwardButton->setEnabled(false);
- m_enterButton = new ClickableIconWidget(
- QIcon::fromTheme("go-jump", QIcon("⏎")), "Navigate to path");
+ m_enterButton = new ZIconWidget(
+ QIcon(":/resources/icons/MaterialSymbolsLightKeyboardReturn.png"),
+ "Navigate to path");
m_addressBar = new QLineEdit();
m_addressBar->setPlaceholderText("Enter path...");
@@ -495,6 +490,9 @@ void AfcExplorerWidget::setupFileExplorer()
leftNavLayout->addWidget(m_forwardButton);
navLayout->addWidget(explorerLeftSideNavButtons);
navLayout->addWidget(m_addressBar);
+ navLayout->addWidget(m_importBtn);
+ navLayout->addWidget(m_exportBtn);
+ navLayout->addWidget(m_addToFavoritesBtn);
navLayout->addWidget(m_enterButton);
// Add the container layout (which centers navWidget) to the main layout
@@ -515,24 +513,28 @@ void AfcExplorerWidget::setupFileExplorer()
explorerLayout->addWidget(m_fileList);
// Connect buttons and actions
- connect(m_backButton, &ClickableIconWidget::clicked, this,
+ connect(m_backButton, &ZIconWidget::clicked, this,
&AfcExplorerWidget::goBack);
- connect(m_forwardButton, &ClickableIconWidget::clicked, this,
+ connect(m_forwardButton, &ZIconWidget::clicked, this,
&AfcExplorerWidget::goForward);
- connect(m_enterButton, &ClickableIconWidget::clicked, this,
+ connect(m_enterButton, &ZIconWidget::clicked, this,
&AfcExplorerWidget::onAddressBarReturnPressed);
connect(m_addressBar, &QLineEdit::returnPressed, this,
&AfcExplorerWidget::onAddressBarReturnPressed);
connect(m_fileList, &QListWidget::itemDoubleClicked, this,
&AfcExplorerWidget::onItemDoubleClicked);
- connect(m_exportBtn, &QPushButton::clicked, this,
+ connect(m_exportBtn, &ZIconWidget::clicked, this,
&AfcExplorerWidget::onExportClicked);
- connect(m_importBtn, &QPushButton::clicked, this,
+ connect(m_importBtn, &ZIconWidget::clicked, this,
&AfcExplorerWidget::onImportClicked);
- connect(m_addToFavoritesBtn, &QPushButton::clicked, this,
+ connect(m_addToFavoritesBtn, &ZIconWidget::clicked, this,
&AfcExplorerWidget::onAddToFavoritesClicked);
+ connect(m_fileList->selectionModel(),
+ &QItemSelectionModel::selectionChanged, this,
+ &AfcExplorerWidget::updateButtonStates);
updateNavigationButtons();
+ updateButtonStates(); // Initialize button states
updateNavStyles();
}
@@ -562,8 +564,10 @@ void AfcExplorerWidget::saveFavoritePlace(const QString &path,
void AfcExplorerWidget::updateNavStyles()
{
- QColor bgColor = isDarkMode() ? qApp->palette().color(QPalette::Light)
- : qApp->palette().color(QPalette::Dark);
+ bool isDark = isDarkMode();
+ QColor lightColor = qApp->palette().color(QPalette::Light);
+ QColor darkColor = qApp->palette().color(QPalette::Dark);
+ QColor bgColor = isDark ? lightColor : darkColor;
QColor borderColor = qApp->palette().color(QPalette::Mid);
QColor accentColor = qApp->palette().color(QPalette::Highlight);
@@ -585,9 +589,27 @@ void AfcExplorerWidget::updateNavStyles()
// Update address bar styles to complement the nav widget
QString addressBarStyles =
QString("QLineEdit { background-color: %1; border-radius: 10px; "
- "border: 1px solid %2; }")
- .arg(bgColor.name())
- .arg(borderColor.lighter().name());
+ "border: 1px solid %2; padding: 2px 4px; color: %3; }"
+ "QLineEdit:focus {border: 3px solid %4; }")
+ .arg(isDark ? QColor(Qt::white).name() : QColor(Qt::black).name())
+ .arg(borderColor.lighter().name())
+ .arg(isDark ? QColor(Qt::black).name() : QColor(Qt::white).name())
+ .arg(COLOR_ACCENT_BLUE.name());
m_addressBar->setStyleSheet(addressBarStyles);
+}
+
+void AfcExplorerWidget::updateButtonStates()
+{
+ QList selectedItems = m_fileList->selectedItems();
+
+ // Export is only enabled if non-directory items are selected
+ bool hasExportableFiles = false;
+ for (QListWidgetItem *item : selectedItems) {
+ if (!item->data(Qt::UserRole).toBool()) { // Not a directory
+ hasExportableFiles = true;
+ break;
+ }
+ }
+ m_exportBtn->setEnabled(hasExportableFiles);
}
\ No newline at end of file
diff --git a/src/afcexplorerwidget.h b/src/afcexplorerwidget.h
index 0446586..ae59c60 100644
--- a/src/afcexplorerwidget.h
+++ b/src/afcexplorerwidget.h
@@ -43,17 +43,17 @@ private slots:
private:
QWidget *m_explorer;
QWidget *m_navWidget;
- QPushButton *m_exportBtn;
- QPushButton *m_importBtn;
- QPushButton *m_addToFavoritesBtn;
+ ZIconWidget *m_exportBtn;
+ ZIconWidget *m_importBtn;
+ ZIconWidget *m_addToFavoritesBtn;
QListWidget *m_fileList;
QStack m_history;
QStack m_forwardHistory;
int m_currentHistoryIndex;
QLineEdit *m_addressBar;
- ClickableIconWidget *m_backButton;
- ClickableIconWidget *m_forwardButton;
- ClickableIconWidget *m_enterButton;
+ ZIconWidget *m_backButton;
+ ZIconWidget *m_forwardButton;
+ ZIconWidget *m_enterButton;
iDescriptorDevice *m_device;
// Current AFC mode
@@ -73,6 +73,7 @@ private:
int import_file_to_device(afc_client_t afc, const char *device_path,
const char *local_path);
void updateNavStyles();
+ void updateButtonStates();
};
#endif // AFCEXPLORER_H
diff --git a/src/appinstalldialog.cpp b/src/appinstalldialog.cpp
index 12d0678..be15062 100644
--- a/src/appinstalldialog.cpp
+++ b/src/appinstalldialog.cpp
@@ -9,6 +9,7 @@
#include
#include
#include
+#include
#include
#include
@@ -170,8 +171,23 @@ void AppInstallDialog::onInstallClicked()
m_actionButton->deleteLater();
m_actionButton = nullptr;
- m_tempDir = QDir::tempPath();
- startDownloadProcess(m_bundleId, m_tempDir, buttonIndex, false);
+ if (m_tempDir) {
+ delete m_tempDir;
+ m_tempDir = nullptr;
+ }
+ // Create a new temporary directory for each installation
+ m_tempDir = new QTemporaryDir();
+ if (!m_tempDir->isValid()) {
+ m_statusLabel->setText("Failed to create temporary directory");
+ m_statusLabel->setStyleSheet(
+ "font-size: 14px; color: #FF3B30; padding: 5px;");
+ QMessageBox::critical(
+ this, "Error",
+ "Could not create temporary directory for download.");
+ return;
+ }
+
+ startDownloadProcess(m_bundleId, m_tempDir->path(), buttonIndex, false);
connect(this, &AppDownloadBaseDialog::downloadFinished, this,
[this, selectedDevice](bool success) {
if (success) {
@@ -182,7 +198,7 @@ void AppInstallDialog::onInstallClicked()
it.
*/
// Find the actual downloaded IPA file
- QDir outDir = m_tempDir;
+ QDir outDir(m_tempDir->path());
QStringList filters;
filters << m_bundleId + "*.ipa";
QStringList matches =
@@ -226,3 +242,11 @@ void AppInstallDialog::reject()
AppDownloadBaseDialog::reject();
}
+
+AppInstallDialog::~AppInstallDialog()
+{
+ if (m_tempDir) {
+ delete m_tempDir;
+ m_tempDir = nullptr;
+ }
+}
\ No newline at end of file
diff --git a/src/appinstalldialog.h b/src/appinstalldialog.h
index 1a522d3..982dfaa 100644
--- a/src/appinstalldialog.h
+++ b/src/appinstalldialog.h
@@ -6,6 +6,7 @@
#include
#include
#include
+#include
class AppInstallDialog : public AppDownloadBaseDialog
{
@@ -15,6 +16,7 @@ public:
const QString &description,
const QString &bundleId,
QWidget *parent = nullptr);
+ ~AppInstallDialog();
protected:
void reject() override;
@@ -27,7 +29,7 @@ private:
QString m_bundleId;
QLabel *m_statusLabel;
QFutureWatcher *m_installWatcher;
- QString m_tempDir;
+ QTemporaryDir *m_tempDir;
void updateDeviceList();
void performInstallation(const QString &ipaPath, const QString &deviceUdid);
};
diff --git a/src/appswidget.cpp b/src/appswidget.cpp
index 9516599..00da097 100644
--- a/src/appswidget.cpp
+++ b/src/appswidget.cpp
@@ -61,13 +61,7 @@ void AppsWidget::setupUI()
m_loginButton = new QPushButton();
m_searchEdit = new ZLineEdit();
- m_searchEdit->setMaximumWidth(400);
- m_searchEdit->setStyleSheet("QLineEdit { "
- " padding: 8px; "
- " border: 1px solid #ccc; "
- " border-radius: 4px; "
- " font-size: 14px; "
- "}");
+ m_searchEdit->setMaximumWidth(350);
// --- Status and Login Button ---
m_manager = AppStoreManager::sharedInstance();
@@ -86,10 +80,10 @@ void AppsWidget::setupUI()
m_statusLabel->setStyleSheet("font-size: 14px; color: #666;");
mainLayout->addWidget(headerWidget);
-
- QAction *searchAction = m_searchEdit->addAction(
- this->style()->standardIcon(QStyle::SP_FileDialogContentsView),
- QLineEdit::TrailingPosition);
+ // todo: implement theme aware icon
+ QAction *searchAction =
+ m_searchEdit->addAction(QIcon(":/resources/icons/MdiLightMagnify.png"),
+ QLineEdit::TrailingPosition);
searchAction->setToolTip("Search");
connect(searchAction, &QAction::triggered, this,
&AppsWidget::performSearch);
diff --git a/src/core/services/avahi/avahi_service.cpp b/src/core/services/avahi/avahi_service.cpp
index 54b46ba..966db94 100644
--- a/src/core/services/avahi/avahi_service.cpp
+++ b/src/core/services/avahi/avahi_service.cpp
@@ -128,7 +128,6 @@ void AvahiService::browseCallback(AvahiServiceBrowser *browser,
switch (event) {
case AVAHI_BROWSER_NEW:
- qDebug() << "Found Apple device:" << name;
if (!avahi_service_resolver_new(service->m_client, interface, protocol,
name, type, domain, AVAHI_PROTO_UNSPEC,
(AvahiLookupFlags)0, resolveCallback,
diff --git a/src/core/services/dnssd/dnssd_service.cpp b/src/core/services/dnssd/dnssd_service.cpp
index 13e1436..97c049e 100644
--- a/src/core/services/dnssd/dnssd_service.cpp
+++ b/src/core/services/dnssd/dnssd_service.cpp
@@ -100,8 +100,6 @@ void DNSSD_API DnssdService::browseCallback(
DnssdService *service = static_cast(context);
if (flags & kDNSServiceFlagsAdd) {
- qDebug() << "Found Apple device:" << serviceName;
-
// Start resolving the service
DNSServiceRef resolveRef;
DNSServiceErrorType err =
diff --git a/src/deviceimagewidget.cpp b/src/deviceimagewidget.cpp
index 3de8fc4..526aeb1 100644
--- a/src/deviceimagewidget.cpp
+++ b/src/deviceimagewidget.cpp
@@ -157,7 +157,7 @@ int DeviceImageWidget::getIosVersionFromDevice() const
/*
this method is only here to calculate the screen area
so that wallpaper perfectly fits to the screen size
- it's costy so if you want to add a new mock run
+ it's costy so if you want to add a new mockup run
through this method qDebug the result and add it to createCompositeImage
example : screenRect = QRect(152, 79, 195, 296);
*/
@@ -204,12 +204,7 @@ QRect DeviceImageWidget::findScreenArea(const QPixmap &mockup) const
bottom++;
}
- // Add a small margin to avoid drawing over the bezel anti-aliasing
- int margin = 2;
-
- return QRect(left + 1 + margin, top + 1 + margin,
- right - left - 2 - (margin * 2),
- bottom - top - 2 - (margin * 2));
+ return QRect(left + 1, top + 1, right - left - 2, bottom - top - 2);
}
QPixmap DeviceImageWidget::createCompositeImage() const
@@ -239,19 +234,19 @@ QPixmap DeviceImageWidget::createCompositeImage() const
QString::fromStdString(m_device->deviceInfo.productType));
if (mockupName == "3") {
- screenRect = QRect(152, 79, 195, 296);
+ screenRect = QRect(145, 72, 209, 310);
} else if (mockupName == "4") {
- screenRect = QRect(421, 188, 366, 534);
+ screenRect = QRect(414, 181, 380, 548);
} else if (mockupName == "5") {
- screenRect = QRect(34, 113, 290, 523);
+ screenRect = QRect(27, 106, 304, 537);
} else if (mockupName == "6") {
- screenRect = QRect(75, 355, 1265, 2256);
+ screenRect = QRect(68, 348, 1279, 2270);
} else if (mockupName == "x") {
- screenRect = QRect(252, 436, 2375, 4989);
+ screenRect = QRect(245, 429, 2389, 5003);
} else if (mockupName == "15") {
- screenRect = QRect(22, 56, 323, 674);
+ screenRect = QRect(15, 49, 337, 688);
} else if (mockupName == "16") {
- screenRect = QRect(24, 61, 319, 668);
+ screenRect = QRect(17, 54, 333, 682);
} else {
// Fallback for unknown devices
screenRect = QRect(mockup.width() * 0.12, mockup.height() * 0.08,
diff --git a/src/deviceinfowidget.cpp b/src/deviceinfowidget.cpp
index 10ecc47..33644dc 100644
--- a/src/deviceinfowidget.cpp
+++ b/src/deviceinfowidget.cpp
@@ -54,23 +54,23 @@ DeviceInfoWidget::DeviceInfoWidget(iDescriptorDevice *device, QWidget *parent)
actionsLayout->setContentsMargins(1, 1, 1, 1);
actionsLayout->setSpacing(10);
- ClickableIconWidget *shutdownBtn = new ClickableIconWidget(
+ ZIconWidget *shutdownBtn = new ZIconWidget(
QIcon(":/resources/icons/IcOutlinePowerSettingsNew.png"), "Shutdown",
this);
shutdownBtn->setIconSize(QSize(20, 20));
- connect(shutdownBtn, &ClickableIconWidget::clicked, this,
+ connect(shutdownBtn, &ZIconWidget::clicked, this,
[device]() { ToolboxWidget::shutdownDevice(device); });
- ClickableIconWidget *restartBtn = new ClickableIconWidget(
+ ZIconWidget *restartBtn = new ZIconWidget(
QIcon(":/resources/icons/IcTwotoneRestartAlt.png"), "Restart", this);
restartBtn->setIconSize(QSize(20, 20));
- connect(restartBtn, &ClickableIconWidget::clicked, this,
+ connect(restartBtn, &ZIconWidget::clicked, this,
[device]() { ToolboxWidget::restartDevice(device); });
- ClickableIconWidget *recoveryBtn = new ClickableIconWidget(
+ ZIconWidget *recoveryBtn = new ZIconWidget(
QIcon(":/resources/icons/HugeiconsWrench01.png"), "Recovery", this);
recoveryBtn->setIconSize(QSize(20, 20));
- connect(recoveryBtn, &ClickableIconWidget::clicked, this,
+ connect(recoveryBtn, &ZIconWidget::clicked, this,
[device]() { ToolboxWidget::_enterRecoveryMode(device); });
actionsLayout->addWidget(shutdownBtn);
diff --git a/src/fileexportdialog.cpp b/src/fileexportdialog.cpp
index 69d428b..287df4c 100644
--- a/src/fileexportdialog.cpp
+++ b/src/fileexportdialog.cpp
@@ -1,5 +1,6 @@
#include "fileexportdialog.h"
#include
+#include
#include
#include
#include
@@ -8,9 +9,10 @@
#include
// TODO: needs progress bar improvements
-FileExportDialog::FileExportDialog(QWidget *parent)
+FileExportDialog::FileExportDialog(const QString &exportDir, QWidget *parent)
: QDialog(parent), m_progressBar(nullptr), m_statusLabel(nullptr),
- m_fileLabel(nullptr), m_cancelButton(nullptr), m_layout(nullptr)
+ m_fileLabel(nullptr), m_cancelButton(nullptr), m_layout(nullptr),
+ m_exportDir(exportDir)
{
setupUI();
}
@@ -136,9 +138,17 @@ void FileExportDialog::showCompletionMessage(int successful, int failed)
QString message;
if (failed == 0) {
- message =
- QString("Successfully exported all %1 files!").arg(successful);
- QMessageBox::information(parentWidget(), "Export Complete", message);
+ // ASK USER TO OPEN FOLDER
+ QMessageBox::StandardButton reply;
+ reply = QMessageBox::question(
+ parentWidget(), "Export Complete",
+ QString("Successfully exported all %1 files! Would you like to "
+ "open the output folder ?")
+ .arg(successful),
+ QMessageBox::Yes | QMessageBox::No);
+ if (reply == QMessageBox::Yes) {
+ QDesktopServices::openUrl(QUrl::fromLocalFile(m_exportDir));
+ }
} else {
message =
QString("Export completed with %1 successful and %2 failed files.")
diff --git a/src/fileexportdialog.h b/src/fileexportdialog.h
index ed8e924..ded27fd 100644
--- a/src/fileexportdialog.h
+++ b/src/fileexportdialog.h
@@ -15,7 +15,8 @@ class FileExportDialog : public QDialog
Q_OBJECT
public:
- explicit FileExportDialog(QWidget *parent = nullptr);
+ explicit FileExportDialog(const QString &exportDir,
+ QWidget *parent = nullptr);
~FileExportDialog() override;
public slots:
@@ -38,6 +39,7 @@ private:
QLabel *m_fileLabel;
QPushButton *m_cancelButton;
QVBoxLayout *m_layout;
+ QString m_exportDir;
};
#endif // FILEEXPORTDIALOG_H
\ No newline at end of file
diff --git a/src/gallerywidget.cpp b/src/gallerywidget.cpp
index b43634a..ca75661 100644
--- a/src/gallerywidget.cpp
+++ b/src/gallerywidget.cpp
@@ -4,6 +4,7 @@
#include "mediapreviewdialog.h"
#include "photoexportmanager.h"
#include "photomodel.h"
+#include "servicemanager.h"
#include
#include
#include
@@ -19,7 +20,6 @@
#include
#include
#include
-#include "servicemanager.h"
void GalleryWidget::load()
{
@@ -209,7 +209,7 @@ void GalleryWidget::onExportSelected()
// Create export dialog and connect signals
// todo:cleanup
- auto *exportDialog = new FileExportDialog(this);
+ auto *exportDialog = new FileExportDialog(exportDir, this);
// Connect PhotoExportManager signals to FileExportDialog
connect(m_exportManager, &PhotoExportManager::exportStarted, exportDialog,
@@ -267,7 +267,7 @@ void GalleryWidget::onExportAll()
// Create export dialog and connect signals
// todo:cleanup
- auto *exportDialog = new FileExportDialog(this);
+ auto *exportDialog = new FileExportDialog(exportDir, this);
// Connect PhotoExportManager signals to FileExportDialog
connect(m_exportManager, &PhotoExportManager::exportStarted, exportDialog,
@@ -399,8 +399,7 @@ void GalleryWidget::loadAlbumList()
{
// Get DCIM directory contents
qDebug() << "Loading album list from /DCIM";
- AFCFileTree dcimTree =
- ServiceManager::safeGetFileTree(m_device, "/DCIM");
+ AFCFileTree dcimTree = ServiceManager::safeGetFileTree(m_device, "/DCIM");
if (!dcimTree.success) {
qDebug() << "Failed to read DCIM directory";
diff --git a/src/iDescriptor-ui.h b/src/iDescriptor-ui.h
index f8ccf13..923609c 100644
--- a/src/iDescriptor-ui.h
+++ b/src/iDescriptor-ui.h
@@ -61,12 +61,12 @@ protected:
}
};
-class ClickableIconWidget : public QWidget
+class ZIconWidget : public QWidget
{
Q_OBJECT
public:
- ClickableIconWidget(const QIcon &icon, const QString &tooltip,
- QWidget *parent = nullptr)
+ ZIconWidget(const QIcon &icon, const QString &tooltip,
+ QWidget *parent = nullptr)
: QWidget(parent), m_icon(icon), m_iconSize(24, 24), m_pressed(false)
{
setToolTip(tooltip);
@@ -190,6 +190,7 @@ enum class iDescriptorTool {
TouchIdTest,
FaceIdTest,
UnmountDevImage,
+ NetworkDevices,
Unknown,
iFuse
};
diff --git a/src/installedappswidget.cpp b/src/installedappswidget.cpp
index 8b5041f..567db6d 100644
--- a/src/installedappswidget.cpp
+++ b/src/installedappswidget.cpp
@@ -24,7 +24,7 @@
AppTabWidget::AppTabWidget(const QString &appName, const QString &bundleId,
const QString &version, QWidget *parent)
: QGroupBox(parent), m_appName(appName), m_bundleId(bundleId),
- m_version(version), m_selected(false), m_hovered(false)
+ m_version(version), m_selected(false)
{
setFixedHeight(60);
setMinimumWidth(100);
@@ -122,41 +122,22 @@ void AppTabWidget::mousePressEvent(QMouseEvent *event)
emit clicked();
}
-void AppTabWidget::enterEvent(QEnterEvent *event)
-{
- Q_UNUSED(event)
- m_hovered = true;
- updateStyles();
-}
-
-void AppTabWidget::leaveEvent(QEvent *event)
-{
- Q_UNUSED(event)
- m_hovered = false;
- updateStyles();
-}
-
void AppTabWidget::updateStyles()
{
- // QStyleHints::colorScheme()
- QString borderStyle;
- // QColor bgColor = qApp->palette().color(QPalette::Window);
+ QString style;
QColor bgColor = isDarkMode() ? qApp->palette().color(QPalette::Light)
: qApp->palette().color(QPalette::Dark);
- qDebug() << styleSheet();
if (m_selected) {
- borderStyle = "QGroupBox { background-color: " +
- qApp->palette().color(QPalette::Highlight).name() +
- "; border-radius: "
- "10px; border : 1px solid " +
- bgColor.lighter().name() + "; }";
+ style = "QGroupBox { background-color: " + COLOR_ACCENT_BLUE.name() +
+ "; border-radius: "
+ "10px; border : 1px solid " +
+ bgColor.lighter().name() + "; }";
} else {
- borderStyle = "QGroupBox { background-color: " + bgColor.name() +
- "; border-radius: 10px; border: 1px solid " +
- bgColor.lighter().name() + "; }";
+ style = "QGroupBox { background-color: " + bgColor.name() +
+ "; border-radius: 10px; border: 1px solid " +
+ bgColor.lighter().name() + "; }";
}
- // update();
- setStyleSheet(borderStyle);
+ setStyleSheet(style);
}
InstalledAppsWidget::InstalledAppsWidget(iDescriptorDevice *device,
@@ -547,7 +528,6 @@ void InstalledAppsWidget::createAppTab(const QString &appName,
new AppTabWidget(appName, bundleId, version, this);
connect(tabWidget, &AppTabWidget::clicked, this,
&InstalledAppsWidget::onAppTabClicked);
- m_appTabs.append(tabWidget);
// Remove the stretch before adding the new tab
m_tabLayout->removeItem(m_tabLayout->itemAt(m_tabLayout->count() - 1));
diff --git a/src/installedappswidget.h b/src/installedappswidget.h
index a01d302..8799cee 100644
--- a/src/installedappswidget.h
+++ b/src/installedappswidget.h
@@ -45,8 +45,6 @@ signals:
protected:
void mousePressEvent(QMouseEvent *event) override;
- void enterEvent(QEnterEvent *event) override;
- void leaveEvent(QEvent *event) override;
private:
void fetchAppIcon();
@@ -56,7 +54,6 @@ private:
QString m_bundleId;
QString m_version;
bool m_selected = false;
- bool m_hovered = false;
QLabel *m_iconLabel;
QLabel *m_nameLabel;
diff --git a/src/jailbrokenwidget.cpp b/src/jailbrokenwidget.cpp
index 29a5fa1..ccd7b74 100644
--- a/src/jailbrokenwidget.cpp
+++ b/src/jailbrokenwidget.cpp
@@ -22,6 +22,7 @@
#include
#include
+// TODO: theming is broken
JailbrokenWidget::JailbrokenWidget(QWidget *parent) : QWidget{parent}
{
QHBoxLayout *mainLayout = new QHBoxLayout(this);
diff --git a/src/networkdeviceswidget.cpp b/src/networkdeviceswidget.cpp
new file mode 100644
index 0000000..2154d34
--- /dev/null
+++ b/src/networkdeviceswidget.cpp
@@ -0,0 +1,230 @@
+#include "networkdeviceswidget.h"
+
+#ifdef __linux__
+#include "core/services/avahi/avahi_service.h"
+#else
+#include "core/services/dnssd/dnssd_service.h"
+#endif
+
+#include
+#include
+#include
+#include
+#include
+
+NetworkDevicesWidget::NetworkDevicesWidget(QWidget *parent) : QWidget(parent)
+{
+ setWindowTitle("Network Devices - iDescriptor");
+ setupUI();
+
+#ifdef __linux__
+ m_networkProvider = new AvahiService(this);
+ connect(m_networkProvider, &AvahiService::deviceAdded, this,
+ &NetworkDevicesWidget::onWirelessDeviceAdded);
+ connect(m_networkProvider, &AvahiService::deviceRemoved, this,
+ &NetworkDevicesWidget::onWirelessDeviceRemoved);
+#else
+ m_networkProvider = new DnssdService(this);
+ connect(m_networkProvider, &DnssdService::deviceAdded, this,
+ &NetworkDevicesWidget::onWirelessDeviceAdded);
+ connect(m_networkProvider, &DnssdService::deviceRemoved, this,
+ &NetworkDevicesWidget::onWirelessDeviceRemoved);
+#endif
+
+ // Start scanning for network devices
+ m_networkProvider->startBrowsing();
+
+ // Initial device list update
+ updateDeviceList();
+}
+
+NetworkDevicesWidget::~NetworkDevicesWidget()
+{
+ if (m_networkProvider) {
+ m_networkProvider->stopBrowsing();
+ }
+}
+
+void NetworkDevicesWidget::setupUI()
+{
+ QVBoxLayout *mainLayout = new QVBoxLayout(this);
+ mainLayout->setContentsMargins(10, 10, 10, 10);
+ mainLayout->setSpacing(10);
+
+ // Status label
+ m_statusLabel = new QLabel("Scanning for network devices...");
+ QFont statusFont = m_statusLabel->font();
+ statusFont.setPointSize(12);
+ statusFont.setWeight(QFont::Medium);
+ m_statusLabel->setFont(statusFont);
+ m_statusLabel->setAlignment(Qt::AlignCenter);
+ mainLayout->addWidget(m_statusLabel);
+
+ // Device group
+ m_deviceGroup = new QGroupBox("Network Devices");
+ QFont groupFont = m_deviceGroup->font();
+ groupFont.setPointSize(14);
+ groupFont.setWeight(QFont::Bold);
+ m_deviceGroup->setFont(groupFont);
+
+ QVBoxLayout *groupLayout = new QVBoxLayout(m_deviceGroup);
+ groupLayout->setContentsMargins(5, 15, 5, 5);
+ groupLayout->setSpacing(0);
+
+ // Scroll area
+ m_scrollArea = new QScrollArea();
+ m_scrollArea->setWidgetResizable(true);
+ m_scrollArea->setMinimumHeight(200);
+ m_scrollArea->setMaximumHeight(400);
+ m_scrollArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
+ m_scrollArea->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded);
+ m_scrollArea->setStyleSheet(
+ "QScrollArea { background: transparent; border: none; }");
+ /* FIXME: We need a better approach to theme awareness */
+ connect(qApp, &QApplication::paletteChanged, this, [this]() {
+ m_scrollArea->setStyleSheet(
+ "QScrollArea { background: transparent; border: none; }");
+ });
+
+ // Scroll content
+ m_scrollContent = new QWidget();
+ m_deviceLayout = new QVBoxLayout(m_scrollContent);
+ m_deviceLayout->setContentsMargins(5, 5, 5, 5);
+ m_deviceLayout->setSpacing(8);
+ m_deviceLayout->addStretch();
+
+ m_scrollArea->setWidget(m_scrollContent);
+ groupLayout->addWidget(m_scrollArea);
+
+ mainLayout->addWidget(m_deviceGroup);
+ mainLayout->addStretch();
+}
+
+void NetworkDevicesWidget::createDeviceCard(const NetworkDevice &device)
+{
+ // Main card frame
+ QWidget *card = new QWidget();
+
+ QVBoxLayout *cardLayout = new QVBoxLayout(card);
+ cardLayout->setContentsMargins(12, 10, 12, 10);
+ cardLayout->setSpacing(4);
+
+ // Device name (primary)
+ QLabel *nameLabel = new QLabel(device.name);
+ nameLabel->setWordWrap(true);
+ QFont nameFont = nameLabel->font();
+ nameFont.setPointSize(13);
+ nameFont.setWeight(QFont::Medium);
+ nameLabel->setFont(nameFont);
+ QPalette namePalette = nameLabel->palette();
+ namePalette.setColor(QPalette::WindowText,
+ palette().color(QPalette::WindowText));
+ nameLabel->setPalette(namePalette);
+
+ // Device info container
+ QWidget *infoContainer = new QWidget();
+ QHBoxLayout *infoLayout = new QHBoxLayout(infoContainer);
+ infoLayout->setContentsMargins(0, 0, 0, 0);
+ infoLayout->setSpacing(12);
+
+ // Address info
+ QLabel *addressLabel = new QLabel(QString("IP: %1").arg(device.address));
+ QFont addressFont = addressLabel->font();
+ addressFont.setPointSize(11);
+ addressLabel->setFont(addressFont);
+ QPalette addressPalette = addressLabel->palette();
+ QColor secondaryColor = palette().color(QPalette::WindowText);
+ secondaryColor.setAlpha(180);
+ addressPalette.setColor(QPalette::WindowText, secondaryColor);
+ addressLabel->setPalette(addressPalette);
+
+ // Port info
+ QLabel *portLabel = new QLabel(QString("Port: %1").arg(device.port));
+ portLabel->setFont(addressFont);
+ portLabel->setPalette(addressPalette);
+
+ infoLayout->addWidget(addressLabel);
+ infoLayout->addWidget(portLabel);
+ infoLayout->addStretch();
+
+ // Status indicator
+ QLabel *statusIndicator = new QLabel("●");
+ QFont statusFont = statusIndicator->font();
+ statusFont.setPointSize(12);
+ statusIndicator->setFont(statusFont);
+ QPalette statusPalette = statusIndicator->palette();
+ statusPalette.setColor(QPalette::WindowText,
+ QColor(52, 199, 89)); // iOS green
+ statusIndicator->setPalette(statusPalette);
+
+ infoLayout->addWidget(statusIndicator);
+
+ cardLayout->addWidget(nameLabel);
+ cardLayout->addWidget(infoContainer);
+
+ // Store the device info as property for later removal
+ card->setProperty("deviceName", device.name);
+ card->setProperty("deviceAddress", device.address);
+
+ // Insert before the stretch
+ m_deviceLayout->insertWidget(m_deviceLayout->count() - 1, card);
+ m_deviceCards.append(card);
+}
+
+void NetworkDevicesWidget::clearDeviceCards()
+{
+ for (QWidget *card : m_deviceCards) {
+ card->deleteLater();
+ }
+ m_deviceCards.clear();
+}
+
+void NetworkDevicesWidget::updateDeviceList()
+{
+ clearDeviceCards();
+
+ QList devices = m_networkProvider->getNetworkDevices();
+
+ if (devices.isEmpty()) {
+ m_statusLabel->setText("No network devices found");
+ } else {
+ m_statusLabel->setText(
+ QString("Found %1 network device(s)").arg(devices.count()));
+
+ for (const NetworkDevice &device : devices) {
+ createDeviceCard(device);
+ }
+ }
+}
+
+void NetworkDevicesWidget::onWirelessDeviceAdded(const NetworkDevice &device)
+{
+ createDeviceCard(device);
+
+ // Update status
+ int deviceCount = m_deviceCards.count();
+ m_statusLabel->setText(
+ QString("Found %1 network device(s)").arg(deviceCount));
+}
+
+void NetworkDevicesWidget::onWirelessDeviceRemoved(const QString &deviceName)
+{
+ // Find and remove the corresponding card
+ for (int i = 0; i < m_deviceCards.count(); ++i) {
+ QWidget *card = m_deviceCards[i];
+ if (card->property("deviceName").toString() == deviceName) {
+ m_deviceCards.removeAt(i);
+ card->deleteLater();
+ break;
+ }
+ }
+
+ // Update status
+ int deviceCount = m_deviceCards.count();
+ if (deviceCount == 0) {
+ m_statusLabel->setText("No network devices found");
+ } else {
+ m_statusLabel->setText(
+ QString("Found %1 network device(s)").arg(deviceCount));
+ }
+}
\ No newline at end of file
diff --git a/src/networkdeviceswidget.h b/src/networkdeviceswidget.h
new file mode 100644
index 0000000..bdd7898
--- /dev/null
+++ b/src/networkdeviceswidget.h
@@ -0,0 +1,49 @@
+#ifndef NETWORKDEVICESWIDGET_H
+#define NETWORKDEVICESWIDGET_H
+
+#ifdef __linux__
+#include "core/services/avahi/avahi_service.h"
+#else
+#include "core/services/dnssd/dnssd_service.h"
+#endif
+
+#include
+#include
+#include
+#include
+#include
+
+class NetworkDevicesWidget : public QWidget
+{
+ Q_OBJECT
+
+public:
+ explicit NetworkDevicesWidget(QWidget *parent = nullptr);
+ ~NetworkDevicesWidget();
+
+private slots:
+ void onWirelessDeviceAdded(const NetworkDevice &device);
+ void onWirelessDeviceRemoved(const QString &deviceName);
+
+private:
+ void setupUI();
+ void createDeviceCard(const NetworkDevice &device);
+ void clearDeviceCards();
+ void updateDeviceList();
+
+ QGroupBox *m_deviceGroup = nullptr;
+ QScrollArea *m_scrollArea = nullptr;
+ QWidget *m_scrollContent = nullptr;
+ QVBoxLayout *m_deviceLayout = nullptr;
+ QLabel *m_statusLabel = nullptr;
+
+#ifdef __linux__
+ AvahiService *m_networkProvider = nullptr;
+#else
+ DnssdService *m_networkProvider = nullptr;
+#endif
+
+ QList m_deviceCards;
+};
+
+#endif // NETWORKDEVICESWIDGET_H
\ No newline at end of file
diff --git a/src/toolboxwidget.cpp b/src/toolboxwidget.cpp
index 2d1b60d..d0960fb 100644
--- a/src/toolboxwidget.cpp
+++ b/src/toolboxwidget.cpp
@@ -141,6 +141,9 @@ void ToolboxWidget::setupUI()
"Mount your iPhone's filesystem on your PC", true, ""});
toolWidgets.append({iDescriptorTool::CableInfoWidget,
"View detailed cable and connection info", true, ""});
+ toolWidgets.append({iDescriptorTool::NetworkDevices,
+ "Discover and monitor devices on your network", false,
+ ""});
for (int i = 0; i < toolWidgets.size(); ++i) {
const auto &tool = toolWidgets[i];
@@ -225,6 +228,9 @@ ClickableWidget *ToolboxWidget::createToolbox(iDescriptorTool tool,
case iDescriptorTool::UnmountDevImage:
title = "Unmount Dev Image";
break;
+ case iDescriptorTool::NetworkDevices:
+ title = "Network Devices";
+ break;
default:
title = "Unknown Tool";
break;
@@ -431,6 +437,21 @@ void ToolboxWidget::onToolboxClicked(iDescriptorTool tool)
cableInfoWidget->resize(600, 400);
cableInfoWidget->show();
} break;
+ case iDescriptorTool::NetworkDevices: {
+ // single instance lock
+ if (!m_networkDevicesWidget) {
+ m_networkDevicesWidget = new NetworkDevicesWidget();
+ m_networkDevicesWidget->setAttribute(Qt::WA_DeleteOnClose);
+ m_networkDevicesWidget->setWindowFlag(Qt::Window);
+ m_networkDevicesWidget->resize(500, 600);
+ connect(m_networkDevicesWidget, &QObject::destroyed, this,
+ [this]() { m_networkDevicesWidget = nullptr; });
+ m_networkDevicesWidget->show();
+ } else {
+ m_networkDevicesWidget->raise();
+ m_networkDevicesWidget->activateWindow();
+ }
+ } break;
default:
qDebug() << "Clicked on unimplemented tool";
break;
diff --git a/src/toolboxwidget.h b/src/toolboxwidget.h
index 5bf98f5..123529c 100644
--- a/src/toolboxwidget.h
+++ b/src/toolboxwidget.h
@@ -4,6 +4,7 @@
#include "devdiskimageswidget.h"
#include "iDescriptor-ui.h"
#include "iDescriptor.h"
+#include "networkdeviceswidget.h"
#include
#include
#include
@@ -42,7 +43,8 @@ private:
QList m_requiresDevice;
iDescriptorDevice *m_currentDevice;
std::string m_uuid;
- DevDiskImagesWidget *m_devDiskImagesWidget;
+ DevDiskImagesWidget *m_devDiskImagesWidget = nullptr;
+ NetworkDevicesWidget *m_networkDevicesWidget = nullptr;
signals:
};
diff --git a/src/zlineedit.cpp b/src/zlineedit.cpp
index 05ed44d..cd4aca2 100644
--- a/src/zlineedit.cpp
+++ b/src/zlineedit.cpp
@@ -1,4 +1,5 @@
#include "zlineedit.h"
+#include "iDescriptor-ui.h"
ZLineEdit::ZLineEdit(QWidget *parent) : QLineEdit(parent) { setupStyles(); }
@@ -29,7 +30,7 @@ void ZLineEdit::updateStyles()
"} "
"QLineEdit:focus { "
" border: 2px solid " +
- qApp->palette().color(QPalette::Highlight).name() +
+ COLOR_ACCENT_BLUE.name() +
"; "
" outline: none; "
"}");