diff --git a/CMakeLists.txt b/CMakeLists.txt index de53787..5853412 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,147 +1,155 @@ # 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.0) +project(alkimia VERSION 8.0.70) 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 ${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(_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() find_package(Qt5 REQUIRED COMPONENTS Core Test WebKitWidgets Qml ) + if(BUILD_APPLETS) + set(PLASMA_COMPONENT Package Plasma) + endif() # search packages used by KDE find_package(KF5 REQUIRED COMPONENTS Config CoreAddons Completion I18n Package Plasma NewStuff KIO IconThemes TextWidgets OPTIONAL_COMPONENTS ${OPT_KF5_COMPONENTS} ) 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) 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() 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( - ${MP_INCLUDE_DIR} ${CMAKE_SOURCE_DIR}/src ${CMAKE_BINARY_DIR}/src ) add_subdirectory(src) add_subdirectory(autotests) add_subdirectory(qml) -if(BUILD_QT4) - add_subdirectory(plasma/applets/onlinequote) -else() - add_subdirectory(plasma/applets/ForeignCurrencies) +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/autotests/alkvaluetest.cpp b/autotests/alkvaluetest.cpp index b29b480..a82a42a 100644 --- a/autotests/alkvaluetest.cpp +++ b/autotests/alkvaluetest.cpp @@ -1,580 +1,591 @@ /*************************************************************************** * Copyright 2010 Thomas Baumgart ipwizard@users.sourceforge.net * * * * 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 "alkvaluetest.h" #include "alkimia/alkvalue.h" #include QTEST_MAIN(AlkValueTest) namespace QTest { template<> char *toString(const AlkValue &val) { return qstrdup(val.toString().toUtf8()); } } void AlkValueTest::init() { } void AlkValueTest::cleanup() { } void AlkValueTest::emptyCtor() { AlkValue *m = new AlkValue(); QCOMPARE(m->toString(), QLatin1String("0/1")); delete m; } void AlkValueTest::copyCtor() { AlkValue a(41, 152); AlkValue b(a); QCOMPARE(a.toString(), QLatin1String("41/152")); QCOMPARE(b.toString(), QLatin1String("41/152")); } void AlkValueTest::intCtor() { AlkValue *m; m = new AlkValue(10); QCOMPARE(m->toString(), QLatin1String("10/1")); delete m; m = new AlkValue(-10); QCOMPARE(m->toString(), QLatin1String("-10/1")); delete m; m = new AlkValue(10, 2); QCOMPARE(m->toString(), QLatin1String("5/1")); delete m; m = new AlkValue(-10, 2); QCOMPARE(m->toString(), QLatin1String("-5/1")); delete m; // the denominator is an unsigned value thus sticking in a negative // one does not make sense m = new AlkValue(-10, -2); QVERIFY(m->toString() != QLatin1String("-5/1")); delete m; } void AlkValueTest::stringCtor() { AlkValue *m; // mixed mode used for some price information in QIF m = new AlkValue(QLatin1String("5 8/16"), QLatin1Char('.')); QCOMPARE(*m, AlkValue(5.5)); delete m; // standard representation m = new AlkValue(QLatin1String("12345/100"), QLatin1Char('.')); QCOMPARE(m->toString(), QLatin1String("2469/20")); delete m; // negative standard representation m = new AlkValue(QLatin1String("-8/32"), QLatin1Char('.')); QCOMPARE(*m, AlkValue(-0.25)); delete m; // false negative standard representation m = new AlkValue(QLatin1String("(8/32)"), QLatin1Char('.')); QCOMPARE(*m, AlkValue(-832)); delete m; // duplicate negative standard representation m = new AlkValue(QLatin1String("(-8/32)"), QLatin1Char('.')); QCOMPARE(*m, AlkValue(-832)); delete m; // duplicate negative standard representation m = new AlkValue(QLatin1String("-(8/32)"), QLatin1Char('.')); QCOMPARE(*m, AlkValue(-832)); delete m; // different decimal symbol m = new AlkValue(QLatin1String("x1.234,568 EUR"), QLatin1Char(',')); QCOMPARE(m->toString(), QLatin1String("154321/125")); delete m; // octal leadin m = new AlkValue(QLatin1String("0.07"), QLatin1Char('.')); QCOMPARE(m->toString(), QLatin1String("7/100")); delete m; m = new AlkValue(QLatin1String("0.09"), QLatin1Char('.')); QCOMPARE(m->toString(), QLatin1String("9/100")); delete m; m = new AlkValue(QLatin1String("09"), QLatin1Char('.')); QCOMPARE(m->toString(), QLatin1String("9/1")); delete m; // negative numbers m = new AlkValue(QLatin1String("x(1,234.)"), QLatin1Char('.')); QCOMPARE(*m, AlkValue(-1234)); delete m; m = new AlkValue(QLatin1String("x-1,234."), QLatin1Char('.')); QCOMPARE(*m, AlkValue(-1234)); delete m; m = new AlkValue(QLatin1String("x1,234.-"), QLatin1Char('.')); QCOMPARE(*m, AlkValue(-1234)); delete m; // empty string m = new AlkValue(QLatin1String(""), QLatin1Char('.')); QCOMPARE(*m, AlkValue()); delete m; m = new AlkValue(QLatin1String("."), QLatin1Char('.')); QCOMPARE(*m, AlkValue()); delete m; m = new AlkValue(QLatin1String(","), QLatin1Char('.')); QCOMPARE(*m, AlkValue()); delete m; m = new AlkValue(QLatin1String("."), QLatin1Char(',')); QCOMPARE(*m, AlkValue()); delete m; } void AlkValueTest::doubleCtor() { for (int i = -123456; i < 123456; ++i) { double d = i; AlkValue r(i, 100); d /= 100; AlkValue t(d, 100); QCOMPARE(t, r); } AlkValue a = AlkValue(1.9999999999998); QVERIFY(a != AlkValue(2, 1)); QVERIFY(a < AlkValue(2, 1)); QVERIFY(a > AlkValue(QLatin1String("1.999999999"), QLatin1Char('.'))); a = AlkValue(1.9999999999998, 100); QCOMPARE(a, AlkValue(2, 1)); a = AlkValue(1.234, AlkValue::precisionToDenominator(2).get_ui()); QCOMPARE(a, AlkValue(123, 100)); } void AlkValueTest::assignment() { // const AlkValue& operator=(const AlkValue& val); AlkValue m0; AlkValue m1(10, 2); m0 = m1; QCOMPARE(m0.toString(), QLatin1String("5/1")); // const AlkValue& operator=(int num); m0 = 6; QCOMPARE(m0.toString(), QLatin1String("6/1")); m0 = -12; QCOMPARE(m0.toString(), QLatin1String("-12/1")); // const AlkValue& operator=(double num); m0 = 123.45; QCOMPARE(m0.toString(), QLatin1String("8687021468732621/70368744177664")); m0 = 1.23e7; QCOMPARE(m0.toString(), QLatin1String("12300000/1")); // const AlkValue& operator=(const QString& str); m0 = QLatin1String("x1234.567"); QCOMPARE(m0.toString(), QLatin1String("1234567/1000")); m0 = QLatin1String("(x1234.567)"); QCOMPARE(m0.toString(), QLatin1String("-1234567/1000")); m0 = QLatin1String("-1234.567"); QCOMPARE(m0.toString(), QLatin1String("-1234567/1000")); } void AlkValueTest::equality() { AlkValue m0, m1; m0 = 123; m1 = QLatin1String("123"); QCOMPARE(m0, m1); m0 = QLatin1String("511/100"); m1 = QLatin1String("5753348523965686/1125899906842600"); QCOMPARE(m0, m1); m0 = QLatin1String("-14279570/100"); m1 = QLatin1String("-1427957/10"); QCOMPARE(m0, m1); m0 = QLatin1String("-7301028/100"); m1 = QLatin1String("-1825257/25"); QCOMPARE(m0, m1); } void AlkValueTest::inequality() { AlkValue m0, m1; m0 = 123; m1 = QLatin1String("124"); QVERIFY(m0 != m1); m0 = QLatin1String("511/100"); m1 = QLatin1String("5753348523965809/1125899906842624"); QVERIFY(m0 != m1); } void AlkValueTest::less() { AlkValue m0, m1; m0 = 12; m1 = 13; QVERIFY(m0 < m1); m0 = -m0; m1 = -m1; QVERIFY(m1 < m0); m0 = 12; m1 = AlkValue(QLatin1String("12.0000000000000000000000000000001"), QLatin1Char('.')); QVERIFY(m0 < m1); QVERIFY(!(m1 < m0)); m0 = -m0; m1 = -m1; QVERIFY(m1 < m0); QVERIFY(!(m0 < m1)); m0 = 12; m1 = m0; QVERIFY(!(m0 < m1)); } void AlkValueTest::greater() { AlkValue m0, m1; m0 = 12; m1 = 13; QVERIFY(m1 > m0); m0 = -m0; m1 = -m1; QVERIFY(m0 > m1); m0 = 12; m1 = AlkValue(QLatin1String("12.0000000000000000000000000000001"), QLatin1Char('.')); QVERIFY(m1 > m0); QVERIFY(!(m0 > m1)); m0 = -m0; m1 = -m1; QVERIFY(m0 > m1); QVERIFY(!(m1 > m0)); m0 = 12; m1 = m0; QVERIFY(!(m0 > m1)); } void AlkValueTest::lessThan() { AlkValue m0, m2; AlkValue m1 = AlkValue(QLatin1String("12.0000000000000000000000000000001"), QLatin1Char('.')); m0 = 12; m2 = 12; QVERIFY(m0 <= m1); QVERIFY(m0 <= m2); m0 = -m0; m1 = -m1; m2 = -m2; QVERIFY(m1 <= m0); QVERIFY(m2 <= m0); } void AlkValueTest::greaterThan() { AlkValue m0, m2; AlkValue m1 = AlkValue(QLatin1String("12.0000000000000000000000000000001"), QLatin1Char('.')); m0 = 12; m2 = 12; QVERIFY(m1 >= m0); QVERIFY(m2 >= m0); m0 = -m0; m1 = -m1; m2 = -m2; QVERIFY(m0 >= m1); QVERIFY(m0 >= m2); } void AlkValueTest::addition() { // AlkValue operator+( const AlkValue& summand ) const; AlkValue m0, m1; m0 = 100; m1 = 23; QCOMPARE((m0 + m1), AlkValue(123)); // AlkValue& operator+= ( const AlkValue& val ); m0 += m1; QCOMPARE(m0, AlkValue(123)); m0 = 100; m1 = -23; QCOMPARE((m0 + m1), AlkValue(77)); m0 += m1; QCOMPARE(m0, AlkValue(77)); } void AlkValueTest::subtraction() { // AlkValue operator-( const AlkValue& minuend ) const; AlkValue m0, m1; m0 = 100; m1 = 23; QCOMPARE((m0 - m1), AlkValue(77)); // AlkValue& operator-= ( const AlkValue& val ); m0 -= m1; QCOMPARE(m0, AlkValue(77)); m0 = 100; m1 = -23; QCOMPARE((m0 - m1), AlkValue(123)); m0 -= m1; QCOMPARE(m0, AlkValue(123)); } void AlkValueTest::multiplication() { // AlkValue operator*( const AlkValue& factor ) const; AlkValue m0, m1; m0 = 100; m1 = 23; QCOMPARE((m0 * m1), AlkValue(2300)); // AlkValue& operator*= ( const AlkValue& val ); m0 *= m1; QCOMPARE(m0, AlkValue(2300)); m0 = 100; m1 = -23; QCOMPARE((m0 * m1), AlkValue(-2300)); m0 *= m1; QCOMPARE(m0, AlkValue(-2300)); // AlkValue operator*( int factor) const; QCOMPARE((m1 * 4), AlkValue(-92)); QCOMPARE((m1 *(-4)), AlkValue(92)); } void AlkValueTest::division() { // AlkValue operator/( const AlkValue& divisor ) const; AlkValue m0, m1; m0 = 100; m1 = 20; QCOMPARE((m0 / m1), AlkValue(5)); // AlkValue& operator/= ( const AlkValue& val ); m0 /= m1; QCOMPARE(m0, AlkValue(5)); m0 = 100; m1 = -20; QCOMPARE((m0 / m1), AlkValue(-5)); m0 /= m1; QCOMPARE(m0, AlkValue(-5)); } +void AlkValueTest::modulo() +{ + AlkValue m0(1025000), m1; + m1 = m0 % 97; + QCOMPARE(m1.abs(), AlkValue(1)); + + m0 = 1024999; + m1 = m0 % 97; + QCOMPARE(m1.abs(), AlkValue(0)); +} + void AlkValueTest::unaryMinus() { // AlkValue operator-() const; AlkValue m0(5); QCOMPARE(-m0, AlkValue(-5)); } void AlkValueTest::abs() { AlkValue m0(-5); AlkValue m1(5); QCOMPARE(m0.abs(), AlkValue(5)); QCOMPARE(m1.abs(), AlkValue(5)); } void AlkValueTest::precision() { AlkValue a(QLatin1String("1234567890"), QLatin1Char('.')); AlkValue b(QLatin1String("1234567890"), QLatin1Char('.')); AlkValue c; // QVERIFY(c.isZero() == true); c = a * b; QCOMPARE(c, AlkValue(QLatin1String("1524157875019052100"), QLatin1Char('.'))); c /= b; QCOMPARE(c, AlkValue(QLatin1String("1234567890"), QLatin1Char('.'))); } void AlkValueTest::convertDenominator() { AlkValue a(123.456); QCOMPARE(a.convertDenominator(), AlkValue(12346, 100)); AlkValue b; a = QLatin1String("-73010.28"); b = QLatin1String("1.95583"); QCOMPARE((a * b).convertDenominator(100), AlkValue(-14279570, 100)); a = QLatin1String("-142795.69"); QCOMPARE((a / b).convertDenominator(100), AlkValue(-7301028, 100)); a = QLatin1String("142795.69"); QCOMPARE((a / b).convertDenominator(100), AlkValue(7301028, 100)); a = AlkValue(1.9999999999998); QVERIFY(a != AlkValue(2, 1)); QVERIFY(a < AlkValue(2, 1)); QVERIFY(a > AlkValue(QLatin1String("1.999999999"), QLatin1Char('.'))); a = AlkValue(1.9999999999998, 100); QCOMPARE(a, AlkValue(2, 1)); } void AlkValueTest::convertPrecision() { AlkValue a(123.456); QCOMPARE(a.convertPrecision(), AlkValue(12346, 100)); AlkValue b; a = QLatin1String("-73010.28"); b = QLatin1String("1.95583"); QCOMPARE((a * b).convertPrecision(2), AlkValue(-14279570, 100)); a = QLatin1String("-142795.69"); QCOMPARE((a / b).convertPrecision(2), AlkValue(-7301028, 100)); a = QLatin1String("142795.69"); QCOMPARE((a / b).convertPrecision(2), AlkValue(7301028, 100)); QCOMPARE(AlkValue(5, 10).convertPrecision(0, AlkValue::RoundFloor), AlkValue()); QCOMPARE(AlkValue(-5, 10).convertPrecision(0, AlkValue::RoundFloor), AlkValue(-1)); QCOMPARE(AlkValue(15, 10).convertPrecision(0, AlkValue::RoundFloor), AlkValue(1)); QCOMPARE(AlkValue(-15, 10).convertPrecision(0, AlkValue::RoundFloor), AlkValue(-2)); QCOMPARE(AlkValue(5, 10).convertPrecision(0, AlkValue::RoundCeil), AlkValue(1)); QCOMPARE(AlkValue(-5, 10).convertPrecision(0, AlkValue::RoundCeil), AlkValue()); QCOMPARE(AlkValue(15, 10).convertPrecision(0, AlkValue::RoundCeil), AlkValue(2)); QCOMPARE(AlkValue(-15, 10).convertPrecision(0, AlkValue::RoundCeil), AlkValue(-1)); QCOMPARE(AlkValue(5, 10).convertPrecision(0, AlkValue::RoundTruncate), AlkValue()); QCOMPARE(AlkValue(-5, 10).convertPrecision(0, AlkValue::RoundTruncate), AlkValue()); QCOMPARE(AlkValue(15, 10).convertPrecision(0, AlkValue::RoundTruncate), AlkValue(1)); QCOMPARE(AlkValue(-15, 10).convertPrecision(0, AlkValue::RoundTruncate), AlkValue(-1)); QCOMPARE(AlkValue(5, 10).convertPrecision(0, AlkValue::RoundPromote), AlkValue(1)); QCOMPARE(AlkValue(-5, 10).convertPrecision(0, AlkValue::RoundPromote), AlkValue(-1)); QCOMPARE(AlkValue(15, 10).convertPrecision(0, AlkValue::RoundPromote), AlkValue(2)); QCOMPARE(AlkValue(-15, 10).convertPrecision(0, AlkValue::RoundPromote), AlkValue(-2)); QCOMPARE(AlkValue(4, 10).convertPrecision(0, AlkValue::RoundHalfDown), AlkValue()); QCOMPARE(AlkValue(5, 10).convertPrecision(0, AlkValue::RoundHalfDown), AlkValue()); QCOMPARE(AlkValue(6, 10).convertPrecision(0, AlkValue::RoundHalfDown), AlkValue(1)); QCOMPARE(AlkValue(-4, 10).convertPrecision(0, AlkValue::RoundHalfDown), AlkValue()); QCOMPARE(AlkValue(-5, 10).convertPrecision(0, AlkValue::RoundHalfDown), AlkValue()); QCOMPARE(AlkValue(-6, 10).convertPrecision(0, AlkValue::RoundHalfDown), AlkValue(-1)); QCOMPARE(AlkValue(14, 10).convertPrecision(0, AlkValue::RoundHalfDown), AlkValue(1)); QCOMPARE(AlkValue(15, 10).convertPrecision(0, AlkValue::RoundHalfDown), AlkValue(1)); QCOMPARE(AlkValue(16, 10).convertPrecision(0, AlkValue::RoundHalfDown), AlkValue(2)); QCOMPARE(AlkValue(-14, 10).convertPrecision(0, AlkValue::RoundHalfDown), AlkValue(-1)); QCOMPARE(AlkValue(-15, 10).convertPrecision(0, AlkValue::RoundHalfDown), AlkValue(-1)); QCOMPARE(AlkValue(-16, 10).convertPrecision(0, AlkValue::RoundHalfDown), AlkValue(-2)); QCOMPARE(AlkValue(4, 10).convertPrecision(0, AlkValue::RoundHalfUp), AlkValue()); QCOMPARE(AlkValue(5, 10).convertPrecision(0, AlkValue::RoundHalfUp), AlkValue(1)); QCOMPARE(AlkValue(6, 10).convertPrecision(0, AlkValue::RoundHalfUp), AlkValue(1)); QCOMPARE(AlkValue(-4, 10).convertPrecision(0, AlkValue::RoundHalfUp), AlkValue()); QCOMPARE(AlkValue(-5, 10).convertPrecision(0, AlkValue::RoundHalfUp), AlkValue(-1)); QCOMPARE(AlkValue(-6, 10).convertPrecision(0, AlkValue::RoundHalfUp), AlkValue(-1)); QCOMPARE(AlkValue(14, 10).convertPrecision(0, AlkValue::RoundHalfUp), AlkValue(1)); QCOMPARE(AlkValue(15, 10).convertPrecision(0, AlkValue::RoundHalfUp), AlkValue(2)); QCOMPARE(AlkValue(16, 10).convertPrecision(0, AlkValue::RoundHalfUp), AlkValue(2)); QCOMPARE(AlkValue(-14, 10).convertPrecision(0, AlkValue::RoundHalfUp), AlkValue(-1)); QCOMPARE(AlkValue(-15, 10).convertPrecision(0, AlkValue::RoundHalfUp), AlkValue(-2)); QCOMPARE(AlkValue(-16, 10).convertPrecision(0, AlkValue::RoundHalfUp), AlkValue(-2)); QCOMPARE(AlkValue(5, 10).convertPrecision(0, AlkValue::RoundRound), AlkValue()); QCOMPARE(AlkValue(-5, 10).convertPrecision(0, AlkValue::RoundRound), AlkValue()); QCOMPARE(AlkValue(15, 10).convertPrecision(0, AlkValue::RoundRound), AlkValue(2)); QCOMPARE(AlkValue(-15, 10).convertPrecision(0, AlkValue::RoundRound), AlkValue(-2)); QCOMPARE(AlkValue(25, 10).convertPrecision(0, AlkValue::RoundRound), AlkValue(2)); QCOMPARE(AlkValue(-25, 10).convertPrecision(0, AlkValue::RoundRound), AlkValue(-2)); } void AlkValueTest::denominatorToPrecision() { QVERIFY(AlkValue::denominatorToPrecision(100) == 2); QVERIFY(AlkValue::denominatorToPrecision(1000000) == 6); QVERIFY(AlkValue::denominatorToPrecision(1) == 0); QVERIFY(AlkValue::denominatorToPrecision(0) == 0); QVERIFY(AlkValue::denominatorToPrecision(-1) == 0); QVERIFY(AlkValue::denominatorToPrecision(-188) == 0); QVERIFY(AlkValue::denominatorToPrecision(200) == 3); } void AlkValueTest::precisionToDenominator() { QVERIFY(AlkValue::precisionToDenominator(2) == 100); QVERIFY(AlkValue::precisionToDenominator(6) == 1000000); QVERIFY(AlkValue::precisionToDenominator(0) == 1); QVERIFY(AlkValue::precisionToDenominator(-1) == 1); QVERIFY(AlkValue::precisionToDenominator(-5) == 1); } void AlkValueTest::valueRef() { AlkValue a(5); mpq_class &val = a.valueRef(); val = mpq_class(1, 3); QCOMPARE(a, AlkValue(1, 3)); a = QLatin1String("1/7"); QCOMPARE(val, mpq_class(1, 7)); } void AlkValueTest::canonicalize() { AlkValue a(5); mpq_class &val(a.valueRef()); QCOMPARE(val, mpq_class(5, 1)); mpz_class i; i = 10; mpq_set_num(val.get_mpq_t(), i.get_mpz_t()); i = 2; mpq_set_den(val.get_mpq_t(), i.get_mpz_t()); QVERIFY(val != mpq_class(5, 1)); QCOMPARE(val, mpq_class(10, 2)); a.canonicalize(); QCOMPARE(val, mpq_class(5, 1)); } diff --git a/autotests/alkvaluetest.h b/autotests/alkvaluetest.h index e42c1e3..3aa4226 100644 --- a/autotests/alkvaluetest.h +++ b/autotests/alkvaluetest.h @@ -1,61 +1,62 @@ /*************************************************************************** * Copyright 2010 Thomas Baumgart ipwizard@users.sourceforge.net * * * * 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 ALKVALUETEST_H #define ALKVALUETEST_H #include class AlkValue; class AlkValueTest : public QObject { Q_OBJECT private slots: void init(); void cleanup(); void emptyCtor(); void copyCtor(); void intCtor(); void stringCtor(); void doubleCtor(); void assignment(); void equality(); void inequality(); void less(); void greater(); void lessThan(); void greaterThan(); void addition(); void subtraction(); void multiplication(); void division(); + void modulo(); void unaryMinus(); void abs(); void precision(); void convertDenominator(); void convertPrecision(); void denominatorToPrecision(); void precisionToDenominator(); void valueRef(); void canonicalize(); }; #endif // ALKVALUETEST_H diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 5eec9b2..abc1929 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,230 +1,234 @@ 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 alkwebpage.cpp ) set(alkimia_INTERNAL_HEADERS alkdateformat.h alkexception.h alkonlinequoteprocess.h alkquoteitem.h alkquotereceiver.h alkwebpage.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 ${QT_USE_LIBSPREFIX}WebKitWidgets KF5::CoreAddons 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 ) set(alkimia_LIB_HEADERS alkcompany.h alkfinancequoteprocess.h alkonlinequote.h alkonlinequotesource.h alkonlinequotesprofile.h alkonlinequotesprofilemanager.h alkonlinequoteswidget.h ) set(alkimia_UI alkonlinequoteswidget${ALKIMIA_LIB_SUFFIX}.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 ${QT_USE_LIBSPREFIX}WebKitWidgets KF5::Completion KF5::I18n KF5::NewStuff KF5::IconThemes KF5::TextWidgets KF5::KIOCore KF5::KIOWidgets ) add_definitions(-DTRANSLATION_DOMAIN=\"alkimia\") endif() target_link_libraries(alkimia PRIVATE alkimia-internal ${QT_USE_LIBSPREFIX}Core ${ALKIMIA_LIBS} ${QT_USE_LIBSPREFIX}WebKit ${QT_USE_LIBSPREFIX}DBus PUBLIC ${MP_LIBRARIES} ) if(NOT BUILD_QT4 AND MSVC) 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) - set(KNSRC_INSTALL_DIR ${SHARE_INSTALL_DIR}/kde4/config) + if(WIN32) + set(KNSRC_INSTALL_DIR ${SHARE_INSTALL_DIR}/config) + else() + set(KNSRC_INSTALL_DIR ${SHARE_INSTALL_DIR}/kde4/config) + endif() else() set(KNSRC_INSTALL_DIR ${CONFIG_INSTALL_DIR}) endif() install(FILES alkimia-quotes.knsrc kmymoney-quotes.knsrc skrooge-quotes.knsrc DESTINATION ${KNSRC_INSTALL_DIR} ) install(PROGRAMS financequote.pl DESTINATION ${DATA_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/alkonlinequotesprofile.cpp b/src/alkonlinequotesprofile.cpp index 38ad1b2..ccbb0c9 100644 --- a/src/alkonlinequotesprofile.cpp +++ b/src/alkonlinequotesprofile.cpp @@ -1,412 +1,438 @@ /*************************************************************************** * Copyright 2018 Ralf Habacker * * Copyright 2019 Thomas Baumgart * * * * 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 #if QT_VERSION >= QT_VERSION_CHECK(5,0,0) #include #include #else #include #include #include #endif 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; bool setupFinanceQuoteScriptPath() { if (m_financeQuoteScriptPath.isEmpty()) { #if QT_VERSION >= QT_VERSION_CHECK(5,0,0) m_financeQuoteScriptPath = QStandardPaths::locate(QStandardPaths::AppDataLocation, QStringLiteral("misc/financequote.pl")); #else m_financeQuoteScriptPath = KGlobal::dirs()->findResource("appdata", QString("misc/financequote.pl")); #endif } return !m_financeQuoteScriptPath.isEmpty(); } Private(AlkOnlineQuotesProfile *p) : m_p(p) , m_profileManager(0) , m_manager(0) , m_config(0) , m_type(Type::Undefined) { setupFinanceQuoteScriptPath(); } ~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 (setupFinanceQuoteScriptPath()) { 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; const QString filename = QString("%1/*.txt").arg(m_GHNSFilePath); #if QT_VERSION >= QT_VERSION_CHECK(5,0,0) const auto resources = QStandardPaths::locateAll(QStandardPaths::DataLocation, filename); #else const QStringList resources = KStandardDirs().findAllResources("data", filename); #endif foreach (const QString &file, resources) { 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; source.setName(source.name() + ".webkit"); result[source.name()] = source; 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() { - // TODO: add windows support if (m_type == Type::KMyMoney5 || m_type == Type::Alkimia5 || m_type == Type::Skrooge5) - return QString("%1/.config").arg(QDir::homePath()); + 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(QDir::homePath()); + return QString("%1/.kde4/share/config").arg(homeRootPath()); return QString(); } QString dataReadPath() { - // TODO: add windows support if (m_type == Type::KMyMoney5 || m_type == Type::Alkimia5 || m_type == Type::Skrooge5) - return QString("/usr/share/kf5"); + return dataRootPath(); else if (m_type == Type::KMyMoney4 || m_type == Type::Alkimia4 || m_type == Type::Skrooge4) - return QString("/usr/share/kde4/apps"); + return QString("%1/kde4/apps").arg(dataRootPath()); return QString(); } QString dataWritePath() { - // TODO: add windows support if (m_type == Type::KMyMoney5 || m_type == Type::Alkimia5 || m_type == Type::Skrooge5) - return QString("%1/.local/share").arg(QDir::homePath()); + 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(QDir::homePath()); + 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 { #if QT_VERSION >= QT_VERSION_CHECK(5,0,0) QString configFile = QStandardPaths::locate(QStandardPaths::AppConfigLocation, d->m_GHNSFile); #else QString configFile = KStandardDirs::locate("config", d->m_GHNSFile); #endif 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; } #include "alkonlinequotesprofile.moc" diff --git a/src/alkonlinequoteswidget.cpp b/src/alkonlinequoteswidget.cpp index d20cbf7..1fda6af 100644 --- a/src/alkonlinequoteswidget.cpp +++ b/src/alkonlinequoteswidget.cpp @@ -1,705 +1,701 @@ /*************************************************************************** * Copyright 2004-2019 Thomas Baumgart tbaumgart@kde.org * * * * 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 "alkonlinequoteswidget.h" #include "alkonlinequote.h" #include "alkonlinequotesprofile.h" #include "alkonlinequotesprofilemanager.h" #include "alkonlinequotesource.h" #include "alkwebpage.h" #include #include #include #include #include #if QT_VERSION >= QT_VERSION_CHECK(5,0,0) #include #include #include #include #define KIcon QIcon #else #include #include #include #include #include #endif #include #include #if QT_VERSION >= QT_VERSION_CHECK(5,0,0) #include #else #include #endif +#if QT_VERSION < QT_VERSION_CHECK(5,0,0) +#include +static KLocale _locale("alkimia"); +#define i18nc(context, text) ki18nc(context, text).toString(&_locale) +#define i18n(text) ki18n(text).toString(&_locale) +#define tr2i18n(text, context) ki18nc(context, text).toString(&_locale) +#endif + class AlkOnlineQuotesWidget::Private : public QWidget, public Ui::AlkOnlineQuotesWidget { Q_OBJECT public: QString m_acceptLanguage; QList m_resetList; AlkOnlineQuoteSource m_currentItem; bool m_quoteInEditing; AlkOnlineQuotesProfile *m_profile; bool m_showProfiles; bool m_showUpload; QPixmap m_emptyIcon; QPixmap m_inWorkIcon; QPixmap m_okIcon; QPixmap m_failIcon; QPixmap m_unknownIcon; QDialog *m_webPageDialog; Private(bool showProfiles, bool showUpload, QWidget *parent); ~Private(); public slots: void slotNewProfile(); void slotDeleteProfile(); void slotSelectProfile(); void slotLoadProfile(); void slotDeleteEntry(); void slotDuplicateEntry(); void slotUpdateEntry(); void slotLoadWidgets(); void slotEntryChanged(); void slotNewEntry(); void slotCheckEntry(); void slotLogStatus(const QString &s); void slotLogError(const QString &s); void slotLogFailed(const QString &id, const QString &symbol); void slotLogQuote(const QString &id, const QString &symbol, const QDate &date, double price); void slotEntryRenamed(QListWidgetItem *item); void slotStartRename(QListWidgetItem *item); void slotInstallEntries(); void slotUploadEntry(); void slotShowButton(); public: void loadProfiles(); void loadQuotesList(const bool updateResetList = false); void clearIcons(); void initIcons(); void setupIcons(const AlkOnlineQuote::Errors &errors); QString singleSymbol() const; QStringList doubleSymbol() const; QString expandedUrl() const; }; AlkOnlineQuotesWidget::Private::Private(bool showProfiles, bool showUpload, QWidget *parent) : QWidget(parent) , m_quoteInEditing(false) , m_profile(nullptr) , m_showProfiles(showProfiles) , m_showUpload(showUpload) , m_inWorkIcon(BarIcon("view-refresh")) , m_okIcon(BarIcon("dialog-ok-apply")) , m_failIcon(BarIcon("dialog-cancel")) , m_webPageDialog(nullptr) { setupUi(parent); profilesGroupBox->setVisible(showProfiles); profileDetailsBox->setVisible(showProfiles); m_showButton->setVisible(!showProfiles && AlkOnlineQuotesProfileManager::instance().webPageEnabled()); m_ghnsSource->setEnabled(showProfiles); m_uploadButton->setVisible(showUpload); m_urlCheckLabel->setMinimumWidth(m_okIcon.width()); loadProfiles(); // TODO move to ui file KGuiItem updateButtenItem(i18nc("Accepts the entered data and stores it", "&Accept"), KIcon("dialog-ok"), i18n("Accepts the entered data and stores it"), i18n("Use this to accept the modified data.")); KGuiItem deleteButtenItem(i18n("&Delete"), KIcon("edit-delete"), i18n("Delete the selected source entry"), i18n("Use this to delete the selected online source entry")); KGuiItem checkButtonItem(i18nc("Check the selected source entry", "&Check Source"), KIcon("document-edit-verify"), i18n("Check the selected source entry"), i18n("Use this to check the selected online source entry")); KGuiItem showButtonItem(i18nc("Show the selected source entry in a web browser", "&Show page"), KIcon("applications-internet"), i18n("Show the selected source entry in a web browser"), i18n("Use this to show the selected online source entry")); KGuiItem newButtenItem(i18nc("Create a new source entry for online quotes", "&New..."), KIcon("document-new"), i18n("Create a new source entry for online quotes"), i18n("Use this to create a new entry for online quotes")); #if QT_VERSION >= QT_VERSION_CHECK(5,0,0) KGuiItem::assign(m_updateButton, updateButtenItem); KGuiItem::assign(m_deleteButton, deleteButtenItem); KGuiItem::assign(m_checkButton, checkButtonItem); KGuiItem::assign(m_showButton, showButtonItem); KGuiItem::assign(m_newButton, newButtenItem); #else m_updateButton->setGuiItem(updateButtenItem); m_deleteButton->setGuiItem(deleteButtenItem); m_checkButton->setGuiItem(checkButtonItem); m_showButton->setGuiItem(showButtonItem); m_newButton->setGuiItem(newButtenItem); #endif connect(m_newProfile, SIGNAL(clicked()), this, SLOT(slotNewProfile())); connect(m_deleteProfile, SIGNAL(clicked()), this, SLOT(slotDeleteProfile())); connect(m_profileList, SIGNAL(itemSelectionChanged()), this, SLOT(slotLoadProfile())); connect(m_updateButton, SIGNAL(clicked()), this, SLOT(slotUpdateEntry())); connect(m_newButton, SIGNAL(clicked()), this, SLOT(slotNewEntry())); connect(m_checkButton, SIGNAL(clicked()), this, SLOT(slotCheckEntry())); connect(m_deleteButton, SIGNAL(clicked()), this, SLOT(slotDeleteEntry())); connect(m_duplicateButton, SIGNAL(clicked()), this, SLOT(slotDuplicateEntry())); connect(m_installButton, SIGNAL(clicked()), this, SLOT(slotInstallEntries())); connect(m_uploadButton, SIGNAL(clicked()), this, SLOT(slotUploadEntry())); connect(m_quoteSourceList, SIGNAL(itemSelectionChanged()), this, SLOT(slotLoadWidgets())); connect(m_quoteSourceList, SIGNAL(itemChanged(QListWidgetItem *)), this, SLOT(slotEntryRenamed(QListWidgetItem *))); connect(m_quoteSourceList, SIGNAL(itemDoubleClicked(QListWidgetItem *)), this, SLOT(slotStartRename(QListWidgetItem *))); connect(m_editURL, SIGNAL(textChanged(QString)), this, SLOT(slotEntryChanged())); connect(m_editSymbol, SIGNAL(textChanged(QString)), this, SLOT(slotEntryChanged())); connect(m_editDate, SIGNAL(textChanged(QString)), this, SLOT(slotEntryChanged())); connect(m_editDateFormat, SIGNAL(textChanged(QString)), this, SLOT(slotEntryChanged())); connect(m_editPrice, SIGNAL(textChanged(QString)), this, SLOT(slotEntryChanged())); connect(m_skipStripping, SIGNAL(toggled(bool)), this, SLOT(slotEntryChanged())); connect(m_ghnsSource, SIGNAL(toggled(bool)), this, SLOT(slotEntryChanged())); connect(m_showButton, SIGNAL(clicked()), this, SLOT(slotShowButton())); m_checkSymbol->setText("ORCL"); m_checkSymbol2->setText("BTC GBP"); m_updateButton->setEnabled(false); slotLoadProfile(); } AlkOnlineQuotesWidget::Private::~Private() { m_webPageDialog->deleteLater(); } void AlkOnlineQuotesWidget::Private::loadProfiles() { AlkOnlineQuotesProfileList list = AlkOnlineQuotesProfileManager::instance().profiles(); if (list.isEmpty()) return; foreach (AlkOnlineQuotesProfile *profile, list) { QListWidgetItem *item = new QListWidgetItem(dynamic_cast(m_profileList)); item->setText(profile->name()); item->setFlags(item->flags() | Qt::ItemIsEditable); } m_profileList->setCurrentRow(0); m_profile = AlkOnlineQuotesProfileManager::instance().profiles().first(); loadQuotesList(); } void AlkOnlineQuotesWidget::Private::loadQuotesList(const bool updateResetList) { m_quoteInEditing = false; QStringList groups = m_profile->quoteSources(); if (updateResetList) { m_resetList.clear(); } m_quoteSourceList->blockSignals(true); m_quoteSourceList->clear(); QStringList::Iterator it; for (it = groups.begin(); it != groups.end(); ++it) { AlkOnlineQuoteSource source(*it, m_profile); if (!source.isValid()) { continue; } QListWidgetItem *item = new QListWidgetItem(*it); item->setFlags(Qt::ItemIsEditable | Qt::ItemIsSelectable | Qt::ItemIsEnabled); m_quoteSourceList->addItem(item); if (updateResetList) { m_resetList += source; } } m_quoteSourceList->sortItems(); QListWidgetItem *item = nullptr; if (!m_currentItem.name().isEmpty()) { QList items = m_quoteSourceList->findItems(m_currentItem.name(), Qt::MatchExactly); if (items.size() > 0) item = items.at(0); if (item) m_quoteSourceList->setCurrentItem(item); } if (!item) { item = m_quoteSourceList->item(0); if (item) m_quoteSourceList->setCurrentItem(item); } m_quoteSourceList->blockSignals(false); slotLoadWidgets(); slotEntryChanged(); } void AlkOnlineQuotesWidget::Private::slotNewProfile() { QListWidgetItem *item = new QListWidgetItem(dynamic_cast(m_profileList)); item->setText(QLatin1String("new profile")); item->setFlags(item->flags() | Qt::ItemIsEditable); } void AlkOnlineQuotesWidget::Private::slotDeleteProfile() { delete m_profileList->currentItem(); } void AlkOnlineQuotesWidget::Private::slotSelectProfile() { slotLoadProfile(); } void AlkOnlineQuotesWidget::Private::slotLoadProfile() { AlkOnlineQuotesProfileList list = AlkOnlineQuotesProfileManager::instance().profiles(); if (!m_showProfiles) { AlkOnlineQuotesProfileList list = AlkOnlineQuotesProfileManager::instance().profiles(); if (list.isEmpty()) return; m_profile = list.first(); loadQuotesList(); return; } foreach (AlkOnlineQuotesProfile *profile, list) { if (m_profileList->currentItem()->text() == profile->name()) { m_profile = profile; loadQuotesList(); m_installButton->setVisible(profile->hasGHNSSupport()); } } bool visible = m_profile->type() != AlkOnlineQuotesProfile::Type::None; m_configFilePath->setText(m_profile->kConfigFile()); m_configFilePath->setVisible(visible); m_configLabel->setEnabled(visible); visible = m_profile->hasGHNSSupport(); m_GHNSConfigFilePath->setText(m_profile->hotNewStuffConfigFile()); m_GHNSConfigFilePath->setVisible(visible); m_GHNSConfigLabel->setEnabled(visible); m_GHNSDataPath->setText(m_profile->hotNewStuffReadPath().join(" ")); m_GHNSDataPath->setVisible(visible); m_GHNSDataLabel->setEnabled(visible); } void AlkOnlineQuotesWidget::Private::slotLoadWidgets() { m_quoteInEditing = false; QListWidgetItem *item = m_quoteSourceList->currentItem(); m_editURL->setEnabled(item != nullptr); m_editSymbol->setEnabled(item != nullptr); m_editPrice->setEnabled(item != nullptr); m_editDate->setEnabled(item != nullptr); m_editDateFormat->setEnabled(item != nullptr); m_skipStripping->setEnabled(item != nullptr); m_editURL->clear(); m_editSymbol->clear(); m_editPrice->clear(); m_editDate->clear(); m_editDateFormat->clear(); if (item) { m_currentItem = AlkOnlineQuoteSource(item->text(), m_profile); m_editURL->setText(m_currentItem.url()); m_editSymbol->setText(m_currentItem.sym()); m_editPrice->setText(m_currentItem.price()); m_editDate->setText(m_currentItem.date()); m_editDateFormat->setText(m_currentItem.dateformat()); m_skipStripping->setChecked(m_currentItem.skipStripping()); m_ghnsSource->setChecked(m_currentItem.isGHNS()); } m_updateButton->setEnabled(false); } void AlkOnlineQuotesWidget::Private::slotEntryChanged() { clearIcons(); bool modified = m_editURL->text() != m_currentItem.url() || m_editSymbol->text() != m_currentItem.sym() || m_editDate->text() != m_currentItem.date() || m_editDateFormat->text() != m_currentItem.dateformat() || m_editPrice->text() != m_currentItem.price() || m_skipStripping->isChecked() != m_currentItem.skipStripping() || m_ghnsSource->isChecked() != m_currentItem.isGHNS(); bool hasWriteSupport = m_profile->type() != AlkOnlineQuotesProfile::Type::None || m_profile->hasGHNSSupport(); bool noNewEntry = m_quoteSourceList->findItems(i18n("New Quote Source"), Qt::MatchExactly).count() == 0; m_newButton->setEnabled(hasWriteSupport && noNewEntry); m_duplicateButton->setEnabled(hasWriteSupport); m_deleteButton->setEnabled(!m_currentItem.isReadOnly() && !m_currentItem.isGHNS()); m_uploadButton->setEnabled(m_profile->hasGHNSSupport() && m_currentItem.isGHNS()); m_updateButton->setEnabled(modified); m_checkButton->setEnabled(!modified); m_checkSymbol->setEnabled(!m_currentItem.url().contains("%2")); m_checkSymbol2->setEnabled(m_currentItem.url().contains("%2")); } void AlkOnlineQuotesWidget::Private::slotDeleteEntry() { QList items = m_quoteSourceList->findItems( m_currentItem.name(), Qt::MatchExactly); if (items.isEmpty()) { return; } QListWidgetItem *item = items.at(0); if (!item) { return; } int ret = KMessageBox::warningContinueCancel(this, i18n("Are you sure to delete this online quote ?"), i18n("Delete online quote"), KStandardGuiItem::cont(), KStandardGuiItem::cancel(), QString("DeletingOnlineQuote")); if (ret == KMessageBox::Cancel) { return; } // keep this order to avoid deleting the wrong current item m_currentItem.remove(); delete item; slotEntryChanged(); } void AlkOnlineQuotesWidget::Private::slotDuplicateEntry() { QList items = m_quoteSourceList->findItems( m_currentItem.name(), Qt::MatchExactly); if (items.isEmpty()) { return; } QListWidgetItem *item = items.at(0); if (!item) { return; } AlkOnlineQuoteSource copy(m_currentItem); copy.setName(copy.name() + i18n(".copy")); copy.setGHNS(false); copy.write(); m_currentItem = copy; loadQuotesList(); } void AlkOnlineQuotesWidget::Private::slotUpdateEntry() { m_currentItem.setUrl(m_editURL->text()); m_currentItem.setSym(m_editSymbol->text()); m_currentItem.setDate(m_editDate->text()); m_currentItem.setDateformat(m_editDateFormat->text()); m_currentItem.setPrice(m_editPrice->text()); m_currentItem.setSkipStripping(m_skipStripping->isChecked()); m_currentItem.setGHNS(m_ghnsSource->isChecked()); m_currentItem.write(); m_checkButton->setEnabled(true); slotEntryChanged(); } void AlkOnlineQuotesWidget::Private::slotNewEntry() { AlkOnlineQuoteSource newSource(i18n("New Quote Source"), m_profile); newSource.write(); m_currentItem = newSource; loadQuotesList(); } void AlkOnlineQuotesWidget::Private::clearIcons() { m_urlCheckLabel->setPixmap(m_emptyIcon); m_dateCheckLabel->setPixmap(m_emptyIcon); m_priceCheckLabel->setPixmap(m_emptyIcon); m_symbolCheckLabel->setPixmap(m_emptyIcon); m_dateFormatCheckLabel->setPixmap(m_emptyIcon); } void AlkOnlineQuotesWidget::Private::initIcons() { m_urlCheckLabel->setPixmap(m_inWorkIcon); m_dateCheckLabel->setPixmap(m_inWorkIcon); m_priceCheckLabel->setPixmap(m_inWorkIcon); m_symbolCheckLabel->setPixmap(m_inWorkIcon); m_dateFormatCheckLabel->setPixmap(m_inWorkIcon); } void AlkOnlineQuotesWidget::Private::setupIcons(const AlkOnlineQuote::Errors &errors) { clearIcons(); if (errors & AlkOnlineQuote::Errors::URL) { m_urlCheckLabel->setPixmap(m_failIcon); } else { m_urlCheckLabel->setPixmap(m_okIcon); m_symbolCheckLabel->setPixmap(errors & AlkOnlineQuote::Errors::Symbol ? m_failIcon : m_okIcon); m_priceCheckLabel->setPixmap(errors & AlkOnlineQuote::Errors::Price ? m_failIcon : m_okIcon); if (errors & AlkOnlineQuote::Errors::Date) { m_dateCheckLabel->setPixmap(m_failIcon); } else { m_dateCheckLabel->setPixmap(m_okIcon); m_dateFormatCheckLabel->setPixmap( errors & AlkOnlineQuote::Errors::DateFormat ? m_failIcon : m_okIcon); } } } void AlkOnlineQuotesWidget::Private::slotCheckEntry() { AlkOnlineQuote quote(m_profile); m_logWindow->setVisible(true); m_logWindow->clear(); clearIcons(); quote.setAcceptLanguage(m_acceptLanguage); connect("e, SIGNAL(status(QString)), this, SLOT(slotLogStatus(QString))); connect("e, SIGNAL(error(QString)), this, SLOT(slotLogError(QString))); connect("e, SIGNAL(failed(QString,QString)), this, SLOT(slotLogFailed(QString,QString))); connect("e, SIGNAL(quote(QString,QString,QDate,double)), this, SLOT(slotLogQuote(QString,QString,QDate,double))); initIcons(); if (m_currentItem.url().contains("%2")) { quote.launch(m_checkSymbol2->text(), m_checkSymbol2->text(), m_currentItem.name()); } else { quote.launch(m_checkSymbol->text(), m_checkSymbol->text(), m_currentItem.name()); } setupIcons(quote.errors()); } void AlkOnlineQuotesWidget::Private::slotLogStatus(const QString &s) { m_logWindow->append(s); } void AlkOnlineQuotesWidget::Private::slotLogError(const QString &s) { slotLogStatus(QString("") + s + QString("")); } void AlkOnlineQuotesWidget::Private::slotLogFailed(const QString &id, const QString &symbol) { slotLogStatus(QString("%1 %2").arg(id, symbol)); } void AlkOnlineQuotesWidget::Private::slotLogQuote(const QString &id, const QString &symbol, const QDate &date, double price) { slotLogStatus(QString("%1 %2 %3 %4").arg(id, symbol, date.toString()).arg( price)); } void AlkOnlineQuotesWidget::Private::slotStartRename(QListWidgetItem *item) { m_quoteInEditing = true; m_quoteSourceList->editItem(item); } void AlkOnlineQuotesWidget::Private::slotEntryRenamed(QListWidgetItem *item) { //if there is no current item selected, exit if (m_quoteInEditing == false || !m_quoteSourceList->currentItem() || item != m_quoteSourceList->currentItem()) { return; } m_quoteInEditing = false; QString text = item->text(); int nameCount = 0; for (int i = 0; i < m_quoteSourceList->count(); ++i) { if (m_quoteSourceList->item(i)->text() == text) { ++nameCount; } } // Make sure we get a non-empty and unique name if (text.length() > 0 && nameCount == 1) { m_currentItem.rename(text); } else { item->setText(m_currentItem.name()); } m_quoteSourceList->sortItems(); m_newButton->setEnabled(m_quoteSourceList->findItems(i18n( "New Quote Source"), Qt::MatchExactly).count() == 0); } void AlkOnlineQuotesWidget::Private::slotInstallEntries() { QString configFile = m_profile->hotNewStuffConfigFile(); QPointer dialog = new KNS3::DownloadDialog(configFile, this); dialog->exec(); delete dialog; loadQuotesList(); } void AlkOnlineQuotesWidget::Private::slotUploadEntry() { QString configFile = m_profile->hotNewStuffConfigFile(); QUrl url = QUrl::fromLocalFile(m_currentItem.ghnsWriteFileName()); qDebug() << "uploading file" << url; QPointer dialog = new KNS3::UploadDialog(configFile, this); dialog->setUploadName(m_currentItem.name()); dialog->setUploadFile(url); dialog->exec(); delete dialog; } void AlkOnlineQuotesWidget::Private::slotShowButton() { if (!m_webPageDialog) { m_webPageDialog = new QDialog; m_webPageDialog->setWindowTitle(i18n("Online Quote HTML Result Window")); QVBoxLayout *layout = new QVBoxLayout; AlkWebPage *webPage = AlkOnlineQuotesProfileManager::instance().webPage(); webPage->setWebInspectorEnabled(true); layout->addWidget(webPage); m_webPageDialog->setLayout(layout); } m_webPageDialog->show(); } QString AlkOnlineQuotesWidget::Private::expandedUrl() const { if (m_currentItem.url().contains("%2")) { return m_currentItem.url().arg(m_checkSymbol2->text()); } else { return m_currentItem.url().arg(m_checkSymbol->text()); } } AlkOnlineQuotesWidget::AlkOnlineQuotesWidget(bool showProfiles, bool showUpload, QWidget *parent) : QWidget(parent) , d(new Private(showProfiles, showUpload, this)) { } AlkOnlineQuotesWidget::~AlkOnlineQuotesWidget() { delete d; } QWidget *AlkOnlineQuotesWidget::profilesWidget() { QFrame *frame = new QFrame; frame->setLayout(d->profilesGroupBox->layout()); return frame; } QWidget *AlkOnlineQuotesWidget::profileDetailsWidget() { QFrame *frame = new QFrame; frame->setLayout(d->profileDetailsBox->layout()); return frame; } QWidget *AlkOnlineQuotesWidget::onlineQuotesWidget() { QFrame *frame = new QFrame; frame->setLayout(d->onlineQuotesGroupBox->layout()); return frame; } QWidget *AlkOnlineQuotesWidget::quoteDetailsWidget() { QFrame *frame = new QFrame; frame->setLayout(d->detailsGroupBox->layout()); return frame; } QWidget *AlkOnlineQuotesWidget::debugWidget() { QFrame *frame = new QFrame; frame->setLayout(d->debugGroupBox->layout()); return frame; } void AlkOnlineQuotesWidget::readConfig() { } void AlkOnlineQuotesWidget::writeConfig() { } void AlkOnlineQuotesWidget::resetConfig() { QStringList::ConstIterator it; QStringList groups = d->m_profile->quoteSources(); // delete all currently defined entries for (it = groups.constBegin(); it != groups.constEnd(); ++it) { AlkOnlineQuoteSource(*it, d->m_profile).remove(); } // and write back the one's from the reset list QList::iterator itr; for (itr = d->m_resetList.begin(); itr != d->m_resetList.end(); ++itr) { (*itr).write(); } d->loadQuotesList(); } QString AlkOnlineQuotesWidget::acceptLanguage() const { return d->m_acceptLanguage; } void AlkOnlineQuotesWidget::setAcceptLanguage(const QString &text) { d->m_acceptLanguage = text; } -#if QT_VERSION < QT_VERSION_CHECK(5,0,0) -class InitCatalog { -public: - InitCatalog() - { - KComponentData a("alkimia", "alkimia"); - } -}; - -static InitCatalog init; -#endif - #include "alkonlinequoteswidget.moc" diff --git a/src/alkvalue.cpp b/src/alkvalue.cpp index 7aed246..52fffc4 100644 --- a/src/alkvalue.cpp +++ b/src/alkvalue.cpp @@ -1,571 +1,579 @@ /*************************************************************************** * Copyright 2010 Thomas Baumgart tbaumgart@kde.org * * Copyright 2018 Thomas Baumgart tbaumgart@kde.org * * * * 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 "alkimia/alkvalue.h" #include #include #include class AlkValue::Private : public QSharedData { public: Private() { } Private(const Private &other) : QSharedData(other) , m_val(other.m_val) { } mpq_class m_val; }; /** * Helper function to convert an mpq_class object into * its internal QString representation. Mainly used for * debugging. */ static QString mpqToString(const mpq_class &val) { char *p = 0; // use the gmp provided conversion routine gmp_asprintf(&p, "%Qd", val.get_mpq_t()); // convert it into a QString QString result = QString::fromLatin1(p); // and free up the resources allocated by gmp_asprintf void (*freefunc)(void *, size_t); mp_get_memory_functions(NULL, NULL, &freefunc); (*freefunc)(p, std::strlen(p) + 1); if (!result.contains(QLatin1Char('/'))) { result += QString::fromLatin1("/1"); } // done return result; } #if 0 /** * Helper function to convert an mpz_class object into * its internal QString representation. Mainly used for * debugging. */ static QString mpzToString(const mpz_class &val) { char *p = 0; // use the gmp provided conversion routine gmp_asprintf(&p, "%Zd", val.get_mpz_t()); // convert it into a QString QString result(QString::fromLatin1(p)); // and free up the resources allocated by gmp_asprintf __gmp_freefunc_t freefunc; mp_get_memory_functions(NULL, NULL, &freefunc); (*freefunc)(p, std::strlen(p) + 1); // done return result; } #endif QSharedDataPointer &AlkValue::sharedZero() { static QSharedDataPointer sharedZeroPointer(new AlkValue::Private); return sharedZeroPointer; } AlkValue::AlkValue() : d(sharedZero()) { } AlkValue::AlkValue(const AlkValue &val) : d(val.d) { } AlkValue::AlkValue(const int num, const unsigned int denom) : d(new Private) { d->m_val = mpq_class(num, denom); d->m_val.canonicalize(); } AlkValue::AlkValue(const mpz_class &num, const mpz_class &denom) : d(new Private) { mpz_set(d->m_val.get_num_mpz_t(), num.get_mpz_t()); mpz_set(d->m_val.get_den_mpz_t(), denom.get_mpz_t()); d->m_val.canonicalize(); } AlkValue::AlkValue(const mpq_class &val) : d(new Private) { d->m_val = val; d->m_val.canonicalize(); } AlkValue::AlkValue(const double &dAmount, const unsigned int denom) : d(new Private) { d->m_val = dAmount; d->m_val.canonicalize(); if (denom != 0) { *this = convertDenominator(denom); } } AlkValue::AlkValue(const QString &str, const QChar &decimalSymbol) : d(new Private) { // empty strings are easy if (str.isEmpty()) { return; } // take care of mixed prices of the form "5 8/16" as well // as own internal string representation QRegExp regExp(QLatin1String("^((\\d+)\\s+|-)?(\\d+/\\d+)")); // +-#2-+ +---#3----+ // +-----#1-----+ if (regExp.indexIn(str) > -1) { d->m_val = qPrintable(str.mid(regExp.pos(3))); d->m_val.canonicalize(); const QString &part1 = regExp.cap(1); if (!part1.isEmpty()) { if (part1 == QLatin1String("-")) { mpq_neg(d->m_val.get_mpq_t(), d->m_val.get_mpq_t()); } else { mpq_class summand(qPrintable(part1)); mpq_add(d->m_val.get_mpq_t(), d->m_val.get_mpq_t(), summand.get_mpq_t()); d->m_val.canonicalize(); } } return; } // qDebug("we got '%s' to convert", qPrintable(str)); // everything else gets down here const QString negChars = QString::fromLatin1("\\-\\(\\)"); const QString validChars = QString::fromLatin1("\\d\\%1%2").arg(decimalSymbol, negChars); QRegExp invCharSet(QString::fromLatin1("[^%1]").arg(validChars)); QRegExp negCharSet(QString::fromLatin1("[%1]").arg(negChars)); QString res(str); // get rid of any character that is not allowed. res.remove(invCharSet); // qDebug("we reduced it to '%s'", qPrintable(res)); // check if number is negative bool isNegative = false; if (res.indexOf(negCharSet) != -1) { isNegative = true; res.remove(negCharSet); } // qDebug("and modified it to '%s'", qPrintable(res)); // if someone uses the decimal symbol more than once, we get // rid of them except the right most one int pos; while (res.count(decimalSymbol) > 1) { pos = res.indexOf(decimalSymbol); res.remove(pos, 1); } // take care of any fractional part pos = res.indexOf(decimalSymbol); int len = res.length(); QString fraction = QString::fromLatin1("/1"); if ((pos != -1) && (pos < len)) { fraction += QString(len - pos - 1, QLatin1Char('0')); res.remove(pos, 1); } // check if the resulting numerator contains any leading zeros ... int cnt = 0; len = res.length() - 1; while (res[cnt] == QLatin1Char('0') && cnt < len) { ++cnt; } // ... and remove them if (cnt) { res.remove(0, cnt); } // in case the numerator is empty, we convert it to "0" if (res.isEmpty()) { res = QLatin1Char('0'); } res += fraction; // looks like we now have a pretty normalized string that we // can convert right away // qDebug("and try to convert '%s'", qPrintable(res)); try { d->m_val = mpq_class(qPrintable(res)); } catch (const std::invalid_argument &) { qWarning("Invalid argument '%s' to mpq_class() in AlkValue. Arguments to ctor: '%s', '%c'", qPrintable( res), qPrintable(str), decimalSymbol.toLatin1()); d->m_val = mpq_class(0); } d->m_val.canonicalize(); // now we make sure that we use the right sign if (isNegative) { d->m_val = -d->m_val; } } AlkValue::~AlkValue() { } QString AlkValue::toString() const { return mpqToString(d->m_val); } double AlkValue::toDouble() const { return d->m_val.get_d(); } AlkValue AlkValue::convertDenominator(int _denom, const RoundingMethod how) const { AlkValue in(*this); mpz_class in_num(mpq_numref(in.d->m_val.get_mpq_t())); AlkValue out; // initialize to zero int sign = sgn(in_num); if (sign != 0) { // sign is either -1 for negative numbers or +1 in all other cases AlkValue temp; mpz_class denom = _denom; // only process in case the denominators are different if (mpz_cmpabs(denom.get_mpz_t(), mpq_denref(d->m_val.get_mpq_t())) != 0) { mpz_class in_denom(mpq_denref(in.d->m_val.get_mpq_t())); mpz_class out_num, out_denom; if (sgn(in_denom) == -1) { // my denom is negative in_num = in_num * (-in_denom); in_num = 1; } mpz_class remainder; int denom_neg = 0; // if the denominator is less than zero, we are to interpret it as // the reciprocal of its magnitude. if (sgn(denom) < 0) { mpz_class temp_a; mpz_class temp_bc; denom = -denom; denom_neg = 1; temp_a = ::abs(in_num); temp_bc = in_denom * denom; remainder = temp_a % temp_bc; out_num = temp_a / temp_bc; out_denom = denom; } else { temp = AlkValue(denom, in_denom); // the canonicalization required here is part of the ctor // temp.d->m_val.canonicalize(); out_num = ::abs(in_num * temp.d->m_val.get_num()); remainder = out_num % temp.d->m_val.get_den(); out_num = out_num / temp.d->m_val.get_den(); out_denom = denom; } if (remainder != 0) { switch (how) { case RoundFloor: if (sign < 0) { out_num = out_num + 1; } break; case RoundCeil: if (sign > 0) { out_num = out_num + 1; } break; case RoundTruncate: break; case RoundPromote: out_num = out_num + 1; break; case RoundHalfDown: if (denom_neg) { if ((2 * remainder) > (in_denom * denom)) { out_num = out_num + 1; } } else if ((2 * remainder) > (temp.d->m_val.get_den())) { out_num = out_num + 1; } break; case RoundHalfUp: if (denom_neg) { if ((2 * remainder) >= (in_denom * denom)) { out_num = out_num + 1; } } else if ((2 * remainder) >= temp.d->m_val.get_den()) { out_num = out_num + 1; } break; case RoundRound: if (denom_neg) { if ((remainder * 2) > (in_denom * denom)) { out_num = out_num + 1; } else if ((2 * remainder) == (in_denom * denom)) { if ((out_num % 2) != 0) { out_num = out_num + 1; } } } else { if ((remainder * 2) > temp.d->m_val.get_den()) { out_num = out_num + 1; } else if ((2 * remainder) == temp.d->m_val.get_den()) { if ((out_num % 2) != 0) { out_num = out_num + 1; } } } break; case RoundNever: qWarning("AlkValue: have remainder \"%s\"->convert(%d, %d)", qPrintable(toString()), _denom, how); break; } } // construct the new output value out = AlkValue(out_num * sign, out_denom); } else { out = *this; } } return out; } AlkValue AlkValue::convertPrecision(int prec, const RoundingMethod how) const { return convertDenominator(precisionToDenominator(prec).get_si(), how); } mpz_class AlkValue::denominatorToPrecision(mpz_class denom) { mpz_class rc = 0; while (denom > 1) { ++rc; denom /= 10; } return rc; } mpz_class AlkValue::precisionToDenominator(mpz_class prec) { mpz_class denom = 1; while ((prec--) > 0) { denom *= 10; } return denom; } const AlkValue &AlkValue::canonicalize() { d->m_val.canonicalize(); return *this; } AlkValue AlkValue::operator+(const AlkValue &right) const { AlkValue result; mpq_add(result.d->m_val.get_mpq_t(), d->m_val.get_mpq_t(), right.d->m_val.get_mpq_t()); result.d->m_val.canonicalize(); return result; } AlkValue AlkValue::operator-(const AlkValue &right) const { AlkValue result; mpq_sub(result.d->m_val.get_mpq_t(), d->m_val.get_mpq_t(), right.d->m_val.get_mpq_t()); result.d->m_val.canonicalize(); return result; } AlkValue AlkValue::operator*(const AlkValue &right) const { AlkValue result; mpq_mul(result.d->m_val.get_mpq_t(), d->m_val.get_mpq_t(), right.d->m_val.get_mpq_t()); result.d->m_val.canonicalize(); return result; } AlkValue AlkValue::operator/(const AlkValue &right) const { AlkValue result; mpq_div(result.d->m_val.get_mpq_t(), d->m_val.get_mpq_t(), right.d->m_val.get_mpq_t()); result.d->m_val.canonicalize(); return result; } +AlkValue AlkValue::operator%(int operand) const +{ + mpz_class num(mpq_numref(d->m_val.get_mpq_t())); + AlkValue result; + result.d->m_val = num % operand; + return result; +} + AlkValue AlkValue::operator*(int factor) const { AlkValue result; mpq_class right(factor); mpq_mul(result.d->m_val.get_mpq_t(), d->m_val.get_mpq_t(), right.get_mpq_t()); result.d->m_val.canonicalize(); return result; } const AlkValue &AlkValue::operator=(const AlkValue &right) { d = right.d; return *this; } const AlkValue &AlkValue::operator=(int right) { d->m_val = right; d->m_val.canonicalize(); return *this; } const AlkValue &AlkValue::operator=(double right) { d->m_val = right; d->m_val.canonicalize(); return *this; } const AlkValue &AlkValue::operator=(const QString &right) { AlkValue other(right, QLatin1Char('.')); d->m_val = other.d->m_val; return *this; } AlkValue AlkValue::abs() const { AlkValue result; mpq_abs(result.d->m_val.get_mpq_t(), d->m_val.get_mpq_t()); result.d->m_val.canonicalize(); return result; } bool AlkValue::operator==(const AlkValue &val) const { if (d == val.d) { return true; } return mpq_equal(d->m_val.get_mpq_t(), val.d->m_val.get_mpq_t()); } bool AlkValue::operator!=(const AlkValue &val) const { if (d == val.d) { return false; } return !mpq_equal(d->m_val.get_mpq_t(), val.d->m_val.get_mpq_t()); } bool AlkValue::operator<(const AlkValue &val) const { return mpq_cmp(d->m_val.get_mpq_t(), val.d->m_val.get_mpq_t()) < 0 ? true : false; } bool AlkValue::operator>(const AlkValue &val) const { return mpq_cmp(d->m_val.get_mpq_t(), val.d->m_val.get_mpq_t()) > 0 ? true : false; } bool AlkValue::operator<=(const AlkValue &val) const { return mpq_cmp(d->m_val.get_mpq_t(), val.d->m_val.get_mpq_t()) <= 0 ? true : false; } bool AlkValue::operator>=(const AlkValue &val) const { return mpq_cmp(d->m_val.get_mpq_t(), val.d->m_val.get_mpq_t()) >= 0 ? true : false; } AlkValue AlkValue::operator-() const { AlkValue result; mpq_neg(result.d->m_val.get_mpq_t(), d->m_val.get_mpq_t()); result.d->m_val.canonicalize(); return result; } AlkValue &AlkValue::operator+=(const AlkValue &right) { mpq_add(d->m_val.get_mpq_t(), d->m_val.get_mpq_t(), right.d->m_val.get_mpq_t()); d->m_val.canonicalize(); return *this; } AlkValue &AlkValue::operator-=(const AlkValue &right) { mpq_sub(d->m_val.get_mpq_t(), d->m_val.get_mpq_t(), right.d->m_val.get_mpq_t()); d->m_val.canonicalize(); return *this; } AlkValue &AlkValue::operator*=(const AlkValue &right) { mpq_mul(d->m_val.get_mpq_t(), d->m_val.get_mpq_t(), right.d->m_val.get_mpq_t()); d->m_val.canonicalize(); return *this; } AlkValue &AlkValue::operator/=(const AlkValue &right) { mpq_div(d->m_val.get_mpq_t(), d->m_val.get_mpq_t(), right.d->m_val.get_mpq_t()); d->m_val.canonicalize(); return *this; } const mpq_class &AlkValue::valueRef() const { return d->m_val; } mpq_class &AlkValue::valueRef() { return d->m_val; } diff --git a/src/alkvalue.h.in b/src/alkvalue.h.in index 06e5efd..5f5f1c1 100644 --- a/src/alkvalue.h.in +++ b/src/alkvalue.h.in @@ -1,265 +1,266 @@ /*************************************************************************** * Copyright 2010 Thomas Baumgart tbaumgart@kde.org * * Copyright 2018 Thomas Baumgart tbaumgart@kde.org * * * * 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 ALKVALUE_H #define ALKVALUE_H #include // Workaround: include before gmpxx.h to fix build with gcc-4.9 /** @todo When gmp version is higer than 5.1.3, remove cstddef include */ #include #include <@MP_HEADER@> // krazy:exclude=camelcase #include #include /** * This class represents a financial value within Alkimia. * It can be used to represent balances, shares, amounts etc. * * @author Thomas Baumgart */ class ALK_EXPORT AlkValue { public: enum RoundingMethod { RoundNever = 0, /**< * Don't do any rounding, simply truncate and * print a warning in case of a remainder. * Otherwise the same as RoundTrunc. */ RoundFloor, /**< * Round to the largest integral value not * greater than @p this. * e.g. 0.5 -> 0.0 and -0.5 -> -1.0 */ RoundCeil, /**< * Round to the smallest integral value not * less than @p this. * e.g. 0.5 -> 1.0 and -0.5 -> -0.0 */ RoundTruncate, /**< * No rounding, simply truncate any fraction */ RoundPromote, /**< * Use RoundCeil for positive and RoundFloor * for negative values of @p this. * e.g. 0.5 -> 1.0 and -0.5 -> -1.0 */ RoundHalfDown, /**< * Round up or down with the following * constraints: * 0.1 .. 0.5 -> 0.0 and 0.6 .. 0.9 -> 1.0 */ RoundHalfUp, /**< * Round up or down with the following * constraints: * 0.1 .. 0.4 -> 0.0 and 0.5 .. 0.9 -> 1.0 */ RoundRound /**< * Use RoundHalfDown for 0.1 .. 0.4 and * RoundHalfUp for 0.6 .. 0.9. Use RoundHalfUp * for 0.5 in case the resulting numerator * is odd, RoundHalfDown in case the resulting * numerator is even. * e.g. 0.5 -> 0.0 and 1.5 -> 2.0 */ }; // Constructors / Destructor /** * This is the standard constructor of an AlkValue object. * The value will be initialized to 0. */ AlkValue(); /// The destructor ~AlkValue(); /// Copy constructor AlkValue(const AlkValue &val); /** * This constructor converts an int into an AlkValue. It can * also convert a rational number when a @a denom is supplied. * * @param num numerator of the rational number * @param denom denominator of the rational number (defaults to 1) */ explicit AlkValue(const int num, const unsigned int denom = 1); /** * Convenience ctor for usage with mpz_class objects as numerator * and denominator. * * @param num numerator of the rational number * @param denom denominator of the rational number (defaults to 1) */ explicit AlkValue(const mpz_class &num, const mpz_class &denom); /** * Convenience ctor to create an AlkValue object based on an mpq_class object */ explicit AlkValue(const mpq_class &val); /** * This constructor converts a double into an AlkValue. In case * a @a denom is supplied with a value different from zero, the * @a val will be rounded to be based on the supplied @a denom. * e.g. val = 1.234 and denom = 100 will construct an AlkValue * of 1.23. The rounding method is @p RoundRound. * * @sa AlkValue::convertDenominator() * * @param val the double value * @param denom the denominator of the resulting AlkValue * * @note In case one wants to use the number of decimal places * to specify the length of the fractional part, use * * @code * AlkValue alk(1.234, AlkValue::precisionToDenominator(2).get_ui()); * // alk == 1.23 * @endcode */ explicit AlkValue(const double &val, const unsigned int denom = 0); /** * This constructor converts a QString into an AlkValue. * Several formats are supported: * * -# prices in the form "8 5/16" * -# our own toString() format * -# others * Others may be enclosed in "(" and ")" and treated as negative. * They may start or end with a dash and treated as negative. * The decimal symbols is identified as provided in @a decimalSymbol. * All other non-numeric characters are skipped */ AlkValue(const QString &str, const QChar &decimalSymbol); /** * Returns the current value converted to the given @a denom (default is 100 * or two digits of precision). The rounding method used is controlled by * the @a how argument and defaults to @p RoundRound. */ AlkValue convertDenominator(const int denom = 100, const RoundingMethod how = RoundRound) const; /** * This is a convenience function for convertDenom but instead of providing * the new denominator one provides the number of digits for the @a precision. * This value defaults to 2. The rounding method used is controlled by * the @a how argument and defaults to @p RoundRound. */ AlkValue convertPrecision(const int precision = 2, const RoundingMethod how = RoundRound) const; // assignment operators const AlkValue & operator=(const AlkValue &val); const AlkValue & operator=(int num); const AlkValue & operator=(double num); const AlkValue & operator=(const QString &str); // comparison bool operator==(const AlkValue &val) const; bool operator!=(const AlkValue &val) const; bool operator<(const AlkValue &val) const; bool operator>(const AlkValue &val) const; bool operator<=(const AlkValue &val) const; bool operator>=(const AlkValue &val) const; // calculation AlkValue operator+(const AlkValue &summand) const; AlkValue operator-(const AlkValue &minuend) const; AlkValue operator*(const AlkValue &factor) const; AlkValue operator/(const AlkValue &divisor) const; + AlkValue operator%(int operand) const; AlkValue operator*(int factor) const; // unary operators AlkValue operator-() const; AlkValue & operator+= (const AlkValue &val); AlkValue & operator-= (const AlkValue &val); AlkValue & operator/= (const AlkValue &val); AlkValue & operator*= (const AlkValue &val); // functions /// @return the absolute value of the AlkValue AlkValue abs() const; /// @return QString representation in form '[-]num/denom'. QString toString() const; double toDouble() const; /** * This method transforms the AlkValue into its canonicalized * form by reducing it to the smallest denominator. Example: * 25/100 will be converted to 1/4. Use this function at the * end of a longer calculation as all AlkValue methods require * the object to be in the canonicalized form. For speed purposes * the conversion is not performed before each operation. * * @return const reference to the object */ const AlkValue& canonicalize(); /// convert a denominator to a precision /// e.g. 100 -> 2, 1000 -> 3 /// in case of a negative @a denom, the function returns 0 static mpz_class denominatorToPrecision(mpz_class denom); /// convert a precision to the corresponding denominator /// e.g. 2 -> 100, 4 -> 10000 /// in case of a negative @a prec, the function returns 1 static mpz_class precisionToDenominator(mpz_class prec); protected: /// \internal unit test class friend class AlkValueTest; /// provides an access method to the private value storage /// for derived classes const mpq_class &valueRef() const; mpq_class &valueRef(); private: /// \internal d-pointer class. class Private; /// \internal d-pointer instance. QSharedDataPointer d; /// \internal shared zero value. static QSharedDataPointer& sharedZero(); // The following methods are not implemented (yet) // ALKIMIA_EXPORT friend QDataStream &operator<<(QDataStream &, const AlkValue &); // ALKIMIA_EXPORT friend QDataStream &operator>>(QDataStream &, AlkValue &); }; #endif diff --git a/tools/onlinequoteseditor/CMakeLists.txt b/tools/onlinequoteseditor/CMakeLists.txt index 0aca5cb..c4a7974 100644 --- a/tools/onlinequoteseditor/CMakeLists.txt +++ b/tools/onlinequoteseditor/CMakeLists.txt @@ -1,61 +1,64 @@ add_subdirectory(icons) include_directories( ${CMAKE_SOURCE_DIR}/src ${CMAKE_BINARY_DIR}/src ) set(SOURCES applicationsettings.cpp main.cpp mainwindow.cpp ) set(HEADERS applicationsettings.h mainwindow.h ) set(UI mainwindow.ui ) ki18n_wrap_ui(SOURCES ${UI} ) ecm_add_app_icon(SOURCES ICONS - icons/16-apps-onlinequoteseditor.png - icons/22-apps-onlinequoteseditor.png - icons/32-apps-onlinequoteseditor.png - icons/48-apps-onlinequoteseditor.png - icons/64-apps-onlinequoteseditor.png - icons/128-apps-onlinequoteseditor.png + icons/16-apps-onlinequoteseditor5.png + icons/22-apps-onlinequoteseditor5.png + icons/32-apps-onlinequoteseditor5.png + icons/48-apps-onlinequoteseditor5.png + icons/64-apps-onlinequoteseditor5.png + icons/128-apps-onlinequoteseditor5.png ) ecm_add_executable(onlinequoteseditor ${SOURCES} ${HEADERS}) if(BUILD_QT4) set(LIBS ${KDE4_KDECORE_LIBS} ${KDE4_KDEUI_LIBS} ${QT_USE_LIBSPREFIX}Core ${QT_USE_LIBSPREFIX}Network ${QT_USE_LIBSPREFIX}WebKit ) else() set(LIBS Qt5::Widgets Qt5::WebKitWidgets KF5::CoreAddons KF5::I18n ) add_definitions(-DTRANSLATION_DOMAIN=\"onlinequoteseditor\") endif() target_link_libraries(onlinequoteseditor alkimia alkimia-internal ${LIBS} ) +set_target_properties(onlinequoteseditor PROPERTIES OUTPUT_NAME onlinequoteseditor${TARGET_SUFFIX}) install(TARGETS onlinequoteseditor ${KDE_INSTALL_TARGETS_DEFAULT_ARGS}) -install(FILES org.kde.onlinequoteeditor.desktop DESTINATION ${XDG_APPS_INSTALL_DIR}) + +configure_file(org.kde.onlinequoteseditor.desktop.cmake ${CMAKE_CURRENT_BINARY_DIR}/org.kde.onlinequoteseditor${TARGET_SUFFIX}.desktop) +install(FILES ${CMAKE_CURRENT_BINARY_DIR}/org.kde.onlinequoteseditor${TARGET_SUFFIX}.desktop DESTINATION ${XDG_APPS_INSTALL_DIR}) diff --git a/tools/onlinequoteseditor/icons/128-apps-onlinequoteseditor.png b/tools/onlinequoteseditor/icons/128-apps-onlinequoteseditor5.png similarity index 100% rename from tools/onlinequoteseditor/icons/128-apps-onlinequoteseditor.png rename to tools/onlinequoteseditor/icons/128-apps-onlinequoteseditor5.png diff --git a/tools/onlinequoteseditor/icons/16-apps-onlinequoteseditor.png b/tools/onlinequoteseditor/icons/16-apps-onlinequoteseditor5.png similarity index 100% rename from tools/onlinequoteseditor/icons/16-apps-onlinequoteseditor.png rename to tools/onlinequoteseditor/icons/16-apps-onlinequoteseditor5.png diff --git a/tools/onlinequoteseditor/icons/22-apps-onlinequoteseditor.png b/tools/onlinequoteseditor/icons/22-apps-onlinequoteseditor5.png similarity index 100% rename from tools/onlinequoteseditor/icons/22-apps-onlinequoteseditor.png rename to tools/onlinequoteseditor/icons/22-apps-onlinequoteseditor5.png diff --git a/tools/onlinequoteseditor/icons/32-apps-onlinequoteseditor.png b/tools/onlinequoteseditor/icons/32-apps-onlinequoteseditor5.png similarity index 100% rename from tools/onlinequoteseditor/icons/32-apps-onlinequoteseditor.png rename to tools/onlinequoteseditor/icons/32-apps-onlinequoteseditor5.png diff --git a/tools/onlinequoteseditor/icons/48-apps-onlinequoteseditor.png b/tools/onlinequoteseditor/icons/48-apps-onlinequoteseditor5.png similarity index 100% rename from tools/onlinequoteseditor/icons/48-apps-onlinequoteseditor.png rename to tools/onlinequoteseditor/icons/48-apps-onlinequoteseditor5.png diff --git a/tools/onlinequoteseditor/icons/64-apps-onlinequoteseditor.png b/tools/onlinequoteseditor/icons/64-apps-onlinequoteseditor5.png similarity index 100% rename from tools/onlinequoteseditor/icons/64-apps-onlinequoteseditor.png rename to tools/onlinequoteseditor/icons/64-apps-onlinequoteseditor5.png diff --git a/tools/onlinequoteseditor/icons/CMakeLists.txt b/tools/onlinequoteseditor/icons/CMakeLists.txt index 1b47b49..d7ccc47 100644 --- a/tools/onlinequoteseditor/icons/CMakeLists.txt +++ b/tools/onlinequoteseditor/icons/CMakeLists.txt @@ -1,11 +1,11 @@ ecm_install_icons( - ICONS 16-apps-onlinequoteseditor.png - 22-apps-onlinequoteseditor.png - 32-apps-onlinequoteseditor.png - 48-apps-onlinequoteseditor.png - 64-apps-onlinequoteseditor.png - 128-apps-onlinequoteseditor.png - sc-apps-onlinequoteseditor.svgz + ICONS 16-apps-onlinequoteseditor5.png + 22-apps-onlinequoteseditor5.png + 32-apps-onlinequoteseditor5.png + 48-apps-onlinequoteseditor5.png + 64-apps-onlinequoteseditor5.png + 128-apps-onlinequoteseditor5.png + sc-apps-onlinequoteseditor5.svgz THEME hicolor DESTINATION ${ICON_INSTALL_DIR} ) diff --git a/tools/onlinequoteseditor/icons/sc-apps-onlinequoteseditor.svgz b/tools/onlinequoteseditor/icons/sc-apps-onlinequoteseditor5.svgz similarity index 100% rename from tools/onlinequoteseditor/icons/sc-apps-onlinequoteseditor.svgz rename to tools/onlinequoteseditor/icons/sc-apps-onlinequoteseditor5.svgz diff --git a/tools/onlinequoteseditor/mainwindow.cpp b/tools/onlinequoteseditor/mainwindow.cpp index 7f072f9..14c4452 100644 --- a/tools/onlinequoteseditor/mainwindow.cpp +++ b/tools/onlinequoteseditor/mainwindow.cpp @@ -1,166 +1,169 @@ /*************************************************************************** * 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 #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.setWebPageEnabled(true); 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("alkimia5", AlkOnlineQuotesProfile::Type::Alkimia5, "alkimia-quotes.knsrc")); manager.addProfile(new AlkOnlineQuotesProfile("skrooge4", AlkOnlineQuotesProfile::Type::Skrooge4, "skrooge-quotes.knsrc")); - manager.addProfile(new AlkOnlineQuotesProfile("skrooge5", AlkOnlineQuotesProfile::Type::Skrooge5, "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 d->ui.setupUi(this); #if QT_VERSION >= QT_VERSION_CHECK(5,0,0) d->ui.mainToolBar->deleteLater(); #endif d->quotesWidget = new AlkOnlineQuotesWidget(true, true); - QDockWidget *profilesWidget = new QDockWidget(tr("Profiles"), this); + QDockWidget *profilesWidget = new QDockWidget(i18n("Profiles"), this); profilesWidget->setObjectName("profilesDockWidget"); profilesWidget->setWidget(d->quotesWidget->profilesWidget()); addDockWidget(Qt::LeftDockWidgetArea, profilesWidget); - QDockWidget *profileDetailsWidget = new QDockWidget(tr("Profile details"), this); + QDockWidget *profileDetailsWidget = new QDockWidget(i18n("Profile details"), this); profileDetailsWidget->setObjectName("profileDetailsDockWidget"); profileDetailsWidget->setWidget(d->quotesWidget->profileDetailsWidget()); addDockWidget(Qt::RightDockWidgetArea, profileDetailsWidget); - QDockWidget *onlineQuotesWidget = new QDockWidget(tr("Online quotes"), this); + QDockWidget *onlineQuotesWidget = new QDockWidget(i18n("Online quotes"), this); onlineQuotesWidget->setObjectName("onlineQuotesDockWidget"); onlineQuotesWidget->setWidget(d->quotesWidget->onlineQuotesWidget()); addDockWidget(Qt::LeftDockWidgetArea, onlineQuotesWidget); - QDockWidget *debugWidget = new QDockWidget(tr("Debug"), this); + QDockWidget *debugWidget = new QDockWidget(i18n("Debug"), this); debugWidget->setObjectName("debugDockWidget"); debugWidget->setWidget(d->quotesWidget->debugWidget()); addDockWidget(Qt::LeftDockWidgetArea, debugWidget); - QDockWidget *quoteDetailsWidget = new QDockWidget(tr("Quote details"), this); + QDockWidget *quoteDetailsWidget = new QDockWidget(i18n("Quote details"), this); quoteDetailsWidget->setObjectName("quoteDetailsDockWidget"); quoteDetailsWidget->setWidget(d->quotesWidget->quoteDetailsWidget()); addDockWidget(Qt::RightDockWidgetArea, quoteDetailsWidget); - QDockWidget *browserWidget = new QDockWidget(tr("Browser"), this); + QDockWidget *browserWidget = new QDockWidget(i18n("Browser"), this); browserWidget->setObjectName("browserDockWidget"); AlkWebPage *webPage = manager.webPage(); connect(webPage, SIGNAL(urlChanged(QUrl)), this, SLOT(slotUrlChanged(QUrl))); d->urlLine = new QLineEdit; connect(d->urlLine, SIGNAL(editingFinished()), this, SLOT(slotEditingFinished())); // 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))); // setup layouts QHBoxLayout *hLayout = new QHBoxLayout; hLayout->addWidget(d->urlLine); hLayout->addWidget(box); QVBoxLayout *layout = new QVBoxLayout; layout->addLayout(hLayout); layout->addWidget(webPage); QWidget *group = new QWidget; group->setLayout(layout); browserWidget->setWidget(group); addDockWidget(Qt::RightDockWidgetArea, browserWidget); setCentralWidget(nullptr); webPage->setWebInspectorEnabled(true); readPositionSettings(); } MainWindow::~MainWindow() { delete d; } void MainWindow::closeEvent(QCloseEvent *event) { writePositionSettings(); QMainWindow::closeEvent(event); } diff --git a/tools/onlinequoteseditor/org.kde.onlinequoteeditor.desktop b/tools/onlinequoteseditor/org.kde.onlinequoteseditor.desktop.cmake similarity index 96% rename from tools/onlinequoteseditor/org.kde.onlinequoteeditor.desktop rename to tools/onlinequoteseditor/org.kde.onlinequoteseditor.desktop.cmake index d01c1a6..1fb47dd 100644 --- a/tools/onlinequoteseditor/org.kde.onlinequoteeditor.desktop +++ b/tools/onlinequoteseditor/org.kde.onlinequoteseditor.desktop.cmake @@ -1,61 +1,61 @@ # KDE Config File [Desktop Entry] Type=Application -Exec=onlinequoteseditor -Icon=onlinequoteseditor -X-DocPath=onlinequoteseditor/index.html +Exec=onlinequoteseditor@TARGET_SUFFIX@ +Icon=onlinequoteseditor@TARGET_SUFFIX@ +X-DocPath=onlinequoteseditor@TARGET_SUFFIX@/index.html Terminal=false Name=Online Quotes Editor Name[ca]=Editor de cotitzacions en línia Name[ca@valencia]=Editor de cotitzacions en línia Name[de]=Editor für Online-Kursnotizen Name[en_GB]=Online Quotes Editor Name[es]=Editor de cotizaciones en línea Name[fr]=Éditeur de cotations en ligne Name[gl]=Editor de cotizacións de internet Name[it]=Editor delle quotazioni in linea Name[ko]=온라인 시세 편집기 Name[nl]=Bewerker voor Online koersen Name[pl]=Edytor wycen z siec Name[pt]=Editor de Cotações 'Online' Name[pt_BR]=Editor de cotações online Name[sk]=Editor online ponúk Name[sv]=Direktkurseditor Name[uk]=Редактор інтернет-курсів Name[x-test]=xxOnline Quotes Editorxx GenericName=Editor for online price quotes GenericName[ca]=Editor per a cotitzacions de preus en línia GenericName[ca@valencia]=Editor per a cotitzacions de preus en línia GenericName[de]=Editor für Online-Kursnotizen GenericName[en_GB]=Editor for online price quotes GenericName[es]=Editor de cotizaciones de precios en línea GenericName[fr]=Éditeur de cotations en ligne GenericName[gl]=Editor de cotizacións de internet GenericName[it]=Editor per le quotazioni dei prezzi in linea GenericName[ko]=온라인 가격 시세 편집기 GenericName[nl]=Bewerker voor online koersen GenericName[pl]=Edytor do wycen z sieci GenericName[pt]=Editor das cotações de preços 'online' GenericName[pt_BR]=Editor de cotações de preço online GenericName[sv]=Editor för direktkurser GenericName[uk]=Редактор інтернет-курсів GenericName[x-test]=xxEditor for online price quotesxx Comment=Editor for online price quotes used by finance applications Comment[ca]=Editor per a cotitzacions de preus en línia usat per les aplicacions financeres Comment[ca@valencia]=Editor per a cotitzacions de preus en línia usat per les aplicacions financeres Comment[de]=Editor für Online-Kursnotizen für Finanz-Anwendungen Comment[en_GB]=Editor for online price quotes used by finance applications Comment[es]=Editor de cotizaciones de precios en línea usado por aplicaciones de finanzas Comment[fr]=Éditeur de cotations en ligne utilisé par les applications financières Comment[gl]=Editor de cotizacións de internet usado por aplicativos financeiros Comment[it]=Editor per le quotazioni dei prezzi in linea utilizzate dalle applicazioni finanziarie Comment[ko]=재무 관리 프로그램에서 사용하는 온라인 가격 시세 편집기 Comment[nl]=Bewerker voor online koersen gebruikt door financiële toepassingen Comment[pl]=Edytor dla wycen z sieci używanych przez aplikacje finansowe Comment[pt]=Editor das cotações de preços 'online' usadas pelas aplicações financeiras Comment[pt_BR]=Editor de cotações de preço online usado por aplicativos financeiros Comment[sk]=Editor pre online cenové ponuky používané finančnými aplikáciami Comment[sv]=Editor för direktkurser använd av ekonomiprogram Comment[uk]=Редактор інтернет-курсів, який використовується у фінансових програмах Comment[x-test]=xxEditor for online price quotes used by finance applicationsxx Categories=Qt;KDE;Office;Finance;