diff --git a/CMakeLists.txt b/CMakeLists.txt index 8f9110b..346175c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,175 +1,182 @@ # The CMake version we require (must be first) cmake_minimum_required(VERSION 3.0.2) if(POLICY CMP0071) cmake_policy(SET CMP0071 NEW) endif() project(alkimia VERSION 8.0.3) option(BUILD_QT4 "Build for Qt4" OFF) option(BUILD_DOXYGEN_DOCS "Build api docs" ON) option(BUILD_APPLETS "Build plasma applets" ON) find_package(ECM 0.0.11 REQUIRED NO_MODULE) set(CMAKE_MODULE_PATH ${ECM_MODULE_PATH} ${ECM_KDE_MODULE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules) include(ECMAddAppIcon) include(ECMInstallIcons) include(KDEInstallDirs) include(KDECMakeSettings) include(KDECompilerSettings NO_POLICY_SCOPE) include(FeatureSummary) # check for PkgConfig, Qt and MPIR/GMP find_package(PkgConfig) if(BUILD_QT4) find_package(Qt4 REQUIRED COMPONENTS QtCore QtGui QtDBus QtTest QtWebKit QtDeclarative) set(QT_USE_LIBSPREFIX Qt4::Qt) set(ALKIMIA_LIB_SUFFIX "") set(ALKIMIA_INCLUDE_SUFFIX "Qt4") set(ALKIMIA_PATH_SUFFIX) set(PC_TARGET_QTPREFIX Qt) set(PC_TARGET_SUFFIX) set(QT_BROWSER_LIB ${QT_USE_LIBSPREFIX}WebKit) add_definitions(-DBUILD_WITH_WEBKIT) set(_kde4_uninstall_rule_created 1) find_package(KDE4 REQUIRED) include(KDE4Defaults) set(KDE_LIBRARIES ${KDE4_KDECORE_LIBS} ${KDE4_KIO_LIBS}) include_directories(${KDE4_INCLUDES}) macro(ki18n_wrap_ui) kde4_add_ui_files(${ARGN}) endmacro(ki18n_wrap_ui) macro(kconfig_add_kcfg_files) kde4_add_kcfg_files(${ARGN}) endmacro(kconfig_add_kcfg_files) macro(ecm_add_executable) kde4_add_executable(${ARGN}) endmacro(ecm_add_executable) macro(ecm_mark_nongui_executable) foreach(_target ${ARGN}) set_target_properties(${_target} PROPERTIES WIN32_EXECUTABLE FALSE MACOSX_BUNDLE FALSE ) endforeach() endmacro(ecm_mark_nongui_executable) macro(ecm_install_icons) kde4_install_icons(${ICON_INSTALL_DIR}) endmacro() add_definitions(-DQStringLiteral=QLatin1String) if(NOT SHARE_INSTALL_DIR) set(SHARE_INSTALL_DIR ${DATA_INSTALL_DIR}) endif() else() option(BUILD_WITH_WEBKIT "Build with Qt Web Kit library" ON) - if(BUILD_WITH_WEBKIT) + option(BUILD_WITH_WEBENGINE "Build with Qt Web Engine instead of WebKit (experimental)" OFF) + if (BUILD_WITH_WEBENGINE) + add_definitions(-DBUILD_WITH_WEBENGINE) + set(QT5_OPTIONAL_COMPONENTS WebEngineWidgets) + message(WARNING "Qt Web Engine support is currently incomplete") + elseif(BUILD_WITH_WEBKIT) add_definitions(-DBUILD_WITH_WEBKIT) set(QT5_OPTIONAL_COMPONENTS WebKitWidgets) else() set(QT5_OPTIONAL_COMPONENTS Widgets) endif() find_package(Qt5 REQUIRED COMPONENTS Core Test ${QT5_OPTIONAL_COMPONENTS} Qml ) if(BUILD_APPLETS) set(PLASMA_COMPONENT Package Plasma) endif() # search packages used by KDE find_package(KF5 REQUIRED COMPONENTS Config CoreAddons KDELibs4Support #I18n #WebKit ${PLASMA_COMPONENT} NewStuff ) set(QT_USE_LIBSPREFIX Qt5::) set(ALKIMIA_LIB_SUFFIX "5") set(ALKIMIA_INCLUDE_SUFFIX "Qt5") set(ALKIMIA_PATH_SUFFIX 5) set(PC_TARGET_QTPREFIX Qt5) set(PC_TARGET_SUFFIX 5) - if(BUILD_WITH_WEBKIT) + if(BUILD_WITH_WEBENGINE) + set(QT_BROWSER_LIB ${QT_USE_LIBSPREFIX}WebEngineWidgets) + elseif(BUILD_WITH_WEBKIT) set(QT_BROWSER_LIB ${QT_USE_LIBSPREFIX}WebKitWidgets) else() set(QT_BROWSER_LIB ${QT_USE_LIBSPREFIX}Widgets) endif() macro(ecm_add_executable) add_executable(${ARGN}) endmacro(ecm_add_executable) if(CMAKE_COMPILER_IS_GNUCC) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-deprecated-declarations") endif() add_definitions( -DKDELIBS4SUPPORT_EXPORT_WRAPPER_H -DKDELIBS4SUPPORT_DEPRECATED_NOISE= -DKDELIBS4SUPPORT_DEPRECATED_EXPORT_NOISE=Q_DECL_IMPORT -DKDELIBS4SUPPORT_DEPRECATED= -DKDELIBS4SUPPORT_DEPRECATED_EXPORT=Q_DECL_IMPORT -DKDELIBS4SUPPORT_EXPORT=Q_DECL_IMPORT ) endif() set(TARGET_SUFFIX ${PC_TARGET_SUFFIX}) add_definitions(-DTARGET_SUFFIX=\"${TARGET_SUFFIX}\") # figure out which multi-precision library to use # MPIR is preferred over GMP find_package(MPIR) if(MPIR_FOUND) set(MP_INCLUDE_DIR ${MPIR_INCLUDE_DIR}) set(MP_LIBRARIES ${MPIR_LIBRARIES}) set(MP_HEADER mpirxx.h) set(MP_CMAKE_MODULE "MPIR") set(PC_LIB mpir) else() find_package(GMP REQUIRED) set(MP_INCLUDE_DIR ${GMP_INCLUDE_DIR}) set(MP_LIBRARIES ${GMP_LIBRARIES}) set(MP_HEADER gmpxx.h) set(MP_CMAKE_MODULE "GMP") set(PC_LIB gmp) endif() # check for Doxygen (for API documentation) if(BUILD_DOXYGEN_DOCS) find_package(Doxygen) endif() feature_summary(WHAT ALL) 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) add_definitions(-DQT_USE_FAST_CONCATENATION -DQT_USE_FAST_OPERATOR_PLUS) include_directories( ${CMAKE_SOURCE_DIR}/src ${CMAKE_BINARY_DIR}/src ) add_subdirectory(src) add_subdirectory(autotests) add_subdirectory(qml) if(BUILD_APPLETS) if(BUILD_QT4) add_subdirectory(plasma/applets/onlinequote) else() add_subdirectory(plasma/applets/ForeignCurrencies) endif() endif() add_subdirectory(tests) add_subdirectory(tools) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 3315d43..2692a79 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,241 +1,241 @@ include(GenerateExportHeader) include(ECMGenerateHeaders) include(ECMGeneratePriFile) include(CMakePackageConfigHelpers) include(ECMSetupVersion) include_directories( ${CMAKE_CURRENT_BINARY_DIR} ) ########### target alkimia-internal ########### set(alkimia_INTERNAL_SRCS alkdateformat.cpp alkexception.cpp alkonlinequoteprocess.cpp alkquoteitem.cpp alkquotereceiver.cpp ) set(alkimia_INTERNAL_HEADERS alkdateformat.h alkexception.h alkonlinequoteprocess.h alkquoteitem.h alkquotereceiver.h ) set(alkimia_INTERNAL_UI ) ki18n_wrap_ui(alkimia_INTERNAL_SRCS ${alkimia_INTERNAL_UI} ) add_library(alkimia-internal STATIC ${alkimia_INTERNAL_SRCS} ${alkimia_INTERNAL_HEADERS}) if(NOT BUILD_QT4) set (ALKIMIA_INTERNAL_LIBS PRIVATE KF5::CoreAddons KF5::KDELibs4Support KF5::NewStuff ) endif() target_link_libraries(alkimia-internal PUBLIC ${QT_USE_LIBSPREFIX}Core ${ALKIMIA_INTERNAL_LIBS}) kde_target_enable_exceptions(alkimia-internal PUBLIC) ########### target alkimia ########### set(ALKIMIA_LIB_VERSION ${alkimia_VERSION}) set(ALKIMIA_LIB_SOVERSION "${alkimia_VERSION_MAJOR}") set(alkimia_LIB_SRCS alkcompany.cpp alkfinancequoteprocess.cpp alkonlinequote.cpp alkonlinequotesource.cpp alkonlinequotesprofile.cpp alkonlinequotesprofilemanager.cpp alkonlinequoteswidget.cpp alkvalue.cpp alkwebpage.cpp ) set(alkimia_LIB_HEADERS alkcompany.h alkfinancequoteprocess.h alkonlinequote.h alkonlinequotesource.h alkonlinequotesprofile.h alkonlinequotesprofilemanager.h alkonlinequoteswidget.h alkwebpage.h ) set(alkimia_UI alkonlinequoteswidget.ui ) configure_file( ${CMAKE_CURRENT_SOURCE_DIR}/alkvalue.h.in ${CMAKE_CURRENT_BINARY_DIR}/alkimia/alkvalue.h IMMEDIATE ) foreach(header ${alkimia_LIB_HEADERS}) configure_file( ${CMAKE_CURRENT_SOURCE_DIR}/${header} ${CMAKE_CURRENT_BINARY_DIR}/alkimia/${header} COPYONLY ) endforeach() add_definitions(-DKNSRC_DIR="${CMAKE_CURRENT_SOURCE_DIR}") ki18n_wrap_ui(alkimia_LIB_SRCS ${alkimia_UI} ) add_library(alkimia SHARED ${alkimia_LIB_SRCS} ${alkimia_INTERNAL_SRCS} ${alkimia_LIB_HEADERS}) kde_target_enable_exceptions(alkimia PUBLIC) if(BUILD_QT4) set(ALKIMIA_LIBS PRIVATE ${KDE_LIBRARIES} ${KDE4_KDEUI_LIBS} ${KDE4_KNEWSTUFF3_LIBS} ) else() set(ALKIMIA_LIBS PRIVATE KF5::KDELibs4Support KF5::NewStuff ) add_definitions(-DTRANSLATION_DOMAIN=\"alkimia\") endif() target_link_libraries(alkimia PRIVATE alkimia-internal ${QT_USE_LIBSPREFIX}Core ${ALKIMIA_LIBS} ${QT_USE_LIBSPREFIX}DBus PUBLIC ${MP_LIBRARIES} ${QT_BROWSER_LIB} ) -if(NOT BUILD_QT4 AND MSVC) +if(NOT BUILD_QT4 AND MSVC AND NOT BUILD_WITH_QWEBENGINE) message(WARNING "Applying fix for broken Qt WebKit package (see https://phabricator.kde.org/T10146 for details)") target_link_libraries(alkimia PRIVATE ${QT_USE_LIBSPREFIX}Test ) endif() install( DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/alkimia DESTINATION ${INCLUDE_INSTALL_DIR}/alkimia/${ALKIMIA_INCLUDE_SUFFIX} COMPONENT Devel ) generate_export_header(alkimia BASE_NAME alk EXPORT_FILE_NAME alkimia/alk_export.h) if(WIN32) set_target_properties(alkimia PROPERTIES SUFFIX "-${ALKIMIA_LIB_SOVERSION}${CMAKE_SHARED_LIBRARY_SUFFIX}") else() set_target_properties(alkimia PROPERTIES VERSION ${ALKIMIA_LIB_VERSION} SOVERSION ${ALKIMIA_LIB_SOVERSION}) endif() set_target_properties(alkimia PROPERTIES OUTPUT_NAME alkimia${ALKIMIA_LIB_SUFFIX}) set(INCLUDE_INSTALL_DIR include/alkimia/${ALKIMIA_INCLUDE_SUFFIX}) install(TARGETS alkimia EXPORT alkimiaTargets ${INSTALL_TARGETS_DEFAULT_ARGS} ) if(BUILD_QT4) if(WIN32) set(KNSRC_INSTALL_DIR ${SHARE_INSTALL_DIR}/config) else() set(KNSRC_INSTALL_DIR ${SHARE_INSTALL_DIR}/kde4/config) endif() set(APPDATA_INSTALL_DIR ${SHARE_INSTALL_DIR}/kde4/apps) set(FQ_LOCAL_INSTALL_PATH ${CMAKE_BINARY_DIR}/share/apps/alkimia${ALKIMIA_PATH_SUFFIX}/misc) else() set(KNSRC_INSTALL_DIR ${CONFIG_INSTALL_DIR}) set(APPDATA_INSTALL_DIR ${SHARE_INSTALL_DIR}) set(FQ_LOCAL_INSTALL_PATH ${CMAKE_BINARY_DIR}/share/alkimia${ALKIMIA_PATH_SUFFIX}/misc) endif() install(FILES alkimia-quotes.knsrc kmymoney-quotes.knsrc skrooge-quotes.knsrc DESTINATION ${KNSRC_INSTALL_DIR} ) # install perl script in expected location of build dir # KDE4: set KDEHOME to build dir # KF5: ??? make_directory(${FQ_LOCAL_INSTALL_PATH}) configure_file(${CMAKE_CURRENT_SOURCE_DIR}/financequote.pl ${FQ_LOCAL_INSTALL_PATH}/financequote.pl IMMEDIATE COPYONLY) install(PROGRAMS financequote.pl DESTINATION ${APPDATA_INSTALL_DIR}/alkimia${ALKIMIA_PATH_SUFFIX}/misc ) if (NOT WIN32) configure_file(${CMAKE_CURRENT_SOURCE_DIR}/libalkimia.pc.in ${CMAKE_CURRENT_BINARY_DIR}/libalkimia${ALKIMIA_PATH_SUFFIX}.pc IMMEDIATE @ONLY) endif(NOT WIN32) ########### create package configuration file ########### # create a Config.cmake and a ConfigVersion.cmake file and install them set(CMAKECONFIG_INSTALL_DIR "${CMAKECONFIG_INSTALL_PREFIX}/LibAlkimia${ALKIMIA_PATH_SUFFIX}-${alkimia_VERSION_MAJOR}.${alkimia_VERSION_MINOR}") ecm_setup_version(${alkimia_VERSION} VARIABLE_PREFIX ALKIMIA PACKAGE_VERSION_FILE "${CMAKE_CURRENT_BINARY_DIR}/LibAlkimia${ALKIMIA_PATH_SUFFIX}ConfigVersion.cmake" COMPATIBILITY SameMajorVersion ) configure_package_config_file( "${CMAKE_CURRENT_SOURCE_DIR}/LibAlkimiaConfig.cmake.in" "${CMAKE_CURRENT_BINARY_DIR}/LibAlkimia${ALKIMIA_PATH_SUFFIX}Config.cmake" INSTALL_DESTINATION ${CMAKECONFIG_INSTALL_DIR} PATH_VARS INCLUDE_INSTALL_DIR ) ########### install files ############### export(TARGETS alkimia FILE "${CMAKE_CURRENT_BINARY_DIR}/LibAlkimia${ALKIMIA_PATH_SUFFIX}Targets.cmake" NAMESPACE Alkimia:: ) install(EXPORT alkimiaTargets FILE LibAlkimia${ALKIMIA_PATH_SUFFIX}Targets.cmake NAMESPACE Alkimia:: DESTINATION ${CMAKECONFIG_INSTALL_DIR} ) install(FILES "${CMAKE_CURRENT_BINARY_DIR}/LibAlkimia${ALKIMIA_PATH_SUFFIX}Config.cmake" "${CMAKE_CURRENT_BINARY_DIR}/LibAlkimia${ALKIMIA_PATH_SUFFIX}ConfigVersion.cmake" "../cmake/modules/Find${MP_CMAKE_MODULE}.cmake" DESTINATION "${CMAKECONFIG_INSTALL_DIR}" COMPONENT Devel) if (NOT WIN32) install(FILES ${CMAKE_CURRENT_BINARY_DIR}/libalkimia${ALKIMIA_PATH_SUFFIX}.pc DESTINATION ${LIB_INSTALL_DIR}/pkgconfig) endif(NOT WIN32) ########### documentation ################### if(DOXYGEN_FOUND) set(APIDOC_DIR ${CMAKE_CURRENT_BINARY_DIR}/apidocs) make_directory(${APIDOC_DIR}) configure_file(${CMAKE_CURRENT_SOURCE_DIR}/libalkimia.doxygen.in ${CMAKE_CURRENT_BINARY_DIR}/libalkimia.doxygen IMMEDIATE) add_custom_target(libalkimia_apidoc ${DOXYGEN} ${CMAKE_CURRENT_BINARY_DIR}/libalkimia.doxygen) endif(DOXYGEN_FOUND) diff --git a/src/alkonlinequote.cpp b/src/alkonlinequote.cpp index 7ac7ed1..5b1b6e6 100644 --- a/src/alkonlinequote.cpp +++ b/src/alkonlinequote.cpp @@ -1,580 +1,580 @@ /*************************************************************************** * Copyright 2004 Ace Jones * * * * This file is part of libalkimia. * * * * libalkimia 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.1 of * * the License or (at your option) version 3 or any later version. * * * * libalkimia 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 "alkonlinequote.h" #include "alkdateformat.h" #include "alkexception.h" #include "alkfinancequoteprocess.h" #include "alkonlinequoteprocess.h" #include "alkonlinequotesprofile.h" #include "alkonlinequotesprofilemanager.h" #include "alkonlinequotesource.h" #include "alkwebpage.h" #include #include #include //#include #include #include #include #include #include #include #include #include #include #include #include #include #include AlkOnlineQuote::Errors::Errors() { } AlkOnlineQuote::Errors::Errors(Type type) { m_type.append(type); } AlkOnlineQuote::Errors::Errors(const Errors &e) { m_type = e.m_type; } AlkOnlineQuote::Errors &AlkOnlineQuote::Errors::operator |=(Type t) { if (!m_type.contains(t)) { m_type.append(t); } return *this; } bool AlkOnlineQuote::Errors::operator &(Type t) const { return m_type.contains(t); } class AlkOnlineQuote::Private : public QObject { Q_OBJECT public: AlkOnlineQuote *m_p; AlkOnlineQuoteProcess m_filter; QString m_quoteData; QString m_symbol; QString m_id; QDate m_date; double m_price; AlkOnlineQuoteSource m_source; AlkOnlineQuote::Errors m_errors; KUrl m_url; QEventLoop *m_eventLoop; QString m_acceptLanguage; AlkOnlineQuotesProfile *m_profile; bool m_ownProfile; static int dbgArea() { static int s_area = KDebug::registerArea("Alkimia (AlkOnlineQuote)"); return s_area; } Private(AlkOnlineQuote *parent) : m_p(parent) , m_eventLoop(nullptr) , m_ownProfile(false) { connect(&m_filter, SIGNAL(processExited(QString)), this, SLOT(slotParseQuote(QString))); } ~Private() { if (m_ownProfile) delete m_profile; } bool initLaunch(const QString &_symbol, const QString &_id, const QString &_source); bool launchWebKitCssSelector(const QString &_symbol, const QString &_id, const QString &_source); bool launchWebKitHtmlParser(const QString &_symbol, const QString &_id, const QString &_source); bool launchNative(const QString &_symbol, const QString &_id, const QString &_source); bool launchFinanceQuote(const QString &_symbol, const QString &_id, const QString &_source); void enter_loop(); bool parsePrice(const QString &pricestr); bool parseDate(const QString &datestr); public slots: void slotLoadStarted(); void slotLoadFinishedHtmlParser(bool ok); void slotLoadFinishedCssSelector(bool ok); bool slotParseQuote(const QString &_quotedata); }; bool AlkOnlineQuote::Private::initLaunch(const QString &_symbol, const QString &_id, const QString &_source) { m_symbol = _symbol; m_id = _id; m_errors = Errors::None; emit m_p->status(QString("(Debug) symbol=%1 id=%2...").arg(_symbol, _id)); // Get sources from the config file QString source = _source; if (source.isEmpty()) { source = "KMyMoney Currency"; } if (!m_profile->quoteSources().contains(source)) { emit m_p->error(i18n("Source %1 does not exist.", source)); m_errors |= Errors::Source; return false; } //m_profile->createSource(source); m_source = AlkOnlineQuoteSource(source, m_profile); KUrl url; // if the source has room for TWO symbols.. if (m_source.url().contains("%2")) { // this is a two-symbol quote. split the symbol into two. valid symbol // characters are: 0-9, A-Z and the dot. anything else is a separator QRegExp splitrx("([0-9a-z\\.]+)[^a-z0-9]+([0-9a-z\\.]+)", Qt::CaseInsensitive); // if we've truly found 2 symbols delimited this way... if (splitrx.indexIn(m_symbol) != -1) { url = KUrl(m_source.url().arg(splitrx.cap(1), splitrx.cap(2))); } else { kDebug(Private::dbgArea()) << "WebPriceQuote::launch() did not find 2 symbols"; } } else { // a regular one-symbol quote url = KUrl(m_source.url().arg(m_symbol)); } m_url = url; return true; } void AlkOnlineQuote::Private::slotLoadFinishedHtmlParser(bool ok) { if (!ok) { emit m_p->error(i18n("Unable to fetch url for %1").arg(m_symbol)); m_errors |= Errors::URL; emit m_p->failed(m_id, m_symbol); } else { // parse symbol slotParseQuote(AlkOnlineQuotesProfileManager::instance().webPage()->toHtml()); } if (m_eventLoop) m_eventLoop->exit(); } void AlkOnlineQuote::Private::slotLoadFinishedCssSelector(bool ok) { if (!ok) { emit m_p->error(i18n("Unable to fetch url for %1").arg(m_symbol)); m_errors |= Errors::URL; emit m_p->failed(m_id, m_symbol); } else { AlkWebPage *webPage = AlkOnlineQuotesProfileManager::instance().webPage(); // parse symbol QString symbol = webPage->getFirstElement(m_source.sym()); if (!symbol.isEmpty()) { emit m_p->status(i18n("Symbol found: '%1'", symbol)); } else { m_errors |= Errors::Symbol; emit m_p->error(i18n("Unable to parse symbol for %1", m_symbol)); } // parse price QString price = webPage->getFirstElement(m_source.price()); bool gotprice = parsePrice(price); // parse date QString date = webPage->getFirstElement(m_source.date()); bool gotdate = parseDate(date); if (gotprice && gotdate) { emit m_p->quote(m_id, m_symbol, m_date, m_price); } else { emit m_p->failed(m_id, m_symbol); } } if (m_eventLoop) m_eventLoop->exit(); } void AlkOnlineQuote::Private::slotLoadStarted() { emit m_p->status(i18n("Fetching URL %1...", m_url.prettyUrl())); } bool AlkOnlineQuote::Private::launchWebKitCssSelector(const QString &_symbol, const QString &_id, const QString &_source) { if (!initLaunch(_symbol, _id, _source)) { return false; } AlkWebPage *webPage = AlkOnlineQuotesProfileManager::instance().webPage(); connect(webPage, SIGNAL(loadStarted()), this, SLOT(slotLoadStarted())); connect(webPage, SIGNAL(loadFinished(bool)), this, SLOT(slotLoadFinishedCssSelector(bool))); webPage->setUrl(m_url); m_eventLoop = new QEventLoop; m_eventLoop->exec(); delete m_eventLoop; disconnect(webPage, SIGNAL(loadStarted()), this, SLOT(slotLoadStarted())); disconnect(webPage, SIGNAL(loadFinished(bool)), this, SLOT(slotLoadFinishedCssSelector(bool))); return !(m_errors & Errors::URL || m_errors & Errors::Price || m_errors & Errors::Date || m_errors & Errors::Data); } bool AlkOnlineQuote::Private::launchWebKitHtmlParser(const QString &_symbol, const QString &_id, const QString &_source) { if (!initLaunch(_symbol, _id, _source)) { return false; } AlkWebPage *webPage = AlkOnlineQuotesProfileManager::instance().webPage(); connect(webPage, SIGNAL(loadStarted()), this, SLOT(slotLoadStarted())); connect(webPage, SIGNAL(loadFinished(bool)), this, SLOT(slotLoadFinishedHtmlParser(bool))); webPage->load(m_url, m_acceptLanguage); m_eventLoop = new QEventLoop; m_eventLoop->exec(); delete m_eventLoop; disconnect(webPage, SIGNAL(loadStarted()), this, SLOT(slotLoadStarted())); disconnect(webPage, SIGNAL(loadFinished(bool)), this, SLOT(slotLoadFinishedHtmlParser(bool))); return !(m_errors & Errors::URL || m_errors & Errors::Price || m_errors & Errors::Date || m_errors & Errors::Data); } bool AlkOnlineQuote::Private::launchNative(const QString &_symbol, const QString &_id, const QString &_source) { bool result = true; if (!initLaunch(_symbol, _id, _source)) { return false; } KUrl url = m_url; if (url.isLocalFile()) { emit m_p->status(i18nc("The process x is executing", "Executing %1...", url.toLocalFile())); m_filter.clearProgram(); m_filter << url.toLocalFile().split(' ', QString::SkipEmptyParts); m_filter.setSymbol(m_symbol); m_filter.setOutputChannelMode(KProcess::MergedChannels); m_filter.start(); if (m_filter.waitForStarted()) { result = true; } else { emit m_p->error(i18n("Unable to launch: %1", url.toLocalFile())); m_errors |= Errors::Script; result = slotParseQuote(QString()); } } else { slotLoadStarted(); QString tmpFile; if (KIO::NetAccess::download(url, tmpFile, 0)) { // kDebug(Private::dbgArea()) << "Downloaded " << tmpFile; kDebug(Private::dbgArea()) << "Downloaded" << tmpFile << "from" << url; QFile f(tmpFile); if (f.open(QIODevice::ReadOnly)) { // Find out the page encoding and convert it to unicode QByteArray page = f.readAll(); KEncodingProber prober(KEncodingProber::Universal); prober.feed(page); QTextCodec *codec = QTextCodec::codecForName(prober.encoding()); if (!codec) { codec = QTextCodec::codecForLocale(); } QString quote = codec->toUnicode(page); f.close(); emit m_p->status(i18n("URL found: %1...", url.prettyUrl())); if (AlkOnlineQuotesProfileManager::instance().webPageEnabled()) - AlkOnlineQuotesProfileManager::instance().webPage()->setContent(quote.toLocal8Bit()); + AlkOnlineQuotesProfileManager::instance().webPage()->setContent(quote.toLocal8Bit(), "text/html"); result = slotParseQuote(quote); } else { emit m_p->error(i18n("Failed to open downloaded file")); m_errors |= Errors::URL; result = slotParseQuote(QString()); } KIO::NetAccess::removeTempFile(tmpFile); } else { emit m_p->error(KIO::NetAccess::lastErrorString()); m_errors |= Errors::URL; result = slotParseQuote(QString()); } } return result; } bool AlkOnlineQuote::Private::launchFinanceQuote(const QString &_symbol, const QString &_id, const QString &_sourcename) { bool result = true; m_symbol = _symbol; m_id = _id; m_errors = Errors::None; m_source = AlkOnlineQuoteSource(_sourcename, m_profile->scriptPath(), "\"([^,\"]*)\",.*", // symbol regexp "[^,]*,[^,]*,\"([^\"]*)\"", // price regexp "[^,]*,([^,]*),.*", // date regexp "%y-%m-%d"); // date format //emit status(QString("(Debug) symbol=%1 id=%2...").arg(_symbol,_id)); AlkFinanceQuoteProcess tmp; QString fQSource = m_profile->type() == AlkOnlineQuotesProfile::Type::Script ? tmp.crypticName(_sourcename) : _sourcename.section(' ', 1); QStringList args; args << "perl" << m_profile->scriptPath() << fQSource << m_symbol; m_filter.clearProgram(); m_filter << args; emit m_p->status(i18nc("Executing 'script' 'online source' 'investment symbol' ", "Executing %1 %2 %3...", args.join(" "), QString(), QString())); m_filter.setOutputChannelMode(KProcess::MergedChannels); m_filter.start(); // This seems to work best if we just block until done. if (m_filter.waitForFinished()) { } else { emit m_p->error(i18n("Unable to launch: %1", m_profile->scriptPath())); m_errors |= Errors::Script; result = slotParseQuote(QString()); } return result; } bool AlkOnlineQuote::Private::parsePrice(const QString &_pricestr) { bool result = true; // Deal with european quotes that come back as X.XXX,XX or XX,XXX // // We will make the assumption that ALL prices have a decimal separator. // So "1,000" always means 1.0, not 1000.0. // // Remove all non-digits from the price string except the last one, and // set the last one to a period. QString pricestr(_pricestr); if (!pricestr.isEmpty()) { int pos = pricestr.lastIndexOf(QRegExp("\\D")); if (pos > 0) { pricestr[pos] = '.'; pos = pricestr.lastIndexOf(QRegExp("\\D"), pos - 1); } while (pos > 0) { pricestr.remove(pos, 1); pos = pricestr.lastIndexOf(QRegExp("\\D"), pos); } m_price = pricestr.toDouble(); kDebug(Private::dbgArea()) << "Price" << pricestr; emit m_p->status(i18n("Price found: '%1' (%2)", pricestr, m_price)); } else { m_errors |= Errors::Price; emit m_p->error(i18n("Unable to parse price for '%1'", m_symbol)); result = false; } return result; } bool AlkOnlineQuote::Private::parseDate(const QString &datestr) { if (!datestr.isEmpty()) { emit m_p->status(i18n("Date found: '%1'", datestr)); AlkDateFormat dateparse(m_source.dateformat()); try { m_date = dateparse.convertString(datestr, false /*strict*/); kDebug(Private::dbgArea()) << "Date" << datestr; emit m_p->status(i18n("Date format found: '%1' -> '%2'", datestr, m_date.toString())); } catch (const AlkException &e) { m_errors |= Errors::DateFormat; emit m_p->error(i18n("Unable to parse date '%1' using format '%2': %3").arg(datestr, dateparse.format(), e.what())); m_date = QDate::currentDate(); emit m_p->status(i18n("Using current date for '%1'").arg(m_symbol)); } } else { m_errors |= Errors::Date; emit m_p->error(i18n("Unable to parse date for '%1'").arg(m_symbol)); m_date = QDate::currentDate(); emit m_p->status(i18n("Using current date for '%1'").arg(m_symbol)); } return true; } /** * Parse quote data according to currently selected web price quote source * * @param _quotedata quote data to parse * @return true parsing successful * @return false parsing unsuccessful */ bool AlkOnlineQuote::Private::slotParseQuote(const QString &_quotedata) { QString quotedata = _quotedata; m_quoteData = quotedata; bool gotprice = false; bool gotdate = false; bool result = true; kDebug(Private::dbgArea()) << "quotedata" << _quotedata; if (!quotedata.isEmpty()) { if (!m_source.skipStripping()) { // // First, remove extraneous non-data elements // // HTML tags quotedata.remove(QRegExp("<[^>]*>")); // &...;'s quotedata.replace(QRegExp("&\\w+;"), " "); // Extra white space quotedata = quotedata.simplified(); kDebug(Private::dbgArea()) << "stripped text" << quotedata; } QRegExp symbolRegExp(m_source.sym()); QRegExp dateRegExp(m_source.date()); QRegExp priceRegExp(m_source.price()); if (symbolRegExp.indexIn(quotedata) > -1) { kDebug(Private::dbgArea()) << "Symbol" << symbolRegExp.cap(1); emit m_p->status(i18n("Symbol found: '%1'", symbolRegExp.cap(1))); } else { m_errors |= Errors::Symbol; emit m_p->error(i18n("Unable to parse symbol for %1", m_symbol)); } if (priceRegExp.indexIn(quotedata) > -1) { gotprice = true; QString pricestr = priceRegExp.cap(1); parsePrice(pricestr); } else { parsePrice(QString()); } if (dateRegExp.indexIn(quotedata) > -1) { gotdate = true; QString datestr = dateRegExp.cap(1); parseDate(datestr); } else { parseDate(QString()); } if (gotprice && gotdate) { emit m_p->quote(m_id, m_symbol, m_date, m_price); } else { emit m_p->failed(m_id, m_symbol); result = false; } } else { m_errors |= Errors::Data; emit m_p->error(i18n("Unable to update price for %1 (empty quote data)", m_symbol)); emit m_p->failed(m_id, m_symbol); result = false; } return result; } AlkOnlineQuote::AlkOnlineQuote(AlkOnlineQuotesProfile *profile, QObject *_parent) : QObject(_parent) , d(new Private(this)) { if (profile) d->m_profile = profile; else { d->m_profile = new AlkOnlineQuotesProfile; d->m_ownProfile = true; } } AlkOnlineQuote::~AlkOnlineQuote() { delete d; } AlkOnlineQuotesProfile *AlkOnlineQuote::profile() { return d->m_profile; } void AlkOnlineQuote::setProfile(AlkOnlineQuotesProfile *profile) { if (profile && d->m_ownProfile) { // switching from own profile to external delete d->m_profile; d->m_ownProfile = false; d->m_profile = profile; } else if (!profile && !d->m_ownProfile) { // switching from external to own profile d->m_profile = new AlkOnlineQuotesProfile; d->m_ownProfile = true; } else if (profile) { // exchange external profile d->m_profile = profile; } } void AlkOnlineQuote::setAcceptLanguage(const QString &language) { d->m_acceptLanguage = language; } bool AlkOnlineQuote::launch(const QString &_symbol, const QString &_id, const QString &_source) { if (AlkOnlineQuoteSource::isFinanceQuote(_source) || d->m_profile->type() == AlkOnlineQuotesProfile::Type::Script) { return d->launchFinanceQuote(_symbol, _id, _source); } else if (_source.endsWith(".css")) { return d->launchWebKitCssSelector(_symbol, _id, _source); } else if (_source.endsWith(".webkit")) { return d->launchWebKitHtmlParser(_symbol, _id, _source); } else { return d->launchNative(_symbol, _id, _source); } } const AlkOnlineQuote::Errors &AlkOnlineQuote::errors() { return d->m_errors; } #include "alkonlinequote.moc" diff --git a/src/alkonlinequotesprofile.cpp b/src/alkonlinequotesprofile.cpp index 4fb8e9d..c3a6c96 100644 --- a/src/alkonlinequotesprofile.cpp +++ b/src/alkonlinequotesprofile.cpp @@ -1,423 +1,423 @@ /*************************************************************************** * Copyright 2018 Ralf Habacker * * * * This file is part of libalkimia. * * * * libalkimia is free software; you can redistribute it and/or * * modify it under the terms of the GNU Lesser General Public License * * as published by the Free Software Foundation; either version 2.1 of * * the License or (at your option) version 3 or any later version. * * * * libalkimia 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 "alkonlinequotesprofile.h" #include "alkonlinequotesprofilemanager.h" #include "alkonlinequotesource.h" #include "alkfinancequoteprocess.h" #include #include #include #include #include #include #include #include #include #include #include class AlkOnlineQuotesProfile::Private : public QObject { Q_OBJECT public: AlkOnlineQuotesProfile *m_p; QString m_name; QString m_GHNSFile; QString m_GHNSFilePath; QString m_kconfigFile; AlkOnlineQuotesProfileManager *m_profileManager; KNS3::DownloadManager *m_manager; KConfig *m_config; Type m_type; static QString m_financeQuoteScriptPath; static QStringList m_financeQuoteSources; Private(AlkOnlineQuotesProfile *p) : m_p(p) , m_profileManager(0) , m_manager(0) , m_config(0) , m_type(Type::Undefined) { if (m_financeQuoteScriptPath.isEmpty()) { m_financeQuoteScriptPath = KGlobal::dirs()->findResource("appdata", QString("misc/financequote.pl")); } } ~Private() { delete m_manager; delete m_config; } void checkUpdates() { m_manager = new KNS3::DownloadManager(m_p->hotNewStuffConfigFile(), this); // to know when checking for updates is done connect(m_manager, SIGNAL(searchResult(KNS3::Entry::List)), this, SLOT(slotUpdatesFound(KNS3::Entry::List))); // to know about finished installations connect(m_manager, SIGNAL(entryStatusChanged(KNS3::Entry)), this, SLOT(entryStatusChanged(KNS3::Entry))); // start checking for updates m_manager->checkForUpdates(); } public Q_SLOTS: void slotUpdatesFound(const KNS3::Entry::List &updates) { foreach (const KNS3::Entry &entry, updates) { qDebug() << entry.name(); } } // to know about finished installations void entryStatusChanged(const KNS3::Entry &entry) { qDebug() << entry.summary(); } const QStringList quoteSourcesNative() { //KSharedConfigPtr kconfig = KGlobal::config(); KConfig config(m_kconfigFile); KConfig *kconfig = &config; QStringList groups = kconfig->groupList(); QStringList::Iterator it; QRegExp onlineQuoteSource(QString("^Online-Quote-Source-(.*)$")); // get rid of all 'non online quote source' entries for (it = groups.begin(); it != groups.end(); it = groups.erase(it)) { if (onlineQuoteSource.indexIn(*it) >= 0) { // Insert the name part it = groups.insert(it, onlineQuoteSource.cap(1)); ++it; } } // Set up each of the default sources. These are done piecemeal so that // when we add a new source, it's automatically picked up. And any changes // are also picked up. QMap defaults = defaultQuoteSources(); QMap::iterator it_source = defaults.begin(); while (it_source != defaults.end()) { if (!groups.contains((*it_source).name())) { groups += (*it_source).name(); (*it_source).write(); kconfig->sync(); } ++it_source; } return groups; } const QStringList quoteSourcesFinanceQuote() { if (m_financeQuoteSources.empty()) { // run the process one time only // since this is a static function it can be called without constructing an object // so we need to make sure that m_financeQuoteScriptPath is properly initialized if (m_financeQuoteScriptPath.isEmpty()) { m_financeQuoteScriptPath = KGlobal::dirs()->findResource("data", QString("alkimia/misc/financequote.pl")); } AlkFinanceQuoteProcess getList; getList.launch(m_financeQuoteScriptPath); while (!getList.isFinished()) { qApp->processEvents(); } m_financeQuoteSources = getList.getSourceList(); } return m_financeQuoteSources; } const QStringList quoteSourcesSkrooge() { return quoteSourcesGHNS(); } const QStringList quoteSourcesGHNS() { QStringList sources; QString relPath = m_GHNSFilePath; foreach (const QString &file, KStandardDirs().findAllResources("data", relPath + QString::fromLatin1("/*.txt"))) { QFileInfo f(file); QString file2 = f.completeBaseName(); AlkOnlineQuoteSource source(file2, m_p); if (source.isEmpty()) { qDebug() << "skipping" << file2; continue; } if (!sources.contains(file2)) { sources.push_back(file2); } } return sources; } const AlkOnlineQuotesProfile::Map defaultQuoteSources() { QMap result; // Use fx-rate.net as the standard currency exchange rate source until // we have the capability to use more than one source. Use a neutral // name for the source. switch (m_p->type()) { case AlkOnlineQuotesProfile::Type::None: case AlkOnlineQuotesProfile::Type::Alkimia4: case AlkOnlineQuotesProfile::Type::Alkimia5: { AlkOnlineQuoteSource source("Alkimia Currency", "https://fx-rate.net/%1/%2", QString(), // symbolregexp "1[ a-zA-Z]+=
*(\\d+\\.\\d+)", "updated\\s\\d+:\\d+:\\d+\\(\\w+\\)\\s+(\\d{1,2}/\\d{2}/\\d{4})", "%d/%m/%y", true // skip HTML stripping ); source.setProfile(m_p); result[source.name()] = source; -#if defined(BUILD_WITH_WEBKIT) +#if defined(BUILD_WITH_WEBKIT) || defined(BUILD_WITH_WEBENGINE) source.setName(source.name() + ".webkit"); result[source.name()] = source; #endif break; } default: break; } return result; } /** * @brief return data root path * @return path */ QString dataRootPath() { return QLibraryInfo::location(QLibraryInfo::PrefixPath) + "/share"; } /** * @brief return home root path * @return path */ QString homeRootPath() { if (m_type == Type::KMyMoney5 || m_type == Type::Alkimia5 || m_type == Type::Skrooge5) return QDir::homePath(); else if (m_type == Type::KMyMoney4 || m_type == Type::Alkimia4 || m_type == Type::Skrooge4) { #ifdef Q_OS_WIN return qgetenv("APPDATA"); #else return QDir::homePath(); #endif } else { return QString(); } } QString configPath() { if (m_type == Type::KMyMoney5 || m_type == Type::Alkimia5 || m_type == Type::Skrooge5) return QString("%1/.config").arg(homeRootPath()); else if (m_type == Type::KMyMoney4 || m_type == Type::Alkimia4 || m_type == Type::Skrooge4) return QString("%1/.kde4/share/config").arg(homeRootPath()); return QString(); } QString dataReadPath() { if (m_type == Type::KMyMoney5 || m_type == Type::Alkimia5 || m_type == Type::Skrooge5) return dataRootPath(); else if (m_type == Type::KMyMoney4 || m_type == Type::Alkimia4 || m_type == Type::Skrooge4) return QString("%1/kde4/apps").arg(dataRootPath()); return QString(); } QString dataWritePath() { if (m_type == Type::KMyMoney5 || m_type == Type::Alkimia5 || m_type == Type::Skrooge5) return QString("%1/.local/share").arg(homeRootPath()); else if (m_type == Type::KMyMoney4 || m_type == Type::Alkimia4 || m_type == Type::Skrooge4) return QString("%1/.kde4/share/apps").arg(homeRootPath()); return QString(); } }; // define static members QString AlkOnlineQuotesProfile::Private::m_financeQuoteScriptPath; QStringList AlkOnlineQuotesProfile::Private::m_financeQuoteSources; AlkOnlineQuotesProfile::AlkOnlineQuotesProfile(const QString &name, Type type, const QString &ghnsConfigFile) : d(new Private(this)) { d->m_name = name; d->m_GHNSFile = ghnsConfigFile; d->m_type = type; if (type == Type::KMyMoney5) d->m_kconfigFile = QString("%1/kmymoney/kmymoneyrc").arg(d->configPath()); else if (type == Type::KMyMoney4) d->m_kconfigFile = QString("%1/kmymoneyrc").arg(d->configPath()); else if (type == Type::Alkimia5 || type == Type::Alkimia4) d->m_kconfigFile = QString("%1/alkimiarc").arg(d->configPath()); else d->m_kconfigFile = ""; if (!d->m_kconfigFile.isEmpty()) d->m_config = new KConfig(d->m_kconfigFile); if (!d->m_GHNSFile.isEmpty()) { KConfig ghnsFile(hotNewStuffConfigFile()); KConfigGroup group = ghnsFile.group("KNewStuff3"); d->m_GHNSFilePath = group.readEntry("TargetDir"); d->checkUpdates(); } } AlkOnlineQuotesProfile::~AlkOnlineQuotesProfile() { delete d; } QString AlkOnlineQuotesProfile::name() const { return d->m_name; } QString AlkOnlineQuotesProfile::hotNewStuffConfigFile() const { QString configFile = KStandardDirs::locate("config", d->m_GHNSFile); if (configFile.isEmpty()) { configFile = QString("%1/%2").arg(KNSRC_DIR, d->m_GHNSFile); } return configFile; } QString AlkOnlineQuotesProfile::hotNewStuffReadFilePath(const QString &fileName) const { foreach(const QString &path, hotNewStuffReadPath()) { QFileInfo f(path + fileName); if (f.exists()) return f.absoluteFilePath(); } return QString(); } QString AlkOnlineQuotesProfile::hotNewStuffWriteFilePath(const QString &fileName) const { return QString("%1%2").arg(hotNewStuffWriteDir(), fileName); } QStringList AlkOnlineQuotesProfile::hotNewStuffReadPath() const { return QStringList() << QString("%1/%2/").arg(d->dataReadPath(), d->m_GHNSFilePath) << hotNewStuffWriteDir(); } QString AlkOnlineQuotesProfile::hotNewStuffWriteDir() const { return QString("%1/%2/").arg(d->dataWritePath(), d->m_GHNSFilePath); } QString AlkOnlineQuotesProfile::hotNewStuffRelPath() const { return d->m_GHNSFilePath; } QString AlkOnlineQuotesProfile::kConfigFile() const { return d->m_kconfigFile; } KConfig *AlkOnlineQuotesProfile::kConfig() const { return d->m_config; } AlkOnlineQuotesProfile::Type AlkOnlineQuotesProfile::type() { return d->m_type; } bool AlkOnlineQuotesProfile::hasGHNSSupport() { return !d->m_GHNSFile.isEmpty(); } const AlkOnlineQuotesProfile::Map AlkOnlineQuotesProfile::defaultQuoteSources() { return d->defaultQuoteSources(); } const QStringList AlkOnlineQuotesProfile::quoteSources() { QStringList result; switch(d->m_type) { case AlkOnlineQuotesProfile::Type::Alkimia4: case AlkOnlineQuotesProfile::Type::Alkimia5: case AlkOnlineQuotesProfile::Type::KMyMoney4: case AlkOnlineQuotesProfile::Type::KMyMoney5: result << d->quoteSourcesNative(); break; case AlkOnlineQuotesProfile::Type::Script: result << d->quoteSourcesFinanceQuote(); break; case AlkOnlineQuotesProfile::Type::None: result << d->defaultQuoteSources().keys(); break; default: break; } if (hasGHNSSupport()) result << d->quoteSourcesGHNS(); return result; } void AlkOnlineQuotesProfile::setManager(AlkOnlineQuotesProfileManager *manager) { d->m_profileManager = manager; } AlkOnlineQuotesProfileManager *AlkOnlineQuotesProfile::manager() { return d->m_profileManager; } QString AlkOnlineQuotesProfile::scriptPath() { return d->m_financeQuoteScriptPath; } #include "alkonlinequotesprofile.moc" diff --git a/src/alkwebpage.cpp b/src/alkwebpage.cpp index cf409fb..2604f8b 100644 --- a/src/alkwebpage.cpp +++ b/src/alkwebpage.cpp @@ -1,171 +1,212 @@ /*************************************************************************** * Copyright 2018 Ralf Habacker * * * * This file is part of libalkimia. * * * * libalkimia is free software; you can redistribute it and/or * * modify it under the terms of the GNU Lesser General Public License * * as published by the Free Software Foundation; either version 2.1 of * * the License or (at your option) version 3 or any later version. * * * * libalkimia 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 "alkwebpage.h" -#if defined(BUILD_WITH_WEBKIT) +#if defined(BUILD_WITH_WEBENGINE) +#include +#include +#include + +QWidget *AlkWebPage::widget() +{ + if (!view()) + setView(new QWebEngineView); + return view(); +} + +void AlkWebPage::load(const QUrl &url, const QString &acceptLanguage) +{ + Q_UNUSED(acceptLanguage) + + setUrl(url); +} + +QString AlkWebPage::toHtml() +{ + return QString(); +} + +QString AlkWebPage::getFirstElement(const QString &symbol) +{ + Q_UNUSED(symbol) + + return QString(); +} + +void AlkWebPage::setWebInspectorEnabled(bool state) +{ + Q_UNUSED(state) +} + +bool AlkWebPage::webInspectorEnabled() +{ + return false; +} + +#elif defined(BUILD_WITH_WEBKIT) #include #include #include #include #include class AlkWebPage::Private { public: QWebInspector *inspector; Private() : inspector(nullptr) { } ~Private() { delete inspector; } void setWebInspectorEnabled(bool enable, QWebPage* page) { page->settings()->setAttribute(QWebSettings::DeveloperExtrasEnabled, enable); delete inspector; inspector = nullptr; if (enable) { inspector = new QWebInspector(); inspector->setPage(page); } } bool webInspectorEnabled(QWebPage *page) { return page->settings()->testAttribute(QWebSettings::DeveloperExtrasEnabled); } }; AlkWebPage::AlkWebPage(QWidget *parent) : QWebView(parent) , d(new Private) { page()->settings()->setAttribute(QWebSettings::JavaEnabled, false); page()->settings()->setAttribute(QWebSettings::AutoLoadImages, false); page()->settings()->setAttribute(QWebSettings::PluginsEnabled, false); } AlkWebPage::~AlkWebPage() { delete d; } QWidget *AlkWebPage::widget() { return this; } void AlkWebPage::load(const QUrl &url, const QString &acceptLanguage) { QNetworkRequest request; request.setUrl(url); if (!acceptLanguage.isEmpty()) request.setRawHeader("Accept-Language", acceptLanguage.toLocal8Bit()); QWebView::load(request); } QString AlkWebPage::toHtml() { QWebFrame *frame = page()->mainFrame(); return frame->toHtml(); } QString AlkWebPage::getFirstElement(const QString &symbol) { QWebFrame *frame = page()->mainFrame(); QWebElement element = frame->findFirstElement(symbol); return element.toPlainText(); } void AlkWebPage::setWebInspectorEnabled(bool enable) { d->setWebInspectorEnabled(enable, page()); } bool AlkWebPage::webInspectorEnabled() { return d->webInspectorEnabled(page()); } #else class AlkWebPage::Private { public: }; AlkWebPage::AlkWebPage(QWidget *parent) : QWidget(parent) , d(new Private) { } AlkWebPage::~AlkWebPage() { delete d; } QWidget *AlkWebPage::widget() { return this; } void AlkWebPage::load(const QUrl &url, const QString &acceptLanguage) { Q_UNUSED(url) Q_UNUSED(acceptLanguage) } void AlkWebPage::setUrl(const QUrl &url) { Q_UNUSED(url) } void AlkWebPage::setContent(const QString &s) { Q_UNUSED(s) } QString AlkWebPage::toHtml() { return QString(); } QString AlkWebPage::getFirstElement(const QString &symbol) { Q_UNUSED(symbol) return QString(); } void AlkWebPage::setWebInspectorEnabled(bool enable) { Q_UNUSED(enable) } bool AlkWebPage::webInspectorEnabled() { return false; } #endif diff --git a/src/alkwebpage.h b/src/alkwebpage.h index dc9686f..43aaef6 100644 --- a/src/alkwebpage.h +++ b/src/alkwebpage.h @@ -1,93 +1,108 @@ /*************************************************************************** * Copyright 2018 Ralf Habacker * * * * This file is part of libalkimia. * * * * libalkimia is free software; you can redistribute it and/or * * modify it under the terms of the GNU Lesser General Public License * * as published by the Free Software Foundation; either version 2.1 of * * the License or (at your option) version 3 or any later version. * * * * libalkimia 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 ALKWEBPAGE_H #define ALKWEBPAGE_H #include #include class QUrl; -#if defined(BUILD_WITH_WEBKIT) +#if defined(BUILD_WITH_WEBENGINE) + +#include + +class ALK_EXPORT AlkWebPage : public QWebEnginePage +{ +public: + QWidget *widget(); + void load(const QUrl &url, const QString &acceptLanguage); + QString toHtml(); + QString getFirstElement(const QString &symbol); + void setWebInspectorEnabled(bool state); + bool webInspectorEnabled(); +}; + +#elif defined(BUILD_WITH_WEBKIT) #include /** * The AlkWebPage class provides an interface * to a browser component with javascript support * It is used for fetching and showing web pages. * * @author Ralf Habacker */ class ALK_EXPORT AlkWebPage : public QWebView { public: AlkWebPage(QWidget *parent = nullptr); virtual ~AlkWebPage(); QWidget *widget(); void load(const QUrl &url, const QString &acceptLanguage); QString toHtml(); QString getFirstElement(const QString &symbol); void setWebInspectorEnabled(bool enable); bool webInspectorEnabled(); private: class Private; Private *d; }; #else #include /** * The AlkWebPage class provides an interface * to a browser component with javascript support * It is used for fetching and showing web pages. * * @author Ralf Habacker */ class ALK_EXPORT AlkWebPage : public QWidget { public: AlkWebPage(QWidget *parent = nullptr); virtual ~AlkWebPage(); QWidget *widget(); void load(const QUrl &url, const QString &acceptLanguage); void setUrl(const QUrl &url); void setContent(const QString &s); QString toHtml(); QString getFirstElement(const QString &symbol); void setWebInspectorEnabled(bool enable); bool webInspectorEnabled(); Q_SIGNALS: void loadStarted(); void loadFinished(bool); private: class Private; Private *d; }; #endif #endif // ALKWEBPAGE_H diff --git a/tools/onlinequoteseditor/mainwindow.cpp b/tools/onlinequoteseditor/mainwindow.cpp index 4de9ae8..59af0c4 100644 --- a/tools/onlinequoteseditor/mainwindow.cpp +++ b/tools/onlinequoteseditor/mainwindow.cpp @@ -1,170 +1,170 @@ /*************************************************************************** * Copyright 2018 Ralf Habacker * * * * This file is part of libalkimia. * * * * libalkimia 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.1 of * * the License or (at your option) version 3 or any later version. * * * * libalkimia 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 "mainwindow.h" #include "ui_mainwindow.h" #include "alkonlinequotesprofile.h" #include "alkonlinequotesprofilemanager.h" #include "alkonlinequoteswidget.h" #include "alkwebpage.h" #include #include #include #include class MainWindow::Private { public: Private() : urlLine(nullptr) , quotesWidget(nullptr) { } ~Private() { delete quotesWidget; } QLineEdit *urlLine; AlkOnlineQuotesWidget *quotesWidget; Ui::MainWindow ui; }; void MainWindow::slotUrlChanged(const QUrl &url) { d->urlLine->setText(url.toString()); } void MainWindow::slotEditingFinished() { AlkOnlineQuotesProfileManager::instance().webPage()->load(QUrl(d->urlLine->text()), d->quotesWidget->acceptLanguage()); } void MainWindow::slotLanguageChanged(const QString &text) { d->quotesWidget->setAcceptLanguage(text); if (!d->urlLine->text().isEmpty()) slotEditingFinished(); } MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) , ApplicationSettings(this, false) , d(new Private) { AlkOnlineQuotesProfileManager &manager = AlkOnlineQuotesProfileManager::instance(); manager.addProfile(new AlkOnlineQuotesProfile("no-config-file", AlkOnlineQuotesProfile::Type::None)); #if QT_VERSION < QT_VERSION_CHECK(5,0,0) manager.addProfile(new AlkOnlineQuotesProfile("alkimia4", AlkOnlineQuotesProfile::Type::Alkimia4, "alkimia-quotes.knsrc")); manager.addProfile(new AlkOnlineQuotesProfile("skrooge4", AlkOnlineQuotesProfile::Type::Skrooge4, "skrooge-quotes.knsrc")); manager.addProfile(new AlkOnlineQuotesProfile("kmymoney4", AlkOnlineQuotesProfile::Type::KMyMoney4, "kmymoney-quotes.knsrc")); #else manager.addProfile(new AlkOnlineQuotesProfile("alkimia5", AlkOnlineQuotesProfile::Type::Alkimia5, "alkimia-quotes.knsrc")); manager.addProfile(new AlkOnlineQuotesProfile("skrooge5", AlkOnlineQuotesProfile::Type::Skrooge5, "skrooge-quotes.knsrc")); manager.addProfile(new AlkOnlineQuotesProfile("kmymoney5", AlkOnlineQuotesProfile::Type::KMyMoney5, "kmymoney-quotes.knsrc")); #endif manager.addProfile(new AlkOnlineQuotesProfile("Finance::Quote", AlkOnlineQuotesProfile::Type::Script)); d->ui.setupUi(this); d->quotesWidget = new AlkOnlineQuotesWidget(true, true); QDockWidget *profilesWidget = new QDockWidget(i18n("Profiles"), this); profilesWidget->setObjectName("profilesDockWidget"); profilesWidget->setWidget(d->quotesWidget->profilesWidget()); addDockWidget(Qt::LeftDockWidgetArea, profilesWidget); QDockWidget *profileDetailsWidget = new QDockWidget(i18n("Profile details"), this); profileDetailsWidget->setObjectName("profileDetailsDockWidget"); profileDetailsWidget->setWidget(d->quotesWidget->profileDetailsWidget()); addDockWidget(Qt::RightDockWidgetArea, profileDetailsWidget); QDockWidget *onlineQuotesWidget = new QDockWidget(i18n("Online quotes"), this); onlineQuotesWidget->setObjectName("onlineQuotesDockWidget"); onlineQuotesWidget->setWidget(d->quotesWidget->onlineQuotesWidget()); addDockWidget(Qt::LeftDockWidgetArea, onlineQuotesWidget); QDockWidget *debugWidget = new QDockWidget(i18n("Debug"), this); debugWidget->setObjectName("debugDockWidget"); debugWidget->setWidget(d->quotesWidget->debugWidget()); addDockWidget(Qt::LeftDockWidgetArea, debugWidget); QDockWidget *quoteDetailsWidget = new QDockWidget(i18n("Quote details"), this); quoteDetailsWidget->setObjectName("quoteDetailsDockWidget"); quoteDetailsWidget->setWidget(d->quotesWidget->quoteDetailsWidget()); addDockWidget(Qt::RightDockWidgetArea, quoteDetailsWidget); -#if defined(BUILD_WITH_WEBKIT) +#if defined(BUILD_WITH_WEBKIT) || defined(BUILD_WITH_WEBENGINE) manager.setWebPageEnabled(true); QDockWidget *browserWidget = new QDockWidget(i18n("Browser"), this); browserWidget->setObjectName("browserDockWidget"); AlkWebPage *webPage = manager.webPage(); connect(webPage, SIGNAL(urlChanged(QUrl)), this, SLOT(slotUrlChanged(QUrl))); // setup url line QHBoxLayout *hLayout = new QHBoxLayout; d->urlLine = new QLineEdit; connect(d->urlLine, SIGNAL(editingFinished()), this, SLOT(slotEditingFinished())); hLayout->addWidget(d->urlLine); // setup language box QComboBox *box = new QComboBox; QList allLocales = QLocale::matchingLocales( QLocale::AnyLanguage, QLocale::AnyScript, QLocale::AnyCountry); QStringList languages; foreach(const QLocale &locale, allLocales) { languages.append(locale.uiLanguages()); } languages.sort(); box->addItems(languages); d->quotesWidget->setAcceptLanguage(box->currentText()); connect(box, SIGNAL(currentIndexChanged(QString)), this, SLOT(slotLanguageChanged(QString))); hLayout->addWidget(box); // setup browser window QVBoxLayout *layout = new QVBoxLayout; layout->addLayout(hLayout); layout->addWidget(webPage->widget()); QWidget *group = new QWidget; group->setLayout(layout); browserWidget->setWidget(group); addDockWidget(Qt::RightDockWidgetArea, browserWidget); webPage->setWebInspectorEnabled(true); #endif setCentralWidget(nullptr); readPositionSettings(); } MainWindow::~MainWindow() { delete d; } void MainWindow::closeEvent(QCloseEvent *event) { writePositionSettings(); QMainWindow::closeEvent(event); }