diff --git a/.arcconfig b/.arcconfig new file mode 100644 index 0000000..377c7ec --- /dev/null +++ b/.arcconfig @@ -0,0 +1,3 @@ +{ + "phabricator.uri" : "https://phabricator.kde.org/" +} diff --git a/COPYING.DOC b/COPYING.DOC index 4a0fe1c..71ec2c4 100644 --- a/COPYING.DOC +++ b/COPYING.DOC @@ -1,397 +1,397 @@ GNU Free Documentation License Version 1.2, November 2002 Copyright (C) 2000,2001,2002 Free Software Foundation, Inc. 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. 0. PREAMBLE The purpose of this License is to make a manual, textbook, or other functional and useful document "free" in the sense of freedom: to assure everyone the effective freedom to copy and redistribute it, with or without modifying it, either commercially or noncommercially. Secondarily, this License preserves for the author and publisher a way to get credit for their work, while not being considered responsible for modifications made by others. This License is a kind of "copyleft", which means that derivative works of the document must themselves be free in the same sense. It complements the GNU General Public License, which is a copyleft license designed for free software. We have designed this License in order to use it for manuals for free software, because free software needs free documentation: a free program should come with manuals providing the same freedoms that the software does. But this License is not limited to software manuals; it can be used for any textual work, regardless of subject matter or whether it is published as a printed book. We recommend this License principally for works whose purpose is instruction or reference. 1. APPLICABILITY AND DEFINITIONS This License applies to any manual or other work, in any medium, that contains a notice placed by the copyright holder saying it can be distributed under the terms of this License. Such a notice grants a world-wide, royalty-free license, unlimited in duration, to use that work under the conditions stated herein. The "Document", below, refers to any such manual or work. Any member of the public is a licensee, and is addressed as "you". You accept the license if you copy, modify or distribute the work in a way requiring permission under copyright law. A "Modified Version" of the Document means any work containing the Document or a portion of it, either copied verbatim, or with modifications and/or translated into another language. A "Secondary Section" is a named appendix or a front-matter section of the Document that deals exclusively with the relationship of the publishers or authors of the Document to the Document's overall subject (or to related matters) and contains nothing that could fall directly within that overall subject. (Thus, if the Document is in part a textbook of mathematics, a Secondary Section may not explain any mathematics.) The relationship could be a matter of historical connection with the subject or with related matters, or of legal, commercial, philosophical, ethical or political position regarding them. The "Invariant Sections" are certain Secondary Sections whose titles are designated, as being those of Invariant Sections, in the notice that says that the Document is released under this License. If a section does not fit the above definition of Secondary then it is not allowed to be designated as Invariant. The Document may contain zero Invariant Sections. If the Document does not identify any Invariant Sections then there are none. The "Cover Texts" are certain short passages of text that are listed, as Front-Cover Texts or Back-Cover Texts, in the notice that says that the Document is released under this License. A Front-Cover Text may be at most 5 words, and a Back-Cover Text may be at most 25 words. A "Transparent" copy of the Document means a machine-readable copy, represented in a format whose specification is available to the general public, that is suitable for revising the document straightforwardly with generic text editors or (for images composed of pixels) generic paint programs or (for drawings) some widely available drawing editor, and that is suitable for input to text formatters or for automatic translation to a variety of formats suitable for input to text formatters. A copy made in an otherwise Transparent file format whose markup, or absence of markup, has been arranged to thwart or discourage subsequent modification by readers is not Transparent. An image format is not Transparent if used for any substantial amount of text. A copy that is not "Transparent" is called "Opaque". Examples of suitable formats for Transparent copies include plain ASCII without markup, Texinfo input format, LaTeX input format, SGML or XML using a publicly available DTD, and standard-conforming simple HTML, PostScript or PDF designed for human modification. Examples of transparent image formats include PNG, XCF and JPG. Opaque formats include proprietary formats that can be read and edited only by proprietary word processors, SGML or XML for which the DTD and/or processing tools are not generally available, and the machine-generated HTML, PostScript or PDF produced by some word processors for output purposes only. The "Title Page" means, for a printed book, the title page itself, plus such following pages as are needed to hold, legibly, the material this License requires to appear in the title page. For works in formats which do not have any title page as such, "Title Page" means the text near the most prominent appearance of the work's title, preceding the beginning of the body of the text. A section "Entitled XYZ" means a named subunit of the Document whose title either is precisely XYZ or contains XYZ in parentheses following text that translates XYZ in another language. (Here XYZ stands for a specific section name mentioned below, such as "Acknowledgements", "Dedications", "Endorsements", or "History".) To "Preserve the Title" of such a section when you modify the Document means that it remains a section "Entitled XYZ" according to this definition. The Document may include Warranty Disclaimers next to the notice which states that this License applies to the Document. These Warranty Disclaimers are considered to be included by reference in this License, but only as regards disclaiming warranties: any other implication that these Warranty Disclaimers may have is void and has no effect on the meaning of this License. 2. VERBATIM COPYING You may copy and distribute the Document in any medium, either commercially or noncommercially, provided that this License, the copyright notices, and the license notice saying this License applies to the Document are reproduced in all copies, and that you add no other conditions whatsoever to those of this License. You may not use technical measures to obstruct or control the reading or further copying of the copies you make or distribute. However, you may accept compensation in exchange for copies. If you distribute a large enough number of copies you must also follow the conditions in section 3. You may also lend copies, under the same conditions stated above, and you may publicly display copies. 3. COPYING IN QUANTITY If you publish printed copies (or copies in media that commonly have printed covers) of the Document, numbering more than 100, and the Document's license notice requires Cover Texts, you must enclose the copies in covers that carry, clearly and legibly, all these Cover Texts: Front-Cover Texts on the front cover, and Back-Cover Texts on the back cover. Both covers must also clearly and legibly identify you as the publisher of these copies. The front cover must present the full title with all words of the title equally prominent and visible. You may add other material on the covers in addition. Copying with changes limited to the covers, as long as they preserve the title of the Document and satisfy these conditions, can be treated as verbatim copying in other respects. If the required texts for either cover are too voluminous to fit legibly, you should put the first ones listed (as many as fit reasonably) on the actual cover, and continue the rest onto adjacent pages. If you publish or distribute Opaque copies of the Document numbering more than 100, you must either include a machine-readable Transparent copy along with each Opaque copy, or state in or with each Opaque copy a computer-network location from which the general network-using public has access to download using public-standard network protocols a complete Transparent copy of the Document, free of added material. If you use the latter option, you must take reasonably prudent steps, when you begin distribution of Opaque copies in quantity, to ensure that this Transparent copy will remain thus accessible at the stated location until at least one year after the last time you distribute an Opaque copy (directly or through your agents or retailers) of that edition to the public. It is requested, but not required, that you contact the authors of the Document well before redistributing any large number of copies, to give them a chance to provide you with an updated version of the Document. 4. MODIFICATIONS You may copy and distribute a Modified Version of the Document under the conditions of sections 2 and 3 above, provided that you release the Modified Version under precisely this License, with the Modified Version filling the role of the Document, thus licensing distribution and modification of the Modified Version to whoever possesses a copy of it. In addition, you must do these things in the Modified Version: A. Use in the Title Page (and on the covers, if any) a title distinct from that of the Document, and from those of previous versions (which should, if there were any, be listed in the History section of the Document). You may use the same title as a previous version if the original publisher of that version gives permission. B. List on the Title Page, as authors, one or more persons or entities responsible for authorship of the modifications in the Modified Version, together with at least five of the principal authors of the Document (all of its principal authors, if it has fewer than five), unless they release you from this requirement. C. State on the Title page the name of the publisher of the Modified Version, as the publisher. D. Preserve all the copyright notices of the Document. E. Add an appropriate copyright notice for your modifications adjacent to the other copyright notices. F. Include, immediately after the copyright notices, a license notice giving the public permission to use the Modified Version under the terms of this License, in the form shown in the Addendum below. G. Preserve in that license notice the full lists of Invariant Sections and required Cover Texts given in the Document's license notice. H. Include an unaltered copy of this License. I. Preserve the section Entitled "History", Preserve its Title, and add to it an item stating at least the title, year, new authors, and publisher of the Modified Version as given on the Title Page. If there is no section Entitled "History" in the Document, create one stating the title, year, authors, and publisher of the Document as given on its Title Page, then add an item describing the Modified Version as stated in the previous sentence. J. Preserve the network location, if any, given in the Document for public access to a Transparent copy of the Document, and likewise the network locations given in the Document for previous versions it was based on. These may be placed in the "History" section. You may omit a network location for a work that was published at least four years before the Document itself, or if the original publisher of the version it refers to gives permission. K. For any section Entitled "Acknowledgements" or "Dedications", Preserve the Title of the section, and preserve in the section all the substance and tone of each of the contributor acknowledgements and/or dedications given therein. L. Preserve all the Invariant Sections of the Document, unaltered in their text and in their titles. Section numbers or the equivalent are not considered part of the section titles. M. Delete any section Entitled "Endorsements". Such a section may not be included in the Modified Version. N. Do not retitle any existing section to be Entitled "Endorsements" or to conflict in title with any Invariant Section. O. Preserve any Warranty Disclaimers. If the Modified Version includes new front-matter sections or appendices that qualify as Secondary Sections and contain no material copied from the Document, you may at your option designate some or all of these sections as invariant. To do this, add their titles to the list of Invariant Sections in the Modified Version's license notice. These titles must be distinct from any other section titles. You may add a section Entitled "Endorsements", provided it contains nothing but endorsements of your Modified Version by various parties--for example, statements of peer review or that the text has been approved by an organization as the authoritative definition of a standard. You may add a passage of up to five words as a Front-Cover Text, and a passage of up to 25 words as a Back-Cover Text, to the end of the list of Cover Texts in the Modified Version. Only one passage of Front-Cover Text and one of Back-Cover Text may be added by (or through arrangements made by) any one entity. If the Document already includes a cover text for the same cover, previously added by you or by arrangement made by the same entity you are acting on behalf of, you may not add another; but you may replace the old one, on explicit permission from the previous publisher that added the old one. The author(s) and publisher(s) of the Document do not by this License give permission to use their names for publicity for or to assert or imply endorsement of any Modified Version. 5. COMBINING DOCUMENTS You may combine the Document with other documents released under this License, under the terms defined in section 4 above for modified versions, provided that you include in the combination all of the Invariant Sections of all of the original documents, unmodified, and list them all as Invariant Sections of your combined work in its license notice, and that you preserve all their Warranty Disclaimers. The combined work need only contain one copy of this License, and multiple identical Invariant Sections may be replaced with a single copy. If there are multiple Invariant Sections with the same name but different contents, make the title of each such section unique by adding at the end of it, in parentheses, the name of the original author or publisher of that section if known, or else a unique number. Make the same adjustment to the section titles in the list of Invariant Sections in the license notice of the combined work. In the combination, you must combine any sections Entitled "History" in the various original documents, forming one section Entitled "History"; likewise combine any sections Entitled "Acknowledgements", and any sections Entitled "Dedications". You must delete all sections Entitled "Endorsements". 6. COLLECTIONS OF DOCUMENTS You may make a collection consisting of the Document and other documents released under this License, and replace the individual copies of this License in the various documents with a single copy that is included in the collection, provided that you follow the rules of this License for verbatim copying of each of the documents in all other respects. You may extract a single document from such a collection, and distribute it individually under this License, provided you insert a copy of this License into the extracted document, and follow this License in all other respects regarding verbatim copying of that document. 7. AGGREGATION WITH INDEPENDENT WORKS A compilation of the Document or its derivatives with other separate and independent documents or works, in or on a volume of a storage or distribution medium, is called an "aggregate" if the copyright resulting from the compilation is not used to limit the legal rights of the compilation's users beyond what the individual works permit. When the Document is included in an aggregate, this License does not apply to the other works in the aggregate which are not themselves derivative works of the Document. If the Cover Text requirement of section 3 is applicable to these copies of the Document, then if the Document is less than one half of the entire aggregate, the Document's Cover Texts may be placed on covers that bracket the Document within the aggregate, or the electronic equivalent of covers if the Document is in electronic form. Otherwise they must appear on printed covers that bracket the whole aggregate. 8. TRANSLATION Translation is considered a kind of modification, so you may distribute translations of the Document under the terms of section 4. Replacing Invariant Sections with translations requires special permission from their copyright holders, but you may include translations of some or all Invariant Sections in addition to the original versions of these Invariant Sections. You may include a translation of this License, and all the license notices in the Document, and any Warranty Disclaimers, provided that you also include the original English version of this License and the original versions of those notices and disclaimers. In case of a disagreement between the translation and the original version of this License or a notice or disclaimer, the original version will prevail. If a section in the Document is Entitled "Acknowledgements", "Dedications", or "History", the requirement (section 4) to Preserve its Title (section 1) will typically require changing the actual title. 9. TERMINATION You may not copy, modify, sublicense, or distribute the Document except as expressly provided for under this License. Any other attempt to copy, modify, sublicense or distribute the Document is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 10. FUTURE REVISIONS OF THIS LICENSE The Free Software Foundation may publish new, revised versions of the GNU Free Documentation License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. See -http://www.gnu.org/copyleft/. +https://www.gnu.org/copyleft/. Each version of the License is given a distinguishing version number. If the Document specifies that a particular numbered version of this License "or any later version" applies to it, you have the option of following the terms and conditions either of that specified version or of any later version that has been published (not as a draft) by the Free Software Foundation. If the Document does not specify a version number of this License, you may choose any version ever published (not as a draft) by the Free Software Foundation. ADDENDUM: How to use this License for your documents To use this License in a document you have written, include a copy of the License in the document and put the following copyright and license notices just after the title page: Copyright (c) YEAR YOUR NAME. Permission is granted to copy, distribute and/or modify this document under the terms of the GNU Free Documentation License, Version 1.2 or any later version published by the Free Software Foundation; with no Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts. A copy of the license is included in the section entitled "GNU Free Documentation License". If you have Invariant Sections, Front-Cover Texts and Back-Cover Texts, replace the "with...Texts." line with this: with the Invariant Sections being LIST THEIR TITLES, with the Front-Cover Texts being LIST, and with the Back-Cover Texts being LIST. If you have Invariant Sections without Cover Texts, or some other combination of the three, merge those two alternatives to suit the situation. If your document contains nontrivial examples of program code, we recommend releasing these examples in parallel under your choice of free software license, such as the GNU General Public License, to permit their use in free software. diff --git a/autotests/testcoursefiles.cpp b/autotests/testcoursefiles.cpp index 547998a..df7f5bd 100644 --- a/autotests/testcoursefiles.cpp +++ b/autotests/testcoursefiles.cpp @@ -1,141 +1,141 @@ /* * Copyright 2013 Andreas Cord-Landwehr * * 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 "testcoursefiles.h" #include "core/resourcemanager.h" #include "core/course.h" #include "core/language.h" #include "core/unit.h" #include "core/phrase.h" #include "core/resources/languageresource.h" #include "core/resources/courseresource.h" #include "../src/settings.h" #include #include #include #include #include #include #include #include TestCourseFiles::TestCourseFiles() : m_systemUseCourseRepositoryValue(Settings::useCourseRepository()) { } void TestCourseFiles::init() { //FIXME has to be ported // KGlobal::dirs()->addResourceDir("appdata" , "./testcourses/"); // KGlobal::dirs()->addResourceDir("appdata" , "./"); // KGlobal::dirs()->addResourceDir("appdata" , "./autotests/"); // KGlobal::dirs()->addResourceDir("appdata" , "./autotests/testcourses/"); Settings::setUseCourseRepository(false); Settings::self()->save(); } void TestCourseFiles::cleanup() { // reset value Settings::setUseCourseRepository(m_systemUseCourseRepositoryValue); Settings::self()->save(); } void TestCourseFiles::courseSchemeValidationTest() { - QUrl schemeFile = QUrl::fromLocalFile("schemes/course.xsd"); + QUrl schemeFile = QUrl::fromLocalFile(QStringLiteral("schemes/course.xsd")); QXmlSchema courseSchema; QVERIFY(courseSchema.load(schemeFile)); QVERIFY(courseSchema.isValid()); - QUrl skeletonFile = QUrl::fromLocalFile("schemes/skeleton.xsd"); + QUrl skeletonFile = QUrl::fromLocalFile(QStringLiteral("schemes/skeleton.xsd")); QXmlSchema skeletonScheme; QVERIFY(skeletonScheme.load(skeletonFile)); QVERIFY(skeletonScheme.isValid()); } void TestCourseFiles::fileLoadSaveCompleteness() { ResourceManager manager; - manager.addLanguage(QUrl::fromLocalFile("data/languages/de.xml")); - manager.addCourse(QUrl::fromLocalFile("data/courses/de.xml")); + manager.addLanguage(QUrl::fromLocalFile(QStringLiteral("data/languages/de.xml"))); + manager.addCourse(QUrl::fromLocalFile(QStringLiteral("data/courses/de.xml"))); // test to encure further logic QVERIFY(manager.courseResources(manager.languageResources().first()->language()).count() == 1); Course *testCourse = manager.courseResources(manager.languageResources().first()->language()).first()->course(); QTemporaryFile outputFile; outputFile.open(); QUrl oldFileName = testCourse->file(); testCourse->setFile(QUrl::fromLocalFile(outputFile.fileName())); testCourse->setLanguage(manager.languageResources().first()->language()); testCourse->sync(); testCourse->setFile(oldFileName); // restore for later tests QFile file(outputFile.fileName()); if (!file.open(QIODevice::ReadOnly)) { qCritical() << "Could not open file to read."; } //TODO this only works, since the resource manager not checks uniqueness of course ids! manager.addCourse(QUrl::fromLocalFile(outputFile.fileName())); Course *compareCourse = manager.courseResources(manager.languageResources().first()->language()).last()->course(); // test that we actually call the different files QVERIFY(testCourse->file().toLocalFile() != compareCourse->file().toLocalFile()); QVERIFY(testCourse->id() == compareCourse->id()); QVERIFY(testCourse->foreignId() == compareCourse->foreignId()); QVERIFY(testCourse->title() == compareCourse->title()); QVERIFY(testCourse->description() == compareCourse->description()); QVERIFY(testCourse->language()->id() == compareCourse->language()->id()); QVERIFY(testCourse->unitList().count() == compareCourse->unitList().count()); Unit *testUnit = testCourse->unitList().first(); Unit *compareUnit = compareCourse->unitList().first(); QVERIFY(testUnit->id() == compareUnit->id()); QVERIFY(testUnit->foreignId() == compareUnit->foreignId()); QVERIFY(testUnit->title() == compareUnit->title()); QVERIFY(testUnit->phraseList().count() == compareUnit->phraseList().count()); Phrase *testPhrase = testUnit->phraseList().first(); Phrase *comparePhrase = new Phrase(this); // Note that this actually means that we DO NOT respect phrase orders by list order! foreach (Phrase *phrase, compareUnit->phraseList()) { if (testPhrase->id() == phrase->id()) { comparePhrase = phrase; break; } } QVERIFY(testPhrase->id() == comparePhrase->id()); QVERIFY(testPhrase->foreignId() == comparePhrase->foreignId()); QVERIFY(testPhrase->text() == comparePhrase->text()); QVERIFY(testPhrase->type() == comparePhrase->type()); QVERIFY(testPhrase->sound().fileName() == comparePhrase->sound().fileName()); QVERIFY(testPhrase->phonemes().count() == comparePhrase->phonemes().count()); //FIXME implement phoneme checks after phonemes are fully implemented } QTEST_GUILESS_MAIN(TestCourseFiles) diff --git a/autotests/testlanguagefiles.cpp b/autotests/testlanguagefiles.cpp index c46738f..cf0c7d8 100644 --- a/autotests/testlanguagefiles.cpp +++ b/autotests/testlanguagefiles.cpp @@ -1,138 +1,138 @@ /* * Copyright 2013 Oindrila Gupta * * 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 "testlanguagefiles.h" #include "core/resourcemanager.h" #include "core/course.h" #include "core/language.h" #include "core/unit.h" #include "core/phrase.h" #include #include #include #include #include #include #include #include #include #include TestLanguageFiles::TestLanguageFiles() { //FIXME port this // KGlobal::dirs()->addResourceDir("appdata" , "./autotests/data"); // KGlobal::dirs()->addResourceDir("appdata" , "./autotests"); // KGlobal::dirs()->addResourceDir("appdata" , "./"); } void TestLanguageFiles::init() { // TODO initialization of test case } void TestLanguageFiles::cleanup() { // TODO cleanup after test run } QXmlSchema TestLanguageFiles::loadXmlSchema(const QString &schemeName) const { - QString relPath = QString("schemes/%1.xsd").arg(schemeName); + QString relPath = QStringLiteral("schemes/%1.xsd").arg(schemeName); QUrl file = QUrl::fromLocalFile(QStandardPaths::locate(QStandardPaths::DataLocation, relPath)); QXmlSchema schema; if (schema.load(file) == false) { qWarning() << "Schema at file " << file.toLocalFile() << " is invalid."; } return schema; } QDomDocument TestLanguageFiles::loadDomDocument(const QUrl &path, const QXmlSchema &schema) const { QDomDocument document; QXmlSchemaValidator validator(schema); if (!validator.validate(path)) { qWarning() << "Schema is not valid, aborting loading of XML document:" << path.toLocalFile(); return document; } QString errorMsg; QFile file(path.toLocalFile()); if (file.open(QIODevice::ReadOnly)) { if (!document.setContent(&file, &errorMsg)) { qWarning() << errorMsg; } } else { qWarning() << "Could not open XML document " << path.toLocalFile() << " for reading, aborting."; } return document; } void TestLanguageFiles::languageSchemeValidationTest() { - QUrl languageFile = QUrl::fromLocalFile("schemes/language.xsd"); + QUrl languageFile = QUrl::fromLocalFile(QStringLiteral("schemes/language.xsd")); QXmlSchema languageSchema; QVERIFY(languageSchema.load(languageFile)); QVERIFY(languageSchema.isValid()); } void TestLanguageFiles::checkIdUniqueness() { ResourceManager manager; - QStringList languageFiles = QStandardPaths::locateAll(QStandardPaths::DataLocation, QString("data/languages/*.xml")); + QStringList languageFiles = QStandardPaths::locateAll(QStandardPaths::DataLocation, QStringLiteral("data/languages/*.xml")); foreach (const QString &file, languageFiles) { qDebug() << "File being parsed: " << file; QStringList idList; const QUrl &languageFile = QUrl::fromLocalFile(file); QVERIFY(languageFile.isLocalFile()); - QXmlSchema schema = loadXmlSchema("language"); + QXmlSchema schema = loadXmlSchema(QStringLiteral("language")); QVERIFY(schema.isValid()); QDomDocument document = loadDomDocument(languageFile, schema); QVERIFY(!document.isNull()); QDomElement root(document.documentElement()); Language *language = new Language(this); language->setFile(languageFile); - language->setId(root.firstChildElement("id").text()); - language->setTitle(root.firstChildElement("title").text()); + language->setId(root.firstChildElement(QStringLiteral("id")).text()); + language->setTitle(root.firstChildElement(QStringLiteral("title")).text()); // create phoneme groups - for (QDomElement groupNode = root.firstChildElement("phonemeGroups").firstChildElement(); + for (QDomElement groupNode = root.firstChildElement(QStringLiteral("phonemeGroups")).firstChildElement(); !groupNode.isNull(); groupNode = groupNode.nextSiblingElement()) { - for (QDomElement phonemeNode = groupNode.firstChildElement("phonemes").firstChildElement(); + for (QDomElement phonemeNode = groupNode.firstChildElement(QStringLiteral("phonemes")).firstChildElement(); !phonemeNode.isNull(); phonemeNode = phonemeNode.nextSiblingElement()) { - QString id = phonemeNode.firstChildElement("id").text(); + QString id = phonemeNode.firstChildElement(QStringLiteral("id")).text(); qDebug() << "ID: " << id; QVERIFY2(!idList.contains(id),"Phoneme ID used more than once in the tested file"); idList.append(id); } } } } QTEST_GUILESS_MAIN(TestLanguageFiles) diff --git a/images/README b/images/README index 811c375..524f604 100644 --- a/images/README +++ b/images/README @@ -1,8 +1,7 @@ Files in this directory are licensed as specified in file COPYING-ARTWORK. -The following files are originally created by the KDE Oxygen project (see -http://www.oxygen-icons.org/ for more information). We omit the file types in -this list as the copyright applies to all file types: +The following files are originally created by the KDE Oxygen project. +We omit the file types in this list as the copyright applies to all file types: * course * course-gray diff --git a/liblearnerprofile/autotests/testlearnerstorage.cpp b/liblearnerprofile/autotests/testlearnerstorage.cpp index 8cabfd6..ba1e20c 100644 --- a/liblearnerprofile/autotests/testlearnerstorage.cpp +++ b/liblearnerprofile/autotests/testlearnerstorage.cpp @@ -1,120 +1,120 @@ /* * Copyright 2016 Andreas Cord-Landwehr * * 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 "testlearnerstorage.h" #include "learner.h" #include "learninggoal.h" #include "storage.h" #include #include using namespace LearnerProfile; TestLearnerStorage::TestLearnerStorage() : m_storage(nullptr) { } void TestLearnerStorage::init() { QVERIFY(m_db.open()); m_storage.reset(new Storage(m_db.fileName(), nullptr)); } void TestLearnerStorage::cleanup() { m_db.close(); } void TestLearnerStorage::testLearnerStorage() { LearningGoal tmpGoal(LearningGoal::Language, QStringLiteral("testgoalid"), nullptr); tmpGoal.setName(QStringLiteral("testgoalname")); Learner tmpLearner; tmpLearner.addGoal(&tmpGoal); - tmpLearner.setName("tester"); + tmpLearner.setName(QStringLiteral("tester")); QVERIFY(m_storage->storeGoal(&tmpGoal)); QVERIFY(m_storage->storeProfile(&tmpLearner)); QList< LearnerProfile::LearningGoal* > loadedGoals = m_storage->loadGoals(); QCOMPARE(loadedGoals.size(), 1); QCOMPARE(loadedGoals.first()->category(), tmpGoal.category()); QCOMPARE(loadedGoals.first()->identifier(), tmpGoal.identifier()); QCOMPARE(loadedGoals.first()->name(), tmpGoal.name()); QList loadedLearner = m_storage->loadProfiles(loadedGoals); QCOMPARE(loadedLearner.size(), 1); QCOMPARE(loadedLearner.first()->identifier(), tmpLearner.identifier()); QCOMPARE(loadedLearner.first()->name(), tmpLearner.name()); QCOMPARE(loadedLearner.first()->goals().size(), 1); QCOMPARE(loadedLearner.first()->goals().first()->identifier(), tmpGoal.identifier()); } void TestLearnerStorage::testProgressLogStorage() { LearningGoal tmpGoal(LearningGoal::Language, QStringLiteral("testgoalid"), nullptr); tmpGoal.setName(QStringLiteral("testgoalname")); Learner tmpLearner; tmpLearner.addGoal(&tmpGoal); - tmpLearner.setName("tester"); + tmpLearner.setName(QStringLiteral("tester")); QVERIFY(m_storage->storeGoal(&tmpGoal)); QVERIFY(m_storage->storeProfile(&tmpLearner)); const QDateTime time{QDateTime::currentDateTime()}; QVERIFY(m_storage->storeProgressLog(&tmpLearner, &tmpGoal, "container", "item", 1, time)); - auto data = m_storage->readProgressLog(&tmpLearner, &tmpGoal, "container", "item"); + auto data = m_storage->readProgressLog(&tmpLearner, &tmpGoal, QStringLiteral("container"), QStringLiteral("item")); QCOMPARE(data.size(), 1); QCOMPARE(data.first().first.toString(Qt::ISODate), time.toString(Qt::ISODate)); } void TestLearnerStorage::testProgressValueStorage() { LearningGoal tmpGoal(LearningGoal::Language, QStringLiteral("testgoalid"), nullptr); tmpGoal.setName(QStringLiteral("testgoalname")); Learner tmpLearner; tmpLearner.addGoal(&tmpGoal); - tmpLearner.setName("tester"); + tmpLearner.setName(QStringLiteral("tester")); QVERIFY(m_storage->storeGoal(&tmpGoal)); QVERIFY(m_storage->storeProfile(&tmpLearner)); // insert QVERIFY(m_storage->storeProgressValue(&tmpLearner, &tmpGoal, "container", "itemA", 1)); QVERIFY(m_storage->storeProgressValue(&tmpLearner, &tmpGoal, "container", "itemB", 1)); - auto data = m_storage->readProgressValues(&tmpLearner, &tmpGoal, "container"); + auto data = m_storage->readProgressValues(&tmpLearner, &tmpGoal, QStringLiteral("container")); QCOMPARE(data.size(), 2); // update QVERIFY(m_storage->storeProgressValue(&tmpLearner, &tmpGoal, "container", "itemA", 2)); - data = m_storage->readProgressValues(&tmpLearner, &tmpGoal, "container"); + data = m_storage->readProgressValues(&tmpLearner, &tmpGoal, QStringLiteral("container")); QCOMPARE(data.find("itemA").value(), 2); QCOMPARE(data.find("itemB").value(), 1); } QTEST_GUILESS_MAIN(TestLearnerStorage) diff --git a/liblearnerprofile/src/learner.cpp b/liblearnerprofile/src/learner.cpp index cf1282a..145d526 100644 --- a/liblearnerprofile/src/learner.cpp +++ b/liblearnerprofile/src/learner.cpp @@ -1,205 +1,205 @@ /* * Copyright 2013-2016 Andreas Cord-Landwehr * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) version 3, or any * later version 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 6 of version 3 of the license. * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . */ #include "learner.h" #include "learner_p.h" #include "learninggoal.h" #include #include #include #include #include "liblearner_debug.h" using namespace LearnerProfile; Learner::Learner(QObject *parent) : QObject(parent) , d(new LearnerPrivate) { connect(this, &Learner::goalAdded, this, &Learner::goalCountChanged); connect(this, static_cast(&Learner::goalRemoved), this, &Learner::goalCountChanged); } Learner::~Learner() { } QString Learner::name() const { return d->m_name; } void Learner::setName(const QString &name) { if (name == d->m_name) { return; } d->m_name = name; emit nameChanged(); } int Learner::identifier() const { return d->m_identifier; } void Learner::setIdentifier(int identifier) { if (identifier == d->m_identifier) { return; } d->m_identifier = identifier; emit identifierChanged(); } QString Learner::imageUrl() const { QString path = d->imagePath(); if (!QFileInfo(path).exists()) { return QString(); } return "file://" + path; } void Learner::clearImage() { const QString path {d->imagePath()}; if (!QFileInfo(path).exists()) { return; } QFile file; if (!file.remove(path)) { qCCritical(LIBLEARNER_LOG) << "could not remove image:" << path; } emit imageChanged(); } void Learner::importImage(const QString &path) { if (!QFileInfo(path).exists()) { qCWarning(LIBLEARNER_LOG) << "image path points to a non-existing file, aborting: " << path; return; } // create image directory if it does not exist QDir dir; if (!dir.exists(d->imageDirectory())) { dir.mkdir(d->imageDirectory()); } QPixmap image = QPixmap(path); image = image.scaled(120, 120); if (!image.save(d->imagePath(), "PNG")) { qCCritical(LIBLEARNER_LOG()) << "could not save scaled image to" << d->imagePath(); } emit imageChanged(); qCDebug(LIBLEARNER_LOG) << "saved scaled image from " << path << " at " << d->imagePath(); } QList< LearningGoal* > Learner::goals() const { return d->m_goals; } void Learner::addGoal(LearnerProfile::LearningGoal *goal) { if (d->m_goals.contains(goal)) { return; } emit goalAboutToBeAdded(goal, d->m_goals.count()); d->m_goals.append(goal); emit goalAdded(); } void Learner::removeGoal(LearnerProfile::LearningGoal *goal) { int index = d->m_goals.indexOf(goal); if (index < 0) { qCritical() << "Cannot remove goal, not found: aborting"; return; } emit goalAboutToBeRemoved(index); d->m_goals.removeAt(index); emit goalRemoved(); emit goalRemoved(this, goal); } bool Learner::hasGoal(LearningGoal* goal) const { foreach (LearningGoal *cmpGoal, d->m_goals) { if (goal->identifier() == cmpGoal->identifier()) { return true; } } return false; } void Learner::setActiveGoal(LearningGoal *goal) { if (d->m_activeGoal.contains(goal->category()) && d->m_activeGoal[goal->category()] == goal) { return; } d->m_activeGoal.insert(goal->category(), goal); emit activeGoalChanged(); } void Learner::setActiveGoal(Learner::Category categoryLearner, const QString &identifier) { // TODO:Qt5 change method parameter to LearningGoal::Category // workaround for Q_INVOKABLE access of enum LearningGoal::Category category = static_cast(categoryLearner); if (d->m_activeGoal.contains(category) && d->m_activeGoal[category]->identifier() == identifier) { return; } foreach (LearningGoal *goal, d->m_goals) { if (goal->category() == category && goal->identifier() == identifier) { setActiveGoal(goal); return; } } qCritical() << "Could not select learning goal with ID " << identifier << ": not registered for this learner"; } LearningGoal * Learner::activeGoal(Learner::Category categoryLearner) const { // TODO:Qt5 change method parameter to LearningGoal::Category // workaround for Q_INVOKABLE access of enum LearningGoal::Category category = static_cast(categoryLearner); if (!d->m_activeGoal.contains(category)) { qCWarning(LIBLEARNER_LOG) << "(Learner " << identifier() << ") No current learning goal set for category " << category << " : fall back to first in list"; foreach (LearningGoal *goal, d->m_goals) { if (goal->category() == category) { return goal; } } - qCWarning(LIBLEARNER_LOG) << "No learning goals of catagory " << category << " registered"; + qCWarning(LIBLEARNER_LOG) << "No learning goals of category " << category << " registered"; return nullptr; } return d->m_activeGoal[category]; } diff --git a/liblearnerprofile/src/learner_p.h b/liblearnerprofile/src/learner_p.h index 0c389da..5af9aed 100644 --- a/liblearnerprofile/src/learner_p.h +++ b/liblearnerprofile/src/learner_p.h @@ -1,65 +1,65 @@ /* * Copyright 2013-2014 Andreas Cord-Landwehr * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) version 3, or any * later version 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 6 of version 3 of the license. * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . */ #ifndef LEARNER_P_H #define LEARNER_P_H #include #include #include #include #include "learninggoal.h" #include namespace LearnerProfile { class LearningGoal; class LearnerPrivate { public: LearnerPrivate() : m_name(QString()) , m_identifier(-1) { } ~LearnerPrivate() {} QString imagePath() const { - const QString name = QString("learner%1.png").arg(m_identifier); + const QString name = QStringLiteral("learner%1.png").arg(m_identifier); return imageDirectory() + name; } QString imageDirectory() const { return QStandardPaths::writableLocation(QStandardPaths::DataLocation) + QLatin1Char('/') - + QString("images") + + QStringLiteral("images") + QLatin1Char('/'); } QString m_name; int m_identifier; QList m_goals; QHash m_activeGoal; }; } #endif // LEARNER_P_H diff --git a/liblearnerprofile/src/profilemanager.cpp b/liblearnerprofile/src/profilemanager.cpp index b436531..16b57fd 100644 --- a/liblearnerprofile/src/profilemanager.cpp +++ b/liblearnerprofile/src/profilemanager.cpp @@ -1,304 +1,304 @@ /* * Copyright 2013-2014 Andreas Cord-Landwehr * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) version 3, or any * later version 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 6 of version 3 of the license. * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . */ #include "profilemanager.h" #include "storage.h" #include "learner.h" #include #include #include "liblearner_debug.h" #include #include #include #include using namespace LearnerProfile; ///BEGIN: ProfileManagerPrivate namespace LearnerProfile { class ProfileManagerPrivate { public: ProfileManagerPrivate(); ~ProfileManagerPrivate() {} void sync(); QList m_profiles; Learner *m_activeProfile; QList m_goals; KConfig *m_config; Storage m_storage; }; } LearnerProfile::ProfileManagerPrivate::ProfileManagerPrivate() : m_profiles(QList()) , m_activeProfile(nullptr) , m_config(nullptr) { // load all profiles from storage m_goals.append(m_storage.loadGoals()); m_profiles.append(m_storage.loadProfiles(m_goals)); // set last used profile - m_config = new KConfig("learnerprofilerc"); + m_config = new KConfig(QStringLiteral("learnerprofilerc")); KConfigGroup activeProfileGroup(m_config, "ActiveProfile"); int lastProfileId = activeProfileGroup.readEntry("profileId", "0").toInt(); QList activeGoalsCategories = activeProfileGroup.readEntry("activeGoalsCategories", QList()); QList activeGoalsIdentifiers = activeProfileGroup.readEntry("activeGoalsIdentifiers", QList()); foreach (Learner *learner, m_profiles) { if (learner->identifier() == lastProfileId) { m_activeProfile = learner; // set active goals if (activeGoalsCategories.count() == activeGoalsIdentifiers.count()) { for (int i = 0; i < activeGoalsCategories.count(); ++i) { m_activeProfile->setActiveGoal( (Learner::Category) activeGoalsCategories.at(i), activeGoalsIdentifiers.at(i)); } } else { qCritical() << "Inconsistent goal category / identifier pairs found: aborting."; } break; } } if (m_activeProfile == nullptr) { qCDebug(LIBLEARNER_LOG) << "No last active profile found, falling back to first found profile"; if (m_profiles.size() > 0) { m_activeProfile = m_profiles.at(0); } } } void ProfileManagerPrivate::sync() { // sync last used profile data if (m_activeProfile) { KConfigGroup activeProfileGroup(m_config, "ActiveProfile"); activeProfileGroup.writeEntry("profileId", m_activeProfile->identifier()); // compute activer learning goals by category QList goalCatogries; QList goalIdentifiers; // compute used goals foreach (LearningGoal *goal, m_activeProfile->goals()) { if (!goalCatogries.contains((int) goal->category())) { goalCatogries.append((int) goal->category()); } } // compute active goals foreach (int category, goalCatogries) { goalIdentifiers.append(m_activeProfile->activeGoal((Learner::Category) category)->identifier()); } activeProfileGroup.writeEntry("activeGoalsCategories", goalCatogries); activeProfileGroup.writeEntry("activeGoalsIdentifiers", goalIdentifiers); } else { qCritical() << "No active profile selected, aborting sync."; } m_config->sync(); //TODO only sync changed learner foreach (Learner *learner, m_profiles) { m_storage.storeProfile(learner); } } ///END: ProfileManagerPrivate ProfileManager::ProfileManager(QObject *parent) : QObject(parent) , d(new ProfileManagerPrivate) { - connect (this, SIGNAL(profileAdded(Learner*,int)), this, SIGNAL(profileCountChanged())); - connect (this, SIGNAL(profileRemoved()), this, SIGNAL(profileCountChanged())); + connect (this, &ProfileManager::profileAdded, this, &ProfileManager::profileCountChanged); + connect (this, &ProfileManager::profileRemoved, this, &ProfileManager::profileCountChanged); foreach (Learner *learner, d->m_profiles) { connect (learner, SIGNAL(goalRemoved(Learner*,LearningGoal*)), this, SLOT(removeLearningGoal(Learner*,LearningGoal*))); } } ProfileManager::~ProfileManager() { foreach (Learner *learner, d->m_profiles) { learner->deleteLater(); } } QList< Learner* > ProfileManager::profiles() const { return d->m_profiles; } int ProfileManager::profileCount() const { return profiles().length(); } void ProfileManager::openImageFileDialog() { const QString imagePath = QFileDialog::getOpenFileName(0, i18n("Open Image"), - "", + QLatin1String(""), i18n("Image Files (*.png *.jpg *.bmp)")); d->m_activeProfile->importImage(imagePath); } Learner * ProfileManager::addProfile(const QString &name) { Learner *learner = new Learner(this); learner->setName(name); // set id int maxUsedId = 0; foreach (Learner *cpLearner, d->m_profiles) { if (cpLearner->identifier() >= maxUsedId) { maxUsedId = cpLearner->identifier(); } } learner->setIdentifier(maxUsedId + 1); d->m_profiles.append(learner); d->m_storage.storeProfile(learner); emit profileAdded(learner, d->m_profiles.count() - 1); if (activeProfile() == 0) { setActiveProfile(learner); } connect (learner, SIGNAL(goalRemoved(Learner*,LearningGoal*)), this, SLOT(removeLearningGoal(Learner*,LearningGoal*))); return learner; } void ProfileManager::removeProfile(Learner *learner) { int index = d->m_profiles.indexOf(learner); if (index < 0) { qCWarning(LIBLEARNER_LOG) << "Profile was not found, aborting"; return; } emit profileAboutToBeRemoved(index); d->m_profiles.removeAt(index); d->m_storage.removeProfile(learner); if (d->m_activeProfile == learner) { if (d->m_profiles.isEmpty()) { setActiveProfile(nullptr); } else { setActiveProfile(d->m_profiles.at(0)); } } emit profileRemoved(); } void ProfileManager::removeLearningGoal(Learner* learner, LearningGoal* goal) { d->m_storage.removeRelation(learner, goal); } Learner * ProfileManager::profile(int index) { if (index < 0 || index >= profiles().count()) { return nullptr; } return profiles().at(index); } QList< LearningGoal* > ProfileManager::goals() const { return d->m_goals; } LearningGoal * ProfileManager::registerGoal(LearningGoal::Category category, const QString &identifier, const QString &name) { // test whether goal is already registered foreach (LearningGoal *cmpGoal, d->m_goals) { if (cmpGoal->category() == category && cmpGoal->identifier() == identifier) { return cmpGoal; } } LearningGoal *goal = new LearningGoal(category, identifier, this); goal->setName(name); d->m_goals.append(goal); d->m_storage.storeGoal(goal); return goal; } LearnerProfile::LearningGoal * LearnerProfile::ProfileManager::goal( LearningGoal::Category category, const QString& identifier) const { foreach (LearningGoal *goal, d->m_goals) { if (goal->category() == category && goal->identifier() == identifier) { return goal; } } return nullptr; } void ProfileManager::recordProgress(Learner *learner, LearningGoal *goal, const QString &container, const QString &item, int logPayload, int valuePayload) { d->m_storage.storeProgressLog(learner, goal, container, item, logPayload, QDateTime::currentDateTime()); d->m_storage.storeProgressValue(learner, goal, container, item, valuePayload); } QHash ProfileManager::progressValues(Learner *learner, LearningGoal *goal, const QString &container) const { if (!learner || !goal) { return QHash(); } return d->m_storage.readProgressValues(learner, goal, container); } void ProfileManager::sync() { d->sync(); } void ProfileManager::sync(Learner *learner) { d->m_storage.storeProfile(learner); } Learner * ProfileManager::activeProfile() const { return d->m_activeProfile; } void ProfileManager::setActiveProfile(Learner* learner) { if (learner == d->m_activeProfile) { return; } d->m_activeProfile = learner; emit activeProfileChanged(); } diff --git a/liblearnerprofile/src/storage.cpp b/liblearnerprofile/src/storage.cpp index 8eeb1e9..f8921a0 100644 --- a/liblearnerprofile/src/storage.cpp +++ b/liblearnerprofile/src/storage.cpp @@ -1,679 +1,679 @@ /* * Copyright 2013-2016 Andreas Cord-Landwehr * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) version 3, or any * later version 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 6 of version 3 of the license. * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . */ #include "storage.h" #include "learner.h" #include "liblearner_debug.h" #include #include #include #include #include #include #include using namespace LearnerProfile; Storage::Storage(QObject* parent) : QObject(parent) , m_databasePath(QStandardPaths::writableLocation( QStandardPaths::DataLocation) + QLatin1Char('/') + "learnerdata.db") , m_errorMessage(QString()) { } Storage::Storage(const QString databasePath, QObject* parent) : QObject(parent) , m_databasePath(databasePath) , m_errorMessage(QString()) { qCDebug(LIBLEARNER_LOG) << "Initialize with custom DB path:" << m_databasePath; } QString Storage::errorMessage() const { return m_errorMessage; } void Storage::raiseError(const QSqlError &error) { - m_errorMessage = QString("%1 : %2").arg(error.driverText()).arg(error.databaseText()); + m_errorMessage = QStringLiteral("%1 : %2").arg(error.driverText()).arg(error.databaseText()); emit errorMessageChanged(); } bool Storage::storeProfile(Learner *learner) { QSqlDatabase db = database(); // test whether ID is present QSqlQuery idExistsQuery(db); - idExistsQuery.prepare("SELECT COUNT(*) FROM profiles WHERE id = :id"); - idExistsQuery.bindValue(":id", learner->identifier()); + idExistsQuery.prepare(QStringLiteral("SELECT COUNT(*) FROM profiles WHERE id = :id")); + idExistsQuery.bindValue(QStringLiteral(":id"), learner->identifier()); idExistsQuery.exec(); if (db.lastError().isValid()) { qCritical() << "ExistsQuery: " << db.lastError().text(); raiseError(db.lastError()); return false; } // go to first result row that contains the count idExistsQuery.next(); if (idExistsQuery.value(0).toInt() < 1) { // in case learner ID is not found in database QSqlQuery insertProfileQuery(db); - insertProfileQuery.prepare("INSERT INTO profiles (id, name) VALUES (?, ?)"); + insertProfileQuery.prepare(QStringLiteral("INSERT INTO profiles (id, name) VALUES (?, ?)")); insertProfileQuery.bindValue(0, learner->identifier()); insertProfileQuery.bindValue(1, learner->name()); insertProfileQuery.exec(); if (insertProfileQuery.lastError().isValid()) { raiseError(insertProfileQuery.lastError()); db.rollback(); return false; } } else { // update name otherwise QSqlQuery updateProfileQuery(db); - updateProfileQuery.prepare("UPDATE profiles SET name = :name WHERE id = :id"); - updateProfileQuery.bindValue(":id", learner->identifier()); - updateProfileQuery.bindValue(":name", learner->name()); + updateProfileQuery.prepare(QStringLiteral("UPDATE profiles SET name = :name WHERE id = :id")); + updateProfileQuery.bindValue(QStringLiteral(":id"), learner->identifier()); + updateProfileQuery.bindValue(QStringLiteral(":name"), learner->name()); updateProfileQuery.exec(); if (updateProfileQuery.lastError().isValid()) { qCritical() << updateProfileQuery.lastError().text(); raiseError(updateProfileQuery.lastError()); db.rollback(); return false; } } // store existing learning goal relations foreach (LearningGoal *goal, learner->goals()) { QSqlQuery relationExistsQuery(db); relationExistsQuery.prepare("SELECT COUNT(*) FROM learner_goals " "WHERE goal_category = :goalCategory " "AND goal_identifier = :goalIdentifier " "AND profile_id = :profileId " ); - relationExistsQuery.bindValue(":goalCategory", goal->category()); - relationExistsQuery.bindValue(":goalIdentifier", goal->identifier()); - relationExistsQuery.bindValue(":profileId", learner->identifier()); + relationExistsQuery.bindValue(QStringLiteral(":goalCategory"), goal->category()); + relationExistsQuery.bindValue(QStringLiteral(":goalIdentifier"), goal->identifier()); + relationExistsQuery.bindValue(QStringLiteral(":profileId"), learner->identifier()); relationExistsQuery.exec(); if (db.lastError().isValid()) { qCritical() << "ExistsQuery: " << db.lastError().text(); raiseError(db.lastError()); return false; } // go to first result row that contains the count relationExistsQuery.next(); if (relationExistsQuery.value(0).toInt() < 1) { QSqlQuery insertProfileQuery(db); - insertProfileQuery.prepare("INSERT INTO learner_goals (goal_category, goal_identifier, profile_id) VALUES (?, ?, ?)"); + insertProfileQuery.prepare(QStringLiteral("INSERT INTO learner_goals (goal_category, goal_identifier, profile_id) VALUES (?, ?, ?)")); insertProfileQuery.bindValue(0, goal->category()); insertProfileQuery.bindValue(1, goal->identifier()); insertProfileQuery.bindValue(2, learner->identifier()); insertProfileQuery.exec(); } } // remove deleted relations QSqlQuery cleanupRelations(db); - cleanupRelations.prepare("DELETE FROM learner_goals WHERE "); + cleanupRelations.prepare(QStringLiteral("DELETE FROM learner_goals WHERE ")); //TODO change creation of relations to same way as remove-relations: explicit connections return true; } bool Storage::removeProfile(Learner *learner) { QSqlDatabase db = database(); QSqlQuery removeProfileQuery(db); // delete learner - removeProfileQuery.prepare("DELETE FROM profiles WHERE id = ?"); + removeProfileQuery.prepare(QStringLiteral("DELETE FROM profiles WHERE id = ?")); removeProfileQuery.bindValue(0, learner->identifier()); removeProfileQuery.exec(); if (removeProfileQuery.lastError().isValid()) { qCritical() << removeProfileQuery.lastError().text(); raiseError(removeProfileQuery.lastError()); db.rollback(); return false; } // delete learning goal relations QSqlQuery removeGoalRelationQuery(db); - removeGoalRelationQuery.prepare("DELETE FROM learner_goals WHERE profile_id = ?"); + removeGoalRelationQuery.prepare(QStringLiteral("DELETE FROM learner_goals WHERE profile_id = ?")); removeGoalRelationQuery.bindValue(0, learner->identifier()); removeGoalRelationQuery.exec(); if (removeGoalRelationQuery.lastError().isValid()) { qCritical() << removeGoalRelationQuery.lastError().text(); raiseError(removeGoalRelationQuery.lastError()); db.rollback(); return false; } return true; } bool Storage::removeRelation(Learner *learner, LearningGoal *goal) { QSqlDatabase db = database(); QSqlQuery removeGoalRelationQuery(db); removeGoalRelationQuery.prepare( "DELETE FROM learner_goals " "WHERE goal_category = :goalCategory " "AND goal_identifier = :goalIdentifier " "AND profile_id = :profileId " ); - removeGoalRelationQuery.bindValue(":goalCategory", goal->category()); - removeGoalRelationQuery.bindValue(":goalIdentifier", goal->identifier()); - removeGoalRelationQuery.bindValue(":profileId", learner->identifier()); + removeGoalRelationQuery.bindValue(QStringLiteral(":goalCategory"), goal->category()); + removeGoalRelationQuery.bindValue(QStringLiteral(":goalIdentifier"), goal->identifier()); + removeGoalRelationQuery.bindValue(QStringLiteral(":profileId"), learner->identifier()); removeGoalRelationQuery.exec(); if (db.lastError().isValid()) { qCritical() << "ExistsQuery: " << db.lastError().text(); raiseError(db.lastError()); return false; } return true; } QList< Learner* > Storage::loadProfiles(QList goals) { QSqlDatabase db = database(); QSqlQuery profileQuery(db); - profileQuery.prepare("SELECT id, name FROM profiles"); + profileQuery.prepare(QStringLiteral("SELECT id, name FROM profiles")); profileQuery.exec(); if (profileQuery.lastError().isValid()) { qCritical() << profileQuery.lastError().text(); raiseError(profileQuery.lastError()); return QList(); } QList profiles; while (profileQuery.next()) { Learner* profile = new Learner(); profile->setIdentifier(profileQuery.value(0).toInt()); profile->setName(profileQuery.value(1).toString()); profiles.append(profile); } // associate to goals QSqlQuery goalRelationQuery(db); - goalRelationQuery.prepare("SELECT goal_category, goal_identifier, profile_id FROM learner_goals"); + goalRelationQuery.prepare(QStringLiteral("SELECT goal_category, goal_identifier, profile_id FROM learner_goals")); goalRelationQuery.exec(); if (goalRelationQuery.lastError().isValid()) { qCritical() << goalRelationQuery.lastError().text(); raiseError(goalRelationQuery.lastError()); return QList(); } while (goalRelationQuery.next()) { Learner *learner = nullptr; LearningGoal *goal = nullptr; foreach (Learner *cmpProfile, profiles) { if (cmpProfile->identifier() == goalRelationQuery.value(2).toInt()) { learner = cmpProfile; break; } } if (!learner) { qCCritical(LIBLEARNER_LOG) << "Could not retrieve learner from database."; return QList(); } foreach (LearningGoal *cmpGoal, goals) { if (cmpGoal->category() == goalRelationQuery.value(0).toInt() && cmpGoal->identifier() == goalRelationQuery.value(1).toString()) { goal = cmpGoal; break; } } if (learner->goals().contains(goal)) { continue; } if (goal) { learner->addGoal(goal); } } return profiles; } bool Storage::storeGoal(LearningGoal *goal) { QSqlDatabase db = database(); // test whether ID is present QSqlQuery goalExistsQuery(db); - goalExistsQuery.prepare("SELECT COUNT(*) FROM goals WHERE category = :category AND identifier = :identifier"); - goalExistsQuery.bindValue(":identifier", goal->identifier()); - goalExistsQuery.bindValue(":category", static_cast(goal->category())); + goalExistsQuery.prepare(QStringLiteral("SELECT COUNT(*) FROM goals WHERE category = :category AND identifier = :identifier")); + goalExistsQuery.bindValue(QStringLiteral(":identifier"), goal->identifier()); + goalExistsQuery.bindValue(QStringLiteral(":category"), static_cast(goal->category())); goalExistsQuery.exec(); if (db.lastError().isValid()) { qCritical() << "ExistsQuery: " << db.lastError().text(); raiseError(db.lastError()); return false; } // go to first result row that contains the count goalExistsQuery.next(); if (goalExistsQuery.value(0).toInt() < 1) { // in case learner ID is not found in database QSqlQuery insertGoalQuery(db); - insertGoalQuery.prepare("INSERT INTO goals (category, identifier, name) VALUES (?, ?, ?)"); + insertGoalQuery.prepare(QStringLiteral("INSERT INTO goals (category, identifier, name) VALUES (?, ?, ?)")); insertGoalQuery.bindValue(0, static_cast(goal->category())); insertGoalQuery.bindValue(1, goal->identifier()); insertGoalQuery.bindValue(2, goal->name()); insertGoalQuery.exec(); if (insertGoalQuery.lastError().isValid()) { raiseError(insertGoalQuery.lastError()); db.rollback(); return false; } return true; } else { // update name otherwise QSqlQuery updateGoalQuery(db); - updateGoalQuery.prepare("UPDATE goals SET name = :name WHERE category = :category AND identifier = :identifier"); - updateGoalQuery.bindValue(":category", static_cast(goal->category())); - updateGoalQuery.bindValue(":identifier", goal->identifier()); - updateGoalQuery.bindValue(":name", goal->name()); + updateGoalQuery.prepare(QStringLiteral("UPDATE goals SET name = :name WHERE category = :category AND identifier = :identifier")); + updateGoalQuery.bindValue(QStringLiteral(":category"), static_cast(goal->category())); + updateGoalQuery.bindValue(QStringLiteral(":identifier"), goal->identifier()); + updateGoalQuery.bindValue(QStringLiteral(":name"), goal->name()); updateGoalQuery.exec(); if (updateGoalQuery.lastError().isValid()) { qCritical() << updateGoalQuery.lastError().text(); raiseError(updateGoalQuery.lastError()); db.rollback(); return false; } return true; } } QList< LearningGoal* > Storage::loadGoals() { QSqlDatabase db = database(); QSqlQuery goalQuery(db); - goalQuery.prepare("SELECT category, identifier, name FROM goals"); + goalQuery.prepare(QStringLiteral("SELECT category, identifier, name FROM goals")); goalQuery.exec(); if (goalQuery.lastError().isValid()) { qCritical() << goalQuery.lastError().text(); raiseError(goalQuery.lastError()); return QList(); } QList goals; while (goalQuery.next()) { LearningGoal::Category category = static_cast(goalQuery.value(0).toInt()); QString identifier = goalQuery.value(1).toString(); QString name = goalQuery.value(2).toString(); LearningGoal* goal = new LearningGoal(category, identifier); goal->setName(name); goals.append(goal); } return goals; } bool Storage::storeProgressLog(Learner *learner, LearningGoal *goal, const QString &container, const QString &item, int payload, const QDateTime &time) { QSqlDatabase db = database(); QSqlQuery insertQuery(db); insertQuery.prepare("INSERT INTO learner_progress_log " "(goal_category, goal_identifier, profile_id, item_container, item, payload, date) " "VALUES (:gcategory, :gidentifier, :pid, :container, :item, :payload, :date)"); - insertQuery.bindValue(":gcategory", static_cast(goal->category())); - insertQuery.bindValue(":gidentifier", goal->identifier()); - insertQuery.bindValue(":pid", learner->identifier()); - insertQuery.bindValue(":container", container); - insertQuery.bindValue(":item", item); - insertQuery.bindValue(":payload", payload); - insertQuery.bindValue(":date", time.toString(Qt::ISODate)); + insertQuery.bindValue(QStringLiteral(":gcategory"), static_cast(goal->category())); + insertQuery.bindValue(QStringLiteral(":gidentifier"), goal->identifier()); + insertQuery.bindValue(QStringLiteral(":pid"), learner->identifier()); + insertQuery.bindValue(QStringLiteral(":container"), container); + insertQuery.bindValue(QStringLiteral(":item"), item); + insertQuery.bindValue(QStringLiteral(":payload"), payload); + insertQuery.bindValue(QStringLiteral(":date"), time.toString(Qt::ISODate)); insertQuery.exec(); if (insertQuery.lastError().isValid()) { raiseError(insertQuery.lastError()); qCCritical(LIBLEARNER_LOG) << "DB Error:" << m_errorMessage; db.rollback(); return false; } return true; } QList> Storage::readProgressLog(Learner *learner, LearningGoal *goal, const QString &container, const QString &item) { QSqlDatabase db = database(); QSqlQuery logQuery(db); logQuery.prepare("SELECT date, payload FROM learner_progress_log " "WHERE goal_category = :goalcategory " "AND goal_identifier = :goalid " "AND profile_id = :profileid " "AND item_container = :container " "AND item = :item"); - logQuery.bindValue(":goalcategory", static_cast(goal->category())); - logQuery.bindValue(":goalid", goal->identifier()); - logQuery.bindValue(":profileid", learner->identifier()); - logQuery.bindValue(":container", container); - logQuery.bindValue(":item", item); + logQuery.bindValue(QStringLiteral(":goalcategory"), static_cast(goal->category())); + logQuery.bindValue(QStringLiteral(":goalid"), goal->identifier()); + logQuery.bindValue(QStringLiteral(":profileid"), learner->identifier()); + logQuery.bindValue(QStringLiteral(":container"), container); + logQuery.bindValue(QStringLiteral(":item"), item); logQuery.exec(); if (logQuery.lastError().isValid()) { qCritical() << logQuery.lastError().text(); raiseError(logQuery.lastError()); return QList>(); } QList> log; while (logQuery.next()) { const QDateTime date{logQuery.value(0).toDateTime()}; int payload{logQuery.value(1).toInt()}; log.append(qMakePair(date, payload)); } return log; } bool Storage::storeProgressValue(Learner *learner, LearningGoal *goal, const QString &container, const QString &item, int payload) { QSqlDatabase db = database(); QSqlQuery query(db); // test if already payload stored query.prepare("SELECT payload FROM learner_progress_value " "WHERE goal_category = :gcategory " "AND goal_identifier = :gidentifier " "AND profile_id = :pid " "AND item_container = :container " "AND item = :item"); - query.bindValue(":gcategory", static_cast(goal->category())); - query.bindValue(":gidentifier", goal->identifier()); - query.bindValue(":pid", learner->identifier()); - query.bindValue(":container", container); - query.bindValue(":item", item); + query.bindValue(QStringLiteral(":gcategory"), static_cast(goal->category())); + query.bindValue(QStringLiteral(":gidentifier"), goal->identifier()); + query.bindValue(QStringLiteral(":pid"), learner->identifier()); + query.bindValue(QStringLiteral(":container"), container); + query.bindValue(QStringLiteral(":item"), item); query.exec(); if (query.lastError().isValid()) { qCritical() << query.lastError().text(); raiseError(query.lastError()); return false; } // if query contains values, perform update query if (query.next()) { query.finish(); // release resources from previous query query.prepare("UPDATE learner_progress_value " "SET payload = :payload " "WHERE goal_category = :gcategory " "AND goal_identifier = :gidentifier " "AND profile_id = :pid " "AND item_container = :container " "AND item = :item"); - query.bindValue(":payload", static_cast(payload)); - query.bindValue(":gcategory", static_cast(goal->category())); - query.bindValue(":gidentifier", goal->identifier()); - query.bindValue(":pid", learner->identifier()); - query.bindValue(":container", container); - query.bindValue(":item", item); + query.bindValue(QStringLiteral(":payload"), static_cast(payload)); + query.bindValue(QStringLiteral(":gcategory"), static_cast(goal->category())); + query.bindValue(QStringLiteral(":gidentifier"), goal->identifier()); + query.bindValue(QStringLiteral(":pid"), learner->identifier()); + query.bindValue(QStringLiteral(":container"), container); + query.bindValue(QStringLiteral(":item"), item); query.exec(); if (query.lastError().isValid()) { qCritical() << query.lastError().text(); raiseError(query.lastError()); db.rollback(); return false; } return true; } // else insert new row else { query.finish(); // release resources from previous query query.prepare("INSERT INTO learner_progress_value " "(goal_category, goal_identifier, profile_id, item_container, item, payload) " "VALUES (:gcategory, :gidentifier, :pid, :container, :item, :payload)"); - query.bindValue(":gcategory", static_cast(goal->category())); - query.bindValue(":gidentifier", goal->identifier()); - query.bindValue(":pid", learner->identifier()); - query.bindValue(":container", container); - query.bindValue(":item", item); - query.bindValue(":payload", static_cast(payload)); + query.bindValue(QStringLiteral(":gcategory"), static_cast(goal->category())); + query.bindValue(QStringLiteral(":gidentifier"), goal->identifier()); + query.bindValue(QStringLiteral(":pid"), learner->identifier()); + query.bindValue(QStringLiteral(":container"), container); + query.bindValue(QStringLiteral(":item"), item); + query.bindValue(QStringLiteral(":payload"), static_cast(payload)); query.exec(); if (query.lastError().isValid()) { qCritical() << query.lastError().text(); raiseError(query.lastError()); db.rollback(); return false; } return true; } Q_UNREACHABLE(); return false; } QHash Storage::readProgressValues(Learner *learner, LearningGoal *goal, const QString &container) { QSqlDatabase db = database(); QSqlQuery query(db); query.prepare("SELECT item, payload FROM learner_progress_value " "WHERE goal_category = :goalcategory " "AND goal_identifier = :goalid " "AND profile_id = :profileid " "AND item_container = :container"); - query.bindValue(":goalcategory", static_cast(goal->category())); - query.bindValue(":goalid", goal->identifier()); - query.bindValue(":profileid", learner->identifier()); - query.bindValue(":container", container); + query.bindValue(QStringLiteral(":goalcategory"), static_cast(goal->category())); + query.bindValue(QStringLiteral(":goalid"), goal->identifier()); + query.bindValue(QStringLiteral(":profileid"), learner->identifier()); + query.bindValue(QStringLiteral(":container"), container); query.exec(); if (query.lastError().isValid()) { qCritical() << query.lastError().text(); raiseError(query.lastError()); return QHash(); } QHash values; while (query.next()) { const QString item{query.value(0).toString()}; const int payload{query.value(1).toInt()}; values.insert(item, payload); } return values; } int Storage::readProgressValue(Learner *learner, LearningGoal *goal, const QString &container, const QString &item) { QSqlDatabase db = database(); QSqlQuery query(db); query.prepare("SELECT payload FROM learner_progress_value " "WHERE goal_category = :goalcategory " "AND goal_identifier = :goalid " "AND profile_id = :profileid " "AND item_container = :container " "AND item = :item"); - query.bindValue(":goalcategory", static_cast(goal->category())); - query.bindValue(":goalid", goal->identifier()); - query.bindValue(":profileid", learner->identifier()); - query.bindValue(":container", container); - query.bindValue(":item", item); + query.bindValue(QStringLiteral(":goalcategory"), static_cast(goal->category())); + query.bindValue(QStringLiteral(":goalid"), goal->identifier()); + query.bindValue(QStringLiteral(":profileid"), learner->identifier()); + query.bindValue(QStringLiteral(":container"), container); + query.bindValue(QStringLiteral(":item"), item); query.exec(); if (query.lastError().isValid()) { qCritical() << query.lastError().text(); raiseError(query.lastError()); return -1; } if (query.next()) { return query.value(0).toInt(); } return -1; } QSqlDatabase Storage::database() { if (QSqlDatabase::contains(QSqlDatabase::defaultConnection)) { return QSqlDatabase::database(QSqlDatabase::defaultConnection); } // create data directory if it does not exist QDir dir = QDir(QStandardPaths::writableLocation(QStandardPaths::DataLocation)); if (!dir.exists()) { dir.mkpath(QStandardPaths::writableLocation(QStandardPaths::DataLocation)); } qCDebug(LIBLEARNER_LOG) << "Database path: " << m_databasePath; - QSqlDatabase db = QSqlDatabase::addDatabase("QSQLITE"); + QSqlDatabase db = QSqlDatabase::addDatabase(QStringLiteral("QSQLITE")); db.setDatabaseName(m_databasePath); if (!db.open()) { qCritical() << "Could not open database: " << db.lastError().text(); raiseError(db.lastError()); return db; } if (!updateSchema()) { qCritical() << "Database scheme not correct."; return db; } // return correctly set up database return db; } bool Storage::updateSchema() { QSqlDatabase db = database(); // check database version format db.exec("CREATE TABLE IF NOT EXISTS metadata (" "key TEXT PRIMARY KEY, " "value TEXT" ")"); if (db.lastError().isValid()) { qCritical() << db.lastError().text(); raiseError(db.lastError()); return false; } - QSqlQuery versionQuery = db.exec("SELECT value FROM metadata WHERE key = 'version'"); + QSqlQuery versionQuery = db.exec(QStringLiteral("SELECT value FROM metadata WHERE key = 'version'")); if (db.lastError().isValid()) { qCritical() << db.lastError().text(); raiseError(db.lastError()); return false; } if (versionQuery.next()) { QString version = versionQuery.value(0).toString(); - if (version != "1") { + if (version != QLatin1String("1")) { m_errorMessage = i18n("Invalid database version '%1'.", version); emit errorMessageChanged(); return false; } } else { if (!db.transaction()) { qCWarning(LIBLEARNER_LOG) << db.lastError().text(); raiseError(db.lastError()); return false; } - db.exec("INSERT INTO metadata (key, value) VALUES ('version', '1')"); + db.exec(QStringLiteral("INSERT INTO metadata (key, value) VALUES ('version', '1')")); if (db.lastError().isValid()) { qCritical() << db.lastError().text(); raiseError(db.lastError()); return false; } if (!db.commit()) { qCritical() << db.lastError().text(); raiseError(db.lastError()); return false; } } // table for learner profiles db.exec("CREATE TABLE IF NOT EXISTS profiles (" "id INTEGER PRIMARY KEY AUTOINCREMENT, " "name TEXT" ")"); if (db.lastError().isValid()) { qCritical() << db.lastError().text(); raiseError(db.lastError()); return false; } // table for registered learning goals db.exec("CREATE TABLE IF NOT EXISTS goals (" "category INTEGER, " // LearningGoal::Category "identifier TEXT, " // identifier, unique per Category "name TEXT, " // name "PRIMARY KEY ( category, identifier )" ")"); if (db.lastError().isValid()) { qCritical() << db.lastError().text(); raiseError(db.lastError()); return false; } // table for learner - learningGoal relations db.exec("CREATE TABLE IF NOT EXISTS learner_goals (" "id INTEGER PRIMARY KEY AUTOINCREMENT, " "goal_category INTEGER, " // LearningGoal::Category "goal_identifier TEXT, " // LearningGoal::Identifier "profile_id INTEGER " // Learner::Identifier ")"); if (db.lastError().isValid()) { qCritical() << db.lastError().text(); raiseError(db.lastError()); return false; } // table for full progress data log db.exec("CREATE TABLE IF NOT EXISTS learner_progress_log (" "id INTEGER PRIMARY KEY AUTOINCREMENT, " "goal_category INTEGER, " // LearningGoal::Category "goal_identifier TEXT, " // LearningGoal::Identifier "profile_id INTEGER, " // Learner::Identifier "item_container TEXT, " "item TEXT, " "payload INTEGER, " "date TEXT" ")"); if (db.lastError().isValid()) { qCritical() << db.lastError().text(); raiseError(db.lastError()); return false; } // table for progress data quick access db.exec("CREATE TABLE IF NOT EXISTS learner_progress_value (" "id INTEGER PRIMARY KEY AUTOINCREMENT, " "goal_category INTEGER, " // LearningGoal::Category "goal_identifier TEXT, " // LearningGoal::Identifier "profile_id INTEGER, " // Learner::Identifier "item_container TEXT, " "item TEXT, " "payload INTEGER" ")"); if (db.lastError().isValid()) { qCritical() << db.lastError().text(); raiseError(db.lastError()); return false; } return true; } diff --git a/liblearnerprofile/src/storage.h b/liblearnerprofile/src/storage.h index 51e35e4..80cf255 100644 --- a/liblearnerprofile/src/storage.h +++ b/liblearnerprofile/src/storage.h @@ -1,103 +1,103 @@ /* * Copyright 2013-2016 Andreas Cord-Landwehr * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) version 3, or any * later version 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 6 of version 3 of the license. * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . */ #ifndef STORAGE_H #define STORAGE_H #include class QSqlError; class QSqlDatabase; namespace LearnerProfile { class Learner; class LearningGoal; /** * \class Storage * Database storage for learner information database. */ class Storage : public QObject { Q_OBJECT Q_PROPERTY(QString errorMessage READ errorMessage NOTIFY errorMessageChanged) public: /** * Default constructor, which sets a default database path at * DataLocation + learnerdata.db */ explicit Storage(QObject* parent = nullptr); /** * \note this constructor is tailored for unit tests */ - Storage(const QString databasePath, QObject* parent = nullptr); + explicit Storage(const QString databasePath, QObject* parent = nullptr); QString errorMessage() const; /** * Store profile in database. This can either be a new or an existing profile. * If it is an existing profile, the corresponding values are updated. */ bool storeProfile(Learner *learner); bool removeProfile(Learner *learner); bool removeRelation(Learner *learner, LearningGoal *goal); QList loadProfiles(QList< LearnerProfile::LearningGoal* > goals); bool storeGoal(LearningGoal *goal); QList loadGoals(); bool storeProgressLog(Learner *learner, LearningGoal *goal, const QString &container, const QString &item, int payload, const QDateTime &time); /** * Load list of progress values for specified item * \return list of date/payload values for this item */ QList> readProgressLog(Learner *learner, LearningGoal *goal, const QString &container, const QString &item); bool storeProgressValue(Learner *learner, LearningGoal *goal, const QString &container, const QString &item, int payload); /** * Load list of progress values for specified container * \return list of item/payload values for all items in container */ QHash readProgressValues(Learner *learner, LearningGoal *goal, const QString &container); /** * Load payload value of specified item. If no value is found, \return -1 */ int readProgressValue(Learner *learner, LearningGoal *goal, const QString &container, const QString &item); Q_SIGNALS: void errorMessageChanged(); protected: QSqlDatabase database(); void raiseError(const QSqlError &error); private: bool updateSchema(); const QString m_databasePath; QString m_errorMessage; }; } #endif // STORAGE_H diff --git a/libsound/src/CMakeLists.txt b/libsound/src/CMakeLists.txt index 612fbba..1ad39ea 100644 --- a/libsound/src/CMakeLists.txt +++ b/libsound/src/CMakeLists.txt @@ -1,66 +1,66 @@ ### # Copyright 2014 Andreas Cord-Landwehr # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # # 1. Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # 2. Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. # # THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR # IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES # OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. # IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT # NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF # THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ### # enable exceptions for this library kde_enable_exceptions() set(sound_LIB_SRCS backendinterface.cpp capturedevicecontroller.cpp outputdevicecontroller.cpp capturebackendinterface.cpp outputbackendinterface.cpp libsound_debug.cpp ) add_library(artikulatesound SHARED ${sound_LIB_SRCS}) generate_export_header(artikulatesound BASE_NAME libsound) target_link_libraries( artikulatesound LINK_PUBLIC KF5::CoreAddons KF5::I18n ) # internal library without any API or ABI guarantee set(GENERIC_LIB_VERSION "0") set(GENERIC_LIB_SOVERSION "0") set_target_properties( artikulatesound PROPERTIES VERSION ${GENERIC_LIB_VERSION} SOVERSION ${GENERIC_LIB_SOVERSION} ) install( TARGETS artikulatesound ${INSTALL_TARGETS_DEFAULT_ARGS} ) if (BUILD_GSTREAMER_PLUGIN) ecm_optional_add_subdirectory(qtgstreamerbackend) endif() if (BUILD_QTMULTIMEDIA_PLUGIN) ecm_optional_add_subdirectory(qtmultimediabackend) -endif() \ No newline at end of file +endif() diff --git a/libsound/src/capturedevicecontroller.cpp b/libsound/src/capturedevicecontroller.cpp index 3fa1077..e46bd94 100644 --- a/libsound/src/capturedevicecontroller.cpp +++ b/libsound/src/capturedevicecontroller.cpp @@ -1,151 +1,151 @@ /* * Copyright 2013-2014 Andreas Cord-Landwehr * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation; either version 2.1 of the License, or * (at your option) 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 Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . */ #include "capturedevicecontroller.h" #include "capturebackendinterface.h" #include "backendinterface.h" #include "libsound_debug.h" #include #include #include #include #include #include /** * \class CaptureDeviceControllerPrivate * \internal * * This is the private data class for \see CaptureDeviceController. - * Note that -- even if the CaptureDeviceController is contructed before its first call -- all + * Note that -- even if the CaptureDeviceController is constructed before its first call -- all * devices get only configured by first instantiation of CaptureDeviceController with a call * to CaptureDeviceControllerPrivate::lazyInit(), called in CaptureDeviceController::self(). */ class CaptureDeviceControllerPrivate { public: CaptureDeviceControllerPrivate(QObject *parent) : m_parent(parent) , m_backend(nullptr) , m_initialized(false) { QStringList dirsToCheck; foreach (const QString &directory, QCoreApplication::libraryPaths()) { dirsToCheck << directory + "/artikulate/libsound"; } // load plugins QPluginLoader loader; foreach (const QString &dir, dirsToCheck) { QVector metadataList = KPluginLoader::findPlugins(dir, [=](const KPluginMetaData &data) { - return data.serviceTypes().contains("artikulate/libsound/backend"); + return data.serviceTypes().contains(QStringLiteral("artikulate/libsound/backend")); }); foreach (const auto &metadata, metadataList) { loader.setFileName(metadata.fileName()); qCDebug(LIBSOUND_LOG) << "Load Plugin: " << metadata.name(); if (!loader.load()) { qCCritical(LIBSOUND_LOG) << "Error while loading plugin: " << metadata.name(); } KPluginFactory *factory = KPluginLoader(loader.fileName()).factory(); if (!factory) { qCCritical(LIBSOUND_LOG) << "Could not load plugin: " << metadata.name(); continue; } BackendInterface *plugin = factory->create(parent, QList< QVariant >()); if (plugin->captureBackend()) { m_backendList.append(plugin->captureBackend()); } } } if (!m_backend && !m_backendList.isEmpty()) { m_backend = m_backendList.first(); } } ~CaptureDeviceControllerPrivate() { delete m_backend; m_backend = nullptr; } void lazyInit() { if (m_initialized) { return; } //TODO currently nothing to do m_initialized = true; } CaptureBackendInterface * backend() const { Q_ASSERT(m_backend); return m_backend; } QObject *m_parent; CaptureBackendInterface *m_backend; QList m_backendList; bool m_initialized; }; CaptureDeviceController::CaptureDeviceController() : d(new CaptureDeviceControllerPrivate(this)) { } CaptureDeviceController::~CaptureDeviceController() { } CaptureDeviceController & CaptureDeviceController::self() { static CaptureDeviceController instance; instance.d->lazyInit(); return instance; } void CaptureDeviceController::startCapture(const QString &filePath) { d->backend()->startCapture(filePath); emit captureStarted(); } void CaptureDeviceController::stopCapture() { d->backend()->stopCapture(); emit captureStopped(); } void CaptureDeviceController::setDevice(const QString &deviceIdentifier) { d->backend()->setDevice(deviceIdentifier); } QList< QString > CaptureDeviceController::devices() const { return d->backend()->devices(); } CaptureDeviceController::State CaptureDeviceController::state() const { return d->backend()->captureState(); } diff --git a/libsound/src/capturedevicecontroller.h b/libsound/src/capturedevicecontroller.h index f9d4713..ba70662 100644 --- a/libsound/src/capturedevicecontroller.h +++ b/libsound/src/capturedevicecontroller.h @@ -1,86 +1,86 @@ /* * Copyright 2013 Andreas Cord-Landwehr * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation; either version 2.1 of the License, or * (at your option) 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 Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . */ #ifndef CAPTUREDEVICECONTROLLER_H #define CAPTUREDEVICECONTROLLER_H #include "libsound_export.h" #include class CaptureDeviceControllerPrivate; class QUrl; /** * \class CaptureDeviceController * - * This singelton class provides a controller for the sound capture device. + * This singleton class provides a controller for the sound capture device. */ class LIBSOUND_EXPORT CaptureDeviceController : public QObject { Q_OBJECT public: enum State { StoppedState, RecordingState, PausedState }; /** * Returns self reference to the controller. First call of this method initializes * capture device controller. * * \return self reference */ static CaptureDeviceController & self(); void startCapture(const QString &filePath); CaptureDeviceController::State state() const; void stopCapture(); void setDevice(const QString &deviceIdentifier); /** * \return list of available capture devices */ QList devices() const; public Q_SLOTS: Q_SIGNALS: void captureStarted(); void captureStopped(); private: Q_DISABLE_COPY(CaptureDeviceController) /** * \internal * Private constructor, \ref self(). */ CaptureDeviceController(); /** * Private destructor. */ ~CaptureDeviceController(); const QScopedPointer d; }; #endif diff --git a/libsound/src/outputdevicecontroller.cpp b/libsound/src/outputdevicecontroller.cpp index 8efb722..5468822 100644 --- a/libsound/src/outputdevicecontroller.cpp +++ b/libsound/src/outputdevicecontroller.cpp @@ -1,178 +1,178 @@ /* * Copyright 2013-2014 Andreas Cord-Landwehr * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation; either version 2.1 of the License, or * (at your option) 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 Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . */ #include "outputdevicecontroller.h" #include "outputbackendinterface.h" #include "backendinterface.h" #include #include #include #include #include #include #include "libsound_debug.h" /** * \class OutputDeviceControllerPrivate * \internal * * This is the private data class for \see OutputDeviceController. - * Note that -- even if the OutputDeviceController is contructed before its first call -- all + * Note that -- even if the OutputDeviceController is constructed before its first call -- all * devices get only configured by first instantiation of OutputDeviceController with a call * to OutputDeviceControllerPrivate::lazyInit(), called in OutputDeviceController::self(). */ class OutputDeviceControllerPrivate { public: OutputDeviceControllerPrivate(OutputDeviceController *parent) : m_parent(parent) , m_backend(nullptr) , m_volume(0) , m_initialized(false) { QStringList dirsToCheck; foreach (const QString &directory, QCoreApplication::libraryPaths()) { dirsToCheck << directory + "/artikulate/libsound"; } // load plugins QPluginLoader loader; foreach (const QString &dir, dirsToCheck) { QVector metadataList = KPluginLoader::findPlugins(dir, [=](const KPluginMetaData &data) { - return data.serviceTypes().contains("artikulate/libsound/backend"); + return data.serviceTypes().contains(QStringLiteral("artikulate/libsound/backend")); }); foreach (const auto &metadata, metadataList) { loader.setFileName(metadata.fileName()); qCDebug(LIBSOUND_LOG) << "Load Plugin: " << metadata.name(); if (!loader.load()) { qCCritical(LIBSOUND_LOG) << "Error while loading plugin: " << metadata.name(); } KPluginFactory *factory = KPluginLoader(loader.fileName()).factory(); if (!factory) { qCCritical(LIBSOUND_LOG) << "Could not load plugin:" << metadata.name(); continue; } BackendInterface *plugin = factory->create(parent, QList< QVariant >()); if (plugin->outputBackend()) { m_backendList.append(plugin->outputBackend()); } } } if (!m_backend && !m_backendList.isEmpty()) { m_backend = m_backendList.first(); } } ~OutputDeviceControllerPrivate() { delete m_backend; } void lazyInit() { if (m_initialized) { return; } m_parent->connect(m_backend, &OutputBackendInterface::stateChanged, m_parent, &OutputDeviceController::emitChangedState); m_volume = m_backend->volume(); m_initialized = true; } OutputBackendInterface * backend() const { Q_ASSERT(m_backend); return m_backend; } OutputDeviceController *m_parent; OutputBackendInterface *m_backend; QList m_backendList; int m_volume; // volume as cubic value bool m_initialized; }; OutputDeviceController::OutputDeviceController() : d(new OutputDeviceControllerPrivate(this)) { } OutputDeviceController::~OutputDeviceController() { } OutputDeviceController & OutputDeviceController::self() { static OutputDeviceController instance; instance.d->lazyInit(); return instance; } void OutputDeviceController::play(const QString& filePath) { d->backend()->setUri(filePath); d->backend()->setVolume(d->m_volume); d->backend()->play(); emit started(); } void OutputDeviceController::play(const QUrl &filePath) { play(filePath.toLocalFile()); } void OutputDeviceController::stop() { d->backend()->stop(); emit stopped(); } OutputDeviceController::State OutputDeviceController::state() const { return d->backend()->state(); } void OutputDeviceController::setVolume(int volume) { // backend only accepts volume, when there is a pipeline // store value here and set it when playing d->backend()->setVolume(volume); d->m_volume = volume; } int OutputDeviceController::volume() const { return d->backend()->volume(); } void OutputDeviceController::emitChangedState() { if (state() == OutputDeviceController::StoppedState) { emit stopped(); return; } if (state() == OutputDeviceController::PlayingState) { emit started(); return; } } diff --git a/libsound/src/outputdevicecontroller.h b/libsound/src/outputdevicecontroller.h index f7caa04..1c013ea 100644 --- a/libsound/src/outputdevicecontroller.h +++ b/libsound/src/outputdevicecontroller.h @@ -1,83 +1,83 @@ /* * Copyright 2013 Andreas Cord-Landwehr * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation; either version 2.1 of the License, or * (at your option) 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 Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . */ #ifndef OUTPUTDEVICECONTROLLER_H #define OUTPUTDEVICECONTROLLER_H #include "libsound_export.h" #include class OutputDeviceControllerPrivate; class QUrl; /** * \class OutputDeviceController * - * This singelton class provides a controller for the sound output device. + * This singleton class provides a controller for the sound output device. */ class LIBSOUND_EXPORT OutputDeviceController : public QObject { Q_OBJECT public: enum State { StoppedState, PlayingState, PausedState }; /** * Returns self reference to the controller. First call of this method initializes * output device controller. * * \return self reference */ static OutputDeviceController & self(); void play(const QString &filePath); void play(const QUrl &filePath); OutputDeviceController::State state() const; void stop(); QString currentSource() const; void setVolume(int volume); int volume() const; public Q_SLOTS: void emitChangedState(); Q_SIGNALS: void started(); void stopped(); private: Q_DISABLE_COPY(OutputDeviceController) /** * \internal * Private constructor, \ref self(). */ OutputDeviceController(); /** * Private destructor. */ ~OutputDeviceController(); const QScopedPointer d; }; #endif diff --git a/libsound/src/qtgstreamerbackend/qtgstreamerbackend.cpp b/libsound/src/qtgstreamerbackend/qtgstreamerbackend.cpp index 7a75dda..fcd6805 100644 --- a/libsound/src/qtgstreamerbackend/qtgstreamerbackend.cpp +++ b/libsound/src/qtgstreamerbackend/qtgstreamerbackend.cpp @@ -1,64 +1,63 @@ /* * Copyright 2016 Andreas Cord-Landwehr * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation; either version 2.1 of the License, or * (at your option) 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 Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . */ #include "qtgstreamerbackend.h" #include "qtgstreamercapturebackend.h" #include "qtgstreameroutputbackend.h" -#include "libsound_export.h" #include K_PLUGIN_FACTORY_WITH_JSON( BackendFactory, "qtgstreamerbackend.json", registerPlugin();) QtGStreamerBackend::QtGStreamerBackend(QObject *parent, const QList< QVariant >&) : BackendInterface("qtgstreamer", parent) , m_captureBackend(nullptr) , m_outputBackend(nullptr) { } QtGStreamerBackend::~QtGStreamerBackend() { if (m_captureBackend) { m_captureBackend->deleteLater(); m_captureBackend = nullptr; } if (m_outputBackend) { m_outputBackend->deleteLater(); m_outputBackend = nullptr; } } CaptureBackendInterface * QtGStreamerBackend::captureBackend() const { if (!m_captureBackend) { m_captureBackend = new QtGStreamerCaptureBackend(); } return m_captureBackend; } OutputBackendInterface * QtGStreamerBackend::outputBackend() const { if (!m_outputBackend) { m_outputBackend = new QtGStreamerOutputBackend(); } return m_outputBackend; } #include "qtgstreamerbackend.moc" diff --git a/libsound/src/qtgstreamerbackend/qtgstreamerbackend.h b/libsound/src/qtgstreamerbackend/qtgstreamerbackend.h index a8a9249..3f81ab1 100644 --- a/libsound/src/qtgstreamerbackend/qtgstreamerbackend.h +++ b/libsound/src/qtgstreamerbackend/qtgstreamerbackend.h @@ -1,47 +1,46 @@ /* * Copyright 2016 Andreas Cord-Landwehr * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation; either version 2.1 of the License, or * (at your option) 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 Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . */ #ifndef GSTREAMERBACKEND_H #define GSTREAMERBACKEND_H #include "../backendinterface.h" -#include "libsound_export.h" class CaptureBackendInterface; class OutputBackendInterface; class QtGStreamerCaptureBackend; class QtGStreamerOutputBackend; -class LIBSOUND_EXPORT QtGStreamerBackend : public BackendInterface +class QtGStreamerBackend : public BackendInterface { Q_OBJECT public: explicit QtGStreamerBackend(QObject *parent, const QList< QVariant >&); virtual ~QtGStreamerBackend(); CaptureBackendInterface * captureBackend() const; OutputBackendInterface * outputBackend() const; private: mutable QtGStreamerCaptureBackend *m_captureBackend; mutable QtGStreamerOutputBackend *m_outputBackend; }; #endif diff --git a/libsound/src/qtgstreamerbackend/qtgstreamerbackend.json b/libsound/src/qtgstreamerbackend/qtgstreamerbackend.json index 533b335..6215dbf 100644 --- a/libsound/src/qtgstreamerbackend/qtgstreamerbackend.json +++ b/libsound/src/qtgstreamerbackend/qtgstreamerbackend.json @@ -1,61 +1,64 @@ { "Encoding": "UTF-8", "KPlugin": { "Category": "Plugins", "Description": "Sound backend for GStreamer.", "Description[ca@valencia]": "Dorsal de so pel GStreamer.", "Description[ca]": "Dorsal de so pel GStreamer.", "Description[cs]": "Podpůrná vrstva zvuku pro GStreamer.", "Description[de]": "Sound-Backend für GStreamer.", "Description[el]": "Σύστημα υποστήριξης ήχου για GStreamer.", + "Description[en_GB]": "Sound backend for GStreamer.", "Description[es]": "Motor de sonido para GStreamer.", "Description[et]": "GStreameri heli-taustaprogramm", "Description[fi]": "GStreamer-äänitaustajärjestelmä", "Description[fr]": "Moteur de son pour GStreamer.", "Description[gl]": "Infraestrutura de son para GStreamer.", "Description[nl]": "Geluidsbackend voor GStreamer.", "Description[nn]": "Lydmotor for GStreamer.", "Description[pl]": "Silnik dźwięku dla GStreamer.", "Description[pt]": "Infra-estrutura de som para o GStreamer.", "Description[pt_BR]": "Infraestrutura de som para o GStreamer.", "Description[ru]": "Работа со звуком через GStreamer.", "Description[sk]": "Zvukový backend pre GStreamer.", "Description[sl]": "Zvočno zaledje za GStreamer.", "Description[sv]": "Ljudgränssnitt för Gstreamer.", "Description[tr]": "GStreamer için ses arka yüzü.", "Description[uk]": "Звуковий модуль для GStreamer.", "Description[x-test]": "xxSound backend for GStreamer.xx", "Description[zh_CN]": "GStreamer 声音后端", "Description[zh_TW]": "GStreamer 音效後端介面。", "Id": "artikulate_gstreamer_backend", "License": "GPL", "Name": "GStreamer Backend", "Name[ca@valencia]": "Dorsal GStreamer", "Name[ca]": "Dorsal GStreamer", "Name[cs]": "Podpůrná vrstva GStreamer", "Name[de]": "Backend für GStreamer", "Name[el]": "Σύστημα υποστήριξης GStreamer", + "Name[en_GB]": "GStreamer Backend", "Name[es]": "Motor GStreamer", "Name[et]": "GStreameri taustaprogramm", "Name[fi]": "GStreamer-taustajärjestelmä", "Name[fr]": "Moteur GStreamer", "Name[gl]": "Infraestrutura de GStreamer", "Name[nl]": "GStreamer-backend", "Name[nn]": "GStreamer-motor", "Name[pl]": "Silnik GStreamer", "Name[pt]": "Infra-Estrutura para o GStreamer", "Name[pt_BR]": "Infraestrutura do GStreamer", "Name[ru]": "Модуль поддержки GStreamer", + "Name[sk]": "GStreamer Backend", "Name[sl]": "Zaledje GStreamer", "Name[sv]": "Gstreamer-gränssnitt", "Name[tr]": "GStreamer Arka Yüzü", "Name[uk]": "Модуль GStreamer", "Name[x-test]": "xxGStreamer Backendxx", "Name[zh_CN]": "GStreamer 后端", "Name[zh_TW]": "GStreamer 後端介面", "ServiceTypes": [ "artikulate/libsound/backend" ], "Version": "0.1" } } diff --git a/libsound/src/qtgstreamerbackend/qtgstreameroutputbackend.cpp b/libsound/src/qtgstreamerbackend/qtgstreameroutputbackend.cpp index aa06b88..37c3782 100644 --- a/libsound/src/qtgstreamerbackend/qtgstreameroutputbackend.cpp +++ b/libsound/src/qtgstreamerbackend/qtgstreameroutputbackend.cpp @@ -1,224 +1,224 @@ /* * Copyright 2010 Marco Ballesio * Copyright 2011 Collabora Ltd. * @author George Kiagiadakis * Copyright 2014 Andreas Cord-Landwehr * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation; either version 2.1 of the License, or * (at your option) 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 Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . */ #include "qtgstreameroutputbackend.h" #include #include #include #include #include #include #include #include #include #include #include #include #include QtGStreamerOutputBackend::QtGStreamerOutputBackend() { QGst::init(); } QtGStreamerOutputBackend::~QtGStreamerOutputBackend() { m_pipeline.clear(); } void QtGStreamerOutputBackend::setUri(const QString & uri) { QString realUri = uri; //if uri is not a real uri, assume it is a file path if (realUri.indexOf("://") < 0) { realUri = QUrl::fromLocalFile(realUri).toEncoded(); } if (!m_pipeline) { m_pipeline = QGst::ElementFactory::make("playbin").dynamicCast(); if (m_pipeline) { //watch the bus for messages QGst::BusPtr bus = m_pipeline->bus(); bus->addSignalWatch(); QGlib::connect(bus, "message", this, &QtGStreamerOutputBackend::onBusMessage); } else { qCritical() << "Failed to create the pipeline"; } } if (m_pipeline) { m_pipeline->setProperty("uri", realUri); } } QTime QtGStreamerOutputBackend::position() const { if (m_pipeline) { //here we query the pipeline about its position //and we request that the result is returned in time format QGst::PositionQueryPtr query = QGst::PositionQuery::create(QGst::FormatTime); m_pipeline->query(query); return QGst::ClockTime(query->position()).toTime(); } else { return QTime(0,0); } } void QtGStreamerOutputBackend::setPosition(const QTime & pos) { QGst::SeekEventPtr evt = QGst::SeekEvent::create( 1.0, QGst::FormatTime, QGst::SeekFlagFlush, QGst::SeekTypeSet, QGst::ClockTime::fromTime(pos), QGst::SeekTypeNone, QGst::ClockTime::None ); m_pipeline->sendEvent(evt); } int QtGStreamerOutputBackend::volume() const { if (m_pipeline) { QGst::StreamVolumePtr svp = m_pipeline.dynamicCast(); if (svp) { return svp->volume(QGst::StreamVolumeFormatCubic) * 10; } } return 0; } void QtGStreamerOutputBackend::setVolume(int volume) { if (m_pipeline) { QGst::StreamVolumePtr svp = m_pipeline.dynamicCast(); if(svp) { svp->setVolume((double)volume / 10, QGst::StreamVolumeFormatCubic); } } } QTime QtGStreamerOutputBackend::length() const { if (m_pipeline) { //here we query the pipeline about the content's duration //and we request that the result is returned in time format QGst::DurationQueryPtr query = QGst::DurationQuery::create(QGst::FormatTime); m_pipeline->query(query); return QGst::ClockTime(query->duration()).toTime(); } else { return QTime(0,0); } } OutputDeviceController::State QtGStreamerOutputBackend::state() const { const QGst::State state = m_pipeline ? m_pipeline->currentState() : QGst::StateNull; switch (state) { case QGst::StateNull: return OutputDeviceController::StoppedState; break; case QGst::StatePaused: return OutputDeviceController::PlayingState; break; case QGst::StatePlaying: return OutputDeviceController::PlayingState; break; default: return OutputDeviceController::StoppedState; } } void QtGStreamerOutputBackend::play() { if (m_pipeline) { m_pipeline->setState(QGst::StatePlaying); } } void QtGStreamerOutputBackend::pause() { if (m_pipeline) { m_pipeline->setState(QGst::StatePaused); } } void QtGStreamerOutputBackend::stop() { if (m_pipeline) { m_pipeline->setState(QGst::StateNull); //once the pipeline stops, the bus is flushed so we will //not receive any StateChangedMessage about this. //so, to inform the ui, we have to emit this signal manually. Q_EMIT stateChanged(); } //TODO this is a temporary fix: // the pipeline should not be cleared after every stop, but only when the backend is destructed - // or specifically resetted. Cause of the problem, both QtGStreamerOutputBackend is globally static + // or specifically reset. Cause of the problem, both QtGStreamerOutputBackend is globally static // object and can be destructed _after_ the also globally static object QGlib::Private::ConnectionsStore // is destroyed. With calling QGst::Pipeline destructor by destructing QtGStreamerOutputBackend, // we get a crash. m_pipeline.clear(); } void QtGStreamerOutputBackend::onBusMessage(const QGst::MessagePtr &message) { switch (message->type()) { case QGst::MessageEos: //End of stream. We reached the end of the file. stop(); break; case QGst::MessageError: //Some error occurred. qCritical() << message.staticCast()->error(); stop(); break; case QGst::MessageStateChanged: //The element in message->source() has changed state if (message->source() == m_pipeline) { handlePipelineStateChange(message.staticCast()); } break; default: break; } } void QtGStreamerOutputBackend::handlePipelineStateChange(const QGst::StateChangedMessagePtr &scm) { switch (scm->newState()) { case QGst::StatePlaying: //start the timer when the pipeline starts playing m_positionTimer.start(100); break; case QGst::StatePaused: //stop the timer when the pipeline pauses if(scm->oldState() == QGst::StatePlaying) { m_positionTimer.stop(); } break; default: break; } Q_EMIT stateChanged(); } diff --git a/libsound/src/qtmultimediabackend/qtmultimediabackend.cpp b/libsound/src/qtmultimediabackend/qtmultimediabackend.cpp index f17e19e..d8b7533 100644 --- a/libsound/src/qtmultimediabackend/qtmultimediabackend.cpp +++ b/libsound/src/qtmultimediabackend/qtmultimediabackend.cpp @@ -1,50 +1,49 @@ /* * Copyright 2016 Andreas Cord-Landwehr * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation; either version 2.1 of the License, or * (at your option) 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 Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . */ #include "qtmultimediabackend.h" #include "qtmultimediacapturebackend.h" #include "qtmultimediaoutputbackend.h" -#include "libsound_export.h" #include K_PLUGIN_FACTORY_WITH_JSON( BackendFactory, "qtmultimediabackend.json", registerPlugin();) QtMultimediaBackend::QtMultimediaBackend(QObject *parent, const QList< QVariant >&) - : BackendInterface("qtmultimedia", parent) + : BackendInterface(QStringLiteral("qtmultimedia"), parent) , m_captureBackend(new QtMultimediaCaptureBackend(this)) , m_outputBackend(new QtMultimediaOutputBackend(this)) { } QtMultimediaBackend::~QtMultimediaBackend() { } CaptureBackendInterface * QtMultimediaBackend::captureBackend() const { return m_captureBackend; } OutputBackendInterface * QtMultimediaBackend::outputBackend() const { return m_outputBackend; } #include "qtmultimediabackend.moc" diff --git a/libsound/src/qtmultimediabackend/qtmultimediabackend.h b/libsound/src/qtmultimediabackend/qtmultimediabackend.h index 2226238..f410b48 100644 --- a/libsound/src/qtmultimediabackend/qtmultimediabackend.h +++ b/libsound/src/qtmultimediabackend/qtmultimediabackend.h @@ -1,47 +1,46 @@ /* * Copyright 2016 Andreas Cord-Landwehr * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation; either version 2.1 of the License, or * (at your option) 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 Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . */ #ifndef QTMULTIMEDIABACKEND_H #define QTMULTIMEDIABACKEND_H #include "../backendinterface.h" -#include "libsound_export.h" class CaptureBackendInterface; class OutputBackendInterface; class QtMultimediaCaptureBackend; class QtMultimediaOutputBackend; -class LIBSOUND_EXPORT QtMultimediaBackend : public BackendInterface +class QtMultimediaBackend : public BackendInterface { Q_OBJECT public: explicit QtMultimediaBackend(QObject *parent, const QList< QVariant >&); virtual ~QtMultimediaBackend(); CaptureBackendInterface * captureBackend() const Q_DECL_OVERRIDE; OutputBackendInterface * outputBackend() const Q_DECL_OVERRIDE; private: QtMultimediaCaptureBackend *m_captureBackend; QtMultimediaOutputBackend *m_outputBackend; }; #endif diff --git a/libsound/src/qtmultimediabackend/qtmultimediabackend.json b/libsound/src/qtmultimediabackend/qtmultimediabackend.json index 606d0d5..09444b7 100644 --- a/libsound/src/qtmultimediabackend/qtmultimediabackend.json +++ b/libsound/src/qtmultimediabackend/qtmultimediabackend.json @@ -1,61 +1,64 @@ { "Encoding": "UTF-8", "KPlugin": { "Category": "Plugins", "Description": "Sound backend for QtMultimedia.", "Description[ca@valencia]": "Dorsal de so pel QtMultimedia.", "Description[ca]": "Dorsal de so pel QtMultimedia.", "Description[cs]": "Podpůrná vrstva zvuku pro QtMultimedia.", "Description[de]": "Sound-Backend für QtMultimedia.", "Description[el]": "Σύστημα υποστήριξης ήχου για QtMultimedia.", + "Description[en_GB]": "Sound backend for QtMultimedia.", "Description[es]": "Motor de sonido para QtMultimedia.", "Description[et]": "QtMultimedia heli-taustaprogramm", "Description[fi]": "QtMultimedia-äänitaustajärjestelmä", "Description[fr]": "Moteur de son pour QtMultimedia", "Description[gl]": "Infraestrutura de son para QtMultimedia.", "Description[nl]": "Geluidsbackend voor QtMultimedia.", "Description[nn]": "Lydmotor for QtMultimedia.", "Description[pl]": "Silnik dźwięku dla QtMultimedia.", "Description[pt]": "Infra-estrutura de som para o QtMultimedia.", "Description[pt_BR]": "Infraestrutura de som para o QtMultimedia.", "Description[ru]": "Работа со звуком через QtMultimedia.", "Description[sk]": "Zvukový backend pre QtMultimedia.", "Description[sl]": "Zvočno zaledje za QtMultimedia.", "Description[sv]": "Ljudgränssnitt för QtMultimedia.", "Description[tr]": "QtMultimedia için ses arka yüzü.", "Description[uk]": "Звуковий модуль для QtMultimedia.", "Description[x-test]": "xxSound backend for QtMultimedia.xx", "Description[zh_CN]": "QtMultimedia 声音后端", "Description[zh_TW]": "QtMultimedia 音效後端介面。", "Id": "artikulate_qtmultimedia_backend", "License": "GPL", "Name": "QtMultimedia Backend", "Name[ca@valencia]": "Dorsal QtMultimedia", "Name[ca]": "Dorsal QtMultimedia", "Name[cs]": "Podpůrná vrstva QtMultimedia", "Name[de]": "Backend für QtMultimedia", "Name[el]": "Σύστημα υποστήριξης QtMultimedia", + "Name[en_GB]": "QtMultimedia Backend", "Name[es]": "Motor QtMultimedia", "Name[et]": "QtMultimedia taustaprogramm", "Name[fi]": "QtMultimedia-taustajärjestelmä", "Name[fr]": "Moteur QtMultimedia", "Name[gl]": "Infraestrutura de QtMultimedia", "Name[nl]": "QtMultimedia-backend", "Name[nn]": "QtMultimedia-motor", "Name[pl]": "Silnik QtMultimedia", "Name[pt]": "Infra-Estrutura para o QtMultimedia", "Name[pt_BR]": "Infraestrutura QtMultimedia", "Name[ru]": "Модуль поддержки QtMultimedia", + "Name[sk]": "QtMultimedia Backend", "Name[sl]": "Zaledje QtMultimedia", "Name[sv]": "QtMultimedia-gränssnitt", "Name[tr]": "QtMultimedia Arka Yüzü", "Name[uk]": "Модуль QtMultimedia", "Name[x-test]": "xxQtMultimedia Backendxx", "Name[zh_CN]": "QtMultimedia 后端", "Name[zh_TW]": "QtMultimedia 後端介面", "ServiceTypes": [ "artikulate/libsound/backend" ], "Version": "0.1" } } diff --git a/libsound/src/qtmultimediabackend/qtmultimediacapturebackend.cpp b/libsound/src/qtmultimediabackend/qtmultimediacapturebackend.cpp index 20c140c..fbe3d16 100644 --- a/libsound/src/qtmultimediabackend/qtmultimediacapturebackend.cpp +++ b/libsound/src/qtmultimediabackend/qtmultimediacapturebackend.cpp @@ -1,84 +1,84 @@ /* * Copyright 2016 Andreas Cord-Landwehr * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation; either version 2.1 of the License, or * (at your option) 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 Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . */ #include "qtmultimediacapturebackend.h" #include "libsound_debug.h" #include #include #include QtMultimediaCaptureBackend::QtMultimediaCaptureBackend(QObject *parent) : CaptureBackendInterface(parent) , m_recorder(new QAudioRecorder) { QString selectedInput = m_recorder->defaultAudioInput(); QAudioEncoderSettings audioSettings; - audioSettings.setCodec("audio/vorbis"); + audioSettings.setCodec(QStringLiteral("audio/vorbis")); audioSettings.setQuality(QMultimedia::HighQuality); m_recorder->setAudioSettings(audioSettings); } QtMultimediaCaptureBackend::~QtMultimediaCaptureBackend() { m_recorder->deleteLater(); m_recorder = nullptr; } CaptureDeviceController::State QtMultimediaCaptureBackend::captureState() const { switch (m_recorder->state()) { case QMediaRecorder::StoppedState: return CaptureDeviceController::StoppedState; break; case QMediaRecorder::RecordingState: return CaptureDeviceController::RecordingState; break; case QMediaRecorder::PausedState: return CaptureDeviceController::PausedState; break; default: return CaptureDeviceController::StoppedState; } } void QtMultimediaCaptureBackend::startCapture(const QString &filePath) { m_recorder->setOutputLocation(QUrl::fromLocalFile(filePath)); m_recorder->record(); } void QtMultimediaCaptureBackend::stopCapture() { m_recorder->stop(); } QStringList QtMultimediaCaptureBackend::devices() const { return m_recorder->audioInputs(); } void QtMultimediaCaptureBackend::setDevice(const QString &deviceIdentifier) { if (devices().contains(deviceIdentifier)) { m_recorder->setAudioInput(deviceIdentifier); } else { qCDebug(LIBSOUND_LOG) << "Could not set unknown capture device:" << deviceIdentifier; } } diff --git a/org.kde.artikulate.appdata.xml b/org.kde.artikulate.appdata.xml index 1dc746d..5a9e559 100644 --- a/org.kde.artikulate.appdata.xml +++ b/org.kde.artikulate.appdata.xml @@ -1,109 +1,129 @@ org.kde.artikulate.desktop CC0-1.0 GPL-2.0+ Artikulate - Artikulate Artikulate Artikulate Artikulate Artikulate Artikulate Artikulate Artikulate Artikulate Artikulate Artikulate Artikulate Artikulate Artikulate Artikulate + Artikulate Artikulate Artikulate Artikulate Artikulate Artikulate Artikulate Artikulate Artikulate Artikulate Artikulate Artikulate Artikulate Artikulate xxArtikulatexx Artikulate Artikulate Artikulate Pronunciation Trainer Artikulate vježba izgovora Entrenador de la pronunciació Artikulate Entrenador de la pronunciació Artikulate Artikulate - učitel výslovnosti Artikulate udtaletræning Artikulate-Aussprachetrainer Artikulate Pronunciation Trainer Artikulate Prononciada Trejnilo Entrenador de pronunciación Artikulate Häälduse harjutaja Artikulate Artikulate ääntämisen harjoitteluohjelma Artikulate, entraînement à la prononciation Adestrador de pronuncia Artikulate Artikulate kiejtés oktató + Pelatih Pengucapan Artikulasi Artikulate 발음 타이머 Utspraak-Lehrer Artikulate Artikulate Trainingsprogramma voor uitspraak Artikulate uttaleøvar Trener artykułowanej wymowy Treino de Pronúncia Artikulate Treinador de Pronúncia Artikulate Тренажёр произношения Artikulate Tréner výslovnosti Artikulate Učenje izgovorjave Artikulate Artikulate uttalsövning Artikulate Telaffuz Eğitici Програма для навчання вимові Artikulate xxArtikulate Pronunciation Trainerxx Artikulate 读音训练器 Artikulate 發音訓練器

