diff --git a/.clang-format b/.clang-format index 548068d..b8140ac 100644 --- a/.clang-format +++ b/.clang-format @@ -31,11 +31,11 @@ BinPackArguments: true BinPackParameters: true BraceWrapping: AfterCaseLabel: false - AfterClass: false + AfterClass: true AfterControlStatement: Never AfterEnum: false - AfterFunction: false - AfterNamespace: false + AfterFunction: true + AfterNamespace: true AfterObjCDeclaration: false AfterStruct: false AfterUnion: false @@ -51,10 +51,6 @@ BraceWrapping: BreakBeforeBinaryOperators: None BreakBeforeConceptDeclarations: true BreakBeforeBraces: Custom -BraceWrapping: - AfterClass: true - AfterFunction: true - AfterNamespace: true BreakBeforeInheritanceComma: false BreakInheritanceList: BeforeColon BreakBeforeTernaryOperators: true diff --git a/.gitmodules b/.gitmodules index 672e543..e69de29 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,9 +0,0 @@ -[submodule "subprojects/nlohmann_json"] - path = subprojects/nlohmann_json - url = https://github.com/nlohmann/json.git -[submodule "subprojects/singleapplication"] - path = subprojects/singleapplication/singleapplication.git - url = https://github.com/itay-grudev/SingleApplication.git -[submodule "subprojects/csv-parser"] - path = subprojects/csv-parser - url = https://github.com/vincentlaucsb/csv-parser.git diff --git a/.vscode/settings.json b/.vscode/settings.json index 43f0c12..41f34ac 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -65,7 +65,37 @@ "condition_variable": "cpp", "mutex": "cpp", "hash_map": "cpp", - "future": "cpp" + "future": "cpp", + "bit": "cpp", + "compare": "cpp", + "concepts": "cpp", + "forward_list": "cpp", + "map": "cpp", + "set": "cpp", + "string": "cpp", + "unordered_set": "cpp", + "iterator": "cpp", + "memory_resource": "cpp", + "random": "cpp", + "semaphore": "cpp", + "stop_token": "cpp", + "__bit_reference": "cpp", + "__bits": "cpp", + "__config": "cpp", + "__debug": "cpp", + "__errc": "cpp", + "__hash_table": "cpp", + "__locale": "cpp", + "__mutex_base": "cpp", + "__node_handle": "cpp", + "__split_buffer": "cpp", + "__threading_support": "cpp", + "__tree": "cpp", + "__tuple": "cpp", + "__verbose_abort": "cpp", + "format": "cpp", + "ios": "cpp", + "locale": "cpp" }, "C_Cpp.clang_format_path": "/usr/bin/clang-format", "cmake.configureOnOpen": true, diff --git a/CMakeLists.txt b/CMakeLists.txt index 2d13e95..86b072a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,13 +1,14 @@ -cmake_minimum_required(VERSION 3.8) +cmake_minimum_required(VERSION 3.30) -project(kima2 VERSION 1.6.0) +project(kima2 VERSION 1.9.2) set(CMAKE_MODULE_PATH "${CMAKE_HOME_DIRECTORY}/cmake") set(CMAKE_CXX_STANDARD 20) set(CMAKE_CXX_STANDARD_REQUIRED ON) -#include(InstallRequiredSystemLibraries) +# For SingleApplication and nlohmann_json +include(FetchContent) if(MSVC) add_compile_options(/W4 /WX) @@ -17,6 +18,7 @@ else() endif() configure_file(config.h.in ${PROJECT_BINARY_DIR}/config.h) +configure_file(de.rustysoft.kima2.metainfo.xml.in ${PROJECT_BINARY_DIR}/de.rustysoft.kima2.metainfo.xml) set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) @@ -28,7 +30,6 @@ if(KIMA2_USE_EXTERNAL_JSON) find_package(nlohmann_json REQUIRED) endif() -add_subdirectory(subprojects) add_subdirectory(src) #if(NOT CMAKE_BUILD_TYPE OR CMAKE_BUILD_TYPE MATCHES Debug) @@ -79,6 +80,8 @@ else(WIN32 AND NOT UNIX) DESTINATION share/${PROJECT_NAME}) install(FILES "${CMAKE_SOURCE_DIR}/misc/kima2.svg" DESTINATION share/icons/hicolor/scalable/apps) + install(FILES de.rustysoft.kima2.metainfo.xml + DESTINATION share/metainfo) endif (WIN32 AND NOT UNIX) if( MINGW ) @@ -95,10 +98,9 @@ if( MINGW ) ${MINGW_PATH}/libwinpthread-1.dll ${MINGW_PATH}/libsqlite3-0.dll ${MINGW_PATH}/libusb-1.0.dll - ${MINGW_PATH}/libxlnt.dll - ${MINGW_PATH}/libicuuc71.dll - ${MINGW_PATH}/libicuin71.dll - ${MINGW_PATH}/libicudt71.dll + ${MINGW_PATH}/libicuuc76.dll + ${MINGW_PATH}/libicuin76.dll + ${MINGW_PATH}/libicudt76.dll ${MINGW_PATH}/libpcre2-16-0.dll ${MINGW_PATH}/libpcre2-8-0.dll ${MINGW_PATH}/zlib1.dll @@ -109,13 +111,12 @@ if( MINGW ) ${MINGW_PATH}/libgraphite2.dll ${MINGW_PATH}/libbz2-1.dll ${MINGW_PATH}/libintl-8.dll - ${MINGW_PATH}/libpcre-1.dll ${MINGW_PATH}/libdouble-conversion.dll ${MINGW_PATH}/libzstd.dll ${MINGW_PATH}/libmd4c.dll ${MINGW_PATH}/libbrotlicommon.dll ${MINGW_PATH}/libbrotlidec.dll - ${MINGW_PATH}/libfmt.dll + #${MINGW_PATH}/libfmt.dll ${MINGW_PATH}/libb2-1.dll ${MINGW_PATH}/libiconv-2.dll) install(FILES ${MINGW_PATH}/../share/qt6/plugins/platforms/qwindows.dll @@ -123,14 +124,13 @@ if( MINGW ) DESTINATION bin/platforms) #install(FILES ${MINGW_PATH}/../share/qt6/plugins/printsupport/windowsprintersupport.dll # DESTINATION bin/printsupport) - install(FILES ${MINGW_PATH}/../share/qt5/translations/qtbase_de.qm + install(FILES ${MINGW_PATH}/../share/qt6/translations/qtbase_de.qm ${MINGW_PATH}/../share/qt6/translations/qt_de.qm ${MINGW_PATH}/../share/qt6/translations/qt_help_de.qm ${MINGW_PATH}/../share/qt6/translations/qtmultimedia_de.qm - #${MINGW_PATH}/../share/qt6/translations/qtquickcontrols_de.qm - #${MINGW_PATH}/../share/qt6/translations/qtscript_de.qm - #${MINGW_PATH}/../share/qt6/translations/qtxmlpatterns_de.qm - DESTINATION bin/translations) + DESTINATION bin/share/qt6/translations) + install(FILES ${MINGW_PATH}/../share/qt6/plugins/styles/qmodernwindowsstyle.dll + DESTINATION bin/styles) endif( MINGW ) include(InstallRequiredSystemLibraries) diff --git a/LICENSE b/LICENSE index e6d4fba..0b127da 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,4 @@ -Copyright © 2018-2022 Martin Brodbeck +Copyright © 2018-2025 Martin Brodbeck Hiermit wird unentgeltlich jeder Person, die eine Kopie der Software und der zugehörigen Dokumentationen (die "Software") erhält, die Erlaubnis erteilt, diff --git a/README.md b/README.md index 28369cf..3c0dd87 100644 --- a/README.md +++ b/README.md @@ -13,19 +13,16 @@ Verkaufsdaten nach dem Verkaufsende auszutauschen. Ebenso können über einen ESC/POS-Drucker Quittungen ausgestellt werden. ## Installation -Auf [rustysoft.de](https://www.rustysoft.de/software/kima2/) werden verschiedene Installationspakete (Arch Linux, -Ubuntu, Windows) angeboten. Bitte die Hinweise dort beachten. +Auf [rustysoft.de](https://www.rustysoft.de/software/kima2/) werden die Installationsmöglichkeiten (Flatpak, Windows-Installer) erläutert. Bitte die Hinweise dort beachten. -### Selbst compilieren +## Selbst compilieren KIMA2 benötigt folgende Libraries: -* Qt5 -* boost >= 1.62 +* Qt 6 +* boost >= 1.80 * libusb-1.0 -* xlnt >= 1.3.0 * nlohmann-json (als 3rdparty submodule vorhanden) -Da Features aus C++17 verwendet werden sowie std::filesystem, sollte als Compiler mindestens -GCC 8 verwendet werden. +Da Features aus C++20 sowie von neueren Compilern verwendet werden, sollte als Compiler mindestens GCC 14 verwendet werden. Die Installationsschritte unter Linux sind wie folgt: ``` @@ -37,4 +34,4 @@ sudo make install ``` Unter Windows muss vorab MinGW eingerichtet werden (z. B. MSYS2). Nach der Compilierung kann mit -`cpack -G NSIS` ein Installationspaket erstellt werden. \ No newline at end of file +`cpack -G NSIS` ein Installationspaket erstellt werden. diff --git a/de.rustysoft.kima2.metainfo.xml.in b/de.rustysoft.kima2.metainfo.xml.in new file mode 100644 index 0000000..9f228f7 --- /dev/null +++ b/de.rustysoft.kima2.metainfo.xml.in @@ -0,0 +1,27 @@ + + + de.rustysoft.kima2 + + KIMA2 + A small cash point program for children’s stuff markets + + MIT + GPL-3.0-or-later + + + + + + +

