diff --git a/org.kde.tellico.appdata.xml b/org.kde.tellico.appdata.xml index a89ebecd..529431d0 100644 --- a/org.kde.tellico.appdata.xml +++ b/org.kde.tellico.appdata.xml @@ -1,558 +1,558 @@ org.kde.tellico.desktop CC0-1.0 GPL-2.0+ Tellico Tellico Tellico Tellico Tellico Tellico Tellico Tellico Tellico Tellico Tellico Tellico Tellico Tellico Tellico Tellico Tellico Tellico Tellico Tellico Tellico Tellico Tellico Tellico Tellico Tellico Tellico Tellico Tellico xxTellicoxx Tellico Collection Manager Menadžer kolekcija Gestor de col·leccions Gestor de col·leccions Správce sbírek Samlingshåndtering Sammlungsverwaltung Collection Manager Gestor de colecciones Kogude haldur Kokoelmahallinta Gestionnaire de collections Xestor de coleccións Gyűjteménykezelő Gerente de collection Pengelola Koleksi Gestore della collezione 소장품 관리 도구 Collectiebeheer Samlingsdatabase Zarządzanie zbiorem Gestor de Colecções Gerenciador de Coleções Менеджер коллекций Správca zbierok Upravljalnik zbirk Samlingshanterare Koleksiyon Yöneticisi Програма для керування збірками xxCollection Managerxx 收藏管理器

Tellico is an application for organizing your collections. It provides default templates for books, bibliographies, videos, music, video games, coins, stamps, trading cards, comic books, and wines. It allows you to enter your collection in a catalogue database, saving many different properties like title, author, etc.

Tellico je aplikacija za organiziranje vaše kolekcije. Omogućava početne predloške za knjige, bibliografije, videa, muziku, video igrice, kovanice, markice, kartice za razmjenu, stripove i vina. Dopušta vam ulaz u vašu kolekiju u katalošku bazu podataka, snimajući mnogo različitih osobina kao što su naslov, autor, itd.

El Tellico és una aplicació per organitzar col·leccions. Proporciona plantilles per defecte per a llibres, bibliografies, vídeos, música, videojocs, monedes, segells, cromos, còmics i vins. Permet introduir les col·leccions en una base de dades de catàlegs, desant moltes propietats diferents com el títol, l'autor, etc.

El Tellico és una aplicació per organitzar col·leccions. Proporciona plantilles per defecte per a llibres, bibliografies, vídeos, música, videojocs, monedes, segells, cromos, còmics i vins. Permet introduir les col·leccions en una base de dades de catàlegs, desant moltes propietats diferents com el títol, l'autor, etc.

Tellico er et program til at organisere dine samlinger. Det giver standardskabeloner til bøger, bibliografier, video, musik, computerspil, mønter, frimærker, byttekort, tegneserier og vine. Det lader dig angive din samling i en katalogdatabase, som gemmer mange forskellige egenskaber, såsom titel, forfatter osv.

Tellico ist eine Anwendung zur Organisation Ihrer Daten. Es enthält Standardvorlagen für Bücher, Literaturverzeichnisse, Videos, Musik, Videospiele, Comicbücher, Münzen, Briefmarken, Karten, Wein, Brettspiele und Dateikataloge. Sie können Ihre Sammlung in einer Katalog-Datenbank eingeben und viele verschiedene Eigenschaften wie Titel, Autor usw. darin speichern.

Tellico is an application for organising your collections. It provides default templates for books, bibliographies, videos, music, video games, coins, stamps, trading cards, comic books, and wines. It allows you to enter your collection in a catalogue database, saving many different properties like title, author, etc.

Tellico es una aplicación para organizar colecciones. Proporciona plantillas predeterminadas para libros, bibliografías, vídeos, música, videojuegos, monedas, sellos, cromos, cómics, y vinos. También le permite almacenar sus colecciones en una base de datos de catálogo, guardando diversas propiedades como el título, el autor, etc.

Tellico on kõikvõimalike kogude haldamiseks mõeldud rakendus. See pakub vaikimisi malle raamatute, bibliograafiate, videode, muusika, videomängude, müntide, markide, mängukaartide, koomiksite ja veinide tarbeks. Rakenduses saab oma kogu sisestada kataloogi andmebaasi, kuhu salvestatakse hulk omadusi, näiteks nimi, autor jne.

Tellico on sovellus kokoelmiesi järjestämiseen. Se tarjoaa oletusmallit kirjoille, lähdeluetteloille, videoille, musiikille, videopeleille, kolikoille, postimerkeille, keräilykorteille, sarjakuville ja viineille. Sen avulla voit syöttää kokoelmasi luettelotietokantaan eri ominaisuudet kuten otsikon, tekijän jne. säilyttäen.

Tellico est une application pour organiser vos collections. Il fournit des modèles par défaut pour les livres, les bibliographies, les vidéos, la musique, les jeux vidéo, les monnaies, les timbres, les cartes à collectionner, les bandes dessinées et les vins. Il vous permet de saisir votre collection dans une base de données de catalogue, enregistrant différentes propriétés telles que le titre, l'auteur, etc.

Tellico é un aplicativo para organizar coleccións. Fornece modelos predeterminados para libros, bibliografías, vídeos, música, videoxogos, moedas, selos, cartas de colección, banda deseñada, e vinos. Permítelle inserir a súa colección a unha base de datos de catálogo na que pode gardar moitas propiedades distintas, como o título de cada elemento, o seu autor, etc.

Tellico es un application pro organisar tu collectiones. Il forni patronos predefinite pro libros, bibliographias, videos, musica, video jocos, numisma, timbros, cartas de commercio, libros in designos e vinos. Il permitte te insertar tu collection inn un base de datos de catalogo, salveguardante multe proprietates como titulo, autor, etc.

Tellico adalah aplikasi untuk mengatur koleksi Anda. Ini menyediakan template baku untuk buku, bibliografi, video, musik, video game, koin, perangko, kartu perdagangan, buku komik, dan wine. Ini memungkinkan Anda untuk memasukkan koleksi Anda di database katalog, menyimpan banyak properti berbeda seperti judul, penulis, dll.

Tellico è un'applicazione per organizzare le tue collezioni. Fornisce modelli predefiniti per libri, bibliografie, video, musica, videogiochi, monete, francobolli, carte da gioco, fumetti e vini. Ti permette di inserire la tua collezione in una base di dati, salvando molte proprietà differenti come titolo, autore, ecc.

Tellico는 내 소장품을 관리하는 응용 프로그램입니다. 책, 서지, 비디오, 음악, 비디오 게임, 동전, 우표, 트레이드 카드, 만화 및 와인에 대한 기본 템플릿을 제공합니다. 카탈로그 데이터베이스에 소장품을 입력하여 제목, 작성자 등과 같은 다양한 속성을 저장할 수 있습니다.

Tellico is een toepassing voor het organiseren van uw verzamelingen. Het biedt standaard sjablonen voor boeken, bibliografieën, video's, muziek, videogames, munten, postzegels, visitekaartjes, strips en wijnen. Het stelt u in staat om uw verzameling in een catalogusdatabase in te voeren, waarin veel verschillende eigenschappen, zoals titel, auteur, etc. opgeslagen kunnen worden.