Artikulate is a pronunciation trainer that helps improving and perfecting a learner's pronunciation skills for a foreign language. It provides courses with native speaker recordings for several training languages. The learner downloads those courses, selects a category of phrases to train, then starts with recording her/his own voice when speaking the phrases and comparing the results to the native speaker's recordings by listening to both. By adjusting and repeating the own pronunciation, the learner can improve his/her skill.

Artikulate je trener izgovora koji poboljšava i usavršava izgovorne vještine za učenike stranih jezik. Ona pruža kurseve sa snimcima na maternjem jeziku za nekoliko jezika obuke. Učenik preuzima ove kurseve, bira kategoriju fraza za obuku, a zatim počinje sa snimanjem njegovog/njenog glasa pri izgovoru fraze.i Porede se rezultati snimaka na maternji i slušaju oba. Podešavanjem i ponavljanja sopstvenog izgovora, učenik može da poboljša njegovu / njenu vještinu.

L'Artikulate és un entrenador de pronunciació que ajuda a millor i perfeccionar les habilitats de pronunciació d'un alumne d'un idioma estranger. Proporciona cursos amb enregistraments d'un parlant natiu. L'alumne baixa aquests cursos, selecciona una categoria de frases per entrenar, després comença enregistrant la seva pròpia veu en pronunciar les frases i compara els resultats amb els enregistraments del parlant natiu escoltant els dos. Ajustant i repetint la pronunciació pròpia, l'alumne pot millorar la seva habilitat.