+ A small cash point program for children's stuff markets. German language only. +

+
+ + de.rustysoft.kima2.desktop + + + https://rustysoft.de/images/software/kima2/screenshot.png + + +
diff --git a/manual/Benutzerhandbuch.odt b/manual/Benutzerhandbuch.odt index 37dac87..c2defc2 100644 Binary files a/manual/Benutzerhandbuch.odt and b/manual/Benutzerhandbuch.odt differ diff --git a/manual/Benutzerhandbuch.pdf b/manual/Benutzerhandbuch.pdf index 9c7b987..793f996 100644 Binary files a/manual/Benutzerhandbuch.pdf and b/manual/Benutzerhandbuch.pdf differ diff --git a/meson.build b/meson.build deleted file mode 100644 index 1ff5920..0000000 --- a/meson.build +++ /dev/null @@ -1,33 +0,0 @@ -project('kima2', 'cpp', default_options : ['cpp_std=c++20'], version : '1.6.0') - -conf_data = configuration_data() -conf_data.set('PROJECT_VERSION', '"' + meson.project_version() + '"') -configure_file(output : 'config.h', - configuration : conf_data) -configuration_inc = include_directories('.') - -#csv = cmake.subproject('csv-parser') -#csv_lib = csv.dependency('csv') - -nlohmann_lib = dependency('nlohmann_json', version : '>=3.5.0', required : false) - -if not nlohmann_lib.found() - nlohmann_inc = include_directories('subprojects/nlohmann_json/single_include') - nlohmann_lib = declare_dependency(include_directories : nlohmann_inc) -endif - -csv_inc = include_directories('subprojects/csv-parser/single_include') -csv_dep = declare_dependency(include_directories : csv_inc) - -singleapp_proj = subproject('singleapplication') -singleapp_lib = singleapp_proj.get_variable('singleapp_lib') -singleapp_dep = singleapp_proj.get_variable('singleapp_dep') - -subdir('src') - -if build_machine.system() == 'linux' - install_data('misc/kima2.svg', install_dir : get_option('datadir') / 'icons/hicolor/scalable/apps') - install_data('misc/kima2.desktop', install_dir : get_option('datadir') / 'applications') - install_data('manual/Benutzerhandbuch.pdf', install_dir : get_option('datadir') / 'kima2') -endif - diff --git a/misc/PKGBUILD b/misc/PKGBUILD index f679f03..a50410c 100644 --- a/misc/PKGBUILD +++ b/misc/PKGBUILD @@ -1,21 +1,27 @@ # Maintainer: Martin Brodbeck pkgname=kima2 -pkgver=1.5.0 +pkgver=1.7.1 pkgrel=1 pkgdesc="A small cash point program for children's things markets (German only)" arch=('i686' 'x86_64') -url="http://www.rustysoft.de/?01_kima2" +url="http://www.rustysoft.de/software/kima2" license=('custom') -depends=('glibc' 'libusb' 'qt5-base' 'sqlite3' 'xlnt') +depends=('glibc' 'libusb' 'qt6-base' 'sqlite3') makedepends=('boost>=1.62') -source=($pkgname-$pkgver.tar.gz) -md5sums=('c0e6a64b5037675edce4ba8bc4639bd3') +source=(git+https://git.rustysoft.de/martin/kima2) +sha256sums=('SKIP') build() { - if [ ! -d $pkgname/build ]; then - mkdir $pkgname/build + cd $pkgname + + git checkout v$pkgver + git submodule init + git submodule update + + if [ -d build ]; then + rm -rf build fi - cd $pkgname/build + mkdir build && cd build cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/usr -DKIMA2_USE_EXTERNAL_JSON=OFF .. make diff --git a/misc/kima2.desktop b/misc/kima2.desktop index 004ab5f..becabdf 100644 --- a/misc/kima2.desktop +++ b/misc/kima2.desktop @@ -3,8 +3,8 @@ Type=Application Name=KIMA2 GenericName=Cash Point Program GenericName[de]=Kassenprogramm -Comment=A small cash point program -Comment[de]=Ein kleines Kassenprogramm +Comment=A small cash point program for children's stuff markets +Comment[de]=Ein kleines Kassenprogramm für Kindersachenmärkte Exec=kima2 Icon=kima2 Categories=Office; diff --git a/misc/kima2.spec b/misc/kima2.spec index 0c3feff..2c586a8 100644 --- a/misc/kima2.spec +++ b/misc/kima2.spec @@ -13,7 +13,6 @@ BuildRequires: boost-date-time BuildRequires: sqlite-devel BuildRequires: libusb-devel BuildRequires: qt5-qtdeclarative-devel -#BuildRequires: pkgconfig(xlnt) #BuildRequires: pkgconfig(pthreads) %description diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 90b6fbc..681cf5c 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -1,23 +1,14 @@ set(Boost_USE_STATIC_LIBS ON) -find_package(Boost 1.62 REQUIRED) +find_package(Boost 1.78 REQUIRED) find_package(SQLite3 REQUIRED) -# Because csv-parser needs threads: -set(THREADS_PREFER_PTHREAD_FLAG ON) -find_package(Threads REQUIRED) +FetchContent_Declare(json URL https://github.com/nlohmann/json/releases/download/v3.11.3/json.tar.xz) +FetchContent_MakeAvailable(json) -find_package(fmt) +add_library(core STATIC) - -if (MINGW) - find_package(XLNT REQUIRED STATIC) -else (MINGW) - find_package(PkgConfig REQUIRED) - pkg_check_modules(XLNT REQUIRED xlnt>=1.3) -endif (MINGW) - -set(CORE_SOURCES +target_sources(core PRIVATE database.cpp entity.cpp entityint.cpp @@ -26,20 +17,16 @@ set(CORE_SOURCES article.cpp sale.cpp marketplace.cpp - excelreader.cpp csvreader.cpp jsonutil.cpp utils.cpp ) - -add_library(core STATIC ${CORE_SOURCES}) -target_include_directories(core PRIVATE ${PROJECT_SOURCE_DIR}/subprojects/csv-parser/single_include) if (WIN32) - target_link_libraries(core PRIVATE sqlite3 nlohmann_json::nlohmann_json ${XLNT_LIBRARY} Threads::Threads fmt::fmt) + target_link_libraries(core PRIVATE sqlite3 nlohmann_json::nlohmann_json) target_link_libraries(core PRIVATE bcrypt) else() - target_link_libraries(core PRIVATE sqlite3 nlohmann_json::nlohmann_json ${XLNT_LIBRARIES} Threads::Threads fmt::fmt) + target_link_libraries(core PRIVATE sqlite3 nlohmann_json::nlohmann_json) endif() target_include_directories(core PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/..) diff --git a/src/core/csvreader.cpp b/src/core/csvreader.cpp index 5e092c9..6c2cdf6 100644 --- a/src/core/csvreader.cpp +++ b/src/core/csvreader.cpp @@ -3,7 +3,8 @@ #include -#include +// #include +#include #ifdef DELETE #undef DELETE @@ -13,17 +14,16 @@ namespace fs = std::filesystem; std::size_t CsvReader::readSellersFromFile(const fs::path &filePath, Marketplace *market) { - csv::CSVFormat format; - format.delimiter(';'); #if defined(_WIN64) || defined(_WIN32) // Windows: Somhow this is necessary in order to open file names with umlauts auto wide = filePath.wstring(); std::string fileName(wide.begin(), wide.end()); - csv::CSVReader csvReader(fileName, format); + std::ifstream infile(fileName); #else - csv::CSVReader csvReader(filePath.string(), format); + // csv::CSVReader csvReader(filePath.string(), format); + std::ifstream infile(filePath.string()); #endif for (auto &seller : market->getSellers()) { @@ -32,28 +32,35 @@ std::size_t CsvReader::readSellersFromFile(const fs::path &filePath, Marketplace market->storeToDb(true); - for (csv::CSVRow &row : csvReader) { - if (!row[0].is_int()) { + std::string line; + + while (getline(infile, line)) { + std::vector strs; + boost::split(strs, line, boost::is_any_of(";")); + + auto seller = std::make_unique(); + + try { + int sellerNo = std::stoi(strs[0]); + seller->setSellerNo(sellerNo); + } catch (std::invalid_argument const &ex) { continue; } - auto seller = std::make_unique(); - seller->setSellerNo(row[0].get()); - if (row[1].is_int()) { - seller->setNumArticlesOffered(row[1].get()); - } else { + if (isNumber(strs[1])) + seller->setNumArticlesOffered(std::stoi(strs[1])); + else seller->setNumArticlesOffered(0); - } // If both, first name and last name, are empty, use N. N. // Else, use the real values. - if (row[2].get().empty() && row[3].get().empty()) { + if (strs[2].empty() && strs[2].empty()) { seller->setFirstName("N."); seller->setLastName("N."); } else { - std::string firstName = row[2].get(); + std::string firstName = strs[2]; seller->setFirstName(trim(firstName)); - std::string lastName = row[3].get(); + std::string lastName = strs[3]; seller->setLastName(trim(lastName)); } diff --git a/src/core/database.cpp b/src/core/database.cpp index 863bc5d..d6b7384 100644 --- a/src/core/database.cpp +++ b/src/core/database.cpp @@ -2,7 +2,7 @@ #include #include -#include +#include #include #include #include @@ -47,7 +47,7 @@ void Database::newDb() fs::path destPath = sourcePath.parent_path() / sourcePath.stem(); auto chronoTime = std::chrono::system_clock::now(); - std::string timeString = fmt::format("{0:%FT%H-%M-%S}", chronoTime); + std::string timeString = std::format("{0:%FT%H-%M-%S}", chronoTime); destPath += std::string("_") += timeString += ".db"; diff --git a/src/core/excelreader.cpp b/src/core/excelreader.cpp deleted file mode 100644 index f2d9372..0000000 --- a/src/core/excelreader.cpp +++ /dev/null @@ -1,72 +0,0 @@ -#include "excelreader.h" -#include "utils.h" - -#include -#include - -namespace fs = std::filesystem; - -std::size_t ExcelReader::readSellersFromFile(const fs::path &filePath, Marketplace *market) -{ - xlnt::workbook wb; - std::ifstream mystream(filePath, std::ios::binary); - if (!mystream.is_open()) { - throw std::runtime_error("Could not open Excel file"); - } - wb.load(mystream); - - for (auto &seller : market->getSellers()) { - seller->setState(Seller::State::DELETE); - } - - market->storeToDb(true); - auto ws = wb.sheet_by_index(0); - - for (auto row : ws.rows(true)) { - // Skip the row if the first value is not a number (= seller no) - if (row[0].data_type() != xlnt::cell::type::number) { - continue; - } - - auto seller = std::make_unique(); - seller->setSellerNo(row[0].value()); - seller->setNumArticlesOffered(row[1].value()); - - // If both, first name and last name, are empty, use N. N. - // Else, use the real values. - if (row[2].value().empty() && row[3].value().empty()) { - seller->setFirstName("N."); - seller->setLastName("N."); - } else { - std::string firstName = row[2].value(); - seller->setFirstName(trim(firstName)); - std::string lastName = row[3].value(); - seller->setLastName(trim(lastName)); - } - - market->getSellers().push_back(std::move(seller)); - } - - // Add one additional seller "RESERVE RESERVE" - auto seller = std::make_unique(); - seller->setSellerNo(market->getNextSellerNo()); - seller->setFirstName("RESERVE"); - seller->setLastName("RESERVE"); - market->getSellers().push_back(std::move(seller)); - - // If there was no special seller "Sonderkonto" in import data, then create one - auto specialSeller = market->findSellerWithSellerNo(0); - if (!specialSeller) { - auto seller = std::make_unique(); - seller->setSellerNo(0); - seller->setLastName("Sonderkonto"); - seller->setFirstName("Sonderkonto"); - seller->setNumArticlesOffered(0); - market->getSellers().push_back(std::move(seller)); - } - - market->sortSellers(); - market->storeToDb(); - - return market->getSellers().size() - 1; // minus 1 because we don't count the "special" seller -} diff --git a/src/core/excelreader.h b/src/core/excelreader.h deleted file mode 100644 index 1d2eefd..0000000 --- a/src/core/excelreader.h +++ /dev/null @@ -1,19 +0,0 @@ -#ifndef EXCEL_READER_H -#define EXCEL_READER_H - -#include "marketplace.h" -#include "seller.h" - -#include -#include -#include -#include - -class ExcelReader -{ -public: - static std::size_t readSellersFromFile(const std::filesystem::path &filePath, - Marketplace *market); -}; - -#endif \ No newline at end of file diff --git a/src/core/jsonutil.cpp b/src/core/jsonutil.cpp index bd24b4e..095a7b0 100644 --- a/src/core/jsonutil.cpp +++ b/src/core/jsonutil.cpp @@ -102,8 +102,10 @@ void JsonUtil::importSales(const std::filesystem::path &filePath, Marketplace *m int source_no = jsonValues["source_no"]; if (source_no == cashPointNo) { - throw std::runtime_error("Die Kassen-Nr. der zu imporierenden Daten wird von dieser Kasse " - "hier bereits verwendet."); + std::string ret = "Die Kassen-Nr. "; + ret += std::to_string(source_no); + ret += " der zu imporierenden Daten wird von dieser Kasse hier bereits verwendet."; + throw std::runtime_error(ret); } market->setSalesToDelete(jsonValues["source_no"]); diff --git a/src/core/meson.build b/src/core/meson.build deleted file mode 100644 index b54f484..0000000 --- a/src/core/meson.build +++ /dev/null @@ -1,13 +0,0 @@ -boost = dependency('boost', modules: ['date_time'], static: true) -xlnt = dependency('xlnt') -sqlite = dependency('sqlite3') - -src = ['database.cpp', 'entity.cpp', 'entityint.cpp', 'entityuuid.cpp', - 'seller.cpp', 'article.cpp', 'sale.cpp', 'marketplace.cpp', - 'excelreader.cpp', 'csvreader.cpp', 'jsonutil.cpp', 'utils.cpp'] - -core_inc = include_directories('..') - -core_lib = static_library('core', src, dependencies: [boost, xlnt, sqlite, nlohmann_lib, csv_dep]) - -core_dep = declare_dependency(link_with: core_lib, include_directories : core_inc) diff --git a/src/core/utils.cpp b/src/core/utils.cpp index 75242f3..1725924 100644 --- a/src/core/utils.cpp +++ b/src/core/utils.cpp @@ -2,13 +2,13 @@ #include #include -#include +#include #include #include #include -using namespace fmt; +//using namespace fmt; std::string formatCentAsEuroString(const int cent, int width) { @@ -32,7 +32,7 @@ std::string formatCentAsEuroString(const int cent, int width) #else std::locale myLocale{"de_DE.utf8"}; #endif - return fmt::format(myLocale, "{:{}.2Lf} €", cent / 100.0L, width); + return std::format(myLocale, "{:{}.2Lf} €", cent / 100.0L, width); } std::string <rim(std::string &str, const std::string &chars) @@ -61,3 +61,10 @@ bool case_insensitive_match(std::string s1, std::string s2) return true; // The strings are same return false; // not matched } + +bool isNumber(const std::string &str) +{ + return !str.empty() && std::find_if(str.begin(), str.end(), [](unsigned char c) { + return !std::isdigit(c); + }) == str.end(); +} \ No newline at end of file diff --git a/src/core/utils.h b/src/core/utils.h index 3a9ac21..0adfd75 100644 --- a/src/core/utils.h +++ b/src/core/utils.h @@ -10,5 +10,6 @@ std::string <rim(std::string &str, const std::string &chars = "\t\n\v\f\r "); std::string &rtrim(std::string &str, const std::string &chars = "\t\n\v\f\r "); std::string &trim(std::string &str, const std::string &chars = "\t\n\v\f\r "); bool case_insensitive_match(std::string s1, std::string s2); +bool isNumber(const std::string &str); #endif diff --git a/src/gui/CMakeLists.txt b/src/gui/CMakeLists.txt index bde8dfc..edd2102 100644 --- a/src/gui/CMakeLists.txt +++ b/src/gui/CMakeLists.txt @@ -9,40 +9,43 @@ set(CMAKE_AUTORCC ON) # Find the QtWidgets library find_package(Qt6 COMPONENTS Widgets Network PrintSupport CONFIG REQUIRED) -#find_package(Qt5Widgets CONFIG REQUIRED) -#find_package(Qt5PrintSupport CONFIG REQUIRED) # For SingleApplication: -#find_package(Qt5Network CONFIG REQUIRED) -set(QAPPLICATION_CLASS QApplication) +set(QT_DEFAULT_MAJOR_VERSION 6 CACHE STRING "Qt version to use, defaults to 6") +set(QAPPLICATION_CLASS QApplication CACHE STRING "Inheritance class for SingleApplication") +FetchContent_Declare( + SingleApplication + GIT_REPOSITORY https://github.com/itay-grudev/SingleApplication + GIT_TAG v3.5.2 +) +FetchContent_MakeAvailable(SingleApplication) add_compile_definitions(QAPPLICATION_CLASS=${QAPPLICATION_CLASS}) -set(GUI_SOURCES - kima2.cpp - mainwindow.cpp - mainwindow.ui - sellerdialog.cpp - sellerdialog.ui - sellermodel.cpp - pricedialog.cpp - pricedialog.ui - basketmodel.cpp - salemodel.cpp - reportdialog.cpp - reportdialog.ui - reportmodel.cpp - settingsdialog.cpp - settingsdialog.ui - ../../kima2.qrc - ${PROJECT_SOURCE_DIR}/subprojects/singleapplication/singleapplication.git/singleapplication.cpp - ${PROJECT_SOURCE_DIR}/subprojects/singleapplication/singleapplication.git/singleapplication_p.cpp +add_executable(kima2) + +target_sources(kima2 PRIVATE + kima2.cpp + mainwindow.cpp + mainwindow.ui + sellerdialog.cpp + sellerdialog.ui + sellermodel.cpp + pricedialog.cpp + pricedialog.ui + basketmodel.cpp + salemodel.cpp + reportdialog.cpp + reportdialog.ui + reportmodel.cpp + settingsdialog.cpp + settingsdialog.ui + ../../kima2.qrc + kima2.rc ) -add_executable(kima2 ${GUI_SOURCES} kima2.rc) target_include_directories(kima2 PRIVATE ${PROJECT_BINARY_DIR}) -target_include_directories(kima2 PRIVATE ${PROJECT_SOURCE_DIR}/subprojects/singleapplication/singleapplication.git) -# target_link_libraries(kima2 core printer Qt5::Widgets Qt5::PrintSupport Qt5::Network stdc++fs) -target_link_libraries(kima2 core printer Qt::Core Qt::PrintSupport Qt::Network stdc++fs) + +target_link_libraries(kima2 core printer Qt::Core Qt::PrintSupport Qt::Network SingleApplication::SingleApplication) if(WIN32) set_target_properties(kima2 PROPERTIES LINK_FLAGS "-mwindows") diff --git a/src/gui/kima2.cpp b/src/gui/kima2.cpp index bd5b470..d2826bd 100644 --- a/src/gui/kima2.cpp +++ b/src/gui/kima2.cpp @@ -13,29 +13,22 @@ #include #include +using namespace Qt::Literals::StringLiterals; + int main(int argc, char *argv[]) { - SingleApplication kimaApp(argc, argv); + SingleApplication kimaApp(argc, argv, false, SingleApplication::Mode::User | SingleApplication::ExcludeAppPath | SingleApplication::ExcludeAppVersion); // QCoreApplication::setOrganizationName("RustySoft"); QCoreApplication::setOrganizationDomain("rustysoft.de"); QCoreApplication::setApplicationName("kima2"); - QTranslator qTranslator; - QLocale german(QLocale::German); -#ifdef __linux__ - bool retVal = - qTranslator.load("qt_" + german.name(), QLibraryInfo::path(QLibraryInfo::TranslationsPath)); - if (!retVal) { - throw std::runtime_error("Could not load translation."); + QTranslator qtTranslator; + + if (qtTranslator.load(QLocale::system(), u"qtbase"_s, u"_"_s, + QLibraryInfo::path(QLibraryInfo::TranslationsPath))) { + kimaApp.installTranslator(&qtTranslator); } -#endif -#ifdef _WIN32 - QApplication::setStyle(QStyleFactory::create("Fusion")); - qTranslator.load("qt_" + german.name(), - QApplication::applicationDirPath() + QDir::separator() + "translations"); -#endif - kimaApp.installTranslator(&qTranslator); QSettings settings{}; while (!settings.contains("global/cashPointNo")) { diff --git a/src/gui/mainwindow.cpp b/src/gui/mainwindow.cpp index 526fe1b..ca9031e 100644 --- a/src/gui/mainwindow.cpp +++ b/src/gui/mainwindow.cpp @@ -9,7 +9,6 @@ #include #include -#include #include #include #include @@ -100,14 +99,13 @@ MainWindow::MainWindow() for (auto location : locations) { if (QFile::exists(location + QString("/Benutzerhandbuch.pdf"))) { QDesktopServices::openUrl( - QUrl(QString("file:///") + location + QString("/Benutzerhandbuch.pdf"), - QUrl::TolerantMode)); + QUrl::fromLocalFile(location + QString("/Benutzerhandbuch.pdf"))); } } }); connect(m_ui.licenseAction, &QAction::triggered, this, [this]() { QString licenseText( - "Copyright © 2018-2022 Martin Brodbeck\n\n" + "Copyright © 2018-2025 Martin Brodbeck\n\n" "Hiermit wird unentgeltlich jeder Person, die eine Kopie der Software und der " "zugehörigen Dokumentationen (die \"Software\") erhält, die Erlaubnis erteilt, " "sie uneingeschränkt zu nutzen, inklusive und ohne Ausnahme mit dem Recht, " @@ -137,12 +135,8 @@ MainWindow::MainWindow() this->setWindowTitle("KIMA2 - Kasse Nr. " + QSettings().value("global/cashPointNo").toString()); }); - connect(m_ui.importSellerExcelAction, &QAction::triggered, this, - &MainWindow::onImportSellerExcelActionTriggered); - connect(m_ui.importSellerJsonAction, &QAction::triggered, this, - &MainWindow::onImportSellerJsonActionTriggered); - connect(m_ui.exportSellerJsonAction, &QAction::triggered, this, - &MainWindow::onExportSellerJsonActionTriggered); + connect(m_ui.importSellerAction, &QAction::triggered, this, + &MainWindow::onImportSellerActionTriggered); connect(m_ui.exportSalesJsonAction, &QAction::triggered, this, &MainWindow::onExportSalesJsonActionTriggered); connect(m_ui.importSalesJsonAction, &QAction::triggered, this, @@ -345,7 +339,8 @@ void MainWindow::onCancelArticleButtonClicked([[maybe_unused]] bool checked) m_ui.basketView->model()->removeRow(iter->row()); } - m_ui.basketSumLabel->setText(m_marketplace->getBasketSumAsString().c_str()); // Update basket sum + m_ui.basketSumLabel->setText( + m_marketplace->getBasketSumAsString().c_str()); // Update basket sum m_ui.sellerNoEdit->setFocus(); } @@ -428,7 +423,8 @@ void MainWindow::onCancelAllArticlesButtonClicked([[maybe_unused]] bool checked) dynamic_cast(m_ui.basketView->model())->cancelSale(); - m_ui.basketSumLabel->setText(m_marketplace->getBasketSumAsString().c_str()); // Update basket sum + m_ui.basketSumLabel->setText( + m_marketplace->getBasketSumAsString().c_str()); // Update basket sum m_ui.sellerNoEdit->setFocus(); } @@ -445,7 +441,7 @@ void MainWindow::onAbout() ">info@rustysoft.de>

