diff --git a/src/gui/macutilities.h b/src/gui/macutilities.h index b9eda325f..cbf00bc09 100644 --- a/src/gui/macutilities.h +++ b/src/gui/macutilities.h @@ -33,8 +33,12 @@ #include #include -QPixmap pixmapForExtension(const QString &ext, const QSize &size); -void overrideDockClickHandler(bool (*dockClickHandler)(id, SEL, ...)); -void displayNotification(const QString &title, const QString &message); +namespace MacUtils +{ + QPixmap pixmapForExtension(const QString &ext, const QSize &size); + void overrideDockClickHandler(bool (*dockClickHandler)(id, SEL, ...)); + void displayNotification(const QString &title, const QString &message); + void openFiles(const QSet &pathsList); +} #endif // MACUTILITIES_H diff --git a/src/gui/macutilities.mm b/src/gui/macutilities.mm index 2a6b0c6f6..5a8e81aeb 100644 --- a/src/gui/macutilities.mm +++ b/src/gui/macutilities.mm @@ -28,56 +28,72 @@ #include "macutilities.h" +#include #include #include #import -QPixmap pixmapForExtension(const QString &ext, const QSize &size) +namespace MacUtils { - @autoreleasepool { - NSImage *image = [[NSWorkspace sharedWorkspace] iconForFileType:ext.toNSString()]; - if (image) { - NSRect rect = NSMakeRect(0, 0, size.width(), size.height()); - CGImageRef cgImage = [image CGImageForProposedRect:&rect context:nil hints:nil]; - return QtMac::fromCGImageRef(cgImage); + QPixmap pixmapForExtension(const QString &ext, const QSize &size) + { + @autoreleasepool { + NSImage *image = [[NSWorkspace sharedWorkspace] iconForFileType:ext.toNSString()]; + if (image) { + NSRect rect = NSMakeRect(0, 0, size.width(), size.height()); + CGImageRef cgImage = [image CGImageForProposedRect:&rect context:nil hints:nil]; + return QtMac::fromCGImageRef(cgImage); + } + + return QPixmap(); } - - return QPixmap(); } -} -void overrideDockClickHandler(bool (*dockClickHandler)(id, SEL, ...)) -{ - NSApplication *appInst = [NSApplication sharedApplication]; - - if (!appInst) - return; - - Class delClass = [[appInst delegate] class]; - SEL shouldHandle = sel_registerName("applicationShouldHandleReopen:hasVisibleWindows:"); - - if (class_getInstanceMethod(delClass, shouldHandle)) { - if (class_replaceMethod(delClass, shouldHandle, (IMP)dockClickHandler, "B@:")) - qDebug("Registered dock click handler (replaced original method)"); - else - qWarning("Failed to replace method for dock click handler"); + void overrideDockClickHandler(bool (*dockClickHandler)(id, SEL, ...)) + { + NSApplication *appInst = [NSApplication sharedApplication]; + + if (!appInst) + return; + + Class delClass = [[appInst delegate] class]; + SEL shouldHandle = sel_registerName("applicationShouldHandleReopen:hasVisibleWindows:"); + + if (class_getInstanceMethod(delClass, shouldHandle)) { + if (class_replaceMethod(delClass, shouldHandle, (IMP)dockClickHandler, "B@:")) + qDebug("Registered dock click handler (replaced original method)"); + else + qWarning("Failed to replace method for dock click handler"); + } + else { + if (class_addMethod(delClass, shouldHandle, (IMP)dockClickHandler, "B@:")) + qDebug("Registered dock click handler"); + else + qWarning("Failed to register dock click handler"); + } } - else { - if (class_addMethod(delClass, shouldHandle, (IMP)dockClickHandler, "B@:")) - qDebug("Registered dock click handler"); - else - qWarning("Failed to register dock click handler"); + + void displayNotification(const QString &title, const QString &message) + { + @autoreleasepool { + NSUserNotification *notification = [[NSUserNotification alloc] init]; + notification.title = title.toNSString(); + notification.informativeText = message.toNSString(); + notification.soundName = NSUserNotificationDefaultSoundName; + + [[NSUserNotificationCenter defaultUserNotificationCenter] deliverNotification:notification]; + } } -} -void displayNotification(const QString &title, const QString &message) -{ - @autoreleasepool { - NSUserNotification *notification = [[NSUserNotification alloc] init]; - notification.title = title.toNSString(); - notification.informativeText = message.toNSString(); - notification.soundName = NSUserNotificationDefaultSoundName; + void openFiles(const QSet &pathsList) + { + @autoreleasepool { + NSMutableArray *pathURLs = [NSMutableArray arrayWithCapacity:pathsList.size()]; + + for (const auto &path : pathsList) + [pathURLs addObject:[NSURL fileURLWithPath:path.toNSString()]]; - [[NSUserNotificationCenter defaultUserNotificationCenter] deliverNotification:notification]; + [[NSWorkspace sharedWorkspace] activateFileViewerSelectingURLs:pathURLs]; + } } } diff --git a/src/gui/mainwindow.cpp b/src/gui/mainwindow.cpp index 431fd7904..790afbeca 100644 --- a/src/gui/mainwindow.cpp +++ b/src/gui/mainwindow.cpp @@ -1298,7 +1298,7 @@ static bool dockClickHandler(id self, SEL cmd, ...) void MainWindow::setupDockClickHandler() { dockMainWindowHandle = this; - overrideDockClickHandler(dockClickHandler); + MacUtils::overrideDockClickHandler(dockClickHandler); } #endif @@ -1557,7 +1557,7 @@ void MainWindow::showNotificationBaloon(QString title, QString msg) const if (!reply.isError()) return; #elif defined(Q_OS_MAC) - displayNotification(title, msg); + MacUtils::displayNotification(title, msg); #else if (m_systrayIcon && QSystemTrayIcon::supportsMessages()) m_systrayIcon->showMessage(title, msg, QSystemTrayIcon::Information, TIME_TRAY_BALLOON); diff --git a/src/gui/properties/propertieswidget.cpp b/src/gui/properties/propertieswidget.cpp index 4f6384da0..2e6fcb625 100644 --- a/src/gui/properties/propertieswidget.cpp +++ b/src/gui/properties/propertieswidget.cpp @@ -65,6 +65,10 @@ #include "ui_propertieswidget.h" +#ifdef Q_OS_MAC +#include "macutilities.h" +#endif + PropertiesWidget::PropertiesWidget(QWidget *parent, MainWindow *mainWindow, TransferListWidget *transferList) : QWidget(parent) , m_ui(new Ui::PropertiesWidget()) @@ -574,10 +578,14 @@ void PropertiesWidget::openFolder(const QModelIndex &index, bool containingFolde // Flush data m_torrent->flushCache(); +#ifdef Q_OS_MAC + MacUtils::openFiles(QSet{absolutePath}); +#else if (containingFolder) Utils::Misc::openFolderSelect(absolutePath); else Utils::Misc::openPath(absolutePath); +#endif } void PropertiesWidget::displayFilesListMenu(const QPoint &) diff --git a/src/gui/torrentcontentmodel.cpp b/src/gui/torrentcontentmodel.cpp index 48a05ec9d..9130c86b7 100644 --- a/src/gui/torrentcontentmodel.cpp +++ b/src/gui/torrentcontentmodel.cpp @@ -150,7 +150,7 @@ namespace { QPixmap pixmapForExtension(const QString &ext) const override { - return ::pixmapForExtension(ext, QSize(32, 32)); + return MacUtils::pixmapForExtension(ext, QSize(32, 32)); } }; #else diff --git a/src/gui/transferlistwidget.cpp b/src/gui/transferlistwidget.cpp index 756588b23..df965096b 100644 --- a/src/gui/transferlistwidget.cpp +++ b/src/gui/transferlistwidget.cpp @@ -62,6 +62,10 @@ #include "transferlistsortmodel.h" #include "updownratiodlg.h" +#ifdef Q_OS_MAC +#include "macutilities.h" +#endif + namespace { using ToggleFn = std::function; @@ -358,10 +362,14 @@ void TransferListWidget::torrentDoubleClicked() torrent->pause(); break; case OPEN_DEST: +#ifdef Q_OS_MAC + MacUtils::openFiles(QSet{torrent->contentPath(true)}); +#else if (torrent->filesCount() == 1) Utils::Misc::openFolderSelect(torrent->contentPath(true)); else Utils::Misc::openPath(torrent->contentPath(true)); +#endif break; } } @@ -548,6 +556,15 @@ void TransferListWidget::hidePriorityColumn(bool hide) void TransferListWidget::openSelectedTorrentsFolder() const { QSet pathsList; +#ifdef Q_OS_MAC + // On macOS you expect both the files and folders to be opened in their parent + // folders prehilighted for opening, so we use a custom method. + foreach (BitTorrent::TorrentHandle *const torrent, getSelectedTorrents()) { + QString path = torrent->contentPath(true); + pathsList.insert(path); + } + MacUtils::openFiles(pathsList); +#else foreach (BitTorrent::TorrentHandle *const torrent, getSelectedTorrents()) { QString path = torrent->contentPath(true); if (!pathsList.contains(path)) { @@ -558,6 +575,7 @@ void TransferListWidget::openSelectedTorrentsFolder() const } pathsList.insert(path); } +#endif } void TransferListWidget::previewSelectedTorrents()