-

L'Artikulate és un entrenador de pronunciació que ajuda a millor i perfeccionar les habilitats de pronunciació d'un alumne d'un idioma estranger. Proporciona cursos amb enregistraments d'un parlant natiu. L'alumne baixa estos cursos, selecciona una categoria de frases per entrenar, després comença gravant la seua pròpia veu en pronunciar les frases i compara els resultats amb els enregistraments del parlant natiu escoltant els dos. Ajustant i repetint la pronunciació pròpia, l'alumne pot millorar la seua habilitat.

+

L'Artikulate és un entrenador de pronunciació que ajuda a millor i perfeccionar les habilitats de pronunciació d'un alumne d'un idioma estranger. Proporciona cursos amb enregistraments d'un parlant natiu. L'alumne baixa aquests cursos, selecciona una categoria de frases per entrenar, després comença enregistrant la seua pròpia veu en pronunciar les frases i compara els resultats amb els enregistraments del parlant natiu escoltant els dos. Ajustant i repetint la pronunciació pròpia, l'alumne pot millorar la seua habilitat.

Artikulate er udtaletræning som hjælper med at forbedre og perfektionere din udtale af et fremmedsprog. Det leverer kurser med modersmåloptagelser for flere øvelsessprog. Eleven downloader disse kurser, vælger en kategori af fraser der skal øves, og begynder så at optage sin egen stemme når fraserne udtales og sammenligner resultaterne med modersmåloptagelserne ved at lytte til begge. Ved at justere og gentage den egen udtale, kan eleven forbedre sine evner.

Artikulate ist eine Anwendung zum Üben der Aussprache und hilft beim Verbessern und Perfektionieren Ihrer Aussprachefähigkeiten für eine fremde Sprache. Dazu können Übungen mit Aufnahmen von Muttersprachlern für mehrere Zielsprachen heruntergeladen werden. Dann wird eine Gruppe von Redewendungen ausgewählt, die eigene Stimme beim Aussprechen der Redewendungen aufgenommen und mit den Aufnahmen von Muttersprachlern verglichen. Durch Anpassen und Wiederholen kann die eigene Aussprache verbessert werden.

Artikulate is a pronunciation trainer that helps improving and perfecting a learner's pronunciation skills for a foreign language. It provides courses with native speaker recordings for several training languages. The learner downloads those courses, selects a category of phrases to train, then starts with recording her/his own voice when speaking the phrases and comparing the results to the native speaker's recordings by listening to both. By adjusting and repeating the own pronunciation, the learner can improve his/her skill.

Artikulate estas prononciada trejnilo kiu helpas plibonigi kaj perfektigi prononciadon de lernanta sperto por fremda lingvo. Ĝi provizas kursojn kun denaksulaj registroj por pluraj trejnataj lingvoj. La lernanto elŝutas tiujn kursojn, elektas kategorion da frazoj trejnendaj, registras sian voĉon dirante la frazojn kaj komparas la rezultojn al registroj de denaskuloj aŭskultante ambaŭ. Ĝustigante kaj ripetante sian propran prononciadon, la lernanto povas plibonigi sian kompetenton.