"); } -void MainWindow::onImportSellerExcelActionTriggered() +void MainWindow::onImportSellerActionTriggered() { if (!m_marketplace->getSales().empty()) { QMessageBox(QMessageBox::Icon::Information, "Import nicht möglich", @@ -455,9 +451,9 @@ void MainWindow::onImportSellerExcelActionTriggered() return; } - auto filename = QFileDialog::getOpenFileName( - this, "Verkäufer importieren", QString(), - "Alle unterstützte Dateien (*.xlsx *.csv);;Excel Dateien (*.xlsx);;CSV Dateien (*.csv)"); + auto filename = + QFileDialog::getOpenFileName(this, "Verkäufer importieren", QString(), + "Alle unterstützte Dateien (*.csv);;CSV Dateien (*.csv)"); if (filename.isEmpty()) return; @@ -469,86 +465,19 @@ void MainWindow::onImportSellerExcelActionTriggered() #endif std::size_t numImported{}; - if (case_insensitive_match(filePath.extension().string(), std::string(".xlsx"))) { - try { - numImported = ExcelReader::readSellersFromFile(filePath, m_marketplace.get()); - } catch (const std::exception &e) { - QMessageBox(QMessageBox::Icon::Critical, "Fehler beim Importieren", - "Beim Import aus der Excel-Datei ist ein Fehler aufgetreten. " - "Sie könnten ggf. versuchen, die Daten aus einer .csv Datei zu imporieren.", - QMessageBox::StandardButton::Ok, this) - .exec(); - std::cerr << e.what() << std::endl; - return; - } - } else { - numImported = CsvReader::readSellersFromFile(filePath, m_marketplace.get()); - } + numImported = CsvReader::readSellersFromFile(filePath, m_marketplace.get()); updateStatLabel(); using namespace std::string_literals; std::ostringstream msg; - msg << "Aus der Excel/CSV-Datei wurden "s << std::to_string(numImported) + msg << "Aus der CSV-Datei wurden "s << std::to_string(numImported) << " Verkäufer importiert."; QMessageBox(QMessageBox::Icon::Information, "Verkäufer erfolgreich importiert", msg.str().c_str(), QMessageBox::StandardButton::Ok, this) .exec(); } -void MainWindow::onImportSellerJsonActionTriggered() -{ - if (!m_marketplace->getSales().empty()) { - QMessageBox(QMessageBox::Icon::Information, "Import nicht möglich", - "Der Import ist nicht möglich, da schon Verkäufe getätigt wurden.", - QMessageBox::StandardButton::Ok, this) - .exec(); - return; - } - - auto filename = QFileDialog::getOpenFileName(this, "Verkäufer importieren", QString(), - "JSON Dateien (*.json)"); - - if (filename.isEmpty()) - return; - -#if defined(_WIN64) || defined(_WIN32) - fs::path filePath(filename.toStdWString()); -#else - fs::path filePath(filename.toStdString()); -#endif - - std::size_t numImported{}; - numImported = JsonUtil::importSellers(filePath, m_marketplace.get()); - - updateStatLabel(); - - using namespace std::string_literals; - std::ostringstream msg; - msg << "Aus der JSON-Datei wurden "s << std::to_string(numImported) - << " Verkäufer importiert."; - QMessageBox(QMessageBox::Icon::Information, "Verkäufer erfolgreich importiert", - msg.str().c_str(), QMessageBox::StandardButton::Ok, this) - .exec(); -} - -void MainWindow::onExportSellerJsonActionTriggered() -{ - auto filename = QFileDialog::getSaveFileName( - this, "Verkäufer exportieren", QString("kima2_verkaeufer.json"), "JSON Dateien (*.json)"); - - if (filename.isEmpty()) - return; - -#if defined(_WIN64) || defined(_WIN32) - fs::path filePath(filename.toStdWString()); -#else - fs::path filePath(filename.toStdString()); -#endif - - JsonUtil::exportSellers(filePath, m_marketplace.get()); -} - void MainWindow::onExportSalesJsonActionTriggered() { QSettings settings; @@ -575,27 +504,30 @@ void MainWindow::onImportSalesJsonActionTriggered() { QSettings settings; - auto filename = QFileDialog::getOpenFileName(this, "Umsätze/Transaktionen importieren", + auto filenames = QFileDialog::getOpenFileNames(this, "Umsätze/Transaktionen importieren", QString(), "JSON Dateien (*.json)"); - if (filename.isEmpty()) + if (filenames.isEmpty()) return; + for(auto filename: filenames) { #if defined(_WIN64) || defined(_WIN32) - fs::path filePath(filename.toStdWString()); + fs::path filePath(filename.toStdWString()); #else - fs::path filePath(filename.toStdString()); + fs::path filePath(filename.toStdString()); #endif - delete m_ui.salesView->model(); - try { - JsonUtil::importSales(filePath, m_marketplace.get(), - settings.value("global/cashPointNo").toInt()); - } catch (std::runtime_error &err) { - QMessageBox(QMessageBox::Icon::Warning, "Import nicht möglich", err.what(), QMessageBox::Ok, - this) - .exec(); + delete m_ui.salesView->model(); + try { + JsonUtil::importSales(filePath, m_marketplace.get(), + settings.value("global/cashPointNo").toInt()); + } catch (std::runtime_error &err) { + QMessageBox(QMessageBox::Icon::Warning, "Import nicht möglich", err.what(), QMessageBox::Ok, + this) + .exec(); + } } + setSaleModel(); updateStatLabel(); } diff --git a/src/gui/mainwindow.h b/src/gui/mainwindow.h index 1fba01a..42ac25f 100644 --- a/src/gui/mainwindow.h +++ b/src/gui/mainwindow.h @@ -39,9 +39,7 @@ class MainWindow : public QMainWindow void checkSellerNo(bool ctrlPressed = false); void onPaidButtonTriggered(); void onGivenSpinBoxValueChanged(double value); - void onImportSellerExcelActionTriggered(); - void onImportSellerJsonActionTriggered(); - void onExportSellerJsonActionTriggered(); + void onImportSellerActionTriggered(); void onExportSalesJsonActionTriggered(); void onImportSalesJsonActionTriggered(); void setSaleModel(); diff --git a/src/gui/mainwindow.ui b/src/gui/mainwindow.ui index a77a297..49ba0fc 100644 --- a/src/gui/mainwindow.ui +++ b/src/gui/mainwindow.ui @@ -423,7 +423,7 @@ drucken
0 0 817 - 30 + 24 @@ -440,16 +440,8 @@ drucken &Verkäufer - - - Importieren - - - - - - + @@ -513,9 +505,9 @@ drucken Exportieren für andere Kasse (JSON) - + - Aus Excel/CSV-Datei (initial) + Aus CSV-Datei (initial) @@ -548,6 +540,11 @@ drucken Lizenz + + + Importieren (aus CSV-Datei) + + diff --git a/src/gui/meson.build b/src/gui/meson.build deleted file mode 100644 index 18bfcee..0000000 --- a/src/gui/meson.build +++ /dev/null @@ -1,23 +0,0 @@ -qt5 = import('qt5') -qt5_dep = dependency('qt5', modules: ['Core', 'Gui', 'PrintSupport', 'Network']) - -thread_dep = dependency('threads') - - -src = ['kima2.cpp', 'mainwindow.cpp', 'sellerdialog.cpp', 'sellermodel.cpp', - 'pricedialog.cpp', 'basketmodel.cpp', 'salemodel.cpp', 'reportdialog.cpp', - 'reportmodel.cpp', 'settingsdialog.cpp'] - -ui = ['mainwindow.ui', 'sellerdialog.ui', 'pricedialog.ui', 'reportdialog.ui', 'settingsdialog.ui'] - -processed = qt5.preprocess(moc_headers : ['basketmodel.h', 'mainwindow.h', 'pricedialog.h', - 'reportdialog.h', 'sellerdialog.h', 'settingsdialog.h', - 'sellermodel.h', 'salemodel.h'], - ui_files : ui, - qresources : '../../kima2.qrc', - dependencies: qt5_dep) - -kima2 = executable('kima2', sources : [src, processed], - dependencies : [qt5_dep, singleapp_dep, core_dep, printer_dep, thread_dep], - include_directories : [configuration_inc], - install : true) diff --git a/src/gui/reportdialog.cpp b/src/gui/reportdialog.cpp index 682c737..2c13baf 100644 --- a/src/gui/reportdialog.cpp +++ b/src/gui/reportdialog.cpp @@ -201,7 +201,7 @@ void ReportDialog::onPrintSellerReceiptButtonClicked() return; auto indexes = selModel->selectedRows(); - auto &seller = m_market->getSellers().at(indexes[0].row()); + std::ranges::sort(indexes); auto printerDevice = convertToPosPrinterDevice(posPrinterDevice.toStdString(), posPrinterEndpoint.toStdString()); @@ -214,10 +214,14 @@ void ReportDialog::onPrintSellerReceiptButtonClicked() printer = std::make_unique(); } - if (printer->isValid()) - printer->printSellerReceipt( - seller.get(), feeInPercent, maxFeeInEuro * 100, - settings.value("global/commune", "Dettingen").toString().toStdString()); + if (printer->isValid()) { + for (const auto &index : indexes) { + auto &seller = m_market->getSellers().at(index.row()); + printer->printSellerReceipt( + seller.get(), feeInPercent, maxFeeInEuro * 100, + settings.value("global/commune", "Dettingen").toString().toStdString()); + } + } } void ReportDialog::onReportViewSelectionChanged(const QItemSelection &selected, diff --git a/src/gui/reportdialog.ui b/src/gui/reportdialog.ui index d5d27b6..5ac21c8 100644 --- a/src/gui/reportdialog.ui +++ b/src/gui/reportdialog.ui @@ -19,7 +19,7 @@ - QAbstractItemView::SingleSelection + QAbstractItemView::ExtendedSelection QAbstractItemView::SelectRows diff --git a/src/meson.build b/src/meson.build deleted file mode 100644 index 439c81e..0000000 --- a/src/meson.build +++ /dev/null @@ -1,3 +0,0 @@ -subdir('core') -subdir('printer') -subdir('gui') diff --git a/src/printer/CMakeLists.txt b/src/printer/CMakeLists.txt index 9dfaa3e..6205d41 100644 --- a/src/printer/CMakeLists.txt +++ b/src/printer/CMakeLists.txt @@ -1,6 +1,6 @@ set(Boost_USE_STATIC_LIBS ON) -find_package(Boost 1.62 REQUIRED) +find_package(Boost 1.78 REQUIRED) if(WIN32) find_package(LIBUSB REQUIRED) @@ -9,15 +9,15 @@ else() pkg_check_modules(LibUSB REQUIRED libusb-1.0) endif() -set(PRINTER_SOURCES - posprinter.cpp - utils.cpp +add_library(printer STATIC) +target_sources(printer PRIVATE + posprinter.cpp + utils.cpp ) -add_library(printer STATIC ${PRINTER_SOURCES}) if(WIN32) target_link_libraries(printer core ${LIBUSB_1_LIBRARIES}) else() target_link_libraries(printer core ${LibUSB_LIBRARIES}) endif() -target_include_directories(printer PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/..) +target_include_directories(printer PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/.. ${Boost_INCLUDE_DIRS}) diff --git a/src/printer/meson.build b/src/printer/meson.build deleted file mode 100644 index 64f0602..0000000 --- a/src/printer/meson.build +++ /dev/null @@ -1,9 +0,0 @@ -libusb = dependency('libusb-1.0') - -src = ['posprinter.cpp', 'utils.cpp'] - -printer_inc = include_directories('..') - -printer_lib = static_library('printer', src, dependencies: [libusb, core_dep]) - -printer_dep = declare_dependency(link_with : printer_lib, include_directories : printer_inc) diff --git a/src/printer/posprinter.cpp b/src/printer/posprinter.cpp index 368d483..61926fc 100644 --- a/src/printer/posprinter.cpp +++ b/src/printer/posprinter.cpp @@ -19,6 +19,8 @@ const std::string PosPrinter::Command::RIGHT_ALIGN = {0x1b, 0x61, 0x02}; const std::string PosPrinter::Command::FONT_SIZE_BIG = {0x1b, 0x21, 0x10}; const std::string PosPrinter::Command::FONT_SIZE_NORMAL = {0x1b, 0x21, 0x00}; const std::string PosPrinter::Command::FEED = {0x1b, 0x64, 0x03}; +const std::string PosPrinter::Command::PARTIAL_CUT = {0x1b, 0x69}; +const std::string PosPrinter::Command::FULL_CUT = {0x1b, 0x6d}; PosPrinter::PosPrinter() : PosPrinter(PrinterDevice()) {} @@ -202,7 +204,7 @@ void PosPrinter::printSellerReceipt(Seller* seller, const int percent, const int << marketFeeAsString(seller->sumInCents(), percent, maxFeeInCent) << "\n"; commandStream << "\nAuszahlung............. " << paymentAsString(seller->sumInCents(), percent, maxFeeInCent) << "\n"; - commandStream << Command::FEED; + commandStream << Command::FEED << Command::FEED; write(commandStream.str()); } diff --git a/src/printer/posprinter.h b/src/printer/posprinter.h index 4ddd083..1cdbff6 100644 --- a/src/printer/posprinter.h +++ b/src/printer/posprinter.h @@ -49,6 +49,8 @@ class PosPrinter static const std::string FONT_SIZE_BIG; static const std::string FONT_SIZE_NORMAL; static const std::string FEED; + static const std::string PARTIAL_CUT; + static const std::string FULL_CUT; }; private: diff --git a/subprojects/CMakeLists.txt b/subprojects/CMakeLists.txt deleted file mode 100644 index 6ef42eb..0000000 --- a/subprojects/CMakeLists.txt +++ /dev/null @@ -1,5 +0,0 @@ -if(NOT KIMA2_USE_EXTERNAL_JSON) - set(JSON_BuildTests OFF CACHE INTERNAL "") - add_subdirectory(nlohmann_json EXCLUDE_FROM_ALL) -endif() -#add_subdirectory(csv-parser) diff --git a/subprojects/csv-parser b/subprojects/csv-parser deleted file mode 160000 index ea547fd..0000000 --- a/subprojects/csv-parser +++ /dev/null @@ -1 +0,0 @@ -Subproject commit ea547fdb16c7baf99bd9ced5febba52cc5da3ca3 diff --git a/subprojects/nlohmann_json b/subprojects/nlohmann_json deleted file mode 160000 index bc889af..0000000 --- a/subprojects/nlohmann_json +++ /dev/null @@ -1 +0,0 @@ -Subproject commit bc889afb4c5bf1c0d8ee29ef35eaaf4c8bef8a5d diff --git a/subprojects/singleapplication/meson.build b/subprojects/singleapplication/meson.build deleted file mode 100644 index 1b43463..0000000 --- a/subprojects/singleapplication/meson.build +++ /dev/null @@ -1,27 +0,0 @@ -project('singleapplication') - -qt5 = import('qt5') -dep_qt5 = dependency('qt5', modules: ['Core', 'Widgets', 'Network']) - -singleapp_inc = include_directories('singleapplication.git') - -singleapp_moc = qt5.preprocess( - moc_headers: ['singleapplication.git/singleapplication.h', 'singleapplication.git/singleapplication_p.h'], - moc_extra_arguments: ['-DQAPPLICATION_CLASS=QApplication'], - dependencies: dep_qt5 -) - -singleapp_lib = static_library('SingleApplication', - ['singleapplication.git/singleapplication.cpp', 'singleapplication.git/singleapplication_p.cpp', singleapp_moc], - include_directories: singleapp_inc, - cpp_args : '-DQAPPLICATION_CLASS=QApplication', - dependencies: dep_qt5 -) - -singleapp_dep = declare_dependency( - include_directories: singleapp_inc, - link_with: singleapp_lib -) - -# On windows, SingleApplication needs to be linked against advapi32. This is -# done by adding 'advapi32' to cpp_winlibs, where it should be by default. diff --git a/subprojects/singleapplication/singleapplication.git b/subprojects/singleapplication/singleapplication.git deleted file mode 160000 index a3ed916..0000000 --- a/subprojects/singleapplication/singleapplication.git +++ /dev/null @@ -1 +0,0 @@ -Subproject commit a3ed916f591c300e97b873fde36863fa37b49fa9