Tellico er eit program for å halda oversikt over samlingane dine. Det kjem med malar for bøker, litteraturlister, filmar, musikk, spel, myntar, frimerke, samlekort, teikneseriar og vin. Du kan katalogisera samlinga di i ein database og lagra mykje ulik informasjon, for eksempel tittel og forfattar.

Tellico jest programem do porządkowania twoich zbiorów. Dostarcza domyślnych szablonów dla książek, bibliografii, filmów, muzyki, gier wideo, monet, znaczków, kart handlowych, komiksów i win. Umożliwia wprowadzenie całego twojego zbioru do bazy danych katalogu, zapisując przy tym wiele różnych właściwości takich jak tytuł, autor, itp.

O Tellico é uma aplicação para organizar as suas colecções. Oferece modelos predefinidos para livros, bibliografias, vídeos, músicas, jogos de vídeo, moedas, selos, cromos, livros de banda desenhada, vinhos, entre outras colecções. Permite-lhe introduzir a sua colecção numa base de dados de catálogos, gravando diversas propriedades, como o título, o autor, etc.

Tellico é um aplicativo para organizar suas coleções. Oferece modelos predefinidos para livros, bibliografias, vídeos, músicas, jogos de vídeo game, moedas, selos, figurinhas, histórias em quadrinhos, vinhos, entre outras coleções. Permite-lhe introduzir sua coleção em um banco de dados de catálogos, gravando diversas propriedades, como título, autor, etc.

Tellico je aplikácia na organizáciu vašich zbierok. Poskytuje predvolené šablóny pre knihy, bibliografie, videá, hudbu, videohry, mince, známky, zberateľské karty, komiksy a vína. Umožní vám zadať vašu kolekciu do databázy katalógu, uložiť mnoho rozličných vlastností ako titul, autor atď.

Tellico je program za upravljanje vaših zbirk. Ponuja privzete predloge za knjige, bibliografije, videe, glasbo, igre, kovance, znamke, karte, stripe in vino. Omogoča vam, da vašo zbirko katalogizirate in shranite z različnimi lastnostmi kot so naslov, avtor, itd.

Tellico är ett program för att organisera samlingar. Det tillhandahåller standardmallar för böcker, bibliografier, videor, musik, videospel, mynt, frimärken, samlarbilder, serier och viner. Det låter dig mata in samlingen i en katalogdatabas, och spara många olika egenskaper som titel, författare, etc.

Tellico koleksiyonlarınızı yönetmek için bir programdır. Kitaplar, kaynakçalar, videolar, müzikler, video oyunları, paralar, pullar, kartlar, çizgi romanlar ve şaraplar için öntanımlı şablonlar içerir. Koleksiyonunuzu bir katalog veritabanına girmenize ve başlık, yazar gibi pek çok özelliğe göre kaydetmenize imkan tanır.

Tellico — програма для упорядковування ваших збірок. У ній передбачено типові шаблони для збірок книг, бібліографій, відео, музичних творів, відеоігор, нумізматики, марок, карток, коміксів та вин. За її допомогою ви зможете вести записи у форматі бази даних, зберігаючи різноманітні дані щодо елементів збірки, зокрема дані щодо назви, автора тощо.

xxTellico is an application for organizing your collections. It provides default templates for books, bibliographies, videos, music, video games, coins, stamps, trading cards, comic books, and wines. It allows you to enter your collection in a catalogue database, saving many different properties like title, author, etc.xx

Features:

Svojstva:

Característiques:

Característiques:

Vlastnosti:

Funktioner:

Funktionen:

Features:

Características:

Omadused:

Ominaisuuksia:

Fonctionnalités !

Funcionalidades:

Szolgáltatások:

Characteristicas:

Fitur:

Funzionalità:

기능:

Mogelijkheden:

Funksjonar:

Cechy:

Funcionalidades:

Funcionalidades:

Возможности:

Funkcie:

Zmožnosti:

Funktioner:

Özellikleri:

Можливості:

xxFeatures:xx

功能:

