diff --git a/macxconf.pri b/macxconf.pri index 09dd13a82..fd0d55e0f 100644 --- a/macxconf.pri +++ b/macxconf.pri @@ -9,7 +9,7 @@ exists($$OUT_PWD/../conf.pri) { include(conf.pri) } -LIBS += -framework Carbon -framework IOKit +LIBS += -framework Carbon -framework IOKit -framework AppKit QT_LANG_PATH = ../dist/qt-translations DIST_PATH = ../dist/mac diff --git a/src/base/CMakeLists.txt b/src/base/CMakeLists.txt index 9ff9521ed..804b68798 100644 --- a/src/base/CMakeLists.txt +++ b/src/base/CMakeLists.txt @@ -141,5 +141,6 @@ endif () if (APPLE) find_library(IOKit_LIBRARY IOKit) find_library(Carbon_LIBRARY Carbon) - target_link_libraries(qbt_base PRIVATE ${Carbon_LIBRARY} ${IOKit_LIBRARY}) + find_library(AppKit_LIBRARY AppKit) + target_link_libraries(qbt_base PRIVATE ${Carbon_LIBRARY} ${IOKit_LIBRARY} ${AppKit_LIBRARY}) endif (APPLE) diff --git a/src/gui/CMakeLists.txt b/src/gui/CMakeLists.txt index 3b9f18838..c17fee4b8 100644 --- a/src/gui/CMakeLists.txt +++ b/src/gui/CMakeLists.txt @@ -118,6 +118,11 @@ transferlistwidget.cpp updownratiodlg.cpp ) +if (APPLE) + list(APPEND QBT_GUI_HEADERS macutilities.h) + list(APPEND QBT_GUI_SOURCES macutilities.mm) +endif (APPLE) + if (WIN32 OR APPLE) list(APPEND QBT_GUI_HEADERS programupdater.h) list(APPEND QBT_GUI_SOURCES programupdater.cpp) diff --git a/src/gui/gui.pri b/src/gui/gui.pri index 889a076c8..81233251e 100644 --- a/src/gui/gui.pri +++ b/src/gui/gui.pri @@ -121,6 +121,11 @@ win32|macx { SOURCES += $$PWD/programupdater.cpp } +macx { + HEADERS += $$PWD/macutilities.h + OBJECTIVE_SOURCES += $$PWD/macutilities.mm +} + FORMS += \ $$PWD/mainwindow.ui \ $$PWD/about.ui \ diff --git a/src/gui/macutilities.h b/src/gui/macutilities.h new file mode 100644 index 000000000..01356d77d --- /dev/null +++ b/src/gui/macutilities.h @@ -0,0 +1,39 @@ +/* + * Bittorrent Client using Qt and libtorrent. + * Copyright (C) 2017 Brian Kendall + * + * 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; either version 2 + * 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * In addition, as a special exception, the copyright holders give permission to + * link this program with the OpenSSL project's "OpenSSL" library (or with + * modified versions of it that use the same license as the "OpenSSL" library), + * and distribute the linked executables. You must obey the GNU General Public + * License in all respects for all of the code used other than "OpenSSL". If you + * modify file(s), you may extend this exception to your version of the file(s), + * but you are not obligated to do so. If you do not wish to do so, delete this + * exception statement from your version. + */ + +#ifndef MACUTILITIES_H +#define MACUTILITIES_H + +#include +#include +#include + +QPixmap pixmapForExtension(const QString &ext, const QSize &size); +void overrideDockClickHandler(bool (*dockClickHandler)(id, SEL, ...)); + +#endif // MACUTILITIES_H diff --git a/src/gui/macutilities.mm b/src/gui/macutilities.mm new file mode 100644 index 000000000..358eef7a1 --- /dev/null +++ b/src/gui/macutilities.mm @@ -0,0 +1,71 @@ +/* + * Bittorrent Client using Qt and libtorrent. + * Copyright (C) 2017 Brian Kendall + * + * 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; either version 2 + * 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * In addition, as a special exception, the copyright holders give permission to + * link this program with the OpenSSL project's "OpenSSL" library (or with + * modified versions of it that use the same license as the "OpenSSL" library), + * and distribute the linked executables. You must obey the GNU General Public + * License in all respects for all of the code used other than "OpenSSL". If you + * modify file(s), you may extend this exception to your version of the file(s), + * but you are not obligated to do so. If you do not wish to do so, delete this + * exception statement from your version. + */ + +#include "macutilities.h" + +#include +#include +#import + +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(); + } +} + +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"); + } +} diff --git a/src/gui/mainwindow.cpp b/src/gui/mainwindow.cpp index 2946cac9f..1409ab3be 100644 --- a/src/gui/mainwindow.cpp +++ b/src/gui/mainwindow.cpp @@ -31,8 +31,6 @@ #include "mainwindow.h" #ifdef Q_OS_MAC -#include -#include #include #include #endif @@ -107,6 +105,10 @@ #include "hidabletabwidget.h" #include "ui_mainwindow.h" +#ifdef Q_OS_MAC +#include "macutilities.h" +#endif + #ifdef Q_OS_MAC void qt_mac_set_dock_menu(QMenu *menu); #endif @@ -1294,29 +1296,9 @@ static bool dockClickHandler(id self, SEL cmd, ...) } void MainWindow::setupDockClickHandler() -{ - Class cls = objc_getClass("NSApplication"); - objc_object *appInst = objc_msgSend(reinterpret_cast(cls), sel_registerName("sharedApplication")); - - if (!appInst) - return; - +{ dockMainWindowHandle = this; - objc_object* delegate = objc_msgSend(appInst, sel_registerName("delegate")); - Class delClass = reinterpret_cast(objc_msgSend(delegate, sel_registerName("class"))); - SEL shouldHandle = sel_registerName("applicationShouldHandleReopen:hasVisibleWindows:"); - if (class_getInstanceMethod(delClass, shouldHandle)) { - if (class_replaceMethod(delClass, shouldHandle, reinterpret_cast(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, reinterpret_cast(dockClickHandler), "B@:")) - qDebug("Registered dock click handler"); - else - qWarning("Failed to register dock click handler"); - } + overrideDockClickHandler(dockClickHandler); } #endif diff --git a/src/gui/torrentcontentmodel.cpp b/src/gui/torrentcontentmodel.cpp index 46cd0c6fa..bc2ae9fc1 100644 --- a/src/gui/torrentcontentmodel.cpp +++ b/src/gui/torrentcontentmodel.cpp @@ -32,14 +32,12 @@ #include #include #include +#include #if defined(Q_OS_WIN) #include #include #include -#elif defined(Q_OS_MAC) -#include -#include #else #include #include @@ -52,13 +50,8 @@ #include "torrentcontentmodelitem.h" #include "torrentcontentmodelfolder.h" #include "torrentcontentmodelfile.h" - #ifdef Q_OS_MAC -struct NSImage; -// This function is a private QtGui library export on macOS -// See src/gui/painting/qcoregraphics_p.h for more details -// QtMac::fromCGImageRef takes a CGImageRef and thus requires a double conversion -QPixmap qt_mac_toQPixmap(const NSImage *image, const QSizeF &size); +#include "macutilities.h" #endif namespace @@ -113,31 +106,23 @@ namespace { const QString ext = info.suffix(); if (!ext.isEmpty()) { - const QPixmap pixmap = pixmapForExtension(ext, QSize(32, 32)); - if (!pixmap.isNull()) - return QIcon(pixmap); + auto cacheIter = m_iconCache.find(ext); + + if (cacheIter != m_iconCache.end()) + return *cacheIter; + + QIcon icon = QIcon(pixmapForExtension(ext, QSize(32, 32))); + if (!icon.isNull()) { + m_iconCache.insert(ext, icon); + return icon; + } } return UnifiedFileIconProvider::icon(info); } private: - QPixmap pixmapForExtension(const QString &ext, const QSize &size) const - { - QMacAutoReleasePool pool; - objc_object *woskspaceCls = reinterpret_cast(objc_getClass("NSWorkspace")); - SEL sharedWorkspaceSel = sel_registerName("sharedWorkspace"); - SEL iconForFileTypeSel = sel_registerName("iconForFileType:"); - - objc_object *sharedWorkspace = objc_msgSend(woskspaceCls, sharedWorkspaceSel); - if (sharedWorkspace) { - objc_object *image = objc_msgSend(sharedWorkspace, iconForFileTypeSel, ext.toNSString()); - if (image) - return qt_mac_toQPixmap(reinterpret_cast(image), size); - } - - return QPixmap(); - } + mutable QMap m_iconCache; }; #else /**