diff --git a/CMakeLists.txt b/CMakeLists.txt index e7b56e10..f05f17c6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,147 +1,168 @@ # CMake version required. This must be the very first line, because it sets default policies affecting everything else cmake_minimum_required(VERSION 3.1) # Project name and version project(Falkon VERSION 2.1.99) # Find ECM, with nice error handling in case of failure include(FeatureSummary) find_package(ECM 5.27.0 CONFIG) set_package_properties(ECM PROPERTIES TYPE REQUIRED DESCRIPTION "Extra CMake Modules." URL "https://projects.kde.org/projects/frameworks/extra-cmake-modules") feature_summary(WHAT REQUIRED_PACKAGES_NOT_FOUND FATAL_ON_MISSING_REQUIRED_PACKAGES) set(CMAKE_MODULE_PATH ${ECM_MODULE_PATH} ${ECM_KDE_MODULE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/cmake) # Many includes from ECM, to get all the nice cmake functions and settings include(KDEInstallDirs) include(KDECMakeSettings) include(KDECompilerSettings NO_POLICY_SCOPE) include(ECMInstallIcons) include(ECMSetupVersion) include(ECMAddAppIcon) include(ECMQtDeclareLoggingCategory) # Output dirs (like ECM 5.38 does) set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/lib") set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin") set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin") # Version (TODO: move to a generated header once qmake support is dropped, to avoid full recompilations when changing this add_definitions(-DFALKON_VERSION=\"${PROJECT_VERSION}\") # Defines that are always set add_definitions(-DQT_NO_URL_CAST_FROM_STRING -DQT_USE_QSTRINGBUILDER -DQT_NO_CAST_TO_ASCII) # Configurable options (TODO: move all defines to a generated header) option(NO_SYSTEM_DATAPATH "TODO" OFF) if (NO_SYSTEM_DATAPATH) add_definitions(-DNO_SYSTEM_DATAPATH) endif() option(NO_X11 "TODO" OFF) if (NO_X11) add_definitions(-DNO_X11) endif() option(PORTABLE_BUILD "TODO" OFF) if (PORTABLE_BUILD) add_definitions(-DPORTABLE_BUILD) endif() option(NONBLOCK_JS_DIALOGS "TODO" OFF) if (NONBLOCK_JS_DIALOGS) add_definitions(-DNONBLOCK_JS_DIALOGS) endif() option(USE_LIBPATH "TODO" "") # REMOVE? if (USE_LIBPATH) add_definitions(-DUSE_LIBPATH=\"${USE_LIBPATH}\") endif() option(DISABLE_DBUS "TODO" OFF) if (DISABLE_DBUS) add_definitions(-DDISABLE_DBUS) endif() option(DISABLE_UPDATES_CHECK "TODO" OFF) if (DISABLE_UPDATES_CHECK) add_definitions(-DDISABLE_UPDATES_CHECK) endif() # Note: the old qmake option DEBUG_BUILD is now -DCMAKE_BUILD_TYPE=Debug, and FALKON_PREFIX is now -DCMAKE_INSTALL_PREFIX # SHARE_FOLDER is now auto-detected, so is USE_LIBPATH. # Mandatory: Qt5 set(QT_MIN_VERSION "5.8.0") find_package(Qt5 ${QT_MIN_VERSION} REQUIRED COMPONENTS Core Widgets Network Sql QuickWidgets PrintSupport WebEngineWidgets WebChannel Test) if (NOT DISABLE_DBUS) find_package(Qt5 ${QT_MIN_VERSION} REQUIRED COMPONENTS DBus) endif() if (UNIX AND NOT APPLE AND NOT NO_X11) find_package(X11) if (X11_FOUND) add_definitions(-DQZ_WS_X11) endif() find_package(XCB COMPONENTS XCB) find_package(Qt5 ${QT_MIN_VERSION} REQUIRED COMPONENTS X11Extras) endif() if (WIN32) add_definitions(-DW7API) find_package(Qt5 ${QT_MIN_VERSION} REQUIRED COMPONENTS WinExtras) - # TODO set var for LIBS += User32.lib Ole32.lib Shell32.lib ShlWapi.lib Gdi32.lib ComCtl32.lib ? + + # taken from https://stackoverflow.com/a/40217291 + macro(get_WIN32_WINNT version) + if (CMAKE_SYSTEM_VERSION) + set(ver ${CMAKE_SYSTEM_VERSION}) + string(REGEX MATCH "^([0-9]+).([0-9])" ver ${ver}) + string(REGEX MATCH "^([0-9]+)" verMajor ${ver}) + # Check for Windows 10, b/c we'll need to convert to hex 'A'. + if ("${verMajor}" MATCHES "10") + set(verMajor "A") + string(REGEX REPLACE "^([0-9]+)" ${verMajor} ver ${ver}) + endif ("${verMajor}" MATCHES "10") + # Remove all remaining '.' characters. + string(REPLACE "." "" ver ${ver}) + # Prepend each digit with a zero. + string(REGEX REPLACE "([0-9A-Z])" "0\\1" ver ${ver}) + set(${version} "0x${ver}") + endif(CMAKE_SYSTEM_VERSION) + endmacro(get_WIN32_WINNT) + + get_WIN32_WINNT(ver) + add_definitions(-D_WIN32_WINNT=${ver}) endif() # Optional: GnomeKeyring find_package(PkgConfig) if (PKG_CONFIG_FOUND) option(BUILD_KEYRING "Gnome keyring password plugin" ON) if (BUILD_KEYRING) pkg_check_modules(GNOME_KEYRING gnome-keyring-1) endif() endif() # Optional: KWallet set(KF5_MIN_VERSION "5.27.0") find_package(KF5Wallet ${KF5_MIN_VERSION} CONFIG) set_package_properties(KF5Wallet PROPERTIES DESCRIPTION "KWallet password backend plugin" TYPE OPTIONAL) # Git revision if (EXISTS "${CMAKE_SOURCE_DIR}/.git") find_package(Git QUIET) if(GIT_FOUND) execute_process( COMMAND ${GIT_EXECUTABLE} rev-parse --short HEAD WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} OUTPUT_VARIABLE GIT_REVISION ) string(REGEX REPLACE "\n" "" GIT_REVISION "${GIT_REVISION}") add_definitions(-DGIT_REVISION=\"${GIT_REVISION}\") else() message(STATUS "Git revision could not be determined") endif() endif() # Include dirs used everywhere include_directories( ${CMAKE_SOURCE_DIR}/src/lib/3rdparty ${CMAKE_SOURCE_DIR}/src/lib/adblock ${CMAKE_SOURCE_DIR}/src/lib/app ${CMAKE_SOURCE_DIR}/src/lib/autofill ${CMAKE_SOURCE_DIR}/src/lib/bookmarks ${CMAKE_SOURCE_DIR}/src/lib/cookies ${CMAKE_SOURCE_DIR}/src/lib/downloads ${CMAKE_SOURCE_DIR}/src/lib/history ${CMAKE_SOURCE_DIR}/src/lib/navigation ${CMAKE_SOURCE_DIR}/src/lib/network ${CMAKE_SOURCE_DIR}/src/lib/notifications ${CMAKE_SOURCE_DIR}/src/lib/opensearch ${CMAKE_SOURCE_DIR}/src/lib/other ${CMAKE_SOURCE_DIR}/src/lib/plugins ${CMAKE_SOURCE_DIR}/src/lib/popupwindow ${CMAKE_SOURCE_DIR}/src/lib/preferences ${CMAKE_SOURCE_DIR}/src/lib/session ${CMAKE_SOURCE_DIR}/src/lib/sidebar ${CMAKE_SOURCE_DIR}/src/lib/tabwidget ${CMAKE_SOURCE_DIR}/src/lib/tools ${CMAKE_SOURCE_DIR}/src/lib/webengine ${CMAKE_SOURCE_DIR}/src/lib/webtab ) # Finally, go into the subdirs add_subdirectory(src) add_subdirectory(tests/autotests) feature_summary(WHAT ALL FATAL_ON_MISSING_REQUIRED_PACKAGES) diff --git a/src/lib/app/datapaths.cpp b/src/lib/app/datapaths.cpp index 1aa4c3d3..9d8e3330 100644 --- a/src/lib/app/datapaths.cpp +++ b/src/lib/app/datapaths.cpp @@ -1,140 +1,144 @@ /* ============================================================ * Falkon - Qt web browser * Copyright (C) 2014-2017 David Rosca * * 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 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * ============================================================ */ #include "datapaths.h" #include "qztools.h" #include #include #include #include Q_GLOBAL_STATIC(DataPaths, qz_data_paths) DataPaths::DataPaths() { init(); } +DataPaths::~DataPaths() +{ +} + // static void DataPaths::setCurrentProfilePath(const QString &profilePath) { qz_data_paths()->initCurrentProfile(profilePath); } // static void DataPaths::setPortableVersion() { DataPaths* d = qz_data_paths(); d->m_paths[Config] = d->m_paths[AppData]; d->m_paths[Profiles] = d->m_paths[Config]; d->m_paths[Profiles].first().append(QLatin1String("/profiles")); d->m_paths[Temp] = d->m_paths[Config]; d->m_paths[Temp].first().append(QLatin1String("/tmp")); // Make sure the Config and Temp paths exists QDir dir; dir.mkpath(d->m_paths[Config].at(0)); dir.mkpath(d->m_paths[Temp].at(0)); } // static QString DataPaths::path(DataPaths::Path path) { Q_ASSERT(!qz_data_paths()->m_paths[path].isEmpty()); return qz_data_paths()->m_paths[path].at(0); } // static QStringList DataPaths::allPaths(DataPaths::Path type) { Q_ASSERT(!qz_data_paths()->m_paths[type].isEmpty()); return qz_data_paths()->m_paths[type]; } // static QString DataPaths::currentProfilePath() { return path(CurrentProfile); } // static void DataPaths::clearTempData() { QzTools::removeDir(path(Temp)); } void DataPaths::init() { #if defined(NO_SYSTEM_DATAPATH) m_paths[AppData].append(QApplication::applicationDirPath()); #endif m_paths[AppData].append(QStandardPaths::standardLocations(QStandardPaths::AppDataLocation)); for (const QString &location : qAsConst(m_paths[AppData])) { initAssetsIn(location); } m_paths[Config].append(QStandardPaths::writableLocation(QStandardPaths::AppConfigLocation)); m_paths[Profiles].append(m_paths[Config].at(0) + QLatin1String("/profiles")); // We also allow to load data from Config path initAssetsIn(m_paths[Config].at(0)); m_tmpdir.reset(new QTemporaryDir(QCoreApplication::applicationName())); m_paths[Temp].append(m_tmpdir->path()); if (!m_tmpdir->isValid()) { qWarning() << "Failed to create temporary directory" << m_tmpdir->path(); } m_paths[Cache].append(QStandardPaths::writableLocation(QStandardPaths::CacheLocation)); // Make sure the Config and Temp paths exists QDir dir; dir.mkpath(m_paths[Config].at(0)); dir.mkpath(m_paths[Temp].at(0)); #ifdef USE_LIBPATH m_paths[Plugins].append(QLatin1String(USE_LIBPATH "/falkon")); #endif } void DataPaths::initCurrentProfile(const QString &profilePath) { m_paths[CurrentProfile].append(profilePath); if (m_paths[Cache].isEmpty()) m_paths[Cache].append(m_paths[CurrentProfile].at(0) + QLatin1String("/cache")); if (m_paths[Sessions].isEmpty()) m_paths[Sessions].append(m_paths[CurrentProfile].at(0) + QLatin1String("/sessions")); QDir dir; dir.mkpath(m_paths[Cache].at(0)); dir.mkpath(m_paths[Sessions].at(0)); } void DataPaths::initAssetsIn(const QString &path) { m_paths[Translations].append(path + QLatin1String("/locale")); m_paths[Themes].append(path + QLatin1String("/themes")); m_paths[Plugins].append(path + QLatin1String("/plugins")); } diff --git a/src/lib/app/datapaths.h b/src/lib/app/datapaths.h index 7b3aab8a..ef87bb43 100644 --- a/src/lib/app/datapaths.h +++ b/src/lib/app/datapaths.h @@ -1,70 +1,71 @@ /* ============================================================ * Falkon - Qt web browser * Copyright (C) 2014 David Rosca * * 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 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * ============================================================ */ #ifndef DATAPATHS_H #define DATAPATHS_H #include #include "qzcommon.h" class QTemporaryDir; class FALKON_EXPORT DataPaths { public: enum Path { AppData = 0, // /usr/share/falkon or . or ../Resources Translations = 1, // $AppData/locale Themes = 2, // $AppData/themes Plugins = 3, // $AppData/plugins Config = 4, // $XDG_CONFIG_HOME/falkon or %LOCALAPPDATA%/falkon or $AppData/data (portable) Profiles = 5, // $Config/profiles CurrentProfile = 6, // $Profiles/current_profile Temp = 7, // $Config/tmp Cache = 8, // $XDG_CACHE_HOME/falkon or $CurrentProfile/cache Sessions = 9, // $CurrentProfile/sessions LastPath = 10 }; explicit DataPaths(); + ~DataPaths(); // Set absolute path of current profile static void setCurrentProfilePath(const QString &profilePath); // Set Config path to $AppData/data static void setPortableVersion(); // Returns main path (Themes -> /usr/share/themes) static QString path(Path type); // Returns all paths (Themes -> /usr/share/themes, ~/.config/falkon/themes) static QStringList allPaths(Path type); // Convenience function for getting CurrentProfile static QString currentProfilePath(); // Remove Temp dir static void clearTempData(); private: void init(); void initCurrentProfile(const QString &profilePath); void initAssetsIn(const QString &path); QStringList m_paths[LastPath]; QScopedPointer m_tmpdir; }; #endif // DATAPATHS_H diff --git a/src/lib/downloads/downloaditem.cpp b/src/lib/downloads/downloaditem.cpp index 58e58a46..44a4632c 100644 --- a/src/lib/downloads/downloaditem.cpp +++ b/src/lib/downloads/downloaditem.cpp @@ -1,336 +1,337 @@ /* ============================================================ * Falkon - Qt web browser * Copyright (C) 2010-2017 David Rosca * * 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 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * ============================================================ */ #include "downloaditem.h" #include "ui_downloaditem.h" #include "mainapplication.h" #include "browserwindow.h" #include "tabwidget.h" #include "webpage.h" #include "downloadmanager.h" #include "networkmanager.h" #include "qztools.h" #include "datapaths.h" #include #include #include #include #include #include #include #include #include #include #ifdef Q_OS_WIN #include "Shlwapi.h" +#include "shellapi.h" #endif //#define DOWNMANAGER_DEBUG DownloadItem::DownloadItem(QListWidgetItem *item, QWebEngineDownloadItem* downloadItem, const QString &path, const QString &fileName, bool openFile, DownloadManager* manager) : QWidget() , ui(new Ui::DownloadItem) , m_item(item) , m_download(downloadItem) , m_path(path) , m_fileName(fileName) , m_downUrl(downloadItem->url()) , m_openFile(openFile) , m_downloading(false) , m_downloadStopped(false) , m_currSpeed(0) , m_received(downloadItem->receivedBytes()) , m_total(downloadItem->totalBytes()) { #ifdef DOWNMANAGER_DEBUG qDebug() << __FUNCTION__ << item << reply << path << fileName; #endif ui->setupUi(this); setMaximumWidth(525); ui->button->setPixmap(QIcon::fromTheme(QSL("process-stop")).pixmap(20, 20)); ui->fileName->setText(m_fileName); ui->downloadInfo->setText(tr("Remaining time unavailable")); setContextMenuPolicy(Qt::CustomContextMenu); connect(this, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(customContextMenuRequested(QPoint))); connect(ui->button, SIGNAL(clicked(QPoint)), this, SLOT(stop())); connect(manager, SIGNAL(resized(QSize)), this, SLOT(parentResized(QSize))); startDownloading(); } void DownloadItem::startDownloading() { connect(m_download, &QWebEngineDownloadItem::finished, this, &DownloadItem::finished); connect(m_download, &QWebEngineDownloadItem::downloadProgress, this, &DownloadItem::downloadProgress); m_downloading = true; m_downTimer.start(); updateDownloadInfo(0, m_download->receivedBytes(), m_download->totalBytes()); #ifdef Q_OS_LINUX // QFileIconProvider uses only suffix on Linux QFileIconProvider iconProvider; QIcon fileIcon = iconProvider.icon(QFileInfo(m_fileName)); if (!fileIcon.isNull()) { ui->fileIcon->setPixmap(fileIcon.pixmap(30)); } else { ui->fileIcon->setPixmap(style()->standardIcon(QStyle::SP_FileIcon).pixmap(30)); } #else ui->fileIcon->hide(); #endif } void DownloadItem::parentResized(const QSize &size) { if (size.width() < 200) { return; } setMaximumWidth(size.width()); } void DownloadItem::finished() { #ifdef DOWNMANAGER_DEBUG qDebug() << __FUNCTION__ << m_reply; #endif bool success = false; QString host = m_download->url().host(); switch (m_download->state()) { case QWebEngineDownloadItem::DownloadCompleted: success = true; ui->downloadInfo->setText(tr("Done - %1 (%2)").arg(host, QDateTime::currentDateTime().toString(Qt::DefaultLocaleShortDate))); break; case QWebEngineDownloadItem::DownloadInterrupted: ui->downloadInfo->setText(tr("Error - %1").arg(host)); break; case QWebEngineDownloadItem::DownloadCancelled: ui->downloadInfo->setText(tr("Cancelled - %1").arg(host)); break; default: break; } ui->progressBar->hide(); ui->button->hide(); ui->frame->hide(); m_item->setSizeHint(sizeHint()); m_downloading = false; if (success && m_openFile) openFile(); emit downloadFinished(true); } void DownloadItem::downloadProgress(qint64 received, qint64 total) { #ifdef DOWNMANAGER_DEBUG qDebug() << __FUNCTION__ << received << total; #endif qint64 currentValue = 0; qint64 totalValue = 0; if (total > 0) { currentValue = received * 100 / total; totalValue = 100; } ui->progressBar->setValue(currentValue); ui->progressBar->setMaximum(totalValue); m_currSpeed = received * 1000.0 / m_downTimer.elapsed(); m_received = received; m_total = total; updateDownloadInfo(m_currSpeed, m_received, m_total); } int DownloadItem::progress() { return ui->progressBar->value(); } bool DownloadItem::isCancelled() { return ui->downloadInfo->text().startsWith(tr("Cancelled")); } QString DownloadItem::remaingTimeToString(QTime time) { if (time < QTime(0, 0, 10)) { return tr("few seconds"); } else if (time < QTime(0, 1)) { //~ singular %n second //~ plural %n seconds return tr("%n seconds", "", time.second()); } else if (time < QTime(1, 0)) { //~ singular %n minute //~ plural %n minutes return tr("%n minutes", "", time.minute()); } else { //~ singular %n hour //~ plural %n hours return tr("%n hours", "", time.hour()); } } QString DownloadItem::currentSpeedToString(double speed) { if (speed < 0) { return tr("Unknown speed"); } speed /= 1024; // kB if (speed < 1000) { return QString::number(speed, 'f', 0) + QLatin1String(" ") + tr("kB/s"); } speed /= 1024; //MB if (speed < 1000) { return QString::number(speed, 'f', 2) + QLatin1String(" ") + tr("MB/s"); } speed /= 1024; //GB return QString::number(speed, 'f', 2) + QLatin1String(" ") + tr("GB/s"); } void DownloadItem::updateDownloadInfo(double currSpeed, qint64 received, qint64 total) { #ifdef DOWNMANAGER_DEBUG qDebug() << __FUNCTION__ << currSpeed << received << total; #endif // QString QString QString QString // | m_remTime | |m_currSize| |m_fileSize| |m_speed| // Remaining 26 minutes - 339MB of 693 MB (350kB/s) int estimatedTime = ((total - received) / 1024) / (currSpeed / 1024); QString speed = currentSpeedToString(currSpeed); // We have QString speed now QTime time; time = time.addSecs(estimatedTime); QString remTime = remaingTimeToString(time); m_remTime = time; QString currSize = QzTools::fileSizeToString(received); QString fileSize = QzTools::fileSizeToString(total); if (fileSize == tr("Unknown size")) { ui->downloadInfo->setText(tr("%2 - unknown size (%3)").arg(currSize, speed)); } else { ui->downloadInfo->setText(tr("Remaining %1 - %2 of %3 (%4)").arg(remTime, currSize, fileSize, speed)); } } void DownloadItem::stop() { #ifdef DOWNMANAGER_DEBUG qDebug() << __FUNCTION__; #endif if (m_downloadStopped) { return; } m_downloadStopped = true; ui->progressBar->hide(); ui->button->hide(); m_item->setSizeHint(sizeHint()); ui->downloadInfo->setText(tr("Cancelled - %1").arg(m_download->url().host())); m_download->cancel(); m_downloading = false; emit downloadFinished(false); } void DownloadItem::mouseDoubleClickEvent(QMouseEvent* e) { openFile(); e->accept(); } void DownloadItem::customContextMenuRequested(const QPoint &pos) { QMenu menu; menu.addAction(QIcon::fromTheme("document-open"), tr("Open File"), this, SLOT(openFile())); menu.addAction(tr("Open Folder"), this, SLOT(openFolder())); menu.addSeparator(); menu.addAction(QIcon::fromTheme("edit-copy"), tr("Copy Download Link"), this, SLOT(copyDownloadLink())); menu.addSeparator(); menu.addAction(QIcon::fromTheme("process-stop"), tr("Cancel downloading"), this, SLOT(stop()))->setEnabled(m_downloading); menu.addAction(QIcon::fromTheme("list-remove"), tr("Remove From List"), this, SLOT(clear()))->setEnabled(!m_downloading); if (m_downloading || ui->downloadInfo->text().startsWith(tr("Cancelled")) || ui->downloadInfo->text().startsWith(tr("Error"))) { menu.actions().at(0)->setEnabled(false); } menu.exec(mapToGlobal(pos)); } void DownloadItem::copyDownloadLink() { QApplication::clipboard()->setText(m_downUrl.toString()); } void DownloadItem::clear() { emit deleteItem(this); } void DownloadItem::openFile() { if (m_downloading) { return; } QFileInfo info(m_path, m_fileName); if (info.exists()) { QDesktopServices::openUrl(QUrl::fromLocalFile(info.absoluteFilePath())); } else { QMessageBox::warning(m_item->listWidget()->parentWidget(), tr("Not found"), tr("Sorry, the file \n %1 \n was not found!").arg(info.absoluteFilePath())); } } void DownloadItem::openFolder() { #ifdef Q_OS_WIN QString winFileName = QSL("%1/%2").arg(m_path, m_fileName); winFileName.replace(QLatin1Char('/'), "\\"); QString shExArg = "/e,/select,\"" + winFileName + "\""; ShellExecute(NULL, NULL, TEXT("explorer.exe"), shExArg.toStdWString().c_str(), NULL, SW_SHOW); #else QDesktopServices::openUrl(QUrl::fromLocalFile(m_path)); #endif } DownloadItem::~DownloadItem() { delete ui; delete m_item; } diff --git a/src/lib/other/registerqappassociation.cpp b/src/lib/other/registerqappassociation.cpp index e061ed72..91d4a18c 100644 --- a/src/lib/other/registerqappassociation.cpp +++ b/src/lib/other/registerqappassociation.cpp @@ -1,419 +1,418 @@ /* ============================================================ * Copyright (C) 2012-2017 S. Razi Alavizadeh * This file is part of Falkon - Qt web browser 2010-2014 * by David Rosca * * 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 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * ============================================================ */ #include "registerqappassociation.h" #include "mainapplication.h" #include "browserwindow.h" #include "ShlObj.h" #include #include #include #include RegisterQAppAssociation::RegisterQAppAssociation(QObject* parent) : QObject(parent) { setPerMachineRegisteration(false); } RegisterQAppAssociation::RegisterQAppAssociation(const QString &appRegisteredName, const QString &appPath, const QString &appIcon, const QString &appDesc, QObject* parent) : QObject(parent) { setPerMachineRegisteration(false); setAppInfo(appRegisteredName, appPath, appIcon, appDesc); } RegisterQAppAssociation::~RegisterQAppAssociation() { } void RegisterQAppAssociation::addCapability(const QString &assocName, const QString &progId, const QString &desc, const QString &iconPath, AssociationType type) { _assocDescHash.insert(progId, QPair(desc, QDir::toNativeSeparators(iconPath))); switch (type) { case FileAssociation: _fileAssocHash.insert(assocName, progId); break; case UrlAssociation: _urlAssocHash.insert(assocName, progId); break; default: break; } } void RegisterQAppAssociation::removeCapability(const QString &assocName) { _fileAssocHash.remove(assocName); _urlAssocHash.remove(assocName); } void RegisterQAppAssociation::setAppInfo(const QString &appRegisteredName, const QString &appPath, const QString &appIcon, const QString &appDesc) { _appRegisteredName = appRegisteredName; _appPath = QDir::toNativeSeparators(appPath); _appIcon = QDir::toNativeSeparators(appIcon); _appDesc = appDesc; } bool RegisterQAppAssociation::isPerMachineRegisteration() { return (_UserRootKey == "HKEY_LOCAL_MACHINE"); } void RegisterQAppAssociation::setPerMachineRegisteration(bool enable) { if (enable) { _UserRootKey = "HKEY_LOCAL_MACHINE"; } else { _UserRootKey = "HKEY_CURRENT_USER"; } } bool RegisterQAppAssociation::registerAppCapabilities() { if (!isVistaOrNewer()) { return true; } // Vista and newer QSettings regLocalMachine("HKEY_LOCAL_MACHINE", QSettings::NativeFormat); QString capabilitiesKey = regLocalMachine.value("Software/RegisteredApplications/" + _appRegisteredName).toString(); if (capabilitiesKey.isEmpty()) { regLocalMachine.setValue("Software/RegisteredApplications/" + _appRegisteredName, QString("Software\\" + _appRegisteredName + "\\Capabilities")); capabilitiesKey = regLocalMachine.value("Software/RegisteredApplications/" + _appRegisteredName).toString(); if (capabilitiesKey.isEmpty()) { QMessageBox::warning(mApp->getWindow(), tr("Warning!"), tr("There are some problems. Please, reinstall Falkon.\n" "Maybe relaunch with administrator right do a magic for you! ;)")); return false; } } capabilitiesKey.replace("\\", "/"); QHash >::const_iterator it = _assocDescHash.constBegin(); while (it != _assocDescHash.constEnd()) { createProgId(it.key()); ++it; } regLocalMachine.setValue(capabilitiesKey + "/ApplicationDescription", _appDesc); regLocalMachine.setValue(capabilitiesKey + "/ApplicationIcon", _appIcon); regLocalMachine.setValue(capabilitiesKey + "/ApplicationName", _appRegisteredName); QHash::const_iterator i = _fileAssocHash.constBegin(); while (i != _fileAssocHash.constEnd()) { regLocalMachine.setValue(capabilitiesKey + "/FileAssociations/" + i.key(), i.value()); ++i; } i = _urlAssocHash.constBegin(); while (i != _urlAssocHash.constEnd()) { regLocalMachine.setValue(capabilitiesKey + "/URLAssociations/" + i.key(), i.value()); ++i; } regLocalMachine.setValue(capabilitiesKey + "/Startmenu/StartMenuInternet", _appPath); return true; } bool RegisterQAppAssociation::isVistaOrNewer() { return (QSysInfo::windowsVersion() >= QSysInfo::WV_VISTA && QSysInfo::windowsVersion() <= QSysInfo::WV_NT_based); } bool RegisterQAppAssociation::isWin10OrNewer() { return (QSysInfo::windowsVersion() >= QSysInfo::WV_WINDOWS10 && QSysInfo::windowsVersion() <= QSysInfo::WV_NT_based); } void RegisterQAppAssociation::registerAssociation(const QString &assocName, AssociationType type) { if (isVistaOrNewer()) { // Vista and newer #ifndef __MINGW32__ IApplicationAssociationRegistration* pAAR; HRESULT hr = CoCreateInstance(CLSID_ApplicationAssociationRegistration, NULL, CLSCTX_INPROC, __uuidof(IApplicationAssociationRegistration), (void**)&pAAR); if (SUCCEEDED(hr)) { switch (type) { case FileAssociation: hr = pAAR->SetAppAsDefault(_appRegisteredName.toStdWString().c_str(), assocName.toStdWString().c_str(), AT_FILEEXTENSION); break; case UrlAssociation: { QSettings regCurrentUserRoot("HKEY_CURRENT_USER", QSettings::NativeFormat); QString currentUrlDefault = regCurrentUserRoot.value("Software/Microsoft/Windows/Shell/Associations/UrlAssociations/" + assocName + "/UserChoice/Progid").toString(); hr = pAAR->SetAppAsDefault(_appRegisteredName.toStdWString().c_str(), assocName.toStdWString().c_str(), AT_URLPROTOCOL); if (SUCCEEDED(hr) && !currentUrlDefault.isEmpty() && currentUrlDefault != _urlAssocHash.value(assocName) ) { regCurrentUserRoot.setValue("Software/Classes" + assocName + "/shell/open/command/backup_progid", currentUrlDefault); } } break; default: break; } pAAR->Release(); } #endif // #ifndef __MINGW32__ } else { // Older than Vista QSettings regUserRoot(_UserRootKey, QSettings::NativeFormat); regUserRoot.beginGroup("Software/Classes"); QSettings regClassesRoot("HKEY_CLASSES_ROOT", QSettings::NativeFormat); switch (type) { case FileAssociation: { QString progId = _fileAssocHash.value(assocName); createProgId(progId); QString currentDefault = regClassesRoot.value(assocName + "/Default").toString(); if (!currentDefault.isEmpty() && currentDefault != progId && regUserRoot.value(assocName + "/backup_val").toString() != progId ) { regUserRoot.setValue(assocName + "/backup_val", currentDefault); } regUserRoot.setValue(assocName + "/.", progId); } break; case UrlAssociation: { QString progId = _urlAssocHash.value(assocName); createProgId(progId); QString currentDefault = regClassesRoot.value(assocName + "/shell/open/command/Default").toString(); QString command = "\"" + _appPath + "\" \"%1\""; if (!currentDefault.isEmpty() && currentDefault != command && regUserRoot.value(assocName + "/shell/open/command/backup_val").toString() != command ) { regUserRoot.setValue(assocName + "/shell/open/command/backup_val", currentDefault); } regUserRoot.setValue(assocName + "/shell/open/command/.", command); regUserRoot.setValue(assocName + "/URL Protocol", ""); break; } default: break; } regUserRoot.endGroup(); } } void RegisterQAppAssociation::registerAllAssociation() { if (isVistaOrNewer() && !registerAppCapabilities()) { return; } QHash::const_iterator i = _fileAssocHash.constBegin(); while (i != _fileAssocHash.constEnd()) { registerAssociation(i.key(), FileAssociation); ++i; } i = _urlAssocHash.constBegin(); while (i != _urlAssocHash.constEnd()) { registerAssociation(i.key(), UrlAssociation); ++i; } if (!isVistaOrNewer()) { #ifndef __MINGW32__ // On Windows Vista or newer for updating icons 'pAAR->SetAppAsDefault()' // calls 'SHChangeNotify()'. Thus, we just need care about older Windows. SHChangeNotify(SHCNE_ASSOCCHANGED, SHCNF_FLUSHNOWAIT, 0 , 0); #endif } } bool RegisterQAppAssociation::showNativeDefaultAppSettingsUi() { if (!isVistaOrNewer()) { return false; } - if (isWin10OrNewer()) { - IApplicationActivationManager* pActivator; - HRESULT hr = CoCreateInstance(CLSID_ApplicationActivationManager, - nullptr, - CLSCTX_INPROC, - IID_IApplicationActivationManager, - (void**)&pActivator); - - if (!SUCCEEDED(hr)) { - return false; - } - - DWORD pid; - hr = pActivator->ActivateApplication( - L"windows.immersivecontrolpanel_cw5n1h2txyewy" // appUserModelId of "Settings" - L"!microsoft.windows.immersivecontrolpanel", // in Windows Store - L"page=SettingsPageAppsDefaults", AO_NONE, &pid); - - if (!SUCCEEDED(hr)) { - return false; - } +#ifdef _WIN32_WINNT_WIN8 + IApplicationActivationManager* pActivator; + HRESULT hr = CoCreateInstance(CLSID_ApplicationActivationManager, + nullptr, + CLSCTX_INPROC, + IID_IApplicationActivationManager, + (void**)&pActivator); + + if (!SUCCEEDED(hr)) { + return false; + } - // Do not check error because we could at least open - // the "Default apps" setting. - pActivator->ActivateApplication( - L"windows.immersivecontrolpanel_cw5n1h2txyewy" - L"!microsoft.windows.immersivecontrolpanel", - L"page=SettingsPageAppsDefaults" - L"&target=SystemSettings_DefaultApps_Browser", AO_NONE, &pid); + DWORD pid; + hr = pActivator->ActivateApplication( + L"windows.immersivecontrolpanel_cw5n1h2txyewy" // appUserModelId of "Settings" + L"!microsoft.windows.immersivecontrolpanel", // in Windows Store + L"page=SettingsPageAppsDefaults", AO_NONE, &pid); - pActivator->Release(); + if (!SUCCEEDED(hr)) { + return false; } - else { - IApplicationAssociationRegistrationUI* pAARUI = NULL; - HRESULT hr = CoCreateInstance(CLSID_ApplicationAssociationRegistrationUI, - NULL, CLSCTX_INPROC, __uuidof(IApplicationAssociationRegistrationUI), - reinterpret_cast< void** > (&pAARUI)); + // Do not check error because we could at least open + // the "Default apps" setting. + pActivator->ActivateApplication( + L"windows.immersivecontrolpanel_cw5n1h2txyewy" + L"!microsoft.windows.immersivecontrolpanel", + L"page=SettingsPageAppsDefaults" + L"&target=SystemSettings_DefaultApps_Browser", AO_NONE, &pid); - if (!SUCCEEDED(hr)) { - return false; - } + pActivator->Release(); +#else // Vista or Win7 + IApplicationAssociationRegistrationUI* pAARUI = NULL; + + HRESULT hr = CoCreateInstance(CLSID_ApplicationAssociationRegistrationUI, + NULL, CLSCTX_INPROC, __uuidof(IApplicationAssociationRegistrationUI), + reinterpret_cast< void** > (&pAARUI)); - hr = pAARUI->LaunchAdvancedAssociationUI(reinterpret_cast(_appRegisteredName.utf16())); - pAARUI->Release(); + if (!SUCCEEDED(hr)) { + return false; } + hr = pAARUI->LaunchAdvancedAssociationUI(reinterpret_cast(_appRegisteredName.utf16())); + pAARUI->Release(); +#endif // _WIN32_WINNT_WIN8 + return true; } void RegisterQAppAssociation::createProgId(const QString &progId) { QSettings regUserRoot(_UserRootKey, QSettings::NativeFormat); regUserRoot.beginGroup("Software/Classes"); QPair pair = _assocDescHash.value(progId); regUserRoot.setValue(progId + "/.", pair.first); regUserRoot.setValue(progId + "/shell/.", "open"); regUserRoot.setValue(progId + "/DefaultIcon/.", pair.second); regUserRoot.setValue(progId + "/shell/open/command/.", QString("\"" + _appPath + "\" \"%1\"")); regUserRoot.endGroup(); } bool RegisterQAppAssociation::isDefaultApp(const QString &assocName, AssociationType type) { if (isVistaOrNewer()) { QSettings regCurrentUserRoot("HKEY_CURRENT_USER", QSettings::NativeFormat); switch (type) { case FileAssociation: { regCurrentUserRoot.beginGroup("Software/Microsoft/Windows/CurrentVersion/Explorer/FileExts"); if (regCurrentUserRoot.childGroups().contains(assocName, Qt::CaseInsensitive)) { return (_fileAssocHash.value(assocName) == regCurrentUserRoot.value(assocName + "/UserChoice/Progid")); } else { regCurrentUserRoot.endGroup(); return false; } break; } case UrlAssociation: { regCurrentUserRoot.beginGroup("Software/Microsoft/Windows/Shell/Associations/UrlAssociations"); if (regCurrentUserRoot.childGroups().contains(assocName, Qt::CaseInsensitive)) { return (_urlAssocHash.value(assocName) == regCurrentUserRoot.value(assocName + "/UserChoice/Progid")); } else { regCurrentUserRoot.endGroup(); return false; } } break; default: break; } } else { QSettings regClassesRoot("HKEY_CLASSES_ROOT", QSettings::NativeFormat); { if (!regClassesRoot.childGroups().contains(assocName, Qt::CaseInsensitive)) { return false; } } switch (type) { case FileAssociation: { return (_fileAssocHash.value(assocName) == regClassesRoot.value(assocName + "/Default")); } break; case UrlAssociation: { QString currentDefault = regClassesRoot.value(assocName + "/shell/open/command/Default").toString(); currentDefault.remove("\""); currentDefault.remove("%1"); currentDefault = currentDefault.trimmed(); return (_appPath == currentDefault); } break; default: break; } } return false; } bool RegisterQAppAssociation::isDefaultForAllCapabilities() { bool result = true; QHash::const_iterator i = _fileAssocHash.constBegin(); while (i != _fileAssocHash.constEnd()) { bool res = isDefaultApp(i.key(), FileAssociation); result &= res; ++i; } i = _urlAssocHash.constBegin(); while (i != _urlAssocHash.constEnd()) { bool res = isDefaultApp(i.key(), UrlAssociation); result &= res; ++i; } return result; }