Artikulate es un entrenador de pronunciación que ayuda a mejorar y perfeccionar la pronunciación de un idioma extranjero por parte del estudiante. Para ello, proporciona cursos que se pueden descargar con grabaciones de hablantes nativos de varios idiomas. El estudiante descarga los cursos, selecciona un conjunto de frases, graba su propia voz al repetir las frases y luego al escucharlas, compara los resultados con las grabaciones de hablantes nativos. Estudiante puede mejorar su pronunciación mediante el ajuste y la repetición.

Artikulate on häälduse harjutamise abiline, mis aitab õppuril täiendada ja täiustada võõrkeele hääldamisoskust. Rakendus pakub mitme keele kursuseid, millele vastavat keelt emakeelena kõnelejad on loonud salvestisi. Õppur laadib kursused alla, valib väljendite kategooria, mida ta soovib harjutada, ning hakkab siis salvestama enda häält, kui ta lausub etteantud fraase, võrreldes hiljem neid emakeeles kõneleja salvestustega. Oma hääldust kohendades ja väljendeid korrates saab võõrkeeleoskust tublisti edendada.

Artikulate auttaa parantamaan vieraan kielen ääntämistä. Siinä on äidinkielisten puhumia kursseja useille kielille. Harjoittelija lataa kurssit, valitsee harjoitusfraasien luokan ja alkaa äänittää fraaseja itse lausuttuina. Oman ääntämisen äänittämisen jälkeen harjoittelija vertaa tuloksiaan äidinkieliseen puhujaan kuuntelemalla sekä oman että ädiinkielisen puhujan äänitteen. Hiomalla ja toistamalla omaa ääntämystään harjoittelija voi parantaa taitoaan.

Artikulate est un programme d'entraînement à la prononciation pour améliorer et perfectionner les compétences dans une langue étrangère. Il fournit des cours avec les enregistrements d'un locuteur natif pour plusieurs langues d'entraînement. L'élève télécharge ces cours, sélectionne une catégorie ou des phrases à travailler, enregistre sa propre voix en prononçant les phrases, puis se compare aux enregistrement du locuteur natif. En ajustant et en répétant sa propre prononciation, l'élève s'améliore au fur et à mesure.

Artikulate é un adestrador de pronuncia para axudarlo a mellorar e perfeccionar a súa pronuncia de distintos idiomas. Fornece cursos con gravacións de falantes nativos para varios idiomas. O estudante descarga os cursos, selecciona unha categoría de frases para adestrar, e comeza coa gravación da súa propia voz pronunciando as frases, e comparando os resultados coas gravacións do falante nativo, escoitando ambas as dúas gravacións. Axustando e repetindo a súa gravación, o estudante pode mellorar a súa pronuncia.

+

Artikulate adalah pelatih pengucapan yang membantu meningkatkan dan menyempurnakan keterampilan pengucapan pembelajar untuk bahasa asing. Ini memberikan kursus dengan rekaman penutur asli untuk beberapa bahasa pelatihan. Pelajar mengunduh kursus-kursus tersebut, memilih kategori frasa untuk dilatih, kemudian mulai dengan merekam suaranya sendiri ketika mengucapkan frasa dan membandingkan hasilnya dengan rekaman penutur asli dengan mendengarkan keduanya. Dengan menyesuaikan dan mengulangi pengucapannya sendiri, pelajar dapat meningkatkan keterampilannya.

Artikulate is en Utspraak-Lehrprogramm dat dor bi hölpen schall, de Utspraak vun en Schöler to verbetern. Dat höllt Lexen mit Opnahmen vun Moderspraaklers för en Reeg vun Spraken praat. De Schöler laadt de Lexen daal, söcht en Kategorie vun Utdrück ut, de he lehren will, nimmt sien egen Stimm op, wenn he de Utdrück weddergifft un vergliekt dat mit de Opnahmen vun de Moderspraaklers. So kann he sien egen Utspraak verbetern.

Artikulate is een trainingsprogramma voor uitspraak dat helpt om het uitspreken van een vreemde taal van een leerling te verbeteren en te perfectioneren. Het biedt lessen met opnamen van sprekers in de eigen taal voor training in verschillende talen. De leerling downloadt deze lessen, selecteert een categorie van te leren frasen, start daarna met het opnemen van haar/zijn eigen stem bij het uitspreken van de frasen en vergelijkt de resultaten met die van de opnamen van de spreker in de eigen taal door naar beiden te luisteren. Door de eigen uitspraak aan te passen en te herhalen, kan de leerling zijn/haar prestaties verbeteren.

Artikulate jest programem do ćwiczenia wymowy, który pomaga ulepszać umiejętności wymowy uczącego się w obcym języku. Zapewnia lekcje z nagraniami rodowitego mówcy dla kilku języków. Uczący się pobiera te lekcje, wybiera kategorię wyrażeń do przećwiczenia, a następnie zaczyna nagrywać swój własny głos porównując przy tym swoje wyniki z głosem rodowitego mówcy, poprzez słuchanie obu. Poprzez dostrajanie i powtarzanie wymowy, uczący się może polepszyć własne umiejętności.

O Artikulate é um treinador de pronúncia que ajuda a melhorar e a aperfeiçoar os dotes de pronúncia de um aluno para uma dada língua estrangeira. Fornece exercícios com gravações de locutores nativos para diversas línguas de treino. O aluno pode transferir esses exercícios, escolher uma categoria de frases a treinar, começando depois a gravar a sua própria voz a falar as frases e a comparar os resultados com as gravações do locutor nativo, ouvindo-os a ambos. Ao ajustar e ao repetir a sua própria pronúncia, o aluno poderá melhorar as suas aptidões.

Artikulate é um treinador de pronúncia que ajuda a melhorar e aperfeiçoar as habilidades de pronúncia de um aluno para um idioma estrangeiro. Fornece exercícios com gravações de locutores nativos para diversos idiomas de treinamento. O aluno pode baixar esses exercícios, escolher uma categoria de frases para treinar, começando com a gravação da sua própria voz ao falar as frases e comparar os resultados com as gravações do locutor nativo, ouvindo-os a ambos. Ao ajustar e repetir a sua própria pronúncia, o aluno poderá melhorar as suas aptidões.

-

Artikulate — тренажёр произношения, который может помочь при тренировке произношения иностранных слов. В программе можно выбрать курсы с записью голосов носителей языка.После того, как выбранный курс будет загружен из Интернета, обучающийся может записать своё произношение фраз и сличить с записью голоса диктора. Повторяя за диктором и исправляя несоответствия, можно улучшить свой навык произношения фраз на иностранном языке.

+

Artikulate — тренажёр произношения, который может помочь при тренировке произношения иностранных слов. В программе можно выбрать курсы с записью голосов носителей языка. После того, как выбранный курс будет загружен из Интернета, обучающийся может записать своё произношение фраз и сличить с записью голоса диктора. Повторяя за диктором и исправляя несоответствия, можно улучшить свой навык произношения фраз на иностранном языке.

Artikulate je tréner výslovnosti, ktorý pomôže zlepšiť výslovnosť študenta pre cudzí jazyk. Poskytuje kurzy s nahrávkami rodeného hovorcu pre niekoľko výukových jazykov. Študent si stiahne tieto kurzy, vyberie kategóriu a frázy na precvičenie, potom začne nahrávať svoj hlas pri hovorení fráz a porovná výsledky s rodeným hovorcom počúvaním oboch. Úpravou a opakovaním výslovnosti si môže študent zlepšiť schopnosti.

Artikulate je pomočnik za učenje izgovorjave, ki vam pomaga izboljšati in izpopolniti izgovorjavo tujega jezika. Ponuja tečaje s posnetki govorca maternega jezika za številne jezike. Učenec prejme tečaje, izbere kategorijo fraz, ki se bi jih rad naučil in nato začne s snemanjem svojega glasu med izgovorjavo fraz. Te posnetke primerja s posnetki govorca maternega jezika. S prilagajanjem in ponavljanjem izgovorjave, lahko učenec izboljša svoje sposobnosti.

Artikulate är ett uttalsövningsprogram som hjälper till att förbättra och fullkomna en elevs uttalsfärdighet på ett främmande språk. Det tillhandahåller kurser med inspelningar av infödda på ett flertal övningsspråk. Eleven laddar ner kurserna, väljer en kategori meningar att öva på, och börjar därefter med att spela in sin egen röst vid uppläsning av meningarna och jämför därefter resultatet med de inföddas inspelningar genom att lyssna på båda. Genom att justera och upprepa sitt eget uttal, kan eleven förbättra sina färdigheter.

Artikulate yabancı dil öğrenmeye çalışanların telaffuzlarını geliştirmelerine ve mükemmelleştirmelerine yardım eden bir telaffuz eğiticisidir. Birçok eğitim dili için doğal konuşma kayıtlarını içeren dersler sağlar. Öğrenci kursları indirir, çalışacağı kategoriyi seçer ve kendi seslendirdiği ifadeleri kaydederek o dili doğal dil olarak konuşanların kayıtlarıyla karşılaştırmaya başlar. Kendi telaffuzunu ayarlayarak ve tekrarlayarak, öğrenci becerilerini artırabilir.

Artikulate — програма для навчання вимові, яка допомагає у покращенні та удосконаленні навичок учня з вимови слів іноземною мовою. У програмі передбачено курси, записані людьми, для яких мова є рідною, створені декількома мовами. Учень може отримати такий курс, вибрати категорію фраз для навчання і розпочати записування власних спроб вимовити фразу та порівняння з еталонними записами. Коригуючи і повторюючи вимов, учень може покращити власні навички.

xxArtikulate is a pronunciation trainer that helps improving and perfecting a learner's pronunciation skills for a foreign language. It provides courses with native speaker recordings for several training languages. The learner downloads those courses, selects a category of phrases to train, then starts with recording her/his own voice when speaking the phrases and comparing the results to the native speaker's recordings by listening to both. By adjusting and repeating the own pronunciation, the learner can improve his/her skill.xx

Artikulate 可以協助改進學生學習的外國語發音。它提供以該語言為母語的人的發音錄音課程。學生只要下載這些課程,選擇要練習的類別或片語,然後開始錄自己的發音,並跟母語發音做比較。只要不斷重複並調整自己的發音,就可以進步。

