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;