- http://tellico-project.org + https://tellico-project.org https://bugs.kde.org/enter_bug.cgi?format=guided&product=tellico - http://tellico-project.org/faq + https://tellico-project.org/faq https://docs.kde.org/development/en/extragear-office/tellico/index.html The main window showing a simple item in a book collection La finestra principal mostra un element senzill en una col·lecció de llibres La finestra principal mostra un element senzill en una col·lecció de llibres Das Hauptfenster mit einem einfachen Eintrag einer Bücher-Sammlung The main window showing a simple item in a book collection La ventana principal mostrando un elemento sencillo de una colección de libros La fenêtre principale, qui affiche un élément simple au sein d'une collection de livres A xanela principal, mostrando un elemento sinxelo nunha colección de libros Window utama menampilkan item sederhana dalam koleksi buku La finestra principale che mostra un semplice elemento in una collezione di libri 도서 소장품의 간단한 항목을 표시하는 주 창 Het hoofdvenster die een eenvoudig item in een boekenverzameling toont Hovudvindauget som viser ei enkel oppføring i ei boksamling Okno główne pokazujące prosty element w zbiorze książek A janela principal a mostrar um único item numa colecção de livros A janela principal a mostrar um único item em uma coleção de livros Huvudfönstret som visar ett enkelt objekt i en boksamling Головне вікно програми із простим записом у збірці книжок xxThe main window showing a simple item in a book collectionxx https://cdn.kde.org/screenshots/tellico/tellico.png The main window showing side-by-side views of a game collection La finestra principal mostra vistes un al costat de l'altra d'una col·lecció de jocs La finestra principal mostra vistes un al costat de l'altra d'una col·lecció de jocs Das Hauptfenster mit einer zweispaltigen Ansicht einer Spiele-Sammlung The main window showing side-by-side views of a game collection La ventana principal mostrando vistas en paralelo de una colección de juegos La fenêtre principale, qui affiche des vues côte-à-côte d'une collection de jeux A xanela principal mostrando as vistas lado a lado dunha colección de xogos Window utama menampilkan tampilan sisi ke sisi dari sebuah koleksi permainan La finestra principale che mostra le viste affiancate di una collezione di libri 게임 소장품을 나란히 표시하는 주 창 Het hoofdvenster die een spellenverzameling naast elkaar toont Hovudvindauget som viser side-ved-side-visingaar av ei spelsamling Okno główne pokazujące widoki zbioru gier obok siebie A janela principal a mostrar vistas lado-a-lado de uma colecção de jogos A janela principal mostrando visões lado a lado de uma coleção de jogos Huvudfönstret som visar en spelsamling med visning sida vid sida Головне вікно програми із панелями паралельного перегляду збірки ігор xxThe main window showing side-by-side views of a game collectionxx https://cdn.kde.org/screenshots/tellico/tellico-games.png KDE tellico - http://tellico-project.org/tellico-31-released + https://tellico-project.org/tellico-31-released - http://tellico-project.org/tellico-311-released + https://tellico-project.org/tellico-311-released - http://tellico-project.org/tellico-312-released + https://tellico-project.org/tellico-312-released - http://tellico-project.org/tellico-313-released + https://tellico-project.org/tellico-313-released - http://tellico-project.org/tellico-314-released + https://tellico-project.org/tellico-314-released - http://tellico-project.org/tellico-32-released + https://tellico-project.org/tellico-32-released - http://tellico-project.org/tellico-321-released + https://tellico-project.org/tellico-321-released
diff --git a/src/gui/datewidget.cpp b/src/gui/datewidget.cpp index e6502bb0..45d85e57 100644 --- a/src/gui/datewidget.cpp +++ b/src/gui/datewidget.cpp @@ -1,329 +1,330 @@ /*************************************************************************** Copyright (C) 2003-2009 Robby Stephenson ***************************************************************************/ /*************************************************************************** * * * 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) version 3 or any later version * * accepted by the membership of KDE e.V. (or its successor approved * * by the membership of KDE e.V.), which shall act as a proxy * * defined in Section 14 of version 3 of the license. * * * * 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 . * * * ***************************************************************************/ // this class borrows heavily from kdateedit.h in the kdepim module // which is Copyright (c) 2002 Cornelius Schumacher // and published under the LGPL #include "datewidget.h" #include "spinbox.h" #include #include +#include #include #include #include #include #include #include #include #include #include using Tellico::GUI::DateWidget; class DateWidget::DatePickerAction : public QWidgetAction { Q_OBJECT public: DatePickerAction( KDatePicker *widget, QObject *parent ) : QWidgetAction( parent ), mDatePicker( widget ), mOriginalParent( widget->parentWidget() ) { } protected: QWidget *createWidget( QWidget *parent ) Q_DECL_OVERRIDE { mDatePicker->setParent( parent ); return mDatePicker; } void deleteWidget( QWidget *widget ) Q_DECL_OVERRIDE { if ( widget != mDatePicker ) { return; } mDatePicker->setParent( mOriginalParent ); } private: KDatePicker *mDatePicker; QWidget *mOriginalParent; }; DateWidget::DateWidget(QWidget* parent_) : QWidget(parent_) { QBoxLayout* l = new QHBoxLayout(this); l->setContentsMargins(0, 0, 0, 0); // 0 allows empty value m_daySpin = new SpinBox(0, 31, this); l->addWidget(m_daySpin, 1); l->setStretchFactor(m_daySpin, 1); m_monthCombo = new KComboBox(false, this); l->addWidget(m_monthCombo, 1); l->setStretchFactor(m_monthCombo, 1); // allow empty item m_monthCombo->addItem(QString()); for(int i = 1; ; ++i) { QString str = QDate::longMonthName(i); if(str.isEmpty()) { break; } m_monthCombo->addItem(str); } m_yearSpin = new SpinBox(QDate::fromJulianDay(0).year(), 9999, this); l->addWidget(m_yearSpin, 1); l->setStretchFactor(m_yearSpin, 1); void (SpinBox::* valueChangedInt)(int) = &SpinBox::valueChanged; void (KComboBox::* activatedInt)(int) = &KComboBox::activated; connect(m_daySpin, valueChangedInt, this, &DateWidget::slotDateChanged); connect(m_monthCombo, activatedInt, this, &DateWidget::slotDateChanged); connect(m_yearSpin, valueChangedInt, this, &DateWidget::slotDateChanged); m_dateButton = new QPushButton(this); m_dateButton->setIcon(QIcon::fromTheme(QStringLiteral("view-pim-calendar"))); connect(m_dateButton, &QAbstractButton::clicked, this, &DateWidget::slotShowPicker); l->addWidget(m_dateButton, 0); m_menu = new QMenu(this); m_menu->hide(); m_picker = new KDatePicker(m_menu); m_picker->setCloseButton(false); connect(m_picker, &KDatePicker::dateEntered, this, &DateWidget::slotDateEntered); connect(m_picker, &KDatePicker::dateSelected, this, &DateWidget::slotDateSelected); m_menu->addAction(new DatePickerAction(m_picker, m_menu)); } DateWidget::~DateWidget() { } void DateWidget::slotDateChanged() { int day = m_daySpin->value(); day = qMin(qMax(day, m_daySpin->minimum()), m_daySpin->maximum()); int m = m_monthCombo->currentIndex(); m = qMin(qMax(m, 0), m_monthCombo->count()-1); int y = m_yearSpin->value(); y = qMin(qMax(y, m_yearSpin->minimum()), m_yearSpin->maximum()); // if all are valid, set this date if(day > m_daySpin->minimum() && m > 0 && y > m_yearSpin->minimum()) { QDate d(y, m, day); setDate(d); } emit signalModified(); } QDate DateWidget::date() const { // possible for either day, month, or year to be empty // in which case a null date is returned int day = m_daySpin->value(); // min value is the empty one if(day == m_daySpin->minimum()) { return QDate(); } int month = m_monthCombo->currentIndex(); if(month == 0) { return QDate(); } int year = m_yearSpin->value(); if(year == m_yearSpin->minimum()) { return QDate(); } return QDate(year, month, day); } QString DateWidget::text() const { // possible for either day, month, or year to be empty // but not all three bool empty = true; // format is "year-month-day" QString s; if(m_yearSpin->value() > m_yearSpin->minimum()) { s += QString::number(m_yearSpin->value()); empty = false; } s += QLatin1Char('-'); // first item is empty if(m_monthCombo->currentIndex() > 0) { // zero-pad to two digits if(m_monthCombo->currentIndex() < 10) { s += QLatin1Char('0'); } s += QString::number(m_monthCombo->currentIndex()); empty = false; } s += QLatin1Char('-'); if(m_daySpin->value() > m_daySpin->minimum()) { // zero-pad to two digits if(m_daySpin->value() < 10) { s += QLatin1Char('0'); } s += QString::number(m_daySpin->value()); empty = false; } return empty ? QString() : s; } void DateWidget::setDate(QDate date_) { m_daySpin->blockSignals(true); m_monthCombo->blockSignals(true); m_yearSpin->blockSignals(true); m_daySpin->setMaximum(date_.daysInMonth()); m_daySpin->setValue(date_.day()); m_monthCombo->setCurrentIndex(date_.month()); // don't subtract 1 since there's the blank first item m_yearSpin->setValue(date_.year()); m_daySpin->blockSignals(false); m_monthCombo->blockSignals(false); m_yearSpin->blockSignals(false); } void DateWidget::setDate(const QString& date_) { m_daySpin->blockSignals(true); m_monthCombo->blockSignals(true); m_yearSpin->blockSignals(true); QStringList s = date_.split(QLatin1Char('-')); bool ok = true; int y = s.count() > 0 ? s[0].toInt(&ok) : m_yearSpin->minimum(); if(!ok) { y = m_yearSpin->minimum(); ok = true; } y = qMin(qMax(y, m_yearSpin->minimum()), m_yearSpin->maximum()); m_yearSpin->setValue(y); int m = s.count() > 1 ? s[1].toInt(&ok) : 0; if(!ok) { m = 0; ok = true; } m = qMin(qMax(m, 0), m_monthCombo->count()-1); m_monthCombo->setCurrentIndex(m); // need to update number of days in month // for now set date to 1 QDate date(y, (m == 0 ? 1 : m), 1); m_daySpin->blockSignals(true); m_daySpin->setMaximum(date.daysInMonth()); m_daySpin->blockSignals(false); int day = s.count() > 2 ? s[2].toInt(&ok) : m_daySpin->minimum(); if(!ok) { day = m_daySpin->minimum(); } day = qMin(qMax(day, m_daySpin->minimum()), m_daySpin->maximum()); m_daySpin->setValue(day); m_daySpin->blockSignals(false); m_monthCombo->blockSignals(false); m_yearSpin->blockSignals(false); // if all are valid, set this date if(day > m_daySpin->minimum() && m > 0 && y > m_yearSpin->minimum()) { QDate d(y, m, day); m_picker->blockSignals(true); m_picker->setDate(d); m_picker->blockSignals(false); } } void DateWidget::clear() { m_daySpin->blockSignals(true); m_monthCombo->blockSignals(true); m_yearSpin->blockSignals(true); m_picker->blockSignals(true); m_daySpin->setValue(m_daySpin->minimum()); m_monthCombo->setCurrentIndex(0); m_yearSpin->setValue(m_yearSpin->minimum()); m_picker->setDate(QDate::currentDate()); m_daySpin->blockSignals(false); m_monthCombo->blockSignals(false); m_yearSpin->blockSignals(false); m_picker->blockSignals(false); } void DateWidget::slotShowPicker() { QRect desk = QApplication::desktop()->screenGeometry(this); QPoint popupPoint = mapToGlobal(QPoint(0, 0)); int dateFrameHeight = m_menu->sizeHint().height(); if(popupPoint.y() + height() + dateFrameHeight > desk.bottom()) { popupPoint.setY(popupPoint.y() - dateFrameHeight); } else { popupPoint.setY(popupPoint.y() + height()); } int dateFrameWidth = m_menu->sizeHint().width(); if(popupPoint.x() + width() > desk.right()) { popupPoint.setX(desk.right() - dateFrameWidth); } else { popupPoint.setX(popupPoint.x() + width() - dateFrameWidth); } if(popupPoint.x() < desk.left()) { popupPoint.setX( desk.left()); } if(popupPoint.y() < desk.top()) { popupPoint.setY(desk.top()); } QDate d = date(); if(d.isValid()) { m_picker->setDate(d); } m_menu->popup(popupPoint); } void DateWidget::slotDateSelected(QDate date_) { if(date_.isValid()) { setDate(date_); emit signalModified(); m_menu->hide(); } } void DateWidget::slotDateEntered(QDate date_) { if(date_.isValid()) { setDate(date_); emit signalModified(); } } #include "datewidget.moc" diff --git a/src/main.cpp b/src/main.cpp index 099ac4ab..9f8d20dc 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,185 +1,185 @@ /*************************************************************************** Copyright (C) 2001-2018 Robby Stephenson ***************************************************************************/ /*************************************************************************** * * * 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) version 3 or any later version * * accepted by the membership of KDE e.V. (or its successor approved * * by the membership of KDE e.V.), which shall act as a proxy * * defined in Section 14 of version 3 of the license. * * * * 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 #include "mainwindow.h" #include "translators/translators.h" // needed for file type enum #include #include #include #include #include #include #include #include #include #include #include #include int main(int argc, char* argv[]) { QApplication app(argc, argv); QGuiApplication::setAttribute(Qt::AA_UseHighDpiPixmaps); KLocalizedString::setApplicationDomain("tellico"); app.setApplicationVersion(QStringLiteral(TELLICO_VERSION)); Q_INIT_RESOURCE(icons); // Migrate KDE4 configuration and data files Kdelibs4ConfigMigrator migrator(QStringLiteral("tellico")); migrator.setConfigFiles(QStringList() << QStringLiteral("tellicorc")); migrator.setUiFiles(QStringList() << QStringLiteral("tellicoui.rc")); if(migrator.migrate()) { // migrate old data typedef QPair StringPair; QList filesToCopy; QList dirsToCreate; Kdelibs4Migration dataMigrator; const QString sourceBasePath = dataMigrator.saveLocation("data", QStringLiteral("tellico")); const QString targetBasePath = QStandardPaths::writableLocation(QStandardPaths::DataLocation) + QLatin1Char('/'); QString sourceFilePath, targetFilePath; // first copy tellico-common.xsl if exists QString fileName = QStringLiteral("tellico-common.xsl"); sourceFilePath = sourceBasePath + QLatin1Char('/') + fileName; targetFilePath = targetBasePath + QLatin1Char('/') + fileName; if(QFile::exists(sourceFilePath) && !QFile::exists(targetFilePath)) { filesToCopy << qMakePair(sourceFilePath, targetFilePath); } // then migrate data directories QStack dirsToCheck; dirsToCheck.push(QStringLiteral("report-templates")); dirsToCheck.push(QStringLiteral("entry-templates")); dirsToCheck.push(QStringLiteral("data-sources")); // this will copy all the images shared between collections dirsToCheck.push(QStringLiteral("data")); while(!dirsToCheck.isEmpty()) { QString dataDir = dirsToCheck.pop(); QDir sourceDir(sourceBasePath + dataDir); if(sourceDir.exists()) { if(!QDir().exists(targetBasePath + dataDir)) { dirsToCreate << (targetBasePath + dataDir); } // grab the internal directories, so we can be recursive QStringList moreDirs = sourceDir.entryList(QDir::Dirs | QDir::NoDotAndDotDot | QDir::NoSymLinks); foreach(const QString& moreDir, moreDirs) { dirsToCheck.push(dataDir + QLatin1Char('/') + moreDir); } QStringList fileNames = sourceDir.entryList(QDir::Files | QDir::NoDotAndDotDot | QDir::NoSymLinks); foreach(const QString& fileName, fileNames) { sourceFilePath = sourceBasePath + dataDir + QLatin1Char('/') + fileName; targetFilePath = targetBasePath + dataDir + QLatin1Char('/') + fileName; if(!QFile::exists(targetFilePath)) { filesToCopy << qMakePair(sourceFilePath, targetFilePath); } } } } foreach(const QString& dir, dirsToCreate) { QDir().mkpath(dir); } foreach(const StringPair& pair, filesToCopy) { QFile::copy(pair.first, pair.second); } // update the configuration cache KSharedConfig::openConfig()->reparseConfiguration(); } KCrash::initialize(); // component name = "tellico" is same as bugs.kde.org product name KAboutData aboutData(QStringLiteral("tellico"), QStringLiteral("Tellico"), QStringLiteral(TELLICO_VERSION), i18n("Tellico - a KDE collection manager"), KAboutLicense::GPL_V2, i18n("(c) 2001-2019, Robby Stephenson"), QString(), - QStringLiteral("http://tellico-project.org")); + QStringLiteral("https://tellico-project.org")); aboutData.addAuthor(QStringLiteral("Robby Stephenson"), QString(), QStringLiteral("robby@periapsis.org")); aboutData.addAuthor(QStringLiteral("Mathias Monnerville"), i18n("Data source scripts")); aboutData.addAuthor(QStringLiteral("Regis Boudin"), QString(), QStringLiteral("regis@boudin.name")); aboutData.addAuthor(QStringLiteral("Petri Damstén"), QString(), QStringLiteral("damu@iki.fi")); aboutData.addAuthor(QStringLiteral("Sebastian Held"), QString()); aboutData.addCredit(QStringLiteral("Virginie Quesnay"), i18n("Icons")); aboutData.addCredit(QStringLiteral("Amarok"), i18n("Code examples and general inspiration"), QString(), QStringLiteral("http://amarok.kde.org")); aboutData.addCredit(QStringLiteral("Greg Ward"), i18n("Author of btparse library")); aboutData.addCredit(QStringLiteral("Robert Gamble"), i18n("Author of libcsv library")); aboutData.addCredit(QStringLiteral("Valentin Lavrinenko"), i18n("Author of rtf2html library")); aboutData.addLicense(KAboutLicense::GPL_V3); QCommandLineParser parser; parser.addOption(QCommandLineOption(QStringList() << QStringLiteral("nofile"), i18n("Do not reopen the last open file"))); parser.addOption(QCommandLineOption(QStringList() << QStringLiteral("bibtex"), i18n("Import as a bibtex file"))); parser.addOption(QCommandLineOption(QStringList() << QStringLiteral("mods"), i18n("Import as a MODS file"))); parser.addOption(QCommandLineOption(QStringList() << QStringLiteral("ris"), i18n("Import as a RIS file"))); parser.addPositionalArgument(QStringLiteral("[filename]"), i18n("File to open")); aboutData.setupCommandLine(&parser); parser.process(app); aboutData.processCommandLine(&parser); KAboutData::setApplicationData(aboutData); if(app.isSessionRestored()) { RESTORE(Tellico::MainWindow); } else { Tellico::MainWindow* tellico = new Tellico::MainWindow(); tellico->show(); tellico->slotShowTipOfDay(false); // slotInit gets called out of a QTimer signal // but it wasn't always completing in-time // so call it manually to ensure it has finished tellico->slotInit(); QStringList args = parser.positionalArguments(); if(args.count() > 0) { if(parser.isSet(QStringLiteral("bibtex"))) { tellico->importFile(Tellico::Import::Bibtex, QUrl::fromUserInput(args.at(0)), Tellico::Import::Replace); } else if(parser.isSet(QStringLiteral("mods"))) { tellico->importFile(Tellico::Import::MODS, QUrl::fromUserInput(args.at(0)), Tellico::Import::Replace); } else if(parser.isSet(QStringLiteral("ris"))) { tellico->importFile(Tellico::Import::RIS, QUrl::fromUserInput(args.at(0)), Tellico::Import::Replace); } else { tellico->slotFileOpen(QUrl::fromUserInput(args.at(0), QDir::currentPath())); } } else { // bit of a hack, I just want the --nofile option // if --nofile is NOT passed, then the file option is set // is it's set, then go ahead and check for opening previous file tellico->initFileOpen(parser.isSet(QStringLiteral("nofile"))); } } return app.exec(); } diff --git a/src/tests/imagejobtest.cpp b/src/tests/imagejobtest.cpp index 344b0b60..393eecaa 100644 --- a/src/tests/imagejobtest.cpp +++ b/src/tests/imagejobtest.cpp @@ -1,373 +1,373 @@ /*************************************************************************** Copyright (C) 2017 Robby Stephenson ***************************************************************************/ /*************************************************************************** * * * 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) version 3 or any later version * * accepted by the membership of KDE e.V. (or its successor approved * * by the membership of KDE e.V.), which shall act as a proxy * * defined in Section 14 of version 3 of the license. * * * * 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 "imagejobtest.h" #include "../images/imagejob.h" #include "../images/imagefactory.h" #include "../images/imageinfo.h" #include #include #include #include #include QTEST_GUILESS_MAIN( ImageJobTest ) bool ImageJobTest::networkIsAvailable() { foreach(const QNetworkInterface& net, QNetworkInterface::allInterfaces()) { if(net.flags().testFlag(QNetworkInterface::IsUp) && !net.flags().testFlag(QNetworkInterface::IsLoopBack)) { return true; } } return false; } void ImageJobTest::initTestCase() { Tellico::ImageFactory::init(); } void ImageJobTest::cleanupTestCase() { } void ImageJobTest::init() { m_result = -1; m_imageId.clear(); } void ImageJobTest::enterLoop() { QEventLoop eventLoop; connect(this, &ImageJobTest::exitLoop, &eventLoop, &QEventLoop::quit); eventLoop.exec(QEventLoop::ExcludeUserInputEvents); } void ImageJobTest::slotGetResult(KJob* job) { m_result = job->error(); emit exitLoop(); } void ImageJobTest::slotAvailable(const QString& id_) { m_result = 0; m_imageId = id_; emit exitLoop(); } void ImageJobTest::testInvalidUrl() { QUrl u; Tellico::ImageJob* job = new Tellico::ImageJob(u); connect(job, &KJob::result, this, &ImageJobTest::slotGetResult); enterLoop(); QCOMPARE(m_result, int(KIO::ERR_MALFORMED_URL)); } void ImageJobTest::testNonexistant() { QUrl u(QStringLiteral("file:///non-existent-location")); Tellico::ImageJob* job = new Tellico::ImageJob(u); connect(job, &KJob::result, this, &ImageJobTest::slotGetResult); enterLoop(); QCOMPARE(m_result, int(KIO::ERR_CANNOT_OPEN_FOR_READING)); } void ImageJobTest::testUnreadable() { QTemporaryFile tmpFile; QVERIFY(tmpFile.open()); QVERIFY(tmpFile.setPermissions(QFileDevice::Permissions())); QUrl u = QUrl::fromLocalFile(tmpFile.fileName()); Tellico::ImageJob* job = new Tellico::ImageJob(u); connect(job, &KJob::result, this, &ImageJobTest::slotGetResult); enterLoop(); QCOMPARE(m_result, int(KIO::ERR_CANNOT_OPEN_FOR_READING)); const Tellico::Data::Image& img = job->image(); QVERIFY(img.isNull()); } void ImageJobTest::testImageInvalid() { // text file is an invalid image QUrl u = QUrl::fromLocalFile(QFINDTESTDATA("imagejobtest.cpp")); QPointer job = new Tellico::ImageJob(u); connect(job.data(), &KJob::result, this, &ImageJobTest::slotGetResult); enterLoop(); QCOMPARE(m_result, int(KIO::ERR_UNKNOWN)); const Tellico::Data::Image& img = job->image(); QVERIFY(img.isNull()); QCOMPARE(img.linkOnly(), false); } void ImageJobTest::testImageLoad() { QUrl u = QUrl::fromLocalFile(QFINDTESTDATA("../../icons/tellico.png")); QPointer job = new Tellico::ImageJob(u); connect(job.data(), &KJob::result, this, &ImageJobTest::slotGetResult); enterLoop(); // success! QCOMPARE(m_result, 0); const Tellico::Data::Image& img = job->image(); QVERIFY(!img.isNull()); QCOMPARE(img.id(), QStringLiteral("dde5bf2cbd90fad8635a26dfb362e0ff.png")); QCOMPARE(img.format(), QByteArray("png")); QCOMPARE(img.linkOnly(), false); // check that the job is automatically deleted qApp->processEvents(); QVERIFY(!job); } void ImageJobTest::testImageLoadWithId() { QUrl u = QUrl::fromLocalFile(QFINDTESTDATA("../../icons/tellico.png")); QPointer job = new Tellico::ImageJob(u, QStringLiteral("tellico-rocks")); connect(job.data(), &KJob::result, this, &ImageJobTest::slotGetResult); enterLoop(); // success! QCOMPARE(m_result, 0); const Tellico::Data::Image& img = job->image(); QVERIFY(!img.isNull()); QCOMPARE(img.id(), QStringLiteral("tellico-rocks")); QCOMPARE(img.format(), QByteArray("png")); QCOMPARE(img.linkOnly(), false); } void ImageJobTest::testImageLink() { QUrl u = QUrl::fromLocalFile(QFINDTESTDATA("../../icons/tellico.png")); QPointer job = new Tellico::ImageJob(u, QString() /* id */, false /* quiet */); job->setLinkOnly(true); connect(job.data(), &KJob::result, this, &ImageJobTest::slotGetResult); enterLoop(); // success! QCOMPARE(m_result, 0); const Tellico::Data::Image& img = job->image(); QVERIFY(!img.isNull()); // id is not the MD5 hash QVERIFY(img.id() != QStringLiteral("dde5bf2cbd90fad8635a26dfb362e0ff.png")); QCOMPARE(img.format(), QByteArray("png")); QCOMPARE(img.linkOnly(), true); } void ImageJobTest::testNetworkImage() { if(!networkIsAvailable()) { QSKIP("This test requires network access", SkipSingle); return; } QUrl u(QStringLiteral("http://tellico-project.org/sites/default/files/logo.png")); QPointer job = new Tellico::ImageJob(u); connect(job.data(), &KJob::result, this, &ImageJobTest::slotGetResult); enterLoop(); // success! QCOMPARE(m_result, 0); const Tellico::Data::Image& img = job->image(); QVERIFY(!img.isNull()); QCOMPARE(img.id(), QStringLiteral("757322046f4aa54290a3d92b05b71ca1.png")); QCOMPARE(img.format(), QByteArray("png")); QCOMPARE(img.linkOnly(), false); // check that the job is automatically deleted qApp->processEvents(); QVERIFY(!job); } void ImageJobTest::testNetworkImageLink() { if(!networkIsAvailable()) { QSKIP("This test requires network access", SkipSingle); return; } - QUrl u(QStringLiteral("http://tellico-project.org/sites/default/files/logo.png")); + QUrl u(QStringLiteral("https://tellico-project.org/sites/default/files/logo.png")); QPointer job = new Tellico::ImageJob(u, QString() /* id */, false /* quiet */); job->setLinkOnly(true); connect(job.data(), &KJob::result, this, &ImageJobTest::slotGetResult); enterLoop(); // success! QCOMPARE(m_result, 0); const Tellico::Data::Image& img = job->image(); QVERIFY(!img.isNull()); QCOMPARE(img.id(), u.url()); QCOMPARE(img.format(), QByteArray("png")); QCOMPARE(img.linkOnly(), true); } void ImageJobTest::testNetworkImageInvalid() { if(!networkIsAvailable()) { QSKIP("This test requires network access", SkipSingle); return; } - QUrl u(QStringLiteral("http://tellico-project.org")); + QUrl u(QStringLiteral("https://tellico-project.org")); QPointer job = new Tellico::ImageJob(u); connect(job.data(), &KJob::result, this, &ImageJobTest::slotGetResult); enterLoop(); QCOMPARE(m_result, int(KIO::ERR_UNKNOWN)); const Tellico::Data::Image& img = job->image(); QVERIFY(img.isNull()); } void ImageJobTest::testFactoryRequestLocal() { QVERIFY(m_imageId.isEmpty()); connect(Tellico::ImageFactory::self(), &Tellico::ImageFactory::imageAvailable, this, &ImageJobTest::slotAvailable); QUrl u = QUrl::fromLocalFile(QFINDTESTDATA("../../icons/tellico.png")); Tellico::ImageFactory::requestImageById(u.url()); // don't need to enter loop since the image is local and signal fires immediately QVERIFY(!m_imageId.isEmpty()); // success! QCOMPARE(m_result, 0); const Tellico::Data::Image& img = Tellico::ImageFactory::imageById(m_imageId); QVERIFY(!img.isNull()); // id is not the MD5 hash QVERIFY(img.id() != QStringLiteral("dde5bf2cbd90fad8635a26dfb362e0ff.png")); QCOMPARE(img.format(), QByteArray("png")); QCOMPARE(img.linkOnly(), true); } void ImageJobTest::testFactoryRequestLocalInvalid() { QVERIFY(m_imageId.isEmpty()); QSignalSpy spy(Tellico::ImageFactory::self(), &Tellico::ImageFactory::imageAvailable); connect(Tellico::ImageFactory::self(), &Tellico::ImageFactory::imageAvailable, this, &ImageJobTest::slotAvailable); // text file is an invalid image QUrl u = QUrl::fromLocalFile(QFINDTESTDATA("imagejobtest.cpp")); Tellico::ImageFactory::requestImageById(u.url()); // it will be a null image, but a local url, so image is still loaded with immediate signal QCOMPARE(spy.count(), 1); // the available image id is the url QCOMPARE(m_imageId, u.url()); // now try to load it const Tellico::Data::Image& img = Tellico::ImageFactory::imageById(m_imageId); QVERIFY(img.isNull()); QCOMPARE(img.linkOnly(), false); // make sure the null image list is updated QVERIFY(Tellico::ImageFactory::self()->hasNullImage(m_imageId)); // the image should not be in local memory now QVERIFY(!Tellico::ImageFactory::self()->hasImageInMemory(m_imageId)); QVERIFY(!Tellico::ImageFactory::self()->hasImageInfo(m_imageId)); } void ImageJobTest::testFactoryRequestNetwork() { if(!networkIsAvailable()) { QSKIP("This test requires network access", SkipSingle); return; } QVERIFY(m_imageId.isEmpty()); connect(Tellico::ImageFactory::self(), &Tellico::ImageFactory::imageAvailable, this, &ImageJobTest::slotAvailable); - QUrl u(QStringLiteral("http://tellico-project.org/sites/default/files/logo.png")); + QUrl u(QStringLiteral("https://tellico-project.org/sites/default/files/logo.png")); Tellico::ImageFactory::requestImageById(u.url()); enterLoop(); QVERIFY(!m_imageId.isEmpty()); // success! QCOMPARE(m_result, 0); // the image should be in local memory now QVERIFY(Tellico::ImageFactory::self()->hasImageInMemory(m_imageId)); QVERIFY(Tellico::ImageFactory::self()->hasImageInfo(m_imageId)); const Tellico::Data::Image& img = Tellico::ImageFactory::imageById(m_imageId); QVERIFY(!img.isNull()); // id is the MD5 hash, since it's not link only QCOMPARE(img.id(), QStringLiteral("757322046f4aa54290a3d92b05b71ca1.png")); QCOMPARE(img.format(), QByteArray("png")); QCOMPARE(img.linkOnly(), false); } void ImageJobTest::testFactoryRequestNetworkLinkOnly() { if(!networkIsAvailable()) { QSKIP("This test requires network access", SkipSingle); return; } QVERIFY(m_imageId.isEmpty()); connect(Tellico::ImageFactory::self(), &Tellico::ImageFactory::imageAvailable, this, &ImageJobTest::slotAvailable); - QUrl u(QStringLiteral("http://tellico-project.org/sites/default/files/logo.png")); + QUrl u(QStringLiteral("https://tellico-project.org/sites/default/files/logo.png")); // first, tell the image factory that the image is link only Tellico::Data::ImageInfo info(u.url(), "PNG", 64, 64, true /* link only */); Tellico::ImageFactory::cacheImageInfo(info); Tellico::ImageFactory::requestImageById(u.url()); enterLoop(); QVERIFY(!m_imageId.isEmpty()); // success! QCOMPARE(m_result, 0); // the image should be in local memory now QVERIFY(Tellico::ImageFactory::self()->hasImageInMemory(m_imageId)); QVERIFY(Tellico::ImageFactory::self()->hasImageInfo(m_imageId)); const Tellico::Data::Image& img = Tellico::ImageFactory::imageById(m_imageId); QVERIFY(!img.isNull()); // id is not the MD5 hash QCOMPARE(img.id(), u.url()); QCOMPARE(img.format(), QByteArray("png")); QCOMPARE(img.linkOnly(), true); } diff --git a/src/tests/tellicoreadtest.cpp b/src/tests/tellicoreadtest.cpp index 425150c7..01269d72 100644 --- a/src/tests/tellicoreadtest.cpp +++ b/src/tests/tellicoreadtest.cpp @@ -1,339 +1,339 @@ /*************************************************************************** Copyright (C) 2009 Robby Stephenson ***************************************************************************/ /*************************************************************************** * * * 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) version 3 or any later version * * accepted by the membership of KDE e.V. (or its successor approved * * by the membership of KDE e.V.), which shall act as a proxy * * defined in Section 14 of version 3 of the license. * * * * 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 "tellicoreadtest.h" #include "../translators/tellicoimporter.h" #include "../collections/bookcollection.h" #include "../collections/coincollection.h" #include "../collectionfactory.h" #include "../translators/tellicoxmlexporter.h" #include "../translators/tellico_xml.h" #include "../images/imagefactory.h" #include "../images/image.h" #include "../fieldformat.h" #include "../entry.h" #include "../utils/xmlhandler.h" #include QTEST_GUILESS_MAIN( TellicoReadTest ) #define QSL(x) QStringLiteral(x) #define TELLICOREAD_NUMBER_OF_CASES 10 void TellicoReadTest::initTestCase() { // need to register this first Tellico::RegisterCollection registerBook(Tellico::Data::Collection::Book, "book"); Tellico::RegisterCollection registerCoin(Tellico::Data::Collection::Coin, "coin"); Tellico::RegisterCollection registerBase(Tellico::Data::Collection::Base, "entry"); for(int i = 1; i < TELLICOREAD_NUMBER_OF_CASES; ++i) { QUrl url = QUrl::fromLocalFile(QFINDTESTDATA(QSL("data/books-format%1.bc").arg(i))); Tellico::Import::TellicoImporter importer(url); Tellico::Data::CollPtr coll = importer.collection(); m_collections.append(coll); } Tellico::ImageFactory::init(); } void TellicoReadTest::init() { Tellico::ImageFactory::clean(true); } void TellicoReadTest::testBookCollection() { Tellico::Data::CollPtr coll1 = m_collections[0]; // skip the first one for(int i = 1; i < m_collections.count(); ++i) { Tellico::Data::CollPtr coll2 = m_collections[i]; QVERIFY(coll2); QCOMPARE(coll1->type(), coll2->type()); QCOMPARE(coll1->title(), coll2->title()); QCOMPARE(coll1->entryCount(), coll2->entryCount()); } } void TellicoReadTest::testEntries() { QFETCH(QString, fieldName); Tellico::Data::FieldPtr field1 = m_collections[0]->fieldByName(fieldName); // skip the first one for(int i = 1; i < m_collections.count(); ++i) { Tellico::Data::FieldPtr field2 = m_collections[i]->fieldByName(fieldName); if(field1 && field2) { QCOMPARE(field1->name(), field2->name()); QCOMPARE(field1->title(), field2->title()); QCOMPARE(field1->category(), field2->category()); QCOMPARE(field1->type(), field2->type()); QCOMPARE(field1->flags(), field2->flags()); QCOMPARE(field1->propertyList(), field2->propertyList()); } for(int j = 0; j < m_collections[0]->entryCount(); ++j) { // don't test id values since the initial value has changed from 0 to 1 Tellico::Data::EntryPtr entry1 = m_collections[0]->entries().at(j); Tellico::Data::EntryPtr entry2 = m_collections[i]->entries().at(j); QVERIFY(entry1); QVERIFY(entry2); QCOMPARE(entry1->field(fieldName), entry2->field(fieldName)); } } } void TellicoReadTest::testEntries_data() { QTest::addColumn("fieldName"); QTest::newRow("title") << QSL("title"); QTest::newRow("author") << QSL("author"); QTest::newRow("publisher") << QSL("publisher"); QTest::newRow("keywords") << QSL("keywords"); QTest::newRow("keyword") << QSL("keyword"); QTest::newRow("genre") << QSL("genre"); QTest::newRow("isbn") << QSL("isbn"); QTest::newRow("pub_year") << QSL("pub_year"); QTest::newRow("rating") << QSL("rating"); QTest::newRow("comments") << QSL("comments"); } void TellicoReadTest::testCoinCollection() { QUrl url = QUrl::fromLocalFile(QFINDTESTDATA("data/coins-format9.tc")); Tellico::Import::TellicoImporter importer(url); Tellico::Data::CollPtr coll = importer.collection(); QVERIFY(coll); QCOMPARE(coll->type(), Tellico::Data::Collection::Coin); Tellico::Data::FieldPtr field = coll->fieldByName(QStringLiteral("title")); // old field has Dependent value, now is Line QVERIFY(field); QCOMPARE(field->type(), Tellico::Data::Field::Line); QCOMPARE(field->title(), QSL("Title")); QVERIFY(field->hasFlag(Tellico::Data::Field::Derived)); Tellico::Data::EntryPtr entry = coll->entries().at(0); // test creating the derived title QCOMPARE(entry->title(), QSL("1974D Jefferson Nickel 0.05")); } void TellicoReadTest::testTableData() { QUrl url = QUrl::fromLocalFile(QFINDTESTDATA("/data/tabletest.tc")); Tellico::Import::TellicoImporter importer(url); Tellico::Data::CollPtr coll = importer.collection(); QVERIFY(coll); QCOMPARE(coll->entryCount(), 3); Tellico::Export::TellicoXMLExporter exporter(coll); exporter.setEntries(coll->entries()); Tellico::Import::TellicoImporter importer2(exporter.text()); Tellico::Data::CollPtr coll2 = importer2.collection(); QVERIFY(coll2); QCOMPARE(coll2->type(), coll->type()); QCOMPARE(coll2->entryCount(), coll->entryCount()); foreach(Tellico::Data::EntryPtr e1, coll->entries()) { Tellico::Data::EntryPtr e2 = coll2->entryById(e1->id()); QVERIFY(e2); foreach(Tellico::Data::FieldPtr f, coll->fields()) { QCOMPARE(f->name() + e1->field(f), f->name() + e2->field(f)); } } // test table value concatenation Tellico::Data::EntryPtr e3(new Tellico::Data::Entry(coll)); coll->addEntries(e3); QString value = QSL("11a") + Tellico::FieldFormat::delimiterString() + QSL("11b") + Tellico::FieldFormat::columnDelimiterString() + QSL("12") + Tellico::FieldFormat::columnDelimiterString() + QSL("13") + Tellico::FieldFormat::rowDelimiterString() + QSL("21") + Tellico::FieldFormat::columnDelimiterString() + QSL("22") + Tellico::FieldFormat::columnDelimiterString() + QSL("23"); e3->setField(QSL("table"), value); QStringList groups = e3->groupNamesByFieldName(QStringLiteral("table")); QCOMPARE(groups.count(), 3); // the order of the group names is not stable (it uses QSet::toList) QCOMPARE(groups.toSet(), QSet() << QSL("11a") << QSL("11b") << QSL("21")); // test having empty value in table Tellico::Data::EntryPtr e = coll2->entryById(2); QVERIFY(e); const QStringList rows = Tellico::FieldFormat::splitTable(e->field(QSL("table"))); QCOMPARE(rows.count(), 1); const QStringList cols = Tellico::FieldFormat::splitRow(rows.at(0)); QCOMPARE(cols.count(), 3); } void TellicoReadTest::testDuplicateLoans() { QUrl url = QUrl::fromLocalFile(QFINDTESTDATA("/data/duplicate_loan.xml")); Tellico::Import::TellicoImporter importer(url); Tellico::Data::CollPtr coll = importer.collection(); QVERIFY(coll); QCOMPARE(coll->borrowers().count(), 1); Tellico::Data::BorrowerPtr bor = coll->borrowers().first(); QVERIFY(bor); QCOMPARE(bor->loans().count(), 1); } void TellicoReadTest::testDuplicateBorrowers() { QUrl url = QUrl::fromLocalFile(QFINDTESTDATA("/data/duplicate_borrower.xml")); Tellico::Import::TellicoImporter importer(url); Tellico::Data::CollPtr coll = importer.collection(); QVERIFY(coll); QCOMPARE(coll->borrowers().count(), 1); Tellico::Data::BorrowerPtr bor = coll->borrowers().first(); QVERIFY(bor); QCOMPARE(bor->loans().count(), 2); } void TellicoReadTest::testLocalImage() { // this is the md5 hash of the tellico.png icon, used as an image id const QString imageId(QSL("dde5bf2cbd90fad8635a26dfb362e0ff.png")); // not yet loaded QVERIFY(!Tellico::ImageFactory::self()->hasImageInMemory(imageId)); QVERIFY(!Tellico::ImageFactory::self()->hasImageInfo(imageId)); QUrl url = QUrl::fromLocalFile(QFINDTESTDATA("/data/local_image.xml")); QFile f(url.toLocalFile()); QVERIFY(f.exists()); QVERIFY(f.open(QIODevice::ReadOnly | QIODevice::Text)); QTextStream in(&f); QString fileText = in.readAll(); // replace %COVER% with image file location fileText.replace(QSL("%COVER%"), QFINDTESTDATA("../../icons/tellico.png")); Tellico::Import::TellicoImporter importer(fileText); Tellico::Data::CollPtr coll = importer.collection(); QVERIFY(coll); QCOMPARE(coll->entries().count(), 1); Tellico::Data::EntryPtr entry = coll->entries().at(0); QVERIFY(entry); QCOMPARE(entry->field(QStringLiteral("cover")), imageId); // the image should be in local memory now QVERIFY(Tellico::ImageFactory::self()->hasImageInMemory(imageId)); QVERIFY(Tellico::ImageFactory::self()->hasImageInfo(imageId)); const Tellico::Data::Image& img = Tellico::ImageFactory::imageById(imageId); QVERIFY(!img.isNull()); } void TellicoReadTest::testRemoteImage() { // this is the md5 hash of the logo.png icon, used as an image id const QString imageId(QSL("757322046f4aa54290a3d92b05b71ca1.png")); // not yet loaded QVERIFY(!Tellico::ImageFactory::self()->hasImageInMemory(imageId)); QVERIFY(!Tellico::ImageFactory::self()->hasImageInfo(imageId)); QUrl url = QUrl::fromLocalFile(QFINDTESTDATA("/data/local_image.xml")); QFile f(url.toLocalFile()); QVERIFY(f.exists()); QVERIFY(f.open(QIODevice::ReadOnly | QIODevice::Text)); QTextStream in(&f); QString fileText = in.readAll(); // replace %COVER% with image file location fileText.replace(QSL("%COVER%"), - QSL("http://tellico-project.org/sites/default/files/logo.png")); + QSL("https://tellico-project.org/sites/default/files/logo.png")); Tellico::Import::TellicoImporter importer(fileText); Tellico::Data::CollPtr coll = importer.collection(); QVERIFY(coll); QCOMPARE(coll->entries().count(), 1); Tellico::Data::EntryPtr entry = coll->entries().at(0); QVERIFY(entry); QCOMPARE(entry->field(QStringLiteral("cover")), imageId); // the image should be in local memory now QVERIFY(Tellico::ImageFactory::self()->hasImageInMemory(imageId)); QVERIFY(Tellico::ImageFactory::self()->hasImageInfo(imageId)); const Tellico::Data::Image& img = Tellico::ImageFactory::imageById(imageId); QVERIFY(!img.isNull()); } void TellicoReadTest::testXMLHandler() { QFETCH(QByteArray, data); QFETCH(QString, expectedString); QFETCH(bool, changeEncoding); QString origString = QString::fromUtf8(data); QCOMPARE(Tellico::XMLHandler::readXMLData(data), expectedString); QCOMPARE(Tellico::XMLHandler::setUtf8XmlEncoding(origString), changeEncoding); } void TellicoReadTest::testXMLHandler_data() { QTest::addColumn("data"); QTest::addColumn("expectedString"); QTest::addColumn("changeEncoding"); QTest::newRow("basic") << QByteArray("value") << QStringLiteral("value") << false; QTest::newRow("utf8") << QByteArray("\nvalue") << QStringLiteral("\nvalue") << false; QTest::newRow("latin1") << QByteArray("\nvalue") << QStringLiteral("\nvalue") << true; } void TellicoReadTest::testXmlName() { QFETCH(bool, valid); QFETCH(QString, input); QFETCH(QString, modified); QCOMPARE(Tellico::XML::validXMLElementName(input), valid); QCOMPARE(Tellico::XML::elementName(input), modified); } void TellicoReadTest::testXmlName_data() { QTest::addColumn("valid"); QTest::addColumn("input"); QTest::addColumn("modified"); QTest::newRow("start") << true << QSL("start") << QSL("start"); QTest::newRow("_start") << true << QSL("_start") << QSL("_start"); QTest::newRow("n42") << true << QSL("n42") << QSL("n42"); // an empty string is handled in CollectionFieldsDialog when creating the field name QTest::newRow("42") << false << QSL("42") << QString(); QTest::newRow("she is") << false << QSL("she is") << QSL("she-is"); QTest::newRow("colon:") << true << QSL("colon:") << QSL("colon:"); QTest::newRow("Svět") << true << QSL("Svět") << QSL("Svět"); QTest::newRow("") << false << QSL("") << QSL("test"); }