http://edu.kde.org/artikulate/ https://bugs.kde.org/enter_bug.cgi?format=guided&product=artikulate http://docs.kde.org/stable/en/kdeedu/artikulate/index.html + https://www.kde.org/community/donations/?app=artikulate&source=appdata + Training in Artikulate + Entrenament a l'Artikulate + Entrenament a l'Artikulate + Übung in Artikulate + Training in Artikulate + Entrenando en Artikulate + Adestrando en Artikulate + Berlatih di Artikulate + Oefening in Artikulate + Ćwiczenie w Artikulate + Exercício no Artikulate + Treinando no Artikulate + Övning i Artikulate + Вправляння в Artikulate + xxTraining in Artikulatexx + 使用 Artikulate 训练 + 在 Artikulate 中訓練 http://edu.kde.org/images/screenshots/resized/artikulate.png KDE artikulate
diff --git a/org.kde.artikulate.desktop b/org.kde.artikulate.desktop index 71fd0c0..3c30d9c 100755 --- a/org.kde.artikulate.desktop +++ b/org.kde.artikulate.desktop @@ -1,82 +1,81 @@ # KDE Config File [Desktop Entry] Type=Application Exec=artikulate -qwindowtitle "%c" %i Icon=artikulate X-DocPath=artikulate/index.html GenericName=Artikulate Pronunciation Trainer GenericName[bs]=Artikulate vježba izgovora GenericName[ca]=Entrenador de la pronunciació Artikulate GenericName[ca@valencia]=Entrenador de la pronunciació Artikulate GenericName[cs]=Artikulate - učitel výslovnosti GenericName[da]=Artikulate udtaletræning GenericName[de]=Artikulate-Aussprachetrainer GenericName[el]=Εκπαιδευτής προφοράς Artikulate GenericName[en_GB]=Artikulate Pronunciation Trainer GenericName[eo]=Artikulate Prononciada Trejnilo GenericName[es]=Entrenador de pronunciación Artikulate GenericName[et]=Häälduse harjutaja Artikulate GenericName[fi]=Artikulate ääntämisen harjoitteluohjelma GenericName[fr]=Programme Artikulate d'entraînement à la prononciation GenericName[gl]=Adestrador de pronuncia Artikulate GenericName[hi]=आर्टिकुलेट उच्चारण प्रशिक्षक GenericName[hu]=Artikulate kiejtés oktató GenericName[ko]=Artikulate 발음 타이머 GenericName[mr]=आर्टिक्युलेट उच्चारण प्रशिक्षक GenericName[nds]=Artikulate - Utspraak öven GenericName[nl]=Artikulate Trainingsprogramma voor uitspraak GenericName[nn]=Artikulate uttaleøving GenericName[pl]=Trener artykułowanej wymowy GenericName[pt]=Treino de Pronúncia Artikulate GenericName[pt_BR]=Treinador de Pronúncia Artikulate GenericName[ro]=Antrenor de pronunție Artikulate GenericName[ru]=Тренажёр произношения Artikulate GenericName[sk]=Tréner výslovnosti Artikulate GenericName[sl]=Učenje izgovorjave Artikulate GenericName[sv]=Artikulate uttalsövning GenericName[te]=అర్టీక్యులెట్ ఉచ్చారణ శిక్షకీ GenericName[tr]=Artikulate Telaffuz Eğitici GenericName[uk]=Навчання вимові Artikulate GenericName[x-test]=xxArtikulate Pronunciation Trainerxx GenericName[zh_CN]=Artikulate 读音训练器 GenericName[zh_TW]=Artikulate 發音訓練師 Terminal=false Name=Artikulate -Name[ast]=Artikulate Name[bs]=Artikulate Name[ca]=Artikulate Name[ca@valencia]=Artikulate Name[cs]=Artikulate Name[da]=Artikulate Name[de]=Artikulate Name[el]=Artikulate Name[en_GB]=Artikulate Name[eo]=Artikulate Name[es]=Artikulate Name[et]=Artikulate Name[fi]=Artikulate Name[fr]=Artikulate Name[gl]=Artikulate Name[hi]=आर्टिकुलेट Name[hu]=Artikulate Name[it]=Artikulate Name[ko]=Artikulate Name[mr]=आर्टिक्युलेट Name[nds]=Artikulate Name[nl]=Artikulate Name[nn]=Artikulate Name[pl]=Artikulate Name[pt]=Artikulate Name[pt_BR]=Artikulate Name[ro]=Artikulate Name[ru]=Artikulate Name[sk]=Artikulate Name[sl]=Artikulate Name[sv]=Artikulate Name[te]=అర్టీక్యులెట్ Name[tr]=Artikulate Name[uk]=Artikulate Name[x-test]=xxArtikulatexx Name[zh_CN]=Artikulate Name[zh_TW]=Artikulate Categories=Qt;KDE;Education;X-KDE-Edu-Misc; diff --git a/src/application.cpp b/src/application.cpp index 118af05..083bfa9 100644 --- a/src/application.cpp +++ b/src/application.cpp @@ -1,119 +1,119 @@ /* * Copyright 2013-2015 Andreas Cord-Landwehr * * 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 "application.h" #include "core/course.h" #include "core/drawertrainingactions.h" #include "core/editorsession.h" #include "core/language.h" #include "core/phoneme.h" #include "core/phonemegroup.h" #include "core/phrase.h" #include "core/player.h" #include "core/recorder.h" #include "core/resourcemanager.h" #include "core/skeleton.h" #include "core/trainingsession.h" #include "core/unit.h" #include "models/coursefiltermodel.h" #include "models/coursemodel.h" #include "models/languagemodel.h" #include "models/languageresourcemodel.h" #include "models/learningprogressmodel.h" #include "models/phonemegroupmodel.h" #include "models/phonememodel.h" #include "models/phonemeunitmodel.h" #include "models/phrasefiltermodel.h" #include "models/phraselistmodel.h" #include "models/phrasemodel.h" #include "models/profilemodel.h" #include "models/skeletonmodel.h" #include "models/unitfiltermodel.h" #include "models/unitmodel.h" #include "qmlcontrols/iconitem.h" #include "liblearnerprofile/src/learner.h" #include "liblearnerprofile/src/profilemanager.h" #include "liblearnerprofile/src/learninggoal.h" #include "liblearnerprofile/src/models/learninggoalmodel.h" #include #include #include #include Application::Application(int& argc, char** argv) : QApplication(argc, argv) { registerQmlTypes(); } void Application::registerQmlTypes() { qmlRegisterUncreatableType( "artikulate", 1, 0, "TrainingSession", - "TrainingSession is unique object provided by the backend"); + QStringLiteral("TrainingSession is unique object provided by the backend")); qmlRegisterUncreatableType( "artikulate", 1, 0, "EditorSession", - "EditorSession is unique object provided by the backend"); + QStringLiteral("EditorSession is unique object provided by the backend")); qmlRegisterUncreatableType( "artikulate", 1, 0, "ResourceManager", - "ResourceManager is unique object provided by the backend"); + QStringLiteral("ResourceManager is unique object provided by the backend")); qmlRegisterUncreatableType( "artikulate", 1, 0, "ProfileManager", - "ProfileManager is unique object provided by the backend"); + QStringLiteral("ProfileManager is unique object provided by the backend")); qmlRegisterType("artikulate", 1, 0, "Learner"); qmlRegisterType("artikulate", 1, 0, "LearningGoal"); qmlRegisterType("artikulate", 1, 0, "Unit"); qmlRegisterType("artikulate", 1, 0, "Skeleton"); qmlRegisterType("artikulate", 1, 0, "Course"); qmlRegisterType("artikulate", 1, 0, "Language"); qmlRegisterType("artikulate", 1, 0, "ResourceManager"); qmlRegisterType("artikulate", 1, 0, "Phrase"); qmlRegisterType("artikulate", 1, 0, "Phoneme"); qmlRegisterType("artikulate", 1, 0, "PhonemeGroup"); qmlRegisterType("artikulate", 1, 0, "Player"); qmlRegisterType("artikulate", 1, 0, "Recorder"); qmlRegisterType("artikulate", 1, 0, "Icon"); qmlRegisterType("artikulate", 1, 0, "DrawerTrainingActions"); qmlRegisterType("artikulate", 1, 0, "CourseModel"); qmlRegisterType("artikulate", 1, 0, "CourseFilterModel"); qmlRegisterType("artikulate", 1, 0, "LanguageModel"); qmlRegisterType("artikulate", 1, 0, "LanguageResourceModel"); // qmlRegisterType("artikulate", 1, 0, "LearningProgressModel");//TODO must be ported to new trainingsession qmlRegisterType("artikulate", 1, 0, "UnitModel"); qmlRegisterType("artikulate", 1, 0, "UnitFilterModel"); qmlRegisterType("artikulate", 1, 0, "PhraseModel"); qmlRegisterType("artikulate", 1, 0, "PhraseListModel"); qmlRegisterType("artikulate", 1, 0, "PhraseFilterModel"); qmlRegisterType("artikulate", 1, 0, "PhonemeModel"); qmlRegisterType("artikulate", 1, 0, "PhonemeGroupModel"); qmlRegisterType("artikulate", 1, 0, "PhonemeUnitModel"); qmlRegisterType("artikulate", 1, 0, "ProfileModel"); qmlRegisterType("artikulate", 1, 0, "SkeletonModel"); qmlRegisterType("artikulate", 1, 0, "LearningGoalModel"); } diff --git a/src/core/course.cpp b/src/core/course.cpp index 77be608..5534564 100644 --- a/src/core/course.cpp +++ b/src/core/course.cpp @@ -1,353 +1,353 @@ /* * Copyright 2013-2015 Andreas Cord-Landwehr * * 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 "course.h" #include "unit.h" #include "language.h" #include "resources/resourceinterface.h" #include "resources/courseresource.h" #include "resourcemanager.h" #include "phonemegroup.h" #include "artikulate_debug.h" #include #include #include #include Course::Course(ResourceInterface *resource) : QObject(resource) , m_resource(qobject_cast(resource)) , m_language(nullptr) , m_modified(false) { } Course::~Course() { foreach (Unit *unit, m_unitList) { unit->deleteLater(); } m_unitList.clear(); // clear phonom units QMultiMap< PhonemeGroup*, QList< QPair > >::iterator groupIter = m_phonemeUnitList.begin(); while (groupIter != m_phonemeUnitList.end()) { QList< QPair >::iterator itemIter = groupIter->begin(); while (itemIter != groupIter->end()) { itemIter->first->deleteLater(); // delete phoneme itemIter->second->deleteLater(); // delete unit ++itemIter; } groupIter->clear(); ++groupIter; } m_phonemeUnitList.clear(); m_phonemeGroupList.clear(); } QString Course::id() const { return m_id; } void Course::setId(const QString &id) { if (id != m_id) { m_id = id; emit idChanged(); setModified(); } } QString Course::foreignId() const { return m_foreignId; } void Course::setForeignId(const QString &id) { m_foreignId = id; } QString Course::title() const { return m_title; } QString Course::i18nTitle() const { return m_resource->i18nTitle(); } void Course::setTitle(const QString &title) { if (QString::compare(title, m_title) != 0) { m_title = title; emit titleChanged(); setModified(); } } QString Course::description() const { return m_description; } void Course::setDescription(const QString &description) { m_description = description; emit descriptionChanged(); } Language * Course::language() const { return m_language; } void Course::setLanguage(Language *language) { Q_ASSERT(language); // TODO this should happen in the ctor foreach (PhonemeGroup *group, language->phonemeGroups()) { addPhonemeGroup(group); } m_language = language; emit languageChanged(); } QUrl Course::file() const { return m_file; } void Course::setFile(const QUrl &file) { m_file = file; } QList< Unit* > Course::unitList() const { return m_unitList; } void Course::addUnit(Unit *unit) { QList::ConstIterator iter = m_unitList.constBegin(); while (iter != m_unitList.constEnd()) { if (unit->id() == (*iter)->id()) { qCWarning(ARTIKULATE_LOG) << "Unit already contained in this course, aborting"; return; } ++iter; } emit unitAboutToBeAdded(unit, m_unitList.length()); m_unitList.append(unit); connect(unit, &Unit::modified, [=]() { setModified(true); }); // these connections are only present for "normal units" and take care to register // there phrases also at phoneme units connect(unit, SIGNAL(phraseAdded(Phrase*)), this, SLOT(registerPhrasePhonemes(Phrase*))); connect(unit, SIGNAL(phraseRemoved(Phrase*)), this, SLOT(removePhrasePhonemes(Phrase*))); emit unitAdded(); setModified(true); } Unit * Course::createUnit() { // find first unused id QStringList unitIds; foreach (Unit *unit, m_unitList) { unitIds.append(unit->id()); } QString id = QUuid::createUuid().toString(); while (unitIds.contains(id)) { id = QUuid::createUuid().toString(); qCWarning(ARTIKULATE_LOG) << "Unit id generator has found a collision, recreating id."; } // create unit Unit *unit = new Unit(this); unit->setCourse(this); unit->setId(id); unit->setTitle(i18n("New Unit")); addUnit(unit); return unit; } Phrase * Course::createPhrase(Unit *unit) { // find globally unique phrase id inside course QStringList phraseIds; foreach (Unit *unit, m_unitList) { foreach (Phrase *phrase, unit->phraseList()) { phraseIds.append(phrase->id()); } } QString id = QUuid::createUuid().toString(); while (phraseIds.contains(id)) { id = QUuid::createUuid().toString(); qCWarning(ARTIKULATE_LOG) << "Phrase id generator has found a collision, recreating id."; } // create unit Phrase *phrase = new Phrase(this); phrase->setId(id); - phrase->setText(""); + phrase->setText(QLatin1String("")); phrase->setType(Phrase::Word); unit->addPhrase(phrase); return phrase; } QList< Unit* > Course::phonemeUnitList(PhonemeGroup *phonemeGroup) const { QList list; QList< QPair >::ConstIterator iter = m_phonemeUnitList.value(phonemeGroup).constBegin(); while (iter != m_phonemeUnitList.value(phonemeGroup).constEnd()) { list.append(iter->second); ++iter; } return list; } Unit * Course::phonemeUnit(Phoneme *phoneme) const { foreach (PhonemeGroup *group, m_phonemeUnitList.keys()) { m_phonemeUnitList.value(group); QList< QPair >::ConstIterator iter = m_phonemeUnitList.value(group).constBegin(); while (iter != m_phonemeUnitList.value(group).constEnd()) { if (iter->first == phoneme) { return iter->second; } ++iter; } } return 0; } PhonemeGroup * Course::phonemeGroup(Unit *unit) const { foreach (PhonemeGroup *group, m_phonemeUnitList.keys()) { m_phonemeUnitList.value(group); QList< QPair >::ConstIterator iter = m_phonemeUnitList.value(group).constBegin(); while (iter != m_phonemeUnitList.value(group).constEnd()) { if (iter->second == unit) { return group; } ++iter; } } return 0; } void Course::addPhonemeGroup(PhonemeGroup *phonemeGroup) { if (m_phonemeUnitList.contains(phonemeGroup)) { qCWarning(ARTIKULATE_LOG) << "Phoneme group already contained in this course, aborting"; return; } emit phonemeGroupAboutToBeAdded(phonemeGroup, m_phonemeGroupList.count()); // add to phoneme list m_phonemeGroupList.append(phonemeGroup); m_phonemeUnitList.insert(phonemeGroup, QList< QPair >()); emit phonemeGroupAdded(); setModified(); } QList Course::phonemeGroupList() const { return m_phonemeGroupList; } bool Course::modified() const { return m_modified; } void Course::setModified(bool modified) { if (m_modified == modified) { return; } m_modified = modified; emit modifiedChanged(); } void Course::sync() { if (!m_file.isValid() || m_file.isEmpty() || m_resource == 0) { qCritical() << "Path" << m_file.toLocalFile() << "not valid, aborting sync operation."; return; } m_resource->sync(); setModified(false); } bool Course::isContributorResource() const { return m_resource->isContributorResource(); } void Course::registerPhrasePhonemes(Phrase *phrase) { // iterate over all phonemes of this phrase foreach (Phoneme *phoneme, phrase->phonemes()) { // try to find corresponding phonem groups (phonem groups are registered on course creation) foreach (PhonemeGroup *group, m_phonemeGroupList) { if (!group->contains(phoneme)) { continue; } // either add phrase to existing unit or register a new one bool phraseRegistered = false; QList< QPair >::ConstIterator iter = m_phonemeUnitList.value(group).constBegin(); while (iter != m_phonemeUnitList.value(group).constEnd()) { if (iter->first->id() == phoneme->id()) { iter->second->addPhrase(phrase); phraseRegistered = true; } ++iter; } // otherwise, need to create a new unit if (phraseRegistered == false) { // create unit based on the phoneme group Unit *unit = new Unit(this); unit->setId(phoneme->id()); unit->setTitle(phoneme->title()); unit->setCourse(this); m_phonemeUnitList[group].append(qMakePair(phoneme, unit)); unit->addPhrase(phrase); } } } } void Course::removePhrasePhonemes(Phrase* phrase) { qCritical() << "Not yet implemented!"; } diff --git a/src/core/language.cpp b/src/core/language.cpp index b490f39..9885dba 100644 --- a/src/core/language.cpp +++ b/src/core/language.cpp @@ -1,124 +1,124 @@ /* * Copyright 2013 Andreas Cord-Landwehr * * 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 "language.h" #include "models/languagemodel.h" #include "phoneme.h" #include "phonemegroup.h" #include "artikulate_debug.h" #include Language::Language(QObject *parent) : QObject(parent) { } Language::~Language() { qDeleteAll(m_phonemeGroups); } QString Language::id() const { return m_id; } void Language::setId(const QString &id) { if (id != m_id) { m_id = id; emit idChanged(); } } QString Language::title() const { return m_title; } void Language::setTitle(const QString &title) { if (QString::compare(title, m_title) != 0) { m_title = title; emit titleChanged(); } } QString Language::i18nTitle() const { return i18n(m_i18nTitle.toUtf8()); } void Language::seti18nTitle(const QString &title) { if (m_i18nTitle == title) { return; } m_i18nTitle = title; emit i18nTitleChanged(); } QUrl Language::file() const { return m_file; } void Language::setFile(const QUrl &file) { m_file = file; } QList Language::phonemes() const { QList list; foreach (PhonemeGroup *group, m_phonemeGroups) { list.append(group->phonemes()); } return list; } QList Language::phonemeGroups() const { return m_phonemeGroups; } PhonemeGroup * Language::addPhonemeGroup(const QString &identifier, const QString &title) { QList::ConstIterator iter = m_phonemeGroups.constBegin(); while (iter != m_phonemeGroups.constEnd()) { if (QString::compare((*iter)->id(), identifier) == 0) { - qCWarning(ARTIKULATE_LOG) << "Prononciation Group identifier already registered, aborting"; + qCWarning(ARTIKULATE_LOG) << "Pronunciation Group identifier already registered, aborting"; return 0; } ++iter; } PhonemeGroup *newGroup = new PhonemeGroup(); newGroup->setId(identifier); newGroup->setTitle(title); m_phonemeGroups.append(newGroup); - connect(newGroup, SIGNAL(phonemeAdded(Phoneme)), this, SIGNAL(phonemesChanged())); - connect(newGroup, SIGNAL(phonemeRemoved(Phoneme)), this, SIGNAL(phonemesChanged())); + connect(newGroup, &PhonemeGroup::phonemeAdded, this, &Language::phonemesChanged); + connect(newGroup, &PhonemeGroup::phonemeRemoved, this, &Language::phonemesChanged); emit phonemeGroupsChanged(); return newGroup; } diff --git a/src/core/phrase.cpp b/src/core/phrase.cpp index 1e9009e..c6c5753 100644 --- a/src/core/phrase.cpp +++ b/src/core/phrase.cpp @@ -1,335 +1,335 @@ /* * Copyright 2013 Andreas Cord-Landwehr * * 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 "phrase.h" #include "libsound/src/capturedevicecontroller.h" #include "libsound/src/outputdevicecontroller.h" #include "unit.h" #include "course.h" #include "settings.h" #include "artikulate_debug.h" #include Phrase::Phrase(QObject *parent) : QObject(parent) , m_type(Phrase::AllTypes) , m_editState(Unknown) , m_unit(nullptr) , m_trainingProgress(0) , m_skipCounter(0) , m_excludedFromUnit(false) { connect(this, &Phrase::idChanged, this, &Phrase::modified); connect(this, &Phrase::typeChanged, this, &Phrase::modified); connect(this, &Phrase::textChanged, this, &Phrase::modified); connect(this, &Phrase::soundChanged, this, &Phrase::modified); connect(this, &Phrase::editStateChanged, this, &Phrase::modified); connect(this, &Phrase::i18nTextChanged, this, &Phrase::modified); connect(this, &Phrase::phonemesChanged, this, &Phrase::modified); connect(this, &Phrase::excludedChanged, this, &Phrase::modified); } Phrase::~Phrase() { } QString Phrase::id() const { return m_id; } void Phrase::setId(const QString &id) { if (id != m_id) { m_id = id; emit idChanged(); } } QString Phrase::foreignId() const { return m_foreignId; } void Phrase::setForeignId(const QString &id) { m_foreignId = id; } QString Phrase::text() const { return m_text; } void Phrase::setText(const QString &text) { if (QString::compare(text, m_text) != 0) { m_text = text.trimmed(); emit textChanged(); } } QString Phrase::i18nText() const { return m_i18nText; } void Phrase::seti18nText(const QString &text) { if (QString::compare(text, m_i18nText) != 0) { // copy unmodified original text string m_i18nText = text; emit i18nTextChanged(); } } Phrase::Type Phrase::type() const { return m_type; } QString Phrase::typeString() const { switch(m_type) { case Word: - return "word"; + return QStringLiteral("word"); case Expression: - return "expression"; + return QStringLiteral("expression"); case Sentence: - return "sentence"; + return QStringLiteral("sentence"); case Paragraph: - return "paragraph"; + return QStringLiteral("paragraph"); default: - return "ERROR_UNKNOWN_TYPE"; + return QStringLiteral("ERROR_UNKNOWN_TYPE"); } } void Phrase::setType(Phrase::Type type) { if (m_type == type) { return; } m_type = type; emit typeChanged(); } void Phrase::setType(const QString &typeString) { - if (typeString == "word") { + if (typeString == QLatin1String("word")) { setType(Word); return; } - if (typeString == "expression") { + if (typeString == QLatin1String("expression")) { setType(Expression); return; } - if (typeString == "sentence") { + if (typeString == QLatin1String("sentence")) { setType(Sentence); return; } - if (typeString == "paragraph") { + if (typeString == QLatin1String("paragraph")) { setType(Paragraph); return; } qCWarning(ARTIKULATE_LOG) << "Cannot set type from unknown identifier, aborting"; return; } Phrase::EditState Phrase::editState() const { return m_editState; } QString Phrase::editStateString() const { switch(m_editState) { case Unknown: - return "unknown"; + return QStringLiteral("unknown"); case Translated: - return "translated"; + return QStringLiteral("translated"); case Completed: - return "completed"; + return QStringLiteral("completed"); default: - return "ERROR_UNKNOWN_EDIT_STATE"; + return QStringLiteral("ERROR_UNKNOWN_EDIT_STATE"); } } void Phrase::setEditState(Phrase::EditState state) { if (m_editState == state) { return; } m_editState = state; emit editStateChanged(); } void Phrase::setEditState(const QString &stateString) { if (stateString.isEmpty()) { return; } - if (stateString == "unknown") { + if (stateString == QLatin1String("unknown")) { setEditState(Unknown); return; } - if (stateString == "translated") { + if (stateString == QLatin1String("translated")) { setEditState(Translated); return; } - if (stateString == "completed") { + if (stateString == QLatin1String("completed")) { setEditState(Completed); return; } qCWarning(ARTIKULATE_LOG) << "Cannot set edit state from unknown identifier " << stateString << ", aborting"; return; } Unit * Phrase::unit() const { return m_unit; } void Phrase::setUnit(Unit *unit) { if (unit == m_unit) { return; } m_unit = unit; emit unitChanged(); } QUrl Phrase::sound() const { return m_nativeSoundFile; } void Phrase::setSound(const QUrl &soundFile) { if (!soundFile.isValid() || soundFile.isEmpty()) { qCWarning(ARTIKULATE_LOG) << "Not setting empty sound file path."; return; } m_nativeSoundFile = soundFile; emit soundChanged(); } QString Phrase::soundFileUrl() const { return m_nativeSoundFile.toLocalFile(); } QString Phrase::soundFileOutputPath() const { if (m_nativeSoundFile.isEmpty()) { QString outputDir = m_unit->course()->file().path() + '/'; //TODO take care that this is proper ASCII return outputDir + id() + ".ogg"; } else { return soundFileUrl(); } } void Phrase::setSoundFileUrl() { if (soundFileOutputPath() != m_nativeSoundFile.toLocalFile()) { m_nativeSoundFile = QUrl::fromLocalFile(soundFileOutputPath()); emit soundChanged(); emit modified(); } } bool Phrase::isExcluded() const { return m_excludedFromUnit; } void Phrase::setExcluded(bool excluded) { if (excluded == m_excludedFromUnit) { return; } m_excludedFromUnit = excluded; emit excludedChanged(); } int Phrase::progress() const { return m_trainingProgress; } void Phrase::setProgress(int value) { if (m_trainingProgress == value) { return; } m_trainingProgress = value; emit progressChanged(); } void Phrase::updateProgress(Phrase::Progress progress) { // logic of progress computation: // a) if skipped 3 times in a row, decrease progress // b) if done and skipped less than two times in a row, increase progress if (progress == Progress::Done) { m_skipCounter = 0; if (m_trainingProgress < 3) { ++m_trainingProgress; emit progressChanged(); } return; } if (progress == Progress::Skip) { ++m_skipCounter; if (m_skipCounter > 2 && m_trainingProgress > 0) { --m_trainingProgress; emit progressChanged(); } return; } } QList Phrase::phonemes() const { return m_phonemes; } bool Phrase::hasPhoneme(Phoneme* phoneme) { return m_phonemes.contains(phoneme); } void Phrase::addPhoneme(Phoneme *phoneme) { if (!m_phonemes.contains(phoneme)) { m_phonemes.append(phoneme); emit phonemesChanged(); //FIXME tell Unit to also send corresponding signal! } } void Phrase::removePhoneme(Phoneme *phoneme) { if (m_phonemes.removeOne(phoneme)) { emit phonemesChanged(); //FIXME tell Unit to also send corresponding signal! } } diff --git a/src/core/phrase.h b/src/core/phrase.h index 1d9156d..def458a 100644 --- a/src/core/phrase.h +++ b/src/core/phrase.h @@ -1,139 +1,139 @@ /* * Copyright 2013-2014 Andreas Cord-Landwehr * * 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 . */ #ifndef PHRASE_H #define PHRASE_H #include "artikulatecore_export.h" #include #include #include #include class QString; class Phoneme; class Unit; class QUrl; class ARTIKULATECORE_EXPORT Phrase : public QObject { Q_OBJECT Q_PROPERTY(QString id READ id WRITE setId NOTIFY idChanged) Q_PROPERTY(QString text READ text WRITE setText NOTIFY textChanged) Q_PROPERTY(QString i18nText READ i18nText WRITE seti18nText NOTIFY i18nTextChanged) Q_PROPERTY(QString soundFileUrl READ soundFileUrl NOTIFY soundChanged) Q_PROPERTY(Phrase::Type type READ type WRITE setType NOTIFY typeChanged) Q_PROPERTY(Phrase::EditState editState READ editState WRITE setEditState NOTIFY editStateChanged) Q_PROPERTY(Unit *unit READ unit NOTIFY unitChanged) Q_PROPERTY(bool excluded READ isExcluded NOTIFY excludedChanged) Q_PROPERTY(int progress READ progress NOTIFY progressChanged) public: Q_ENUMS(EditState) Q_ENUMS(TrainingState) Q_ENUMS(Type) enum EditState { Unknown, Translated, Completed }; enum TrainingState { //TODO not needed anymore with statistics Trained, Untrained }; enum class Progress { Skip, Done }; enum Type { Word, Expression, Sentence, Paragraph, AllTypes }; explicit Phrase(QObject *parent = nullptr); ~Phrase(); QString id() const; void setId(const QString &id); QString foreignId() const; void setForeignId(const QString &id); QString text() const; void setText(const QString &text); QString i18nText() const; void seti18nText(const QString &text); Unit * unit() const; void setUnit(Unit *unit); Phrase::Type type() const; QString typeString() const; void setType(Phrase::Type type); void setType(const QString &typeString); QString soundFileUrl() const; Q_INVOKABLE QString soundFileOutputPath() const; Q_INVOKABLE void setSoundFileUrl(); Phrase::EditState editState() const; QString editStateString() const; void setEditState(Phrase::EditState state); void setEditState(const QString &stateString); QUrl sound() const; void setSound(const QUrl &soundFile); QList phonemes() const; bool isExcluded() const; void setExcluded(bool excluded = false); int progress() const; void setProgress(int value); void updateProgress(Phrase::Progress progress); Q_INVOKABLE bool hasPhoneme(Phoneme *phoneme); Q_INVOKABLE void addPhoneme(Phoneme *phoneme); Q_INVOKABLE void removePhoneme(Phoneme *phoneme); Q_SIGNALS: void idChanged(); void unitChanged(); void textChanged(); void i18nTextChanged(); void typeChanged(); void editStateChanged(); void soundChanged(); void excludedChanged(); void phonemesChanged(); void modified(); void progressChanged(); private: Q_DISABLE_COPY(Phrase) QString m_id; QString m_foreignId; QString m_text; QString m_i18nText; Type m_type; EditState m_editState; Unit *m_unit; unsigned m_trainingProgress; - int m_skipCounter; // count how many skips occured since last progress update + int m_skipCounter; // count how many skips occurred since last progress update bool m_excludedFromUnit; QList m_phonemes; QUrl m_nativeSoundFile; }; #endif // PHRASE_H diff --git a/src/core/player.cpp b/src/core/player.cpp index cfc85fd..c231356 100644 --- a/src/core/player.cpp +++ b/src/core/player.cpp @@ -1,105 +1,105 @@ /* * Copyright 2013 Andreas Cord-Landwehr * * 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 "player.h" #include "libsound/src/outputdevicecontroller.h" #include #include "artikulate_debug.h" #include #include Player::Player(QObject *parent) : QObject(parent) , m_soundFile(QString()) , m_playbackState(StoppedState) { } Player::~Player() { // nothing to do } void Player::setSoundFile(const QUrl &fileUrl) { if (!fileUrl.isValid() || fileUrl.isEmpty()) { qCWarning(ARTIKULATE_LOG) << "Not setting empty sound file path."; return; } m_soundFile = fileUrl; emit soundFileChanged(); } void Player::setSoundFile(const QString& fileUrl) { OutputDeviceController::self().stop(); setSoundFile(QUrl::fromLocalFile(fileUrl)); } QString Player::soundFile() const { return m_soundFile.toLocalFile(); } Player::PlaybackState Player::state() const { return m_playbackState; } void Player::playback() { OutputDeviceController::self().disconnect(); if (m_soundFile.isEmpty()) { qCritical() << "Abort playing sound, no file available"; return; } qCDebug(ARTIKULATE_LOG) << this << "Playback sound in file "<< m_soundFile.toLocalFile(); OutputDeviceController::self().play(QUrl::fromLocalFile(m_soundFile.toLocalFile())); m_playbackState = PlayingState; - connect(&OutputDeviceController::self(), SIGNAL(started()), this, SLOT(updateState())); - connect(&OutputDeviceController::self(), SIGNAL(stopped()), this, SLOT(updateState())); + connect(&OutputDeviceController::self(), &OutputDeviceController::started, this, &Player::updateState); + connect(&OutputDeviceController::self(), &OutputDeviceController::stopped, this, &Player::updateState); emit stateChanged(); } void Player::stop() { OutputDeviceController::self().stop(); OutputDeviceController::self().disconnect(); m_playbackState = StoppedState; emit stateChanged(); } void Player::updateState() { if (OutputDeviceController::self().state() == OutputDeviceController::StoppedState && state() == PlayingState ) { m_playbackState = StoppedState; emit stateChanged(); } if (OutputDeviceController::self().state() == OutputDeviceController::PlayingState && state() != PlayingState ) { m_playbackState = PlayingState; emit stateChanged(); } } diff --git a/src/core/recorder.cpp b/src/core/recorder.cpp index c5daabf..a590d5c 100644 --- a/src/core/recorder.cpp +++ b/src/core/recorder.cpp @@ -1,101 +1,101 @@ /* * Copyright 2013 Andreas Cord-Landwehr * * 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 "recorder.h" #include "libsound/src/capturedevicecontroller.h" #include #include "artikulate_debug.h" #include #include #include Recorder::Recorder(QObject *parent) : QObject(parent) , m_state(StoppedState) - , m_recordingBufferFile(QDir::tempPath() + QLatin1String("/XXXXXX.ogg")) + , m_recordingBufferFile(QDir::tempPath() + QStringLiteral("/XXXXXX.ogg")) { } Recorder::~Recorder() { // clear resources m_recordingBufferFile.close(); } Recorder::CaptureState Recorder::state() const { return m_state; } void Recorder::startCapture() { if (CaptureDeviceController::self().state() == CaptureDeviceController::RecordingState) { qCWarning(ARTIKULATE_LOG) << "Stopped capture before starting new capture, since was still active."; CaptureDeviceController::self().stopCapture(); } m_recordingBufferFile.open(); qCDebug(ARTIKULATE_LOG) << "Start recording to temporary file " << m_recordingBufferFile.fileName(); CaptureDeviceController::self().startCapture(m_recordingBufferFile.fileName()); m_state = RecordingState; emit stateChanged(); } void Recorder::stop() { CaptureDeviceController::self().stopCapture(); m_state = StoppedState; emit stateChanged(); emit recordingFileChanged(); } QString Recorder::recordingFile() const { if (!m_recordingBufferFile.isOpen()) { return QString(); } return m_recordingBufferFile.fileName(); } void Recorder::storeToFile(const QString &path) { if (m_recordingBufferFile.isOpen()) { QFile targetFile; targetFile.setFileName(path); if (!targetFile.exists() || targetFile.remove()) { m_recordingBufferFile.copy(path); m_recordingBufferFile.close(); emit recordingFileChanged(); } else { qCritical() << "Could not save buffered sound data to file, aborting."; } } else { qCritical() << "No buffer present."; } } void Recorder::clearBuffer() { if (m_recordingBufferFile.isOpen()) { m_recordingBufferFile.close(); emit recordingFileChanged(); } } diff --git a/src/core/resourcemanager.cpp b/src/core/resourcemanager.cpp index e4ffca3..3118d8d 100644 --- a/src/core/resourcemanager.cpp +++ b/src/core/resourcemanager.cpp @@ -1,473 +1,475 @@ /* * Copyright 2013-2015 Andreas Cord-Landwehr * * 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 "resourcemanager.h" #include "language.h" #include "course.h" #include "skeleton.h" #include "unit.h" #include "phrase.h" #include "phoneme.h" #include "phonemegroup.h" #include "resources/languageresource.h" #include "resources/courseresource.h" #include "resources/skeletonresource.h" #include "settings.h" #include "liblearnerprofile/src/profilemanager.h" #include "liblearnerprofile/src/learninggoal.h" #include #include #include #include #include #include #include #include #include #include "artikulate_debug.h" #include #include ResourceManager::ResourceManager(QObject *parent) : QObject(parent) { } void ResourceManager::loadCourseResources() { //TODO fix this method such that it may be called many times of e.g. updating // reload config, could be changed in dialogs Settings::self()->load(); // register skeleton resources QDir skeletonRepository = QDir(Settings::courseRepositoryPath()); skeletonRepository.setFilter(QDir::Files | QDir::Hidden); - if (!skeletonRepository.cd("skeletons")) { + if (!skeletonRepository.cd(QStringLiteral("skeletons"))) { qCritical() << "There is no subdirectory \"skeletons\" in directory " << skeletonRepository.path() << " cannot load skeletons."; } else { // read skeletons QFileInfoList list = skeletonRepository.entryInfoList(); for (int i = 0; i < list.size(); ++i) { QFileInfo fileInfo = list.at(i); addSkeleton(QUrl::fromLocalFile(fileInfo.absoluteFilePath())); } } // register contributor course files QDir courseRepository = QDir(Settings::courseRepositoryPath()); - if (!courseRepository.cd("courses")) { + if (!courseRepository.cd(QStringLiteral("courses"))) { qCritical() << "There is no subdirectory \"courses\" in directory " << courseRepository.path() << " cannot load courses."; } else { // find courses courseRepository.setFilter(QDir::Dirs | QDir::NoDotAndDotDot); QFileInfoList courseDirList = courseRepository.entryInfoList(); // traverse all course directories foreach (const QFileInfo &info, courseDirList) { QDir courseDir = QDir(info.absoluteFilePath()); courseDir.setFilter(QDir::Dirs | QDir::NoDotAndDotDot); QFileInfoList courseLangDirList = courseDir.entryInfoList(); // traverse all language directories for each course foreach (const QFileInfo &langInfo, courseLangDirList) { QString languageId = langInfo.fileName(); QDir courseLangDir = QDir(langInfo.absoluteFilePath()); courseLangDir.setFilter(QDir::Files); QStringList nameFilters; - nameFilters.append("*.xml"); + nameFilters.append(QStringLiteral("*.xml")); QFileInfoList courses = courseLangDir.entryInfoList(nameFilters); // find and add course files foreach (const QFileInfo &courseInfo, courses) { CourseResource * course = addCourse(QUrl::fromLocalFile(courseInfo.filePath())); if (course != nullptr) { course->setContributorResource(true); } } } } } // register GHNS course resources QStringList dirs = QStandardPaths::standardLocations(QStandardPaths::DataLocation); foreach (const QString &testdir, dirs) { QDirIterator it(testdir + "/courses/", QDirIterator::Subdirectories); while (it.hasNext()) { QDir dir(it.next()); dir.setFilter(QDir::Files | QDir::NoSymLinks); QFileInfoList list = dir.entryInfoList(); for (int i = 0; i < list.size(); ++i) { QFileInfo fileInfo = list.at(i); - if (fileInfo.completeSuffix() != "xml") { + if (fileInfo.completeSuffix() != QLatin1String("xml")) { continue; } addCourse(QUrl::fromLocalFile(fileInfo.absoluteFilePath())); } } } //TODO this signal should only be emitted when repository was added/removed // yet the call to this method is very seldom and emitting it too often is not that harmful emit repositoryChanged(); } void ResourceManager::loadLanguageResources() { // load language resources // all other resources are only loaded on demand QStringList dirs = QStandardPaths::standardLocations(QStandardPaths::GenericDataLocation); foreach (const QString &testdir, dirs) { QDir dir(testdir + "/artikulate/languages/"); dir.setFilter(QDir::Files | QDir::NoSymLinks); QFileInfoList list = dir.entryInfoList(); for (int i = 0; i < list.size(); ++i) { QFileInfo fileInfo = list.at(i); - if (fileInfo.completeSuffix() != "xml") { + if (fileInfo.completeSuffix() != QLatin1String("xml")) { continue; } addLanguage(QUrl::fromLocalFile(fileInfo.absoluteFilePath())); } } } void ResourceManager::sync() { QMap< QString, QList< CourseResource* > >::iterator iter; for (iter = m_courseResources.begin(); iter != m_courseResources.end(); ++iter) { foreach (auto const &courseRes, iter.value()) { courseRes->sync(); } } foreach (auto const &courseRes, m_skeletonResources) { courseRes->sync(); } } bool ResourceManager::modified() const { QMap< QString, QList< CourseResource* > >::const_iterator iter; for (iter = m_courseResources.constBegin(); iter != m_courseResources.constEnd(); ++iter) { foreach (auto const &courseRes, iter.value()) { if (courseRes->isOpen() && courseRes->course()->modified()) { return true; } } } foreach (auto const &courseRes, m_skeletonResources) { if (courseRes->isOpen() && courseRes->skeleton()->modified()) { return true; } } return false; } void ResourceManager::addLanguage(const QUrl &languageFile) { if (m_loadedResources.contains(languageFile.toLocalFile())) { return; } LanguageResource *resource = new LanguageResource(this, languageFile); emit languageResourceAboutToBeAdded(resource, m_languageResources.count()); m_languageResources.append(resource); m_loadedResources.append(languageFile.toLocalFile()); m_courseResources.insert(resource->identifier(), QList()); emit languageResourceAdded(); } bool ResourceManager::isRepositoryManager() const { return !Settings::courseRepositoryPath().isEmpty(); } QString ResourceManager::repositoryUrl() const { return Settings::courseRepositoryPath(); } QList< LanguageResource* > ResourceManager::languageResources() const { return m_languageResources; } Language * ResourceManager::language(int index) const { Q_ASSERT(index >= 0 && index < m_languageResources.count()); return m_languageResources.at(index)->language(); } Language * ResourceManager::language(LearnerProfile::LearningGoal *learningGoal) const { if (!learningGoal) { return nullptr; } if (learningGoal->category() != LearnerProfile::LearningGoal::Language) { qCritical() << "Cannot translate non-language learning goal to language"; return nullptr; } foreach (LanguageResource *resource, m_languageResources) { if (resource->identifier() == learningGoal->identifier()) { return resource->language(); } } qCritical() << "No language registered with identifier " << learningGoal->identifier() << ": aborting"; return nullptr; } QList< CourseResource* > ResourceManager::courseResources(Language *language) { if (!language) { QList courses; for (auto iter = m_courseResources.constBegin(); iter != m_courseResources.constEnd(); ++iter) { courses.append(iter.value()); } return courses; } // return empty list if no course available for language if (!m_courseResources.contains(language->id())) { return QList< CourseResource* >(); } return m_courseResources[language->id()]; } Course * ResourceManager::course(Language *language, int index) const { Q_ASSERT(m_courseResources.contains(language->id())); Q_ASSERT(index >= 0 && index < m_courseResources[language->id()].count()); return m_courseResources[language->id()].at(index)->course(); } void ResourceManager::reloadCourseOrSkeleton(Course *courseOrSkeleton) { if (!courseOrSkeleton) { qCritical() << "Cannot reload non-existing course"; return; } if (!courseOrSkeleton->file().isValid()) { qCritical() << "Cannot reload temporary file, aborting."; return; } // figure out if this is a course or a skeleton if (courseOrSkeleton->language()) { // only course files have a language //TODO better add a check if this is contained in the course list // to catch possible errors QUrl file = courseOrSkeleton->file(); m_loadedResources.removeOne(courseOrSkeleton->file().toLocalFile()); removeCourse(courseOrSkeleton); addCourse(file); } else { foreach (SkeletonResource *resource, m_skeletonResources) { if (resource->identifier() == courseOrSkeleton->id()) { resource->reload(); return; } } } } void ResourceManager::updateCourseFromSkeleton(Course *course) { //TODO implement status information that are shown at mainwindow if (course->foreignId().isEmpty()) { qCritical() << "No skeleton ID specified, aborting update."; return; } Course *skeleton = nullptr; QList::ConstIterator iter = m_skeletonResources.constBegin(); while (iter != m_skeletonResources.constEnd()) { if ((*iter)->identifier() == course->foreignId()) { skeleton = (*iter)->skeleton(); break; } ++iter; } if (!skeleton) { qCritical() << "Could not find skeleton with id " << course->foreignId() << ", aborting update."; return; } // update now foreach (Unit *unitSkeleton, skeleton->unitList()) { // import unit if not exists Unit *currentUnit = nullptr; bool found = false; foreach (Unit *unit, course->unitList()) { if (unit->foreignId() == unitSkeleton->id()) { found = true; currentUnit = unit; break; } } if (found == false) { currentUnit = new Unit(course); currentUnit->setId(QUuid::createUuid().toString()); currentUnit->setTitle(unitSkeleton->title()); currentUnit->setForeignId(unitSkeleton->id()); currentUnit->setCourse(course); course->addUnit(currentUnit); course->setModified(true); } // update phrases foreach (Phrase *phraseSkeleton, unitSkeleton->phraseList()) { bool found = false; foreach (Phrase *phrase, currentUnit->phraseList()) { if (phrase->foreignId() == phraseSkeleton->id()) { if (phrase->i18nText() != phraseSkeleton->text()) { phrase->setEditState(Phrase::Unknown); phrase->seti18nText(phraseSkeleton->text()); } found = true; break; } } if (found == false) { Phrase *newPhrase = new Phrase(course); newPhrase->setForeignId(phraseSkeleton->id()); newPhrase->setId(QUuid::createUuid().toString()); newPhrase->setText(phraseSkeleton->text()); newPhrase->seti18nText(phraseSkeleton->text()); newPhrase->setType(phraseSkeleton->type()); newPhrase->setUnit(currentUnit); currentUnit->addPhrase(newPhrase); course->setModified(true); } } } // FIXME deassociate removed phrases qCDebug(ARTIKULATE_LOG) << "Update performed!"; } CourseResource * ResourceManager::addCourse(const QUrl &courseFile) { CourseResource *resource = new CourseResource(this, courseFile); if (resource->language().isEmpty()) { + delete resource; qCritical() << "Could not load course, language unknown:" << courseFile.toLocalFile(); return nullptr; } // skip already loaded resources if (m_loadedResources.contains(courseFile.toLocalFile())) { + delete resource; return nullptr; } m_loadedResources.append(courseFile.toLocalFile()); addCourseResource(resource); emit languageCoursesChanged(); return resource; } void ResourceManager::addCourseResource(CourseResource *resource) { Q_ASSERT(m_courseResources.contains(resource->language())); if (m_courseResources.contains(resource->language())) { emit courseResourceAboutToBeAdded(resource, m_courseResources[resource->language()].count()); } else { emit courseResourceAboutToBeAdded(resource, 0); m_courseResources.insert(resource->language(), QList()); } m_courseResources[resource->language()].append(resource); emit courseResourceAdded(); } void ResourceManager::removeCourse(Course *course) { for (int index = 0; index < m_courseResources[course->language()->id()].length(); ++index) { if (m_courseResources[course->language()->id()].at(index)->course() == course) { emit courseResourceAboutToBeRemoved(index); m_courseResources[course->language()->id()].removeAt(index); course->deleteLater(); return; } } } Course * ResourceManager::createCourse(Language *language, Skeleton *skeleton) { // set path - QString path = QString("%1/%2/%3/%4/%4.xml") + QString path = QStringLiteral("%1/%2/%3/%4/%4.xml") .arg(Settings::courseRepositoryPath()) - .arg("courses") + .arg(QStringLiteral("courses")) .arg(skeleton->id()) .arg(language->id()); CourseResource * courseRes = new CourseResource(this, QUrl::fromLocalFile(path)); Q_ASSERT(courseRes); Course *course = courseRes->course(); Q_ASSERT(course); course->setId(QUuid::createUuid().toString()); course->setTitle(skeleton->title()); course->setDescription(skeleton->description()); course->setFile(QUrl::fromLocalFile(path)); course->setLanguage(language); // set skeleton course->setForeignId(skeleton->id()); addCourseResource(courseRes); return course; } void ResourceManager::addSkeleton(const QUrl &skeletonFile) { SkeletonResource *resource = new SkeletonResource(this, skeletonFile); addSkeletonResource(resource); } void ResourceManager::addSkeletonResource(SkeletonResource *resource) { // skip already loaded resources if (m_loadedResources.contains(resource->path().toLocalFile())) { return; } m_loadedResources.append(resource->path().toLocalFile()); emit skeletonAboutToBeAdded(resource->skeleton(), m_skeletonResources.count()); m_skeletonResources.append(resource); emit skeletonAdded(); } void ResourceManager::removeSkeleton(Skeleton *skeleton) { for (int index = 0; index < m_skeletonResources.length(); ++index) { if (m_skeletonResources.at(index)->identifier() == skeleton->id()) { emit skeletonAboutToBeRemoved(index, index); m_skeletonResources.removeAt(index); emit skeletonRemoved(); skeleton->deleteLater(); return; } } } QList< SkeletonResource* > ResourceManager::skeletonResources() { return m_skeletonResources; } diff --git a/src/core/resourcemanager.h b/src/core/resourcemanager.h index 53e1f36..f37799c 100644 --- a/src/core/resourcemanager.h +++ b/src/core/resourcemanager.h @@ -1,216 +1,216 @@ /* * Copyright 2013-2015 Andreas Cord-Landwehr * * 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 . */ #ifndef RESOURCEMANAGER_H #define RESOURCEMANAGER_H #include "artikulatecore_export.h" #include #include #include #include #include "liblearnerprofile/src/learninggoal.h" class SkeletonResource; class CourseResource; class LanguageResource; class Skeleton; class Language; class Course; class ProfileManager; class QUrl; namespace LearnerProfile { class ProfileManager; } /** * \class ResourceManager * This class loads and stores all data files of the application. */ class ARTIKULATECORE_EXPORT ResourceManager : public QObject { Q_OBJECT Q_PROPERTY(bool isRepositoryManager READ isRepositoryManager NOTIFY repositoryChanged) Q_PROPERTY(QString repositoryUrl READ repositoryUrl NOTIFY repositoryChanged) public: explicit ResourceManager(QObject *parent = nullptr); /** * Load all course resources. * This loading is very fast, since course files are only partly (~20 top lines) parsed and - * the complete parsing is postproned until first access. + * the complete parsing is postponed until first access. * * This method is safe to be called several times for incremental updates. */ Q_INVOKABLE void loadCourseResources(); /** * This method loads all language files that are provided in the standard directories * for this application. */ void loadLanguageResources(); /** * save all changes to course resources */ void sync(); /** * \return \c true if any course or skeleton is modified, otherwise \c false */ bool modified() const; /** * \return \c true if a repository is used, else \c false */ Q_INVOKABLE bool isRepositoryManager() const; /** * \return path to working repository, if one is set */ QString repositoryUrl() const; /** * \return list of all available language specifications */ QList languageResources() const; /** * \return language by \p index */ Q_INVOKABLE Language * language(int index) const; /** * \return language by \p learningGoal */ Q_INVOKABLE Language * language(LearnerProfile::LearningGoal* learningGoal) const; /** * \return list of all loaded courses for language \p language */ QList courseResources(Language *language); Q_INVOKABLE Course * course(Language *language, int index) const; /** * Reset the file for this course or skeleton. * * \param course the course to be reloaded */ Q_INVOKABLE void reloadCourseOrSkeleton(Course *course); /** * Imports units and phrases from skeleton, deassociates removed ones. * * \param course the course to be update */ void updateCourseFromSkeleton(Course *course); /** * Add language to resource manager by parsing the given language specification file. * * \param languageFile is the local XML file containing the language */ void addLanguage(const QUrl &languageFile); /** * Adds course to resource manager by parsing the given course specification file. * * \param courseFile is the local XML file containing the course * \return true if loaded successfully, otherwise false */ CourseResource * addCourse(const QUrl &courseFile); /** * Adds course to resource manager. If the course's language is not registered, the language * is registered by this method. * * \param resource the course resource to add to resource manager */ void addCourseResource(CourseResource *resource); /** * Remove course from resource manager. If the course is modified its changes are NOT * written. For writing changes, the Course::sync() method must be called directly. * * \param course is the course to be removed */ void removeCourse(Course *course); /** * Create new course for \p language and derived from \p skeleton. * * \return created course */ Q_INVOKABLE Course * createCourse(Language *language, Skeleton *skeleton); /** * Adds skeleton resource to resource manager * * \param resource the skeleton resource to add to resource manager */ void addSkeleton(const QUrl &skeletonFile); /** * Adds skeleton resource to resource manager * * \param resource the skeleton resource to add to resource manager */ void addSkeletonResource(SkeletonResource *resource); /** * Remove skeleton from resource manager. If the skeleton is modified its changes are NOT * written. For writing changes, the Skeleton::sync() method must be called directly. * * \param skeleton is the skeleton to be removed */ void removeSkeleton(Skeleton *skeleton); /** * \return list of all loaded skeletons resources */ QList skeletonResources(); Q_SIGNALS: void languageResourceAdded(); void languageResourceAboutToBeAdded(LanguageResource*,int); void languageResourceRemoved(); void languageResourceAboutToBeRemoved(int); void repositoryChanged(); void courseResourceAdded(); void courseResourceAboutToBeAdded(CourseResource*,int); void courseResourceAboutToBeRemoved(int); void skeletonAdded(); void skeletonAboutToBeAdded(Course*,int); void skeletonRemoved(); void skeletonAboutToBeRemoved(int,int); void languageCoursesChanged(); private: QList m_languageResources; QMap > m_courseResources; //!> (language-id, course-resource) QList m_skeletonResources; QStringList m_loadedResources; }; #endif // RESOURCEMANAGER_H diff --git a/src/core/resources/courseresource.cpp b/src/core/resources/courseresource.cpp index edf907e..ad26f4e 100644 --- a/src/core/resources/courseresource.cpp +++ b/src/core/resources/courseresource.cpp @@ -1,488 +1,488 @@ /* * Copyright 2013-2015 Andreas Cord-Landwehr * Copyright 2013 Oindrila Gupta * * 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 "courseresource.h" #include "core/resourcemanager.h" #include "core/language.h" #include "core/course.h" #include "core/unit.h" #include "core/phoneme.h" #include "core/phonemegroup.h" #include "core/resources/languageresource.h" #include #include #include #include #include #include #include #include #include "artikulate_debug.h" class CourseResourcePrivate { public: CourseResourcePrivate(ResourceManager *resourceManager) : m_resourceManager(resourceManager) , m_type(ResourceInterface::CourseResourceType) , m_courseResource(nullptr) { } ~CourseResourcePrivate() { } ResourceManager *m_resourceManager; QUrl m_path; ResourceInterface::Type m_type; QString m_identifier; QString m_title; QString m_language; QString m_i18nTitle; Course *m_courseResource; }; CourseResource::CourseResource(ResourceManager *resourceManager, const QUrl &path) : ResourceInterface(resourceManager) , d(new CourseResourcePrivate(resourceManager)) { d->m_path = path; // load basic information from language file, but does not parse everything QXmlStreamReader xml; QFile file(path.toLocalFile()); if (file.open(QIODevice::ReadOnly)) { xml.setDevice(&file); xml.readNextStartElement(); while (xml.readNext() && !xml.atEnd()) { if (xml.name() == "id") { d->m_identifier = xml.readElementText(); continue; } if (xml.name() == "title") { d->m_title = xml.readElementText(); d->m_i18nTitle = d->m_title; continue; } //TODO i18nTitle must be implemented, currently missing and hence not parsed if (xml.name() == "language") { d->m_language = xml.readElementText(); continue; } // quit reading when basic elements are read if (!d->m_identifier.isEmpty() && !d->m_title.isEmpty() && !d->m_i18nTitle.isEmpty() && !d->m_language.isEmpty() ) { break; } } if (xml.hasError()) { qCritical() << "Error occurred when reading Course XML file:" << path.toLocalFile(); } } xml.clear(); file.close(); } CourseResource::~CourseResource() { } QString CourseResource::identifier() { if (d->m_courseResource) { return d->m_courseResource->id(); } return d->m_identifier; } QString CourseResource::title() { if (d->m_courseResource) { return d->m_courseResource->title(); } return d->m_title; } QString CourseResource::i18nTitle() { if (d->m_courseResource) { return d->m_courseResource->title(); //TODO } return d->m_i18nTitle; } QString CourseResource::language() const { if (d->m_courseResource) { return d->m_courseResource->language()->id(); } return d->m_language; } ResourceInterface::Type CourseResource::type() const { return d->m_type; } void CourseResource::sync() { Q_ASSERT(path().isValid()); Q_ASSERT(path().isLocalFile()); Q_ASSERT(!path().isEmpty()); // if resource was never loaded, it cannot be changed if (d->m_courseResource == nullptr) { qCDebug(ARTIKULATE_LOG) << "Aborting sync, course was not parsed."; return; } //TODO // // not writing back if not modified // if (!d->m_courseResource->modified()) { // qCDebug(ARTIKULATE_LOG) << "Aborting sync, course was not modified."; // return; // } // write back to file // create directories if necessary QFileInfo info(path().adjusted(QUrl::RemoveFilename|QUrl::StripTrailingSlash).path()); if (!info.exists()) { qCDebug(ARTIKULATE_LOG) << "create xml output file directory, not existing"; QDir dir; dir.mkpath(path().adjusted(QUrl::RemoveFilename|QUrl::StripTrailingSlash).path()); } //TODO port to KSaveFile QFile file(path().toLocalFile()); if (!file.open(QIODevice::WriteOnly)) { qCWarning(ARTIKULATE_LOG) << "Unable to open file " << file.fileName() << " in write mode, aborting."; return; } file.write(serializedDocument().toByteArray()); return; } void CourseResource::exportGhns(const QString &path) { // ensure that course is loaded before exporting it Course *course = CourseResource::course(); // filename const QString fileName = identifier() + ".tar.bz2"; - KTar tar = KTar(path + '/' + fileName, QString("application/x-bzip")); + KTar tar = KTar(path + '/' + fileName, QStringLiteral("application/x-bzip")); if (!tar.open(QIODevice::WriteOnly)) { qCWarning(ARTIKULATE_LOG) << "Unable to open tar file" << path + '/' + fileName << "in write mode, aborting."; return; } foreach (auto *unit, course->unitList()) { foreach (auto *phrase, unit->phraseList()) { if (QFile::exists(phrase->soundFileUrl())) { tar.addLocalFile(phrase->soundFileUrl(), phrase->id() + ".ogg"); } } } tar.writeFile(identifier() + ".xml", serializedDocument(true).toByteArray()); tar.close(); } void CourseResource::close() { d->m_courseResource->deleteLater(); d->m_courseResource = nullptr; } bool CourseResource::isOpen() const { return (d->m_courseResource != nullptr); } QUrl CourseResource::path() const { if (d->m_courseResource) { return d->m_courseResource->file(); } return d->m_path; } QObject * CourseResource::resource() { if (d->m_courseResource != nullptr) { return d->m_courseResource; } // if file does not exist, create new course QFileInfo info(d->m_path.toLocalFile()); if (!info.exists()) { d->m_courseResource = new Course(this); d->m_courseResource->setId(d->m_identifier); d->m_courseResource->setTitle(d->m_title); return d->m_courseResource; } // load existing file - QXmlSchema schema = loadXmlSchema("course"); + QXmlSchema schema = loadXmlSchema(QStringLiteral("course")); if (!schema.isValid()) { return nullptr; } QDomDocument document = loadDomDocument(path(), schema); if (document.isNull()) { qCWarning(ARTIKULATE_LOG) << "Could not parse document " << path().toLocalFile() << ", aborting."; return nullptr; } // create course QDomElement root(document.documentElement()); d->m_courseResource = new Course(this); d->m_courseResource->setFile(d->m_path); - d->m_courseResource->setId(root.firstChildElement("id").text()); - d->m_courseResource->setTitle(root.firstChildElement("title").text()); - d->m_courseResource->setDescription(root.firstChildElement("description").text()); - if (!root.firstChildElement("foreignId").isNull()) { - d->m_courseResource->setForeignId(root.firstChildElement("foreignId").text()); + d->m_courseResource->setId(root.firstChildElement(QStringLiteral("id")).text()); + d->m_courseResource->setTitle(root.firstChildElement(QStringLiteral("title")).text()); + d->m_courseResource->setDescription(root.firstChildElement(QStringLiteral("description")).text()); + if (!root.firstChildElement(QStringLiteral("foreignId")).isNull()) { + d->m_courseResource->setForeignId(root.firstChildElement(QStringLiteral("foreignId")).text()); } // set language //TODO not efficient to load completely every language for this comparison - QString language = root.firstChildElement("language").text(); + QString language = root.firstChildElement(QStringLiteral("language")).text(); foreach(LanguageResource * resource, d->m_resourceManager->languageResources()) { if (resource->language()->id() == language) { d->m_courseResource->setLanguage(resource->language()); break; } } if (d->m_courseResource->language() == nullptr) { qCWarning(ARTIKULATE_LOG) << "Language ID" << language << "unknown, could not register any language, aborting"; return nullptr; } // create units - for (QDomElement unitNode = root.firstChildElement("units").firstChildElement(); + for (QDomElement unitNode = root.firstChildElement(QStringLiteral("units")).firstChildElement(); !unitNode.isNull(); unitNode = unitNode.nextSiblingElement()) { Unit *unit = new Unit(d->m_courseResource); - unit->setId(unitNode.firstChildElement("id").text()); + unit->setId(unitNode.firstChildElement(QStringLiteral("id")).text()); unit->setCourse(d->m_courseResource); - unit->setTitle(unitNode.firstChildElement("title").text()); - if (!unitNode.firstChildElement("foreignId").isNull()) { - unit->setForeignId(unitNode.firstChildElement("foreignId").text()); + unit->setTitle(unitNode.firstChildElement(QStringLiteral("title")).text()); + if (!unitNode.firstChildElement(QStringLiteral("foreignId")).isNull()) { + unit->setForeignId(unitNode.firstChildElement(QStringLiteral("foreignId")).text()); } d->m_courseResource->addUnit(unit); // create phrases - for (QDomElement phraseNode = unitNode.firstChildElement("phrases").firstChildElement(); + for (QDomElement phraseNode = unitNode.firstChildElement(QStringLiteral("phrases")).firstChildElement(); !phraseNode.isNull(); phraseNode = phraseNode.nextSiblingElement()) { unit->addPhrase(parsePhrase(phraseNode, unit)); // add to unit at last step to produce only one signal //FIXME phrase does not cause unit signals that phonemes list is changed } } d->m_courseResource->setModified(false); return d->m_courseResource; } Course * CourseResource::course() { return qobject_cast(resource()); } Phrase* CourseResource::parsePhrase(QDomElement phraseNode, Unit* parentUnit) const { Phrase *phrase = new Phrase(parentUnit); - phrase->setId(phraseNode.firstChildElement("id").text()); - phrase->setText(phraseNode.firstChildElement("text").text()); - phrase->seti18nText(phraseNode.firstChildElement("i18nText").text()); + phrase->setId(phraseNode.firstChildElement(QStringLiteral("id")).text()); + phrase->setText(phraseNode.firstChildElement(QStringLiteral("text")).text()); + phrase->seti18nText(phraseNode.firstChildElement(QStringLiteral("i18nText")).text()); phrase->setUnit(parentUnit); - if (!phraseNode.firstChildElement("soundFile").text().isEmpty()) { + if (!phraseNode.firstChildElement(QStringLiteral("soundFile")).text().isEmpty()) { phrase->setSound(QUrl::fromLocalFile( path().adjusted(QUrl::RemoveFilename|QUrl::StripTrailingSlash).path() - + '/' + phraseNode.firstChildElement("soundFile").text()) + + '/' + phraseNode.firstChildElement(QStringLiteral("soundFile")).text()) ); } - phrase->setType(phraseNode.firstChildElement("type").text()); - phrase->setEditState(phraseNode.firstChildElement("editState").text()); - if (!phraseNode.firstChildElement("foreignId").isNull()) { - phrase->setForeignId(phraseNode.firstChildElement("foreignId").text()); + phrase->setType(phraseNode.firstChildElement(QStringLiteral("type")).text()); + phrase->setEditState(phraseNode.firstChildElement(QStringLiteral("editState")).text()); + if (!phraseNode.firstChildElement(QStringLiteral("foreignId")).isNull()) { + phrase->setForeignId(phraseNode.firstChildElement(QStringLiteral("foreignId")).text()); } // add phonemes QList phonemes = d->m_courseResource->language()->phonemes(); - for (QDomElement phonemeID = phraseNode.firstChildElement("phonemes").firstChildElement(); + for (QDomElement phonemeID = phraseNode.firstChildElement(QStringLiteral("phonemes")).firstChildElement(); !phonemeID.isNull(); phonemeID = phonemeID.nextSiblingElement()) { QString id = phonemeID.text(); if (id.isEmpty()) { qCritical() << "Phoneme ID string is empty for phrase "<< phrase->id() <<", aborting."; continue; } foreach (Phoneme *phoneme, phonemes) { if (phoneme->id() == id) { phrase->addPhoneme(phoneme); break; } } } - if (!phraseNode.firstChildElement("excluded").isNull() && - phraseNode.firstChildElement("excluded").text() == "true") + if (!phraseNode.firstChildElement(QStringLiteral("excluded")).isNull() && + phraseNode.firstChildElement(QStringLiteral("excluded")).text() == QLatin1String("true")) { phrase->setExcluded(true); } return phrase; } QDomDocument CourseResource::serializedDocument(bool trainingExport) const { QDomDocument document; // prepare xml header - QDomProcessingInstruction header = document.createProcessingInstruction("xml", "version=\"1.0\""); + QDomProcessingInstruction header = document.createProcessingInstruction(QStringLiteral("xml"), QStringLiteral("version=\"1.0\"")); document.appendChild(header); // create main element - QDomElement root = document.createElement("course"); + QDomElement root = document.createElement(QStringLiteral("course")); document.appendChild(root); - QDomElement idElement = document.createElement("id"); - QDomElement titleElement = document.createElement("title"); - QDomElement descriptionElement = document.createElement("description"); - QDomElement languageElement = document.createElement("language"); + QDomElement idElement = document.createElement(QStringLiteral("id")); + QDomElement titleElement = document.createElement(QStringLiteral("title")); + QDomElement descriptionElement = document.createElement(QStringLiteral("description")); + QDomElement languageElement = document.createElement(QStringLiteral("language")); idElement.appendChild(document.createTextNode(d->m_courseResource->id())); titleElement.appendChild(document.createTextNode(d->m_courseResource->title())); descriptionElement.appendChild(document.createTextNode(d->m_courseResource->description())); languageElement.appendChild(document.createTextNode(d->m_courseResource->language()->id())); - QDomElement unitListElement = document.createElement("units"); + QDomElement unitListElement = document.createElement(QStringLiteral("units")); // create units foreach (Unit *unit, d->m_courseResource->unitList()) { - QDomElement unitElement = document.createElement("unit"); + QDomElement unitElement = document.createElement(QStringLiteral("unit")); - QDomElement unitIdElement = document.createElement("id"); - QDomElement unitTitleElement = document.createElement("title"); - QDomElement unitPhraseListElement = document.createElement("phrases"); + QDomElement unitIdElement = document.createElement(QStringLiteral("id")); + QDomElement unitTitleElement = document.createElement(QStringLiteral("title")); + QDomElement unitPhraseListElement = document.createElement(QStringLiteral("phrases")); unitIdElement.appendChild(document.createTextNode(unit->id())); unitTitleElement.appendChild(document.createTextNode(unit->title())); // construct phrases foreach (Phrase *phrase, unit->phraseList()) { if (trainingExport && phrase->soundFileUrl().isEmpty()) { continue; } unitPhraseListElement.appendChild(serializedPhrase(phrase, document)); } if (trainingExport && unitPhraseListElement.childNodes().count() == 0) { continue; } // construct the unit element unitElement.appendChild(unitIdElement); if (!unit->foreignId().isEmpty()) { - QDomElement unitForeignIdElement = document.createElement("foreignId"); + QDomElement unitForeignIdElement = document.createElement(QStringLiteral("foreignId")); unitForeignIdElement.appendChild(document.createTextNode(unit->foreignId())); unitElement.appendChild(unitForeignIdElement); } unitElement.appendChild(unitTitleElement); unitElement.appendChild(unitPhraseListElement); unitListElement.appendChild(unitElement); } root.appendChild(idElement); if (!d->m_courseResource->foreignId().isEmpty()) { - QDomElement courseForeignIdElement = document.createElement("foreignId"); + QDomElement courseForeignIdElement = document.createElement(QStringLiteral("foreignId")); courseForeignIdElement.appendChild(document.createTextNode(d->m_courseResource->foreignId())); root.appendChild(courseForeignIdElement); } root.appendChild(titleElement); root.appendChild(descriptionElement); root.appendChild(languageElement); root.appendChild(unitListElement); return document; } QDomElement CourseResource::serializedPhrase(Phrase *phrase, QDomDocument &document) const { - QDomElement phraseElement = document.createElement("phrase"); - QDomElement phraseIdElement = document.createElement("id"); - QDomElement phraseTextElement = document.createElement("text"); - QDomElement phrasei18nTextElement = document.createElement("i18nText"); - QDomElement phraseSoundFileElement = document.createElement("soundFile"); - QDomElement phraseTypeElement = document.createElement("type"); - QDomElement phraseEditStateElement = document.createElement("editState"); - QDomElement phrasePhonemeListElement = document.createElement("phonemes"); + QDomElement phraseElement = document.createElement(QStringLiteral("phrase")); + QDomElement phraseIdElement = document.createElement(QStringLiteral("id")); + QDomElement phraseTextElement = document.createElement(QStringLiteral("text")); + QDomElement phrasei18nTextElement = document.createElement(QStringLiteral("i18nText")); + QDomElement phraseSoundFileElement = document.createElement(QStringLiteral("soundFile")); + QDomElement phraseTypeElement = document.createElement(QStringLiteral("type")); + QDomElement phraseEditStateElement = document.createElement(QStringLiteral("editState")); + QDomElement phrasePhonemeListElement = document.createElement(QStringLiteral("phonemes")); phraseIdElement.appendChild(document.createTextNode(phrase->id())); phraseTextElement.appendChild(document.createTextNode(phrase->text())); phrasei18nTextElement.appendChild(document.createTextNode(phrase->i18nText())); phraseSoundFileElement.appendChild(document.createTextNode(phrase->sound().fileName())); phraseTypeElement.appendChild(document.createTextNode(phrase->typeString())); phraseEditStateElement.appendChild(document.createTextNode(phrase->editStateString())); // add phonemes foreach (Phoneme *phoneme, phrase->phonemes()) { - QDomElement phonemeElement = document.createElement("phonemeID"); + QDomElement phonemeElement = document.createElement(QStringLiteral("phonemeID")); phonemeElement.appendChild(document.createTextNode(phoneme->id())); phrasePhonemeListElement.appendChild(phonemeElement); } phraseElement.appendChild(phraseIdElement); if (!phrase->foreignId().isEmpty()) { - QDomElement phraseForeignIdElement = document.createElement("foreignId"); + QDomElement phraseForeignIdElement = document.createElement(QStringLiteral("foreignId")); phraseForeignIdElement.appendChild(document.createTextNode(phrase->foreignId())); phraseElement.appendChild(phraseForeignIdElement); } phraseElement.appendChild(phraseTextElement); phraseElement.appendChild(phrasei18nTextElement); phraseElement.appendChild(phraseSoundFileElement); phraseElement.appendChild(phraseTypeElement); phraseElement.appendChild(phraseEditStateElement); phraseElement.appendChild(phrasePhonemeListElement); if (phrase->isExcluded()) { - QDomElement phraseIsExcludedElement = document.createElement("excluded"); - phraseIsExcludedElement.appendChild(document.createTextNode("true")); + QDomElement phraseIsExcludedElement = document.createElement(QStringLiteral("excluded")); + phraseIsExcludedElement.appendChild(document.createTextNode(QStringLiteral("true"))); phraseElement.appendChild(phraseIsExcludedElement); } return phraseElement; } diff --git a/src/core/resources/languageresource.cpp b/src/core/resources/languageresource.cpp index dc543f3..529d615 100644 --- a/src/core/resources/languageresource.cpp +++ b/src/core/resources/languageresource.cpp @@ -1,190 +1,190 @@ /* * Copyright 2013 Andreas Cord-Landwehr * * 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 "languageresource.h" #include "core/resourcemanager.h" #include "core/language.h" #include "core/phoneme.h" #include "core/phonemegroup.h" #include #include #include #include #include #include "artikulate_debug.h" class LanguageResourcePrivate { public: LanguageResourcePrivate(ResourceManager *resourceManager) : m_resourceManager(resourceManager) , m_type(ResourceInterface::LanguageResourceType) , m_languageResource(nullptr) { } ~LanguageResourcePrivate() { } ResourceManager *m_resourceManager; QUrl m_path; ResourceInterface::Type m_type; QString m_identifier; QString m_title; QString m_i18nTitle; Language *m_languageResource; }; LanguageResource::LanguageResource(ResourceManager *resourceManager, const QUrl &path) : ResourceInterface(resourceManager) , d(new LanguageResourcePrivate(resourceManager)) { d->m_path = path; // load basic information from language file, but does not parse everything QXmlStreamReader xml; QFile file(path.toLocalFile()); if (file.open(QIODevice::ReadOnly)) { xml.setDevice(&file); xml.readNextStartElement(); while (xml.readNext() && !xml.atEnd()) { if (xml.name() == "id") { d->m_identifier = xml.readElementText(); } if (xml.name() == "title") { d->m_title = xml.readElementText(); } if (xml.name() == "i18nTitle") { d->m_i18nTitle = xml.readElementText(); } // quit reading when basic elements are read if (!d->m_identifier.isEmpty() && !d->m_title.isEmpty() && !d->m_i18nTitle.isEmpty() ) { break; } } if (xml.hasError()) { qCritical() << "Error occurred when reading Language XML file:" << path.toLocalFile(); } } xml.clear(); file.close(); } LanguageResource::~LanguageResource() { } QString LanguageResource::identifier() { return d->m_identifier; } QString LanguageResource::title() { return d->m_title; } QString LanguageResource::i18nTitle() { return d->m_i18nTitle; } ResourceInterface::Type LanguageResource::type() const { return d->m_type; } void LanguageResource::close() { // do nothing // language files are never closed } bool LanguageResource::isOpen() const { return (d->m_languageResource != 0); } QUrl LanguageResource::path() const { return d->m_path; } QObject * LanguageResource::resource() { if (d->m_languageResource != 0) { return d->m_languageResource; } if (!d->m_path.isLocalFile()) { qCWarning(ARTIKULATE_LOG) << "Cannot open language file at " << d->m_path.toLocalFile() << ", aborting."; return 0; } - QXmlSchema schema = loadXmlSchema("language"); + QXmlSchema schema = loadXmlSchema(QStringLiteral("language")); if (!schema.isValid()) { return 0; } QDomDocument document = loadDomDocument(d->m_path, schema); if (document.isNull()) { qCWarning(ARTIKULATE_LOG) << "Could not parse document " << d->m_path.toLocalFile() << ", aborting."; return 0; } QDomElement root(document.documentElement()); d->m_languageResource = new Language(this); d->m_languageResource->setFile(d->m_path); - d->m_languageResource->setId(root.firstChildElement("id").text()); - d->m_languageResource->setTitle(root.firstChildElement("title").text()); - d->m_languageResource->seti18nTitle(root.firstChildElement("i18nTitle").text()); + d->m_languageResource->setId(root.firstChildElement(QStringLiteral("id")).text()); + d->m_languageResource->setTitle(root.firstChildElement(QStringLiteral("title")).text()); + d->m_languageResource->seti18nTitle(root.firstChildElement(QStringLiteral("i18nTitle")).text()); // create phoneme groups - for (QDomElement groupNode = root.firstChildElement("phonemeGroups").firstChildElement(); + for (QDomElement groupNode = root.firstChildElement(QStringLiteral("phonemeGroups")).firstChildElement(); !groupNode.isNull(); groupNode = groupNode.nextSiblingElement()) { PhonemeGroup *group = d->m_languageResource->addPhonemeGroup( - groupNode.firstChildElement("id").text(), - groupNode.firstChildElement("title").text()); - group->setDescription(groupNode.attribute("description")); + groupNode.firstChildElement(QStringLiteral("id")).text(), + groupNode.firstChildElement(QStringLiteral("title")).text()); + group->setDescription(groupNode.attribute(QStringLiteral("description"))); // register phonemes - for (QDomElement phonemeNode = groupNode.firstChildElement("phonemes").firstChildElement(); + for (QDomElement phonemeNode = groupNode.firstChildElement(QStringLiteral("phonemes")).firstChildElement(); !phonemeNode.isNull(); phonemeNode = phonemeNode.nextSiblingElement()) { - group->addPhoneme(phonemeNode.firstChildElement("id").text(), phonemeNode.firstChildElement("title").text()); + group->addPhoneme(phonemeNode.firstChildElement(QStringLiteral("id")).text(), phonemeNode.firstChildElement(QStringLiteral("title")).text()); } } return d->m_languageResource; } Language * LanguageResource::language() { return qobject_cast(resource()); } diff --git a/src/core/resources/resourceinterface.cpp b/src/core/resources/resourceinterface.cpp index f5111d0..f63566e 100644 --- a/src/core/resources/resourceinterface.cpp +++ b/src/core/resources/resourceinterface.cpp @@ -1,95 +1,95 @@ /* * Copyright 2013-2015 Andreas Cord-Landwehr * * 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 "resourceinterface.h" #include "artikulate_debug.h" #include #include #include #include #include #include #include ResourceInterface::ResourceInterface(ResourceManager *resourceManager) : QObject() , m_contributorResource(false) { Q_UNUSED(resourceManager) } ResourceInterface::~ResourceInterface() { } void ResourceInterface::setContributorResource(bool contributorResource) { m_contributorResource = contributorResource; } bool ResourceInterface::isContributorResource() const { return m_contributorResource; } void ResourceInterface::sync() { qCWarning(ARTIKULATE_LOG) << "Resource does not implement syncing."; } void ResourceInterface::reload() { qCWarning(ARTIKULATE_LOG) << "Resource does not implement reloading."; } QXmlSchema ResourceInterface::loadXmlSchema(const QString &schemeName) const { - QString relPath = QString("schemes/%1.xsd").arg(schemeName); + QString relPath = QStringLiteral("schemes/%1.xsd").arg(schemeName); QUrl file = QUrl::fromLocalFile(QStandardPaths::locate(QStandardPaths::GenericDataLocation, "artikulate/" + relPath)); QXmlSchema schema; if (file.isEmpty() || schema.load(file) == false) { qCWarning(ARTIKULATE_LOG) << "Schema at file " << file.toLocalFile() << " is invalid."; } return schema; } QDomDocument ResourceInterface::loadDomDocument(const QUrl &path, const QXmlSchema &schema) const { QDomDocument document; QXmlSchemaValidator validator(schema); if (!validator.validate(path)) { qCWarning(ARTIKULATE_LOG) << "Schema is not valid, aborting loading of XML document:" << path.toLocalFile(); return document; } QString errorMsg; QFile file(path.toLocalFile()); if (file.open(QIODevice::ReadOnly)) { if (!document.setContent(&file, &errorMsg)) { qCWarning(ARTIKULATE_LOG) << errorMsg; } } else { qCWarning(ARTIKULATE_LOG) << "Could not open XML document " << path.toLocalFile() << " for reading, aborting."; } return document; } diff --git a/src/core/resources/resourceinterface.h b/src/core/resources/resourceinterface.h index e52c443..90c5b22 100644 --- a/src/core/resources/resourceinterface.h +++ b/src/core/resources/resourceinterface.h @@ -1,132 +1,133 @@ /* * Copyright 2013 Andreas Cord-Landwehr * * 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 . */ #ifndef RESOURCEINTERFACE_H #define RESOURCEINTERFACE_H +#include "artikulatecore_export.h" #include class ResourceManager; class QUrl; class QXmlSchema; class QDomDocument; -class ResourceInterface : public QObject +class ARTIKULATECORE_EXPORT ResourceInterface : public QObject { Q_OBJECT public: enum Type { LanguageResourceType, CourseResourceType, SkeletonResourceType }; explicit ResourceInterface(ResourceManager *resourceManager); virtual ~ResourceInterface(); /** * \return unique identifier */ virtual QString identifier() = 0; /** * \return human readable localized title */ virtual QString title() = 0; /** * \return human readable title in English */ virtual QString i18nTitle() = 0; /** * Set resource to be read-only. */ virtual void setContributorResource(bool contributorResource=true); /** * \returns true if resource is readonly, otherwise false */ virtual bool isContributorResource() const; /** * \return type of resource */ virtual Type type() const = 0; /** * \return true if resource is loaded, otherwise false */ virtual bool isOpen() const = 0; /** * close resource without writing changes back to file */ virtual void close() = 0; /** * \return path to resource file */ virtual QUrl path() const = 0; /** * Write changes to resource back to file. * This operation does _not_ close the file. */ virtual void sync(); /** * Reload resource from file. */ virtual void reload(); /** * \return reference to the loaded resource * if resource is not open yet, it will be loaded */ virtual QObject * resource() = 0; /** * Load XSD file given by its file name (without ".xsd" suffix). The method searches exclusively * the standard install dir for XSD files in subdirectory "schemes/". * * \param schemeName name of the Xml schema without suffix * \return loaded XML Schema */ QXmlSchema loadXmlSchema(const QString &schemeName) const; /** * Load XML file given by \p file that confirms with XML schema \p scheme. * * \param path is the path to the XML file to be loaded * \param scheme is the XML schema describing the DOM * \return the loaded DOM document */ QDomDocument loadDomDocument(const QUrl &path, const QXmlSchema &schema) const; private: bool m_contributorResource; //!< identifies this resource as an editable resource }; #endif diff --git a/src/core/resources/skeletonresource.cpp b/src/core/resources/skeletonresource.cpp index 5ab1ef2..d78103b 100644 --- a/src/core/resources/skeletonresource.cpp +++ b/src/core/resources/skeletonresource.cpp @@ -1,325 +1,325 @@ /* * Copyright 2013 Andreas Cord-Landwehr * * 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 "skeletonresource.h" #include "core/resourcemanager.h" #include "core/language.h" #include "core/skeleton.h" #include "core/unit.h" #include "core/phoneme.h" #include "core/phonemegroup.h" #include "core/resources/languageresource.h" #include #include #include #include #include #include "artikulate_debug.h" class SkeletonResourcePrivate { public: SkeletonResourcePrivate(ResourceManager *resourceManager) : m_resourceManager(resourceManager) , m_type(ResourceInterface::SkeletonResourceType) , m_skeletonResource(nullptr) { } ~SkeletonResourcePrivate() { } ResourceManager *m_resourceManager; QUrl m_path; ResourceInterface::Type m_type; QString m_identifier; QString m_title; QString m_i18nTitle; Skeleton *m_skeletonResource; }; SkeletonResource::SkeletonResource(ResourceManager *resourceManager, const QUrl &path) : ResourceInterface(resourceManager) , d(new SkeletonResourcePrivate(resourceManager)) { d->m_path = path; // load basic information from language file, but does not parse everything QXmlStreamReader xml; QFile file(path.toLocalFile()); if (file.open(QIODevice::ReadOnly)) { xml.setDevice(&file); xml.readNextStartElement(); while (xml.readNext() && !xml.atEnd()) { if (xml.name() == "id") { d->m_identifier = xml.readElementText(); continue; } if (xml.name() == "title") { d->m_title = xml.readElementText(); d->m_i18nTitle = d->m_title; continue; } //TODO i18nTitle must be implemented, currently missing and hence not parsed // quit reading when basic elements are read if (!d->m_identifier.isEmpty() && !d->m_title.isEmpty() && !d->m_i18nTitle.isEmpty() ) { break; } } if (xml.hasError()) { qCritical() << "Error occurred when reading Skeleton XML file:" << path.toLocalFile(); } } xml.clear(); file.close(); } SkeletonResource::SkeletonResource(ResourceManager* resourceManager, Skeleton *skeleton) : ResourceInterface(resourceManager) , d(new SkeletonResourcePrivate(resourceManager)) { d->m_type = ResourceInterface::SkeletonResourceType; d->m_path = skeleton->file(); d->m_identifier = skeleton->id(); d->m_title = skeleton->title(); d->m_skeletonResource = skeleton; } SkeletonResource::~SkeletonResource() { } QString SkeletonResource::identifier() { if (d->m_skeletonResource) { return d->m_skeletonResource->id(); } return d->m_identifier; } QString SkeletonResource::title() { if (d->m_skeletonResource) { return d->m_skeletonResource->title(); } return d->m_title; } QString SkeletonResource::i18nTitle() { if (d->m_skeletonResource) { return d->m_skeletonResource->title(); //TODO } return d->m_i18nTitle; } ResourceInterface::Type SkeletonResource::type() const { return d->m_type; } void SkeletonResource::close() { d->m_skeletonResource->deleteLater(); d->m_skeletonResource = nullptr; } void SkeletonResource::sync() { Q_ASSERT(path().isValid()); Q_ASSERT(path().isLocalFile()); Q_ASSERT(!path().isEmpty()); // if resource was never loaded, it cannot be changed if (!d->m_skeletonResource) { qCDebug(ARTIKULATE_LOG) << "Aborting sync, skeleton was not parsed."; return; } // // not writing back if not modified // if (!d->m_skeletonResource->modified()) { // qCDebug(ARTIKULATE_LOG) << "Aborting sync, skeleton was not modified."; // return; // } QDomDocument document; // prepare xml header - QDomProcessingInstruction header = document.createProcessingInstruction("xml", "version=\"1.0\""); + QDomProcessingInstruction header = document.createProcessingInstruction(QStringLiteral("xml"), QStringLiteral("version=\"1.0\"")); document.appendChild(header); // create main element - QDomElement root = document.createElement("skeleton"); + QDomElement root = document.createElement(QStringLiteral("skeleton")); document.appendChild(root); - QDomElement idElement = document.createElement("id"); - QDomElement titleElement = document.createElement("title"); - QDomElement descriptionElement = document.createElement("description"); + QDomElement idElement = document.createElement(QStringLiteral("id")); + QDomElement titleElement = document.createElement(QStringLiteral("title")); + QDomElement descriptionElement = document.createElement(QStringLiteral("description")); idElement.appendChild(document.createTextNode(d->m_skeletonResource->id())); titleElement.appendChild(document.createTextNode(d->m_skeletonResource->title())); descriptionElement.appendChild(document.createTextNode(d->m_skeletonResource->description())); - QDomElement unitListElement = document.createElement("units"); + QDomElement unitListElement = document.createElement(QStringLiteral("units")); // create units foreach (Unit *unit, d->m_skeletonResource->unitList()) { - QDomElement unitElement = document.createElement("unit"); + QDomElement unitElement = document.createElement(QStringLiteral("unit")); - QDomElement unitIdElement = document.createElement("id"); - QDomElement unitTitleElement = document.createElement("title"); - QDomElement unitPhraseListElement = document.createElement("phrases"); + QDomElement unitIdElement = document.createElement(QStringLiteral("id")); + QDomElement unitTitleElement = document.createElement(QStringLiteral("title")); + QDomElement unitPhraseListElement = document.createElement(QStringLiteral("phrases")); unitIdElement.appendChild(document.createTextNode(unit->id())); unitTitleElement.appendChild(document.createTextNode(unit->title())); // construct phrases foreach (Phrase *phrase, unit->phraseList()) { - QDomElement phraseElement = document.createElement("phrase"); - QDomElement phraseIdElement = document.createElement("id"); - QDomElement phraseTextElement = document.createElement("text"); - QDomElement phraseTypeElement = document.createElement("type"); + QDomElement phraseElement = document.createElement(QStringLiteral("phrase")); + QDomElement phraseIdElement = document.createElement(QStringLiteral("id")); + QDomElement phraseTextElement = document.createElement(QStringLiteral("text")); + QDomElement phraseTypeElement = document.createElement(QStringLiteral("type")); phraseIdElement.appendChild(document.createTextNode(phrase->id())); phraseTextElement.appendChild(document.createTextNode(phrase->text())); phraseTypeElement.appendChild(document.createTextNode(phrase->typeString())); phraseElement.appendChild(phraseIdElement); phraseElement.appendChild(phraseTextElement); phraseElement.appendChild(phraseTypeElement); unitPhraseListElement.appendChild(phraseElement); } // construct the unit element unitElement.appendChild(unitIdElement); unitElement.appendChild(unitTitleElement); unitElement.appendChild(unitPhraseListElement); unitListElement.appendChild(unitElement); } root.appendChild(idElement); root.appendChild(titleElement); root.appendChild(descriptionElement); root.appendChild(unitListElement); // write back to file //TODO port to KSaveFile QFile file(path().toLocalFile()); if (!file.open(QIODevice::WriteOnly)) { qCWarning(ARTIKULATE_LOG) << "Unable to open file " << file.fileName() << " in write mode, aborting."; return; } file.write(document.toByteArray()); return; } void SkeletonResource::reload() { qCritical() << "NOT IMPLEMENTED"; } bool SkeletonResource::isOpen() const { return (d->m_skeletonResource != nullptr); } QUrl SkeletonResource::path() const { if (d->m_skeletonResource) { return d->m_skeletonResource->file(); } return d->m_path; } QObject * SkeletonResource::resource() { if (d->m_skeletonResource) { return d->m_skeletonResource; } if (!path().isLocalFile()) { qCWarning(ARTIKULATE_LOG) << "Cannot open skeleton file at " << path().toLocalFile() << ", aborting."; return nullptr; } - QXmlSchema schema = loadXmlSchema("skeleton"); + QXmlSchema schema = loadXmlSchema(QStringLiteral("skeleton")); if (!schema.isValid()) { return nullptr; } QDomDocument document = loadDomDocument(path(), schema); if (document.isNull()) { qCWarning(ARTIKULATE_LOG) << "Could not parse document " << path().toLocalFile() << ", aborting."; return nullptr; } // create skeleton QDomElement root(document.documentElement()); d->m_skeletonResource = new Skeleton(this); d->m_skeletonResource->setFile(d->m_path); - d->m_skeletonResource->setId(root.firstChildElement("id").text()); - d->m_skeletonResource->setTitle(root.firstChildElement("title").text()); - d->m_skeletonResource->setDescription(root.firstChildElement("title").text()); + d->m_skeletonResource->setId(root.firstChildElement(QStringLiteral("id")).text()); + d->m_skeletonResource->setTitle(root.firstChildElement(QStringLiteral("title")).text()); + d->m_skeletonResource->setDescription(root.firstChildElement(QStringLiteral("title")).text()); // create units - for (QDomElement unitNode = root.firstChildElement("units").firstChildElement(); + for (QDomElement unitNode = root.firstChildElement(QStringLiteral("units")).firstChildElement(); !unitNode.isNull(); unitNode = unitNode.nextSiblingElement()) { Unit *unit = new Unit(d->m_skeletonResource); - unit->setId(unitNode.firstChildElement("id").text()); + unit->setId(unitNode.firstChildElement(QStringLiteral("id")).text()); unit->setCourse(d->m_skeletonResource); - unit->setTitle(unitNode.firstChildElement("title").text()); + unit->setTitle(unitNode.firstChildElement(QStringLiteral("title")).text()); d->m_skeletonResource->addUnit(unit); // create phrases - for (QDomElement phraseNode = unitNode.firstChildElement("phrases").firstChildElement(); + for (QDomElement phraseNode = unitNode.firstChildElement(QStringLiteral("phrases")).firstChildElement(); !phraseNode.isNull(); phraseNode = phraseNode.nextSiblingElement()) { Phrase *phrase = new Phrase(unit); - phrase->setId(phraseNode.firstChildElement("id").text()); - phrase->setText(phraseNode.firstChildElement("text").text()); - phrase->setType(phraseNode.firstChildElement("type").text()); + phrase->setId(phraseNode.firstChildElement(QStringLiteral("id")).text()); + phrase->setText(phraseNode.firstChildElement(QStringLiteral("text")).text()); + phrase->setType(phraseNode.firstChildElement(QStringLiteral("type")).text()); phrase->setUnit(unit); unit->addPhrase(phrase); } } d->m_skeletonResource->setModified(false); return d->m_skeletonResource; } Skeleton * SkeletonResource::skeleton() { return qobject_cast(resource()); } diff --git a/src/main.cpp b/src/main.cpp index ed30784..50b554d 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,65 +1,65 @@ /* * Copyright 2013-2017 Andreas Cord-Landwehr * * 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 "mainwindow.h" #include "application.h" #include "version.h" #include #include #include #include "artikulate_debug.h" int main(int argc, char **argv) { KLocalizedString::setApplicationDomain("artikulate"); Application app(argc, argv); - KAboutData aboutData("artikulate", + KAboutData aboutData(QStringLiteral("artikulate"), i18nc("@title Displayed program name", "Artikulate"), ARTIKULATE_VERSION_STRING, i18nc("@title KAboutData: short program description", "Artikulate Pronunciation Trainer"), KAboutLicense::GPL_V2, i18nc("@info:credit", "(c) 2013-2017 The Artikulate Developers"), i18nc("@title Short program description", "Training your pronunciation in a foreign language.") ); aboutData.addAuthor(i18nc("@info:credit Developer name", "Andreas Cord-Landwehr"), i18nc("@info:credit Role", "Original Author"), - "cordlandwehr@kde.org"); + QStringLiteral("cordlandwehr@kde.org")); aboutData.addAuthor(i18nc("@info:credit Developer name", "Samikshan Bairagya"), i18nc("@info:credit Role", "Developer"), - "samikshan@gmail.com"); + QStringLiteral("samikshan@gmail.com")); aboutData.addAuthor(i18nc("@info:credit Developer name", "Oindrila Gupta"), i18nc("@info:credit Role", "Developer and Course Data")); aboutData.addAuthor(i18nc("@info:credit Developer name", "Magdalena Konkiewicz"), i18nc("@info:credit Role", "Developer and Course Data")); aboutData.setTranslator(i18nc("NAME OF TRANSLATORS", "Your names"), i18nc("EMAIL OF TRANSLATORS", "Your emails")); KAboutData::setApplicationData(aboutData); KCrash::initialize(); new MainWindow(); return app.exec(); } diff --git a/src/main_editor.cpp b/src/main_editor.cpp index bafe664..2a35896 100644 --- a/src/main_editor.cpp +++ b/src/main_editor.cpp @@ -1,57 +1,57 @@ /* * Copyright 2015 Andreas Cord-Landwehr * * 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 "version.h" #include "application.h" #include "mainwindow_editor.h" #include #include #include #include "artikulate_debug.h" int main(int argc, char **argv) { Application app(argc, argv); KLocalizedString::setApplicationDomain("artikulate"); - KAboutData aboutData("artikulate_editor", + KAboutData aboutData(QStringLiteral("artikulate_editor"), ki18nc("@title Displayed program name", "Artikulate Editor").toString(), ARTIKULATE_VERSION_STRING, ki18nc("@title KAboutData: short program description", "Artikulate Course Editor").toString(), KAboutLicense::GPL_V2, ki18nc("@info:credit", "(c) 2013-2016 The Artikulate Developers").toString(), ki18nc("@title Short program description", "Edit Artikulate course files.").toString() ); aboutData.addAuthor(ki18nc("@info:credit Developer name", "Andreas Cord-Landwehr").toString(), ki18nc("@info:credit Role", "Original Author").toString(), - "cordlandwehr@kde.org"); + QStringLiteral("cordlandwehr@kde.org")); KAboutData::setApplicationData(aboutData); KCrash::initialize(); MainWindowEditor *mainWindow = new MainWindowEditor(); QSize size(800, 600); mainWindow->setMinimumSize(size); mainWindow->show(); return app.exec(); } diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index b6a05ee..eb27ed8 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -1,219 +1,219 @@ /* * Copyright 2013-2015 Andreas Cord-Landwehr * * 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 "mainwindow.h" #include "ui/resourcesdialogpage.h" #include "ui/sounddevicedialogpage.h" #include "ui/appearencedialogpage.h" #include "core/resourcemanager.h" #include "core/trainingsession.h" #include "core/editorsession.h" #include "core/resources/courseresource.h" #include "models/languagemodel.h" #include "settings.h" #include "liblearnerprofile/src/profilemanager.h" #include "liblearnerprofile/src/learner.h" #include "libsound/src/outputdevicecontroller.h" #include "artikulate_debug.h" #include #include #include #include #include #include #include #include #include #include #include #include using namespace LearnerProfile; MainWindow::MainWindow() - : m_actionCollection(new KActionCollection(this, "artikulate")) + : m_actionCollection(new KActionCollection(this, QStringLiteral("artikulate"))) , m_helpMenu(new KHelpMenu) , m_resourceManager(new ResourceManager(this)) , m_profileManager(new LearnerProfile::ProfileManager(this)) , m_trainingSession(new TrainingSession(this)) { rootContext()->setContextObject(new KLocalizedContext(this)); // load saved sound settings OutputDeviceController::self().setVolume(Settings::audioOutputVolume()); // load resources m_resourceManager->loadLanguageResources(); if (m_resourceManager->languageResources().count() == 0) { qFatal("No language resources found, cannot start application."); } m_resourceManager->loadCourseResources(); m_trainingSession->setProfileManager(m_profileManager); // create menu setupActions(); // set view - rootContext()->setContextProperty("g_resourceManager", m_resourceManager); - rootContext()->setContextProperty("g_trainingSession", m_trainingSession); - rootContext()->setContextProperty("g_profileManager", m_profileManager); - rootContext()->setContextProperty("kcfg_UseContributorResources", Settings::useCourseRepository()); - rootContext()->setContextProperty("kcfg_ShowMenuBar", Settings::showMenuBar()); + rootContext()->setContextProperty(QStringLiteral("g_resourceManager"), m_resourceManager); + rootContext()->setContextProperty(QStringLiteral("g_trainingSession"), m_trainingSession); + rootContext()->setContextProperty(QStringLiteral("g_profileManager"), m_profileManager); + rootContext()->setContextProperty(QStringLiteral("kcfg_UseContributorResources"), Settings::useCourseRepository()); + rootContext()->setContextProperty(QStringLiteral("kcfg_ShowMenuBar"), Settings::showMenuBar()); // set starting screen - load(QUrl("qrc:/artikulate/qml/Main.qml")); + load(QUrl(QStringLiteral("qrc:/artikulate/qml/Main.qml"))); // settings from kcfg values // updateTrainingPhraseFont(); //FIXME deactivated while porting // create training profile if none exists: if (!m_profileManager->activeProfile()) { m_profileManager->addProfile(i18n("Unnamed Identity")); } // connect to QML signals; connect(rootObjects().first(), SIGNAL(triggerSettingsDialog()), this, SLOT(showSettingsDialog())); connect(rootObjects().first(), SIGNAL(triggerAction(QString)), this, SLOT(triggerAction(QString))); connect(rootObjects().first(), SIGNAL(switchMenuBarVisibility()), this, SLOT(switchMenuBarVisibility())); // set font for the phrase in trainer to default from kcfg file - QObject *phraseText = rootObjects().first()->findChild("phraseText"); + QObject *phraseText = rootObjects().first()->findChild(QStringLiteral("phraseText")); if (phraseText) { phraseText->setProperty("font", Settings::trainingPhraseFont()); } } MainWindow::~MainWindow() { // save current settings for case of closing Settings::self()->save(); m_profileManager->sync(); } ResourceManager * MainWindow::resourceManager() const { return m_resourceManager; } KActionCollection * MainWindow::actionCollection() { return m_actionCollection; } void MainWindow::setupActions() { QAction *settingsAction = new QAction(i18nc("@item:inmenu", "Configure Artikulate"), this); - connect(settingsAction, SIGNAL(triggered()), SLOT(showSettingsDialog())); - actionCollection()->addAction("settings", settingsAction); - settingsAction->setIcon(QIcon::fromTheme("configure")); + connect(settingsAction, &QAction::triggered, this, &MainWindow::showSettingsDialog); + actionCollection()->addAction(QStringLiteral("settings"), settingsAction); + settingsAction->setIcon(QIcon::fromTheme(QStringLiteral("configure"))); QAction *configLearnerProfileAction = new QAction(i18nc("@item:inmenu", "Learner Profile"), this); - connect(configLearnerProfileAction, SIGNAL(triggered(bool)), this, SLOT(configLearnerProfile())); - actionCollection()->addAction("config_learner_profile", configLearnerProfileAction); - configLearnerProfileAction->setIcon(QIcon::fromTheme("user-identity")); + connect(configLearnerProfileAction, &QAction::triggered, this, &MainWindow::configLearnerProfile); + actionCollection()->addAction(QStringLiteral("config_learner_profile"), configLearnerProfileAction); + configLearnerProfileAction->setIcon(QIcon::fromTheme(QStringLiteral("user-identity"))); KStandardAction::helpContents(m_helpMenu, SLOT(appHelpActivated()), actionCollection()); KStandardAction::reportBug(m_helpMenu, SLOT(reportBug()), actionCollection()); KStandardAction::aboutKDE(m_helpMenu, SLOT(aboutKDE()), actionCollection()); KStandardAction::aboutApp(m_helpMenu, SLOT(aboutApplication()), actionCollection()); KStandardAction::quit(qApp, SLOT(quit()), actionCollection()); } void MainWindow::showSettingsDialog() { - if (KConfigDialog::showDialog("settings")) { + if (KConfigDialog::showDialog(QStringLiteral("settings"))) { return; } - QPointer dialog = new KConfigDialog(0, "settings", Settings::self()); + QPointer dialog = new KConfigDialog(0, QStringLiteral("settings"), Settings::self()); ResourcesDialogPage *resourceDialog = new ResourcesDialogPage(m_resourceManager); SoundDeviceDialogPage *soundDialog = new SoundDeviceDialogPage(); AppearenceDialogPage *appearenceDialog = new AppearenceDialogPage(); resourceDialog->loadSettings(); soundDialog->loadSettings(); appearenceDialog->loadSettings(); - dialog->addPage(soundDialog, i18nc("@item:inmenu", "Sound Devices"), "audio-headset", i18nc("@title:tab", "Sound Device Settings"), true); - dialog->addPage(appearenceDialog, i18nc("@item:inmenu", "Fonts"), "preferences-desktop-font", i18nc("@title:tab", "Training Phrase Font"), true); - dialog->addPage(resourceDialog, i18nc("@item:inmenu", "Course Resources"), "repository", i18nc("@title:tab", "Resource Repository Settings"), true); + dialog->addPage(soundDialog, i18nc("@item:inmenu", "Sound Devices"), QStringLiteral("audio-headset"), i18nc("@title:tab", "Sound Device Settings"), true); + dialog->addPage(appearenceDialog, i18nc("@item:inmenu", "Fonts"), QStringLiteral("preferences-desktop-font"), i18nc("@title:tab", "Training Phrase Font"), true); + dialog->addPage(resourceDialog, i18nc("@item:inmenu", "Course Resources"), QStringLiteral("repository"), i18nc("@title:tab", "Resource Repository Settings"), true); // connect(dialog, SIGNAL(settingsChanged(const QString&)), resourceDialog, SLOT(loadSettings())); // connect(dialog, SIGNAL(settingsChanged(const QString&)), soundDialog, SLOT(loadSettings())); - connect(dialog, SIGNAL(accepted()), resourceDialog, SLOT(saveSettings())); - connect(dialog, SIGNAL(accepted()), soundDialog, SLOT(saveSettings())); - connect(dialog, SIGNAL(accepted()), appearenceDialog, SLOT(saveSettings())); - connect(dialog, SIGNAL(accepted()), SLOT(updateTrainingPhraseFont())); - connect(dialog, SIGNAL(accepted()), SLOT(updateKcfgUseContributorResources())); - connect(dialog, SIGNAL(finished()), soundDialog, SLOT(stopPlaying())); - connect(dialog, SIGNAL(finished()), soundDialog, SLOT(stopRecord())); + connect(dialog.data(), &QDialog::accepted, resourceDialog, &ResourcesDialogPage::saveSettings); + connect(dialog.data(), &QDialog::accepted, soundDialog, &SoundDeviceDialogPage::saveSettings); + connect(dialog.data(), &QDialog::accepted, appearenceDialog, &AppearenceDialogPage::saveSettings); + connect(dialog.data(), &QDialog::accepted, this, &MainWindow::updateTrainingPhraseFont); + connect(dialog.data(), &QDialog::accepted, this, &MainWindow::updateKcfgUseContributorResources); + connect(dialog.data(), &QDialog::finished, soundDialog, &SoundDeviceDialogPage::stopPlaying); + connect(dialog.data(), &QDialog::finished, soundDialog, &SoundDeviceDialogPage::stopRecord); dialog->exec(); } void MainWindow::updateTrainingPhraseFont() { - QObject *phraseText = rootObjects().first()->findChild("phraseText"); + QObject *phraseText = rootObjects().first()->findChild(QStringLiteral("phraseText")); if (!phraseText) { qCDebug(ARTIKULATE_LOG) << "no phraseText context object found, aborting"; return; } QFont f = phraseText->property("font").value(); phraseText->setProperty("font", Settings::trainingPhraseFont()); } void MainWindow::updateKcfgUseContributorResources() { - rootContext()->setContextProperty("kcfg_UseContributorResources", Settings::useCourseRepository()); + rootContext()->setContextProperty(QStringLiteral("kcfg_UseContributorResources"), Settings::useCourseRepository()); } void MainWindow::configLearnerProfile() { qCritical() << "Not implemented"; //FIXME } void MainWindow::triggerAction(const QString &actionName) { QAction * action = actionCollection()->action(actionName); if (action) { action->trigger(); } else { qCritical() << "Action is not registered:" << actionName; } } void MainWindow::switchMenuBarVisibility() { Settings::setShowMenuBar(!Settings::showMenuBar()); - rootContext()->setContextProperty("kcfg_ShowMenuBar", Settings::showMenuBar()); + rootContext()->setContextProperty(QStringLiteral("kcfg_ShowMenuBar"), Settings::showMenuBar()); } bool MainWindow::queryClose() { Settings::self()->save(); // FIXME make sure all learner data is written to database return true; } diff --git a/src/mainwindow_editor.cpp b/src/mainwindow_editor.cpp index d41c0f9..df979ec 100644 --- a/src/mainwindow_editor.cpp +++ b/src/mainwindow_editor.cpp @@ -1,198 +1,198 @@ /* * Copyright 2013-2015 Andreas Cord-Landwehr * * 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 "mainwindow_editor.h" #include "ui/resourcesdialogpage.h" #include "ui/sounddevicedialogpage.h" #include "ui/appearencedialogpage.h" #include "ui/exportghnsdialog.h" #include "core/resourcemanager.h" #include "core/editorsession.h" #include "core/resources/courseresource.h" #include "models/languagemodel.h" #include "settings.h" #include "libsound/src/outputdevicecontroller.h" #include #include #include #include #include #include #include #include #include #include #include #include "artikulate_debug.h" #include #include #include #include #include #include #include #include #include #include using namespace LearnerProfile; MainWindowEditor::MainWindowEditor() : m_resourceManager(new ResourceManager(this)) , m_editorSession(new EditorSession(this)) , m_widget(new QQuickWidget) { m_editorSession->setResourceManager(m_resourceManager); - setWindowIcon(QIcon::fromTheme("artikulate")); + setWindowIcon(QIcon::fromTheme(QStringLiteral("artikulate"))); setWindowTitle(qAppName()); setAutoSaveSettings(); // workaround for QTBUG-40765 qApp->setAttribute(Qt::AA_DontCreateNativeWidgetSiblings); // load saved sound settings OutputDeviceController::self().setVolume(Settings::audioOutputVolume()); // load resources m_resourceManager->loadLanguageResources(); if (m_resourceManager->languageResources().count() == 0) { qFatal("No language resources found, cannot start application."); } m_resourceManager->loadCourseResources(); // create menu setupActions(); // set view m_widget->resize(QSize(800, 600)); m_widget->rootContext()->setContextObject(new KLocalizedContext(m_widget)); - m_widget->rootContext()->setContextProperty("g_resourceManager", m_resourceManager); - m_widget->rootContext()->setContextProperty("editorSession", m_editorSession); + m_widget->rootContext()->setContextProperty(QStringLiteral("g_resourceManager"), m_resourceManager); + m_widget->rootContext()->setContextProperty(QStringLiteral("editorSession"), m_editorSession); // set starting screen - m_widget->setSource(QUrl("qrc:/artikulate/qml/Editor.qml")); + m_widget->setSource(QUrl(QStringLiteral("qrc:/artikulate/qml/Editor.qml"))); m_widget->setResizeMode(QQuickWidget::SizeRootObjectToView); QAction *newAct = KStandardAction::save(this, SLOT(save()), actionCollection()); - actionCollection()->addAction("save", newAct); + actionCollection()->addAction(QStringLiteral("save"), newAct); // set status bar statusBar()->setEnabled(true); QLabel *repositoryLabel = new QLabel; repositoryLabel->setText(i18n("Course Repository: %1", m_resourceManager->repositoryUrl())); connect(m_resourceManager, &ResourceManager::repositoryChanged, [=]() { repositoryLabel->setText(i18n("Course Repository: %1", m_resourceManager->repositoryUrl())); }); statusBar()->insertWidget(0, repositoryLabel); - createGUI("artikulateui_editor.rc"); + createGUI(QStringLiteral("artikulateui_editor.rc")); setCentralWidget(m_widget); } MainWindowEditor::~MainWindowEditor() { // save current settings for case of closing Settings::self()->save(); } ResourceManager * MainWindowEditor::resourceManager() const { return m_resourceManager; } void MainWindowEditor::setupActions() { QAction *settingsAction = new QAction(i18nc("@item:inmenu", "Configure Artikulate"), this); connect(settingsAction, &QAction::triggered, this, &MainWindowEditor::showSettingsDialog); - actionCollection()->addAction("settings", settingsAction); - settingsAction->setIcon(QIcon::fromTheme("configure")); + actionCollection()->addAction(QStringLiteral("settings"), settingsAction); + settingsAction->setIcon(QIcon::fromTheme(QStringLiteral("configure"))); QAction *exportAction = new QAction(i18nc("@item:inmenu", "Export GHNS Files"), this); connect(exportAction, &QAction::triggered, [=]() { QPointer dialog = new ExportGhnsDialog(m_resourceManager); dialog->exec(); }); - actionCollection()->addAction("export_ghns", exportAction); - exportAction->setIcon(QIcon::fromTheme("document-export")); + actionCollection()->addAction(QStringLiteral("export_ghns"), exportAction); + exportAction->setIcon(QIcon::fromTheme(QStringLiteral("document-export"))); KStandardAction::quit(this, SLOT(quit()), actionCollection()); - setupGUI(Keys | Save | Create, "artikulateui_editor.rc"); + setupGUI(Keys | Save | Create, QStringLiteral("artikulateui_editor.rc")); } void MainWindowEditor::showSettingsDialog() { - if (KConfigDialog::showDialog("settings")) { + if (KConfigDialog::showDialog(QStringLiteral("settings"))) { return; } - QPointer dialog = new KConfigDialog(0, "settings", Settings::self()); + QPointer dialog = new KConfigDialog(0, QStringLiteral("settings"), Settings::self()); ResourcesDialogPage *resourceDialog = new ResourcesDialogPage(m_resourceManager); SoundDeviceDialogPage *soundDialog = new SoundDeviceDialogPage(); AppearenceDialogPage *appearenceDialog = new AppearenceDialogPage(); resourceDialog->loadSettings(); soundDialog->loadSettings(); appearenceDialog->loadSettings(); - dialog->addPage(soundDialog, i18nc("@item:inmenu", "Sound Devices"), "audio-headset", i18nc("@title:tab", "Sound Device Settings"), true); - dialog->addPage(appearenceDialog, i18nc("@item:inmenu", "Fonts"), "preferences-desktop-font", i18nc("@title:tab", "Training Phrase Font"), true); - dialog->addPage(resourceDialog, i18nc("@item:inmenu", "Course Resources"), "repository", i18nc("@title:tab", "Resource Repository Settings"), true); + dialog->addPage(soundDialog, i18nc("@item:inmenu", "Sound Devices"), QStringLiteral("audio-headset"), i18nc("@title:tab", "Sound Device Settings"), true); + dialog->addPage(appearenceDialog, i18nc("@item:inmenu", "Fonts"), QStringLiteral("preferences-desktop-font"), i18nc("@title:tab", "Training Phrase Font"), true); + dialog->addPage(resourceDialog, i18nc("@item:inmenu", "Course Resources"), QStringLiteral("repository"), i18nc("@title:tab", "Resource Repository Settings"), true); connect(dialog.data(), &QDialog::accepted, resourceDialog, &ResourcesDialogPage::saveSettings); connect(dialog.data(), &QDialog::accepted, soundDialog, &SoundDeviceDialogPage::saveSettings); connect(dialog.data(), &QDialog::accepted, appearenceDialog, &AppearenceDialogPage::saveSettings); dialog->exec(); } void MainWindowEditor::save() { m_resourceManager->sync(); } void MainWindowEditor::quit() { if (queryClose()) { qApp->quit(); } } bool MainWindowEditor::queryClose() { if (!m_resourceManager->modified()) { return true; } int result = KMessageBox::warningYesNoCancel(nullptr, i18nc("@info", "The currently open course contains unsaved changes. Do you want to save them?")); switch(result) { case KMessageBox::Yes: m_resourceManager->sync(); return true; case KMessageBox::No: return true; default: return false; } } diff --git a/src/models/coursefiltermodel.cpp b/src/models/coursefiltermodel.cpp index 2862879..e0b50e2 100644 --- a/src/models/coursefiltermodel.cpp +++ b/src/models/coursefiltermodel.cpp @@ -1,109 +1,109 @@ /* * Copyright 2014 Andreas Cord-Landwehr * * 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 "coursefiltermodel.h" #include "models/coursemodel.h" #include #include #include #include "artikulate_debug.h" CourseFilterModel::CourseFilterModel(QObject* parent) : QSortFilterProxyModel(parent) , m_courseModel(nullptr) , m_view(CourseFilterModel::AllResources) { } CourseModel * CourseFilterModel::courseModel() const { return m_courseModel; } void CourseFilterModel::setView(CourseFilterModel::CourseResourceView view) { if (view == m_view) { return; } m_view = view; invalidateFilter(); emit viewChanged(); emit filteredCountChanged(); } CourseFilterModel::CourseResourceView CourseFilterModel::view() const { return m_view; } void CourseFilterModel::setCourseModel(CourseModel *courseModel) { if (courseModel == m_courseModel) { return; } if (m_courseModel) { disconnect(m_courseModel, &CourseModel::languageChanged, this, &CourseFilterModel::filteredCountChanged); disconnect(m_courseModel, &CourseModel::rowCountChanged, this, &CourseFilterModel::filteredCountChanged); } m_courseModel = courseModel; connect(m_courseModel, &CourseModel::languageChanged, this, &CourseFilterModel::filteredCountChanged); connect(m_courseModel, &CourseModel::rowCountChanged, this, &CourseFilterModel::filteredCountChanged); setSourceModel(m_courseModel); sort(0); emit courseModelChanged(); emit filteredCountChanged(); } int CourseFilterModel::filteredCount() const { return rowCount(); } bool CourseFilterModel::lessThan(const QModelIndex &left, const QModelIndex &right) const { return QSortFilterProxyModel::lessThan(left, right); } bool CourseFilterModel::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const { QModelIndex index = sourceModel()->index(sourceRow, 0, sourceParent); switch (m_view) { case CourseFilterModel::AllResources: return true; case CourseFilterModel::OnlyContributorResources: return sourceModel()->data(index, CourseModel::ContributerResourceRole).toBool(); case CourseFilterModel::OnlyGetHotNewStuffResources: return !sourceModel()->data(index, CourseModel::ContributerResourceRole).toBool(); default: return true; } } QVariant CourseFilterModel::course(int row) const { return m_courseModel->data(m_courseModel->index(row, 0), CourseModel::DataRole); -} \ No newline at end of file +} diff --git a/src/models/languageresourcemodel.cpp b/src/models/languageresourcemodel.cpp index 8e7ddcb..fcda2f7 100644 --- a/src/models/languageresourcemodel.cpp +++ b/src/models/languageresourcemodel.cpp @@ -1,277 +1,277 @@ /* * Copyright 2013 Andreas Cord-Landwehr * * 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 "languageresourcemodel.h" #include "core/language.h" #include "core/course.h" #include "core/resourcemanager.h" #include "core/resources/languageresource.h" #include "core/resources/courseresource.h" #include #include #include #include "artikulate_debug.h" LanguageResourceModel::LanguageResourceModel(QObject* parent) : QAbstractListModel(parent) , m_resourceManager(nullptr) , m_view(LanguageModel::NonEmptyGhnsOnlyLanguages) , m_signalMapper(new QSignalMapper(this)) { connect(m_signalMapper, SIGNAL(mapped(int)), SLOT(emitLanguageChanged(int))); } QHash< int, QByteArray > LanguageResourceModel::roleNames() const { QHash roles; roles[TitleRole] = "title"; roles[I18nTitleRole] = "i18nTitle"; roles[IdRole] = "id"; roles[DataRole] = "dataRole"; roles[CourseNumberRole] = "courseNumberRole"; return roles; } void LanguageResourceModel::setResourceManager(ResourceManager *resourceManager) { if (m_resourceManager == resourceManager) { return; } beginResetModel(); if (m_resourceManager) { m_resourceManager->disconnect(this); } m_resourceManager = resourceManager; if (m_resourceManager) { - connect(m_resourceManager, SIGNAL(languageResourceAboutToBeAdded(LanguageResource*,int)), - SLOT(onLanguageResourceAboutToBeAdded(LanguageResource*,int))); - connect(m_resourceManager, SIGNAL(languageResourceAdded()), - SLOT(onLanguageResourceAdded())); - connect(m_resourceManager, SIGNAL(languageResourceAboutToBeRemoved(int)), - SLOT(onLanguageResourceAboutToBeRemoved(int))); - connect(m_resourceManager, SIGNAL(languageResourceRemoved()), - SLOT(onLanguageResourceRemoved())); - connect(m_resourceManager, SIGNAL(languageCoursesChanged()), - SLOT(updateDisplayedLanguages())); + connect(m_resourceManager, &ResourceManager::languageResourceAboutToBeAdded, + this, &LanguageResourceModel::onLanguageResourceAboutToBeAdded); + connect(m_resourceManager, &ResourceManager::languageResourceAdded, + this, &LanguageResourceModel::onLanguageResourceAdded); + connect(m_resourceManager, &ResourceManager::languageResourceAboutToBeRemoved, + this, &LanguageResourceModel::onLanguageResourceAboutToBeRemoved); + connect(m_resourceManager, &ResourceManager::languageResourceRemoved, + this, &LanguageResourceModel::onLanguageResourceRemoved); + connect(m_resourceManager, &ResourceManager::languageCoursesChanged, + this, &LanguageResourceModel::updateDisplayedLanguages); } updateResources(); endResetModel(); emit resourceManagerChanged(); } ResourceManager * LanguageResourceModel::resourceManager() const { return m_resourceManager; } QVariant LanguageResourceModel::data(const QModelIndex &index, int role) const { if (!index.isValid()) { return QVariant(); } if (index.row() >= m_resources.count()) { return QVariant(); } Language * const language = m_resources.at(index.row())->language(); switch(role) { case Qt::DisplayRole: return !language->title().isEmpty() ? QVariant(language->title()): QVariant(i18nc("@item:inlistbox:", "unknown")); case Qt::ToolTipRole: return QVariant(language->title()); case TitleRole: return language->title(); case I18nTitleRole: return language->i18nTitle(); case IdRole: return language->id(); case DataRole: return QVariant::fromValue(language); case CourseNumberRole: return m_resources.count(); default: return QVariant(); } } int LanguageResourceModel::rowCount(const QModelIndex &parent) const { if (parent.isValid()) { return 0; } return m_resources.count(); } void LanguageResourceModel::onLanguageResourceAboutToBeAdded(LanguageResource *resource, int index) { if (!displayResource(resource)) { return; } beginInsertRows(QModelIndex(), index, index); m_resources.append(resource); connect(resource->language(), SIGNAL(titleChanged()), m_signalMapper, SLOT(map())); connect(resource->language(), SIGNAL(phonemesChanged()), m_signalMapper, SLOT(map())); connect(resource->language(), SIGNAL(phonemeGroupsChanged()), m_signalMapper, SLOT(map())); } void LanguageResourceModel::onLanguageResourceAdded() { updateMappings(); endInsertRows(); } void LanguageResourceModel::onLanguageResourceAboutToBeRemoved(int index) { if (!m_resourceManager) { return; } LanguageResource *originalResource = m_resourceManager->languageResources().at(index); int modelIndex = m_resources.indexOf(originalResource); if (modelIndex == -1) { qCWarning(ARTIKULATE_LOG) << "Cannot remove language from model, not registered"; return; } beginRemoveRows(QModelIndex(), modelIndex, modelIndex); originalResource->disconnect(m_signalMapper); m_resources.removeAt(modelIndex); } void LanguageResourceModel::onLanguageResourceRemoved() { endRemoveRows(); } void LanguageResourceModel::emitLanguageChanged(int row) { emit languageChanged(row); emit dataChanged(index(row, 0), index(row, 0)); } QVariant LanguageResourceModel::headerData(int section, Qt::Orientation orientation, int role) const { if (role != Qt::DisplayRole) { return QVariant(); } if (orientation == Qt::Vertical) { return QVariant(section + 1); } return QVariant(i18nc("@title:column", "Language")); } void LanguageResourceModel::setView(LanguageModel::LanguageResourceView view) { if (m_view == view) { return; } emit beginResetModel(); m_view = view; updateResources(); emit endResetModel(); } void LanguageResourceModel::updateDisplayedLanguages() { emit beginResetModel(); updateResources(); emit endResetModel(); } LanguageModel::LanguageResourceView LanguageResourceModel::view() const { return m_view; } bool LanguageResourceModel::displayResource(LanguageResource* resource) const { if (m_view == LanguageModel::AllLanguages) { return true; } // otherwise compute data needed for decision QList courses = m_resourceManager->courseResources(resource->language()); int contribCount = 0; if (m_view == LanguageModel::NonEmptyLanguages && courses.count() > 0) { return true; } // compute data for determining whether language shall be shown or not foreach (CourseResource *course, courses) { if (course->isContributorResource()) { ++contribCount; } } if (m_view == LanguageModel::NonEmptyContributorOnlyResources && contribCount > 0) { return true; } if (m_view == LanguageModel::NonEmptyGhnsOnlyLanguages && courses.count() - contribCount > 0) { return true; } return false; } void LanguageResourceModel::updateResources() { if (!m_resourceManager) { return; } m_resources.clear(); QList resources = m_resourceManager->languageResources(); foreach (LanguageResource *language, resources) { if (displayResource(language)) { m_resources.append(language); } } updateMappings(); } void LanguageResourceModel::updateMappings() { int languages = m_resources.count(); for (int i = 0; i < languages; i++) { m_signalMapper->setMapping(m_resources.at(i), i); } } diff --git a/src/models/learningprogressmodel.cpp b/src/models/learningprogressmodel.cpp index ee8eabc..c21bad8 100644 --- a/src/models/learningprogressmodel.cpp +++ b/src/models/learningprogressmodel.cpp @@ -1,167 +1,167 @@ /* * Copyright 2013 Andreas Cord-Landwehr * * 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 "learningprogressmodel.h" #include "core/trainingsession.h" #include #include #include "artikulate_debug.h" LearningProgressModel::LearningProgressModel(QObject *parent) : QAbstractTableModel(parent) , m_session(nullptr) { } QHash< int, QByteArray > LearningProgressModel::roleNames() const { QHash roles; roles[TitleRole] = "title"; roles[DataRole] = "dataRole"; return roles; } int LearningProgressModel::rowCount(const QModelIndex& parent) const { Q_UNUSED(parent) if (m_session == 0) { return 0; } // row for every try, plus extra column for 0st and (max + 1)st try return m_session->maximumTries() + 2; } int LearningProgressModel::columnCount(const QModelIndex &parent) const { Q_UNUSED(parent) // we have 4 different word types return 4; } TrainingSession * LearningProgressModel::session() const { return m_session; } void LearningProgressModel::setSession(TrainingSession *session) { if (m_session == session) { return; } beginResetModel(); if (m_session) { m_session->disconnect(this); } m_session = session; connect(session, SIGNAL(finished()), this, SLOT(updateResults())); connect(session, SIGNAL(finished()), this, SIGNAL(maximumTriesChanged())); endResetModel(); emit sessionChanged(); } int LearningProgressModel::maximumTries() const { if (!m_session) { return 0; } return m_session->maximumTries(); } int LearningProgressModel::maximumPhrasesPerTry() const { if (!m_session) { return 0; } return m_session->maximumPhrasesPerTry(); } QVariant LearningProgressModel::data(const QModelIndex &index, int role) const { if (!index.isValid()) { qCWarning(ARTIKULATE_LOG) << "Invalid index requested"; return QVariant(); } // column tooltip is number of needed tries // first try is 0 if (role == Qt::ToolTipRole) { return QVariant(index.row()); } - // otherwise we only suppert displayrole + // otherwise we only support displayrole if (role != Qt::DisplayRole) { return QVariant(); } // handle special rows if (index.row() == 0 || index.row() == m_session->maximumTries() + 1) { return QVariant(0); } // normal tries int tries = index.row(); switch (index.column()) { case Phrase::Word: return QVariant(m_session->numberPhrasesGroupedByTries(TrainingSession::Word, tries)); case Phrase::Sentence: return QVariant(m_session->numberPhrasesGroupedByTries(TrainingSession::Sentence, tries)); case Phrase::Expression: return QVariant(m_session->numberPhrasesGroupedByTries(TrainingSession::Expression, tries)); case Phrase::Paragraph: return QVariant(m_session->numberPhrasesGroupedByTries(TrainingSession::Paragraph, tries)); default: return QVariant(); } } QVariant LearningProgressModel::headerData(int section, Qt::Orientation orientation, int role) const { if (role != Qt::DisplayRole) { return QVariant(); } if (orientation == Qt::Vertical) { return QAbstractTableModel::headerData(section, orientation, role); } switch (section) { case Phrase::Word: return QVariant(i18n("Words")); case Phrase::Sentence: return QVariant(i18n("Sentences")); case Phrase::Expression: return QVariant(i18n("Expressions")); case Phrase::Paragraph: return QVariant(i18n("Paragraphs")); default: return QVariant(); } } void LearningProgressModel::updateResults() { beginResetModel(); //nothing to do endResetModel(); } diff --git a/src/models/phonemegroupmodel.cpp b/src/models/phonemegroupmodel.cpp index 49b0a89..3a03f31 100644 --- a/src/models/phonemegroupmodel.cpp +++ b/src/models/phonemegroupmodel.cpp @@ -1,172 +1,172 @@ /* * Copyright 2013 Andreas Cord-Landwehr * * 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 "phonemegroupmodel.h" #include "core/course.h" #include "core/unit.h" #include "core/phonemegroup.h" #include #include #include #include "artikulate_debug.h" PhonemeGroupModel::PhonemeGroupModel(QObject *parent) : QAbstractListModel(parent) , m_course(nullptr) , m_signalMapper(new QSignalMapper(this)) { connect(m_signalMapper, SIGNAL(mapped(int)), SLOT(emitPhonemeGroupChanged(int))); } QHash< int, QByteArray > PhonemeGroupModel::roleNames() const { QHash roles; roles[TitleRole] = "title"; roles[IdRole] = "id"; roles[DataRole] = "dataRole"; return roles; } void PhonemeGroupModel::setCourse(Course *course) { if (m_course == course) { return; } beginResetModel(); if (m_course) { m_course->disconnect(this); } m_course = course; if (m_course) { - connect(m_course, SIGNAL(phonemeGroupAboutToBeAdded(PhonemeGroup*,int)), SLOT(onPhonemeGroupAboutToBeAdded(PhonemeGroup*,int))); - connect(m_course, SIGNAL(phonemeGroupAdded()), SLOT(onPhonemeGroupAdded())); - connect(m_course, SIGNAL(phonemeGroupAboutToBeRemoved(int,int)), SLOT(onPhonemeGroupsAboutToBeRemoved(int,int))); - connect(m_course, SIGNAL(phonemeGroupRemoved()), SLOT(onPhonemeGroupsRemoved())); + connect(m_course, &Course::phonemeGroupAboutToBeAdded, this, &PhonemeGroupModel::onPhonemeGroupAboutToBeAdded); + connect(m_course, &Course::phonemeGroupAdded, this, &PhonemeGroupModel::onPhonemeGroupAdded); + connect(m_course, &Course::phonemeGroupAboutToBeRemoved, this, &PhonemeGroupModel::onPhonemeGroupsAboutToBeRemoved); + connect(m_course, &Course::phonemeGroupRemoved, this, &PhonemeGroupModel::onPhonemeGroupsRemoved); } endResetModel(); emit courseChanged(); } Course * PhonemeGroupModel::course() const { return m_course; } QVariant PhonemeGroupModel::data(const QModelIndex& index, int role) const { Q_ASSERT(m_course); if (!index.isValid()) { return QVariant(); } if (index.row() >= m_course->phonemeGroupList().count()) { return QVariant(); } PhonemeGroup * const phonemeGroup = m_course->phonemeGroupList().at(index.row()); switch(role) { case Qt::DisplayRole: return !phonemeGroup->title().isEmpty()? QVariant(phonemeGroup->title()): QVariant(i18nc("@item:inlistbox:", "unknown")); case Qt::ToolTipRole: return QVariant(phonemeGroup->title()); case TitleRole: return phonemeGroup->title(); case IdRole: return phonemeGroup->id(); case DataRole: return QVariant::fromValue(phonemeGroup); default: return QVariant(); } } int PhonemeGroupModel::rowCount(const QModelIndex& parent) const { if (!m_course) { return 0; } if (parent.isValid()) { return 0; } return m_course->phonemeGroupList().count(); } void PhonemeGroupModel::onPhonemeGroupAboutToBeAdded(PhonemeGroup *phonemeGroup, int index) { connect(phonemeGroup, SIGNAL(titleChanged()), m_signalMapper, SLOT(map())); //TODO add missing signals beginInsertRows(QModelIndex(), index, index); } void PhonemeGroupModel::onPhonemeGroupAdded() { updateMappings(); endInsertRows(); } void PhonemeGroupModel::onPhonemeGroupsAboutToBeRemoved(int first, int last) { beginRemoveRows(QModelIndex(), first, last); } void PhonemeGroupModel::onPhonemeGroupsRemoved() { endRemoveRows(); } void PhonemeGroupModel::emitPhonemeGroupChanged(int row) { emit phonemeGroupChanged(row); emit dataChanged(index(row, 0), index(row, 0)); } QVariant PhonemeGroupModel::headerData(int section, Qt::Orientation orientation, int role) const { if (role != Qt::DisplayRole) { return QVariant(); } if (orientation == Qt::Vertical) { return QVariant(section + 1); } return QVariant(i18nc("@title:column", "Phoneme Group")); } void PhonemeGroupModel::updateMappings() { int phonemeGroups = m_course->phonemeGroupList().count(); for (int i = 0; i < phonemeGroups; i++) { m_signalMapper->setMapping(m_course->phonemeGroupList().at(i), i); } } diff --git a/src/models/phonemeunitmodel.cpp b/src/models/phonemeunitmodel.cpp index 90de0c3..1770a45 100644 --- a/src/models/phonemeunitmodel.cpp +++ b/src/models/phonemeunitmodel.cpp @@ -1,209 +1,209 @@ /* * Copyright 2013 Andreas Cord-Landwehr * * 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 "phonemeunitmodel.h" #include "core/course.h" #include "core/unit.h" #include "core/phonemegroup.h" #include #include #include #include "artikulate_debug.h" PhonemeUnitModel::PhonemeUnitModel(QObject *parent) : QAbstractListModel(parent) , m_course(nullptr) , m_phonemeGroup(nullptr) , m_signalMapper(new QSignalMapper(this)) { connect(m_signalMapper, SIGNAL(mapped(int)), SLOT(emitUnitChanged(int))); - connect(this, SIGNAL(phonemeGroupChanged()), this, SIGNAL(countChanged())); - connect(this, SIGNAL(courseChanged()), this, SIGNAL(countChanged())); + connect(this, &PhonemeUnitModel::phonemeGroupChanged, this, &PhonemeUnitModel::countChanged); + connect(this, &PhonemeUnitModel::courseChanged, this, &PhonemeUnitModel::countChanged); } QHash< int, QByteArray > PhonemeUnitModel::roleNames() const { QHash roles; roles[TitleRole] = "title"; roles[NumberPhrasesRole] = "numberPhrases"; roles[IdRole] = "id"; roles[DataRole] = "dataRole"; roles[PhonemeGroupRole] = "phonemeGroupRole"; return roles; } void PhonemeUnitModel::setCourse(Course *course) { if (m_course == course) { return; } beginResetModel(); if (m_course) { m_course->disconnect(this); } m_course = course; if (m_course) { - connect(m_course, SIGNAL(phonemeGroupAboutToBeAdded(PhonemeGroup*,int)), SLOT(onUnitAboutToBeAdded(PhonemeGroup*,int))); - connect(m_course, SIGNAL(phonemeGroupAdded()), SLOT(onUnitAdded())); - connect(m_course, SIGNAL(phonemeGroupAboutToBeRemoved(int,int)), SLOT(onUnitsAboutToBeRemoved(int,int))); - connect(m_course, SIGNAL(phonemeGroupRemoved()), SLOT(onUnitsRemoved())); + connect(m_course, &Course::phonemeGroupAboutToBeAdded, this, &PhonemeUnitModel::onUnitAboutToBeAdded); + connect(m_course, &Course::phonemeGroupAdded, this, &PhonemeUnitModel::onUnitAdded); + connect(m_course, &Course::phonemeGroupAboutToBeRemoved, this, &PhonemeUnitModel::onUnitsAboutToBeRemoved); + connect(m_course, &Course::phonemeGroupRemoved, this, &PhonemeUnitModel::onUnitsRemoved); } endResetModel(); emit courseChanged(); } void PhonemeUnitModel::setPhonemeGroup(PhonemeGroup* phonemeGroup) { if (m_phonemeGroup == phonemeGroup) { return; } beginResetModel(); m_phonemeGroup = phonemeGroup; endResetModel(); emit phonemeGroupChanged(); } PhonemeGroup* PhonemeUnitModel::phonemeGroup() const { return m_phonemeGroup; } Course * PhonemeUnitModel::course() const { return m_course; } QVariant PhonemeUnitModel::data(const QModelIndex& index, int role) const { Q_ASSERT(m_course); Q_ASSERT(m_phonemeGroup); if (!index.isValid()) { return QVariant(); } if (index.row() >= m_course->phonemeUnitList(m_phonemeGroup).count()) { return QVariant(); } Unit * const unit = m_course->phonemeUnitList(m_phonemeGroup).at(index.row()); switch(role) { case Qt::DisplayRole: return !unit->title().isEmpty()? QVariant(unit->title()): QVariant(i18nc("@item:inlistbox:", "unknown")); case Qt::ToolTipRole: return QVariant(unit->title()); case TitleRole: return unit->title(); case NumberPhrasesRole: return unit->phraseList().count(); case IdRole: return unit->id(); case DataRole: return QVariant::fromValue(unit); case PhonemeGroupRole: return QVariant::fromValue(m_course->phonemeGroup(unit)); default: return QVariant(); } } int PhonemeUnitModel::rowCount(const QModelIndex& parent) const { if (!m_course) { return 0; } if (parent.isValid()) { return 0; } return m_course->phonemeUnitList(m_phonemeGroup).count(); } void PhonemeUnitModel::onUnitAboutToBeAdded(PhonemeGroup *phonemeGroup, int index) { connect(phonemeGroup, SIGNAL(titleChanged()), m_signalMapper, SLOT(map())); //TODO add missing signals beginInsertRows(QModelIndex(), index, index); } void PhonemeUnitModel::onUnitAdded() { updateMappings(); endInsertRows(); emit countChanged(); } void PhonemeUnitModel::onUnitsAboutToBeRemoved(int first, int last) { beginRemoveRows(QModelIndex(), first, last); } void PhonemeUnitModel::onUnitsRemoved() { endRemoveRows(); emit countChanged(); } void PhonemeUnitModel::emitUnitChanged(int row) { emit unitChanged(row); emit dataChanged(index(row, 0), index(row, 0)); } QVariant PhonemeUnitModel::headerData(int section, Qt::Orientation orientation, int role) const { if (role != Qt::DisplayRole) { return QVariant(); } if (orientation == Qt::Vertical) { return QVariant(section + 1); } return QVariant(i18nc("@title:column", "Phoneme Unit")); } int PhonemeUnitModel::count() const { if (!m_course || !m_phonemeGroup) { return 0; } return m_course->phonemeUnitList(m_phonemeGroup).count(); } void PhonemeUnitModel::updateMappings() { int units = m_course->phonemeUnitList(m_phonemeGroup).count(); for (int i = 0; i < units; i++) { m_signalMapper->setMapping(m_course->phonemeUnitList(m_phonemeGroup).at(i), i); } } diff --git a/src/models/phraselistmodel.cpp b/src/models/phraselistmodel.cpp index 53d34a4..64ee479 100644 --- a/src/models/phraselistmodel.cpp +++ b/src/models/phraselistmodel.cpp @@ -1,210 +1,210 @@ /* * Copyright 2013 Andreas Cord-Landwehr * * 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 "phraselistmodel.h" #include "core/unit.h" #include "core/phrase.h" #include #include #include PhraseListModel::PhraseListModel(QObject *parent) : QAbstractListModel(parent) , m_unit(nullptr) , m_signalMapper(new QSignalMapper(this)) { connect(m_signalMapper, SIGNAL(mapped(int)), SLOT(emitPhraseChanged(int))); // connect all phrase number operations to single signal - connect(this, SIGNAL(typeChanged()), this, SIGNAL(countChanged())); - connect(this, SIGNAL(unitChanged()), this, SIGNAL(countChanged())); + connect(this, &PhraseListModel::typeChanged, this, &PhraseListModel::countChanged); + connect(this, &PhraseListModel::unitChanged, this, &PhraseListModel::countChanged); } QHash< int, QByteArray > PhraseListModel::roleNames() const { QHash roles; roles[TextRole] = "text"; roles[SoundFileRole] = "soundFile"; roles[IdRole] = "id"; roles[TypeRole] = "type"; roles[ExcludedRole] = "excludedRole"; roles[DataRole] = "dataRole"; return roles; } void PhraseListModel::setUnit(Unit *unit) { if (m_unit == unit) { return; } beginResetModel(); if (m_unit) { m_unit->disconnect(this); foreach (Phrase *phrase, m_unit->phraseList()) { phrase->disconnect(this); } } m_unit = unit; if (m_unit) { // initial setting of signal mappings - connect(m_unit, SIGNAL(phraseAboutToBeAdded(Phrase*,int)), SLOT(onPhraseAboutToBeAdded(Phrase*,int))); + connect(m_unit, &Unit::phraseAboutToBeAdded, this, &PhraseListModel::onPhraseAboutToBeAdded); connect(m_unit, SIGNAL(phraseAdded()), SLOT(onPhraseAdded())); - connect(m_unit, SIGNAL(phraseAboutToBeRemoved(int,int)), SLOT(onPhrasesAboutToBeRemoved(int,int))); + connect(m_unit, &Unit::phraseAboutToBeRemoved, this, &PhraseListModel::onPhrasesAboutToBeRemoved); connect(m_unit, SIGNAL(phraseRemoved()), SLOT(onPhrasesRemoved())); // insert and connect all already existing phrases int phrases = m_unit->phraseList().count(); for (int i=0; i < phrases; ++i) { onPhraseAboutToBeAdded(m_unit->phraseList().at(i), i); endInsertRows(); emit countChanged(); } updateMappings(); } // emit done endResetModel(); emit unitChanged(); } Unit * PhraseListModel::unit() const { return m_unit; } QVariant PhraseListModel::data(const QModelIndex &index, int role) const { Q_ASSERT(m_unit); if (!index.isValid()) { return QVariant(); } if (index.row() >= m_unit->phraseList().count()) { return QVariant(); } Phrase * const phrase = m_unit->phraseList().at(index.row()); switch(role) { case Qt::DisplayRole: return !phrase->text().isEmpty()? QVariant(phrase->text()): QVariant(i18nc("@item:inlistbox:", "unknown")); case Qt::ToolTipRole: return QVariant(phrase->text()); case TextRole: return phrase->text(); case SoundFileRole: return phrase->sound(); case IdRole: return phrase->id(); case TypeRole: return phrase->type(); case ExcludedRole: return phrase->isExcluded(); case DataRole: return QVariant::fromValue(phrase); default: return QVariant(); } } int PhraseListModel::rowCount(const QModelIndex &parent) const { if (!m_unit) { return 0; } if (parent.isValid()) { return 0; } return m_unit->phraseList().count(); } void PhraseListModel::onPhraseAboutToBeAdded(Phrase *phrase, int index) { connect(phrase, SIGNAL(textChanged()), m_signalMapper, SLOT(map())); connect(phrase, SIGNAL(typeChanged()), m_signalMapper, SLOT(map())); connect(phrase, SIGNAL(excludedChanged()), m_signalMapper, SLOT(map())); beginInsertRows(QModelIndex(), index, index); } void PhraseListModel::onPhraseAdded() { updateMappings(); endInsertRows(); emit countChanged(); } void PhraseListModel::onPhrasesAboutToBeRemoved(int first, int last) { beginRemoveRows(QModelIndex(), first, last); } void PhraseListModel::onPhrasesRemoved() { endRemoveRows(); emit countChanged(); } void PhraseListModel::emitPhraseChanged(int row) { beginResetModel(); endResetModel(); //FIXME very inefficient, but workaround to force new filtering in phrasefiltermodel // to exclude possible new excluded phrases emit phraseChanged(row); emit dataChanged(index(row, 0), index(row, 0)); } QVariant PhraseListModel::headerData(int section, Qt::Orientation orientation, int role) const { if (role != Qt::DisplayRole) { return QVariant(); } if (orientation == Qt::Vertical) { return QVariant(section + 1); } return QVariant(i18nc("@title:column", "Phrase")); } int PhraseListModel::count() const { if (!m_unit) { return 0; } return m_unit->phraseList().count(); } void PhraseListModel::updateMappings() { if (!m_unit) { return; } int phrases = m_unit->phraseList().count(); for (int i = 0; i < phrases; ++i) { m_signalMapper->setMapping(m_unit->phraseList().at(i), i); } } diff --git a/src/models/profilemodel.cpp b/src/models/profilemodel.cpp index bbd25bb..aa88e0d 100644 --- a/src/models/profilemodel.cpp +++ b/src/models/profilemodel.cpp @@ -1,175 +1,175 @@ /* * Copyright 2013 Andreas Cord-Landwehr * * 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 "profilemodel.h" #include "liblearnerprofile/src/profilemanager.h" #include "liblearnerprofile/src/learner.h" #include #include #include #include "artikulate_debug.h" using namespace LearnerProfile; ProfileModel::ProfileModel(QObject *parent) : QAbstractListModel(parent) , m_profileManager(nullptr) , m_signalMapper(new QSignalMapper(this)) { connect(m_signalMapper, SIGNAL(mapped(int)), SLOT(emitProfileChanged(int))); } QHash< int, QByteArray > ProfileModel::roleNames() const { QHash roles; roles[IdRole] = "id"; roles[NameRole] = "name"; roles[DataRole] = "dataRole"; return roles; } void ProfileModel::setProfileManager(ProfileManager *profileManager) { if (m_profileManager == profileManager) { return; } beginResetModel(); if (m_profileManager) { m_profileManager->disconnect(this); foreach (Learner *learner, m_profileManager->profiles()) { learner->disconnect(this); } } m_profileManager = profileManager; if (m_profileManager) { // initial setting of signal mappings - connect(m_profileManager, SIGNAL(profileAdded(Learner*,int)), SLOT(onProfileAdded(Learner*,int))); + connect(m_profileManager, &ProfileManager::profileAdded, this, &ProfileModel::onProfileAdded); connect(m_profileManager, SIGNAL(profileAboutToBeRemoved(int)), SLOT(onProfilesAboutToBeRemoved(int))); // insert and connect all already existing profiles int profiles = m_profileManager->profiles().count(); for (int i = 0; i < profiles; ++i) { onProfileAdded(m_profileManager->profiles().at(i), i); } updateMappings(); } endResetModel(); } ProfileManager * ProfileModel::profileManager() const { return m_profileManager; } QVariant ProfileModel::data(const QModelIndex &index, int role) const { Q_ASSERT(m_profileManager); if (!index.isValid()) { return QVariant(); } if (index.row() >= m_profileManager->profiles().count()) { return QVariant(); } Learner * const learner = m_profileManager->profiles().at(index.row()); switch(role) { case Qt::DisplayRole: return !learner->name().isEmpty()? QVariant(learner->name()): QVariant(i18nc("@item:inlistbox:", "unknown")); case Qt::ToolTipRole: return QVariant(learner->name()); case IdRole: return learner->identifier(); case NameRole: return learner->name(); case DataRole: return QVariant::fromValue(learner); default: return QVariant(); } } int ProfileModel::rowCount(const QModelIndex &parent) const { if (!m_profileManager) { return 0; } if (parent.isValid()) { return 0; } return m_profileManager->profiles().count(); } void ProfileModel::onProfileAdded(Learner *learner, int index) { connect(learner, SIGNAL(nameChanged()), m_signalMapper, SLOT(map())); connect(learner, SIGNAL(identifierChanged()), m_signalMapper, SLOT(map())); beginInsertRows(QModelIndex(), index, index); updateMappings(); endInsertRows(); } void ProfileModel::onProfileAboutToBeRemoved(int index) { beginRemoveRows(QModelIndex(), index, index); endRemoveRows(); } void ProfileModel::emitProfileChanged(int row) { beginResetModel(); endResetModel(); //FIXME very inefficient, but workaround to force new filtering in phrasefiltermodel // to exclude possible new excluded phrases emit profileChanged(row); emit dataChanged(index(row, 0), index(row, 0)); } QVariant ProfileModel::headerData(int section, Qt::Orientation orientation, int role) const { if (role != Qt::DisplayRole) { return QVariant(); } if (orientation == Qt::Vertical) { return QVariant(section + 1); } return QVariant(i18nc("@title:column", "Profile")); } void ProfileModel::updateMappings() { if (!m_profileManager) { return; } int profiles = m_profileManager->profiles().count(); for (int i = 0; i < profiles; ++i) { m_signalMapper->setMapping(m_profileManager->profiles().at(i), i); } } diff --git a/src/models/skeletonmodel.cpp b/src/models/skeletonmodel.cpp index 498ba1d..9fba19d 100644 --- a/src/models/skeletonmodel.cpp +++ b/src/models/skeletonmodel.cpp @@ -1,185 +1,185 @@ /* * Copyright 2013 Andreas Cord-Landwehr * * 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 "skeletonmodel.h" #include "core/course.h" #include "core/resourcemanager.h" #include "core/skeleton.h" #include "core/resources/skeletonresource.h" #include #include #include #include "artikulate_debug.h" SkeletonModel::SkeletonModel(QObject *parent) : QAbstractListModel(parent) , m_resourceManager(nullptr) , m_signalMapper(new QSignalMapper(this)) { connect(m_signalMapper, SIGNAL(mapped(int)), SLOT(emitSkeletonChanged(int))); } QHash< int, QByteArray > SkeletonModel::roleNames() const { QHash roles; roles[TitleRole] = "title"; roles[DescriptionRole] = "description"; roles[IdRole] = "id"; roles[DataRole] = "dataRole"; return roles; } void SkeletonModel::setResourceManager(ResourceManager *resourceManager) { if (m_resourceManager == resourceManager) { return; } beginResetModel(); if (m_resourceManager) { m_resourceManager->disconnect(this); } m_resourceManager = resourceManager; if (m_resourceManager) { - connect(m_resourceManager, SIGNAL(skeletonAboutToBeAdded(Course*,int)), SLOT(onSkeletonAboutToBeAdded(Course*,int))); - connect(m_resourceManager, SIGNAL(skeletonAdded()), SLOT(onSkeletonAdded())); - connect(m_resourceManager, SIGNAL(skeletonAboutToBeRemoved(int,int)), SLOT(onSkeletonsAboutToBeRemoved(int,int))); - connect(m_resourceManager, SIGNAL(skeletonRemoved()), SLOT(onSkeletonsRemoved())); + connect(m_resourceManager, &ResourceManager::skeletonAboutToBeAdded, this, &SkeletonModel::onSkeletonAboutToBeAdded); + connect(m_resourceManager, &ResourceManager::skeletonAdded, this, &SkeletonModel::onSkeletonAdded); + connect(m_resourceManager, &ResourceManager::skeletonAboutToBeRemoved, this, &SkeletonModel::onSkeletonsAboutToBeRemoved); + connect(m_resourceManager, &ResourceManager::skeletonRemoved, this, &SkeletonModel::onSkeletonsRemoved); } endResetModel(); emit resourceManagerChanged(); } ResourceManager * SkeletonModel::resourceManager() const { return m_resourceManager; } QVariant SkeletonModel::data(const QModelIndex& index, int role) const { if (!index.isValid()) { return QVariant(); } if (index.row() >= m_resourceManager->skeletonResources().count()) { return QVariant(); } Skeleton * const skeleton = m_resourceManager->skeletonResources().at(index.row())->skeleton(); switch(role) { case Qt::DisplayRole: return !skeleton->title().isEmpty() ? QVariant(skeleton->title()): QVariant(i18nc("@item:inlistbox:", "unknown")); case Qt::ToolTipRole: return QVariant(skeleton->title()); case TitleRole: return skeleton->title(); case DescriptionRole: return skeleton->description(); case IdRole: return skeleton->id(); case DataRole: return QVariant::fromValue(skeleton); default: return QVariant(); } } int SkeletonModel::rowCount(const QModelIndex &parent) const { if (!m_resourceManager) { return 0; } if (parent.isValid()) { return 0; } return m_resourceManager->skeletonResources().count(); } void SkeletonModel::onSkeletonAboutToBeAdded(Course *skeleton, int index) { connect(skeleton, SIGNAL(titleChanged()), m_signalMapper, SLOT(map())); //TODO add missing signals beginInsertRows(QModelIndex(), index, index); } void SkeletonModel::onSkeletonAdded() { updateMappings(); endInsertRows(); emit countChanged(); } void SkeletonModel::onSkeletonsAboutToBeRemoved(int first, int last) { beginRemoveRows(QModelIndex(), first, last); } void SkeletonModel::onSkeletonsRemoved() { endRemoveRows(); emit countChanged(); } void SkeletonModel::emitSkeletonChanged(int row) { emit skeletonChanged(row); emit dataChanged(index(row, 0), index(row, 0)); } QVariant SkeletonModel::headerData(int section, Qt::Orientation orientation, int role) const { if (role != Qt::DisplayRole) { return QVariant(); } if (orientation == Qt::Vertical) { return QVariant(section + 1); } return QVariant(i18nc("@title:column", "Skeleton")); } int SkeletonModel::count() const { return m_resourceManager->skeletonResources().count(); } void SkeletonModel::updateMappings() { int skeletons = m_resourceManager->skeletonResources().count(); for (int i = 0; i < skeletons; ++i) { m_signalMapper->setMapping(m_resourceManager->skeletonResources().at(i)->skeleton(), i); } } QVariant SkeletonModel::skeleton(int row) const { return data(index(row, 0), SkeletonModel::DataRole); } diff --git a/src/qml/CourseSwitcher.qml b/src/qml/CourseSwitcher.qml index d74be19..749f517 100644 --- a/src/qml/CourseSwitcher.qml +++ b/src/qml/CourseSwitcher.qml @@ -1,125 +1,125 @@ /* * Copyright 2013-2015 Andreas Cord-Landwehr * * 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 . */ import QtQuick 2.1 import QtQuick.Controls 1.2 import artikulate 1.0 Item { id: root property ResourceManager resourceManager property Language language property Course selectedCourse property int view : CourseFilterModel.AllResources signal courseSelected(variant course) width: 300 height: Math.max(buttonLeft.height, courseView.height) Connections { target: courseModel onLanguageChanged: { - if (courseModel.course(courseView.currentIndex) != null) { + if (courseModel.course(courseView.currentIndex) !== null) { selectedCourse = courseModel.course(courseView.currentIndex) courseSelected(selectedCourse) } } } Component.onCompleted: { courseView.currentIndex = 0 } Component { id: itemDelegate Row { width: root.width - buttonLeft.width - buttonRight.width height: theme.mediumIconSize spacing: 10 Icon { id: icon icon: "artikulate-course" width: theme.smallMediumIconSize height: theme.smallMediumIconSize anchors.verticalCenter: parent.verticalCenter } Label { id: courseTitleLabel anchors.verticalCenter: parent.verticalCenter height: paintedHeight font.pointSize: theme.fontPointSize text: model.title } } } Row { spacing : 10 ToolButton { id : buttonLeft iconName: "arrow-left" enabled: courseView.currentIndex > 0 onClicked: { languageView.decrementCurrentIndex() } } ListView { id: courseView width: root.width - buttonLeft.width - buttonRight.width height: theme.mediumIconSize clip: true snapMode: ListView.SnapToItem orientation: ListView.Horizontal model: CourseFilterModel { view: root.view courseModel: CourseModel { id: courseModel resourceManager: root.resourceManager language: root.language } } onCurrentIndexChanged: { - if (courseModel.language == null) { + if (courseModel.language === null) { return; } - if (courseModel.course(currentIndex) != null) { + if (courseModel.course(currentIndex) !== null) { selectedCourse = courseModel.course(currentIndex) courseSelected(selectedCourse) } } delegate: itemDelegate } ToolButton { id: buttonRight enabled: courseView.currentIndex < courseView.count - 2 iconName: "arrow-right" onClicked: { courseView.incrementCurrentIndex() } } } } diff --git a/src/qml/Editor.qml b/src/qml/Editor.qml index 1351e14..5160b1b 100644 --- a/src/qml/Editor.qml +++ b/src/qml/Editor.qml @@ -1,265 +1,265 @@ /* * Copyright 2013-2015 Andreas Cord-Landwehr * * 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 . */ import QtQuick 2.1 import QtQuick.Controls 1.4 import QtQuick.Layouts 1.2 import QtQml.Models 2.2 import artikulate 1.0 Item { id: root width: 400 //parent.width height: 400 //parent.height Item { id: theme property string backgroundColor: "#ffffff" property int smallIconSize: 18 property int smallMediumIconSize: 22 property int mediumIconSize: 32 property int fontPointSize: 11 } LanguageModel { id: languageModel view: LanguageModel.AllLanguages resourceModel: LanguageResourceModel { resourceManager: g_resourceManager } } CourseModel { id: courseModel resourceManager: g_resourceManager language: editorSession.language } UnitModel { id: selectedUnitModel course: editorSession.course } ColumnLayout { id: main anchors { fill: parent topMargin: 20 rightMargin: 20 bottomMargin: 20 leftMargin: 20 } spacing: 10 RowLayout { Label { visible: !g_resourceManager.isRepositoryManager text: i18n("no repository set") color: "red" } Label { text: i18n("Course Prototype:") } ComboBox { Layout.minimumWidth: 300 model: SkeletonModel { id: skeletonModel resourceManager: g_resourceManager } textRole: "title" onCurrentIndexChanged: { editorSession.skeleton = skeletonModel.skeleton(currentIndex) } } Button { id: buttonEditSkeleton Layout.minimumWidth: 200 text: i18n("Edit Prototype") iconName: "code-class" checkable: true onClicked: editorSession.editSkeleton = checked } Item { Layout.fillWidth: true } Button { id: buttonSyncFromSkeleton enabled: !buttonEditSkeleton.checked Layout.minimumWidth: 200 text: i18n("Sync Prototype") tooltip: i18n("Update the course with elements from prototype.") iconName: "view-refresh" onClicked: editorSession.updateCourseFromSkeleton() } CheckBox { Layout.alignment: Qt.AlignRight enabled: false//FIXME for now deactivating non-skeleton mode text: i18n("Prototype Mode") checked: editorSession.skeletonMode onClicked: { editorSession.skeletonMode = !editorSession.skeletonMode } } } RowLayout { id: languageRow Label { text: i18n("Language:") } ComboBox { Layout.minimumWidth: 200 Layout.fillWidth: true enabled: !buttonEditSkeleton.checked model: languageModel textRole: "i18nTitle" onCurrentIndexChanged: { editorSession.language = languageModel.language(currentIndex) } } } RowLayout { id: courseRow visible: { if (buttonEditSkeleton.checked) { return false } - if (editorSession.skeletonMode && editorSession.course != null) { + if (editorSession.skeletonMode && editorSession.course !== null) { return false } if (!editorSession.skeletonMode - && editorSession.language != null - && editorSession.course != null + && editorSession.language !== null + && editorSession.course !== null ) { return false } return true } Label { text: i18n("There is no course in the selected language.") } ComboBox { // course selection only necessary when we do not edit skeleton derived course id: comboCourse visible: !editorSession.skeletonMode Layout.fillWidth: true model: courseModel textRole: "title" onCurrentIndexChanged: { if (courseModel.course(currentIndex)) { editorSession.course = courseModel.course(currentIndex) } } onVisibleChanged: { if (visible && courseModel.course(currentIndex)) { editorSession.course = courseModel.course(currentIndex) } } } Button { text: i18n("Create Course") iconName: "journal-new" onClicked: { editorSession.course = g_resourceManager.createCourse(editorSession.language, editorSession.skeleton) } } Item { Layout.fillHeight: true } //dummy } RowLayout { id: mainRow - visible: editorSession.course != null + visible: editorSession.course !== null Layout.fillHeight: true ColumnLayout { ScrollView { Layout.minimumWidth: Math.floor(main.width * 0.3) Layout.fillHeight: true TreeView { id: phraseTree height: { mainRow.height - (newUnitButton.visible ? newUnitButton.height : 0) - 10 } width: Math.floor(main.width * 0.3) - 20 TableViewColumn { title: i18n("Units & Phrases") role: "text" } model: PhraseModel { id: phraseModel course: editorSession.course } selection: ItemSelectionModel { model: phraseTree.model } itemDelegate: Item { Text { anchors.verticalCenter: parent.verticalCenter color: styleData.textColor elide: styleData.elideMode text: styleData.value } } onClicked: { if (phraseModel.isPhrase(index)) { editorSession.phrase = phraseModel.phrase(index) } else { editorSession.phrase = null editorSession.unit = phraseModel.unit(index) } } Connections { target: editorSession onPhraseChanged: { - if (editorSession.phrase == null) { + if (editorSession.phrase === null) { return } phraseTree.expand(phraseModel.indexUnit(editorSession.phrase.unit)) phraseTree.selection.setCurrentIndex( phraseModel.indexPhrase(editorSession.phrase), ItemSelectionModel.ClearAndSelect) } } } } Button { // add units only if skeleton id: newUnitButton visible: !editorSession.skeletonMode || editorSession.editSkeleton iconName: "list-add" text: i18n("New Unit") onClicked: phraseModel.course.createUnit() } } ColumnLayout { UnitEditor { - visible: editorSession.unit != null && editorSession.phrase == null + visible: editorSession.unit !== null && editorSession.phrase === null unit: editorSession.unit editPhrases: editorSession.skeletonMode && editorSession.editSkeleton } PhraseEditor { - visible: editorSession.phrase != null + visible: editorSession.phrase !== null phrase: editorSession.phrase isSkeletonPhrase: editorSession.editSkeleton Layout.minimumWidth: Math.floor(main.width * 0.6) Layout.fillHeight: true } } } } } diff --git a/src/qml/ProfileSelector.qml b/src/qml/ProfileSelector.qml index a9f6ee2..237b03e 100644 --- a/src/qml/ProfileSelector.qml +++ b/src/qml/ProfileSelector.qml @@ -1,125 +1,125 @@ /* * Copyright 2012 Sebastian Gottfried * Copyright 2013-2014 Andreas Cord-Landwehr * * 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 . */ import QtQuick 2.1 import QtQuick.Controls 1.2 import artikulate 1.0 FocusScope { id: root signal profileChosen(variant profile) function createNewProfile() { var profile = profileManager.addProfile(i18n("Unnamed Identity")) profileForm.profile = profile } function selectProfile(index) { list.currentIndex = index profileForm.profile = profileManager.profile(index) } Component.onCompleted : { profileForm.profile = profileManager.activeProfile list.currentIndex = -1 // clear highlighting as fallback for (var i=0; i < profileManager.profileCount; ++i) { - if (profileManager.activeProfile == profileManager.profile(i)) { + if (profileManager.activeProfile === profileManager.profile(i)) { list.currentIndex = i return; } } } Column { anchors.fill: parent spacing: 10 Row { height: parent.height - selectButton.height - parent.spacing width: parent.width spacing: 10 ScrollView { id: listContainer height: parent.height width: Math.round((parent.width - parent.spacing) / 2) ListView { id: list anchors.fill: parent model: profileManager.profileCount + 1 clip: true delegate: ListItem { property bool isNewButton: index >= profileManager.profileCount // update list-index when profilemanager is changed Connections { target: profileManager onActiveProfileChanged: { - if (profileManager.activeProfile == profileManager.profile(index)) { + if (profileManager.activeProfile === profileManager.profile(index)) { list.currentIndex = index } } } width: list.width - 10 title: isNewButton? i18n("Create New Learner Identity"): index < profileManager.profileCount? profileManager.profile(index).name: null label.font.italic: isNewButton iconName: isNewButton? "list-add": "user-identity" onSelected: { list.currentIndex = index if (isNewButton) { createNewProfile() } else { selectProfile(index) } } } } } ProfileDetailsItem { id: profileForm width: parent.width - listContainer.width - parent.spacing height: parent.height onDeletionRequest: { profileManager.removeProfile(profileForm.profile) profileForm.profile = null } } } Button { id: selectButton anchors.horizontalCenter: parent.horizontalCenter iconName: "go-next-view" text: i18n("Use Selected Identity") enabled: list.currentIndex !== -1 && list.currentIndex < profileManager.profileCount onClicked: { root.profileChosen(profileManager.profile(list.currentIndex)) profileManager.activeProfile = profileManager.profile(list.currentIndex) } } } } diff --git a/src/qml/ProfileUserImageItem.qml b/src/qml/ProfileUserImageItem.qml index c5cada1..3679f56 100644 --- a/src/qml/ProfileUserImageItem.qml +++ b/src/qml/ProfileUserImageItem.qml @@ -1,62 +1,62 @@ /* * Copyright 2016 Andreas Cord-Landwehr * * 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 . */ import QtQuick 2.1 import QtQuick.Controls 1.2 import artikulate 1.0 Item { id: root height: 120 width: height property Learner profile: null Component { id: profileImage Image { Connections { target: profile onImageUrlChanged: { // trigger reload source = "" source = profile.imageUrl } } anchors.fill: parent fillMode: Image.Pad cache: false source: profile.imageUrl } } Component { id: dummyImage Icon { anchors.fill: parent icon: "user-identity" } } Loader { anchors.fill: parent - sourceComponent: profile.imageUrl == "" ? dummyImage : profileImage + sourceComponent: profile.imageUrl === "" ? dummyImage : profileImage } } diff --git a/src/qml/SoundPlayer.qml b/src/qml/SoundPlayer.qml index 99c576e..1f70be2 100644 --- a/src/qml/SoundPlayer.qml +++ b/src/qml/SoundPlayer.qml @@ -1,75 +1,75 @@ /* * Copyright 2013-2015 Andreas Cord-Landwehr * * 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 . */ import QtQuick 2.1 import QtQuick.Controls 2.1 as QQC2 import org.kde.kirigami 2.0 as Kirigami2 import artikulate 1.0 Item { id: root width: button.width height: button.height property string fileUrl signal stopped() Player { id: playerBackend soundFileUrl: root.fileUrl } QQC2.RoundButton { id: button enabled: fileUrl != "" Kirigami2.Icon { source: "media-playback-start" width: 32 height: width anchors.centerIn: parent } onClicked: { - if (playerBackend.state == Player.PlayingState) { + if (playerBackend.state === Player.PlayingState) { playerBackend.stop(); return; } - if (playerBackend.state == Player.StoppedState) { + if (playerBackend.state === Player.StoppedState) { playerBackend.playback(); return; } } Connections { target: playerBackend onStateChanged: { // set next possible action icon - if (playerBackend.state == Player.PlayingState) { + if (playerBackend.state === Player.PlayingState) { button.iconName = "media-playback-stop"; return } - if (playerBackend.state == Player.StoppedState) { + if (playerBackend.state === Player.StoppedState) { button.iconName = "media-playback-start"; return } } } } } diff --git a/src/qml/SoundRecorder.qml b/src/qml/SoundRecorder.qml index b87f1ba..fa2f244 100644 --- a/src/qml/SoundRecorder.qml +++ b/src/qml/SoundRecorder.qml @@ -1,84 +1,84 @@ /* * Copyright 2014-2015 Andreas Cord-Landwehr * * 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 . */ import QtQuick 2.1 import QtQuick.Controls 2.1 as QQC2 import org.kde.kirigami 2.0 as Kirigami2 import artikulate 1.0 Item { id: root width: button.width height: button.height property string outputFileUrl: recorderBackend.recordingFile signal stopped() function storeToFile(filePath) { recorderBackend.storeToFile(filePath); phrase.setSoundFileUrl() } function clearBuffer() { recorderBackend.clearBuffer() } Recorder { id: recorderBackend } QQC2.RoundButton { id: button Kirigami2.Icon { id: icon source: "media-record" width: 32 height: width anchors.centerIn: parent } onClicked: { - if (recorderBackend.state == Recorder.RecordingState) { + if (recorderBackend.state === Recorder.RecordingState) { console.log("try to stop recording"); recorderBackend.stop(); return; } - if (recorderBackend.state == Recorder.StoppedState) { + if (recorderBackend.state === Recorder.StoppedState) { console.log("SoundRecorder: start capture"); recorderBackend.startCapture(); return; } } Connections { target: recorderBackend onStateChanged: { // update icon - if (recorderBackend.state == Recorder.RecordingState) { + if (recorderBackend.state === Recorder.RecordingState) { icon.source = "media-playback-stop"; } - if (recorderBackend.state == Recorder.StoppedState) { + if (recorderBackend.state === Recorder.StoppedState) { icon.source = "media-record"; } } } } } diff --git a/src/ui/exportghnsdialog.h b/src/ui/exportghnsdialog.h index afe4964..ae4d8ea 100644 --- a/src/ui/exportghnsdialog.h +++ b/src/ui/exportghnsdialog.h @@ -1,47 +1,47 @@ /* * Copyright 2013 Andreas Cord-Landwehr * * 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 . */ #ifndef EXPORTGHNSDIALOG_H #define EXPORTGHNSDIALOG_H #include "ui_exportghnsdialog.h" #include #include class ResourceManager; class ExportGhnsDialog : public QDialog { Q_OBJECT public: - ExportGhnsDialog(ResourceManager *manager); + explicit ExportGhnsDialog(ResourceManager *manager); ~ExportGhnsDialog(); public Q_SLOTS: void onExportCourse(); private: Ui::ExportGhnsDialog *ui; const ResourceManager *m_manager; }; #endif diff --git a/src/ui/sounddevicedialogpage.cpp b/src/ui/sounddevicedialogpage.cpp index 60f84a8..6829d32 100644 --- a/src/ui/sounddevicedialogpage.cpp +++ b/src/ui/sounddevicedialogpage.cpp @@ -1,176 +1,176 @@ /* * Copyright 2013 Andreas Cord-Landwehr * * 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 "sounddevicedialogpage.h" #include "libsound/src/capturedevicecontroller.h" #include "libsound/src/outputdevicecontroller.h" #include "settings.h" #include #include SoundDeviceDialogPage::SoundDeviceDialogPage() : QWidget(0) { ui = new Ui::SoundDeviceDialogPage; ui->setupUi(this); // set buttons - ui->buttonPlayTestSound->setIcon(QIcon::fromTheme("media-playback-start")); - ui->buttonPlayRecordedTestSound->setIcon(QIcon::fromTheme("media-playback-start")); - ui->buttonRecordTestSound->setIcon(QIcon::fromTheme("media-record")); + ui->buttonPlayTestSound->setIcon(QIcon::fromTheme(QStringLiteral("media-playback-start"))); + ui->buttonPlayRecordedTestSound->setIcon(QIcon::fromTheme(QStringLiteral("media-playback-start"))); + ui->buttonRecordTestSound->setIcon(QIcon::fromTheme(QStringLiteral("media-record"))); ui->buttonPlayRecordedTestSound->setEnabled(false); // set input volume slider //TODO Qt5.x port: reenable, since that we can set audioinput volume // ui->kcfg_AudioInputVolume->setTickInterval(1); // ui->kcfg_AudioInputVolume->setMinimum(1); // ui->kcfg_AudioInputVolume->setMaximum(100); // set output volume slider ui->kcfg_AudioOutputVolume->setTickInterval(0); ui->kcfg_AudioOutputVolume->setMinimum(0); ui->kcfg_AudioOutputVolume->setMaximum(20); // devices QStringList devices = CaptureDeviceController::self().devices(); for (int i=0; i < devices.length(); ++i) { ui->kcfg_AudioInputDevice->insertItem(i, devices.at(i), i); } //TODO Gst::Device will allow selecting devices again with GStreamer 1.4 // temporary file for recording test m_recordTestFile.open(); // connections - connect(ui->kcfg_AudioOutputVolume, SIGNAL(valueChanged(int)), this, SLOT(setVolume(int))); - connect(ui->buttonPlayTestSound, SIGNAL(clicked(bool)), this, SLOT(playTestSound())); - connect(ui->buttonPlayRecordedTestSound, SIGNAL(clicked(bool)), this, SLOT(playRecordedSound())); - connect(ui->buttonRecordTestSound, SIGNAL(clicked(bool)), this, SLOT(recordSound())); - connect(&OutputDeviceController::self(), SIGNAL(started()), this, SLOT(updatePlayButtonIcons())); - connect(&OutputDeviceController::self(), SIGNAL(stopped()), this, SLOT(updatePlayButtonIcons())); + connect(ui->kcfg_AudioOutputVolume, &QAbstractSlider::valueChanged, this, &SoundDeviceDialogPage::setVolume); + connect(ui->buttonPlayTestSound, &QAbstractButton::clicked, this, &SoundDeviceDialogPage::playTestSound); + connect(ui->buttonPlayRecordedTestSound, &QAbstractButton::clicked, this, &SoundDeviceDialogPage::playRecordedSound); + connect(ui->buttonRecordTestSound, &QAbstractButton::clicked, this, &SoundDeviceDialogPage::recordSound); + connect(&OutputDeviceController::self(), &OutputDeviceController::started, this, &SoundDeviceDialogPage::updatePlayButtonIcons); + connect(&OutputDeviceController::self(), &OutputDeviceController::stopped, this, &SoundDeviceDialogPage::updatePlayButtonIcons); } SoundDeviceDialogPage::~SoundDeviceDialogPage() { CaptureDeviceController::self().stopCapture(); CaptureDeviceController::self().setDevice(Settings::audioInputDevice()); delete ui; } void SoundDeviceDialogPage::loadSettings() { ui->kcfg_AudioInputDevice->setCurrentIndex( ui->kcfg_AudioInputDevice->findText(Settings::audioInputDevice())); // ui->kcfg_AudioInputVolume->setValue(Settings::audioInputVolume()); ui->kcfg_AudioOutputVolume->setValue(Settings::audioOutputVolume()); } void SoundDeviceDialogPage::setVolume(int volume) { OutputDeviceController::self().setVolume(volume); } void SoundDeviceDialogPage::saveSettings() { Settings::setAudioInputDevice(ui->kcfg_AudioInputDevice->itemText(ui->kcfg_AudioInputDevice->currentIndex())); // Settings::setAudioInputVolume(ui->kcfg_AudioInputVolume->value()); Settings::setAudioOutputVolume((int) ui->kcfg_AudioOutputVolume->value()); OutputDeviceController::self().setVolume(ui->kcfg_AudioOutputVolume->value()); Settings::self()->save(); } void SoundDeviceDialogPage::playTestSound() { if (OutputDeviceController::self().state() == OutputDeviceController::PlayingState) { OutputDeviceController::self().stop(); return; } - QString testsoundFile = QStandardPaths::locate(QStandardPaths::DataLocation, QString("sounds/testsound.ogg")); + QString testsoundFile = QStandardPaths::locate(QStandardPaths::DataLocation, QStringLiteral("sounds/testsound.ogg")); OutputDeviceController::self().setVolume(ui->kcfg_AudioOutputVolume->value()); OutputDeviceController::self().play(QUrl::fromLocalFile(testsoundFile)); } void SoundDeviceDialogPage::playRecordedSound() { if (OutputDeviceController::self().state() == OutputDeviceController::PlayingState) { OutputDeviceController::self().stop(); return; } OutputDeviceController::self().setVolume(ui->kcfg_AudioOutputVolume->value()); OutputDeviceController::self().play(QUrl::fromLocalFile(m_recordTestFile.fileName())); } void SoundDeviceDialogPage::stopPlaying() { OutputDeviceController::self().stop(); } void SoundDeviceDialogPage::recordSound() { if (CaptureDeviceController::self().state() == CaptureDeviceController::RecordingState) { CaptureDeviceController::self().stopCapture(); - ui->buttonRecordTestSound->setIcon(QIcon::fromTheme("media-record")); + ui->buttonRecordTestSound->setIcon(QIcon::fromTheme(QStringLiteral("media-record"))); ui->buttonPlayRecordedTestSound->setEnabled(true); return; } - ui->buttonRecordTestSound->setIcon(QIcon::fromTheme("artikulate-media-record-active")); + ui->buttonRecordTestSound->setIcon(QIcon::fromTheme(QStringLiteral("artikulate-media-record-active"))); CaptureDeviceController::self().setDevice(ui->kcfg_AudioInputDevice->currentText()); CaptureDeviceController::self().startCapture(m_recordTestFile.fileName()); } void SoundDeviceDialogPage::stopRecord() { if (CaptureDeviceController::self().state() == CaptureDeviceController::RecordingState) { CaptureDeviceController::self().stopCapture(); - ui->buttonRecordTestSound->setIcon(QIcon::fromTheme("media-record")); + ui->buttonRecordTestSound->setIcon(QIcon::fromTheme(QStringLiteral("media-record"))); ui->buttonPlayRecordedTestSound->setEnabled(true); } } void SoundDeviceDialogPage::updatePlayButtonIcons() { // default sound output test switch (OutputDeviceController::self().state()) { case OutputDeviceController::PlayingState: - ui->buttonPlayTestSound->setIcon(QIcon::fromTheme("media-playback-stop")); + ui->buttonPlayTestSound->setIcon(QIcon::fromTheme(QStringLiteral("media-playback-stop"))); break; case OutputDeviceController::StoppedState: - ui->buttonPlayTestSound->setIcon(QIcon::fromTheme("media-playback-start")); + ui->buttonPlayTestSound->setIcon(QIcon::fromTheme(QStringLiteral("media-playback-start"))); break; default: - ui->buttonPlayTestSound->setIcon(QIcon::fromTheme("media-playback-start")); + ui->buttonPlayTestSound->setIcon(QIcon::fromTheme(QStringLiteral("media-playback-start"))); } // recorded sound output test switch (OutputDeviceController::self().state()) { case OutputDeviceController::PlayingState: - ui->buttonPlayRecordedTestSound->setIcon(QIcon::fromTheme("media-playback-stop")); + ui->buttonPlayRecordedTestSound->setIcon(QIcon::fromTheme(QStringLiteral("media-playback-stop"))); break; case OutputDeviceController::StoppedState: - ui->buttonPlayRecordedTestSound->setIcon(QIcon::fromTheme("media-playback-start")); + ui->buttonPlayRecordedTestSound->setIcon(QIcon::fromTheme(QStringLiteral("media-playback-start"))); break; default: - ui->buttonPlayRecordedTestSound->setIcon(QIcon::fromTheme("media-playback-start")); + ui->buttonPlayRecordedTestSound->setIcon(QIcon::fromTheme(QStringLiteral("media-playback-start"))); } }