diff --git a/CMakeLists.txt b/CMakeLists.txt index 9262a816..cf74b6ac 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,252 +1,252 @@ if(POLICY CMP0048) cmake_policy(SET CMP0048 NEW) endif(POLICY CMP0048) project( kbibtex VERSION "0.9.50" ) cmake_minimum_required( VERSION 3.2.0 FATAL_ERROR ) set( QT_MIN_VERSION "5.6.0" ) set( - KF5_MIN_VERSION "5.26.0" + KF5_MIN_VERSION "5.51.0" ) find_package(ECM 5.19 REQUIRED NO_MODULE) add_definitions( -DTRANSLATION_DOMAIN="kbibtex" -DHAVE_QTWIDGETS -DHAVE_KF5 -DHAVE_ICU ) set( CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake/modules ${ECM_MODULE_PATH} ${ECM_KDE_MODULE_DIR} ${CMAKE_MODULE_PATH} ) include(KDEInstallDirs) include(KDECompilerSettings NO_POLICY_SCOPE) include(KDECMakeSettings) include(ECMInstallIcons) include(ECMSetupVersion) include(ECMAddAppIcon) include(GenerateExportHeader) find_package( Qt5 ${QT_MIN_VERSION} CONFIG REQUIRED Core Widgets Network XmlPatterns Concurrent ) if (MSVC) MESSAGE( STATUS "Disabling building tests when using Microsoft Visual Studio C++ compiler" ) # Note to any developer: Try to enable building tests and see which issues you may encounter. # Examples may include: (1) char* texts which exceed the size limit supported by MSVC which # is about 2^16 bytes and (2) characters in strings written in \uXXXX notation not supported # in 1252 encoding as assumed by MSVC for C++ source files. SET(BUILD_TESTING OFF) endif (MSVC) if( BUILD_TESTING ) add_definitions(-DBUILD_TESTING) find_package( Qt5Test ${QT_MIN_VERSION} CONFIG REQUIRED ) if (WRITE_RAWDATAFILE) add_definitions(-DWRITE_RAWDATAFILE) endif(WRITE_RAWDATAFILE) set( TESTSET_DIRECTORY "" CACHE PATH "Directory where the local checkout of Git repository 'kbibtex-testset' is located" ) endif( BUILD_TESTING ) find_package( Qt5WebEngineWidgets ${QT_MIN_VERSION} QUIET CONFIG ) find_package( Qt5WebKitWidgets ${QT_MIN_VERSION} QUIET CONFIG ) find_package( KF5 ${KF5_MIN_VERSION} REQUIRED I18n XmlGui KIO IconThemes ItemViews Completion Parts CoreAddons Service Wallet Crash ) find_package(KF5DocTools) find_package(KF5TextEditor NO_MODULE) ecm_setup_version( PROJECT VARIABLE_PREFIX KBIBTEX SOVERSION ${KBIBTEX_VERSION_MAJOR} VERSION_HEADER "${CMAKE_CURRENT_BINARY_DIR}/kbibtex-version.h" ) if((${KBIBTEX_VERSION_PATCH} GREATER 50) OR (${KBIBTEX_VERSION_PATCH} EQUAL 50)) # If the version number indicates a pre-release version such as # '0.7.90', i.e. a beta version for the major release 0.8, # increment release version from 0.7 to 0.8 math(EXPR KBIBTEX_RELEASE_VERSION_MINOR "${KBIBTEX_VERSION_MINOR} + 1") set( KBIBTEX_RELEASE_VERSION ${KBIBTEX_VERSION_MAJOR}.${KBIBTEX_RELEASE_VERSION_MINOR} ) else((${KBIBTEX_VERSION_PATCH} GREATER 50) OR (${KBIBTEX_VERSION_PATCH} EQUAL 50)) set( KBIBTEX_RELEASE_VERSION ${KBIBTEX_VERSION_MAJOR}.${KBIBTEX_VERSION_MINOR} ) endif((${KBIBTEX_VERSION_PATCH} GREATER 50) OR (${KBIBTEX_VERSION_PATCH} EQUAL 50)) option( UNITY_BUILD "Compile multiple C++ files in one big, merged file (\"Unity build\")\nSee also http://t-fischer.dreamwidth.org/3054.html" ) if(UNITY_BUILD) message(STATUS "Unity build enabled") else(UNITY_BUILD) message(STATUS "Unity build disabled (default), use option UNITY_BUILD to enable it") endif(UNITY_BUILD) # # FIXME may have to be cleaned up a little bit # # Contributed by Jeremy Cribb # if( # APPLE # ) # find_library( # SYS_CONFIG_LIBRARY # SystemConfiguration # ) # mark_as_advanced( # SYS_CONFIG_LIBRARY # ) # # SET(TARGET_EXTERNAL_LIBRARIES iconv ${SYS_CONFIG_LIBRARY}) # set( # ICONV_INCLUDE_DIR # "/opt/local/include" # ) # set( # ICONV_LIBRARIES # "/opt/local/lib/libiconv.dylib" # ) # set( # LIBXSLT_LIBRARIES # "/opt/local/lib/libxslt.dylib" # ) # endif( # APPLE # ) find_package( Poppler REQUIRED COMPONENTS Qt5 ) find_package( ICU REQUIRED COMPONENTS uc i18n ) if( ICU_FOUND ) message( STATUS "Found ICU " ${ICU_VERSION} ) include_directories(${ICU_INCLUDE_DIRS}) endif( ICU_FOUND ) find_package( Qca-qt5 "2.1.0" ) find_package( QtOAuth ) if (Qca-qt5_FOUND AND QTOAUTH_FOUND) message(STATUS "Found Qca-qt5 " ${Qca-qt5_VERSION} " and QtOAuth") add_definitions(-DHAVE_ZOTERO=1) set(BUILD_ZOTERO ON) else(Qca-qt5_FOUND AND QTOAUTH_FOUND) message(STATUS "No Qca-qt5 or no QtOAuth, disabling certain functionality") set(BUILD_ZOTERO OFF) endif(Qca-qt5_FOUND AND QTOAUTH_FOUND) # Make an educated guess where to find headers for QCA-Qt5 # TODO isn't there an "official" way to determine this directory? find_path( QTCRYPTO_INCLUDE_DIR qca.h HINTS ENV ${Qt5Core_INCLUDE_DIRS} ENV ${QT_INCLUDES} PATH_SUFFIXES QtCrypto ) add_subdirectory( config ) add_subdirectory( src ) add_subdirectory( xslt ) add_subdirectory( mime ) if(KF5DocTools_FOUND) add_subdirectory(doc) endif() # macro_optional_add_subdirectory( # po # ) feature_summary(WHAT ALL INCLUDE_QUIET_PACKAGES FATAL_ON_MISSING_REQUIRED_PACKAGES) diff --git a/src/config/bibtexentries.cpp b/src/config/bibtexentries.cpp index 3f401576..b639a911 100644 --- a/src/config/bibtexentries.cpp +++ b/src/config/bibtexentries.cpp @@ -1,181 +1,181 @@ /*************************************************************************** - * Copyright (C) 2004-2018 by Thomas Fischer * + * Copyright (C) 2004-2019 by Thomas Fischer * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, see . * ***************************************************************************/ #include "bibtexentries.h" #include #ifdef HAVE_KF5 #include #include #include #else // HAVE_KF5 #include #define i18n(text) QObject::tr(text) #endif // HAVE_KF5 #include "preferences.h" #include "logging_config.h" bool operator==(const EntryDescription &a, const EntryDescription &b) { return a.upperCamelCase == b.upperCamelCase; } uint qHash(const EntryDescription &a) { return qHash(a.upperCamelCase); } class BibTeXEntries::BibTeXEntriesPrivate { public: static const QVector entryDescriptionsBibTeX; #ifdef HAVE_KF5 static const QVector entryDescriptionsBibLaTeX; #endif // HAVE_KF5 }; BibTeXEntries::BibTeXEntries(const QVector &other) : QVector(other), d(new BibTeXEntriesPrivate()) { /// nothing } BibTeXEntries::~BibTeXEntries() { delete d; } const BibTeXEntries &BibTeXEntries::instance() { static const QVector entryDescriptionsBibTeX { EntryDescription {QStringLiteral("Article"), QString(), i18n("Journal Article"), {QStringLiteral("author"), QStringLiteral("title"), QStringLiteral("journal"), QStringLiteral("year")}, {QStringLiteral("volume"), QStringLiteral("number"), QStringLiteral("pages"), QStringLiteral("month"), QStringLiteral("note")}}, EntryDescription {QStringLiteral("InProceedings"), i18n("Conference"), QStringLiteral("Publication in Conference Proceedings"), {QStringLiteral("author"), QStringLiteral("title"), QStringLiteral("booktitle"), QStringLiteral("year")}, {QStringLiteral("editor"), QStringLiteral("volume^number"), QStringLiteral("series"), QStringLiteral("pages"), QStringLiteral("address"), QStringLiteral("month"), QStringLiteral("organization"), QStringLiteral("publisher"), QStringLiteral("note")}}, EntryDescription {QStringLiteral("Proceedings"), QString(), i18n("Conference or Workshop Proceedings"), {QStringLiteral("title"), QStringLiteral("year")}, {QStringLiteral("editor"), QStringLiteral("volume^number"), QStringLiteral("series"), QStringLiteral("address"), QStringLiteral("month"), QStringLiteral("organization"), QStringLiteral("publisher"), QStringLiteral("note")}}, EntryDescription {QStringLiteral("TechReport"), QString(), i18n("Technical Report"), {QStringLiteral("author"), QStringLiteral("title"), QStringLiteral("institution"), QStringLiteral("year")}, {QStringLiteral("type"), QStringLiteral("number"), QStringLiteral("address"), QStringLiteral("month"), QStringLiteral("note")}}, EntryDescription {QStringLiteral("Misc"), QString(), i18n("Miscellaneous"), {}, {QStringLiteral("author"), QStringLiteral("title"), QStringLiteral("howpublished"), QStringLiteral("month"), QStringLiteral("year"), QStringLiteral("note")}}, EntryDescription {QStringLiteral("Book"), QString(), i18n("Book"), {QStringLiteral("author^editor"), QStringLiteral("title"), QStringLiteral("publisher"), QStringLiteral("year")}, {QStringLiteral("volume^number"), QStringLiteral("series"), QStringLiteral("address"), QStringLiteral("edition"), QStringLiteral("month"), QStringLiteral("note")}}, EntryDescription {QStringLiteral("InBook"), QString(), i18n("Part of a Book"), {QStringLiteral("author^editor"), QStringLiteral("title"), QStringLiteral("chapter|pages"), QStringLiteral("publisher"), QStringLiteral("year")}, {QStringLiteral("volume^number"), QStringLiteral("series"), QStringLiteral("type"), QStringLiteral("address"), QStringLiteral("edition"), QStringLiteral("month"), QStringLiteral("note")}}, EntryDescription {QStringLiteral("InCollection"), QString(), i18n("Part of a Book with own Title"), {QStringLiteral("author"), QStringLiteral("title"), QStringLiteral("booktitle"), QStringLiteral("publisher"), QStringLiteral("year")}, {QStringLiteral("editor"), QStringLiteral("volume^number"), QStringLiteral("series"), QStringLiteral("type"), QStringLiteral("chapter"), QStringLiteral("pages"), QStringLiteral("address"), QStringLiteral("edition"), QStringLiteral("month"), QStringLiteral("note")}}, EntryDescription {QStringLiteral("PhDThesis"), QString(), i18n("PhD Thesis"), {QStringLiteral("author"), QStringLiteral("title"), QStringLiteral("school"), QStringLiteral("year")}, {QStringLiteral("type"), QStringLiteral("address"), QStringLiteral("month"), QStringLiteral("note")}}, EntryDescription {QStringLiteral("MastersThesis"), QString(), i18n("Master's Thesis"), {QStringLiteral("author"), QStringLiteral("title"), QStringLiteral("school"), QStringLiteral("year")}, {QStringLiteral("type"), QStringLiteral("address"), QStringLiteral("month"), QStringLiteral("note")}}, EntryDescription {QStringLiteral("Unpublished"), QString(), i18n("Unpublished Material"), {QStringLiteral("author"), QStringLiteral("title"), QStringLiteral("note")}, {QStringLiteral("month"), QStringLiteral("year")}}, EntryDescription {QStringLiteral("Manual"), QString(), i18n("Manual"), {QStringLiteral("title")}, {QStringLiteral("author"), QStringLiteral("organization"), QStringLiteral("address"), QStringLiteral("edition"), QStringLiteral("month"), QStringLiteral("year"), QStringLiteral("note")}}, EntryDescription {QStringLiteral("Booklet"), QString(), i18n("Booklet"), {QStringLiteral("title")}, {QStringLiteral("author"), QStringLiteral("howpublished"), QStringLiteral("address"), QStringLiteral("month"), QStringLiteral("year"), QStringLiteral("note")}} }; #ifdef HAVE_KF5 static const QVector entryDescriptionsBibLaTeX { EntryDescription {QStringLiteral("Article"), QString(), i18n("Journal Article"), {QStringLiteral("author"), QStringLiteral("title"), QStringLiteral("journaltitle"), QStringLiteral("year^date")}, {QStringLiteral("translator"), QStringLiteral("annotator"), QStringLiteral("commentator"), QStringLiteral("subtitle"), QStringLiteral("titleaddon"), QStringLiteral("editor"), QStringLiteral("editora"), QStringLiteral("editorb"), QStringLiteral("editorc"), QStringLiteral("journalsubtitle"), QStringLiteral("issuetitle"), QStringLiteral("issuesubtitle"), QStringLiteral("language"), QStringLiteral("origlanguage"), QStringLiteral("series"), QStringLiteral("volume"), QStringLiteral("number"), QStringLiteral("eid"), QStringLiteral("issue"), QStringLiteral("month"), QStringLiteral("pages"), QStringLiteral("version"), QStringLiteral("note"), QStringLiteral("issn"), QStringLiteral("addendum"), QStringLiteral("pubstate"), QStringLiteral("doi"), QStringLiteral("eprint"), QStringLiteral("eprintclass"), QStringLiteral("eprinttype"), QStringLiteral("url"), QStringLiteral("urldate")}}, EntryDescription {QStringLiteral("Book"), QString(), i18n("Book"), {QStringLiteral("author"), QStringLiteral("title"), QStringLiteral("year^date")}, {QStringLiteral("editor"), QStringLiteral("editora"), QStringLiteral("editorb"), QStringLiteral("editorc"), QStringLiteral("translator"), QStringLiteral("annotator"), QStringLiteral("commentator"), QStringLiteral("introduction"), QStringLiteral("foreword"), QStringLiteral("afterword"), QStringLiteral("subtitle"), QStringLiteral("titleaddon"), QStringLiteral("maintitle"), QStringLiteral("mainsubtitle"), QStringLiteral("maintitleaddon"), QStringLiteral("language"), QStringLiteral("origlanguage"), QStringLiteral("volume"), QStringLiteral("part"), QStringLiteral("edition"), QStringLiteral("volumes"), QStringLiteral("series"), QStringLiteral("number"), QStringLiteral("note"), QStringLiteral("publisher"), QStringLiteral("location"), QStringLiteral("isbn"), QStringLiteral("chapter"), QStringLiteral("pages"), QStringLiteral("pagetotal"), QStringLiteral("addendum"), QStringLiteral("pubstate"), QStringLiteral("doi"), QStringLiteral("eprint"), QStringLiteral("eprintclass"), QStringLiteral("eprinttype"), QStringLiteral("url"), QStringLiteral("urldate")}}, EntryDescription {QStringLiteral("MVBook"), QString(), i18n("Multi-volume Book"), {QStringLiteral("author"), QStringLiteral("title"), QStringLiteral("year^date")}, {QStringLiteral("editor"), QStringLiteral("editora"), QStringLiteral("editorb"), QStringLiteral("editorc"), QStringLiteral("translator"), QStringLiteral("annotator"), QStringLiteral("commentator"), QStringLiteral("introduction"), QStringLiteral("foreword"), QStringLiteral("afterword"), QStringLiteral("subtitle"), QStringLiteral("titleaddon"), QStringLiteral("language"), QStringLiteral("origlanguage"), QStringLiteral("edition"), QStringLiteral("volumes"), QStringLiteral("series"), QStringLiteral("number"), QStringLiteral("note"), QStringLiteral("publisher"), QStringLiteral("location"), QStringLiteral("isbn"), QStringLiteral("pagetotal"), QStringLiteral("addendum"), QStringLiteral("pubstate"), QStringLiteral("doi"), QStringLiteral("eprint"), QStringLiteral("eprintclass"), QStringLiteral("eprinttype"), QStringLiteral("url"), QStringLiteral("urldate")}}, EntryDescription {QStringLiteral("InBook"), QString(), i18n("Part of a Book"), {QStringLiteral("author"), QStringLiteral("title"), QStringLiteral("booktitle"), QStringLiteral("year^date")}, {QStringLiteral("bookauthor"), QStringLiteral("editor"), QStringLiteral("editora"), QStringLiteral("editorb"), QStringLiteral("editorc"), QStringLiteral("translator"), QStringLiteral("annotator"), QStringLiteral("commentator"), QStringLiteral("introduction"), QStringLiteral("foreword"), QStringLiteral("afterword"), QStringLiteral("subtitle"), QStringLiteral("titleaddon"), QStringLiteral("maintitle"), QStringLiteral("mainsubtitle"), QStringLiteral("maintitleaddon"), QStringLiteral("booksubtitle"), QStringLiteral("booktitleaddon"), QStringLiteral("language"), QStringLiteral("origlanguage"), QStringLiteral("volume"), QStringLiteral("part"), QStringLiteral("edition"), QStringLiteral("volumes"), QStringLiteral("series"), QStringLiteral("number"), QStringLiteral("note"), QStringLiteral("publisher"), QStringLiteral("location"), QStringLiteral("isbn"), QStringLiteral("chapter"), QStringLiteral("pages"), QStringLiteral("addendum"), QStringLiteral("pubstate"), QStringLiteral("doi"), QStringLiteral("eprint"), QStringLiteral("eprintclass"), QStringLiteral("eprinttype"), QStringLiteral("url"), QStringLiteral("urldate")}}, EntryDescription {QStringLiteral("BookInBook"), QString(), i18n("Former Monograph as Part of a Book"), {QStringLiteral("author"), QStringLiteral("title"), QStringLiteral("booktitle"), QStringLiteral("year^date")}, {QStringLiteral("bookauthor"), QStringLiteral("editor"), QStringLiteral("editora"), QStringLiteral("editorb"), QStringLiteral("editorc"), QStringLiteral("translator"), QStringLiteral("annotator"), QStringLiteral("commentator"), QStringLiteral("introduction"), QStringLiteral("foreword"), QStringLiteral("afterword"), QStringLiteral("subtitle"), QStringLiteral("titleaddon"), QStringLiteral("maintitle"), QStringLiteral("mainsubtitle"), QStringLiteral("maintitleaddon"), QStringLiteral("booksubtitle"), QStringLiteral("booktitleaddon"), QStringLiteral("language"), QStringLiteral("origlanguage"), QStringLiteral("volume"), QStringLiteral("part"), QStringLiteral("edition"), QStringLiteral("volumes"), QStringLiteral("series"), QStringLiteral("number"), QStringLiteral("note"), QStringLiteral("publisher"), QStringLiteral("location"), QStringLiteral("isbn"), QStringLiteral("chapter"), QStringLiteral("pages"), QStringLiteral("addendum"), QStringLiteral("pubstate"), QStringLiteral("doi"), QStringLiteral("eprint"), QStringLiteral("eprintclass"), QStringLiteral("eprinttype"), QStringLiteral("url"), QStringLiteral("urldate")}}, EntryDescription {QStringLiteral("SuppBook"), QString(), i18n("Supplemental Material in a Book"), {QStringLiteral("author"), QStringLiteral("title"), QStringLiteral("booktitle"), QStringLiteral("year^date")}, {QStringLiteral("bookauthor"), QStringLiteral("editor"), QStringLiteral("editora"), QStringLiteral("editorb"), QStringLiteral("editorc"), QStringLiteral("translator"), QStringLiteral("annotator"), QStringLiteral("commentator"), QStringLiteral("introduction"), QStringLiteral("foreword"), QStringLiteral("afterword"), QStringLiteral("subtitle"), QStringLiteral("titleaddon"), QStringLiteral("maintitle"), QStringLiteral("mainsubtitle"), QStringLiteral("maintitleaddon"), QStringLiteral("booksubtitle"), QStringLiteral("booktitleaddon"), QStringLiteral("language"), QStringLiteral("origlanguage"), QStringLiteral("volume"), QStringLiteral("part"), QStringLiteral("edition"), QStringLiteral("volumes"), QStringLiteral("series"), QStringLiteral("number"), QStringLiteral("note"), QStringLiteral("publisher"), QStringLiteral("location"), QStringLiteral("isbn"), QStringLiteral("chapter"), QStringLiteral("pages"), QStringLiteral("addendum"), QStringLiteral("pubstate"), QStringLiteral("doi"), QStringLiteral("eprint"), QStringLiteral("eprintclass"), QStringLiteral("eprinttype"), QStringLiteral("url"), QStringLiteral("urldate")}}, EntryDescription {QStringLiteral("Booklet"), QString(), i18n("Booklet"), {QStringLiteral("author^editor"), QStringLiteral("title"), QStringLiteral("year^date")}, {QStringLiteral("subtitle"), QStringLiteral("titleaddon"), QStringLiteral("language"), QStringLiteral("howpublished"), QStringLiteral("type"), QStringLiteral("note"), QStringLiteral("location"), QStringLiteral("chapter"), QStringLiteral("pages"), QStringLiteral("pagetotal"), QStringLiteral("addendum"), QStringLiteral("pubstate"), QStringLiteral("doi"), QStringLiteral("eprint"), QStringLiteral("eprintclass"), QStringLiteral("eprinttype"), QStringLiteral("url"), QStringLiteral("urldate")}}, EntryDescription {QStringLiteral("Collection"), QString(), i18n("Single-volume Collection"), {QStringLiteral("editor"), QStringLiteral("title"), QStringLiteral("year^date")}, {QStringLiteral("editora"), QStringLiteral("editorb"), QStringLiteral("editorc"), QStringLiteral("translator"), QStringLiteral("annotator"), QStringLiteral("commentator"), QStringLiteral("introduction"), QStringLiteral("foreword"), QStringLiteral("afterword"), QStringLiteral("subtitle"), QStringLiteral("titleaddon"), QStringLiteral("maintitle"), QStringLiteral("mainsubtitle"), QStringLiteral("maintitleaddon"), QStringLiteral("language"), QStringLiteral("origlanguage"), QStringLiteral("volume"), QStringLiteral("part"), QStringLiteral("edition"), QStringLiteral("volumes"), QStringLiteral("series"), QStringLiteral("number"), QStringLiteral("note"), QStringLiteral("publisher"), QStringLiteral("location"), QStringLiteral("isbn"), QStringLiteral("chapter"), QStringLiteral("pages"), QStringLiteral("pagetotal"), QStringLiteral("addendum"), QStringLiteral("pubstate"), QStringLiteral("doi"), QStringLiteral("eprint"), QStringLiteral("eprintclass"), QStringLiteral("eprinttype"), QStringLiteral("url"), QStringLiteral("urldate")}}, EntryDescription {QStringLiteral("MVCollection"), QString(), i18n("Multi-volume Collection"), {QStringLiteral("editor"), QStringLiteral("title"), QStringLiteral("year^date")}, {QStringLiteral("editora"), QStringLiteral("editorb"), QStringLiteral("editorc"), QStringLiteral("translator"), QStringLiteral("annotator"), QStringLiteral("commentator"), QStringLiteral("introduction"), QStringLiteral("foreword"), QStringLiteral("afterword"), QStringLiteral("subtitle"), QStringLiteral("titleaddon"), QStringLiteral("language"), QStringLiteral("origlanguage"), QStringLiteral("edition"), QStringLiteral("volumes"), QStringLiteral("series"), QStringLiteral("number"), QStringLiteral("note"), QStringLiteral("publisher"), QStringLiteral("location"), QStringLiteral("isbn"), QStringLiteral("pagetotal"), QStringLiteral("addendum"), QStringLiteral("pubstate"), QStringLiteral("doi"), QStringLiteral("eprint"), QStringLiteral("eprintclass"), QStringLiteral("eprinttype"), QStringLiteral("url"), QStringLiteral("urldate")}}, EntryDescription {QStringLiteral("InCollection"), QString(), i18n("Part of a Book with own Title"), {QStringLiteral("author"), QStringLiteral("editor"), QStringLiteral("title"), QStringLiteral("booktitle"), QStringLiteral("year^date")}, {QStringLiteral("editora"), QStringLiteral("editorb"), QStringLiteral("editorc"), QStringLiteral("translator"), QStringLiteral("annotator"), QStringLiteral("commentator"), QStringLiteral("introduction"), QStringLiteral("foreword"), QStringLiteral("afterword"), QStringLiteral("subtitle"), QStringLiteral("titleaddon"), QStringLiteral("maintitle"), QStringLiteral("mainsubtitle"), QStringLiteral("maintitleaddon"), QStringLiteral("booksubtitle"), QStringLiteral("booktitleaddon"), QStringLiteral("language"), QStringLiteral("origlanguage"), QStringLiteral("volume"), QStringLiteral("part"), QStringLiteral("edition"), QStringLiteral("volumes"), QStringLiteral("series"), QStringLiteral("number"), QStringLiteral("note"), QStringLiteral("publisher"), QStringLiteral("location"), QStringLiteral("isbn"), QStringLiteral("chapter"), QStringLiteral("pages"), QStringLiteral("addendum"), QStringLiteral("pubstate"), QStringLiteral("doi"), QStringLiteral("eprint"), QStringLiteral("eprintclass"), QStringLiteral("eprinttype"), QStringLiteral("url"), QStringLiteral("urldate")}}, EntryDescription {QStringLiteral("SuppCollection"), QString(), i18n("Supplemental Material in a Collection"), {QStringLiteral("author"), QStringLiteral("editor"), QStringLiteral("title"), QStringLiteral("booktitle"), QStringLiteral("year^date")}, {QStringLiteral("editora"), QStringLiteral("editorb"), QStringLiteral("editorc"), QStringLiteral("translator"), QStringLiteral("annotator"), QStringLiteral("commentator"), QStringLiteral("introduction"), QStringLiteral("foreword"), QStringLiteral("afterword"), QStringLiteral("subtitle"), QStringLiteral("titleaddon"), QStringLiteral("maintitle"), QStringLiteral("mainsubtitle"), QStringLiteral("maintitleaddon"), QStringLiteral("booksubtitle"), QStringLiteral("booktitleaddon"), QStringLiteral("language"), QStringLiteral("origlanguage"), QStringLiteral("volume"), QStringLiteral("part"), QStringLiteral("edition"), QStringLiteral("volumes"), QStringLiteral("series"), QStringLiteral("number"), QStringLiteral("note"), QStringLiteral("publisher"), QStringLiteral("location"), QStringLiteral("isbn"), QStringLiteral("chapter"), QStringLiteral("pages"), QStringLiteral("addendum"), QStringLiteral("pubstate"), QStringLiteral("doi"), QStringLiteral("eprint"), QStringLiteral("eprintclass"), QStringLiteral("eprinttype"), QStringLiteral("url"), QStringLiteral("urldate")}}, EntryDescription {QStringLiteral("Manual"), QString(), i18n("Manual"), {QStringLiteral("author^editor"), QStringLiteral("title"), QStringLiteral("year^date")}, {QStringLiteral("subtitle"), QStringLiteral("titleaddon"), QStringLiteral("language"), QStringLiteral("edition"), QStringLiteral("type"), QStringLiteral("series"), QStringLiteral("number"), QStringLiteral("version"), QStringLiteral("note"), QStringLiteral("organization"), QStringLiteral("publisher"), QStringLiteral("location"), QStringLiteral("isbn"), QStringLiteral("chapter"), QStringLiteral("pages"), QStringLiteral("pagetotal"), QStringLiteral("addendum"), QStringLiteral("pubstate"), QStringLiteral("doi"), QStringLiteral("eprint"), QStringLiteral("eprintclass"), QStringLiteral("eprinttype"), QStringLiteral("url"), QStringLiteral("urldate")}}, EntryDescription {QStringLiteral("Misc"), QString(), i18n("Miscellaneous"), {QStringLiteral("author^editor"), QStringLiteral("title"), QStringLiteral("year^date")}, {QStringLiteral("subtitle"), QStringLiteral("titleaddon"), QStringLiteral("language"), QStringLiteral("howpublished"), QStringLiteral("type"), QStringLiteral("version"), QStringLiteral("note"), QStringLiteral("organization"), QStringLiteral("location"), QStringLiteral("date"), QStringLiteral("month"), QStringLiteral("year"), QStringLiteral("addendum"), QStringLiteral("pubstate"), QStringLiteral("doi"), QStringLiteral("eprint"), QStringLiteral("eprintclass"), QStringLiteral("eprinttype"), QStringLiteral("url"), QStringLiteral("urldate")}}, EntryDescription {QStringLiteral("Online"), QStringLiteral("Electronic"), i18n("Online Resource"), {QStringLiteral("author^editor"), QStringLiteral("title"), QStringLiteral("year^date"), QStringLiteral("url")}, {QStringLiteral("subtitle"), QStringLiteral("titleaddon"), QStringLiteral("language"), QStringLiteral("version"), QStringLiteral("note"), QStringLiteral("organization"), QStringLiteral("date"), QStringLiteral("month"), QStringLiteral("year"), QStringLiteral("addendum"), QStringLiteral("pubstate"), QStringLiteral("urldate")}}, EntryDescription {QStringLiteral("Patent"), QString(), i18n("Patent"), {QStringLiteral("author"), QStringLiteral("title"), QStringLiteral("number"), QStringLiteral("year^date")}, {QStringLiteral("holder"), QStringLiteral("subtitle"), QStringLiteral("titleaddon"), QStringLiteral("type"), QStringLiteral("version"), QStringLiteral("location"), QStringLiteral("note"), QStringLiteral("date"), QStringLiteral("month"), QStringLiteral("year"), QStringLiteral("addendum"), QStringLiteral("pubstate"), QStringLiteral("doi"), QStringLiteral("eprint"), QStringLiteral("eprintclass"), QStringLiteral("eprinttype"), QStringLiteral("url"), QStringLiteral("urldate")}}, EntryDescription {QStringLiteral("Periodical"), QString(), i18n("Periodical (Complete Issue)"), {QStringLiteral("editor"), QStringLiteral("title"), QStringLiteral("year^date")}, {QStringLiteral("editora"), QStringLiteral("editorb"), QStringLiteral("editorc"), QStringLiteral("subtitle"), QStringLiteral("issuetitle"), QStringLiteral("issuesubtitle"), QStringLiteral("language"), QStringLiteral("series"), QStringLiteral("volume"), QStringLiteral("number"), QStringLiteral("issue"), QStringLiteral("date"), QStringLiteral("month"), QStringLiteral("year"), QStringLiteral("note"), QStringLiteral("issn"), QStringLiteral("addendum"), QStringLiteral("pubstate"), QStringLiteral("doi"), QStringLiteral("eprint"), QStringLiteral("eprintclass"), QStringLiteral("eprinttype"), QStringLiteral("url"), QStringLiteral("urldate")}}, EntryDescription {QStringLiteral("SuppPeriodical"), QString(), i18n("Supplemental Material in a Periodical"), {QStringLiteral("author"), QStringLiteral("title"), QStringLiteral("journaltitle"), QStringLiteral("year^date")}, {QStringLiteral("translator"), QStringLiteral("annotator"), QStringLiteral("commentator"), QStringLiteral("subtitle"), QStringLiteral("titleaddon"), QStringLiteral("editor"), QStringLiteral("editora"), QStringLiteral("editorb"), QStringLiteral("editorc"), QStringLiteral("journalsubtitle"), QStringLiteral("issuetitle"), QStringLiteral("issuesubtitle"), QStringLiteral("language"), QStringLiteral("origlanguage"), QStringLiteral("series"), QStringLiteral("volume"), QStringLiteral("number"), QStringLiteral("eid"), QStringLiteral("issue"), QStringLiteral("month"), QStringLiteral("pages"), QStringLiteral("version"), QStringLiteral("note"), QStringLiteral("issn"), QStringLiteral("addendum"), QStringLiteral("pubstate"), QStringLiteral("doi"), QStringLiteral("eprint"), QStringLiteral("eprintclass"), QStringLiteral("eprinttype"), QStringLiteral("url"), QStringLiteral("urldate")}}, EntryDescription {QStringLiteral("Proceedings"), QString(), i18n("Conference or Workshop Proceedings"), {QStringLiteral("editor"), QStringLiteral("title"), QStringLiteral("year^date")}, {QStringLiteral("subtitle"), QStringLiteral("titleaddon"), QStringLiteral("maintitle"), QStringLiteral("mainsubtitle"), QStringLiteral("maintitleaddon"), QStringLiteral("eventtitle"), QStringLiteral("eventdate"), QStringLiteral("venue"), QStringLiteral("language"), QStringLiteral("volume"), QStringLiteral("part"), QStringLiteral("volumes"), QStringLiteral("series"), QStringLiteral("number"), QStringLiteral("note"), QStringLiteral("organization"), QStringLiteral("publisher"), QStringLiteral("location"), QStringLiteral("month"), QStringLiteral("isbn"), QStringLiteral("chapter"), QStringLiteral("pages"), QStringLiteral("pagetotal"), QStringLiteral("addendum"), QStringLiteral("pubstate"), QStringLiteral("doi"), QStringLiteral("eprint"), QStringLiteral("eprintclass"), QStringLiteral("eprinttype"), QStringLiteral("url"), QStringLiteral("urldate")}}, EntryDescription {QStringLiteral("MVProceedings"), QString(), i18n("Multi-volume Proceedings"), {QStringLiteral("editor"), QStringLiteral("title"), QStringLiteral("year^date")}, {QStringLiteral("subtitle"), QStringLiteral("titleaddon"), QStringLiteral("eventtitle"), QStringLiteral("eventdate"), QStringLiteral("venue"), QStringLiteral("language"), QStringLiteral("volumes"), QStringLiteral("series"), QStringLiteral("number"), QStringLiteral("note"), QStringLiteral("organization"), QStringLiteral("publisher"), QStringLiteral("location"), QStringLiteral("month"), QStringLiteral("isbn"), QStringLiteral("pagetotal"), QStringLiteral("addendum"), QStringLiteral("pubstate"), QStringLiteral("doi"), QStringLiteral("eprint"), QStringLiteral("eprintclass"), QStringLiteral("eprinttype"), QStringLiteral("url"), QStringLiteral("urldate")}}, EntryDescription {QStringLiteral("InProceedings"), QStringLiteral("Conference"), i18n("Publication in Conference Proceedings"), {QStringLiteral(" author"), QStringLiteral("editor"), QStringLiteral("title"), QStringLiteral("booktitle"), QStringLiteral("year^date")}, {QStringLiteral("subtitle"), QStringLiteral("titleaddon"), QStringLiteral("maintitle"), QStringLiteral("mainsubtitle"), QStringLiteral("maintitleaddon"), QStringLiteral("booksubtitle"), QStringLiteral("booktitleaddon"), QStringLiteral("eventtitle"), QStringLiteral("eventdate"), QStringLiteral("venue"), QStringLiteral("language"), QStringLiteral("volume"), QStringLiteral("part"), QStringLiteral("volumes"), QStringLiteral("series"), QStringLiteral("number"), QStringLiteral("note"), QStringLiteral("organization"), QStringLiteral("publisher"), QStringLiteral("location"), QStringLiteral("month"), QStringLiteral("isbn"), QStringLiteral("chapter"), QStringLiteral("pages"), QStringLiteral("addendum"), QStringLiteral("pubstate"), QStringLiteral("doi"), QStringLiteral("eprint"), QStringLiteral("eprintclass"), QStringLiteral("eprinttype"), QStringLiteral("url"), QStringLiteral("urldate")}}, EntryDescription {QStringLiteral("Reference"), QString(), i18n("Single-volume Reference (e.g. Encyclopedia)"), {QStringLiteral("editor"), QStringLiteral("title"), QStringLiteral("year^date")}, {QStringLiteral("editora"), QStringLiteral("editorb"), QStringLiteral("editorc"), QStringLiteral("translator"), QStringLiteral("annotator"), QStringLiteral("commentator"), QStringLiteral("introduction"), QStringLiteral("foreword"), QStringLiteral("afterword"), QStringLiteral("subtitle"), QStringLiteral("titleaddon"), QStringLiteral("maintitle"), QStringLiteral("mainsubtitle"), QStringLiteral("maintitleaddon"), QStringLiteral("language"), QStringLiteral("origlanguage"), QStringLiteral("volume"), QStringLiteral("part"), QStringLiteral("edition"), QStringLiteral("volumes"), QStringLiteral("series"), QStringLiteral("number"), QStringLiteral("note"), QStringLiteral("publisher"), QStringLiteral("location"), QStringLiteral("isbn"), QStringLiteral("chapter"), QStringLiteral("pages"), QStringLiteral("pagetotal"), QStringLiteral("addendum"), QStringLiteral("pubstate"), QStringLiteral("doi"), QStringLiteral("eprint"), QStringLiteral("eprintclass"), QStringLiteral("eprinttype"), QStringLiteral("url"), QStringLiteral("urldate")}}, EntryDescription {QStringLiteral("MVReference"), QString(), i18n("Multi-volume Reference (e.g. Encyclopedia)"), {QStringLiteral("editor"), QStringLiteral("title"), QStringLiteral("year^date")}, {QStringLiteral("editora"), QStringLiteral("editorb"), QStringLiteral("editorc"), QStringLiteral("translator"), QStringLiteral("annotator"), QStringLiteral("commentator"), QStringLiteral("introduction"), QStringLiteral("foreword"), QStringLiteral("afterword"), QStringLiteral("subtitle"), QStringLiteral("titleaddon"), QStringLiteral("language"), QStringLiteral("origlanguage"), QStringLiteral("edition"), QStringLiteral("volumes"), QStringLiteral("series"), QStringLiteral("number"), QStringLiteral("note"), QStringLiteral("publisher"), QStringLiteral("location"), QStringLiteral("isbn"), QStringLiteral("pagetotal"), QStringLiteral("addendum"), QStringLiteral("pubstate"), QStringLiteral("doi"), QStringLiteral("eprint"), QStringLiteral("eprintclass"), QStringLiteral("eprinttype"), QStringLiteral("url"), QStringLiteral("urldate")}}, EntryDescription {QStringLiteral("InReference"), QString(), i18n("Part of a Reference"), {QStringLiteral("author"), QStringLiteral("editor"), QStringLiteral("title"), QStringLiteral("booktitle"), QStringLiteral("year^date")}, {QStringLiteral("editora"), QStringLiteral("editorb"), QStringLiteral("editorc"), QStringLiteral("translator"), QStringLiteral("annotator"), QStringLiteral("commentator"), QStringLiteral("introduction"), QStringLiteral("foreword"), QStringLiteral("afterword"), QStringLiteral("subtitle"), QStringLiteral("titleaddon"), QStringLiteral("maintitle"), QStringLiteral("mainsubtitle"), QStringLiteral("maintitleaddon"), QStringLiteral("booksubtitle"), QStringLiteral("booktitleaddon"), QStringLiteral("language"), QStringLiteral("origlanguage"), QStringLiteral("volume"), QStringLiteral("part"), QStringLiteral("edition"), QStringLiteral("volumes"), QStringLiteral("series"), QStringLiteral("number"), QStringLiteral("note"), QStringLiteral("publisher"), QStringLiteral("location"), QStringLiteral("isbn"), QStringLiteral("chapter"), QStringLiteral("pages"), QStringLiteral("addendum"), QStringLiteral("pubstate"), QStringLiteral("doi"), QStringLiteral("eprint"), QStringLiteral("eprintclass"), QStringLiteral("eprinttype"), QStringLiteral("url"), QStringLiteral("urldate")}}, EntryDescription {QStringLiteral("Report"), QString(), i18n("Report"), {QStringLiteral("author"), QStringLiteral("title"), QStringLiteral("type"), QStringLiteral("institution"), QStringLiteral("year^date")}, {QStringLiteral("subtitle"), QStringLiteral("titleaddon"), QStringLiteral("language"), QStringLiteral("number"), QStringLiteral("version"), QStringLiteral("note"), QStringLiteral("location"), QStringLiteral("month"), QStringLiteral("isrn"), QStringLiteral("chapter"), QStringLiteral("pages"), QStringLiteral("pagetotal"), QStringLiteral("addendum"), QStringLiteral("pubstate"), QStringLiteral("doi"), QStringLiteral("eprint"), QStringLiteral("eprintclass"), QStringLiteral("eprinttype"), QStringLiteral("url"), QStringLiteral("urldate")}}, EntryDescription {QStringLiteral("Thesis"), QString(), i18n("Thesis"), {QStringLiteral("author"), QStringLiteral("title"), QStringLiteral("type"), QStringLiteral("institution"), QStringLiteral("year^date")}, {QStringLiteral("subtitle"), QStringLiteral("titleaddon"), QStringLiteral("language"), QStringLiteral("note"), QStringLiteral("location"), QStringLiteral("month"), QStringLiteral("isbn"), QStringLiteral("chapter"), QStringLiteral("pages"), QStringLiteral("pagetotal"), QStringLiteral("addendum"), QStringLiteral("pubstate"), QStringLiteral("doi"), QStringLiteral("eprint"), QStringLiteral("eprintclass"), QStringLiteral("eprinttype"), QStringLiteral("url"), QStringLiteral("urldate")}}, EntryDescription {QStringLiteral("Unpublished"), QString(), i18n("Unpublished Material"), {QStringLiteral("author"), QStringLiteral("title"), QStringLiteral("year^date")}, {QStringLiteral("subtitle"), QStringLiteral("titleaddon"), QStringLiteral("language"), QStringLiteral("howpublished"), QStringLiteral("note"), QStringLiteral("location"), QStringLiteral("isbn"), QStringLiteral("date"), QStringLiteral("month"), QStringLiteral("year"), QStringLiteral("addendum"), QStringLiteral("pubstate"), QStringLiteral("url"), QStringLiteral("urldate")}}, EntryDescription {QStringLiteral("MastersThesis"), QString(), i18n("Master's Thesis"), {QStringLiteral("author"), QStringLiteral("title"), QStringLiteral("institution"), QStringLiteral("year^date")}, {QStringLiteral("subtitle"), QStringLiteral("titleaddon"), QStringLiteral("type"), QStringLiteral("language"), QStringLiteral("note"), QStringLiteral("location"), QStringLiteral("month"), QStringLiteral("isbn"), QStringLiteral("chapter"), QStringLiteral("pages"), QStringLiteral("pagetotal"), QStringLiteral("addendum"), QStringLiteral("pubstate"), QStringLiteral("doi"), QStringLiteral("eprint"), QStringLiteral("eprintclass"), QStringLiteral("eprinttype"), QStringLiteral("url"), QStringLiteral("urldate")}}, EntryDescription {QStringLiteral("PhDThesis"), QString(), i18n("PhD Thesis"), {QStringLiteral("author"), QStringLiteral("title"), QStringLiteral("institution"), QStringLiteral("year^date")}, {QStringLiteral("subtitle"), QStringLiteral("titleaddon"), QStringLiteral("type"), QStringLiteral("language"), QStringLiteral("note"), QStringLiteral("location"), QStringLiteral("month"), QStringLiteral("isbn"), QStringLiteral("chapter"), QStringLiteral("pages"), QStringLiteral("pagetotal"), QStringLiteral("addendum"), QStringLiteral("pubstate"), QStringLiteral("doi"), QStringLiteral("eprint"), QStringLiteral("eprintclass"), QStringLiteral("eprinttype"), QStringLiteral("url"), QStringLiteral("urldate")}}, EntryDescription {QStringLiteral("TechReport"), QString(), i18n("Technical Report"), {QStringLiteral("author"), QStringLiteral("title"), QStringLiteral("institution"), QStringLiteral("year^date")}, {QStringLiteral("subtitle"), QStringLiteral("titleaddon"), QStringLiteral("type"), QStringLiteral("language"), QStringLiteral("number"), QStringLiteral("version"), QStringLiteral("note"), QStringLiteral("location"), QStringLiteral("month"), QStringLiteral("isrn"), QStringLiteral("chapter"), QStringLiteral("pages"), QStringLiteral("pagetotal"), QStringLiteral("addendum"), QStringLiteral("pubstate"), QStringLiteral("doi"), QStringLiteral("eprint"), QStringLiteral("eprintclass"), QStringLiteral("eprinttype"), QStringLiteral("url"), QStringLiteral("urldate")}} }; #endif // HAVE_KF5 static const BibTeXEntries singletonBibTeX(entryDescriptionsBibTeX) #ifdef HAVE_KF5 , singletonBibLaTeX(entryDescriptionsBibLaTeX) #endif // HAVE_KF5 ; #ifdef HAVE_KF5 - return Preferences::bibliographySystem() == Preferences::BibLaTeX ? singletonBibLaTeX : singletonBibTeX; + return Preferences::instance().bibliographySystem() == Preferences::BibLaTeX ? singletonBibLaTeX : singletonBibTeX; #else // HAVE_KF5 return singletonBibTeX; #endif // HAVE_KF5 } QString BibTeXEntries::format(const QString &name, KBibTeX::Casing casing) const { QString iName = name.toLower(); switch (casing) { case KBibTeX::cLowerCase: return iName; case KBibTeX::cUpperCase: return name.toUpper(); case KBibTeX::cInitialCapital: iName[0] = iName[0].toUpper(); return iName; case KBibTeX::cLowerCamelCase: { for (const auto &ed : const_cast(*this)) { /// configuration file uses camel-case QString itName = ed.upperCamelCase.toLower(); if (itName == iName) { iName = ed.upperCamelCase; break; } } /// make an educated guess how camel-case would look like iName[0] = iName[0].toLower(); return iName; } case KBibTeX::cUpperCamelCase: { for (const auto &ed : const_cast(*this)) { /// configuration file uses camel-case QString itName = ed.upperCamelCase.toLower(); if (itName == iName) { iName = ed.upperCamelCase; break; } } /// make an educated guess how camel-case would look like iName[0] = iName[0].toUpper(); return iName; } } return name; } QString BibTeXEntries::label(const QString &name) const { const QString iName = name.toLower(); for (const auto &ed : const_cast(*this)) { /// Configuration file uses camel-case, convert this to lower case for faster comparison QString itName = ed.upperCamelCase.toLower(); if (itName == iName || (!(itName = ed.upperCamelCaseAlt.toLower()).isEmpty() && itName == iName)) return ed.label; } return QString(); } diff --git a/src/config/bibtexfields.cpp b/src/config/bibtexfields.cpp index 736911ac..5d1171db 100644 --- a/src/config/bibtexfields.cpp +++ b/src/config/bibtexfields.cpp @@ -1,411 +1,411 @@ /*************************************************************************** - * Copyright (C) 2004-2018 by Thomas Fischer * + * Copyright (C) 2004-2019 by Thomas Fischer * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, see . * ***************************************************************************/ #include "bibtexfields.h" #include #include #ifdef HAVE_KF5 #include #include #include #else // HAVE_KF5 #define i18n(text) QObject::tr(text) #endif // HAVE KF5 #include "preferences.h" #include "logging_config.h" bool operator==(const FieldDescription &a, const FieldDescription &b) { return a.upperCamelCase == b.upperCamelCase; } uint qHash(const FieldDescription &a) { return qHash(a.upperCamelCase); } class BibTeXFields::BibTeXFieldsPrivate { public: static const QVector fieldDescriptionsBibTeX; static const QVector fieldDescriptionsBibLaTeX; BibTeXFields *p; #ifdef HAVE_KF5 KSharedConfigPtr layoutConfig; #endif // HAVE_KF5 BibTeXFieldsPrivate(BibTeXFields *parent) : p(parent) { #ifdef HAVE_KF5 KSharedConfigPtr config(KSharedConfig::openConfig(QStringLiteral("kbibtexrc"))); KConfigGroup configGroup(config, QStringLiteral("User Interface")); const QString stylefile = configGroup.readEntry(QStringLiteral("CurrentStyle"), QString(QStringLiteral("bibtex"))).append(QStringLiteral(".kbstyle")); layoutConfig = KSharedConfig::openConfig(stylefile, KConfig::FullConfig, QStandardPaths::AppDataLocation); if (layoutConfig->groupList().isEmpty()) qCWarning(LOG_KBIBTEX_CONFIG) << "The configuration file for BibTeX fields could not be located or is empty:" << stylefile; #endif // HAVE_KF5 } #ifdef HAVE_KF5 void load() { for (BibTeXFields::Iterator it = p->begin(); it != p->end(); ++it) { auto &fd = *it; const QString groupName = QStringLiteral("Column") + fd.upperCamelCase + fd.upperCamelCaseAlt; KConfigGroup configGroup(layoutConfig, groupName); fd.visible.clear(); if (configGroup.exists()) { const QStringList keyList = configGroup.keyList(); for (const QString &key : keyList) { if (!key.startsWith(QStringLiteral("Visible_"))) continue; ///< a key other than a 'visibility' key const QString treeViewName = key.mid(8); fd.visible.insert(treeViewName, configGroup.readEntry(key, fd.defaultVisible)); } } } } void save() { for (const auto &fd : const_cast(*p)) { const QString groupName = QStringLiteral("Column") + fd.upperCamelCase + fd.upperCamelCaseAlt; KConfigGroup configGroup(layoutConfig, groupName); const QList keys = fd.visible.keys(); for (const QString &treeViewName : keys) { const QString key = QStringLiteral("Visible_") + treeViewName; configGroup.writeEntry(key, fd.visible.value(treeViewName, fd.defaultVisible)); } } layoutConfig->sync(); } void resetToDefaults(const QString &treeViewName) { for (BibTeXFields::Iterator it = p->begin(); it != p->end(); ++it) { const QString groupName = QStringLiteral("Column") + it->upperCamelCase + it->upperCamelCaseAlt; KConfigGroup configGroup(layoutConfig, groupName); configGroup.deleteEntry("Visible_" + treeViewName); } layoutConfig->sync(); load(); } #endif // HAVE_KF5 }; BibTeXFields::BibTeXFields(const QVector &other) : QVector(other), d(new BibTeXFieldsPrivate(this)) { #ifdef HAVE_KF5 d->load(); #endif // HAVE_KF5 } BibTeXFields::~BibTeXFields() { delete d; } /// This function cannot return a 'const' BibTeXFields object /// similarly like BibTeXEntries::instance as the FieldDescription's /// QMap visible will be modified. BibTeXFields &BibTeXFields::instance() { static const QVector fieldDescriptionsBibTeX { FieldDescription {QStringLiteral("^type"), QString(), {}, i18n("Element Type"), KBibTeX::tfSource, KBibTeX::tfSource, 5, {}, true, true}, FieldDescription {QStringLiteral("^id"), QString(), {}, i18n("Identifier"), KBibTeX::tfSource, KBibTeX::tfSource, 6, {}, true, true}, FieldDescription {QStringLiteral("Title"), QString(), {}, i18n("Title"), KBibTeX::tfPlainText, KBibTeX::tfPlainText | KBibTeX::tfReference | KBibTeX::tfSource, 14, {}, false, false}, FieldDescription {QStringLiteral("Title"), QStringLiteral("BookTitle"), {}, i18n("Title or Book Title"), KBibTeX::tfPlainText, KBibTeX::tfPlainText | KBibTeX::tfReference | KBibTeX::tfSource, 14, {}, true, false}, FieldDescription {QStringLiteral("Author"), QStringLiteral("Editor"), {}, i18n("Author or Editor"), KBibTeX::tfPlainText, KBibTeX::tfPlainText | KBibTeX::tfReference | KBibTeX::tfSource, 7, {}, true, false}, FieldDescription {QStringLiteral("Author"), QString(), {}, i18n("Author"), KBibTeX::tfPerson, KBibTeX::tfPerson | KBibTeX::tfReference, 7, {}, false, false}, FieldDescription {QStringLiteral("Editor"), QString(), {}, i18n("Editor"), KBibTeX::tfPerson, KBibTeX::tfPerson | KBibTeX::tfReference, 7, {}, false, false}, FieldDescription {QStringLiteral("Month"), QString(), {}, i18n("Month"), KBibTeX::tfPlainText, KBibTeX::tfPlainText | KBibTeX::tfReference | KBibTeX::tfSource, 3, {}, false, false}, FieldDescription {QStringLiteral("Year"), QString(), {}, i18n("Year"), KBibTeX::tfPlainText, KBibTeX::tfPlainText | KBibTeX::tfReference | KBibTeX::tfSource, 2, {}, true, false}, FieldDescription {QStringLiteral("Journal"), QString(), {}, i18n("Journal"), KBibTeX::tfPlainText, KBibTeX::tfPlainText | KBibTeX::tfReference | KBibTeX::tfSource, 4, {}, false, false}, FieldDescription {QStringLiteral("Volume"), QString(), {}, i18n("Volume"), KBibTeX::tfPlainText, KBibTeX::tfPlainText | KBibTeX::tfReference | KBibTeX::tfSource, 1, {}, false, false}, FieldDescription {QStringLiteral("Number"), QString(), {}, i18n("Number"), KBibTeX::tfPlainText, KBibTeX::tfPlainText | KBibTeX::tfReference | KBibTeX::tfSource, 1, {}, false, false}, FieldDescription {QStringLiteral("ISSN"), QString(), {}, i18n("ISSN"), KBibTeX::tfPlainText, KBibTeX::tfPlainText | KBibTeX::tfReference | KBibTeX::tfSource, 2, {}, false, true}, FieldDescription {QStringLiteral("ISBN"), QString(), {}, i18n("ISBN"), KBibTeX::tfPlainText, KBibTeX::tfPlainText | KBibTeX::tfReference | KBibTeX::tfSource, 2, {}, false, true}, FieldDescription {QStringLiteral("ISBN"), QStringLiteral("ISSN"), {}, i18n("ISBN or ISSN"), KBibTeX::tfPlainText, KBibTeX::tfPlainText | KBibTeX::tfReference | KBibTeX::tfSource, 2, {}, false, false}, FieldDescription {QStringLiteral("HowPublished"), QString(), {}, i18n("How Published"), KBibTeX::tfPlainText, KBibTeX::tfPlainText | KBibTeX::tfReference | KBibTeX::tfSource, 5, {}, false, false}, FieldDescription {QStringLiteral("Note"), QString(), {}, i18n("Note"), KBibTeX::tfPlainText, KBibTeX::tfPlainText | KBibTeX::tfReference | KBibTeX::tfSource, 5, {}, false, false}, FieldDescription {QStringLiteral("Abstract"), QString(), {}, i18n("Abstract"), KBibTeX::tfPlainText, KBibTeX::tfPlainText | KBibTeX::tfReference | KBibTeX::tfSource, 7, {}, false, true}, FieldDescription {QStringLiteral("Pages"), QString(), {}, i18n("Pages"), KBibTeX::tfPlainText, KBibTeX::tfPlainText | KBibTeX::tfReference | KBibTeX::tfSource, 2, {}, true, false}, FieldDescription {QStringLiteral("Publisher"), QString(), {}, i18n("Publisher"), KBibTeX::tfPlainText, KBibTeX::tfPlainText | KBibTeX::tfReference | KBibTeX::tfSource, 5, {}, false, false}, FieldDescription {QStringLiteral("Institution"), QString(), {}, i18n("Institution"), KBibTeX::tfPlainText, KBibTeX::tfPlainText | KBibTeX::tfReference | KBibTeX::tfSource, 5, {}, false, false}, FieldDescription {QStringLiteral("BookTitle"), QString(), {}, i18n("Book Title"), KBibTeX::tfPlainText, KBibTeX::tfPlainText | KBibTeX::tfReference | KBibTeX::tfSource, 14, {}, false, false}, FieldDescription {QStringLiteral("Series"), QString(), {}, i18n("Series"), KBibTeX::tfPlainText, KBibTeX::tfPlainText | KBibTeX::tfReference | KBibTeX::tfSource, 12, {}, false, false}, FieldDescription {QStringLiteral("Edition"), QString(), {}, i18n("Edition"), KBibTeX::tfPlainText, KBibTeX::tfPlainText | KBibTeX::tfReference | KBibTeX::tfSource, 2, {}, false, false}, FieldDescription {QStringLiteral("Chapter"), QString(), {}, i18n("Chapter"), KBibTeX::tfPlainText, KBibTeX::tfPlainText | KBibTeX::tfReference | KBibTeX::tfSource, 1, {}, false, false}, FieldDescription {QStringLiteral("Organization"), QString(), {}, i18n("Organization"), KBibTeX::tfPlainText, KBibTeX::tfPlainText | KBibTeX::tfReference | KBibTeX::tfSource, 2, {}, false, false}, FieldDescription {QStringLiteral("School"), QString(), {}, i18n("School"), KBibTeX::tfPlainText, KBibTeX::tfPlainText | KBibTeX::tfReference | KBibTeX::tfSource, 2, {}, false, false}, FieldDescription {QStringLiteral("Keywords"), QString(), {}, i18n("Keywords"), KBibTeX::tfKeyword, KBibTeX::tfKeyword | KBibTeX::tfSource, 3, {}, false, true}, FieldDescription {QStringLiteral("CrossRef"), QString(), {}, i18n("Cross Reference"), KBibTeX::tfVerbatim, KBibTeX::tfVerbatim, 3, {}, false, true}, FieldDescription {QStringLiteral("DOI"), QString(), {}, i18n("DOI"), KBibTeX::tfVerbatim, KBibTeX::tfVerbatim, 1, {}, false, true}, FieldDescription {QStringLiteral("URL"), QString(), {}, i18n("URL"), KBibTeX::tfVerbatim, KBibTeX::tfVerbatim, 3, {}, false, true}, FieldDescription {QStringLiteral("Location"), QString(), {}, i18n("Location"), KBibTeX::tfPlainText, KBibTeX::tfPlainText | KBibTeX::tfReference | KBibTeX::tfSource, 3, {}, false, false}, FieldDescription {QStringLiteral("Address"), QString(), {}, i18n("Address"), KBibTeX::tfPlainText, KBibTeX::tfPlainText | KBibTeX::tfReference | KBibTeX::tfSource, 3, {}, false, false}, FieldDescription {QStringLiteral("Type"), QString(), {}, i18n("Type"), KBibTeX::tfPlainText, KBibTeX::tfPlainText | KBibTeX::tfReference | KBibTeX::tfSource, 2, {}, false, false}, FieldDescription {QStringLiteral("Key"), QString(), {}, i18n("Key"), KBibTeX::tfPlainText, KBibTeX::tfPlainText | KBibTeX::tfReference | KBibTeX::tfSource, 2, {}, false, true}, FieldDescription {QStringLiteral("X-Color"), QString(), {}, i18n("Color"), KBibTeX::tfVerbatim, KBibTeX::tfVerbatim | KBibTeX::tfSource, 2, {}, false, true}, FieldDescription {QStringLiteral("X-Stars"), QString(), {}, i18n("Stars"), KBibTeX::tfVerbatim, KBibTeX::tfVerbatim | KBibTeX::tfSource, 4, {}, false, true}, }; static const QVector fieldDescriptionsBibLaTeX { FieldDescription {QStringLiteral("^type"), QString(), {}, i18n("Element Type"), KBibTeX::tfSource, KBibTeX::tfSource, 5, {}, true, true}, FieldDescription {QStringLiteral("^id"), QString(), {}, i18n("Identifier"), KBibTeX::tfSource, KBibTeX::tfSource, 6, {}, true, true}, FieldDescription {QStringLiteral("Title"), QString(), {}, i18n("Title"), KBibTeX::tfPlainText, KBibTeX::tfPlainText | KBibTeX::tfReference | KBibTeX::tfSource, 14, {}, false, false}, FieldDescription {QStringLiteral("Title"), QStringLiteral("BookTitle"), {}, i18n("Title or Book Title"), KBibTeX::tfPlainText, KBibTeX::tfPlainText | KBibTeX::tfReference | KBibTeX::tfSource, 14, {}, true, false}, FieldDescription {QStringLiteral("SubTitle"), QString(), {}, i18n("Subtitle"), KBibTeX::tfPlainText, KBibTeX::tfPlainText | KBibTeX::tfReference | KBibTeX::tfSource, 14, {}, false, false}, FieldDescription {QStringLiteral("TitleAddon"), QString(), {}, i18n("Title Addon"), KBibTeX::tfPlainText, KBibTeX::tfPlainText | KBibTeX::tfReference | KBibTeX::tfSource, 14, {}, false, false}, FieldDescription {QStringLiteral("ShortTitle"), QString(), {}, i18n("Shortitle"), KBibTeX::tfPlainText, KBibTeX::tfPlainText | KBibTeX::tfReference | KBibTeX::tfSource, 4, {}, false, false}, FieldDescription {QStringLiteral("OrigTitle"), QString(), {}, i18n("Original Title"), KBibTeX::tfPlainText, KBibTeX::tfPlainText | KBibTeX::tfReference | KBibTeX::tfSource, 2, {}, false, false}, FieldDescription {QStringLiteral("ReprintTitle"), QString(), {}, i18n("Reprint Title"), KBibTeX::tfPlainText, KBibTeX::tfPlainText | KBibTeX::tfReference | KBibTeX::tfSource, 2, {}, false, false}, FieldDescription {QStringLiteral("MainTitle"), QString(), {}, i18n("Main Title"), KBibTeX::tfPlainText, KBibTeX::tfPlainText | KBibTeX::tfReference | KBibTeX::tfSource, 14, {}, false, false}, FieldDescription {QStringLiteral("MainSubTitle"), QString(), {}, i18n("Main Subtitle"), KBibTeX::tfPlainText, KBibTeX::tfPlainText | KBibTeX::tfReference | KBibTeX::tfSource, 14, {}, false, false}, FieldDescription {QStringLiteral("MainTitleAddon"), QString(), {}, i18n("Maintitle Addon"), KBibTeX::tfPlainText, KBibTeX::tfPlainText | KBibTeX::tfReference | KBibTeX::tfSource, 14, {}, false, false}, FieldDescription {QStringLiteral("Author"), QStringLiteral("Editor"), {}, i18n("Author or Editor"), KBibTeX::tfPlainText, KBibTeX::tfPlainText | KBibTeX::tfReference | KBibTeX::tfSource, 7, {}, true, false}, FieldDescription {QStringLiteral("Author"), QString(), {}, i18n("Author"), KBibTeX::tfPerson, KBibTeX::tfPerson | KBibTeX::tfReference, 7, {}, false, false}, FieldDescription {QStringLiteral("ShortAuthor"), QString(), {}, i18n("Short Author"), KBibTeX::tfPerson, KBibTeX::tfPerson | KBibTeX::tfReference, 7, {}, false, false}, FieldDescription {QStringLiteral("NameAddon"), QString(), {}, i18n("Name Addon"), KBibTeX::tfPerson, KBibTeX::tfPerson | KBibTeX::tfReference, 7, {}, false, false}, FieldDescription {QStringLiteral("AuthorType"), QString(), {}, i18n("Author Type"), KBibTeX::tfPerson, KBibTeX::tfPerson | KBibTeX::tfReference, 7, {}, false, false}, FieldDescription {QStringLiteral("BookAuthor"), QString(), {}, i18n("Book Author"), KBibTeX::tfPerson, KBibTeX::tfPerson | KBibTeX::tfReference, 7, {}, false, false}, FieldDescription {QStringLiteral("Editor"), QString(), {}, i18n("Editor"), KBibTeX::tfPerson, KBibTeX::tfPerson | KBibTeX::tfReference, 7, {}, false, false}, FieldDescription {QStringLiteral("ShortEditor"), QString(), {}, i18n("Short Editor"), KBibTeX::tfPerson, KBibTeX::tfPerson | KBibTeX::tfReference, 7, {}, false, false}, FieldDescription {QStringLiteral("EditorType"), QString(), {}, i18n("Editor Type"), KBibTeX::tfPerson, KBibTeX::tfPerson | KBibTeX::tfReference, 7, {}, false, false}, FieldDescription {QStringLiteral("EditorA"), QString(), {}, i18n("Editor A"), KBibTeX::tfPerson, KBibTeX::tfPerson | KBibTeX::tfReference, 7, {}, false, false}, FieldDescription {QStringLiteral("EditorAType"), QString(), {}, i18n("Editor A Type"), KBibTeX::tfPerson, KBibTeX::tfPerson | KBibTeX::tfReference, 7, {}, false, false}, FieldDescription {QStringLiteral("EditorB"), QString(), {}, i18n("Editor B"), KBibTeX::tfPerson, KBibTeX::tfPerson | KBibTeX::tfReference, 7, {}, false, false}, FieldDescription {QStringLiteral("EditorBType"), QString(), {}, i18n("Editor B Type"), KBibTeX::tfPerson, KBibTeX::tfPerson | KBibTeX::tfReference, 7, {}, false, false}, FieldDescription {QStringLiteral("EditorC"), QString(), {}, i18n("Editor C"), KBibTeX::tfPerson, KBibTeX::tfPerson | KBibTeX::tfReference, 7, {}, false, false}, FieldDescription {QStringLiteral("EditorCType"), QString(), {}, i18n("Editor C Type"), KBibTeX::tfPerson, KBibTeX::tfPerson | KBibTeX::tfReference, 7, {}, false, false}, FieldDescription {QStringLiteral("Translator"), QString(), {}, i18n("Translator"), KBibTeX::tfPerson, KBibTeX::tfPerson | KBibTeX::tfReference, 7, {}, false, false}, FieldDescription {QStringLiteral("Afterword"), QString(), {}, i18n("Afterword Author"), KBibTeX::tfPerson, KBibTeX::tfPerson | KBibTeX::tfReference, 7, {}, false, false}, FieldDescription {QStringLiteral("Introduction"), QString(), {}, i18n("Introduction Author"), KBibTeX::tfPerson, KBibTeX::tfPerson | KBibTeX::tfReference, 7, {}, false, false}, FieldDescription {QStringLiteral("Foreword"), QString(), {}, i18n("Foreword Author"), KBibTeX::tfPerson, KBibTeX::tfPerson | KBibTeX::tfReference, 7, {}, false, false}, FieldDescription {QStringLiteral("Annotator"), QString(), {}, i18n("Annotator"), KBibTeX::tfPerson, KBibTeX::tfPerson | KBibTeX::tfReference, 7, {}, false, false}, FieldDescription {QStringLiteral("Commentator"), QString(), {}, i18n("Commentator"), KBibTeX::tfPerson, KBibTeX::tfPerson | KBibTeX::tfReference, 7, {}, false, false}, FieldDescription {QStringLiteral("Holder"), QString(), {}, i18n("Patent Holder"), KBibTeX::tfPerson, KBibTeX::tfPerson | KBibTeX::tfReference, 7, {}, false, false}, FieldDescription {QStringLiteral("Month"), QString(), {}, i18n("Month"), KBibTeX::tfPlainText, KBibTeX::tfPlainText | KBibTeX::tfReference | KBibTeX::tfSource, 3, {}, false, false}, FieldDescription {QStringLiteral("Year"), QString(), {}, i18n("Year"), KBibTeX::tfPlainText, KBibTeX::tfPlainText | KBibTeX::tfReference | KBibTeX::tfSource, 2, {}, false, false}, FieldDescription {QStringLiteral("Date"), QString(), {}, i18n("Date"), KBibTeX::tfPlainText, KBibTeX::tfPlainText | KBibTeX::tfReference | KBibTeX::tfSource, 2, {}, false, false}, FieldDescription {QStringLiteral("Year"), QStringLiteral("Date"), {}, i18n("Date or Year"), KBibTeX::tfPlainText, KBibTeX::tfPlainText | KBibTeX::tfReference | KBibTeX::tfSource, 2, {}, true, false}, FieldDescription {QStringLiteral("EventDate"), QString(), {}, i18n("Event Date"), KBibTeX::tfPlainText, KBibTeX::tfPlainText | KBibTeX::tfReference | KBibTeX::tfSource, 2, {}, false, false}, FieldDescription {QStringLiteral("OrigDate"), QString(), {}, i18n("Original Date"), KBibTeX::tfPlainText, KBibTeX::tfPlainText | KBibTeX::tfReference | KBibTeX::tfSource, 2, {}, false, false}, FieldDescription {QStringLiteral("JournalTitle"), QString(), {QStringLiteral("Journal")}, i18n("Journal Title"), KBibTeX::tfPlainText, KBibTeX::tfPlainText | KBibTeX::tfReference | KBibTeX::tfSource, 4, {}, false, false}, FieldDescription {QStringLiteral("JournalSubTitle"), QString(), {}, i18n("Journal Subtitle"), KBibTeX::tfPlainText, KBibTeX::tfPlainText | KBibTeX::tfReference | KBibTeX::tfSource, 4, {}, false, false}, FieldDescription {QStringLiteral("ShortJournal"), QString(), {}, i18n("Journal Shortitle"), KBibTeX::tfPlainText, KBibTeX::tfPlainText | KBibTeX::tfReference | KBibTeX::tfSource, 4, {}, false, false}, FieldDescription {QStringLiteral("Volume"), QString(), {}, i18n("Volume"), KBibTeX::tfPlainText, KBibTeX::tfPlainText | KBibTeX::tfReference | KBibTeX::tfSource, 1, {}, false, false}, FieldDescription {QStringLiteral("Volumes"), QString(), {}, i18n("Number of Volumes"), KBibTeX::tfPlainText, KBibTeX::tfPlainText | KBibTeX::tfReference | KBibTeX::tfSource, 1, {}, false, false}, FieldDescription {QStringLiteral("Number"), QString(), {}, i18n("Number"), KBibTeX::tfPlainText, KBibTeX::tfPlainText | KBibTeX::tfReference | KBibTeX::tfSource, 1, {}, false, false}, FieldDescription {QStringLiteral("Version"), QString(), {}, i18n("Version"), KBibTeX::tfPlainText, KBibTeX::tfPlainText | KBibTeX::tfReference | KBibTeX::tfSource, 1, {}, false, false}, FieldDescription {QStringLiteral("Part"), QString(), {}, i18n("Part"), KBibTeX::tfPlainText, KBibTeX::tfPlainText | KBibTeX::tfReference | KBibTeX::tfSource, 1, {}, false, false}, FieldDescription {QStringLiteral("Issue"), QString(), {}, i18n("Issue"), KBibTeX::tfPlainText, KBibTeX::tfPlainText | KBibTeX::tfReference | KBibTeX::tfSource, 1, {}, false, false}, FieldDescription {QStringLiteral("IASN"), QString(), {}, i18n("IASN"), KBibTeX::tfPlainText, KBibTeX::tfPlainText | KBibTeX::tfReference | KBibTeX::tfSource, 2, {}, false, false}, FieldDescription {QStringLiteral("ISMN"), QString(), {}, i18n("ISMN"), KBibTeX::tfPlainText, KBibTeX::tfPlainText | KBibTeX::tfReference | KBibTeX::tfSource, 2, {}, false, false}, FieldDescription {QStringLiteral("ISRN"), QString(), {}, i18n("ISRN"), KBibTeX::tfPlainText, KBibTeX::tfPlainText | KBibTeX::tfReference | KBibTeX::tfSource, 2, {}, false, false}, FieldDescription {QStringLiteral("ISSN"), QString(), {}, i18n("ISSN"), KBibTeX::tfPlainText, KBibTeX::tfPlainText | KBibTeX::tfReference | KBibTeX::tfSource, 2, {}, false, false}, FieldDescription {QStringLiteral("ISBN"), QString(), {}, i18n("ISBN"), KBibTeX::tfPlainText, KBibTeX::tfPlainText | KBibTeX::tfReference | KBibTeX::tfSource, 2, {}, false, false}, FieldDescription {QStringLiteral("ISBN"), QStringLiteral("ISSN"), {}, i18n("ISBN or ISSN"), KBibTeX::tfPlainText, KBibTeX::tfPlainText | KBibTeX::tfReference | KBibTeX::tfSource, 2, {}, false, false}, FieldDescription {QStringLiteral("ISWC"), QString(), {}, i18n("ISWC"), KBibTeX::tfPlainText, KBibTeX::tfPlainText | KBibTeX::tfReference | KBibTeX::tfSource, 2, {}, false, false}, FieldDescription {QStringLiteral("HowPublished"), QString(), {}, i18n("How Published"), KBibTeX::tfPlainText, KBibTeX::tfPlainText | KBibTeX::tfReference | KBibTeX::tfSource, 5, {}, false, false}, FieldDescription {QStringLiteral("Note"), QString(), {}, i18n("Note"), KBibTeX::tfPlainText, KBibTeX::tfPlainText | KBibTeX::tfReference | KBibTeX::tfSource, 5, {}, false, false}, FieldDescription {QStringLiteral("Addendum"), QString(), {}, i18n("Addendum"), KBibTeX::tfPlainText, KBibTeX::tfPlainText | KBibTeX::tfReference | KBibTeX::tfSource, 5, {}, false, false}, FieldDescription {QStringLiteral("Annotation"), QString(), {QStringLiteral("Annote")}, i18n("Annotation"), KBibTeX::tfPlainText, KBibTeX::tfPlainText | KBibTeX::tfReference | KBibTeX::tfSource, 5, {}, false, false}, FieldDescription {QStringLiteral("Abstract"), QString(), {}, i18n("Abstract"), KBibTeX::tfPlainText, KBibTeX::tfPlainText | KBibTeX::tfReference | KBibTeX::tfSource, 7, {}, false, true}, FieldDescription {QStringLiteral("Pages"), QString(), {}, i18n("Pages"), KBibTeX::tfPlainText, KBibTeX::tfPlainText | KBibTeX::tfReference | KBibTeX::tfSource, 2, {}, true, false}, FieldDescription {QStringLiteral("PageTotal"), QString(), {}, i18n("Total Pages"), KBibTeX::tfPlainText, KBibTeX::tfPlainText | KBibTeX::tfReference | KBibTeX::tfSource, 2, {}, false, false}, FieldDescription {QStringLiteral("Pagination"), QString(), {}, i18n("Pagination"), KBibTeX::tfPlainText, KBibTeX::tfPlainText | KBibTeX::tfReference | KBibTeX::tfSource, 2, {}, false, false}, FieldDescription {QStringLiteral("BookPagination"), QString(), {}, i18n("Book Pagination"), KBibTeX::tfPlainText, KBibTeX::tfPlainText | KBibTeX::tfReference | KBibTeX::tfSource, 2, {}, false, false}, FieldDescription {QStringLiteral("Publisher"), QString(), {}, i18n("Publisher"), KBibTeX::tfPlainText, KBibTeX::tfPlainText | KBibTeX::tfReference | KBibTeX::tfSource, 5, {}, false, false}, FieldDescription {QStringLiteral("OrigPublisher"), QString(), {}, i18n("Original Publisher"), KBibTeX::tfPlainText, KBibTeX::tfPlainText | KBibTeX::tfReference | KBibTeX::tfSource, 2, {}, false, false}, FieldDescription {QStringLiteral("Institution"), QString(), {QStringLiteral("School")}, i18n("Institution"), KBibTeX::tfPlainText, KBibTeX::tfPlainText | KBibTeX::tfReference | KBibTeX::tfSource, 5, {}, false, false}, FieldDescription {QStringLiteral("BookTitle"), QString(), {}, i18n("Book Title"), KBibTeX::tfPlainText, KBibTeX::tfPlainText | KBibTeX::tfReference | KBibTeX::tfSource, 14, {}, false, false}, FieldDescription {QStringLiteral("BookSubTitle"), QString(), {}, i18n("Book Subtitle"), KBibTeX::tfPlainText, KBibTeX::tfPlainText | KBibTeX::tfReference | KBibTeX::tfSource, 14, {}, false, false}, FieldDescription {QStringLiteral("IssueTitle"), QString(), {}, i18n("Issue Title"), KBibTeX::tfPlainText, KBibTeX::tfPlainText | KBibTeX::tfReference | KBibTeX::tfSource, 1, {}, false, false}, FieldDescription {QStringLiteral("IssueSubTitle"), QString(), {}, i18n("Issue Subtitle"), KBibTeX::tfPlainText, KBibTeX::tfPlainText | KBibTeX::tfReference | KBibTeX::tfSource, 1, {}, false, false}, FieldDescription {QStringLiteral("BookTitleAddon"), QString(), {}, i18n("Booktitle Addon"), KBibTeX::tfPlainText, KBibTeX::tfPlainText | KBibTeX::tfReference | KBibTeX::tfSource, 14, {}, false, false}, FieldDescription {QStringLiteral("Series"), QString(), {}, i18n("Series"), KBibTeX::tfPlainText, KBibTeX::tfPlainText | KBibTeX::tfReference | KBibTeX::tfSource, 12, {}, false, false}, FieldDescription {QStringLiteral("ShortSeries"), QString(), {}, i18n("Series Shortitle"), KBibTeX::tfPlainText, KBibTeX::tfPlainText | KBibTeX::tfReference | KBibTeX::tfSource, 4, {}, false, false}, FieldDescription {QStringLiteral("Edition"), QString(), {}, i18n("Edition"), KBibTeX::tfPlainText, KBibTeX::tfPlainText | KBibTeX::tfReference | KBibTeX::tfSource, 2, {}, false, false}, FieldDescription {QStringLiteral("Chapter"), QString(), {}, i18n("Chapter"), KBibTeX::tfPlainText, KBibTeX::tfPlainText | KBibTeX::tfReference | KBibTeX::tfSource, 1, {}, false, false}, FieldDescription {QStringLiteral("Organization"), QString(), {}, i18n("Organization"), KBibTeX::tfPlainText, KBibTeX::tfPlainText | KBibTeX::tfReference | KBibTeX::tfSource, 2, {}, false, false}, FieldDescription {QStringLiteral("EventTitle"), QString(), {}, i18n("Event Title"), KBibTeX::tfPlainText, KBibTeX::tfPlainText | KBibTeX::tfReference | KBibTeX::tfSource, 2, {}, false, false}, FieldDescription {QStringLiteral("Venue"), QString(), {}, i18n("Venue"), KBibTeX::tfPlainText, KBibTeX::tfPlainText | KBibTeX::tfReference | KBibTeX::tfSource, 2, {}, false, false}, FieldDescription {QStringLiteral("IndexTitle"), QString(), {}, i18n("Index Title"), KBibTeX::tfPlainText, KBibTeX::tfPlainText | KBibTeX::tfReference | KBibTeX::tfSource, 2, {}, false, true}, FieldDescription {QStringLiteral("Keywords"), QString(), {}, i18n("Keywords"), KBibTeX::tfKeyword, KBibTeX::tfKeyword | KBibTeX::tfSource, 3, {}, false, true}, FieldDescription {QStringLiteral("CrossRef"), QString(), {}, i18n("Cross Reference"), KBibTeX::tfVerbatim, KBibTeX::tfVerbatim, 3, {}, false, true}, FieldDescription {QStringLiteral("XRef"), QString(), {}, i18n("XRef"), KBibTeX::tfVerbatim, KBibTeX::tfVerbatim, 3, {}, false, true}, FieldDescription {QStringLiteral("DOI"), QString(), {}, i18n("DOI"), KBibTeX::tfVerbatim, KBibTeX::tfVerbatim, 1, {}, false, false}, FieldDescription {QStringLiteral("EPrint"), QString(), {}, i18n("E-Print"), KBibTeX::tfVerbatim, KBibTeX::tfVerbatim, 1, {}, false, false}, FieldDescription {QStringLiteral("EPrintClass"), QString(), {QStringLiteral("PrimaryClass")}, i18n("E-Print Class"), KBibTeX::tfPlainText, KBibTeX::tfPlainText | KBibTeX::tfReference | KBibTeX::tfSource, 2, {}, false, false}, FieldDescription {QStringLiteral("EPrintType"), QString(), {QStringLiteral("ArchivePrefix")}, i18n("E-Print Type"), KBibTeX::tfPlainText, KBibTeX::tfPlainText | KBibTeX::tfReference | KBibTeX::tfSource, 2, {}, false, false}, FieldDescription {QStringLiteral("URL"), QString(), {}, i18n("URL"), KBibTeX::tfVerbatim, KBibTeX::tfVerbatim, 3, {}, false, false}, FieldDescription {QStringLiteral("URLDate"), QString(), {}, i18n("URL Date"), KBibTeX::tfPlainText, KBibTeX::tfPlainText | KBibTeX::tfReference | KBibTeX::tfSource, 3, {}, false, false}, FieldDescription {QStringLiteral("File"), QString(), {QStringLiteral("PDF")}, i18n("Local File URL"), KBibTeX::tfVerbatim, KBibTeX::tfVerbatim, 3, {}, false, true}, FieldDescription {QStringLiteral("Location"), QString(), {QStringLiteral("Address")}, i18n("Location"), KBibTeX::tfPlainText, KBibTeX::tfPlainText | KBibTeX::tfReference | KBibTeX::tfSource, 3, {}, false, false}, FieldDescription {QStringLiteral("OrigLocation"), QString(), {}, i18n("Original Location"), KBibTeX::tfPlainText, KBibTeX::tfPlainText | KBibTeX::tfReference | KBibTeX::tfSource, 2, {}, false, false}, FieldDescription {QStringLiteral("Type"), QString(), {}, i18n("Type"), KBibTeX::tfPlainText, KBibTeX::tfPlainText | KBibTeX::tfReference | KBibTeX::tfSource, 2, {}, false, false}, FieldDescription {QStringLiteral("EID"), QString(), {}, i18n("EID"), KBibTeX::tfPlainText, KBibTeX::tfPlainText | KBibTeX::tfReference | KBibTeX::tfSource, 2, {}, false, false}, FieldDescription {QStringLiteral("Label"), QString(), {}, i18n("Label"), KBibTeX::tfPlainText, KBibTeX::tfPlainText | KBibTeX::tfReference | KBibTeX::tfSource, 2, {}, false, true}, FieldDescription {QStringLiteral("ShortHand"), QString(), {}, i18n("Shorthand"), KBibTeX::tfPlainText, KBibTeX::tfPlainText | KBibTeX::tfReference | KBibTeX::tfSource, 2, {}, false, true}, FieldDescription {QStringLiteral("ShortHandIntro"), QString(), {}, i18n("Shorthand Intro"), KBibTeX::tfPlainText, KBibTeX::tfPlainText | KBibTeX::tfReference | KBibTeX::tfSource, 2, {}, false, true}, FieldDescription {QStringLiteral("PubState"), QString(), {}, i18n("Publication State"), KBibTeX::tfPlainText, KBibTeX::tfPlainText | KBibTeX::tfReference | KBibTeX::tfSource, 2, {}, false, false}, FieldDescription {QStringLiteral("Language"), QString(), {}, i18n("Language"), KBibTeX::tfPlainText, KBibTeX::tfPlainText | KBibTeX::tfReference | KBibTeX::tfSource, 2, {}, false, false}, FieldDescription {QStringLiteral("OrigLanguage"), QString(), {}, i18n("Original Language"), KBibTeX::tfPlainText, KBibTeX::tfPlainText | KBibTeX::tfReference | KBibTeX::tfSource, 2, {}, false, false}, FieldDescription {QStringLiteral("Library"), QString(), {}, i18n("Library"), KBibTeX::tfPlainText, KBibTeX::tfPlainText | KBibTeX::tfReference | KBibTeX::tfSource, 2, {}, false, false}, FieldDescription {QStringLiteral("X-Color"), QString(), {}, i18n("Color"), KBibTeX::tfVerbatim, KBibTeX::tfVerbatim | KBibTeX::tfSource, 2, {}, false, true}, FieldDescription {QStringLiteral("Gender"), QString(), {}, i18n("Gender"), KBibTeX::tfPlainText, KBibTeX::tfPlainText | KBibTeX::tfReference | KBibTeX::tfSource, 2, {}, false, true}, FieldDescription {QStringLiteral("Hyphenation"), QString(), {}, i18n("Hyphenation"), KBibTeX::tfPlainText, KBibTeX::tfPlainText | KBibTeX::tfReference | KBibTeX::tfSource, 2, {}, false, true}, FieldDescription {QStringLiteral("IndexSortTitle"), QString(), {}, i18n("Index Sorttitle"), KBibTeX::tfPlainText, KBibTeX::tfPlainText | KBibTeX::tfReference | KBibTeX::tfSource, 2, {}, false, true}, FieldDescription {QStringLiteral("Options"), QString(), {}, i18n("Entry Options"), KBibTeX::tfPlainText, KBibTeX::tfPlainText | KBibTeX::tfReference | KBibTeX::tfSource, 2, {}, false, true}, FieldDescription {QStringLiteral("Presort"), QString(), {}, i18n("Presort"), KBibTeX::tfPlainText, KBibTeX::tfPlainText | KBibTeX::tfReference | KBibTeX::tfSource, 2, {}, false, true}, FieldDescription {QStringLiteral("SortKey"), QString(), {QStringLiteral("Key")}, i18n("Sort Key"), KBibTeX::tfPlainText, KBibTeX::tfPlainText | KBibTeX::tfReference | KBibTeX::tfSource, 2, {}, false, true}, FieldDescription {QStringLiteral("SortName"), QString(), {}, i18n("Sort Names"), KBibTeX::tfPerson, KBibTeX::tfPerson | KBibTeX::tfReference, 7, {}, false, false}, FieldDescription {QStringLiteral("SortShortHand"), QString(), {}, i18n("Sort Shorthand"), KBibTeX::tfPlainText, KBibTeX::tfPlainText | KBibTeX::tfReference | KBibTeX::tfSource, 2, {}, false, true}, FieldDescription {QStringLiteral("SortTitle"), QString(), {}, i18n("Sort Title"), KBibTeX::tfPlainText, KBibTeX::tfPlainText | KBibTeX::tfReference | KBibTeX::tfSource, 2, {}, false, true}, FieldDescription {QStringLiteral("SortYear"), QString(), {}, i18n("Sort Year"), KBibTeX::tfPlainText, KBibTeX::tfPlainText | KBibTeX::tfReference | KBibTeX::tfSource, 2, {}, false, true}, FieldDescription {QStringLiteral("ISAN"), QString(), {}, i18n("ISAN"), KBibTeX::tfPlainText, KBibTeX::tfPlainText | KBibTeX::tfReference | KBibTeX::tfSource, 2, {}, false, false}, FieldDescription {QStringLiteral("Location"), QString(), {}, i18n("Location"), KBibTeX::tfPlainText, KBibTeX::tfPlainText | KBibTeX::tfReference | KBibTeX::tfSource, 3, {}, false, false}, }; static BibTeXFields singletonBibTeX(fieldDescriptionsBibTeX), singletonBibLaTeX(fieldDescriptionsBibLaTeX); - return Preferences::bibliographySystem() == Preferences::BibLaTeX ? singletonBibLaTeX : singletonBibTeX; + return Preferences::instance().bibliographySystem() == Preferences::BibLaTeX ? singletonBibLaTeX : singletonBibTeX; } #ifdef HAVE_KF5 void BibTeXFields::save() { d->save(); } void BibTeXFields::resetToDefaults(const QString &treeViewName) { d->resetToDefaults(treeViewName); } #endif // HAVE_KF5 QString BibTeXFields::format(const QString &name, KBibTeX::Casing casing) const { QString iName = name.toLower(); switch (casing) { case KBibTeX::cLowerCase: return iName; case KBibTeX::cUpperCase: return name.toUpper(); case KBibTeX::cInitialCapital: iName[0] = iName[0].toUpper(); return iName; case KBibTeX::cLowerCamelCase: { for (const auto &fd : const_cast(*this)) { /// configuration file uses camel-case QString itName = fd.upperCamelCase.toLower(); if (itName == iName && fd.upperCamelCaseAlt.isEmpty()) { iName = fd.upperCamelCase; break; } } /// make an educated guess how camel-case would look like iName[0] = iName[0].toLower(); return iName; } case KBibTeX::cUpperCamelCase: { for (const auto &fd : const_cast(*this)) { /// configuration file uses camel-case QString itName = fd.upperCamelCase.toLower(); if (itName == iName && fd.upperCamelCaseAlt.isEmpty()) { iName = fd.upperCamelCase; break; } } /// make an educated guess how camel-case would look like iName[0] = iName[0].toUpper(); return iName; } } return name; } const FieldDescription BibTeXFields::find(const QString &name) const { const QString iName = name.toLower(); for (const auto &fd : const_cast(*this)) { if (fd.upperCamelCase.toLower() == iName && fd.upperCamelCaseAlt.isEmpty()) return fd; } qCWarning(LOG_KBIBTEX_CONFIG) << "No field description for " << name << "(" << iName << ")"; return FieldDescription {QString(), QString(), {}, QString(), KBibTeX::tfSource, KBibTeX::tfSource, 0, {}, false, false}; } KBibTeX::TypeFlag BibTeXFields::typeFlagFromString(const QString &typeFlagString) { KBibTeX::TypeFlag result = (KBibTeX::TypeFlag)0; if (typeFlagString == QStringLiteral("Text")) result = KBibTeX::tfPlainText; else if (typeFlagString == QStringLiteral("Source")) result = KBibTeX::tfSource; else if (typeFlagString == QStringLiteral("Person")) result = KBibTeX::tfPerson; else if (typeFlagString == QStringLiteral("Keyword")) result = KBibTeX::tfKeyword; else if (typeFlagString == QStringLiteral("Reference")) result = KBibTeX::tfReference; else if (typeFlagString == QStringLiteral("Verbatim")) result = KBibTeX::tfVerbatim; return result; } KBibTeX::TypeFlags BibTeXFields::typeFlagsFromString(const QString &typeFlagsString) { KBibTeX::TypeFlags result; const QStringList list = typeFlagsString.split(';'); for (const QString &s : list) result |= typeFlagFromString(s); return result; } QString BibTeXFields::typeFlagsToString(KBibTeX::TypeFlags typeFlags) { QStringList resultList; if (typeFlags & KBibTeX::tfPlainText) resultList << QStringLiteral("Text"); if (typeFlags & KBibTeX::tfSource) resultList << QStringLiteral("Source"); if (typeFlags & KBibTeX::tfPerson) resultList << QStringLiteral("Person"); if (typeFlags & KBibTeX::tfKeyword) resultList << QStringLiteral("Keyword"); if (typeFlags & KBibTeX::tfReference) resultList << QStringLiteral("Reference"); if (typeFlags & KBibTeX::tfVerbatim) resultList << QStringLiteral("Verbatim"); return resultList.join(QChar(';')); } QString BibTeXFields::typeFlagToString(KBibTeX::TypeFlag typeFlag) { if (typeFlag == KBibTeX::tfPlainText) return QStringLiteral("Text"); if (typeFlag == KBibTeX::tfSource) return QStringLiteral("Source"); if (typeFlag == KBibTeX::tfPerson) return QStringLiteral("Person"); if (typeFlag == KBibTeX::tfKeyword) return QStringLiteral("Keyword"); if (typeFlag == KBibTeX::tfReference) return QStringLiteral("Reference"); if (typeFlag == KBibTeX::tfVerbatim) return QStringLiteral("Verbatim"); return QString(); } diff --git a/src/global/preferences.cpp b/src/global/preferences.cpp index 38014fc3..22b579a2 100644 --- a/src/global/preferences.cpp +++ b/src/global/preferences.cpp @@ -1,117 +1,184 @@ /*************************************************************************** - * Copyright (C) 2004-2018 by Thomas Fischer * + * Copyright (C) 2004-2019 by Thomas Fischer * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, see . * ***************************************************************************/ #include "preferences.h" #include #ifdef HAVE_KF5 #include #include +#include #include #else // HAVE_KF5 #define I18N_NOOP(text) QObject::tr(text) #define i18n(text) QObject::tr(text) #endif // HAVE_KF5 #ifdef HAVE_KF5 #include "notificationhub.h" #endif // HAVE_KF5 const QString Preferences::groupColor = QStringLiteral("Color Labels"); const QString Preferences::keyColorCodes = QStringLiteral("colorCodes"); const QStringList Preferences::defaultColorCodes {QStringLiteral("#cc3300"), QStringLiteral("#0033ff"), QStringLiteral("#009966"), QStringLiteral("#f0d000")}; const QString Preferences::keyColorLabels = QStringLiteral("colorLabels"); // FIXME // clazy warns: QString(const char*) being called [-Wclazy-qstring-uneeded-heap-allocations] // ... but using QStringLiteral may break the translation process? const QStringList Preferences::defaultColorLabels {I18N_NOOP("Important"), I18N_NOOP("Unread"), I18N_NOOP("Read"), I18N_NOOP("Watch")}; const QString Preferences::groupGeneral = QStringLiteral("General"); const QString Preferences::keyBackupScope = QStringLiteral("backupScope"); const Preferences::BackupScope Preferences::defaultBackupScope = LocalOnly; const QString Preferences::keyNumberOfBackups = QStringLiteral("numberOfBackups"); const int Preferences::defaultNumberOfBackups = 5; const QString Preferences::groupUserInterface = QStringLiteral("User Interface"); const QString Preferences::keyElementDoubleClickAction = QStringLiteral("elementDoubleClickAction"); const Preferences::ElementDoubleClickAction Preferences::defaultElementDoubleClickAction = ActionOpenEditor; const QString Preferences::keyEncoding = QStringLiteral("encoding"); const QString Preferences::defaultEncoding = QStringLiteral("LaTeX"); const QString Preferences::keyStringDelimiter = QStringLiteral("stringDelimiter"); const QString Preferences::defaultStringDelimiter = QStringLiteral("{}"); const QString Preferences::keyQuoteComment = QStringLiteral("quoteComment"); const Preferences::QuoteComment Preferences::defaultQuoteComment = qcNone; const QString Preferences::keyKeywordCasing = QStringLiteral("keywordCasing"); const KBibTeX::Casing Preferences::defaultKeywordCasing = KBibTeX::cLowerCase; const QString Preferences::keyProtectCasing = QStringLiteral("protectCasing"); const Qt::CheckState Preferences::defaultProtectCasing = Qt::PartiallyChecked; const QString Preferences::keyListSeparator = QStringLiteral("ListSeparator"); const QString Preferences::defaultListSeparator = QStringLiteral("; "); +const Preferences::BibliographySystem Preferences::defaultBibliographySystem = Preferences::BibTeX; + /** * Preferences for Data objects */ const QString Preferences::keyPersonNameFormatting = QStringLiteral("personNameFormatting"); const QString Preferences::personNameFormatLastFirst = QStringLiteral("<%l><, %s><, %f>"); const QString Preferences::personNameFormatFirstLast = QStringLiteral("<%f ><%l>< %s>"); const QString Preferences::defaultPersonNameFormatting = personNameFormatLastFirst; -const Preferences::BibliographySystem Preferences::defaultBibliographySystem = Preferences::BibTeX; +class Preferences::Private +{ +private: + Preferences *parent; + +public: +#ifdef HAVE_KF5 + KSharedConfigPtr config; + KConfigWatcher::Ptr watcher; +#endif // HAVE_KF5 + + static const QString keyBibliographySystem; +#ifdef HAVE_KF5 + bool bibliographySystemDirtyFlag; + Preferences::BibliographySystem bibliographySystemCached; +#endif // HAVE_KF5 + + Private(Preferences *_parent) + : parent(_parent) + { +#ifdef HAVE_KF5 + config = KSharedConfig::openConfig(QStringLiteral("kbibtexrc")); + watcher = KConfigWatcher::create(config); + bibliographySystemDirtyFlag = true; + bibliographySystemCached = defaultBibliographySystem; +#endif // HAVE_KF5 + } +}; + +const QString Preferences::Private::keyBibliographySystem = QStringLiteral("BibliographySystem"); + +Preferences &Preferences::instance() +{ + static Preferences singleton; + return singleton; +} + +Preferences::Preferences() + : d(new Preferences::Private(this)) +{ +#ifdef HAVE_KF5 + QObject::connect(d->watcher.data(), &KConfigWatcher::configChanged, [this](const KConfigGroup & group, const QByteArrayList & names) { + QSet eventsToPublish; + if (group.name() == QStringLiteral("General")) { + if (names.contains(Preferences::Private::keyBibliographySystem.toLatin1())) { + qDebug() << "Bibliography system got changed by another Preferences instance"; + d->bibliographySystemDirtyFlag = true; + eventsToPublish.insert(NotificationHub::EventBibliographySystemChanged); + } + } + + for (const int eventId : eventsToPublish) + NotificationHub::publishEvent(eventId); + }); + +#endif // HAVE_KF5 +} + +Preferences::~Preferences() +{ + delete d; +} Preferences::BibliographySystem Preferences::bibliographySystem() { #ifdef HAVE_KF5 - static KSharedConfigPtr config(KSharedConfig::openConfig(QStringLiteral("kbibtexrc"))); - static const KConfigGroup configGroup(config, QStringLiteral("General")); - config->reparseConfiguration(); - const int index = configGroup.readEntry(QStringLiteral("BibliographySystem"), static_cast(defaultBibliographySystem)); - if (index != static_cast(Preferences::BibTeX) && index != static_cast(Preferences::BibLaTeX)) { - qWarning() << "Configuration file setting for Bibliography System has an invalid value, using default as fallback"; - setBibliographySystem(defaultBibliographySystem); - return defaultBibliographySystem; - } else - return static_cast(index); + if (d->bibliographySystemDirtyFlag) { + d->config->reparseConfiguration(); + static const KConfigGroup configGroup(d->config, QStringLiteral("General")); + const int index = configGroup.readEntry(Preferences::Private::keyBibliographySystem, static_cast(defaultBibliographySystem)); + if (index != static_cast(Preferences::BibTeX) && index != static_cast(Preferences::BibLaTeX)) { + qWarning() << "Configuration file setting for Bibliography System has an invalid value, using default as fallback"; + setBibliographySystem(defaultBibliographySystem); + d->bibliographySystemCached = defaultBibliographySystem; + } else + d->bibliographySystemCached = static_cast(index); + d->bibliographySystemDirtyFlag = false; + } + return d->bibliographySystemCached; #else // HAVE_KF5 return defaultBibliographySystem; #endif // HAVE_KF5 } bool Preferences::setBibliographySystem(const Preferences::BibliographySystem bibliographySystem) { #ifdef HAVE_KF5 - static KSharedConfigPtr config(KSharedConfig::openConfig(QStringLiteral("kbibtexrc"))); - static KConfigGroup configGroup(config, QStringLiteral("General")); - config->reparseConfiguration(); - const int prevIndex = configGroup.readEntry(QStringLiteral("BibliographySystem"), static_cast(defaultBibliographySystem)); + d->bibliographySystemDirtyFlag = false; + d->bibliographySystemCached = bibliographySystem; + static KConfigGroup configGroup(d->config, QStringLiteral("General")); + const int prevIndex = configGroup.readEntry(Preferences::Private::keyBibliographySystem, static_cast(defaultBibliographySystem)); const int newIndex = static_cast(bibliographySystem); if (prevIndex == newIndex) return false; /// If old and new bibliography system are the same, return 'false' directly - configGroup.writeEntry(QStringLiteral("BibliographySystem"), newIndex); - config->sync(); + configGroup.writeEntry(Preferences::Private::keyBibliographySystem, newIndex, KConfig::Notify /** to catch changes via KConfigWatcher */); + d->config->sync(); NotificationHub::publishEvent(NotificationHub::EventBibliographySystemChanged); #else // HAVE_KF5 Q_UNUSED(bibliographySystem); #endif // HAVE_KF5 return true; } const QMap Preferences::availableBibliographySystems() { static const QMap result {{Preferences::BibTeX, i18n("BibTeX")}, {Preferences::BibLaTeX, i18n("BibLaTeX")}}; return result; } diff --git a/src/global/preferences.h b/src/global/preferences.h index b24fd2e4..b18b5c02 100644 --- a/src/global/preferences.h +++ b/src/global/preferences.h @@ -1,84 +1,100 @@ /*************************************************************************** - * Copyright (C) 2004-2018 by Thomas Fischer * + * Copyright (C) 2004-2019 by Thomas Fischer * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, see . * ***************************************************************************/ #ifndef KBIBTEX_GLOBAL_PREFERENCES_H #define KBIBTEX_GLOBAL_PREFERENCES_H #include "kbibtex.h" /** @author Thomas Fischer */ class Preferences { public: + static Preferences &instance(); + ~Preferences(); + +protected: + Preferences(); + +public: + + /// *** Bibliography system, as of now either BibTeX or BibLaTeX /// Bibliography system: either BibTeX or BibLaTeX enum BibliographySystem { BibTeX = 0, BibLaTeX = 1 }; /// Default bibliography system if nothing else is set or defined static const BibliographySystem defaultBibliographySystem; - /// Current bibliography system as read from configuration file - static BibliographySystem bibliographySystem(); - /// Update bibliography system in configuration file - /// @return true if the set bibliography system differed from the previous value, false if both were the same - static bool setBibliographySystem(const BibliographySystem bibliographySystem); + /// Retrieve current bibliography system + BibliographySystem bibliographySystem(); + /// Set bibliography system + /// @return true if the set bibliography system is differed from the previous value, false if both were the same + bool setBibliographySystem(const BibliographySystem bibliographySystem); /// Map of supported bibliography systems, should be the same as in enum BibliographySystem static const QMap availableBibliographySystems(); enum BackupScope { NoBackup, LocalOnly, BothLocalAndRemote }; enum ElementDoubleClickAction { ActionOpenEditor = 0, ActionViewDocument = 1 }; /** * Preferences for File objects */ enum QuoteComment { qcNone = 0, qcCommand = 1, qcPercentSign = 2 }; static const QString groupColor; static const QString keyColorCodes; static const QStringList defaultColorCodes; static const QString keyColorLabels; static const QStringList defaultColorLabels; static const QString groupGeneral; static const QString keyBackupScope; static const BackupScope defaultBackupScope; static const QString keyNumberOfBackups; static const int defaultNumberOfBackups; static const QString groupUserInterface; static const QString keyElementDoubleClickAction; static const ElementDoubleClickAction defaultElementDoubleClickAction; static const QString keyEncoding; static const QString defaultEncoding; static const QString keyStringDelimiter; static const QString defaultStringDelimiter; static const QString keyQuoteComment; static const QuoteComment defaultQuoteComment; static const QString keyKeywordCasing; static const KBibTeX::Casing defaultKeywordCasing; static const QString keyProtectCasing; static const Qt::CheckState defaultProtectCasing; static const QString keyListSeparator; static const QString defaultListSeparator; static const QString keyPersonNameFormatting; static const QString personNameFormatLastFirst; static const QString personNameFormatFirstLast; static const QString defaultPersonNameFormatting; + + +private: + Q_DISABLE_COPY(Preferences) + + class Private; + Private *const d; }; #endif // KBIBTEX_GLOBAL_PREFERENCES_H diff --git a/src/gui/config/entrylayout.cpp b/src/gui/config/entrylayout.cpp index 7d10418e..f54dc5cb 100644 --- a/src/gui/config/entrylayout.cpp +++ b/src/gui/config/entrylayout.cpp @@ -1,141 +1,141 @@ /*************************************************************************** - * Copyright (C) 2004-2018 by Thomas Fischer * + * Copyright (C) 2004-2019 by Thomas Fischer * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, see . * ***************************************************************************/ #include "entrylayout.h" #include #include #include #include #include "preferences.h" #include "logging_gui.h" static const int entryLayoutMaxTabCount = 256; static const int entryLayoutMaxFieldPerTabCount = 256; class EntryLayout::EntryLayoutPrivate { public: EntryLayout *p; EntryLayoutPrivate(EntryLayout *parent) : p(parent) { /// nothing } static QString convert(KBibTeX::FieldInputType fil) { switch (fil) { case KBibTeX::SingleLine : return QStringLiteral("SingleLine"); case KBibTeX::MultiLine : return QStringLiteral("MultiLine"); case KBibTeX::List : return QStringLiteral("List"); case KBibTeX::URL : return QStringLiteral("URL"); case KBibTeX::Month : return QStringLiteral("Month"); case KBibTeX::Edition : return QStringLiteral("Edition"); case KBibTeX::Color : return QStringLiteral("Color"); case KBibTeX::PersonList : return QStringLiteral("PersonList"); case KBibTeX::KeywordList : return QStringLiteral("KeywordList"); case KBibTeX::CrossRef : return QStringLiteral("CrossRef"); case KBibTeX::StarRating : return QStringLiteral("StarRating"); case KBibTeX::UrlList : return QStringLiteral("UrlList"); } return QString(); } static KBibTeX::FieldInputType convert(const QString &text) { if (text == QStringLiteral("List")) return KBibTeX::List; else if (text == QStringLiteral("MultiLine")) return KBibTeX::MultiLine; else if (text == QStringLiteral("URL")) return KBibTeX::URL; else if (text == QStringLiteral("UrlList")) return KBibTeX::UrlList; else if (text == QStringLiteral("Month")) return KBibTeX::Month; else if (text == QStringLiteral("Edition")) return KBibTeX::Edition; else if (text == QStringLiteral("Color")) return KBibTeX::Color; else if (text == QStringLiteral("PersonList")) return KBibTeX::PersonList; else if (text == QStringLiteral("KeywordList")) return KBibTeX::KeywordList; else if (text == QStringLiteral("CrossRef")) return KBibTeX::CrossRef; else if (text == QStringLiteral("StarRating")) return KBibTeX::StarRating; else return KBibTeX::SingleLine; } }; EntryLayout::EntryLayout(const QString &style) : QVector >(), d(new EntryLayoutPrivate(this)) { load(style); } EntryLayout::~EntryLayout() { delete d; } const EntryLayout &EntryLayout::instance() { static const EntryLayout singletonBibTeX(QStringLiteral("bibtex")), singletonBibLaTeX(QStringLiteral("biblatex")); - return Preferences::bibliographySystem() == Preferences::BibLaTeX ? singletonBibLaTeX : singletonBibTeX; + return Preferences::instance().bibliographySystem() == Preferences::BibLaTeX ? singletonBibLaTeX : singletonBibTeX; } void EntryLayout::load(const QString &style) { clear(); const QString stylefile = QStringLiteral("kbibtex/") + style + QStringLiteral(".kbstyle"); KSharedConfigPtr layoutConfig = KSharedConfig::openConfig(stylefile, KConfig::FullConfig, QStandardPaths::GenericDataLocation); static const QString groupName = QStringLiteral("EntryLayoutTab"); const KConfigGroup configGroup(layoutConfig, groupName); const int tabCount = qMin(configGroup.readEntry("count", 0), entryLayoutMaxTabCount); for (int tab = 1; tab <= tabCount; ++tab) { const QString groupName = QString(QStringLiteral("EntryLayoutTab%1")).arg(tab); const KConfigGroup configGroup(layoutConfig, groupName); QSharedPointer etl = QSharedPointer(new EntryTabLayout); etl->uiCaption = i18n(configGroup.readEntry("uiCaption", QString()).toUtf8().constData()); etl->iconName = configGroup.readEntry("iconName", "entry"); etl->columns = configGroup.readEntry("columns", 1); if (etl->uiCaption.isEmpty()) continue; const int fieldCount = qMin(configGroup.readEntry("count", 0), entryLayoutMaxFieldPerTabCount); for (int field = 1; field <= fieldCount; ++field) { SingleFieldLayout sfl; sfl.bibtexLabel = configGroup.readEntry(QString(QStringLiteral("bibtexLabel%1")).arg(field), QString()); sfl.uiLabel = i18n(configGroup.readEntry(QString(QStringLiteral("uiLabel%1")).arg(field), QString()).toUtf8().constData()); sfl.fieldInputLayout = EntryLayoutPrivate::convert(configGroup.readEntry(QString(QStringLiteral("fieldInputLayout%1")).arg(field), "SingleLine")); if (sfl.bibtexLabel.isEmpty() || sfl.uiLabel.isEmpty()) continue; etl->singleFieldLayouts.append(sfl); } append(etl); } if (isEmpty()) qCWarning(LOG_KBIBTEX_GUI) << "List of entry layouts is empty"; } diff --git a/src/gui/element/elementwidgets.cpp b/src/gui/element/elementwidgets.cpp index f0e8a1b4..8049d7c7 100644 --- a/src/gui/element/elementwidgets.cpp +++ b/src/gui/element/elementwidgets.cpp @@ -1,1380 +1,1380 @@ /*************************************************************************** * Copyright (C) 2004-2019 by Thomas Fischer * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, see . * ***************************************************************************/ #include "elementwidgets.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "preferences.h" #include "idsuggestions.h" #include "fileinfo.h" #include "kbibtex.h" #include "bibtexentries.h" #include "bibtexfields.h" #include "fileimporterbibtex.h" #include "fileexporterbibtex.h" #include "file.h" #include "fieldinput.h" #include "entry.h" #include "macro.h" #include "preamble.h" #include "fieldlineedit.h" #include "delayedexecutiontimer.h" #include "logging_gui.h" static const unsigned int interColumnSpace = 16; static const char *PropertyIdSuggestion = "PropertyIdSuggestion"; ElementWidget::ElementWidget(QWidget *parent) : QWidget(parent), isReadOnly(false), m_file(nullptr), m_isModified(false) { /// nothing } bool ElementWidget::isModified() const { return m_isModified; } void ElementWidget::setModified(bool newIsModified) { m_isModified = newIsModified; emit modified(newIsModified); } void ElementWidget::gotModified() { setModified(true); } EntryConfiguredWidget::EntryConfiguredWidget(const QSharedPointer &entryTabLayout, QWidget *parent) : ElementWidget(parent), fieldInputCount(entryTabLayout->singleFieldLayouts.size()), numCols(entryTabLayout->columns), etl(entryTabLayout) { gridLayout = new QGridLayout(this); /// Initialize list of field input widgets plus labels listOfLabeledFieldInput = new LabeledFieldInput*[fieldInputCount]; createGUI(); } EntryConfiguredWidget::~EntryConfiguredWidget() { delete[] listOfLabeledFieldInput; } bool EntryConfiguredWidget::apply(QSharedPointer element) const { if (isReadOnly) return false; /// never save data if in read-only mode QSharedPointer entry = element.dynamicCast(); if (entry.isNull()) return false; for (QMap::ConstIterator it = bibtexKeyToWidget.constBegin(); it != bibtexKeyToWidget.constEnd(); ++it) { Value value; it.value()->apply(value); entry->remove(it.key()); if (!value.isEmpty()) entry->insert(it.key(), value); } return true; } bool EntryConfiguredWidget::reset(QSharedPointer element) { QSharedPointer entry = element.dynamicCast(); if (entry.isNull()) return false; /// clear all widgets for (QMap::Iterator it = bibtexKeyToWidget.begin(); it != bibtexKeyToWidget.end(); ++it) { it.value()->setFile(m_file); it.value()->clear(); } for (Entry::ConstIterator it = entry->constBegin(); it != entry->constEnd(); ++it) { const QString key = it.key().toLower(); if (bibtexKeyToWidget.contains(key)) { FieldInput *fieldInput = bibtexKeyToWidget[key]; fieldInput->setElement(element.data()); fieldInput->reset(it.value()); } } return true; } bool EntryConfiguredWidget::validate(QWidget **widgetWithIssue, QString &message) const { for (int i = fieldInputCount - 1; i >= 0; --i) { const bool v = listOfLabeledFieldInput[i]->fieldInput->validate(widgetWithIssue, message); if (!v) return false; } return true; } void EntryConfiguredWidget::showReqOptWidgets(bool forceVisible, const QString &entryType) { layoutGUI(forceVisible, entryType); } void EntryConfiguredWidget::setReadOnly(bool isReadOnly) { ElementWidget::setReadOnly(isReadOnly); for (QMap::Iterator it = bibtexKeyToWidget.begin(); it != bibtexKeyToWidget.end(); ++it) it.value()->setReadOnly(isReadOnly); } QString EntryConfiguredWidget::label() { return etl->uiCaption; } QIcon EntryConfiguredWidget::icon() { return QIcon::fromTheme(etl->iconName); } void EntryConfiguredWidget::setFile(const File *file) { for (QMap::Iterator it = bibtexKeyToWidget.begin(); it != bibtexKeyToWidget.end(); ++it) { it.value()->setFile(file); if (file != nullptr) { /// list of unique values for same field QStringList list = file->uniqueEntryValuesList(it.key()); /// for crossref fields, add all entries' ids if (it.key().toLower() == Entry::ftCrossRef) list.append(file->allKeys(File::etEntry)); /// add macro keys list.append(file->allKeys(File::etMacro)); it.value()->setCompletionItems(list); } } ElementWidget::setFile(file); } bool EntryConfiguredWidget::canEdit(const Element *element) { return Entry::isEntry(*element); } void EntryConfiguredWidget::createGUI() { int i = 0; for (const SingleFieldLayout &sfl : const_cast &>(etl->singleFieldLayouts)) { LabeledFieldInput *labeledFieldInput = new LabeledFieldInput; /// create an editing widget for this field const FieldDescription &fd = BibTeXFields::instance().find(sfl.bibtexLabel); labeledFieldInput->fieldInput = new FieldInput(sfl.fieldInputLayout, fd.preferredTypeFlag, fd.typeFlags, this); labeledFieldInput->fieldInput->setFieldKey(sfl.bibtexLabel); bibtexKeyToWidget.insert(sfl.bibtexLabel, labeledFieldInput->fieldInput); connect(labeledFieldInput->fieldInput, &FieldInput::modified, this, &EntryConfiguredWidget::gotModified); /// memorize if field input should grow vertically (e.g. is a list) labeledFieldInput->isVerticallyMinimumExpaning = sfl.fieldInputLayout == KBibTeX::MultiLine || sfl.fieldInputLayout == KBibTeX::List || sfl.fieldInputLayout == KBibTeX::PersonList || sfl.fieldInputLayout == KBibTeX::KeywordList; /// create a label next to the editing widget labeledFieldInput->label = new QLabel(QString(QStringLiteral("%1:")).arg(sfl.uiLabel), this); labeledFieldInput->label->setBuddy(labeledFieldInput->fieldInput->buddy()); /// align label's text vertically to match field input const Qt::Alignment horizontalAlignment = static_cast(labeledFieldInput->label->style()->styleHint(QStyle::SH_FormLayoutLabelAlignment)) & Qt::AlignHorizontal_Mask; labeledFieldInput->label->setAlignment(horizontalAlignment | (labeledFieldInput->isVerticallyMinimumExpaning ? Qt::AlignTop : Qt::AlignVCenter)); listOfLabeledFieldInput[i] = labeledFieldInput; ++i; } layoutGUI(true); } void EntryConfiguredWidget::layoutGUI(bool forceVisible, const QString &entryType) { QStringList visibleItems; if (!forceVisible && !entryType.isEmpty()) { const QString entryTypeLc = entryType.toLower(); for (const auto &ed : BibTeXEntries::instance()) { if (entryTypeLc == ed.upperCamelCase.toLower() || entryTypeLc == ed.upperCamelCaseAlt.toLower()) { /// this ugly conversion is necessary because we have a "^" (xor) and "|" (and/or) /// syntax to differentiate required items (not used yet, but will be used /// later if missing required items are marked). QString visible = ed.requiredItems.join(QStringLiteral(",")); visible += QLatin1Char(',') + ed.optionalItems.join(QStringLiteral(",")); visible = visible.replace(QLatin1Char('|'), QLatin1Char(',')).replace(QLatin1Char('^'), QLatin1Char(',')); visibleItems = visible.split(QStringLiteral(",")); break; } } } else if (!forceVisible) { // TODO is this an error condition? } /// variables to keep track which and how many field inputs will be visible int countVisible = 0; QScopedArrayPointer visible(new bool[fieldInputCount]); /// ... and if any field input is vertically expaning /// (e.g. a list, important for layout) bool anyoneVerticallyExpanding = false; for (int i = fieldInputCount - 1; i >= 0; --i) { listOfLabeledFieldInput[i]->label->setVisible(false); listOfLabeledFieldInput[i]->fieldInput->setVisible(false); gridLayout->removeWidget(listOfLabeledFieldInput[i]->label); gridLayout->removeWidget(listOfLabeledFieldInput[i]->fieldInput); const QString key = bibtexKeyToWidget.key(listOfLabeledFieldInput[i]->fieldInput).toLower(); const FieldDescription &fd = BibTeXFields::instance().find(key); Value value; listOfLabeledFieldInput[i]->fieldInput->apply(value); /// Hide non-required and non-optional type-dependent fields, /// except if the field has content visible[i] = forceVisible || fd.typeIndependent || !value.isEmpty() || visibleItems.contains(key); if (visible[i]) { ++countVisible; anyoneVerticallyExpanding |= listOfLabeledFieldInput[i]->isVerticallyMinimumExpaning; } } int numRows = countVisible / numCols; if (countVisible % numCols > 0) ++numRows; gridLayout->setRowStretch(numRows, anyoneVerticallyExpanding ? 0 : 1000); int col = 0, row = 0; for (int i = 0; i < fieldInputCount; ++i) if (visible[i]) { /// add label and field input to new position in grid layout gridLayout->addWidget(listOfLabeledFieldInput[i]->label, row, col * 3); gridLayout->addWidget(listOfLabeledFieldInput[i]->fieldInput, row, col * 3 + 1); /// set row stretch gridLayout->setRowStretch(row, listOfLabeledFieldInput[i]->isVerticallyMinimumExpaning ? 1000 : 0); /// set column stretch and spacing gridLayout->setColumnStretch(col * 3, 1); gridLayout->setColumnStretch(col * 3 + 1, 1000); if (col > 0) gridLayout->setColumnMinimumWidth(col * 3 - 1, interColumnSpace); /// count rows and columns correctly ++row; if (row >= numRows) { row = 0; ++col; } /// finally, set label and field input visible again listOfLabeledFieldInput[i]->label->setVisible(true); listOfLabeledFieldInput[i]->fieldInput->setVisible(true); // FIXME expensive! } if (countVisible > 0) { /// fix row stretch for (int i = numRows + 1; i < 100; ++i) gridLayout->setRowStretch(i, 0); /// hide unused columns for (int i = (col + (row == 0 ? 0 : 1)) * 3 - 1; i < 100; ++i) { gridLayout->setColumnMinimumWidth(i, 0); gridLayout->setColumnStretch(i, 0); } } } ReferenceWidget::ReferenceWidget(QWidget *parent) : ElementWidget(parent), m_applyElement(nullptr), m_entryIdManuallySet(false), m_element(QSharedPointer()) { createGUI(); } bool ReferenceWidget::apply(QSharedPointer element) const { if (isReadOnly) return false; /// never save data if in read-only mode bool result = false; QSharedPointer entry = element.dynamicCast(); if (!entry.isNull()) { entry->setType(computeType()); entry->setId(entryId->text()); result = true; } else { QSharedPointer macro = element.dynamicCast(); if (!macro.isNull()) { macro->setKey(entryId->text()); result = true; } } return result; } bool ReferenceWidget::reset(QSharedPointer element) { /// if signals are not deactivated, the "modified" signal would be emitted when /// resetting the widgets' values disconnect(entryType->lineEdit(), &KLineEdit::textChanged, this, &ReferenceWidget::gotModified); disconnect(entryId, &KLineEdit::textEdited, this, &ReferenceWidget::entryIdManuallyChanged); bool result = false; QSharedPointer entry = element.dynamicCast(); if (!entry.isNull()) { entryType->setEnabled(!isReadOnly); buttonSuggestId->setEnabled(!isReadOnly); QString type = BibTeXEntries::instance().format(entry->type(), KBibTeX::cUpperCamelCase); int index = entryType->findData(type); if (index == -1) { const QString typeLower(type.toLower()); for (const auto &ed : BibTeXEntries::instance()) if (typeLower == ed.upperCamelCaseAlt.toLower()) { index = entryType->findData(ed.upperCamelCase); break; } } entryType->setCurrentIndex(index); if (index == -1) { /// A customized value not known to KBibTeX entryType->lineEdit()->setText(type); } entryId->setText(entry->id()); /// New entries have no values. Use this fact /// to recognize new entries, for which it is /// allowed to automatic set their ids /// if a default id suggestion had been specified. m_entryIdManuallySet = entry->count() > 0; result = true; } else { entryType->setEnabled(false); buttonSuggestId->setEnabled(false); QSharedPointer macro = element.dynamicCast(); if (!macro.isNull()) { entryType->lineEdit()->setText(i18n("Macro")); entryId->setText(macro->key()); result = true; } } connect(entryId, &KLineEdit::textEdited, this, &ReferenceWidget::entryIdManuallyChanged); connect(entryType->lineEdit(), &KLineEdit::textChanged, this, &ReferenceWidget::gotModified); return result; } bool ReferenceWidget::validate(QWidget **widgetWithIssue, QString &message) const { message.clear(); static const QRegularExpression validTypeRegExp(QStringLiteral("^[a-z]+$"), QRegularExpression::CaseInsensitiveOption); const QString type = computeType(); const QRegularExpressionMatch validTypeMatch = validTypeRegExp.match(type); if (!validTypeMatch.hasMatch() || validTypeMatch.capturedLength() != type.length()) { if (widgetWithIssue != nullptr) *widgetWithIssue = entryType; message = i18n("Element type '%1' is invalid.", type); return false; } static const QRegularExpression validIdRegExp(QStringLiteral("^[a-z0-9][a-z0-9_:.+/$\\\"&-]*$"), QRegularExpression::CaseInsensitiveOption); const QString id = entryId->text(); const QRegularExpressionMatch validIdMatch = validIdRegExp.match(id); if (!validIdMatch.hasMatch() || validIdMatch.capturedLength() != id.length()) { if (widgetWithIssue != nullptr) *widgetWithIssue = entryId; message = i18n("Id '%1' is invalid", id); return false; } return true; } void ReferenceWidget::setReadOnly(bool isReadOnly) { ElementWidget::setReadOnly(isReadOnly); entryId->setReadOnly(isReadOnly); entryType->setEnabled(!isReadOnly); } QString ReferenceWidget::label() { return QString(); } QIcon ReferenceWidget::icon() { return QIcon(); } bool ReferenceWidget::canEdit(const Element *element) { return Entry::isEntry(*element) || Macro::isMacro(*element); } void ReferenceWidget::setOriginalElement(const QSharedPointer &orig) { m_element = orig; } QString ReferenceWidget::currentId() const { return entryId->text(); } void ReferenceWidget::setCurrentId(const QString &newId) { entryId->setText(newId); } void ReferenceWidget::createGUI() { QHBoxLayout *layout = new QHBoxLayout(this); entryType = new KComboBox(this); entryType->setEditable(true); entryType->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Preferred); QLabel *label = new QLabel(i18n("Type:"), this); label->setBuddy(entryType); layout->addWidget(label); layout->addWidget(entryType); layout->addSpacing(interColumnSpace); entryId = new KLineEdit(this); entryId->setClearButtonEnabled(true); label = new QLabel(i18n("Id:"), this); label->setBuddy(entryId); layout->addWidget(label); layout->addWidget(entryId); for (const auto &ed : BibTeXEntries::instance()) entryType->addItem(ed.label, ed.upperCamelCase); /// Sort the combo box locale-aware. Thus we need a SortFilterProxyModel QSortFilterProxyModel *proxy = new QSortFilterProxyModel(entryType); proxy->setSortLocaleAware(true); proxy->setSourceModel(entryType->model()); entryType->model()->setParent(proxy); entryType->setModel(proxy); entryType->model()->sort(0); /// Button with a menu listing a set of preconfigured id suggestions buttonSuggestId = new QPushButton(QIcon::fromTheme(QStringLiteral("view-filter")), QString(), this); buttonSuggestId->setToolTip(i18n("Select a suggested id for this entry")); layout->addWidget(buttonSuggestId); QMenu *suggestionsMenu = new QMenu(buttonSuggestId); buttonSuggestId->setMenu(suggestionsMenu); connect(entryType->lineEdit(), &KLineEdit::textChanged, this, &ReferenceWidget::gotModified); connect(entryId, &KLineEdit::textEdited, this, &ReferenceWidget::entryIdManuallyChanged); connect(entryType->lineEdit(), &KLineEdit::textChanged, this, &ReferenceWidget::entryTypeChanged); connect(suggestionsMenu, &QMenu::aboutToShow, this, &ReferenceWidget::prepareSuggestionsMenu); } void ReferenceWidget::prepareSuggestionsMenu() { /// Collect information on the current entry as it is edited QSharedPointer guiDataEntry(new Entry()); m_applyElement->apply(guiDataEntry); QSharedPointer crossrefResolvedEntry(guiDataEntry->resolveCrossref(m_file)); static const IdSuggestions *idSuggestions = new IdSuggestions(); QMenu *suggestionsMenu = buttonSuggestId->menu(); suggestionsMenu->clear(); /// Keep track of shown suggestions to avoid duplicates QSet knownIdSuggestion; const QString defaultSuggestion = idSuggestions->defaultFormatId(*crossrefResolvedEntry.data()); const auto formatIdList = idSuggestions->formatIdList(*crossrefResolvedEntry.data()); for (const QString &suggestionBase : formatIdList) { bool isDefault = suggestionBase == defaultSuggestion; QString suggestion = suggestionBase; /// Test for duplicate ids, use fallback ids with numeric suffix if (m_file != nullptr && m_file->containsKey(suggestion)) { int suffix = 2; while (m_file->containsKey(suggestion = suggestionBase + QChar('_') + QString::number(suffix))) ++suffix; } /// Keep track of shown suggestions to avoid duplicates if (knownIdSuggestion.contains(suggestion)) continue; else knownIdSuggestion.insert(suggestion); /// Create action for suggestion, use icon depending if default or not QAction *suggestionAction = new QAction(suggestion, suggestionsMenu); suggestionAction->setIcon(QIcon::fromTheme(isDefault ? QStringLiteral("favorites") : QStringLiteral("view-filter"))); /// Mesh action into GUI suggestionsMenu->addAction(suggestionAction); connect(suggestionAction, &QAction::triggered, this, &ReferenceWidget::insertSuggestionFromAction); /// Remember suggestion string for time when action gets triggered suggestionAction->setProperty(PropertyIdSuggestion, suggestion); } } void ReferenceWidget::insertSuggestionFromAction() { QAction *action = qobject_cast(sender()); if (action != nullptr) { const QString suggestion = action->property(PropertyIdSuggestion).toString(); entryId->setText(suggestion); } } void ReferenceWidget::entryIdManuallyChanged() { m_entryIdManuallySet = true; gotModified(); } void ReferenceWidget::setEntryIdByDefault() { if (isReadOnly) { /// Never set the suggestion automatically if in read-only mode return; } if (m_entryIdManuallySet) { /// If user changed entry id manually, /// do not overwrite it by a default value return; } static const IdSuggestions *idSuggestions = new IdSuggestions(); /// If there is a default suggestion format set ... if (idSuggestions->hasDefaultFormat()) { /// Collect information on the current entry as it is edited QSharedPointer guiDataEntry(new Entry()); m_applyElement->apply(guiDataEntry); QSharedPointer crossrefResolvedEntry(guiDataEntry->resolveCrossref(m_file)); /// Determine default suggestion based on current data const QString defaultSuggestion = idSuggestions->defaultFormatId(*crossrefResolvedEntry.data()); if (!defaultSuggestion.isEmpty()) { disconnect(entryId, &KLineEdit::textEdited, this, &ReferenceWidget::entryIdManuallyChanged); /// Apply default suggestion to widget entryId->setText(defaultSuggestion); connect(entryId, &KLineEdit::textEdited, this, &ReferenceWidget::entryIdManuallyChanged); } } } QString ReferenceWidget::computeType() const { if (entryType->currentIndex() < 0 || entryType->lineEdit()->isModified()) return BibTeXEntries::instance().format(entryType->lineEdit()->text(), KBibTeX::cUpperCamelCase); else return entryType->itemData(entryType->currentIndex()).toString(); } FilesWidget::FilesWidget(QWidget *parent) : ElementWidget(parent) { QVBoxLayout *layout = new QVBoxLayout(this); fileList = new FieldInput(KBibTeX::UrlList, KBibTeX::tfVerbatim /* eventually ignored, see constructor of UrlListEdit */, KBibTeX::tfVerbatim /* eventually ignored, see constructor of UrlListEdit */, this); fileList->setFieldKey(QStringLiteral("^external")); layout->addWidget(fileList); connect(fileList, &FieldInput::modified, this, &FilesWidget::gotModified); } bool FilesWidget::apply(QSharedPointer element) const { if (isReadOnly) return false; /// never save data if in read-only mode QSharedPointer entry = element.dynamicCast(); if (entry.isNull()) return false; for (const QString &keyStem : keyStart) for (int i = 1; i < 32; ++i) { /// FIXME replace number by constant const QString key = i > 1 ? keyStem + QString::number(i) : keyStem; entry->remove(key); } Value combinedValue; fileList->apply(combinedValue); Value urlValue, doiValue, localFileValue; urlValue.reserve(combinedValue.size()); doiValue.reserve(combinedValue.size()); localFileValue.reserve(combinedValue.size()); for (const auto &valueItem : const_cast(combinedValue)) { const QSharedPointer verbatimText = valueItem.dynamicCast(); if (!verbatimText.isNull()) { const QString text = verbatimText->text(); QRegularExpressionMatch match; if ((match = KBibTeX::urlRegExp.match(text)).hasMatch()) { /// add full URL VerbatimText *newVT = new VerbatimText(match.captured(0)); /// test for duplicates if (urlValue.contains(*newVT)) delete newVT; else urlValue.append(QSharedPointer(newVT)); } else if ((match = KBibTeX::doiRegExp.match(text)).hasMatch()) { /// add DOI VerbatimText *newVT = new VerbatimText(match.captured(0)); /// test for duplicates if (doiValue.contains(*newVT)) delete newVT; else doiValue.append(QSharedPointer(newVT)); } else { /// add anything else (e.g. local file) VerbatimText *newVT = new VerbatimText(*verbatimText); /// test for duplicates if (localFileValue.contains(*newVT)) delete newVT; else localFileValue.append(QSharedPointer(newVT)); } } } if (urlValue.isEmpty()) entry->remove(Entry::ftUrl); else entry->insert(Entry::ftUrl, urlValue); if (localFileValue.isEmpty()) { entry->remove(Entry::ftFile); entry->remove(Entry::ftLocalFile); - } else if (Preferences::bibliographySystem() == Preferences::BibLaTeX) { + } else if (Preferences::instance().bibliographySystem() == Preferences::BibLaTeX) { entry->remove(Entry::ftLocalFile); entry->insert(Entry::ftFile, localFileValue); - } else if (Preferences::bibliographySystem() == Preferences::BibTeX) { + } else if (Preferences::instance().bibliographySystem() == Preferences::BibTeX) { entry->remove(Entry::ftFile); entry->insert(Entry::ftLocalFile, localFileValue); } if (doiValue.isEmpty()) entry->remove(Entry::ftDOI); else entry->insert(Entry::ftDOI, doiValue); return true; } bool FilesWidget::reset(QSharedPointer element) { QSharedPointer entry = element.dynamicCast(); if (entry.isNull()) return false; Value combinedValue; for (const QString &keyStem : keyStart) for (int i = 1; i < 32; ++i) { /// FIXME replace number by constant const QString key = i > 1 ? keyStem + QString::number(i) : keyStem; const Value &value = entry->operator [](key); for (const auto &valueItem : const_cast(value)) combinedValue.append(valueItem); } fileList->setElement(element.data()); fileList->reset(combinedValue); return true; } bool FilesWidget::validate(QWidget **widgetWithIssue, QString &message) const { return fileList->validate(widgetWithIssue, message); } void FilesWidget::setReadOnly(bool isReadOnly) { ElementWidget::setReadOnly(isReadOnly); fileList->setReadOnly(isReadOnly); } QString FilesWidget::label() { return i18n("External"); } QIcon FilesWidget::icon() { return QIcon::fromTheme(QStringLiteral("emblem-symbolic-link")); } void FilesWidget::setFile(const File *file) { ElementWidget::setFile(file); fileList->setFile(file); } bool FilesWidget::canEdit(const Element *element) { return Entry::isEntry(*element); } const QStringList FilesWidget::keyStart {Entry::ftUrl, QStringLiteral("postscript"), Entry::ftLocalFile, Entry::ftDOI, Entry::ftFile, QStringLiteral("ee"), QStringLiteral("biburl")}; OtherFieldsWidget::OtherFieldsWidget(const QStringList &blacklistedFields, QWidget *parent) : ElementWidget(parent), blackListed(blacklistedFields) { internalEntry = QSharedPointer(new Entry()); createGUI(); } OtherFieldsWidget::~OtherFieldsWidget() { delete fieldContent; } bool OtherFieldsWidget::apply(QSharedPointer element) const { if (isReadOnly) return false; /// never save data if in read-only mode QSharedPointer entry = element.dynamicCast(); if (entry.isNull()) return false; for (const QString &key : const_cast(deletedKeys)) entry->remove(key); for (const QString &key : const_cast(modifiedKeys)) { entry->remove(key); entry->insert(key, internalEntry->value(key)); } return true; } bool OtherFieldsWidget::reset(QSharedPointer element) { QSharedPointer entry = element.dynamicCast(); if (entry.isNull()) return false; internalEntry = QSharedPointer(new Entry(*entry.data())); deletedKeys.clear(); // FIXME clearing list may be premature here... modifiedKeys.clear(); // FIXME clearing list may be premature here... updateList(); updateGUI(); return true; } bool OtherFieldsWidget::validate(QWidget **, QString &) const { /// No checks to make here; all actual check will be conducted in actionAddApply(..) return true; } void OtherFieldsWidget::setReadOnly(bool isReadOnly) { ElementWidget::setReadOnly(isReadOnly); fieldName->setReadOnly(isReadOnly); fieldContent->setReadOnly(isReadOnly); /// will take care of enabled/disabling buttons updateGUI(); updateList(); } QString OtherFieldsWidget::label() { return i18n("Other Fields"); } QIcon OtherFieldsWidget::icon() { return QIcon::fromTheme(QStringLiteral("other")); } bool OtherFieldsWidget::canEdit(const Element *element) { return Entry::isEntry(*element); } void OtherFieldsWidget::listElementExecuted(QTreeWidgetItem *item, int column) { Q_UNUSED(column) /// we do not care which column got clicked QString key = item->text(0); fieldName->setText(key); fieldContent->reset(internalEntry->value(key)); } void OtherFieldsWidget::listCurrentChanged(QTreeWidgetItem *item, QTreeWidgetItem *previous) { Q_UNUSED(previous) bool validUrl = false; bool somethingSelected = item != nullptr; buttonDelete->setEnabled(somethingSelected && !isReadOnly); if (somethingSelected) { currentUrl = QUrl(item->text(1)); validUrl = currentUrl.isValid() && currentUrl.isLocalFile() & QFileInfo::exists(currentUrl.toLocalFile()); if (!validUrl) { const QRegularExpressionMatch urlRegExpMatch = KBibTeX::urlRegExp.match(item->text(1)); if (urlRegExpMatch.hasMatch()) { currentUrl = QUrl(urlRegExpMatch.captured(0)); validUrl = currentUrl.isValid(); buttonOpen->setEnabled(validUrl); } } } if (!validUrl) currentUrl = QUrl(); buttonOpen->setEnabled(validUrl); } void OtherFieldsWidget::actionAddApply() { if (isReadOnly) return; /// never modify anything if in read-only mode QString key = fieldName->text(), message; Value value; if (!fieldContent->validate(nullptr, message)) return; ///< invalid values should not get applied if (!fieldContent->apply(value)) return; if (internalEntry->contains(key)) internalEntry->remove(key); internalEntry->insert(key, value); if (!modifiedKeys.contains(key)) modifiedKeys << key; updateList(); updateGUI(); gotModified(); } void OtherFieldsWidget::actionDelete() { if (isReadOnly) return; /// never modify anything if in read-only mode Q_ASSERT_X(otherFieldsList->currentItem() != nullptr, "OtherFieldsWidget::actionDelete", "otherFieldsList->currentItem() is NULL"); QString key = otherFieldsList->currentItem()->text(0); if (!deletedKeys.contains(key)) deletedKeys << key; internalEntry->remove(key); updateList(); updateGUI(); listCurrentChanged(otherFieldsList->currentItem(), nullptr); gotModified(); } void OtherFieldsWidget::actionOpen() { if (currentUrl.isValid()) { /// Guess mime type for url to open QMimeType mimeType = FileInfo::mimeTypeForUrl(currentUrl); const QString mimeTypeName = mimeType.name(); /// Ask KDE subsystem to open url in viewer matching mime type #if KIO_VERSION < 0x051f00 // < 5.31.0 KRun::runUrl(currentUrl, mimeTypeName, this, false, false); #else // KIO_VERSION < 0x051f00 // >= 5.31.0 KRun::runUrl(currentUrl, mimeTypeName, this, KRun::RunFlags()); #endif // KIO_VERSION < 0x051f00 } } void OtherFieldsWidget::createGUI() { QGridLayout *layout = new QGridLayout(this); /// set row and column stretches based on chosen layout layout->setColumnStretch(0, 0); layout->setColumnStretch(1, 1); layout->setColumnStretch(2, 0); layout->setRowStretch(0, 0); layout->setRowStretch(1, 1); layout->setRowStretch(2, 0); layout->setRowStretch(3, 0); layout->setRowStretch(4, 1); QLabel *label = new QLabel(i18n("Name:"), this); layout->addWidget(label, 0, 0, 1, 1); label->setAlignment(static_cast(label->style()->styleHint(QStyle::SH_FormLayoutLabelAlignment))); fieldName = new KLineEdit(this); layout->addWidget(fieldName, 0, 1, 1, 1); label->setBuddy(fieldName); buttonAddApply = new QPushButton(QIcon::fromTheme(QStringLiteral("list-add")), i18n("Add"), this); buttonAddApply->setEnabled(false); layout->addWidget(buttonAddApply, 0, 2, 1, 1); label = new QLabel(i18n("Content:"), this); layout->addWidget(label, 1, 0, 1, 1); label->setAlignment(static_cast(label->style()->styleHint(QStyle::SH_FormLayoutLabelAlignment))); fieldContent = new FieldInput(KBibTeX::MultiLine, KBibTeX::tfSource, KBibTeX::tfSource, this); layout->addWidget(fieldContent, 1, 1, 1, 2); label->setBuddy(fieldContent->buddy()); label = new QLabel(i18n("List:"), this); layout->addWidget(label, 2, 0, 1, 1); label->setAlignment(static_cast(label->style()->styleHint(QStyle::SH_FormLayoutLabelAlignment))); otherFieldsList = new QTreeWidget(this); otherFieldsList->setHeaderLabels(QStringList {i18n("Key"), i18n("Value")}); otherFieldsList->setRootIsDecorated(false); layout->addWidget(otherFieldsList, 2, 1, 3, 1); label->setBuddy(otherFieldsList); buttonDelete = new QPushButton(QIcon::fromTheme(QStringLiteral("list-remove")), i18n("Delete"), this); buttonDelete->setEnabled(false); layout->addWidget(buttonDelete, 2, 2, 1, 1); buttonOpen = new QPushButton(QIcon::fromTheme(QStringLiteral("document-open")), i18n("Open"), this); buttonOpen->setEnabled(false); layout->addWidget(buttonOpen, 3, 2, 1, 1); connect(otherFieldsList, &QTreeWidget::itemActivated, this, &OtherFieldsWidget::listElementExecuted); connect(otherFieldsList, &QTreeWidget::currentItemChanged, this, &OtherFieldsWidget::listCurrentChanged); connect(otherFieldsList, &QTreeWidget::itemSelectionChanged, this, &OtherFieldsWidget::updateGUI); connect(fieldName, &KLineEdit::textEdited, this, &OtherFieldsWidget::updateGUI); connect(buttonAddApply, &QPushButton::clicked, this, &OtherFieldsWidget::actionAddApply); connect(buttonDelete, &QPushButton::clicked, this, &OtherFieldsWidget::actionDelete); connect(buttonOpen, &QPushButton::clicked, this, &OtherFieldsWidget::actionOpen); } void OtherFieldsWidget::updateList() { const QString selText = otherFieldsList->selectedItems().isEmpty() ? QString() : otherFieldsList->selectedItems().first()->text(0); const QString curText = otherFieldsList->currentItem() == nullptr ? QString() : otherFieldsList->currentItem()->text(0); otherFieldsList->clear(); for (Entry::ConstIterator it = internalEntry->constBegin(); it != internalEntry->constEnd(); ++it) if (!blackListed.contains(it.key().toLower())) { QTreeWidgetItem *item = new QTreeWidgetItem(); item->setText(0, it.key()); item->setText(1, PlainTextValue::text(it.value())); item->setIcon(0, QIcon::fromTheme(QStringLiteral("entry"))); // FIXME otherFieldsList->addTopLevelItem(item); item->setSelected(selText == it.key()); if (it.key() == curText) otherFieldsList->setCurrentItem(item); } } void OtherFieldsWidget::updateGUI() { QString key = fieldName->text(); if (key.isEmpty() || blackListed.contains(key, Qt::CaseInsensitive)) // TODO check for more (e.g. spaces) buttonAddApply->setEnabled(false); else { buttonAddApply->setEnabled(!isReadOnly); buttonAddApply->setText(internalEntry->contains(key) ? i18n("Apply") : i18n("Add")); buttonAddApply->setIcon(internalEntry->contains(key) ? QIcon::fromTheme(QStringLiteral("document-edit")) : QIcon::fromTheme(QStringLiteral("list-add"))); } } MacroWidget::MacroWidget(QWidget *parent) : ElementWidget(parent) { createGUI(); } MacroWidget::~MacroWidget() { delete fieldInputValue; } bool MacroWidget::apply(QSharedPointer element) const { if (isReadOnly) return false; /// never save data if in read-only mode QSharedPointer macro = element.dynamicCast(); if (macro.isNull()) return false; Value value; bool result = fieldInputValue->apply(value); macro->setValue(value); return result; } bool MacroWidget::reset(QSharedPointer element) { QSharedPointer macro = element.dynamicCast(); if (macro.isNull()) return false; return fieldInputValue->reset(macro->value()); } bool MacroWidget::validate(QWidget **widgetWithIssue, QString &message) const { return fieldInputValue->validate(widgetWithIssue, message); } void MacroWidget::setReadOnly(bool isReadOnly) { ElementWidget::setReadOnly(isReadOnly); fieldInputValue->setReadOnly(isReadOnly); } QString MacroWidget::label() { return i18n("Macro"); } QIcon MacroWidget::icon() { return QIcon::fromTheme(QStringLiteral("macro")); } bool MacroWidget::canEdit(const Element *element) { return Macro::isMacro(*element); } void MacroWidget::createGUI() { QBoxLayout *layout = new QHBoxLayout(this); QLabel *label = new QLabel(i18n("Value:"), this); layout->addWidget(label, 0); label->setAlignment(static_cast(label->style()->styleHint(QStyle::SH_FormLayoutLabelAlignment))); fieldInputValue = new FieldInput(KBibTeX::MultiLine, KBibTeX::tfPlainText, KBibTeX::tfPlainText | KBibTeX::tfSource, this); layout->addWidget(fieldInputValue, 1); label->setBuddy(fieldInputValue->buddy()); connect(fieldInputValue, &FieldInput::modified, this, &MacroWidget::gotModified); } PreambleWidget::PreambleWidget(QWidget *parent) : ElementWidget(parent) { createGUI(); } bool PreambleWidget::apply(QSharedPointer element) const { if (isReadOnly) return false; /// never save data if in read-only mode QSharedPointer preamble = element.dynamicCast(); if (preamble.isNull()) return false; Value value; bool result = fieldInputValue->apply(value); preamble->setValue(value); return result; } bool PreambleWidget::reset(QSharedPointer element) { QSharedPointer preamble = element.dynamicCast(); if (preamble.isNull()) return false; return fieldInputValue->reset(preamble->value()); } bool PreambleWidget::validate(QWidget **widgetWithIssue, QString &message) const { return fieldInputValue->validate(widgetWithIssue, message); } void PreambleWidget::setReadOnly(bool isReadOnly) { ElementWidget::setReadOnly(isReadOnly); fieldInputValue->setReadOnly(isReadOnly); } QString PreambleWidget::label() { return i18n("Preamble"); } QIcon PreambleWidget::icon() { return QIcon::fromTheme(QStringLiteral("preamble")); } bool PreambleWidget::canEdit(const Element *element) { return Preamble::isPreamble(*element); } void PreambleWidget::createGUI() { QBoxLayout *layout = new QHBoxLayout(this); QLabel *label = new QLabel(i18n("Value:"), this); layout->addWidget(label, 0); label->setAlignment(static_cast(label->style()->styleHint(QStyle::SH_FormLayoutLabelAlignment))); fieldInputValue = new FieldInput(KBibTeX::MultiLine, KBibTeX::tfSource, KBibTeX::tfSource, this); // FIXME: other editing modes beyond Source applicable? layout->addWidget(fieldInputValue, 1); label->setBuddy(fieldInputValue->buddy()); connect(fieldInputValue, &FieldInput::modified, this, &PreambleWidget::gotModified); } class SourceWidget::Private { public: KComboBox *messages; QPushButton *buttonRestore; FileImporterBibTeX *importerBibTeX; DelayedExecutionTimer *delayedExecutionTimer; Private(SourceWidget *parent) : messages(nullptr), buttonRestore(nullptr), importerBibTeX(new FileImporterBibTeX(parent)), delayedExecutionTimer(new DelayedExecutionTimer(1500, 500, parent)) { /// nothing } void addMessage(const FileImporter::MessageSeverity severity, const QString &messageText) { const QIcon icon = severity == FileImporter::SeverityInfo ? QIcon::fromTheme(QStringLiteral("dialog-information")) : (severity == FileImporter::SeverityWarning ? QIcon::fromTheme(QStringLiteral("dialog-warning")) : (severity == FileImporter::SeverityError ? QIcon::fromTheme(QStringLiteral("dialog-error")) : QIcon::fromTheme(QStringLiteral("dialog-question")))); messages->addItem(icon, messageText); } }; SourceWidget::SourceWidget(QWidget *parent) : ElementWidget(parent), elementClass(elementInvalid), d(new SourceWidget::Private(this)) { createGUI(); connect(document, &KTextEditor::Document::textChanged, d->delayedExecutionTimer, &DelayedExecutionTimer::trigger); connect(document, &KTextEditor::Document::textChanged, d->messages, &KComboBox::clear); connect(d->delayedExecutionTimer, &DelayedExecutionTimer::triggered, this, &SourceWidget::updateMessage); } SourceWidget::~SourceWidget() { delete document; delete d; } void SourceWidget::setElementClass(ElementClass elementClass) { this->elementClass = elementClass; updateMessage(); } bool SourceWidget::apply(QSharedPointer element) const { if (isReadOnly) return false; ///< never save data if in read-only mode const QString text = document->text(); const QScopedPointer file(d->importerBibTeX->fromString(text)); if (file.isNull() || file->count() != 1) return false; QSharedPointer entry = element.dynamicCast(); QSharedPointer readEntry = file->first().dynamicCast(); if (!readEntry.isNull() && !entry.isNull()) { if (elementClass != elementEntry) return false; ///< Source widget should only edit Entry objects entry->operator =(*readEntry.data()); //entry = readEntry; return true; } else { QSharedPointer macro = element.dynamicCast(); QSharedPointer readMacro = file->first().dynamicCast(); if (!readMacro.isNull() && !macro.isNull()) { if (elementClass != elementMacro) return false; ///< Source widget should only edit Macro objects macro->operator =(*readMacro.data()); return true; } else { QSharedPointer preamble = element.dynamicCast(); QSharedPointer readPreamble = file->first().dynamicCast(); if (!readPreamble.isNull() && !preamble.isNull()) { if (elementClass != elementPreamble) return false; ///< Source widget should only edit Preamble objects preamble->operator =(*readPreamble.data()); return true; } else { qCWarning(LOG_KBIBTEX_GUI) << "Do not know how to apply source code"; return false; } } } } bool SourceWidget::reset(QSharedPointer element) { /// if signals are not deactivated, the "modified" signal would be emitted when /// resetting the widget's value disconnect(document, &KTextEditor::Document::textChanged, this, &SourceWidget::gotModified); FileExporterBibTeX exporter(this); exporter.setEncoding(QStringLiteral("utf-8")); const QString exportedText = exporter.toString(element, m_file); if (!exportedText.isEmpty()) { originalText = exportedText; /// Limitation of KTextEditor: If editor is read-only, no text can be set /// Therefore, temporarily lift read-only status const bool originalReadWriteStatus = document->isReadWrite(); document->setReadWrite(true); const bool settingTextSuccessful = document->setText(originalText); if (!settingTextSuccessful) qCWarning(LOG_KBIBTEX_GUI) << "Could not set BibTeX source code to source editor"; document->setReadWrite(originalReadWriteStatus); } else qCWarning(LOG_KBIBTEX_GUI) << "Converting entry to BibTeX source resulting in empty text"; connect(document, &KTextEditor::Document::textChanged, this, &SourceWidget::gotModified); return !exportedText.isEmpty(); } bool SourceWidget::validate(QWidget **widgetWithIssue, QString &message) const { message.clear(); d->messages->clear(); const QString text = document->text(); connect(d->importerBibTeX, &FileImporterBibTeX::message, this, &SourceWidget::addMessage); const QScopedPointer file(d->importerBibTeX->fromString(text)); disconnect(d->importerBibTeX, &FileImporterBibTeX::message, this, &SourceWidget::addMessage); if (file.isNull() || file->count() != 1) { if (widgetWithIssue != nullptr) *widgetWithIssue = document->views().first(); ///< We create one view initially, so this should never fail message = i18n("Given source code does not parse as one single BibTeX element."); return false; } bool result = false; switch (elementClass) { case elementEntry: { QSharedPointer entry = file->first().dynamicCast(); result = !entry.isNull(); if (!result) message = i18n("Given source code does not parse as one single BibTeX entry."); } break; case elementMacro: { QSharedPointer macro = file->first().dynamicCast(); result = !macro.isNull(); if (!result) message = i18n("Given source code does not parse as one single BibTeX macro."); } break; case elementPreamble: { QSharedPointer preamble = file->first().dynamicCast(); result = !preamble.isNull(); if (!result) message = i18n("Given source code does not parse as one single BibTeX preamble."); } break; // case elementComment // TODO? default: message = QString(QStringLiteral("elementClass is unknown: %1")).arg(elementClass); result = false; } if (!result && widgetWithIssue != nullptr) *widgetWithIssue = document->views().first(); ///< We create one view initially, so this should never fail if (message.isEmpty() && d->messages->count() == 0) d->addMessage(FileImporter::SeverityInfo, i18n("No issues detected")); return result; } void SourceWidget::setReadOnly(bool isReadOnly) { ElementWidget::setReadOnly(isReadOnly); d->buttonRestore->setEnabled(!isReadOnly); document->setReadWrite(!isReadOnly); } QString SourceWidget::label() { return i18n("Source"); } QIcon SourceWidget::icon() { return QIcon::fromTheme(QStringLiteral("code-context")); } bool SourceWidget::canEdit(const Element *element) { Q_UNUSED(element) return true; /// source widget should be able to edit any element } void SourceWidget::createGUI() { QGridLayout *layout = new QGridLayout(this); layout->setColumnStretch(0, 1); layout->setColumnStretch(1, 0); layout->setRowStretch(0, 1); layout->setRowStretch(1, 0); KTextEditor::Editor *editor = KTextEditor::Editor::instance(); document = editor->createDocument(this); document->setHighlightingMode(QStringLiteral("BibTeX")); KTextEditor::View *view = document->createView(this); layout->addWidget(view, 0, 0, 1, 2); d->messages = new KComboBox(this); layout->addWidget(d->messages, 1, 0, 1, 1); d->buttonRestore = new QPushButton(QIcon::fromTheme(QStringLiteral("edit-undo")), i18n("Restore"), this); layout->addWidget(d->buttonRestore, 1, 1, 1, 1); connect(d->buttonRestore, &QPushButton::clicked, this, static_cast(&SourceWidget::reset)); connect(document, &KTextEditor::Document::textChanged, this, &SourceWidget::gotModified); } void SourceWidget::reset() { /// if signals are not deactivated, the "modified" signal would be emitted when /// resetting the widget's value disconnect(document, &KTextEditor::Document::textChanged, this, &SourceWidget::gotModified); document->setText(originalText); setModified(false); connect(document, &KTextEditor::Document::textChanged, this, &SourceWidget::gotModified); } void SourceWidget::addMessage(const FileImporter::MessageSeverity severity, const QString &messageText) { d->addMessage(severity, messageText); } void SourceWidget::updateMessage() { QString message; const bool validationResult = validate(nullptr, message); if (!message.isEmpty()) { if (validationResult) addMessage(FileImporter::SeverityInfo, message); else addMessage(FileImporter::SeverityError, message); } } #include "elementwidgets.moc" diff --git a/src/gui/element/findpdfui.cpp b/src/gui/element/findpdfui.cpp index e073e728..5b61579e 100644 --- a/src/gui/element/findpdfui.cpp +++ b/src/gui/element/findpdfui.cpp @@ -1,535 +1,535 @@ /*************************************************************************** - * Copyright (C) 2004-2018 by Thomas Fischer * + * Copyright (C) 2004-2019 by Thomas Fischer * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, see . * ***************************************************************************/ #include "findpdfui.h" #include "findpdfui_p.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "preferences.h" #include "fileinfo.h" #include "fieldlistedit.h" #include "logging_gui.h" class PDFListModel; const int posLabelUrl = 0; const int posLabelPreview = 1; const int posViewButton = 2; const int posRadioNoDownload = 3; const int posRadioDownload = 4; const int posRadioURLonly = 5; /// inspired by KNewStuff3's ItemsViewDelegate PDFItemDelegate::PDFItemDelegate(QListView *itemView, QObject *parent) : KWidgetItemDelegate(itemView, parent), m_parent(itemView) { /// nothing } void PDFItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const { QStyle *style = QApplication::style(); style->drawPrimitive(QStyle::PE_PanelItemViewItem, &option, painter, nullptr); painter->save(); if (option.state & QStyle::State_Selected) { painter->setPen(QPen(option.palette.highlightedText().color())); } else { painter->setPen(QPen(option.palette.text().color())); } /// draw icon based on mime-type QPixmap icon = index.data(Qt::DecorationRole).value(); if (!icon.isNull()) { int margin = option.fontMetrics.height() / 3; painter->drawPixmap(margin, margin + option.rect.top(), KIconLoader::SizeMedium, KIconLoader::SizeMedium, icon); } painter->restore(); } QList PDFItemDelegate::createItemWidgets(const QModelIndex &index) const { Q_UNUSED(index) // FIXME really of no use? QList list; /// first, the label with shows the found PDF file's origin (URL) KSqueezedTextLabel *label = new KSqueezedTextLabel(); label->setBackgroundRole(QPalette::NoRole); label->setAlignment(Qt::AlignTop | Qt::AlignLeft); list << label; Q_ASSERT_X(list.count() == posLabelUrl + 1, "QList PDFItemDelegate::createItemWidgets() const", "list.count() != posLabelUrl + 1"); /// a label with shows either the PDF's title or a text snipplet QLabel *previewLabel = new QLabel(); previewLabel->setBackgroundRole(QPalette::NoRole); previewLabel->setAlignment(Qt::AlignTop | Qt::AlignLeft); list << previewLabel; Q_ASSERT_X(list.count() == posLabelPreview + 1, "QList PDFItemDelegate::createItemWidgets() const", "list.count() != posLabelPreview + 1"); /// add a push button to view the PDF file QPushButton *pushButton = new QPushButton(QIcon::fromTheme(QStringLiteral("application-pdf")), i18n("View")); list << pushButton; connect(pushButton, &QPushButton::clicked, this, &PDFItemDelegate::slotViewPDF); Q_ASSERT_X(list.count() == posViewButton + 1, "QList PDFItemDelegate::createItemWidgets() const", "list.count() != posViewButton + 1"); /// a button group to choose what to do with this particular PDF file QButtonGroup *bg = new QButtonGroup(); /// button group's first choice: ignore file (discard it) QRadioButton *radioButton = new QRadioButton(i18n("Ignore")); bg->addButton(radioButton); list << radioButton; connect(radioButton, &QRadioButton::toggled, this, &PDFItemDelegate::slotRadioNoDownloadToggled); Q_ASSERT_X(list.count() == posRadioNoDownload + 1, "QList PDFItemDelegate::createItemWidgets() const", "list.count() != posRadioNoDownload + 1"); /// download this file and store it locally, user will be asked for "Save As" radioButton = new QRadioButton(i18n("Download")); bg->addButton(radioButton); list << radioButton; connect(radioButton, &QRadioButton::toggled, this, &PDFItemDelegate::slotRadioDownloadToggled); Q_ASSERT_X(list.count() == posRadioDownload + 1, "QList PDFItemDelegate::createItemWidgets() const", "list.count() != posRadioDownload + 1"); /// paste URL into BibTeX entry, no local copy is stored radioButton = new QRadioButton(i18n("Use URL only")); bg->addButton(radioButton); list << radioButton; connect(radioButton, &QRadioButton::toggled, this, &PDFItemDelegate::slotRadioURLonlyToggled); Q_ASSERT_X(list.count() == posRadioURLonly + 1, "QList PDFItemDelegate::createItemWidgets() const", "list.count() != posRadioURLonly + 1"); return list; } /// Update the widgets /// Clazy warns: "Missing reference on non-trivial type" for argument 'widgets', /// but KWidgetItemDelegate defines this function this way and cannot be changed. void PDFItemDelegate::updateItemWidgets(const QList widgets, const QStyleOptionViewItem &option, const QPersistentModelIndex &index) const { if (!index.isValid()) return; const PDFListModel *model = qobject_cast(index.model()); if (model == nullptr) { qCDebug(LOG_KBIBTEX_GUI) << "WARNING - INVALID MODEL!"; return; } /// determine some variables used for layout const int margin = option.fontMetrics.height() / 3; const int buttonHeight = option.fontMetrics.height() * 6 / 3; const int maxTextWidth = qMax(qMax(option.fontMetrics.width(i18n("Use URL only")), option.fontMetrics.width(i18n("Ignore"))), qMax(option.fontMetrics.width(i18n("Download")), option.fontMetrics.width(i18n("View")))); const int buttonWidth = maxTextWidth * 3 / 2; const int labelWidth = option.rect.width() - 3 * margin - KIconLoader::SizeMedium; const int labelHeight = option.fontMetrics.height();//(option.rect.height() - 4 * margin - buttonHeight) / 2; /// Total height = margin + labelHeight + margin + labelHeight + marin + buttonHeight + margin /// = option.fontMetrics.height() * (1/3 + 1 + 1/3 + 1 + 1/3 + 6/3 + 1/3) /// = option.fontMetrics.height() * 16 / 3 /// setup label which will show the PDF file's URL KSqueezedTextLabel *label = qobject_cast(widgets[posLabelUrl]); if (label != nullptr) { const QString text = index.data(PDFListModel::URLRole).toUrl().toDisplayString(); label->setText(text); label->setToolTip(text); label->move(margin * 2 + KIconLoader::SizeMedium, margin); label->resize(labelWidth, labelHeight); } /// setup label which will show the PDF's title or textual beginning QLabel *previewLabel = qobject_cast(widgets[posLabelPreview]); if (previewLabel != nullptr) { previewLabel->setText(index.data(PDFListModel::TextualPreviewRole).toString()); previewLabel->move(margin * 2 + KIconLoader::SizeMedium, margin * 2 + labelHeight); previewLabel->resize(labelWidth, labelHeight); } /// setup the view button QPushButton *viewButton = qobject_cast(widgets[posViewButton]); if (viewButton != nullptr) { const QSize hint = viewButton->sizeHint(); const int h = hint.isValid() ? qMin(buttonHeight, hint.height()) : buttonHeight; viewButton->move(margin * 2 + KIconLoader::SizeMedium, option.rect.height() - margin - h); viewButton->resize(buttonWidth, h); } /// setup each of the three radio buttons for (int i = 0; i < 3; ++i) { QRadioButton *radioButton = qobject_cast(widgets[posRadioNoDownload + i]); if (radioButton != nullptr) { const QSize hint = radioButton->sizeHint(); const int h = hint.isValid() ? qMin(buttonHeight, hint.height()) : buttonHeight; radioButton->move(option.rect.width() - margin - (3 - i) * (buttonWidth + margin), option.rect.height() - margin - h); radioButton->resize(buttonWidth, h); bool ok = false; radioButton->setChecked(i + FindPDF::NoDownload == index.data(PDFListModel::DownloadModeRole).toInt(&ok) && ok); } } } QSize PDFItemDelegate::sizeHint(const QStyleOptionViewItem &option, const QModelIndex &) const { /// set a size that is suiteable QSize size; size.setWidth(option.fontMetrics.width(i18n("Download")) * 6); size.setHeight(qMax(option.fontMetrics.height() * 16 / 3, static_cast(KIconLoader::SizeMedium))); ///< KIconLoader::SizeMedium should be 32 return size; } /** * Method is called when the "View PDF" button of a list item is clicked. * Opens the associated URL or its local copy using the system's default viewer. */ void PDFItemDelegate::slotViewPDF() { QModelIndex index = focusedIndex(); if (index.isValid()) { const QString tempfileName = index.data(PDFListModel::TempFileNameRole).toString(); const QUrl url = index.data(PDFListModel::URLRole).toUrl(); if (!tempfileName.isEmpty()) { /// Guess mime type for url to open QUrl tempUrl(tempfileName); QMimeType mimeType = FileInfo::mimeTypeForUrl(tempUrl); const QString mimeTypeName = mimeType.name(); /// Ask KDE subsystem to open url in viewer matching mime type #if KIO_VERSION < 0x051f00 // < 5.31.0 KRun::runUrl(tempUrl, mimeTypeName, itemView(), false, false, url.toDisplayString()); #else // KIO_VERSION < 0x051f00 // >= 5.31.0 KRun::runUrl(tempUrl, mimeTypeName, itemView(), KRun::RunFlags(), url.toDisplayString()); #endif // KIO_VERSION < 0x051f00 } else if (url.isValid()) { /// Guess mime type for url to open QMimeType mimeType = FileInfo::mimeTypeForUrl(url); const QString mimeTypeName = mimeType.name(); /// Ask KDE subsystem to open url in viewer matching mime type #if KIO_VERSION < 0x051f00 // < 5.31.0 KRun::runUrl(url, mimeTypeName, itemView(), false, false); #else // KIO_VERSION < 0x051f00 // >= 5.31.0 KRun::runUrl(url, mimeTypeName, itemView(), KRun::RunFlags()); #endif // KIO_VERSION < 0x051f00 } } } /** * Updated the model when the user selects the radio button for ignoring a PDF file. */ void PDFItemDelegate::slotRadioNoDownloadToggled(bool checked) { QModelIndex index = focusedIndex(); if (index.isValid() && checked) { m_parent->model()->setData(index, FindPDF::NoDownload, PDFListModel::DownloadModeRole); } } /** * Updated the model when the user selects the radio button for downloading a PDF file. */ void PDFItemDelegate::slotRadioDownloadToggled(bool checked) { QModelIndex index = focusedIndex(); if (index.isValid() && checked) { m_parent->model()->setData(index, FindPDF::Download, PDFListModel::DownloadModeRole); } } /** * Updated the model when the user selects the radio button for keeping a PDF file's URL. */ void PDFItemDelegate::slotRadioURLonlyToggled(bool checked) { QModelIndex index = focusedIndex(); if (index.isValid() && checked) { m_parent->model()->setData(index, FindPDF::URLonly, PDFListModel::DownloadModeRole); } } PDFListModel::PDFListModel(QList &resultList, QObject *parent) : QAbstractListModel(parent), m_resultList(resultList) { /// nothing } int PDFListModel::rowCount(const QModelIndex &parent) const { /// row cout depends on number of found PDF references int count = parent == QModelIndex() ? m_resultList.count() : 0; return count; } QVariant PDFListModel::data(const QModelIndex &index, int role) const { if (index != QModelIndex() && index.parent() == QModelIndex() && index.row() < m_resultList.count()) { if (role == Qt::DisplayRole) return m_resultList[index.row()].url.toDisplayString(); else if (role == URLRole) return m_resultList[index.row()].url; else if (role == TextualPreviewRole) return m_resultList[index.row()].textPreview; else if (role == Qt::ToolTipRole) return QStringLiteral("") + m_resultList[index.row()].textPreview + QStringLiteral(""); ///< 'qt' tags required for word wrap else if (role == TempFileNameRole) { if (m_resultList[index.row()].tempFilename != nullptr) return m_resultList[index.row()].tempFilename->fileName(); else return QVariant(); } else if (role == DownloadModeRole) return m_resultList[index.row()].downloadMode; else if (role == Qt::DecorationRole) { /// make an educated guess on the icon, based on URL or path QString iconName = FileInfo::mimeTypeForUrl(m_resultList[index.row()].url).iconName(); iconName = iconName == QStringLiteral("application-octet-stream") ? QStringLiteral("application-pdf") : iconName; return QIcon::fromTheme(iconName).pixmap(KIconLoader::SizeMedium, KIconLoader::SizeMedium); } else return QVariant(); } return QVariant(); } bool PDFListModel::setData(const QModelIndex &index, const QVariant &value, int role) { if (index != QModelIndex() && index.row() < m_resultList.count() && role == DownloadModeRole) { bool ok = false; const FindPDF::DownloadMode downloadMode = static_cast(value.toInt(&ok)); if (ok) { m_resultList[index.row()].downloadMode = downloadMode; return true; } } return false; } QVariant PDFListModel::headerData(int section, Qt::Orientation orientation, int role) const { Q_UNUSED(orientation); if (section == 0) { if (role == Qt::DisplayRole || role == Qt::ToolTipRole) { return i18n("Result"); } else return QVariant(); } return QVariant(); } class FindPDFUI::Private { private: FindPDFUI *p; public: QListView *listViewResult; QLabel *labelMessage; QList resultList; FindPDF *findpdf; Private(FindPDFUI *parent) : p(parent), findpdf(new FindPDF(parent)) { setupGUI(); } void setupGUI() { QGridLayout *layout = new QGridLayout(p); const int minWidth = p->fontMetrics().height() * 40; const int minHeight = p->fontMetrics().height() * 20; p->setMinimumSize(minWidth, minHeight); listViewResult = new QListView(p); layout->addWidget(listViewResult, 0, 0); listViewResult->setEnabled(false); listViewResult->hide(); labelMessage = new QLabel(p); layout->addWidget(labelMessage, 1, 0); labelMessage->setMinimumSize(minWidth, minHeight); labelMessage->setAlignment(Qt::AlignVCenter | Qt::AlignCenter); static_cast(p->parent())->setCursor(Qt::WaitCursor); } }; FindPDFUI::FindPDFUI(Entry &entry, QWidget *parent) : QWidget(parent), d(new Private(this)) { d->labelMessage->show(); d->labelMessage->setText(i18n("Starting to search...")); connect(d->findpdf, &FindPDF::finished, this, &FindPDFUI::searchFinished); connect(d->findpdf, &FindPDF::progress, this, &FindPDFUI::searchProgress); d->findpdf->search(entry); } FindPDFUI::~FindPDFUI() { for (QList::Iterator it = d->resultList.begin(); it != d->resultList.end();) { delete it->tempFilename; it = d->resultList.erase(it); } } void FindPDFUI::interactiveFindPDF(Entry &entry, const File &bibtexFile, QWidget *parent) { QPointer dlg = new QDialog(parent); QPointer widget = new FindPDFUI(entry, dlg); dlg->setWindowTitle(i18n("Find PDF")); QBoxLayout *layout = new QVBoxLayout(dlg); layout->addWidget(widget); QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Abort | QDialogButtonBox::Ok | QDialogButtonBox::Cancel, Qt::Horizontal, dlg); layout->addWidget(buttonBox); dlg->setLayout(layout); buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false); connect(widget.data(), &FindPDFUI::resultAvailable, buttonBox->button(QDialogButtonBox::Ok), &QWidget::setEnabled); connect(widget.data(), &FindPDFUI::resultAvailable, buttonBox->button(QDialogButtonBox::Abort), &QWidget::setDisabled); connect(buttonBox->button(QDialogButtonBox::Ok), &QPushButton::clicked, dlg.data(), &QDialog::accept); connect(buttonBox->button(QDialogButtonBox::Cancel), &QPushButton::clicked, dlg.data(), &QDialog::reject); connect(buttonBox->button(QDialogButtonBox::Abort), &QPushButton::clicked, widget.data(), &FindPDFUI::stopSearch); if (dlg->exec() == QDialog::Accepted) widget->apply(entry, bibtexFile); delete dlg; } void FindPDFUI::apply(Entry &entry, const File &bibtexFile) { QAbstractItemModel *model = d->listViewResult->model(); for (int i = 0; i < model->rowCount(); ++i) { bool ok = false; FindPDF::DownloadMode downloadMode = static_cast(model->data(model->index(i, 0), PDFListModel::DownloadModeRole).toInt(&ok)); if (!ok) { qCDebug(LOG_KBIBTEX_GUI) << "Could not interprete download mode"; downloadMode = FindPDF::NoDownload; } QUrl url = model->data(model->index(i, 0), PDFListModel::URLRole).toUrl(); QString tempfileName = model->data(model->index(i, 0), PDFListModel::TempFileNameRole).toString(); if (downloadMode == FindPDF::URLonly && url.isValid()) { bool alreadyContained = false; for (QMap::ConstIterator it = entry.constBegin(); !alreadyContained && it != entry.constEnd(); ++it) // FIXME this will terribly break if URLs in an entry's URL field are separated with semicolons alreadyContained |= it.key().toLower().startsWith(Entry::ftUrl) && PlainTextValue::text(it.value()) == url.toDisplayString(); if (!alreadyContained) { Value value; value.append(QSharedPointer(new VerbatimText(url.toDisplayString()))); if (!entry.contains(Entry::ftUrl)) entry.insert(Entry::ftUrl, value); else for (int i = 2; i < 256; ++i) { const QString keyName = QString(QStringLiteral("%1%2")).arg(Entry::ftUrl).arg(i); if (!entry.contains(keyName)) { entry.insert(keyName, value); break; } } } } else if (downloadMode == FindPDF::Download && !tempfileName.isEmpty()) { QUrl startUrl = bibtexFile.property(File::Url, QUrl()).toUrl(); const QString absoluteFilename = QFileDialog::getSaveFileName(this, i18n("Save URL '%1'", url.url(QUrl::PreferLocalFile)), startUrl.adjusted(QUrl::RemoveFilename | QUrl::StripTrailingSlash).path(), QStringLiteral("application/pdf")); if (!absoluteFilename.isEmpty()) { const QString visibleFilename = UrlListEdit::askRelativeOrStaticFilename(this, absoluteFilename, startUrl); qCDebug(LOG_KBIBTEX_GUI) << "Saving PDF from " << url << " to file " << absoluteFilename << " known as " << visibleFilename; // FIXME test for overwrite QFile::copy(tempfileName, absoluteFilename); bool alreadyContained = false; for (QMap::ConstIterator it = entry.constBegin(); !alreadyContained && it != entry.constEnd(); ++it) alreadyContained |= (it.key().toLower().startsWith(Entry::ftFile) || it.key().toLower().startsWith(Entry::ftLocalFile) || it.key().toLower().startsWith(Entry::ftUrl)) && PlainTextValue::text(it.value()) == url.toDisplayString(); if (!alreadyContained) { Value value; value.append(QSharedPointer(new VerbatimText(visibleFilename))); - const QString fieldNameStem = Preferences::bibliographySystem() == Preferences::BibTeX ? Entry::ftLocalFile : Entry::ftFile; + const QString fieldNameStem = Preferences::instance().bibliographySystem() == Preferences::BibTeX ? Entry::ftLocalFile : Entry::ftFile; if (!entry.contains(fieldNameStem)) entry.insert(fieldNameStem, value); else for (int i = 2; i < 256; ++i) { const QString keyName = QString(QStringLiteral("%1%2")).arg(fieldNameStem).arg(i); if (!entry.contains(keyName)) { entry.insert(keyName, value); break; } } } } } } } void FindPDFUI::searchFinished() { d->labelMessage->hide(); d->listViewResult->show(); d->resultList = d->findpdf->results(); d->listViewResult->setModel(new PDFListModel(d->resultList, d->listViewResult)); d->listViewResult->setItemDelegate(new PDFItemDelegate(d->listViewResult, d->listViewResult)); d->listViewResult->setEnabled(true); d->listViewResult->reset(); static_cast(parent())->unsetCursor(); emit resultAvailable(true); } void FindPDFUI::searchProgress(int visitedPages, int runningJobs, int foundDocuments) { d->listViewResult->hide(); d->labelMessage->show(); d->labelMessage->setText(i18n("Number of visited pages: %1
Number of running downloads: %2
Number of found documents: %3
", visitedPages, runningJobs, foundDocuments)); } void FindPDFUI::stopSearch() { d->findpdf->abort(); searchFinished(); } void FindPDFUI::abort() { d->findpdf->abort(); } diff --git a/src/gui/preferences/settingsgeneralwidget.cpp b/src/gui/preferences/settingsgeneralwidget.cpp index c6003018..26209d8c 100644 --- a/src/gui/preferences/settingsgeneralwidget.cpp +++ b/src/gui/preferences/settingsgeneralwidget.cpp @@ -1,132 +1,132 @@ /*************************************************************************** - * Copyright (C) 2004-2018 by Thomas Fischer * + * Copyright (C) 2004-2019 by Thomas Fischer * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, see . * ***************************************************************************/ #include "settingsgeneralwidget.h" #include #include #include #include #include #include "guihelper.h" #include "value.h" #include "preferences.h" class SettingsGeneralWidget::SettingsGeneralWidgetPrivate { private: SettingsGeneralWidget *p; KComboBox *comboBoxBibliographySystem; KComboBox *comboBoxPersonNameFormatting; const Person dummyPerson; QString restartRequiredMsg; KSharedConfigPtr config; const QString configGroupName; public: SettingsGeneralWidgetPrivate(SettingsGeneralWidget *parent) : p(parent), dummyPerson(Person(i18n("John"), i18n("Doe"), i18n("Jr."))), restartRequiredMsg(i18n("Changing this option requires a restart to take effect.")), config(KSharedConfig::openConfig(QStringLiteral("kbibtexrc"))), configGroupName(QStringLiteral("General")) { setupGUI(); } void loadState() { - comboBoxBibliographySystem->setCurrentIndex(comboBoxBibliographySystem->findData(QVariant::fromValue(static_cast(Preferences::bibliographySystem())))); + comboBoxBibliographySystem->setCurrentIndex(comboBoxBibliographySystem->findData(QVariant::fromValue(static_cast(Preferences::instance().bibliographySystem())))); KConfigGroup configGroup(config, configGroupName); QString personNameFormatting = configGroup.readEntry(Preferences::keyPersonNameFormatting, Preferences::defaultPersonNameFormatting); int row = GUIHelper::selectValue(comboBoxPersonNameFormatting->model(), Person::transcribePersonName(&dummyPerson, personNameFormatting)); comboBoxPersonNameFormatting->setCurrentIndex(row); } void saveState() { - Preferences::setBibliographySystem(static_cast(comboBoxBibliographySystem->currentData().toInt())); + Preferences::instance().setBibliographySystem(static_cast(comboBoxBibliographySystem->currentData().toInt())); KConfigGroup configGroup(config, configGroupName); configGroup.writeEntry(Preferences::keyPersonNameFormatting, comboBoxPersonNameFormatting->itemData(comboBoxPersonNameFormatting->currentIndex())); config->sync(); } void resetToDefaults() { comboBoxBibliographySystem->setCurrentIndex(static_cast(Preferences::defaultBibliographySystem)); int row = GUIHelper::selectValue(comboBoxPersonNameFormatting->model(), Person::transcribePersonName(&dummyPerson, Preferences::defaultPersonNameFormatting)); comboBoxPersonNameFormatting->setCurrentIndex(row); } void setupGUI() { QFormLayout *layout = new QFormLayout(p); comboBoxBibliographySystem = new KComboBox(false, p); comboBoxBibliographySystem->setObjectName(QStringLiteral("comboBoxBibliographySystem")); const QMap &availableBibliographySystems = Preferences::availableBibliographySystems(); for (QMap::ConstIterator it = availableBibliographySystems.constBegin(), itEnd = availableBibliographySystems.constEnd(); it != itEnd; ++it) comboBoxBibliographySystem->addItem(it.value(), QVariant::fromValue(static_cast(it.key()))); layout->addRow(i18n("Bibliography System:"), comboBoxBibliographySystem); connect(comboBoxBibliographySystem, static_cast(&QComboBox::currentIndexChanged), p, &SettingsGeneralWidget::changed); comboBoxPersonNameFormatting = new KComboBox(false, p); layout->addRow(i18n("Person Names Formatting:"), comboBoxPersonNameFormatting); const QStringList formattingOptions {Preferences::personNameFormatFirstLast, Preferences::personNameFormatLastFirst}; for (const QString &formattingOption : formattingOptions) { comboBoxPersonNameFormatting->addItem(Person::transcribePersonName(&dummyPerson, formattingOption), formattingOption); } comboBoxPersonNameFormatting->setToolTip(restartRequiredMsg); connect(comboBoxPersonNameFormatting, static_cast(&QComboBox::currentIndexChanged), p, &SettingsGeneralWidget::changed); } }; SettingsGeneralWidget::SettingsGeneralWidget(QWidget *parent) : SettingsAbstractWidget(parent), d(new SettingsGeneralWidgetPrivate(this)) { d->loadState(); } SettingsGeneralWidget::~SettingsGeneralWidget() { delete d; } QString SettingsGeneralWidget::label() const { return i18n("General"); } QIcon SettingsGeneralWidget::icon() const { return QIcon::fromTheme(QStringLiteral("kbibtex")); } void SettingsGeneralWidget::loadState() { d->loadState(); } void SettingsGeneralWidget::saveState() { d->saveState(); } void SettingsGeneralWidget::resetToDefaults() { d->resetToDefaults(); } diff --git a/src/io/fileimporterris.cpp b/src/io/fileimporterris.cpp index 1d193a19..bf92b773 100644 --- a/src/io/fileimporterris.cpp +++ b/src/io/fileimporterris.cpp @@ -1,340 +1,340 @@ /*************************************************************************** - * Copyright (C) 2004-2018 by Thomas Fischer * + * Copyright (C) 2004-2019 by Thomas Fischer * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, see . * ***************************************************************************/ #include "fileimporterris.h" #include #include #include #include #include #include "preferences.h" #include "kbibtex.h" #include "entry.h" #include "value.h" #include "logging_io.h" #define appendValue(entry, fieldname, newvalue) { Value value = (entry)->value((fieldname)); value.append((newvalue)); (entry)->insert((fieldname), value); } #define removeDuplicates(entry, fieldname) { Value value = (entry)->value((fieldname)); if (!(value).isEmpty()) removeDuplicateValueItems((value)); if (!(value).isEmpty()) (entry)->insert((fieldname), value); } class FileImporterRIS::FileImporterRISPrivate { public: FileImporterRIS *parent; int referenceCounter; bool cancelFlag; bool protectCasing; typedef struct { QString key; QString value; } RISitem; typedef QVector RISitemList; FileImporterRISPrivate(FileImporterRIS *_parent) : parent(_parent), referenceCounter(0), cancelFlag(false), protectCasing(false) { /// nothing } RISitemList readElement(QTextStream &textStream) { RISitemList result; QString line = textStream.readLine(); while (!line.startsWith(QStringLiteral("TY - ")) && !textStream.atEnd()) line = textStream.readLine(); if (textStream.atEnd()) return result; QString key, value; while (!line.startsWith(QStringLiteral("ER -")) && !textStream.atEnd()) { if (line.mid(2, 3) == QStringLiteral(" -")) { if (!value.isEmpty()) { RISitem item; item.key = key; item.value = value; result.append(item); } key = line.left(2); value = line.mid(6).simplified(); } else { line = line.simplified(); if (line.length() > 1) { /// multi-line field are joined to one long line value += QLatin1Char(' ') + line; } } line = textStream.readLine(); } if (!line.startsWith(QStringLiteral("ER -")) && textStream.atEnd()) { qCWarning(LOG_KBIBTEX_IO) << "Expected that entry that starts with 'TY' ends with 'ER' but instead met end of file"; /// Instead of an 'emit' ... QMetaObject::invokeMethod(parent, "message", Qt::DirectConnection, QGenericReturnArgument(), Q_ARG(FileImporter::MessageSeverity, SeverityWarning), Q_ARG(QString, QStringLiteral("Expected that entry that starts with 'TY' ends with 'ER' but instead met end of file"))); } if (!value.isEmpty()) { RISitem item; item.key = key; item.value = value; result.append(item); } return result; } inline QString optionallyProtectCasing(const QString &text) const { if (protectCasing) return QLatin1Char('{') + text + QLatin1Char('}'); else return text; } Element *nextElement(QTextStream &textStream) { RISitemList list = readElement(textStream); if (list.empty()) return nullptr; QString entryType = Entry::etMisc; Entry *entry = new Entry(entryType, QString(QStringLiteral("RIS_%1")).arg(referenceCounter++)); QString journalName, startPage, endPage, date; int fieldCounter = 0; for (RISitemList::iterator it = list.begin(); it != list.end(); ++it) { if ((*it).key == QStringLiteral("TY")) { if ((*it).value.startsWith(QStringLiteral("BOOK")) || (*it).value.startsWith(QStringLiteral("SER"))) entryType = Entry::etBook; else if ((*it).value.startsWith(QStringLiteral("CHAP"))) entryType = Entry::etInBook; else if ((*it).value.startsWith(QStringLiteral("CONF"))) entryType = Entry::etInProceedings; else if ((*it).value.startsWith(QStringLiteral("JFULL")) || (*it).value.startsWith(QStringLiteral("JOUR")) || (*it).value.startsWith(QStringLiteral("MGZN"))) entryType = Entry::etArticle; else if ((*it).value.startsWith(QStringLiteral("RPRT"))) entryType = Entry::etTechReport; else if ((*it).value.startsWith(QStringLiteral("THES"))) entryType = Entry::etPhDThesis; // FIXME what about etMastersThesis? else if ((*it).value.startsWith(QStringLiteral("UNPB"))) entryType = Entry::etUnpublished; entry->setType(entryType); } else if ((*it).key == QStringLiteral("AU") || (*it).key == QStringLiteral("A1")) { Person *person = splitName((*it).value); if (person != nullptr) appendValue(entry, Entry::ftAuthor, QSharedPointer(person)); } else if ((*it).key == QStringLiteral("ED") || (*it).key == QStringLiteral("A2")) { Person *person = splitName((*it).value); if (person != nullptr) appendValue(entry, Entry::ftEditor, QSharedPointer(person)); } else if ((*it).key == QStringLiteral("ID")) { entry->setId((*it).value); } else if ((*it).key == QStringLiteral("Y1") || (*it).key == QStringLiteral("PY")) { date = (*it).value; } else if ((*it).key == QStringLiteral("Y2")) { if (date.isEmpty()) date = (*it).value; } else if ((*it).key == QStringLiteral("AB") || (*it).key == QStringLiteral("N2")) { appendValue(entry, Entry::ftAbstract, QSharedPointer(new PlainText((*it).value))); } else if ((*it).key == QStringLiteral("N1")) { appendValue(entry, Entry::ftNote, QSharedPointer<PlainText>(new PlainText((*it).value))); } else if ((*it).key == QStringLiteral("KW")) { QString text = (*it).value; const QRegularExpression splitRegExp(text.contains(QStringLiteral(";")) ? QStringLiteral("\\s*[;\\n]\\s*") : (text.contains(QStringLiteral(",")) ? QStringLiteral("\\s*[,\\n]\\s*") : QStringLiteral("\\n"))); QStringList newKeywords = text.split(splitRegExp, QString::SkipEmptyParts); for (QStringList::Iterator it = newKeywords.begin(); it != newKeywords.end(); ++it) appendValue(entry, Entry::ftKeywords, QSharedPointer<Keyword>(new Keyword(*it))); } else if ((*it).key == QStringLiteral("TI") || (*it).key == QStringLiteral("T1")) { appendValue(entry, Entry::ftTitle, QSharedPointer<PlainText>(new PlainText(optionallyProtectCasing((*it).value)))); } else if ((*it).key == QStringLiteral("T3")) { appendValue(entry, Entry::ftSeries, QSharedPointer<PlainText>(new PlainText((*it).value))); } else if ((*it).key == QStringLiteral("JO") || (*it).key == QStringLiteral("J1") || (*it).key == QStringLiteral("J2")) { if (journalName.isEmpty()) journalName = (*it).value; } else if ((*it).key == QStringLiteral("JF") || (*it).key == QStringLiteral("JA")) { journalName = (*it).value; } else if ((*it).key == QStringLiteral("VL")) { appendValue(entry, Entry::ftVolume, QSharedPointer<PlainText>(new PlainText((*it).value))); } else if ((*it).key == QStringLiteral("CP")) { appendValue(entry, Entry::ftChapter, QSharedPointer<PlainText>(new PlainText((*it).value))); } else if ((*it).key == QStringLiteral("IS")) { appendValue(entry, Entry::ftNumber, QSharedPointer<PlainText>(new PlainText((*it).value))); } else if ((*it).key == QStringLiteral("DO") || (*it).key == QStringLiteral("M3")) { const QRegularExpressionMatch doiRegExpMatch = KBibTeX::doiRegExp.match((*it).value); if (doiRegExpMatch.hasMatch()) appendValue(entry, Entry::ftDOI, QSharedPointer<VerbatimText>(new VerbatimText(doiRegExpMatch.captured()))); } else if ((*it).key == QStringLiteral("PB")) { appendValue(entry, Entry::ftPublisher, QSharedPointer<PlainText>(new PlainText((*it).value))); } else if ((*it).key == QStringLiteral("IN")) { appendValue(entry, Entry::ftSchool, QSharedPointer<PlainText>(new PlainText((*it).value))); } else if ((*it).key == QStringLiteral("SN")) { const QString fieldName = entryType == Entry::etBook || entryType == Entry::etInBook ? Entry::ftISBN : Entry::ftISSN; appendValue(entry, fieldName, QSharedPointer<PlainText>(new PlainText((*it).value))); } else if ((*it).key == QStringLiteral("CY")) { appendValue(entry, Entry::ftLocation, QSharedPointer<PlainText>(new PlainText((*it).value))); } else if ((*it).key == QStringLiteral("AD")) { appendValue(entry, Entry::ftAddress, QSharedPointer<PlainText>(new PlainText((*it).value))); } else if ((*it).key == QStringLiteral("L1") || (*it).key == QStringLiteral("L2") || (*it).key == QStringLiteral("L3") || (*it).key == QStringLiteral("UR")) { QString fieldValue = (*it).value; fieldValue.replace(QStringLiteral("<Go to ISI>://"), QStringLiteral("isi://")); const QRegularExpressionMatch doiRegExpMatch = KBibTeX::doiRegExp.match(fieldValue); const QRegularExpressionMatch urlRegExpMatch = KBibTeX::urlRegExp.match(fieldValue); - const QString fieldName = doiRegExpMatch.hasMatch() ? Entry::ftDOI : (KBibTeX::urlRegExp.match((*it).value).hasMatch() ? Entry::ftUrl : (Preferences::bibliographySystem() == Preferences::BibTeX ? Entry::ftLocalFile : Entry::ftFile)); + const QString fieldName = doiRegExpMatch.hasMatch() ? Entry::ftDOI : (KBibTeX::urlRegExp.match((*it).value).hasMatch() ? Entry::ftUrl : (Preferences::instance().bibliographySystem() == Preferences::BibTeX ? Entry::ftLocalFile : Entry::ftFile)); fieldValue = doiRegExpMatch.hasMatch() ? doiRegExpMatch.captured() : (urlRegExpMatch.hasMatch() ? urlRegExpMatch.captured() : fieldValue); if (fieldValue.startsWith(QStringLiteral("file:///"))) fieldValue = fieldValue.mid(7); appendValue(entry, fieldName, QSharedPointer<VerbatimText>(new VerbatimText(fieldValue))); } else if ((*it).key == QStringLiteral("SP")) { startPage = (*it).value; } else if ((*it).key == QStringLiteral("EP")) { endPage = (*it).value; } else { const QString fieldName = QString(QStringLiteral("RISfield_%1_%2")).arg(fieldCounter++).arg((*it).key.left(2)); appendValue(entry, fieldName, QSharedPointer<PlainText>(new PlainText((*it).value))); } } if (!journalName.isEmpty()) { const QString fieldName = entryType == Entry::etInBook || entryType == Entry::etInProceedings ? Entry::ftBookTitle : Entry::ftJournal; Value value = entry->value(fieldName); value.append(QSharedPointer<PlainText>(new PlainText(optionallyProtectCasing(journalName)))); entry->insert(fieldName, value); } if (!startPage.isEmpty() || !endPage.isEmpty()) { QString page; if (startPage.isEmpty()) page = endPage; else if (endPage.isEmpty()) page = startPage; else page = startPage + QChar(0x2013) + endPage; Value value; value.append(QSharedPointer<PlainText>(new PlainText(page))); entry->insert(Entry::ftPages, value); } QStringList dateFragments = date.split(QStringLiteral("/"), QString::SkipEmptyParts); if (dateFragments.count() > 0) { bool ok; int year = dateFragments[0].toInt(&ok); if (ok && year > 1000 && year < 3000) { Value value = entry->value(Entry::ftYear); value.append(QSharedPointer<PlainText>(new PlainText(QString::number(year)))); entry->insert(Entry::ftYear, value); } else { qCWarning(LOG_KBIBTEX_IO) << "Invalid year: " << dateFragments[0]; /// Instead of an 'emit' ... QMetaObject::invokeMethod(parent, "message", Qt::DirectConnection, QGenericReturnArgument(), Q_ARG(FileImporter::MessageSeverity, SeverityWarning), Q_ARG(QString, QString(QStringLiteral("Invalid year: '%1'")).arg(dateFragments[0]))); } } if (dateFragments.count() > 1) { bool ok; int month = dateFragments[1].toInt(&ok); if (ok && month >= 1 && month <= 12) { Value value = entry->value(Entry::ftMonth); value.append(QSharedPointer<MacroKey>(new MacroKey(KBibTeX::MonthsTriple[month - 1]))); entry->insert(Entry::ftMonth, value); } else { qCWarning(LOG_KBIBTEX_IO) << "Invalid month: " << dateFragments[1]; /// Instead of an 'emit' ... QMetaObject::invokeMethod(parent, "message", Qt::DirectConnection, QGenericReturnArgument(), Q_ARG(FileImporter::MessageSeverity, SeverityWarning), Q_ARG(QString, QString(QStringLiteral("Invalid month: '%1'")).arg(dateFragments[1]))); } } removeDuplicates(entry, Entry::ftDOI); removeDuplicates(entry, Entry::ftUrl); return entry; } void removeDuplicateValueItems(Value &value) { if (value.count() < 2) return; /// Values with one or no ValueItem cannot have duplicates QSet<QString> uniqueStrings; for (Value::Iterator it = value.begin(); it != value.end();) { const QString itemString = PlainTextValue::text(*it); if (uniqueStrings.contains(itemString)) it = value.erase(it); else { uniqueStrings.insert(itemString); ++it; } } } }; FileImporterRIS::FileImporterRIS(QObject *parent) : FileImporter(parent), d(new FileImporterRISPrivate(this)) { // nothing } FileImporterRIS::~FileImporterRIS() { delete d; } File *FileImporterRIS::load(QIODevice *iodevice) { if (!iodevice->isReadable() && !iodevice->open(QIODevice::ReadOnly)) { qCWarning(LOG_KBIBTEX_IO) << "Input device not readable"; emit message(SeverityError, QStringLiteral("Input device not readable")); return nullptr; } d->cancelFlag = false; d->referenceCounter = 0; QTextStream textStream(iodevice); File *result = new File(); while (!d->cancelFlag && !textStream.atEnd()) { emit progress(textStream.pos(), iodevice->size()); QCoreApplication::instance()->processEvents(); Element *element = d->nextElement(textStream); if (element != nullptr) result->append(QSharedPointer<Element>(element)); QCoreApplication::instance()->processEvents(); } emit progress(100, 100); if (d->cancelFlag) { delete result; result = nullptr; } iodevice->close(); if (result != nullptr) result->setProperty(File::ProtectCasing, static_cast<int>(d->protectCasing ? Qt::Checked : Qt::Unchecked)); return result; } bool FileImporterRIS::guessCanDecode(const QString &text) { return text.indexOf(QStringLiteral("TY - ")) >= 0; } void FileImporterRIS::setProtectCasing(bool protectCasing) { d->protectCasing = protectCasing; } void FileImporterRIS::cancel() { d->cancelFlag = true; } diff --git a/src/networking/associatedfiles.cpp b/src/networking/associatedfiles.cpp index 414cbb6c..4338beb4 100644 --- a/src/networking/associatedfiles.cpp +++ b/src/networking/associatedfiles.cpp @@ -1,184 +1,184 @@ /*************************************************************************** - * Copyright (C) 2004-2018 by Thomas Fischer <fischer@unix-ag.uni-kl.de> * + * Copyright (C) 2004-2019 by Thomas Fischer <fischer@unix-ag.uni-kl.de> * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, see <https://www.gnu.org/licenses/>. * ***************************************************************************/ #include "associatedfiles.h" #include <QFileInfo> #include <QDir> #include <KIO/CopyJob> #include <KJobWidgets> #include "preferences.h" #include "logging_networking.h" bool AssociatedFiles::urlIsLocal(const QUrl &url) { // FIXME same function as in UrlListEdit; move to common code base? const QString scheme = url.scheme(); /// Test various schemes such as "http", "https", "ftp", ... return !scheme.startsWith(QStringLiteral("http")) && !scheme.startsWith(QStringLiteral("ftp")) && !scheme.startsWith(QStringLiteral("sftp")) && !scheme.startsWith(QStringLiteral("fish")) && !scheme.startsWith(QStringLiteral("webdav")) && scheme != QStringLiteral("smb"); } QString AssociatedFiles::relativeFilename(const QUrl &documentUrl, const QUrl &baseUrl) { if (documentUrl.isEmpty()) { qCWarning(LOG_KBIBTEX_NETWORKING) << "document URL has to point to a file location or URL"; return documentUrl.url(QUrl::PreferLocalFile); } if (baseUrl.isEmpty() || baseUrl.isRelative()) { qCWarning(LOG_KBIBTEX_NETWORKING) << "base URL has to point to an absolute file location or URL"; return documentUrl.url(QUrl::PreferLocalFile); } if (documentUrl.scheme() != baseUrl.scheme() || (documentUrl.scheme() != QStringLiteral("file") && documentUrl.host() != baseUrl.host())) { qCWarning(LOG_KBIBTEX_NETWORKING) << "document URL and base URL do not match (protocol, host, ...)"; return documentUrl.url(QUrl::PreferLocalFile); } /// First, resolve the provided document URL to an absolute URL /// using the given base URL QUrl internalDocumentUrl = documentUrl; if (internalDocumentUrl.isRelative()) internalDocumentUrl = baseUrl.resolved(internalDocumentUrl); /// Get the absolute path of the base URL const QString baseUrlDirectory = QFileInfo(baseUrl.path()).absolutePath(); /// Let QDir calculate the relative directory return QDir(baseUrlDirectory).relativeFilePath(internalDocumentUrl.path()); } QString AssociatedFiles::absoluteFilename(const QUrl &documentUrl, const QUrl &baseUrl) { if (documentUrl.isEmpty()) { qCWarning(LOG_KBIBTEX_NETWORKING) << "document URL has to point to a file location or URL"; return documentUrl.url(QUrl::PreferLocalFile); } if (documentUrl.isRelative() && (baseUrl.isEmpty() || baseUrl.isRelative())) { qCWarning(LOG_KBIBTEX_NETWORKING) << "base URL has to point to an absolute file location or URL if the document URL is relative"; return documentUrl.url(QUrl::PreferLocalFile); } if (documentUrl.isRelative() && (documentUrl.scheme() != baseUrl.scheme() || (documentUrl.scheme() != QStringLiteral("file") && documentUrl.host() != baseUrl.host()))) { qCWarning(LOG_KBIBTEX_NETWORKING) << "document URL and base URL do not match (protocol, host, ...), but necessary if the document URL is relative"; return documentUrl.url(QUrl::PreferLocalFile); } /// Resolve the provided document URL to an absolute URL /// using the given base URL QUrl internalDocumentUrl = documentUrl; if (internalDocumentUrl.isRelative()) internalDocumentUrl = baseUrl.resolved(internalDocumentUrl); return internalDocumentUrl.url(QUrl::PreferLocalFile); } QString AssociatedFiles::associateDocumentURL(const QUrl &document, QSharedPointer<Entry> &entry, const File *bibTeXFile, PathType pathType, const bool dryRun) { Q_ASSERT(bibTeXFile != nullptr); // FIXME more graceful? const QUrl baseUrl = bibTeXFile->property(File::Url).toUrl(); if (baseUrl.isEmpty() && pathType == ptRelative) { /// If no base URL was given but still a relative path was requested, /// revert choice and enforce the generation of an absolute one pathType = ptAbsolute; } const bool isLocal = urlIsLocal(document); - const QString field = isLocal ? (Preferences::bibliographySystem() == Preferences::BibTeX ? Entry::ftLocalFile : Entry::ftFile) : Entry::ftUrl; + const QString field = isLocal ? (Preferences::instance().bibliographySystem() == Preferences::instance().BibTeX ? Entry::ftLocalFile : Entry::ftFile) : Entry::ftUrl; QString finalUrl = pathType == ptAbsolute ? absoluteFilename(document, baseUrl) : relativeFilename(document, baseUrl); if (!dryRun) { /// only if not pretending bool alreadyContained = false; for (QMap<QString, Value>::ConstIterator it = entry->constBegin(); !alreadyContained && it != entry->constEnd(); ++it) { const Value v = it.value(); for (Value::ConstIterator vit = v.constBegin(); !alreadyContained && vit != v.constEnd(); ++vit) { if (PlainTextValue::text(*vit) == finalUrl) alreadyContained = true; } } if (!alreadyContained) { Value value = entry->value(field); value.append(QSharedPointer<VerbatimText>(new VerbatimText(finalUrl))); entry->insert(field, value); } } return finalUrl; } QString AssociatedFiles::associateDocumentURL(const QUrl &document, const File *bibTeXFile, PathType pathType) { Q_ASSERT(bibTeXFile != nullptr); // FIXME more graceful? const QUrl baseUrl = bibTeXFile->property(File::Url).toUrl(); if (baseUrl.isEmpty() && pathType == ptRelative) { /// If no base URL was given but still a relative path was requested, /// revert choice and enforce the generation of an absolute one pathType = ptAbsolute; } QString finalUrl = pathType == ptAbsolute ? absoluteFilename(document, baseUrl) : relativeFilename(document, baseUrl); return finalUrl; } QUrl AssociatedFiles::copyDocument(const QUrl &sourceUrl, const QString &entryId, const File *bibTeXFile, RenameOperation renameOperation, MoveCopyOperation moveCopyOperation, QWidget *widget, const QString &userDefinedFilename, const bool dryRun) { Q_ASSERT(bibTeXFile != nullptr); // FIXME more graceful? if (moveCopyOperation == mcoNoCopyMove) return sourceUrl; /// nothing to do if no move or copy requested if (entryId.isEmpty() && renameOperation == roEntryId) { /// If no entry id was given but still a rename after entry id was requested, /// revert choice and enforce keeping the original name renameOperation = roKeepName; } const QUrl baseUrl = bibTeXFile->property(File::Url).toUrl(); const QUrl internalSourceUrl = baseUrl.resolved(sourceUrl); const QFileInfo internalSourceInfo(internalSourceUrl.path()); QString filename = internalSourceInfo.fileName(); QString suffix = internalSourceInfo.suffix(); if (suffix.isEmpty()) { suffix = QStringLiteral("html"); filename.append(QLatin1Char('.')).append(suffix); } if (filename.isEmpty()) filename = internalSourceUrl.url(QUrl::PreferLocalFile).remove(QDir::separator()).remove(QLatin1Char('/')).remove(QLatin1Char(':')).remove(QLatin1Char('.')) + QStringLiteral(".") + suffix; if (!bibTeXFile->hasProperty(File::Url)) return QUrl(); /// no valid URL set of BibTeX file object QUrl targetUrl = bibTeXFile->property(File::Url).toUrl(); if (targetUrl.isEmpty()) return QUrl(); /// no valid URL set of BibTeX file object const QString targetPath = QFileInfo(targetUrl.path()).absolutePath(); targetUrl.setPath(targetPath + QDir::separator() + (renameOperation == roEntryId ? entryId + QStringLiteral(".") + suffix : (renameOperation == roUserDefined ? userDefinedFilename : filename))); if (!dryRun) { /// only if not pretending bool success = true; if (urlIsLocal(internalSourceUrl) && urlIsLocal(targetUrl)) { QFile(targetUrl.path()).remove(); success &= QFile::copy(internalSourceUrl.path(), targetUrl.path()); // FIXME check if succeeded if (moveCopyOperation == mcoMove) { success &= QFile(internalSourceUrl.path()).remove(); } } else { // FIXME non-blocking KIO::CopyJob *moveCopyJob = moveCopyOperation == mcoMove ? KIO::move(sourceUrl, targetUrl, KIO::HideProgressInfo | KIO::Overwrite) : KIO::copy(sourceUrl, targetUrl, KIO::HideProgressInfo | KIO::Overwrite); KJobWidgets::setWindow(moveCopyJob, widget); success &= moveCopyJob->exec(); } if (!success) return QUrl(); ///< either copy/move or delete operation failed } return targetUrl; }