diff --git a/src/app/qml/Book.qml b/src/app/qml/Book.qml index a01aaa4..5e99184 100644 --- a/src/app/qml/Book.qml +++ b/src/app/qml/Book.qml @@ -1,523 +1,523 @@ /* * Copyright (C) 2015 Dan Leinir Turthra Jensen * * 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 . * */ import QtQuick 2.2 import QtQuick.Layouts 1.1 import QtQuick.Controls 2.2 as QtControls import QtQuick.Window 2.2 import org.kde.kirigami 2.1 as Kirigami import org.kde.peruse 0.1 as Peruse import "listcomponents" as ListComponents /** * @brief Page that handles reading the book. * * */ Kirigami.Page { id: root; objectName: "bookViewer"; clip: true; // Remove all the padding when we've hidden controls. Content is king! topPadding: applicationWindow().controlsVisible ? (applicationWindow() && applicationWindow().header ? applicationWindow().header.height : 0) : 0; leftPadding: applicationWindow().controlsVisible ? Kirigami.Units.gridUnit : 0; rightPadding: applicationWindow().controlsVisible ? Kirigami.Units.gridUnit : 0; bottomPadding: applicationWindow().controlsVisible ? Kirigami.Units.gridUnit : 0; background: Rectangle { anchors.fill: parent; opacity: applicationWindow().controlsVisible ? 0 : 1; Behavior on opacity { NumberAnimation { duration: applicationWindow().animationDuration; } } color: "black"; } // Perhaps we should store and restore this? property bool showControls: true; property Item pageStackItem: applicationWindow().pageStack.layers.currentItem; onPageStackItemChanged: { if(root.isCurrentPage) { applicationWindow().controlsVisible = root.showControls; } else { root.showControls = applicationWindow().controlsVisible; applicationWindow().controlsVisible = true; } } property bool rtlMode: false; /** * zoomMode: Peruse.Config.ZoomMode */ property int zoomMode: Peruse.Config.ZoomFull; property string file; property int currentPage; property int totalPages; onCurrentPageChanged: { // set off a timer to slightly postpone saving the current page, so it doesn't happen during animations etc updateCurrent.start(); } function nextPage() { if(viewLoader.item.currentPage < viewLoader.item.pageCount - 1) { viewLoader.item.currentPage++; } else { bookInfo.showBookInfo(file); } } function previousPage() { if(viewLoader.item.currentPage > 0) { viewLoader.item.currentPage--; } else { bookInfo.showBookInfo(file); } } function closeBook() { applicationWindow().contextDrawer.close(); // also for storing current page (otherwise postponed a bit after page change, done here as well to ensure it really happens) applicationWindow().controlsVisible = true; applicationWindow().pageStack.layers.pop(); applicationWindow().globalDrawer.open(); } property Item contextualTopItems: ListView { id: thumbnailNavigator; anchors.fill: parent; clip: true; delegate: thumbnailComponent; } Component { id: thumbnailComponent; Item { width: parent.width; height: Kirigami.Units.gridUnit * 6; MouseArea { anchors.fill: parent; onClicked: viewLoader.item.currentPage = model.index; } Rectangle { anchors.fill: parent; color: Kirigami.Theme.highlightColor; opacity: root.currentPage === model.index ? 1 : 0; Behavior on opacity { NumberAnimation { duration: Kirigami.Units.shortDuration; } } } Image { anchors { top: parent.top; left: parent.left; right: parent.right; margins: Kirigami.Units.smallSpacing; } height: parent.height - pageTitle.height - Kirigami.Units.smallSpacing * 2; asynchronous: true; fillMode: Image.PreserveAspectFit; source: model.url; } QtControls.Label { id: pageTitle; anchors { left: parent.left; right: parent.right; bottom: parent.bottom; } height: paintedHeight; text: model.title; elide: Text.ElideMiddle; horizontalAlignment: Text.AlignHCenter; } } } function toggleFullscreen() { applicationWindow().contextDrawer.close(); if(applicationWindow().visibility !== Window.FullScreen) { applicationWindow().visibility = Window.FullScreen; } else { applicationWindow().visibility = Window.AutomaticVisibility; } } property list mobileActions: [ Kirigami.Action { - text: applicationWindow().visibility !== Window.FullScreen ? i18nc("Enter full screen mode on a touch-based device", "Go full screen") : i18nc("Exit full sceen mode on a touch based device", "Exit full screen"); + text: applicationWindow().visibility !== Window.FullScreen ? i18nc("Enter full screen mode on a touch-based device", "Go Full Screen") : i18nc("Exit full sceen mode on a touch based device", "Exit Full Screen"); iconName: "view-fullscreen"; onTriggered: toggleFullscreen(); enabled: root.isCurrentPage && applicationWindow().deviceType === applicationWindow().deviceTypePhone; }, Kirigami.Action { - text: i18nc("Action used on touch devices to close the currently open book and return to whatever page was most recently shown", "Close book"); + text: i18nc("Action used on touch devices to close the currently open book and return to whatever page was most recently shown", "Close Book"); shortcut: bookInfo.sheetOpen ? "" : "Esc"; iconName: "dialog-close"; onTriggered: closeBook(); enabled: root.isCurrentPage && applicationWindow().deviceType === applicationWindow().deviceTypePhone; } ] property list desktopActions: [ Kirigami.Action { - text: i18nc("Top level entry leading to a submenu with options for the book display", "View options"); + text: i18nc("Top level entry leading to a submenu with options for the book display", "View Options"); iconName: "configure"; QtObject { property string text: "Reading Direction" } Kirigami.Action { text: "Left to Right" iconName: "format-text-direction-ltr"; shortcut: rtlMode ? "r" : ""; enabled: root.isCurrentPage && applicationWindow().deviceType === applicationWindow().deviceTypeDesktop && root.rtlMode === true; onTriggered: { root.rtlMode = false; } } Kirigami.Action { text: "Right to Left" iconName: "format-text-direction-rtl"; shortcut: rtlMode ? "" : "r"; enabled: root.isCurrentPage && applicationWindow().deviceType === applicationWindow().deviceTypeDesktop && root.rtlMode === false; onTriggered: { root.rtlMode = true; } } QtObject {} // QtObject { // property string text: "Zoom" // } // Kirigami.Action { // text: "Fit full page" // iconName: "zoom-fit-best"; // enabled: root.isCurrentPage && applicationWindow().deviceType === applicationWindow().deviceTypeDesktop && root.zoomMode !== Peruse.Config.ZoomFull; // onTriggered: { root.zoomMode = Peruse.Config.ZoomFull; } // } // Kirigami.Action { // text: "Fit width" // iconName: "zoom-fit-width"; // enabled: root.isCurrentPage && applicationWindow().deviceType === applicationWindow().deviceTypeDesktop && root.zoomMode !== Peruse.Config.ZoomFitWidth; // onTriggered: { root.zoomMode = Peruse.Config.ZoomFitWidth; } // } // Kirigami.Action { // text: "Fit height" // iconName: "zoom-fit-height"; // enabled: root.isCurrentPage && applicationWindow().deviceType === applicationWindow().deviceTypeDesktop && root.zoomMode !== Peruse.Config.ZoomFitHeight; // onTriggered: { root.zoomMode = Peruse.Config.ZoomFitHeight; } // } // QtObject {} }, Kirigami.Action { - text: i18nc("Go to the previous page in the book", "Previous page"); + text: i18nc("Go to the previous page in the book", "Previous Page"); shortcut: root.isCurrentPage && bookInfo.sheetOpen ? "" : StandardKey.MoveToPreviousChar; iconName: "go-previous"; onTriggered: previousPage(); enabled: root.isCurrentPage && applicationWindow().deviceType === applicationWindow().deviceTypeDesktop; }, Kirigami.Action { - text: i18nc("Go to the next page in the book", "Next page"); + text: i18nc("Go to the next page in the book", "Next Page"); shortcut: bookInfo.sheetOpen ? "" : StandardKey.MoveToNextChar; iconName: "go-next"; onTriggered: nextPage(); enabled: root.isCurrentPage && applicationWindow().deviceType === applicationWindow().deviceTypeDesktop; }, Kirigami.Action { - text: applicationWindow().visibility !== Window.FullScreen ? i18nc("Enter full screen mode on a non-touch-based device", "Go full screen") : i18nc("Exit full sceen mode on a non-touch based device", "Exit full screen"); + text: applicationWindow().visibility !== Window.FullScreen ? i18nc("Enter full screen mode on a non-touch-based device", "Go Full Screen") : i18nc("Exit full sceen mode on a non-touch based device", "Exit Full Screen"); shortcut: (applicationWindow().visibility === Window.FullScreen) ? (bookInfo.sheetOpen ? "" : "Esc") : "f"; iconName: "view-fullscreen"; onTriggered: toggleFullscreen(); enabled: root.isCurrentPage && applicationWindow().deviceType === applicationWindow().deviceTypeDesktop; }, Kirigami.Action { - text: i18nc("Action used on non-touch devices to close the currently open book and return to whatever page was most recently shown", "Close book"); + text: i18nc("Action used on non-touch devices to close the currently open book and return to whatever page was most recently shown", "Close Book"); shortcut: (applicationWindow().visibility === Window.FullScreen) ? "" : (bookInfo.sheetOpen ? "" : "Esc"); iconName: "dialog-close"; onTriggered: closeBook(); enabled: root.isCurrentPage && applicationWindow().deviceType === applicationWindow().deviceTypeDesktop; }, // Invisible actions, for use in bookInfo Kirigami.Action { visible: false; shortcut: bookInfo.sheetOpen ? StandardKey.MoveToPreviousChar : ""; onTriggered: bookInfo.previousBook(); enabled: root.isCurrentPage && applicationWindow().deviceType === applicationWindow().deviceTypeDesktop; }, Kirigami.Action { visible: false; shortcut: bookInfo.sheetOpen ? StandardKey.MoveToNextChar : ""; onTriggered: bookInfo.nextBook(); enabled: root.isCurrentPage && applicationWindow().deviceType === applicationWindow().deviceTypeDesktop; }, Kirigami.Action { visible: false; shortcut: bookInfo.sheetOpen ? "Return" : ""; onTriggered: bookInfo.openSelected(); enabled: root.isCurrentPage && applicationWindow().deviceType === applicationWindow().deviceTypeDesktop; } ] actions { contextualActions: applicationWindow().deviceType === applicationWindow().deviceTypePhone ? mobileActions : desktopActions; main: bookInfo.sheetOpen ? bookInfoAction : mainBookAction; } Kirigami.Action { id: mainBookAction; - text: applicationWindow().visibility !== Window.FullScreen ? i18nc("Enter full screen mode on any device type", "Go full screen") : i18nc("Exit full screen mode on any device type", "Exit full screen"); + text: applicationWindow().visibility !== Window.FullScreen ? i18nc("Enter full screen mode on any device type", "Go Full Screen") : i18nc("Exit full screen mode on any device type", "Exit Full Screen"); iconName: "view-fullscreen"; onTriggered: toggleFullscreen(); enabled: root.isCurrentPage; } Kirigami.Action { id: bookInfoAction; text: i18nc("Closes the book information drawer", "Close"); shortcut: bookInfo.sheetOpen ? "Esc" : ""; iconName: "dialog-cancel"; onTriggered: bookInfo.close(); enabled: root.isCurrentPage; } /** * This holds an instance of ViewerBase, which can either be the * Okular viewer(the fallback one), or one of the type specific * ones(ImageBrowser based). */ Item { width: root.width - (root.leftPadding + root.rightPadding); height: root.height - (root.topPadding + root.bottomPadding); Timer { id: updateCurrent; interval: applicationWindow().animationDuration; running: false; repeat: false; onTriggered: { if(viewLoader.item && viewLoader.item.pagesModel && viewLoader.item.pagesModel.currentPage !== undefined) { viewLoader.item.pagesModel.currentPage = root.currentPage; } } } NumberAnimation { id: thumbnailMovementAnimation; target: thumbnailNavigator; property: "contentY"; duration: applicationWindow().animationDuration; easing.type: Easing.InOutQuad; } Loader { id: viewLoader; anchors.fill: parent; property bool loadingCompleted: false; onLoaded: { if(status === Loader.Error) { // huh! problem... } else { item.file = root.file; } } Binding { target: viewLoader.item; property: "rtlMode"; value: root.rtlMode; } Binding { target: viewLoader.item; property: "zoomMode"; value: root.zoomMode; } Connections { target: viewLoader.item; onLoadingCompleted: { if(success) { thumbnailNavigator.model = viewLoader.item.pagesModel; if(viewLoader.item.thumbnailComponent) { thumbnailNavigator.delegate = viewLoader.item.thumbnailComponent; } else { thumbnailNavigator.delegate = thumbnailComponent; } peruseConfig.setFilesystemProperty(root.file, "totalPages", viewLoader.item.pageCount); if(root.totalPages !== viewLoader.item.pageCount) { root.totalPages = viewLoader.item.pageCount; } viewLoader.item.currentPage = root.currentPage; viewLoader.loadingCompleted = true; root.title = viewLoader.item.title; applicationWindow().globalDrawer.close(); } } onCurrentPageChanged: { if(root.currentPage !== viewLoader.item.currentPage && viewLoader.loadingCompleted) { root.currentPage = viewLoader.item.currentPage; } thumbnailMovementAnimation.running = false; var currentPos = thumbnailNavigator.contentY; var newPos; thumbnailNavigator.positionViewAtIndex(viewLoader.item.currentPage, ListView.Center); newPos = thumbnailNavigator.contentY; thumbnailMovementAnimation.from = currentPos; thumbnailMovementAnimation.to = newPos; thumbnailMovementAnimation.running = true; } onGoNextPage: root.nextPage(); onGoPreviousPage: root.previousPage(); } } } /** * Overlay with book information and a series selection. */ Kirigami.OverlaySheet { id: bookInfo; function setNewCurrentIndex(newIndex) { seriesListAnimation.running = false; var currentPos = seriesListView.contentX; var newPos; seriesListView.positionViewAtIndex(newIndex, ListView.Center); newPos = seriesListView.contentX; seriesListAnimation.from = currentPos; seriesListAnimation.to = newPos; seriesListAnimation.running = true; seriesListView.currentIndex = newIndex; } function nextBook() { if(seriesListView.currentIndex < seriesListView.model.rowCount()) { setNewCurrentIndex(seriesListView.currentIndex + 1); } } function previousBook() { if(seriesListView.currentIndex > 0) { setNewCurrentIndex(seriesListView.currentIndex - 1); } } function openSelected() { if (detailsTile.filename!==root.file) { closeBook(); applicationWindow().showBook(detailsTile.filename, detailsTile.currentPage); } } function showBookInfo(filename) { if(sheetOpen) { return; } seriesListView.model = contentList.seriesModelForEntry(filename); if (seriesListView.model) { setNewCurrentIndex(seriesListView.model.indexOfFile(filename)); } open(); } onSheetOpenChanged: { if(sheetOpen === false) { applicationWindow().controlsVisible = controlsShown; } else { controlsShown = applicationWindow().controlsVisible; applicationWindow().controlsVisible = true; } } property bool controlsShown; property QtObject currentBook: fakeBook; property QtObject fakeBook: Peruse.PropertyContainer { property string author: ""; property string title: ""; property string filename: ""; property string publisher: ""; property string thumbnail: ""; property string currentPage: "0"; property string totalPages: "0"; } Column { clip: true; width: root.width - Kirigami.Units.largeSpacing * 2; height: childrenRect.height + Kirigami.Units.largeSpacing * 2; spacing: Kirigami.Units.largeSpacing; ListComponents.BookTile { id: detailsTile; height: neededHeight; width: parent.width; author: bookInfo.currentBook.readProperty("author"); publisher: bookInfo.currentBook.readProperty("publisher"); title: bookInfo.currentBook.readProperty("title"); filename: bookInfo.currentBook.readProperty("filename"); thumbnail: bookInfo.currentBook.readProperty("thumbnail"); categoryEntriesCount: 0; currentPage: bookInfo.currentBook.readProperty("currentPage"); totalPages: bookInfo.currentBook.readProperty("totalPages"); onBookSelected: { if(root.file !== filename) { openSelected(); } } onBookDeleteRequested: { // Not strictly needed for the listview itself, but it's kind of // nice for making sure the details tile is right var oldIndex = seriesListView.currentIndex; seriesListView.currentIndex = -1; contentList.removeBook(detailsTile.filename, true); seriesListView.currentIndex = oldIndex; } } // tags and ratings, comment by self // store hook for known series with more content ListView { id: seriesListView; width: parent.width; height: Kirigami.Units.gridUnit * 12; orientation: ListView.Horizontal; NumberAnimation { id: seriesListAnimation; target: seriesListView; property: "contentX"; duration: applicationWindow().animationDuration; easing.type: Easing.InOutQuad; } delegate: ListComponents.BookTileTall { height: model.filename != "" ? neededHeight : 1; width: seriesListView.width / 3; author: model.author; title: model.title; filename: model.filename; thumbnail: model.thumbnail; categoryEntriesCount: 0; currentPage: model.currentPage; totalPages: model.totalPages; onBookSelected:{ if (seriesListView.currentIndex !== model.index) { bookInfo.setNewCurrentIndex(model.index); } else { bookInfo.openSelected(); } } selected: seriesListView.currentIndex === model.index; } onCurrentIndexChanged: { bookInfo.currentBook = model.get(currentIndex); } } } } onFileChanged: { // Let's set the page title to something useful var book = contentList.bookFromFile(file); root.title = book.readProperty("title"); // The idea is to have a number of specialised options as relevant to various // types of comic books, and then finally fall back to Okular as a catch-all // but generic viewer component. var attemptFallback = true; var mimetype = contentList.contentModel.getMimetype(file); console.debug("Mimetype is " + mimetype); if(mimetype == "application/x-cbz" || mimetype == "application/x-cbr" || mimetype == "application/vnd.comicbook+zip" || mimetype == "application/vnd.comicbook+rar") { viewLoader.source = "viewers/cbr.qml"; attemptFallback = false; } if(mimetype == "inode/directory" || mimetype == "image/jpeg" || mimetype == "image/png") { viewLoader.source = "viewers/folderofimages.qml"; attemptFallback = false; } if(attemptFallback) { viewLoader.source = "viewers/okular.qml"; } } } diff --git a/src/app/qml/BookDetails.qml b/src/app/qml/BookDetails.qml index b5aa8c8..b41b9e5 100644 --- a/src/app/qml/BookDetails.qml +++ b/src/app/qml/BookDetails.qml @@ -1,206 +1,206 @@ /* * Copyright (C) 2015 Dan Leinir Turthra Jensen * * 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 . * */ import QtQuick 2.2 import QtQuick.Layouts 1.1 import QtQuick.Controls 2.2 as QtControls import org.kde.kirigami 2.1 as Kirigami import org.kde.peruse 0.1 as Peruse import "listcomponents" as ListComponents Item { id: root; property string file; height: childrenRect.height; onFileChanged: { var book = contentList.get(contentList.indexOfFile(file)); filename = book.readProperty("filename"); filetitle = book.readProperty("filetitle"); title = book.readProperty("title"); series = book.readProperty("series"); author = book.readProperty("author"); publisher = book.readProperty("publisher"); created = book.readProperty("created"); lastOpenedTime = book.readProperty("lastOpenedTime"); totalPages = book.readProperty("totalPages"); currentPage = book.readProperty("currentPage"); thumbnail = book.readProperty("thumbnail"); dataRepeater.model.clear(); dataRepeater.model.append({"label": i18nc("Label for the author field", "Author:"), value: root.author}); dataRepeater.model.append({"label": i18nc("Label for the publisher field", "Publisher:"), value: root.publisher}); dataRepeater.model.append({"label": i18nc("Label for the series field", "Series:"), value: root.series}); dataRepeater.model.append({"label": i18nc("Label for the filename field", "Filename:"), value: root.filename}); } property string filename; property string filetitle; // property string title; property string series; property string author; property string publisher; property date created; property date lastOpenedTime; property int totalPages; property int currentPage; property string thumbnail; Column { anchors.horizontalCenter: parent.horizontalCenter; spacing: Kirigami.Units.smallSpacing; width: parent.width; height: childrenRect.height; Item { id: bookCover; anchors { horizontalCenter: parent.horizontalCenter; margins: Kirigami.Units.largeSpacing; } width: Math.min(parent.width - Kirigami.Units.largeSpacing * 2, Kirigami.Units.iconSizes.enormous + Kirigami.Units.largeSpacing * 2); height: width; Rectangle { anchors.centerIn: coverImage; width: coverImage.paintedWidth + Kirigami.Units.smallSpacing * 2; height: coverImage.paintedHeight + Kirigami.Units.smallSpacing * 2; color: Kirigami.Theme.viewBackgroundColor; border { width: 2; color: Kirigami.Theme.viewTextColor; } radius: 2; } Image { id: coverImage; anchors { fill: parent; margins: Kirigami.Units.largeSpacing; } source: root.thumbnail; asynchronous: true; fillMode: Image.PreserveAspectFit; } } Repeater { id: dataRepeater; model: ListModel {} delegate: Item { id: base; width: root.width; height: valueLabel.height; QtControls.Label { anchors { top: parent.top; left: parent.left; right: parent.horizontalCenter; bottom: parent.bottom; } verticalAlignment: Text.AlignTop; text: model.label; } QtControls.Label { id: valueLabel; anchors { top: parent.top; left: parent.horizontalCenter; right: parent.right; } verticalAlignment: Text.AlignTop; height: paintedHeight; wrapMode: Text.WordWrap; text: model.value; } } } Item { id: deleteBase; width: root.width; height: deleteButton.height + Kirigami.Units.largeSpacing * 2; Behavior on height { PropertyAnimation { duration: applicationWindow().animationDuration; } } states: [ State { name: "confirmDelete"; PropertyChanges { target: deleteButton; opacity: 0; } PropertyChanges { target: deleteConfirmBase; opacity: 1; } PropertyChanges { target: deleteBase; height: deleteConfirmBase.height; } } ] QtControls.Button { id: deleteButton; - text: i18nc("Spawn inline dialog box to confirm permanent removal of this book", "Delete from device"); + text: i18nc("Spawn inline dialog box to confirm permanent removal of this book", "Delete from Device"); anchors { top: parent.top; topMargin: Kirigami.Units.largeSpacing; horizontalCenter: parent.horizontalCenter; } iconName: "edit-delete"; onClicked: deleteBase.state = "confirmDelete"; Behavior on opacity { PropertyAnimation { duration: applicationWindow().animationDuration; } } } Item { id: deleteConfirmBase; opacity: 0; width: root.width; Behavior on opacity { PropertyAnimation { duration: applicationWindow().animationDuration; } } height: yesDelete.height + confirmDeleteLabel.height + Kirigami.Units.largeSpacing * 2 + Kirigami.Units.smallSpacing; QtControls.Label { id: confirmDeleteLabel; anchors { top: parent.top; topMargin: Kirigami.Units.largeSpacing; left: parent.left; right: parent.right; } height: paintedHeight; wrapMode: Text.WordWrap; horizontalAlignment: Text.AlignHCenter; text: i18nc("Dialog text for delete book dialog", "Are you sure you want to delete this from your device?"); } QtControls.Button { id: yesDelete; anchors { top: confirmDeleteLabel.bottom; topMargin: Kirigami.Units.smallSpacing; right: parent.horizontalCenter; rightMargin: (parent.width - width) / 4; } - text: i18nc("Confirmation button for book delete dialog", "Yes, really delete"); + text: i18nc("Confirmation button for book delete dialog", "Yes, Really Delete"); iconName: "dialog-ok"; onClicked: { contentList.removeBook(root.file, true); applicationWindow().pageStack.pop(); } } QtControls.Button { anchors { top: confirmDeleteLabel.bottom; topMargin: Kirigami.Units.smallSpacing; left: parent.horizontalCenter; leftMargin: (parent.width - width) / 4; } - text: i18nc("Cancellation button or book delete dialog", "No, cancel delete"); + text: i18nc("Cancellation button or book delete dialog", "No, Cancel Delete"); iconName: "dialog-cancel"; onClicked: deleteBase.state = ""; } } } } } diff --git a/src/app/qml/Bookshelf.qml b/src/app/qml/Bookshelf.qml index e8fac02..90eb316 100644 --- a/src/app/qml/Bookshelf.qml +++ b/src/app/qml/Bookshelf.qml @@ -1,219 +1,219 @@ /* * Copyright (C) 2015 Dan Leinir Turthra Jensen * * 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 . * */ import QtQuick 2.2 import QtQuick.Layouts 1.1 import QtQuick.Controls 1.4 as QtControls import org.kde.kirigami 2.1 as Kirigami import org.kde.peruse 0.1 as Peruse import "listcomponents" as ListComponents /** * @brief This lays out the books and categories. * * It uses BookTileTall for the books and CategoryTileTall * for showing the categories. Categories can be selected to open * a new bookshelf from the right, showing the entries in that * subcategory. This is particularly in use with the folder category. * * There is also access to the SearchBox, and it is possible to access * a BookTile by press+holding the thumbnail. * This holds information about the book. */ Kirigami.ScrollablePage { id: root; title: headerText; property string categoryName: "bookshelf"; objectName: "bookshelf"; property alias model: shelfList.model; property string sectionRole: "title"; property int sectionCriteria: ViewSection.FirstCharacter; signal bookSelected(string filename, int currentPage); property string headerText; function openBook(index) { applicationWindow().contextDrawer.close(); if(shelfList.model.indexIsBook(index)) { var book = shelfList.model.get(index); root.bookSelected(book.readProperty("filename"), book.readProperty("currentPage")); } else { var catEntry = shelfList.model.getEntry(index); applicationWindow().pageStack.push(bookshelf, { focus: true, headerText: catEntry.readProperty("title"), model: catEntry.readProperty("entriesModel") }); } } function closeShelf() { applicationWindow().contextDrawer.close(); applicationWindow().pageStack.pop(); } property list mobileActions; property list desktopActions: [ Kirigami.Action { text: i18nc("Navigate one page back", "Back"); shortcut: bookDetails.sheetOpen ? "" : "Esc"; iconName: "dialog-close"; onTriggered: closeShelf(); enabled: root.isCurrentPage && applicationWindow().deviceType === applicationWindow().deviceTypeDesktop && applicationWindow().pageStack.currentIndex > 0; }, // Kirigami.Action { -// text: i18nc("Select the previous book in the list", "Select previous book"); +// text: i18nc("Select the previous book in the list", "Select Previous Book"); // shortcut: StandardKey.MoveToPreviousChar // iconName: "go-previous"; // onTriggered: shelfList.previousEntry(); // enabled: root.isCurrentPage && applicationWindow().deviceType === applicationWindow().deviceTypeDesktop; // }, // Kirigami.Action { -// text: i18nc("Select the next book in the list", "Select next book"); +// text: i18nc("Select the next book in the list", "Select Next Book"); // shortcut: StandardKey.MoveToNextChar; // iconName: "go-next"; // onTriggered: shelfList.nextEntry(); // enabled: root.isCurrentPage && applicationWindow().deviceType === applicationWindow().deviceTypeDesktop; // }, Kirigami.Action { - text: i18nc("Open the book which is currently selected in the list", "Open selected book"); + text: i18nc("Open the book which is currently selected in the list", "Open Selected Book"); shortcut: "Return"; iconName: "document-open"; onTriggered: openBook(shelfList.currentIndex); enabled: root.isCurrentPage && applicationWindow().deviceType === applicationWindow().deviceTypeDesktop; } ] actions { contextualActions: PLASMA_PLATFORM.substring(0, 5) === "phone" ? mobileActions : desktopActions; main: bookDetails.sheetOpen ? bookDetailsAction : mainShelfAction; } Kirigami.Action { id: mainShelfAction; text: i18nc("search in the list of books (not inside the books)", "Search Books"); iconName: "system-search"; onTriggered: searchBox.activate(); enabled: root.isCurrentPage; } Kirigami.Action { id: bookDetailsAction; text: i18n("Closes the book details drawer", "Close"); shortcut: bookDetails.sheetOpen ? "Esc" : ""; iconName: "dialog-cancel"; onTriggered: bookDetails.close(); enabled: root.isCurrentPage; } GridView { id: shelfList; SearchBox { id: searchBox; anchors { top: parent.top; left: parent.left; right: parent.right; } maxHeight: parent.height; model: root.model; onBookSelected: root.bookSelected(filename, currentPage); } clip: true; footer: Item { width: parent.width; height: Kirigami.Units.iconSizes.large + Kirigami.Units.largeSpacing; } cellWidth: width / 2; cellHeight: root.height * 3 / 8; currentIndex: -1; function previousEntry() { if(currentIndex > 0) { currentIndex--; } } function nextEntry() { if(currentIndex < model.rowCount() - 1) { currentIndex++; } } delegate: Item { height: model.categoryEntriesCount === 0 ? bookTile.neededHeight : categoryTile.neededHeight; width: root.width / 2; ListComponents.CategoryTileTall { id: categoryTile; height: model.categoryEntriesCount > 0 ? neededHeight : 0; width: parent.width; count: model.categoryEntriesCount; title: model.title; entriesModel: model.categoryEntriesModel ? model.categoryEntriesModel : null; selected: shelfList.currentIndex === index; } ListComponents.BookTileTall { id: bookTile; height: model.categoryEntriesCount < 1 ? neededHeight : 0; width: parent.width; - author: model.author ? model.author : i18nc("used for the author data in book lists if autor is empty", "(unknown)"); + author: model.author ? model.author : i18nc("used for the author data in book lists if author is empty", "(unknown)"); title: model.title; filename: model.filename; thumbnail: model.categoryEntriesCount < 1 ? model.thumbnail : ""; categoryEntriesCount: model.categoryEntriesCount; currentPage: model.currentPage; totalPages: model.totalPages; onBookSelected: root.bookSelected(filename, currentPage); selected: shelfList.currentIndex === index; onPressAndHold: bookDetails.showBookInfo(model.index); pressIndicator: true; } } } Kirigami.OverlaySheet { id: bookDetails; function showBookInfo(index) { currentBook = root.model.getEntry(index); open(); } property QtObject currentBook: fakeBook; property QtObject fakeBook: Peruse.PropertyContainer { property string author: ""; property string title: ""; property string filename: ""; property string publisher: ""; property string thumbnail: ""; property string currentPage: "0"; property string totalPages: "0"; } ListComponents.BookTile { id: detailsTile; height: neededHeight; width: shelfList.width - Kirigami.Units.largeSpacing * 2; author: bookDetails.currentBook.readProperty("author"); publisher: bookDetails.currentBook.readProperty("publisher"); title: bookDetails.currentBook.readProperty("title"); filename: bookDetails.currentBook.readProperty("filename"); thumbnail: bookDetails.currentBook.readProperty("thumbnail"); categoryEntriesCount: 0; currentPage: bookDetails.currentBook.readProperty("currentPage"); totalPages: bookDetails.currentBook.readProperty("totalPages"); onBookSelected: { bookDetails.close(); applicationWindow().showBook(filename, currentPage); } onBookDeleteRequested: { contentList.removeBook(detailsTile.filename, true); close(); } } } } diff --git a/src/app/qml/Main.qml b/src/app/qml/Main.qml index 57667fc..99ae1db 100644 --- a/src/app/qml/Main.qml +++ b/src/app/qml/Main.qml @@ -1,49 +1,49 @@ /* * Copyright (C) 2015 Dan Leinir Turthra Jensen * * 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 . * */ import QtQuick 2.2 import QtQuick.Window 2.2 import QtQuick.Dialogs 1.2 PeruseMain { id: root; width: Screen.desktopAvailableWidth * 0.6; height: Screen.desktopAvailableHeight * 0.7; function openOther() { openDlg.open(); } FileDialog { id: openDlg; - title: i18nc("Title of a standard file open dialog used to open a book not in the collection", "Please choose a comic to open"); + title: i18nc("@title:window standard file open dialog used to open a book not in the collection", "Please Choose a Comic to Open"); folder: root.homeDir(); property int splitPos: osIsWindows ? 8 : 7; onAccepted: { if(openDlg.fileUrl.toString().substring(0, 7) === "file://") { root.showBook(openDlg.fileUrl.toString().substring(splitPos), 0); } } onRejected: { // Just do nothing, we don't really care... } } } diff --git a/src/app/qml/MobileMain.qml b/src/app/qml/MobileMain.qml index 7b48eb7..fff3329 100644 --- a/src/app/qml/MobileMain.qml +++ b/src/app/qml/MobileMain.qml @@ -1,56 +1,56 @@ /* * Copyright (C) 2015 Dan Leinir Turthra Jensen * * 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 . * */ import QtQuick 2.1 import org.kde.kirigami 2.1 as Kirigami import org.kde.peruse 0.1 as Peruse PeruseMain { id: root; width: 750; height: 1100; function openOther() { pageStack.push(openDlg, { folder: root.homeDir() } ); } Component { id: openDlg; Kirigami.Page { id: root; property string folder; - title: i18nc("Title of a page which lets you open comics not in your collection by using a standard touch-friendly dig-down style filesystem browser", "Open comics not in your collection"); + title: i18nc("Title of a page which lets you open comics not in your collection by using a standard touch-friendly dig-down style filesystem browser", "Open Comics not in Your Collection"); FileFinder { width: root.width - (root.leftPadding + root.rightPadding); height: root.height - (root.topPadding + root.bottomPadding); folder: root.folder; showFiles: true; property int splitPos: osIsWindows ? 8 : 7; onAccepted: { if(selectedItem().substring(0, 7) === "file://") { showBook(selectedItem().substring(splitPos), 0); } } onAborted: pageStack.pop(); } } } } diff --git a/src/app/qml/PeruseMain.qml b/src/app/qml/PeruseMain.qml index 05f70a5..eb68f5e 100644 --- a/src/app/qml/PeruseMain.qml +++ b/src/app/qml/PeruseMain.qml @@ -1,299 +1,299 @@ /* * Copyright (C) 2015 Dan Leinir Turthra Jensen * * 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 . * */ import QtQuick 2.1 import QtQuick.Layouts 1.1 import QtQuick.Controls 1.4 as QtControls import QtQuick.Window 2.2 import org.kde.kirigami 2.1 as Kirigami import org.kde.peruse 0.1 as Peruse import org.kde.contentlist 0.1 /** * @brief main application window. * * This splits the window in two sections: * - A section where you can select comics. * - A "global drawer" which can be used to switch between categories * and access settings and the open book dialog. * * The global drawer controls which is the main component on the left. * It initializes on WelcomePage. The category filters are each handled * by a BookShelf. The store page by Store and the settings by Settings. * * This also controls the bookViewer, which is a Book object where the * main reading of comics is done. * * There is also the PeruseContextDrawer, which is only accesible on the book * page and requires flicking in from the right. */ Kirigami.ApplicationWindow { id: mainWindow; title: "Comic Book Reader"; property int animationDuration: 200; property bool isLoading: true; pageStack.initialPage: welcomePage; visible: true; // If the controls are not visible, being able to drag the pagestack feels really weird, // so we just turn that ability off :) pageStack.interactive: controlsVisible; /// Which type of device we're running on. 0 is desktop, 1 is phone property int deviceType: PLASMA_PLATFORM.substring(0, 5) === "phone" ? 1 : 0; property int deviceTypeDesktop: 0; property int deviceTypePhone: 1; function showBook(filename, currentPage) { if(mainWindow.pageStack.lastItem.objectName === "bookViewer") { mainWindow.pageStack.pop(); } mainWindow.pageStack.layers.push(bookViewer, { focus: true, file: filename, currentPage: currentPage }) peruseConfig.bookOpened(filename); } Peruse.BookListModel { id: contentList; contentModel: ContentList { autoSearch: false onSearchCompleted: { mainWindow.isLoading = false; mainWindow.globalDrawer.actions = globalDrawerActions; } ContentQuery { type: ContentQuery.Comics locations: peruseConfig.bookLocations } } onCacheLoadedChanged: { if(!cacheLoaded) { return; } contentList.contentModel.setKnownFiles(contentList.knownBookFiles()); contentList.contentModel.startSearch() } } Peruse.Config { id: peruseConfig; } function homeDir() { return peruseConfig.homeDir(); } header: Kirigami.ApplicationHeader {} contextDrawer: PeruseContextDrawer { id: contextDrawer; } globalDrawer: Kirigami.GlobalDrawer { title: i18nc("application title for the sidebar", "Peruse"); titleIcon: "peruse"; drawerOpen: PLASMA_PLATFORM.substring(0, 5) === "phone" ? false : true; modal: PLASMA_PLATFORM.substring(0, 5) === "phone" ? true : false; actions: [] } property list globalDrawerActions: [ Kirigami.Action { - text: "Welcome"; + text: i18nc("Switch to the welcome page", "Welcome"); iconName: "start-over"; checked: mainWindow.currentCategory === "welcomePage"; onTriggered: { changeCategory(welcomePage); pageStack.currentItem.updateRecent(); } }, Kirigami.Action { }, Kirigami.Action { text: i18nc("Switch to the listing page showing the most recently discovered books", "Recently Added Books"); iconName: "appointment-new"; checked: mainWindow.currentCategory === "bookshelfAdded"; onTriggered: changeCategory(bookshelfAdded); }, Kirigami.Action { text: i18nc("Switch to the listing page showing items grouped by title", "Group by Title"); iconName: "view-media-title"; checked: mainWindow.currentCategory === "bookshelfTitle"; onTriggered: changeCategory(bookshelfTitle); }, Kirigami.Action { text: i18nc("Switch to the listing page showing items grouped by author", "Group by Author"); iconName: "actor"; checked: mainWindow.currentCategory === "bookshelfAuthor"; onTriggered: changeCategory(bookshelfAuthor); }, Kirigami.Action { text: i18nc("Switch to the listing page showing items grouped by series", "Group by Series"); iconName: "edit-group"; checked: currentCategory === "bookshelfSeries"; onTriggered: changeCategory(bookshelfSeries); }, Kirigami.Action { text: i18nc("Switch to the listing page showing items grouped by publisher", "Group by Publisher"); iconName: "view-media-publisher"; checked: mainWindow.currentCategory === "bookshelfPublisher"; onTriggered: changeCategory(bookshelfPublisher); }, Kirigami.Action { text: i18nc("Switch to the listing page showing items grouped by their filesystem folder", "Filter by Folder"); iconName: "tag-folder"; checked: mainWindow.currentCategory === "bookshelfFolder"; onTriggered: changeCategory(bookshelfFolder); }, Kirigami.Action { }, Kirigami.Action { - text: i18nc("Open a book from somewhere on disk (uses the open dialog, or a drilldown on touch devices)", "Open other..."); + text: i18nc("Open a book from somewhere on disk (uses the open dialog, or a drilldown on touch devices)", "Open Other..."); iconName: "document-open"; onTriggered: openOther(); }, Kirigami.Action { text: i18nc("Switch to the book store page", "Get Hot New Books"); iconName: "get-hot-new-stuff"; checked: mainWindow.currentCategory === "storePage"; onTriggered: changeCategory(storePage); }, Kirigami.Action { }, Kirigami.Action { text: i18nc("Open the settings page", "Settings"); iconName: "configure" checked: mainWindow.currentCategory === "settingsPage"; onTriggered: changeCategory(settingsPage); } ] Component { id: welcomePage; WelcomePage { onBookSelected: mainWindow.showBook(filename, currentPage); } } Component { id: bookViewer; Book { id: viewerRoot; onCurrentPageChanged: { contentList.setBookData(viewerRoot.file, "currentPage", viewerRoot.currentPage); } onTotalPagesChanged: { contentList.setBookData(viewerRoot.file, "totalPages", viewerRoot.totalPages); } } } Component { id: bookshelfTitle; Bookshelf { model: contentList.titleCategoryModel; headerText: i18nc("Title of the page with books grouped by the title start letters", "Group by Title"); onBookSelected: mainWindow.showBook(filename, currentPage); categoryName: "bookshelfTitle"; } } Component { id: bookshelfAdded; Bookshelf { model: contentList.newlyAddedCategoryModel; headerText: i18nc("Title of the page with all books ordered by which was added most recently", "Recently Added Books"); sectionRole: "created"; sectionCriteria: ViewSection.FullString; onBookSelected: mainWindow.showBook(filename, currentPage); categoryName: "bookshelfAdded"; } } Component { id: bookshelfSeries; Bookshelf { model: contentList.seriesCategoryModel; headerText: i18nc("Title of the page with books grouped by what series they are in", "Group by Series"); onBookSelected: mainWindow.showBook(filename, currentPage); categoryName: "bookshelfSeries"; } } Component { id: bookshelfAuthor; Bookshelf { model: contentList.authorCategoryModel; headerText: i18nc("Title of the page with books grouped by author", "Group by Author"); onBookSelected: mainWindow.showBook(filename, currentPage); categoryName: "bookshelfAuthor"; } } Component { id: bookshelfPublisher; Bookshelf { model: contentList; headerText: i18nc("Title of the page with books grouped by who published them", "Group by Publisher"); onBookSelected: mainWindow.showBook(filename, currentPage); categoryName: "bookshelfPublisher"; } } Component { id: bookshelfFolder; Bookshelf { model: contentList.folderCategoryModel; headerText: i18nc("Title of the page with books grouped by what folder they are in", "Filter by Folder"); onBookSelected: mainWindow.showBook(filename, currentPage); categoryName: "bookshelfFolder"; } } Component { id: bookshelf; Bookshelf { onBookSelected: mainWindow.showBook(filename, currentPage); } } Component { id: storePage; Store { } } Component { id: settingsPage; Settings { } } property string currentCategory: "welcomePage"; function changeCategory(categoryItem) { // Clear all the way to the welcome page if we change the category... mainWindow.pageStack.clear(); mainWindow.pageStack.push(categoryItem); currentCategory = mainWindow.pageStack.currentItem.categoryName; if (PLASMA_PLATFORM.substring(0, 5) === "phone") { globalDrawer.close(); } } } diff --git a/src/app/qml/SearchBox.qml b/src/app/qml/SearchBox.qml index 5c7da00..a735209 100644 --- a/src/app/qml/SearchBox.qml +++ b/src/app/qml/SearchBox.qml @@ -1,132 +1,132 @@ /* * Copyright (C) 2016 Dan Leinir Turthra Jensen * * 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 . * */ import QtQuick 2.2 import QtQuick.Controls 1.4 as QtControls import org.kde.kirigami 2.1 as Kirigami import org.kde.peruse 0.1 as Peruse import "listcomponents" as ListComponents /** * @brief A box that holds search results * * This shows up when selecting the search from * BookShelf or the WelcomePage. It draws BookTileTall * and CategoryTileTall for the entries it finds. */ Rectangle { id: root; property int maxHeight: parent.height; property alias model: searchFilterProxy.sourceModel; color: Kirigami.Theme.viewBackgroundColor; function activate() { searchField.forceActiveFocus(); Qt.inputMethod.show(); // Would be nice if this happened automatically, but... no such luck } signal bookSelected(string filename, int currentPage); clip: true; height: searchField.focus || searchField.text.length > 0 ? searchHeight : 0; Behavior on height { PropertyAnimation { duration: applicationWindow().animationDuration; easing.type: Easing.InOutQuad; } } property int searchHeight: searchField.text.length > 0 ? maxHeight : searchField.height; QtControls.TextField { id: searchField; anchors { top: parent.top; left: parent.left; right: parent.right; } placeholderText: i18nc("placeholder text for the search field", "Tap and type to search"); onTextChanged: { if(text.length > 0) { searchTimer.start(); } else { searchTimer.stop(); } } } Timer { id: searchTimer; interval: 250; repeat: false; running: false; onTriggered: searchFilterProxy.setFilterFixedString(searchField.text); } GridView { id: searchList; clip: true; anchors { top: searchField.bottom; left: parent.left; right: parent.right; bottom: parent.bottom; } footer: Item { width: parent.width; height: Kirigami.Units.iconSizes.large + Kirigami.Units.largeSpacing; } cellWidth: width / 2; cellHeight: Math.max( (root.height * 2 / 7), Math.min(cellWidth, (Kirigami.Units.iconSizes.enormous + Kirigami.Units.largeSpacing * 3 + Kirigami.Theme.defaultFont.pixelSize)) ); currentIndex: -1; model: Peruse.FilterProxy { id: searchFilterProxy; } function previousEntry() { if(currentIndex > 0) { currentIndex--; } } function nextEntry() { if(currentIndex < model.rowCount() - 1) { currentIndex++; } } delegate: Item { height: model.categoryEntriesCount === 0 ? bookTile.neededHeight : categoryTile.neededHeight; width: root.width / 2; ListComponents.CategoryTileTall { id: categoryTile; height: model.categoryEntriesCount > 0 ? neededHeight : 0; width: parent.width; count: model.categoryEntriesCount; title: model.title; entriesModel: model.categoryEntriesModel ? model.categoryEntriesModel : null; selected: searchList.currentIndex === index; } ListComponents.BookTileTall { id: bookTile; height: model.categoryEntriesCount < 1 ? neededHeight : 0; width: parent.width; - author: model.author ? model.author : i18nc("used for the author data in book lists if autor is empty", "(unknown)"); + author: model.author ? model.author : i18nc("used for the author data in book lists if author is empty", "(unknown)"); title: model.title; filename: model.filename; thumbnail: model.thumbnail; categoryEntriesCount: model.categoryEntriesCount; currentPage: model.currentPage; totalPages: model.totalPages; onBookSelected: root.bookSelected(filename, currentPage); selected: searchList.currentIndex === index; } } } } diff --git a/src/app/qml/Settings.qml b/src/app/qml/Settings.qml index 3f25611..dd2b8dc 100644 --- a/src/app/qml/Settings.qml +++ b/src/app/qml/Settings.qml @@ -1,185 +1,185 @@ /* * Copyright (C) 2015 Dan Leinir Turthra Jensen * * 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 . * */ import QtQuick 2.2 import QtQuick.Layouts 1.1 import QtQuick.Controls 2.2 as QtControls import QtQuick.Dialogs 1.0 import org.kde.kirigami 2.1 as Kirigami import org.kde.peruse 0.1 as Peruse import "listcomponents" as ListComponents /** * @brief This holds toggles and dials to configure Peruse. * * Its main purpose is to add and remove entries from the list of booklocations. */ Kirigami.Page { id: root; property string categoryName: "settingsPage"; title: i18nc("title of the settings page", "Settings"); Item { width: root.width - (root.leftPadding + root.rightPadding); height: root.height - (root.topPadding + root.bottomPadding); Column { width: parent.width; height: childrenRect.height; clip: true; Item { width: parent.width; height: Kirigami.Units.largeSpacing; } Item { height: folderHeader.height; width: parent.width - folderAdd.width - Kirigami.Units.smallSpacing; ListComponents.Section { id: folderHeader; text: i18nc("title of the list of search folders", "Search Folders"); } QtControls.ToolButton { id: folderAdd; contentItem: Kirigami.Icon { source: "list-add"; } onClicked: { if(PLASMA_PLATFORM.substring(0, 5) === "phone") { applicationWindow().pageStack.push(folderDlg); } else { desktopFolderDlg.open(); } } anchors { verticalCenter: parent.verticalCenter; left: parent.right; } } } Item { width: parent.width; height: Kirigami.Units.largeSpacing; } Repeater { model: peruseConfig.bookLocations; delegate: Kirigami.SwipeListItem { id: listItem; actions: [ Kirigami.Action { text: i18nc("remove the search folder from the list", "Delete"); iconName: "list-remove" onTriggered: peruseConfig.removeBookLocation(peruseConfig.bookLocations[index]); } ] QtControls.Label { anchors { verticalCenter: parent.verticalCenter; left: parent.left; leftMargin: Kirigami.Units.largeSpacing; } text: peruseConfig.bookLocations[index]; } } } } Rectangle { id: addingNewBooksProgress; color: Kirigami.Theme.viewBackgroundColor; anchors.fill: parent; opacity: 0; Behavior on opacity { PropertyAnimation { duration: applicationWindow().animationDuration; } } Connections { target: contentList.contentModel; onSearchCompleted: addingNewBooksProgress.opacity = 0; } enabled: opacity > 0; MouseArea { enabled: parent.enabled; anchors.fill: parent; } QtControls.Label { anchors { bottom: parent.verticalCenter; left: parent.left; right: parent.right; } horizontalAlignment: Text.AlignHCenter; text: i18nc("shown with a throbber when searching for books on the device", "Please wait while we find your books..."); } QtControls.BusyIndicator { id: loadingSpinner; anchors { top: parent.verticalCenter; left: parent.left; right: parent.right; } running: addingNewBooksProgress.enabled; } QtControls.Label { anchors { top: loadingSpinner.bottom; left: parent.left; right: parent.right; } horizontalAlignment: Text.AlignHCenter; text: contentList.count; } } Component { id: folderDlg; Kirigami.Page { id: root; title: "Select a folder" FileFinder { anchors.fill: parent; folder: peruseConfig.homeDir(); showFiles: false; onAccepted: { peruseConfig.addBookLocation(selectedItem()); applicationWindow().pageStack.pop(); root.doSearch(); } onAborted: applicationWindow().pageStack.pop(); } } } } FileDialog { id: desktopFolderDlg; - title: i18nc("title for the dialogue used to add a new search folder", "Select a folder"); + title: i18nc("@title:window dialogue used to add a new search folder", "Select a Folder"); selectFolder: true; folder: shortcuts.home; onAccepted: { peruseConfig.addBookLocation(desktopFolderDlg.fileUrl); root.doSearch(); } } function doSearch() { // Now search for new items in that locations... var locations = peruseConfig.bookLocations; addingNewBooksProgress.opacity = 1; contentList.contentModel.addLocation(locations[locations.length - 1]); contentList.contentModel.startSearch(); } } diff --git a/src/app/qml/WelcomePage.qml b/src/app/qml/WelcomePage.qml index 5e7a7f3..b5ff953 100644 --- a/src/app/qml/WelcomePage.qml +++ b/src/app/qml/WelcomePage.qml @@ -1,526 +1,526 @@ /* * Copyright (C) 2015 Dan Leinir Turthra Jensen * * 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 . * */ import QtQuick 2.2 import QtQuick.Layouts 1.1 import QtQuick.Controls 2.2 as QtControls import org.kde.kirigami 2.1 as Kirigami import org.kde.peruse 0.1 as Peruse import "listcomponents" as ListComponents /** * @brief The page that Peruse opens up on. * * The WelcomePage shares some resemblance to the * BookShelf pages in that it allows the user to select a comic, * but where BookShelf pages are really for discovery and searching * through categories and subcategories, the WelcomePage is primarily * for selecting the recently opened and new books, which the user is most likely * to look at when they want to read. * * It uses BookTileTall to show the selectable books, SearchBox to search books * and Section to indicate a subsection. */ Kirigami.Page { id: root; property string categoryName: "welcomePage"; title: i18nc("title of the welcome page", "Welcome"); signal bookSelected(string filename, int currentPage); function updateRecent() { startWithThese.updateRecentlyRead(); } property list mobileActions; property list desktopActions: [ Kirigami.Action { - text: i18n("Open selected book"); + text: i18n("Open Selected Book"); shortcut: "Return"; iconName: "document-open"; onTriggered: bookSelected(startWithThese.currentItem.filename, startWithThese.currentItem.currentPage); enabled: root.isCurrentPage && applicationWindow().deviceType === applicationWindow().deviceTypeDesktop; }, Kirigami.Action { - text: i18nc("select the previous book entry in the list", "Previous book"); + text: i18nc("select the previous book entry in the list", "Previous Book"); shortcut: StandardKey.MoveToPreviousChar; iconName: "go-previous"; onTriggered: startWithThese.selectPrevious(); enabled: root.isCurrentPage && applicationWindow().deviceType === applicationWindow().deviceTypeDesktop; }, Kirigami.Action { - text: i18nc("select the next book entry in the list", "Next book"); + text: i18nc("select the next book entry in the list", "Next Book"); shortcut: StandardKey.MoveToNextChar; iconName: "go-next"; onTriggered: startWithThese.selectNext(); enabled: root.isCurrentPage && applicationWindow().deviceType === applicationWindow().deviceTypeDesktop; } ] actions { contextualActions: PLASMA_PLATFORM.substring(0, 5) === "phone" ? mobileActions : desktopActions; main: Kirigami.Action { text: i18nc("search in the list of books (not inside the books)", "Search Books"); iconName: "system-search"; onTriggered: searchBox.activate(); enabled: root.isCurrentPage; } } Item { width: root.width - (root.leftPadding + root.rightPadding); height: root.height - root.topPadding; SearchBox { id: searchBox; anchors { top: parent.top; left: parent.left; right: parent.right; } model: contentList.newlyAddedCategoryModel; maxHeight: parent.height - titleContainer.height / 2; onBookSelected: root.bookSelected(filename, currentPage); } Item { id: titleContainer; anchors { top: searchBox.bottom; left: parent.left; right: parent.right; } height: applicationWindow().isLoading ? (parent.height / 2) : (appNameLabel.height + appDescriptionLabel.height + Kirigami.Units.largeSpacing); Behavior on height { PropertyAnimation { duration: applicationWindow().animationDuration; easing.type: Easing.InOutQuad; } } Kirigami.Heading { id: appNameLabel; anchors { left: parent.left; right: parent.right; bottom: parent.verticalCenter; } text: "Peruse"; horizontalAlignment: Text.AlignHCenter; } QtControls.Label { id: appDescriptionLabel; anchors { top: parent.verticalCenter; left: parent.left; right: parent.right; } text: i18nc("application subtitle", "Comic Book Reader"); horizontalAlignment: Text.AlignHCenter; } Rectangle { anchors.centerIn: parent; height: 1; color: Kirigami.Theme.textColor; width: appDescriptionLabel.paintedWidth; } } Flickable { id: startWithThese; property QtObject mostRecentlyRead0: fakeBook; property QtObject mostRecentlyRead1: fakeBook; property QtObject mostRecentlyRead2: fakeBook; property QtObject mostRecentlyRead3: fakeBook; property QtObject mostRecentlyRead4: fakeBook; property QtObject mostRecentlyRead5: fakeBook; property int mostRecentlyAdded0: -1; function updateRecentlyRead() { mostRecentlyAdded0 = -1; mostRecentlyRead0 = mostRecentlyRead1 = mostRecentlyRead2 = mostRecentlyRead3 = mostRecentlyRead4 = mostRecentlyRead5 = fakeBook; startWithThese.mostRecentlyRead0 = contentList.bookFromFile(peruseConfig.recentlyOpened[0]); startWithThese.mostRecentlyRead1 = contentList.bookFromFile(peruseConfig.recentlyOpened[1]); startWithThese.mostRecentlyRead2 = contentList.bookFromFile(peruseConfig.recentlyOpened[2]); startWithThese.mostRecentlyRead3 = contentList.bookFromFile(peruseConfig.recentlyOpened[3]); startWithThese.mostRecentlyRead4 = contentList.bookFromFile(peruseConfig.recentlyOpened[4]); startWithThese.mostRecentlyRead5 = contentList.bookFromFile(peruseConfig.recentlyOpened[5]); if(startWithThese.currentItem != null) { startWithThese.currentItem = rread0; } // the model might be null, if we haven't actually got any entries... so, let's check that // and just leave the whole thing empty in that case :) if(contentList.newlyAddedCategoryModel) { startWithThese.mostRecentlyAdded0 = 0; newItemsRepeater.model = Math.min(10, Math.floor((contentList.newlyAddedCategoryModel.rowCount() - 5))); } } Connections { target: peruseConfig; onRecentlyOpenedChanged: startWithThese.updateRecentlyRead(); } Connections { target: applicationWindow(); onIsLoadingChanged: { if(applicationWindow().isLoading === false) { startWithThese.updateRecentlyRead(); } } } anchors { top: titleContainer.bottom; left: parent.left; right: parent.right; bottom: parent.bottom; } opacity: applicationWindow().isLoading ? 0 : 1; Behavior on opacity { PropertyAnimation { duration: applicationWindow().animationDuration; } } contentWidth: width; contentHeight: recentItemsColumn.height; clip: true; property Item currentItem: null; property var itemArray: [rread0, rread1, rread2, rread3, rread4, rread5]; function selectNext() { var index = itemArray.indexOf(currentItem); if(index < itemArray.length) { var nextItem = itemArray[index + 1]; if(nextItem !== undefined && nextItem.height > 0) { currentItem = nextItem; } } } function selectPrevious() { var index = itemArray.indexOf(currentItem); if(index > 0) { currentItem = itemArray[index - 1]; } } Peruse.PropertyContainer { id: fakeBook; property string author: "unnamed"; property string title: "unnamed"; property string filename: ""; property string thumbnail: ""; property string currentPage: "0"; property string totalPages: "0"; } Column { id: recentItemsColumn; width: parent.width; height: childrenRect.height; ListComponents.Section { - text: i18nc("title of list of recently opened books", "Continue reading"); + text: i18nc("title of list of recently opened books", "Continue Reading"); width: startWithThese.width; height: rread0.height > 0 ? paintedHeight : 0; visible: height > 0; } Row { anchors.horizontalCenter: parent.horizontalCenter; width: childrenRect.width; height: childrenRect.height; ListComponents.BookTileTall { id: rread0; height: startWithThese.mostRecentlyRead0.readProperty("filename") != "" ? neededHeight : 0; width: startWithThese.width / 2; author: startWithThese.mostRecentlyRead0.readProperty("author"); title: startWithThese.mostRecentlyRead0.readProperty("title"); filename: startWithThese.mostRecentlyRead0.readProperty("filename"); thumbnail: startWithThese.mostRecentlyRead0.readProperty("thumbnail"); categoryEntriesCount: 0; currentPage: startWithThese.mostRecentlyRead0.readProperty("currentPage"); totalPages: startWithThese.mostRecentlyRead0.readProperty("totalPages"); onBookSelected: root.bookSelected(filename, currentPage); selected: startWithThese.currentItem === this; } ListComponents.BookTileTall { id: rread1; height: startWithThese.mostRecentlyRead1.readProperty("filename") != "" ? neededHeight : 0; width: startWithThese.width / 2; author: startWithThese.mostRecentlyRead1.readProperty("author"); title: startWithThese.mostRecentlyRead1.readProperty("title"); filename: startWithThese.mostRecentlyRead1.readProperty("filename"); thumbnail: startWithThese.mostRecentlyRead1.readProperty("thumbnail"); categoryEntriesCount: 0; currentPage: startWithThese.mostRecentlyRead1.readProperty("currentPage"); totalPages: startWithThese.mostRecentlyRead1.readProperty("totalPages"); onBookSelected: root.bookSelected(filename, currentPage); selected: startWithThese.currentItem === this; } } Row { anchors.horizontalCenter: parent.horizontalCenter; width: childrenRect.width; height: childrenRect.height; ListComponents.BookTileTall { id: rread2; height: startWithThese.mostRecentlyRead2.readProperty("filename") != "" ? neededHeight : 0; width: startWithThese.width / 4; author: startWithThese.mostRecentlyRead2.readProperty("author"); title: startWithThese.mostRecentlyRead2.readProperty("title"); filename: startWithThese.mostRecentlyRead2.readProperty("filename"); thumbnail: startWithThese.mostRecentlyRead2.readProperty("thumbnail"); categoryEntriesCount: 0; currentPage: startWithThese.mostRecentlyRead2.readProperty("currentPage"); totalPages: startWithThese.mostRecentlyRead2.readProperty("totalPages"); onBookSelected: root.bookSelected(filename, currentPage); selected: startWithThese.currentItem === this; } ListComponents.BookTileTall { id: rread3; height: startWithThese.mostRecentlyRead3.readProperty("filename") != "" ? neededHeight : 0; width: startWithThese.width / 4; author: startWithThese.mostRecentlyRead3.readProperty("author"); title: startWithThese.mostRecentlyRead3.readProperty("title"); filename: startWithThese.mostRecentlyRead3.readProperty("filename"); thumbnail: startWithThese.mostRecentlyRead3.readProperty("thumbnail"); categoryEntriesCount: 0; currentPage: startWithThese.mostRecentlyRead3.readProperty("currentPage"); totalPages: startWithThese.mostRecentlyRead3.readProperty("totalPages"); onBookSelected: root.bookSelected(filename, currentPage); selected: startWithThese.currentItem === this; } ListComponents.BookTileTall { id: rread4; height: startWithThese.mostRecentlyRead4.readProperty("filename") != "" ? neededHeight : 0; width: startWithThese.width / 4; author: startWithThese.mostRecentlyRead4.readProperty("author"); title: startWithThese.mostRecentlyRead4.readProperty("title"); filename: startWithThese.mostRecentlyRead4.readProperty("filename"); thumbnail: startWithThese.mostRecentlyRead4.readProperty("thumbnail"); categoryEntriesCount: 0; currentPage: startWithThese.mostRecentlyRead4.readProperty("currentPage"); totalPages: startWithThese.mostRecentlyRead4.readProperty("totalPages"); onBookSelected: root.bookSelected(filename, currentPage); selected: startWithThese.currentItem === this; } ListComponents.BookTileTall { id: rread5; height: startWithThese.mostRecentlyRead5.readProperty("filename") != "" ? neededHeight : 0; width: startWithThese.width / 4; author: startWithThese.mostRecentlyRead5.readProperty("author"); title: startWithThese.mostRecentlyRead5.readProperty("title"); filename: startWithThese.mostRecentlyRead5.readProperty("filename"); thumbnail: startWithThese.mostRecentlyRead5.readProperty("thumbnail"); categoryEntriesCount: 0; currentPage: startWithThese.mostRecentlyRead5.readProperty("currentPage"); totalPages: startWithThese.mostRecentlyRead5.readProperty("totalPages"); onBookSelected: root.bookSelected(filename, currentPage); selected: startWithThese.currentItem === this; } } ListComponents.Section { - text: i18nc("title of list of recently discovered books", "Recently added"); + text: i18nc("title of list of recently discovered books", "Recently Added"); width: startWithThese.width; height: paintedHeight; } QtControls.Label { visible: !firstRecentlyAddedBook.visible; height: visible ? paintedHeight : 0; width: startWithThese.width; - text: i18nc("description text for the recently discovered list, shown when no items exist in the search paths", "You have no comics on your device. Please put some into your Documents or Downloads folder (for example by downloading some) and they will show up here!"); + text: i18nc("description text for the recently discovered list, shown when no items exist in the search paths", "You have no comics on your device. Please put some into your Documents or Downloads folder (for example by downloading some) and they will show up here."); wrapMode: Text.WordWrap; horizontalAlignment: Text.AlignHCenter; } Row { height: childrenRect.height; width: childrenRect.width; anchors.horizontalCenter: parent.horizontalCenter; ListComponents.BookTileTall { id: firstRecentlyAddedBook; visible: filename !== ""; height: visible ? neededHeight : 0; width: startWithThese.width / 2; property QtObject book: contentList.newlyAddedCategoryModel ? contentList.newlyAddedCategoryModel.get(startWithThese.mostRecentlyAdded0) : fakeBook; author: book.readProperty("author"); title: book.readProperty("title"); filename: book.readProperty("filename"); thumbnail: book.readProperty("thumbnail"); categoryEntriesCount: 0; currentPage: book.readProperty("currentPage"); totalPages: book.readProperty("totalPages"); onBookSelected: root.bookSelected(filename, currentPage); } ListComponents.BookTileTall { visible: filename !== ""; height: visible ? neededHeight : 0; width: startWithThese.width / 2; property QtObject book: contentList.newlyAddedCategoryModel ? contentList.newlyAddedCategoryModel.get(startWithThese.mostRecentlyAdded0 + 1) : fakeBook; author: book.readProperty("author"); title: book.readProperty("title"); filename: book.readProperty("filename"); thumbnail: book.readProperty("thumbnail"); categoryEntriesCount: 0; currentPage: book.readProperty("currentPage"); totalPages: book.readProperty("totalPages"); onBookSelected: root.bookSelected(filename, currentPage); } } Row { height: childrenRect.height; width: childrenRect.width; anchors.horizontalCenter: parent.horizontalCenter; ListComponents.BookTileTall { visible: filename !== ""; height: visible ? neededHeight : 0; width: startWithThese.width / 3; property QtObject book: contentList.newlyAddedCategoryModel ? contentList.newlyAddedCategoryModel.get(startWithThese.mostRecentlyAdded0 + 2) : fakeBook; author: book.readProperty("author"); title: book.readProperty("title"); filename: book.readProperty("filename"); thumbnail: book.readProperty("thumbnail"); categoryEntriesCount: 0; currentPage: book.readProperty("currentPage"); totalPages: book.readProperty("totalPages"); onBookSelected: root.bookSelected(filename, currentPage); } ListComponents.BookTileTall { visible: filename !== ""; height: visible ? neededHeight : 0; width: startWithThese.width / 3; property QtObject book: contentList.newlyAddedCategoryModel ? contentList.newlyAddedCategoryModel.get(startWithThese.mostRecentlyAdded0 + 3) : fakeBook; author: book.readProperty("author"); title: book.readProperty("title"); filename: book.readProperty("filename"); thumbnail: book.readProperty("thumbnail"); categoryEntriesCount: 0; currentPage: book.readProperty("currentPage"); totalPages: book.readProperty("totalPages"); onBookSelected: root.bookSelected(filename, currentPage); } ListComponents.BookTileTall { visible: filename !== ""; height: visible ? neededHeight : 0; width: startWithThese.width / 3; property QtObject book: contentList.newlyAddedCategoryModel ? contentList.newlyAddedCategoryModel.get(startWithThese.mostRecentlyAdded0 + 4) : fakeBook; author: book.readProperty("author"); title: book.readProperty("title"); filename: book.readProperty("filename"); thumbnail: book.readProperty("thumbnail"); categoryEntriesCount: 0; currentPage: book.readProperty("currentPage"); totalPages: book.readProperty("totalPages"); onBookSelected: root.bookSelected(filename, currentPage); } } Repeater { id: newItemsRepeater; model: 0; Row { width: childrenRect.width; height: childrenRect.height; anchors.horizontalCenter: parent.horizontalCenter; ListComponents.BookTileTall { visible: filename != ""; height: visible ? neededHeight : 0; width: startWithThese.width / 4; property QtObject book: contentList.newlyAddedCategoryModel ? contentList.newlyAddedCategoryModel.get((index * 4) + 5) : fakeBook; author: book.readProperty("author"); title: book.readProperty("title"); filename: book.readProperty("filename"); thumbnail: book.readProperty("thumbnail"); categoryEntriesCount: 0; currentPage: book.readProperty("currentPage"); totalPages: book.readProperty("totalPages"); onBookSelected: root.bookSelected(filename, currentPage); } ListComponents.BookTileTall { visible: filename != ""; height: visible ? neededHeight : 0; width: startWithThese.width / 4; property QtObject book: contentList.newlyAddedCategoryModel ? contentList.newlyAddedCategoryModel.get((index * 4) + 6) : fakeBook; author: book.readProperty("author"); title: book.readProperty("title"); filename: book.readProperty("filename"); thumbnail: book.readProperty("thumbnail"); categoryEntriesCount: 0; currentPage: book.readProperty("currentPage"); totalPages: book.readProperty("totalPages"); onBookSelected: root.bookSelected(filename, currentPage); } ListComponents.BookTileTall { visible: filename != ""; height: visible ? neededHeight : 0; width: startWithThese.width / 4; property QtObject book: contentList.newlyAddedCategoryModel ? contentList.newlyAddedCategoryModel.get((index * 4) + 7) : fakeBook; author: book.readProperty("author"); title: book.readProperty("title"); filename: book.readProperty("filename"); thumbnail: book.readProperty("thumbnail"); categoryEntriesCount: 0; currentPage: book.readProperty("currentPage"); totalPages: book.readProperty("totalPages"); onBookSelected: root.bookSelected(filename, currentPage); } ListComponents.BookTileTall { visible: filename != ""; height: visible ? neededHeight : 0; width: startWithThese.width / 4; property QtObject book: contentList.newlyAddedCategoryModel ? contentList.newlyAddedCategoryModel.get((index * 4) + 8) : fakeBook; author: book.readProperty("author"); title: book.readProperty("title"); filename: book.readProperty("filename"); thumbnail: book.readProperty("thumbnail"); categoryEntriesCount: 0; currentPage: book.readProperty("currentPage"); totalPages: book.readProperty("totalPages"); onBookSelected: root.bookSelected(filename, currentPage); } } } Item { width: parent.width; height: Kirigami.Units.iconSizes.large + Kirigami.Units.largeSpacing; } } } Item { id: loadingProgress; anchors { top: parent.verticalCenter; left: parent.left; right: parent.right; bottom: parent.bottom; } opacity: applicationWindow().isLoading ? 1 : 0; Behavior on opacity { PropertyAnimation { duration: applicationWindow().animationDuration; } } QtControls.Label { anchors { bottom: parent.verticalCenter; left: parent.left; right: parent.right; } horizontalAlignment: Text.AlignHCenter; text: i18nc("shown with a throbber when searching for books on the device", "Please wait while we find your books..."); } QtControls.BusyIndicator { id: loadingSpinner; anchors { top: parent.verticalCenter; left: parent.left; right: parent.right; } running: applicationWindow().isLoading; } QtControls.Label { anchors { top: loadingSpinner.bottom; left: parent.left; right: parent.right; } horizontalAlignment: Text.AlignHCenter; text: contentList.count; } } } } diff --git a/src/app/qml/listcomponents/BookTile.qml b/src/app/qml/listcomponents/BookTile.qml index ad5ab53..3282019 100644 --- a/src/app/qml/listcomponents/BookTile.qml +++ b/src/app/qml/listcomponents/BookTile.qml @@ -1,258 +1,258 @@ /* * Copyright (C) 2015 Dan Leinir Turthra Jensen * * 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 . * */ import QtQuick 2.1 import QtQuick.Controls 2.2 as QtControls import org.kde.kirigami 2.1 as Kirigami /** * @brief small window with book information. * * This shows a bit of information about the book and gives a * selector with the other books in the series. * * It pops up after finishing a book in Book, and when pressing long * on a BookTileTall item in BookShelf. * */ Item { id: root; property bool selected: false; property alias title: bookTitle.text; property string author; property string publisher; property alias filename: bookFile.text; property alias thumbnail: coverImage.source; property int categoryEntriesCount; property string currentPage; property string totalPages; signal bookSelected(string filename, int currentPage); signal bookDeleteRequested(); property int neededHeight: bookCover.height;// + bookAuthorLabel.height + bookFile.height + Kirigami.Units.smallSpacing * 4; visible: height > 1; enabled: visible; clip: true; Rectangle { anchors.fill: parent; color: Kirigami.Theme.highlightColor; opacity: root.selected ? 1 : 0; Behavior on opacity { NumberAnimation { duration: Kirigami.Units.shortDuration; } } } Item { id: bookCover; anchors { top: parent.top; left: parent.left; } width: root.width / 3; height: width * 1.5; Image { id: coverImage; anchors { fill: parent; margins: Kirigami.Units.smallSpacing; } asynchronous: true; fillMode: Image.PreserveAspectFit; } MouseArea { anchors.fill: parent; onClicked: root.bookSelected(root.filename, root.currentPage); } } Kirigami.Heading { id: bookTitle; anchors { top: parent.top; leftMargin: Kirigami.Units.smallSpacing; left: bookCover.right; right: parent.right; } maximumLineCount: 1; elide: Text.ElideMiddle; font.weight: Font.Bold; MouseArea { anchors.fill: parent; onClicked: root.bookSelected(root.filename, root.currentPage); } Rectangle { anchors { left: parent.left; top: parent.baseline; topMargin: 2; } height: 2; width: parent.paintedWidth; color: Kirigami.Theme.linkColor; } } QtControls.Label { id: bookAuthorLabel; anchors { top: bookTitle.bottom; left: bookCover.right; leftMargin: Kirigami.Units.smallSpacing; } width: paintedWidth; text: "Author"; font.bold: true; } QtControls.Label { id: bookAuthor; anchors { top: bookTitle.bottom; left: bookAuthorLabel.right; leftMargin: Kirigami.Units.smallSpacing; right: parent.right; } elide: Text.ElideRight; text: root.author === "" ? "(unknown)" : root.author; opacity: (text === "(unknown)" || text === "") ? 0.3 : 1; } QtControls.Label { id: bookPublisherLabel; anchors { top: bookAuthorLabel.bottom; left: bookCover.right; leftMargin: Kirigami.Units.smallSpacing; } width: paintedWidth; text: "Publisher"; font.bold: true; } QtControls.Label { id: bookPublisher; anchors { top: bookAuthor.bottom; left: bookPublisherLabel.right; leftMargin: Kirigami.Units.smallSpacing; right: parent.right; } elide: Text.ElideRight; text: root.publisher === "" ? "(unknown)" : root.publisher; opacity: (text === "(unknown)" || text === "") ? 0.3 : 1; } QtControls.Label { id: bookFile; anchors { top: bookPublisherLabel.bottom; left: bookCover.right; leftMargin: Kirigami.Units.smallSpacing; right: parent.right; } elide: Text.ElideMiddle; opacity: 0.3; font.pointSize: Kirigami.Theme.defaultFont.pointSize * 0.8; maximumLineCount: 1; } Item { id: descriptionContainer; anchors { top: bookFile.bottom; left: bookCover.right; right: parent.right; bottom: deleteBase.top; margins: Kirigami.Units.smallSpacing; } QtControls.Label { anchors.fill: parent; verticalAlignment: Text.AlignTop; text: i18nc("Placeholder text for the book description field when no description is set", "(no description available for this book)"); opacity: 0.3; } } Item { id: deleteBase; anchors { left: bookCover.right; leftMargin: Kirigami.Units.smallSpacing; right: parent.right; bottom: parent.bottom; } height: deleteButton.height + Kirigami.Units.smallSpacing * 2; Behavior on height { PropertyAnimation { duration: applicationWindow().animationDuration; easing.type: Easing.InOutQuad; } } states: [ State { name: "confirmDelete"; PropertyChanges { target: deleteButton; opacity: 0; } PropertyChanges { target: deleteConfirmBase; opacity: 1; } PropertyChanges { target: deleteBase; height: deleteConfirmBase.height; } } ] QtControls.Button { id: deleteButton; - text: i18nc("Spawn inline dialog box to confirm permanent removal of this book", "Delete from device"); + text: i18nc("Spawn inline dialog box to confirm permanent removal of this book", "Delete from Device"); anchors { bottom: parent.bottom; right: parent.right; margins: Kirigami.Units.smallSpacing; } // iconName: "edit-delete"; onClicked: deleteBase.state = "confirmDelete"; Behavior on opacity { PropertyAnimation { duration: applicationWindow().animationDuration; easing.type: Easing.InOutQuad; } } } Item { id: deleteConfirmBase; opacity: 0; width: parent.width; Behavior on opacity { PropertyAnimation { duration: applicationWindow().animationDuration; easing.type: Easing.InOutQuad; } } height: yesDelete.height + confirmDeleteLabel.height + Kirigami.Units.largeSpacing * 2 + Kirigami.Units.smallSpacing; QtControls.Label { id: confirmDeleteLabel; anchors { top: parent.top; topMargin: Kirigami.Units.largeSpacing; left: parent.left; right: parent.right; } height: paintedHeight; wrapMode: Text.WordWrap; horizontalAlignment: Text.AlignHCenter; text: i18nc("Dialog text for delete book dialog", "Are you sure you want to delete this from your device?"); } QtControls.Button { id: yesDelete; anchors { top: confirmDeleteLabel.bottom; topMargin: Kirigami.Units.smallSpacing; right: parent.horizontalCenter; rightMargin: (parent.width - width) / 4; } - text: i18nc("Confirmation button for book delete dialog", "Yes, really delete"); + text: i18nc("Confirmation button for book delete dialog", "Yes, Really Delete"); // iconName: "dialog-ok"; onClicked: root.bookDeleteRequested(); } QtControls.Button { anchors { top: confirmDeleteLabel.bottom; topMargin: Kirigami.Units.smallSpacing; left: parent.horizontalCenter; leftMargin: (parent.width - width) / 4; } - text: i18nc("Cancellation button or book delete dialog", "No, cancel delete"); + text: i18nc("Cancellation button or book delete dialog", "No, Cancel Delete"); // iconName: "dialog-cancel"; onClicked: deleteBase.state = ""; } } } } diff --git a/src/creator/qml/AddPageSheet.qml b/src/creator/qml/AddPageSheet.qml index 9c1af70..25b667c 100644 --- a/src/creator/qml/AddPageSheet.qml +++ b/src/creator/qml/AddPageSheet.qml @@ -1,86 +1,86 @@ /* * Copyright (C) 2015 Dan Leinir Turthra Jensen * * 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 . * */ import QtQuick 2.2 import QtQuick.Dialogs 1.2 import QtQuick.Controls 2.2 as QtControls import org.kde.kirigami 2.1 as Kirigami /** * @brief overlay with options for adding a page. * * It is accessed from Book */ Kirigami.OverlaySheet { id: root; property int addPageAfter: 0; property QtObject model; Column { height: childrenRect.height; spacing: Kirigami.Units.smallSpacing; Kirigami.Heading { width: parent.width; height: paintedHeight; - text: i18nc("title text for the add page sheet", "Add A Page?"); + text: i18nc("title text for the add page sheet", "Add a Page?"); } QtControls.Label { width: parent.width; height: paintedHeight; text: i18nc("help text for the add page sheet", "Please select the method you want to add the new page. No changes will be made outside of the project by performing these actions."); wrapMode: Text.WrapAtWordBoundaryOrAnywhere; } Item { width: parent.width; height: Kirigami.Units.largeSpacing; } QtControls.Button { anchors.horizontalCenter: parent.horizontalCenter; // iconName: "document-open"; - text: i18nc("button to add a page by finding an image on the filesystem and copying it into the book", "Copy an image from your device"); + text: i18nc("@action:button add a page by finding an image on the filesystem and copying it into the book", "Copy Image from Device"); onClicked: openDlg.open(); FileDialog { id: openDlg; - title: i18nc("Title of a standard file open dialog used to find a page to add to the book", "Please choose an image to add"); + title: i18nc("@title:window standard file open dialog used to find a page to add to the book", "Please Choose an Image to Add"); folder: mainWindow.homeDir(); property int splitPos: osIsWindows ? 8 : 7; onAccepted: { if(openDlg.fileUrl.toString().substring(0, 7) === "file://") { root.model.addPageFromFile(openDlg.fileUrl.toString().substring(splitPos)); root.close(); } } onRejected: { // Just do nothing, we don't really care... } } } QtControls.Button { anchors.horizontalCenter: parent.horizontalCenter; // iconName: "document-new"; - text: i18nc("button to add a page by creating a new image using an image editor", "Create a new image using an image editor"); + text: i18nc("@action:button add a page by creating a new image using an image editor", "Create a New Image Using an Image Editor"); } QtControls.Button { anchors.horizontalCenter: parent.horizontalCenter; // iconName: "camera"; - text: i18nc("button to add a page by taking a photo with a camera", "Take a photo and add that"); + text: i18nc("@action:button add a page by taking a photo with a camera", "Take a Photo and Add That"); } } } diff --git a/src/creator/qml/Book.qml b/src/creator/qml/Book.qml index cc7c23d..d0da2b9 100644 --- a/src/creator/qml/Book.qml +++ b/src/creator/qml/Book.qml @@ -1,201 +1,201 @@ /* * Copyright (C) 2015 Dan Leinir Turthra Jensen * * 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 . * */ import QtQuick 2.2 import QtQuick.Controls 2.2 as QtControls import org.kde.kirigami 2.1 as Kirigami import org.kde.peruse 0.1 as Peruse /** * @brief the page that deals with editing the book. * * This is primarily a list of pages that can be moved around. These are inside * Kirigami ListSwipeItems. * * This also has a button to add pages, which calls up AddPageSheet. * And a button to edit the book metadata, which calls up BookMetainfoPage. */ Kirigami.ScrollablePage { id: root; property string categoryName: "book"; title: i18nc("title of the main book editor page", "Editing %1", bookModel.title == "" ? root.filename : bookModel.title); property string filename; actions { left: addPageSheet.opened ? null : saveBookAction; main: addPageSheet.opened ? closeAddPageSheetAction : defaultMainAction; right: addPageSheet.opened ? null : addPageAction; } Kirigami.Action { id: saveBookAction; text: i18nc("Saves the book to a file on disk", "Save Book"); iconName: "document-save"; onTriggered: bookModel.saveBook(); enabled: bookModel.hasUnsavedChanges; } Kirigami.Action { id: addPageAction; text: i18nc("adds a new page at the end of the book", "Add Page"); iconName: "list-add"; onTriggered: addPage(bookModel.pageCount); } Kirigami.Action { id: defaultMainAction; text: i18nc("causes a dialog to show in which the user can edit the meta information for the entire book", "Edit Metainfo"); iconName: "document-edit"; onTriggered: pageStack.push(editMetaInfo); } Kirigami.Action { id: closeAddPageSheetAction; - text: i18nc("closes the the add page sheet", "Do Not Add A Page"); + text: i18nc("closes the the add page sheet", "Do not Add a Page"); iconName: "dialog-cancel"; onTriggered: addPageSheet.close(); } function addPage(afterWhatIndex) { addPageSheet.addPageAfter = afterWhatIndex; addPageSheet.open(); } ListView { id: bookList; model: Peruse.ArchiveBookModel { id: bookModel; qmlEngine: globalQmlEngine; readWrite: true; filename: root.filename; } Component { id: editMetaInfo; BookMetainfoPage { model: bookModel; } } Component { id: editBookPage; BookPage { model: bookModel; onSave: { bookList.updateTitle(index, currentPage.title("")); } } } function updateTitle(index, title) { //Need to add feature to update data here. } delegate: Kirigami.SwipeListItem { id: listItem; height: Kirigami.Units.iconSizes.huge + Kirigami.Units.smallSpacing * 2; supportsMouseEvents: true; onClicked: ; actions: [ Kirigami.Action { text: i18nc("swap the position of this page with the previous one", "Move Up"); iconName: "go-up" onTriggered: { bookModel.swapPages(model.index, model.index - 1); } enabled: model.index > 0; visible: enabled; }, Kirigami.Action { text: i18nc("swap the position of this page with the next one", "Move Down"); iconName: "go-down" onTriggered: { bookModel.swapPages(model.index, model.index + 1); } enabled: model.index < bookModel.pageCount - 1; visible: enabled; }, Kirigami.Action { text: i18nc("remove the page from the book", "Delete Page"); iconName: "list-remove" onTriggered: {} }, Kirigami.Action { text: i18nc("add a page to the book after this one", "Add Page After This"); iconName: "list-add" onTriggered: root.addPage(model.index); }, Kirigami.Action { text: i18nc("Edit page data such as title, frames, etc.", "Edit Page"); iconName: "document-edit"; onTriggered: { pageStack.push(editBookPage, { index: model.index, pageUrl: model.url }) } } ] Item { anchors.fill: parent; Item { id: bookCover; anchors { top: parent.top; left: parent.left; bottom: parent.bottom; } width: height; Image { id: coverImage; anchors { fill: parent; margins: Kirigami.Units.smallSpacing; } asynchronous: true; fillMode: Image.PreserveAspectFit; source: model.url; } } QtControls.Label { anchors { verticalCenter: parent.verticalCenter; left: bookCover.right; leftMargin: Kirigami.Units.largeSpacing; } text: model.title; } } } Rectangle { id: processingBackground; anchors.fill: parent; opacity: bookModel.processing ? 0.5 : 0; Behavior on opacity { PropertyAnimation { duration: mainWindow.animationDuration; } } MouseArea { anchors.fill: parent; enabled: parent.opacity > 0; onClicked: { } } } QtControls.BusyIndicator { anchors { horizontalCenter: processingBackground.horizontalCenter; top: parent.top topMargin: x; } running: processingBackground.opacity > 0; visible: running; } } AddPageSheet { id: addPageSheet; model: bookModel; } } diff --git a/src/creator/qml/BookMetainfoPage.qml b/src/creator/qml/BookMetainfoPage.qml index fad931c..9eacfb5 100644 --- a/src/creator/qml/BookMetainfoPage.qml +++ b/src/creator/qml/BookMetainfoPage.qml @@ -1,1055 +1,1055 @@ /* * Copyright (C) 2015 Dan Leinir Turthra Jensen * * 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 . * */ import QtQuick 2.2 import QtQuick.Controls 2.2 as QtControls import org.kde.kirigami 2.1 as Kirigami import "metainfoeditors" /** * @brief Page with form to edit the comic metadata. * * Most metadata entries are quite simple. * * Others, like Author, need a dedicated entry editor (AuthorEntryEditor). */ Kirigami.ScrollablePage { id: root; title: i18nc("title text for the book meta information editor sheet", "Edit Meta Information"); property QtObject model; actions { main: saveAndCloseAction; } Kirigami.Action { id: saveAndCloseAction; text: i18nc("Saves the remaining unsaved edited fields and closes the metainfo editor", "Save and Close Editor"); iconName: "dialog-ok"; shortcut: "Esc"; onTriggered: { // Save the default title/annotation/keywords. root.model.acbfData.metaData.bookInfo.setTitle(defaultTitle.text, ""); root.model.acbfData.metaData.bookInfo.setAnnotation(defaultAnnotation.text.split("\n\n"), ""); var keywords = defaultKeywords.text.split(",") for (var i in keywords) { keywords[i] = keywords[i].trim(); } root.model.acbfData.metaData.bookInfo.setKeywords(keywords, ""); // Ensure there's a default language entry. if (root.model.acbfData.metaData.bookInfo.languageEntryList.indexOf("") === -1) { root.model.acbfData.metaData.bookInfo.addLanguage(""); } root.model.setDirty(); pageStack.pop(); } } Column { id: contentColumn; width: root.width - (root.leftPadding + root.rightPadding); height: childrenRect.height; spacing: Kirigami.Units.smallSpacing; Kirigami.Heading { width: parent.width; height: paintedHeight + Kirigami.Units.smallSpacing * 2; text: i18nc("label text for the edit field for the book title", "Title"); } Item { width: parent.width; height: Kirigami.Units.smallSpacing; } QtControls.TextField { id: defaultTitle; width: parent.width; placeholderText: i18nc("placeholder text for default title text-input", "Write to add default title"); text:root.model.acbfData ? root.model.acbfData.metaData.bookInfo.title("") : ""; } Kirigami.Heading { width: parent.width; height: paintedHeight + Kirigami.Units.smallSpacing * 2; text: i18nc("label text for the edit field for the default annotation", "Annotation"); } Item { width: parent.width; height: Kirigami.Units.smallSpacing; } QtControls.TextArea { id: defaultAnnotation; width: parent.width; placeholderText: i18nc("placeholder text for default annotiation text-area", "Write to add default annotation"); text:root.model.acbfData ? root.model.acbfData.metaData.bookInfo.annotation("").join("\n\n") : ""; } Kirigami.Heading { width: parent.width; height: paintedHeight + Kirigami.Units.smallSpacing * 2; text: i18nc("label text for the edit field for the keyword list", "Keywords"); } Item { width: parent.width; height: Kirigami.Units.smallSpacing; } QtControls.TextField { id: defaultKeywords; width: parent.width; - placeholderText: i18nc("placeholder text for the add new keyword text entry", "Write a comma seperated list of keywords."); + placeholderText: i18nc("placeholder text for the add new keyword text entry", "Write a comma separated list of keywords."); text:root.model.acbfData ? root.model.acbfData.metaData.bookInfo.keywords("").join(", ") : ""; } Kirigami.Heading { width: parent.width; height: paintedHeight + Kirigami.Units.smallSpacing * 2; - text: i18nc("label text for the edit field for the author list", "Authors (" + authorRepeater.count + ")"); + text: i18nc("label text for the edit field for the author list", "Authors (%1)", authorRepeater.count); } Repeater { id: authorRepeater; model: root.model.acbfData ? root.model.acbfData.metaData.bookInfo.authorNames : 0; delegate: QtControls.Label { width: parent.width - removeAuthorButton.width - Kirigami.Units.smallSpacing; text: modelData.length > 0 ? modelData : "(unnamed)"; QtControls.Button { id: editAuthorButton; anchors { right: removeAuthorButton.left; leftMargin: Kirigami.Units.smallSpacing; } contentItem: Kirigami.Icon { source: "document-edit"; } height: parent.height; width: height; onClicked: { authorEditor.index = model.index; authorEditor.open(); } } QtControls.Button { id: removeAuthorButton; anchors { left: parent.right; leftMargin: Kirigami.Units.smallSpacing; } contentItem: Kirigami.Icon { source: "list-remove"; } height: parent.height; width: height; onClicked: { // When removing, set the model dirty first, and then remove the entry to avoid reference errors. root.model.setDirty(); root.model.acbfData.metaData.bookInfo.removeAuthor(index); } } } } Item { width: parent.width; height: Kirigami.Units.smallSpacing; } QtControls.TextField { width: parent.width - addAuthorButton.width - Kirigami.Units.smallSpacing; placeholderText: i18nc("placeholder text for the add new author text entry", "Write to add new author (nickname)"); Keys.onReturnPressed: addAuthor(); function addAuthor() { if(text !== "") { // Just add an author where only the nickname is defined root.model.acbfData.metaData.bookInfo.addAuthor("", "", "", "", "", text, [""], [""]); root.model.setDirty(); text = ""; } } QtControls.Button { id: addAuthorButton; anchors { left: parent.right; leftMargin: Kirigami.Units.smallSpacing; } contentItem: Kirigami.Icon { source: "list-add"; } height: parent.height; width: height; onClicked: parent.addAuthor(); } } Kirigami.Heading { width: parent.width; height: paintedHeight + Kirigami.Units.smallSpacing * 2; text: i18nc("label text for the edit field for the genre list", "Genres"); } Repeater { model: root.model.acbfData ? root.model.acbfData.metaData.bookInfo.genres : 0; delegate: Item { width: parent.width; height: childrenRect.height; QtControls.Label { id: genreText; width: parent.width - removeGenreButton.width - Kirigami.Units.smallSpacing; text: modelData; QtControls.Button { id: removeGenreButton; anchors { left: parent.right; leftMargin: Kirigami.Units.smallSpacing; } contentItem: Kirigami.Icon { source: "list-remove"; } height: parent.height; width: height; onClicked: { root.model.setDirty(); root.model.acbfData.metaData.bookInfo.removeGenre(modelData); } } } QtControls.Slider { anchors { top: genreText.bottom; topMargin: Kirigami.Units.smallSpacing; } from: 0; to: 100; stepSize: 1.0; width: genreText.width; value: root.model.acbfData.metaData.bookInfo.genrePercentage(modelData); onValueChanged: { if(value > 0 && value !== root.model.acbfData.metaData.bookInfo.genrePercentage(modelData)) { root.model.acbfData.metaData.bookInfo.setGenre(modelData, value); root.model.setDirty(); } } } } } Item { width: parent.width; height: Kirigami.Units.smallSpacing; } QtControls.ComboBox { width: parent.width - addGenreButton.width - Kirigami.Units.smallSpacing; model: root.model.acbfData ? root.model.acbfData.metaData.bookInfo.availableGenres().filter(checkGenreInUse) : 0; Keys.onReturnPressed: addGenre(); function addGenre() { root.model.acbfData.metaData.bookInfo.setGenre(currentText); root.model.setDirty(); currentIndex=0; } function checkGenreInUse (genre) { return root.model.acbfData.metaData.bookInfo.genres.indexOf(genre) === -1; } QtControls.Button { id: addGenreButton; anchors { left: parent.right; leftMargin: Kirigami.Units.smallSpacing; } contentItem: Kirigami.Icon { source: "list-add"; } height: parent.height; width: height; onClicked: parent.addGenre(); } } Kirigami.Heading { width: parent.width; height: paintedHeight + Kirigami.Units.smallSpacing * 2; text: i18nc("label text for the edit field for the character list", "Characters"); } Repeater { model: root.model.acbfData ? root.model.acbfData.metaData.bookInfo.characters : 0; delegate: QtControls.TextField { width: parent.width - removeCharacterButton.width - Kirigami.Units.smallSpacing; text: modelData; onEditingFinished: root.model.acbfData.metaData.bookInfo.characters[index] = text; QtControls.Button { id: removeCharacterButton; anchors { left: parent.right; leftMargin: Kirigami.Units.smallSpacing; } contentItem: Kirigami.Icon { source: "list-remove"; } height: parent.height; width: height; onClicked: { root.model.setDirty(); root.model.acbfData.metaData.bookInfo.removeCharacter(modelData); } } } } Item { width: parent.width; height: Kirigami.Units.smallSpacing; } QtControls.TextField { width: parent.width - addCharacterButton.width - Kirigami.Units.smallSpacing; placeholderText: i18nc("placeholder text for the add new character text entry", "Write to add new character"); Keys.onReturnPressed: addCharacter(); function addCharacter() { if(text !== "") { root.model.acbfData.metaData.bookInfo.addCharacter(text); root.model.setDirty(); text = ""; } } QtControls.Button { id: addCharacterButton; anchors { left: parent.right; leftMargin: Kirigami.Units.smallSpacing; } contentItem: Kirigami.Icon { source: "list-add"; } height: parent.height; width: height; onClicked: parent.addCharacter(); } } Kirigami.Heading { width: parent.width; height: paintedHeight + Kirigami.Units.smallSpacing * 2; text: i18nc("label text for the edit field for the sequence list", "Sequence"); } Repeater { id: sequenceListRepeater; model: root.model.acbfData ? root.model.acbfData.metaData.bookInfo.sequenceCount : 0; delegate: Item { width: parent.width; height: childrenRect.height; function updateSeries() { root.model.acbfData.metaData.bookInfo.sequence(modelData).title = seriesTextField.text; if (numberField.value !== root.model.acbfData.metaData.bookInfo.sequence(modelData).number) { root.model.acbfData.metaData.bookInfo.sequence(modelData).number = numberField.value; } if (volumeField.value !== root.model.acbfData.metaData.bookInfo.sequence(modelData).volume) { root.model.acbfData.metaData.bookInfo.sequence(modelData).volume = volumeField.value; } root.model.setDirty(); } QtControls.TextField { id: seriesTextField; width: parent.width - removeSequenceButton.width - Kirigami.Units.smallSpacing; text: root.model.acbfData.metaData.bookInfo.sequence(modelData).title; onEditingFinished: parent.updateSeries(); } QtControls.Label { - text: i18nc("Label for sequence number:","Number:"); + text: i18nc("Label for sequence number","Number:"); id: sequenceNumberLabel; height:numberField.height; anchors { top: seriesTextField.bottom; topMargin: Kirigami.Units.smallSpacing; } } QtControls.SpinBox { anchors { top: seriesTextField.bottom; topMargin: Kirigami.Units.smallSpacing; left: sequenceNumberLabel.right; leftMargin: Kirigami.Units.smallSpacing; } value : root.model.acbfData.metaData.bookInfo.sequence(modelData).number; width : ((seriesTextField.width+Kirigami.Units.smallSpacing)/2)-(sequenceNumberLabel.width+Kirigami.Units.smallSpacing); id: numberField; onValueChanged: parent.updateSeries(); from: 0; to: 99999; editable: true; } QtControls.Label { - text: i18nc("Label for sequence Volume:","Volume:"); + text: i18nc("Label for sequence volume","Volume:"); id: sequenceVolumeLabel; height:volumeField.height; anchors { left: numberField.right; leftMargin: Kirigami.Units.smallSpacing; top: seriesTextField.bottom; topMargin: Kirigami.Units.smallSpacing; } } QtControls.SpinBox { anchors { left: sequenceVolumeLabel.right; leftMargin: Kirigami.Units.smallSpacing; top: seriesTextField.bottom; topMargin: Kirigami.Units.smallSpacing; } value : root.model.acbfData.metaData.bookInfo.sequence(modelData).volume; width : (seriesTextField.width/2)-(Kirigami.Units.smallSpacing*1.5)-(sequenceVolumeLabel.width+Kirigami.Units.smallSpacing); id: volumeField; onValueChanged: parent.updateSeries(); from: 0; to: 99999; editable: true; } QtControls.Button { id: removeSequenceButton; anchors { left: seriesTextField.right; leftMargin: Kirigami.Units.smallSpacing; } contentItem: Kirigami.Icon { source: "list-remove"; } height: seriesTextField.height; width: height; onClicked: { root.model.setDirty(); root.model.acbfData.metaData.bookInfo.removeSequence(index); } } } } Item { width: parent.width; height: Kirigami.Units.smallSpacing; } QtControls.TextField { width: parent.width - addSequenceButton.width - Kirigami.Units.smallSpacing; placeholderText: i18nc("placeholder text for the add new series text entry", "Write to add new series"); Keys.onReturnPressed:addSequence(); function addSequence() { if(text !== "") { root.model.acbfData.metaData.bookInfo.addSequence(0, text); root.model.setDirty(); text = ""; } } QtControls.Button { id: addSequenceButton; anchors { left: parent.right; leftMargin: Kirigami.Units.smallSpacing; } contentItem: Kirigami.Icon { source: "list-add"; } height: parent.height; width: height; onClicked: parent.addSequence(); } } Kirigami.Heading { width: parent.width; height: paintedHeight + Kirigami.Units.smallSpacing * 2; text: i18nc("label text for the edit field for the database reference list", "Database References"); } Repeater { model: root.model.acbfData ? root.model.acbfData.metaData.bookInfo.databaseRefCount : 0; delegate: Item { width: parent.width; height: childrenRect.height; function updateDatabaseRef() { root.model.acbfData.metaData.bookInfo.databaseRef(modelData).reference = referenceTextField.text root.model.acbfData.metaData.bookInfo.databaseRef(modelData).dbname = databaseNameField.text root.model.acbfData.metaData.bookInfo.databaseRef(modelData).type = referenceTypeField.text root.model.setDirty(); } QtControls.TextField { id: referenceTextField; width: parent.width - removeReferenceButton.width - Kirigami.Units.smallSpacing; text: root.model.acbfData.metaData.bookInfo.databaseRef(modelData).reference; onEditingFinished: parent.updateDatabaseRef(); } QtControls.TextField { anchors { top: referenceTextField.bottom; topMargin: Kirigami.Units.smallSpacing; } width : (referenceTextField.width+Kirigami.Units.smallSpacing)/2; id: databaseNameField; text: root.model.acbfData.metaData.bookInfo.databaseRef(modelData).dbname; onEditingFinished: parent.updateDatabaseRef(); } QtControls.TextField { anchors { left: databaseNameField.right; leftMargin: Kirigami.Units.smallSpacing; top: referenceTextField.bottom; topMargin: Kirigami.Units.smallSpacing; } width : (referenceTextField.width/2)-(Kirigami.Units.smallSpacing*1.5); id: referenceTypeField; text: root.model.acbfData.metaData.bookInfo.databaseRef(modelData).type; placeholderText: i18nc("placeholder text for the add reference type text entry", "Write to add reference type"); onEditingFinished: parent.updateDatabaseRef(); } QtControls.Button { id: removeReferenceButton; anchors { left: referenceTextField.right; leftMargin: Kirigami.Units.smallSpacing; } contentItem: Kirigami.Icon { source: "list-remove"; } height: referenceTextField.height; width: height; onClicked: { root.model.setDirty(); root.model.acbfData.metaData.bookInfo.removeDatabaseRef(index); } } } } Item { width: parent.width; height: Kirigami.Units.smallSpacing; } Item { width: parent.width; height: childrenRect.height; function addReference() { if(addReferenceField.text !== "" && addDatabaseNameField.text !== "") { root.model.acbfData.metaData.bookInfo.addDatabaseRef(addReferenceField.text, addDatabaseNameField.text); root.model.setDirty(); addReferenceField.text = ""; addDatabaseNameField.text = ""; } } QtControls.TextField { id: addReferenceField width: parent.width - addReferenceButton.width - Kirigami.Units.smallSpacing; placeholderText: i18nc("placeholder text for the add new reference text entry", "Write to add new reference"); Keys.onReturnPressed: parent.addReference(); } QtControls.TextField { id: addDatabaseNameField anchors { top: addReferenceField.bottom; topMargin: Kirigami.Units.smallSpacing; } width: parent.width - addReferenceButton.width - Kirigami.Units.smallSpacing; placeholderText: i18nc("placeholder text for the add databasename text entry", "Write to add database name for new reference."); Keys.onReturnPressed: parent.addReference(); } QtControls.Button { id: addReferenceButton; anchors { left: addReferenceField.right; leftMargin: Kirigami.Units.smallSpacing; } contentItem: Kirigami.Icon { source: "list-add"; } height: addReferenceField.height; width: height; onClicked: parent.addReference(); } } Kirigami.Heading { width: parent.width; height: paintedHeight + Kirigami.Units.smallSpacing * 2; text: i18nc("label text for the edit field for the content rating list", "Content Ratings"); } Repeater { model: root.model.acbfData ? root.model.acbfData.metaData.bookInfo.contentRatingCount : 0; delegate: Item { width: parent.width; height: childrenRect.height; function updateRating() { root.model.acbfData.metaData.bookInfo.contentRating(modelData).rating = ratingNameField.text root.model.acbfData.metaData.bookInfo.contentRating(modelData).type = systemNameField.text root.model.setDirty(); } QtControls.TextField { width : (parent.width-removeRatingButton.width+Kirigami.Units.smallSpacing)/2; id: ratingNameField; text: root.model.acbfData.metaData.bookInfo.contentRating(modelData).rating; placeholderText: i18nc("placeholder text for the add content rating text entry", "Write to add rating label."); onEditingFinished: parent.updateRating(); } QtControls.TextField { anchors { left: ratingNameField.right; leftMargin: Kirigami.Units.smallSpacing; } width : ((parent.width-removeRatingButton.width)/2)-(Kirigami.Units.smallSpacing*1.5); id: systemNameField; text: root.model.acbfData.metaData.bookInfo.contentRating(modelData).type; placeholderText: i18nc("placeholder text for the add content rating system text entry", "Write to add rating system."); onEditingFinished: parent.updateRating(); } QtControls.Button { id: removeRatingButton; anchors { left: systemNameField.right; leftMargin: Kirigami.Units.smallSpacing; } contentItem: Kirigami.Icon { source: "list-remove"; } height: systemNameField.height; width: height; onClicked: { root.model.setDirty(); root.model.acbfData.metaData.bookInfo.removeContentRating(index); } } } } Item { width: parent.width; height: childrenRect.height; function addRating() { if(addRatingField.text !== "" && addSystemField.text !== "") { root.model.acbfData.metaData.bookInfo.addContentRating(addRatingField.text, addSystemField.text); root.model.setDirty(); addRatingField.text = ""; addSystemField.text = ""; } } QtControls.TextField { width : (parent.width-addRatingButton.width+Kirigami.Units.smallSpacing)/2; id: addRatingField; placeholderText: i18nc("placeholder text for the add content rating text entry", "Write to add rating label."); onEditingFinished: parent.addRating(); } QtControls.TextField { anchors { left: addRatingField.right; leftMargin: Kirigami.Units.smallSpacing; } width : ((parent.width-addRatingButton.width)/2)-(Kirigami.Units.smallSpacing*1.5); id: addSystemField; placeholderText: i18nc("placeholder text for the add content rating system text entry", "Write to add rating system."); onEditingFinished: parent.addRating(); } QtControls.Button { id: addRatingButton; anchors { left: addSystemField.right; leftMargin: Kirigami.Units.smallSpacing; } contentItem: Kirigami.Icon { source: "list-add"; } height: addSystemField.height; width: height; onClicked: parent.addRating(); } } Kirigami.Heading { width: parent.width; height: paintedHeight + Kirigami.Units.smallSpacing * 2; text: i18nc("label text for the form for reading direction.", "Reading Direction"); } QtControls.CheckBox { width: parent.width; text: i18nc("label text for right to left checkbox.", "Right to left."); checked: root.model.acbfData.metaData.bookInfo.rightToLeft; onCheckStateChanged: root.model.acbfData.metaData.bookInfo.rightToLeft = checked; } Kirigami.Heading { width: parent.width; height: paintedHeight + Kirigami.Units.smallSpacing * 2; text: i18nc("label text for the form for the publishing info list", "Publisher Info"); } QtControls.Label { text: i18nc("Label for publisher", "Publisher:"); } QtControls.TextField { width : parent.width; id: publisher; placeholderText: i18nc("placeholder text for the publisher entry", "Write to add publisher"); text: root.model.acbfData? root.model.acbfData.metaData.publishInfo.publisher: ""; onEditingFinished: { if (root.model.acbfData && text !=="") { root.model.acbfData.metaData.publishInfo.publisher = text root.model.setDirty(); } } } QtControls.Label { text: i18nc("Label for publishing date", "Publishing Date:"); } Item { width : parent.width; id: publishingDate; height: childrenRect.height; property date publishingDate: root.model.acbfData.metaData.publishInfo.publishDate; function changePublishDate() { root.model.acbfData.metaData.publishInfo.setPublishDateFromInts(pdYear.text, (pdMonth.currentIndex+1), pdDate.value); root.model.setDirty(); } QtControls.TextField { id: pdYear width: (parent.width-(Kirigami.Units.smallSpacing*2))/3; text: parent.publishingDate.getFullYear(); onEditingFinished: parent.changePublishDate(); inputMask: "9999" inputMethodHints: Qt.ImhFormattedNumbersOnly; } QtControls.ComboBox { id: pdMonth anchors { left: pdYear.right; margins: Kirigami.Units.smallSpacing; } model: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11] width: (parent.width-(Kirigami.Units.smallSpacing*2))/3; currentIndex: parent.publishingDate.getMonth(); displayText: Qt.locale().monthName(currentText, Locale.LongFormat); onActivated: parent.changePublishDate(); delegate: QtControls.ItemDelegate { text:Qt.locale().monthName(modelData, Locale.LongFormat); } } QtControls.SpinBox { id: pdDate anchors { left: pdMonth.right; margins: Kirigami.Units.smallSpacing; } width: (parent.width-(Kirigami.Units.smallSpacing*2))/3; height: pdMonth.height; from: 1; to: 31; editable: true; value: parent.publishingDate.getDate(); onValueChanged: parent.changePublishDate(); } } QtControls.Label { text: i18nc("Label for city", "City:"); } QtControls.TextField { width : parent.width; id: city; placeholderText: i18nc("placeholder text for the publishing city entry", "Write to add city"); text: root.model.acbfData? root.model.acbfData.metaData.publishInfo.city: ""; onEditingFinished: { if (root.model.acbfData && text !=="") { root.model.acbfData.metaData.publishInfo.city = text ; root.model.setDirty(); } } } QtControls.Label { - text: i18nc("Label for isbn", "ISBN:"); + text: i18nc("Label for ISBN", "ISBN:"); } QtControls.TextField { width : parent.width; id: isbn; - placeholderText: i18nc("placeholder text for the publishing isbn entry", "Write to add isbn"); + placeholderText: i18nc("placeholder text for the publishing ISBN entry", "Write to add ISBN"); text: root.model.acbfData? root.model.acbfData.metaData.publishInfo.isbn: ""; onEditingFinished: { if (root.model.acbfData && text !=="") { root.model.acbfData.metaData.publishInfo.isbn = text root.model.setDirty(); } } } QtControls.Label { text: i18nc("Label for license", "License:"); } QtControls.TextField { width : parent.width; id: license; placeholderText: i18nc("placeholder text for the publishing license entry", "Write to add license"); text: root.model.acbfData? root.model.acbfData.metaData.publishInfo.license: ""; onEditingFinished: { if (root.model.acbfData && text !=="") { root.model.acbfData.metaData.publishInfo.license = text root.model.setDirty(); } } } Kirigami.Heading { width: parent.width; height: paintedHeight + Kirigami.Units.smallSpacing * 2; text: i18nc("label text for the form for the document info list", "Document Authors"); } Repeater { id: docAuthorRepeater; model: root.model.acbfData ? root.model.acbfData.metaData.documentInfo.authorNames : 0; delegate: QtControls.Label { width: parent.width - removeDocAuthorButton.width - Kirigami.Units.smallSpacing; text: modelData.length > 0 ? modelData : "(unnamed)"; QtControls.Button { id: editDocAuthorButton; anchors { right: removeDocAuthorButton.left; leftMargin: Kirigami.Units.smallSpacing; } contentItem: Kirigami.Icon { source: "document-edit"; } height: parent.height; width: height; onClicked: { docAuthorEditor.index = model.index; docAuthorEditor.open(); } } QtControls.Button { id: removeDocAuthorButton; anchors { left: parent.right; leftMargin: Kirigami.Units.smallSpacing; } contentItem: Kirigami.Icon { source: "list-remove"; } height: parent.height; width: height; onClicked: { // When removing, set the model dirty first, and then remove the entry to avoid reference errors. root.model.setDirty(); root.model.acbfData.metaData.documentInfo.removeAuthor(index); } } } } Item { width: parent.width; height: Kirigami.Units.smallSpacing; } QtControls.TextField { width: parent.width - addDocAuthorButton.width - Kirigami.Units.smallSpacing; placeholderText: i18nc("placeholder text for the add new author text entry", "Write to add new author (nickname)"); Keys.onReturnPressed: addAuthor(); function addAuthor() { if(text !== "") { // Just add an author where only the nickname is defined root.model.acbfData.metaData.documentInfo.addAuthor("", "", "", "", "", text, [""], [""]); root.model.setDirty(); text = ""; } } QtControls.Button { id: addDocAuthorButton; anchors { left: parent.right; leftMargin: Kirigami.Units.smallSpacing; } contentItem: Kirigami.Icon { source: "list-add"; } height: parent.height; width: height; onClicked: parent.addAuthor(); } } Kirigami.Heading { width: parent.width; height: paintedHeight + Kirigami.Units.smallSpacing * 2; text: i18nc("label text for the form for the sources list", "Document Sources"); } Repeater { model: root.model.acbfData ? root.model.acbfData.metaData.documentInfo.source : 0; delegate: Item { width: parent.width; height: childrenRect.height; QtControls.TextField { id: sourceText; width: parent.width - removeSourceButton.width - Kirigami.Units.smallSpacing; text: modelData; onEditingFinished: root.model.acbfData.metaData.documentInfo.sources[index] = text; QtControls.Button { id: removeSourceButton; anchors { left: parent.right; leftMargin: Kirigami.Units.smallSpacing; } contentItem: Kirigami.Icon { source: "list-remove"; } height: parent.height; width: height; onClicked: { root.model.setDirty(); root.model.acbfData.metaData.documentInfo.removeSource(index); } } } } } Item { width: parent.width; height: Kirigami.Units.smallSpacing; } QtControls.TextField { width: parent.width - addSourceButton.width - Kirigami.Units.smallSpacing; Keys.onReturnPressed: addEntry(); function addEntry() { if (text !== "") { root.model.acbfData.metaData.documentInfo.source.push(text); root.model.setDirty(); text = ""; } } QtControls.Button { id: addSourceButton; anchors { left: parent.right; leftMargin: Kirigami.Units.smallSpacing; } contentItem: Kirigami.Icon { source: "list-add"; } height: parent.height; width: height; onClicked: parent.addEntry(); } } Kirigami.Heading { width: parent.width; height: paintedHeight + Kirigami.Units.smallSpacing * 2; text: i18nc("label text for the form for the history list", "Document History"); } Repeater { model: root.model.acbfData ? root.model.acbfData.metaData.documentInfo.history : 0; delegate: Item { width: parent.width; height: childrenRect.height; QtControls.TextField { id: historyText; width: parent.width - removeHistoryButton.width - Kirigami.Units.smallSpacing; text: modelData; onEditingFinished: root.model.acbfData.metaData.documentInfo.history[index] = text; QtControls.Button { id: removeHistoryButton; anchors { left: parent.right; leftMargin: Kirigami.Units.smallSpacing; } contentItem: Kirigami.Icon { source: "list-remove"; } height: parent.height; width: height; onClicked: { root.model.setDirty(); root.model.acbfData.metaData.documentInfo.removeHistoryLine(index); } } } } } Item { width: parent.width; height: Kirigami.Units.smallSpacing; } QtControls.TextField { width: parent.width - addHistoryButton.width - Kirigami.Units.smallSpacing; Keys.onReturnPressed: addEntry(); function addEntry() { if (text !== "") { root.model.acbfData.metaData.documentInfo.history.push(text); root.model.setDirty(); text = ""; } } QtControls.Button { id: addHistoryButton; anchors { left: parent.right; leftMargin: Kirigami.Units.smallSpacing; } contentItem: Kirigami.Icon { source: "list-add"; } height: parent.height; width: height; onClicked: parent.addEntry(); } } Item { width: parent.width; height: Kirigami.Units.smallSpacing; } Item { width: parent.width; height: childrenRect.height; QtControls.Label { id: versionLabel; height: versionSpinBox.height; text: i18nc("Label for the document version spinbox","Document Version:"); } QtControls.SpinBox { id: versionSpinBox; anchors { top: versionLabel.top; left: versionLabel.right; leftMargin: Kirigami.Units.smallSpacing; } width: parent.width - (Kirigami.Units.smallSpacing*2) - versionLabel.width - addHistoryButton.width; from:0; to: 100 * 100; stepSize: 100; editable: true; property int decimals: 2; property real realValue: value / 100; validator: DoubleValidator { bottom: Math.min(versionSpinBox.from, versionSpinBox.to) top: Math.max(versionSpinBox.from, versionSpinBox.to) } textFromValue: function(value, locale) { return Number(value / 100).toLocaleString(locale, 'f', decimals) } valueFromText: function(text, locale) { return Number.fromLocaleString(locale, text) * 100 } value: root.model.acbfData.metaData.documentInfo.version * 100; onFocusChanged: { if (root.model.acbfData.metaData.documentInfo.version*100!==value) { root.model.acbfData.metaData.documentInfo.version = value/100; } } } } Kirigami.Heading { width: parent.width; height: paintedHeight + Kirigami.Units.smallSpacing * 2; text: i18nc("label text for the form for the body background color.", "General Page Background Color"); } QtControls.TextField { text: root.model.acbfData.body.bgcolor; placeholderText: "#ffffff"; onEditingFinished: root.model.acbfData.body.bgcolor = text; } AuthorEntryEditor { id: authorEditor; bookinfo: root.model.acbfData.metaData.bookInfo; onSave: { root.model.acbfData.metaData.bookInfo.setAuthor(index, activity, language, firstName, middleName, lastName, nickName, homePage, email); root.model.setDirty(); } } AuthorEntryEditor { id: docAuthorEditor; bookinfo: root.model.acbfData.metaData.documentInfo; onSave: { root.model.acbfData.metaData.documentInfo.setAuthor(index, activity, language, firstName, middleName, lastName, nickName, homePage, email); root.model.setDirty(); } } } } diff --git a/src/creator/qml/CreateNewBook.qml b/src/creator/qml/CreateNewBook.qml index d78db14..644cf32 100644 --- a/src/creator/qml/CreateNewBook.qml +++ b/src/creator/qml/CreateNewBook.qml @@ -1,134 +1,134 @@ /* * Copyright (C) 2016 Dan Leinir Turthra Jensen * * 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 . * */ import QtQuick 2.2 import QtQuick.Dialogs 1.2 import QtQuick.Controls 2.2 as QtControls import org.kde.kirigami 2.1 as Kirigami import org.kde.peruse 0.1 as Peruse /** * @brief page with a form for creating a new comic. * * It asks for the default title, folder and cover image, * and when done it open the new book in Book. */ Kirigami.Page { id: root; property string categoryName: "createNewBook"; title: i18nc("title of the new book creation page", "Create New Book"); actions { main: Kirigami.Action { text: i18nc("Accept button which will create a new book", "Create Book"); iconName: "dialog-ok"; property int splitPos: osIsWindows ? 8 : 7; onTriggered: { var filename = newBookModel.createBook(getFolderDlg.folder.toString().substring(splitPos), titleEdit.text, getCoverDlg.fileUrl.toString().substring(splitPos)); if(filename.length > 0) { mainWindow.openBook(filename); } } } } Peruse.ArchiveBookModel { id: newBookModel; qmlEngine: globalQmlEngine; } Column { id: contentColumn; width: root.width - (root.leftPadding + root.rightPadding); height: childrenRect.height; spacing: Kirigami.Units.smallSpacing; Kirigami.Heading { width: parent.width; height: paintedHeight + Kirigami.Units.smallSpacing * 2; text: i18nc("label text for the edit field for the book title", "Title"); } QtControls.TextField { id: titleEdit; width: parent.width; text: i18nc("Default name for new books", "Untitled"); } Kirigami.Heading { width: parent.width; height: paintedHeight + Kirigami.Units.smallSpacing * 2; text: i18nc("label text for the edit field for the file system location for the book", "Folder"); } QtControls.Label { width: parent.width - getFolderButton.width; text: getFolderDlg.folder; QtControls.Button { id: getFolderButton; anchors.left: parent.right; height: parent.height; width: height; contentItem: Kirigami.Icon { source: "folder-open" } onClicked: getFolderDlg.open(); } FileDialog { id: getFolderDlg; - title: i18nc("Title of a folder dialog used to select the location of a new book", "Please choose the location for the book"); + title: i18nc("@title:window folder dialog used to select the location of a new book", "Please Choose the Location for the Book"); folder: mainWindow.homeDir(); selectFolder: true; } } Kirigami.Heading { width: parent.width - getCoverButton.width; height: paintedHeight + Kirigami.Units.smallSpacing * 2; text: i18nc("label text for the edit field for the cover image for the book", "Cover Image"); QtControls.Button { id: getCoverButton; anchors.left: parent.right; height: getFolderButton.height; width: height; contentItem: Kirigami.Icon { source: "folder-open" } onClicked: getCoverDlg.open(); } FileDialog { id: getCoverDlg; - title: i18nc("Title of a file dialog used to select the cover image for a new book", "Please choose your cover image"); + title: i18nc("@title:window file dialog used to select the cover image for a new book", "Please Choose Your Cover Image"); folder: mainWindow.homeDir(); nameFilters: [ "JPEG images (*.jpg, *.jpeg)", "All files (*)" ]; } } Item { width: parent.width; height: Kirigami.Units.iconSizes.enormous + Kirigami.Units.smallSpacing; Image { anchors.centerIn: parent; height: Kirigami.Units.iconSizes.enormous; width: Kirigami.Units.iconSizes.enormous; asynchronous: true; fillMode: Image.PreserveAspectFit; source: getCoverDlg.fileUrl; } } } } diff --git a/src/creator/qml/Main.qml b/src/creator/qml/Main.qml index d230087..d0f4ed2 100644 --- a/src/creator/qml/Main.qml +++ b/src/creator/qml/Main.qml @@ -1,155 +1,155 @@ /* * Copyright (C) 2015 Dan Leinir Turthra Jensen * * 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 . * */ import QtQuick 2.2 import QtQuick.Window 2.2 import QtQuick.Dialogs 1.2 import org.kde.kirigami 2.1 as Kirigami import org.kde.peruse 0.1 as Peruse /** * @brief Main window of the application. * * This splits the window in two sections: * - A section where you can modify a comic. * - A "global drawer" which can be used to select actions. * * WelcomePage is the opening page and holds options to create a new comic. * CreateNewBook gives options for creating a new book from scratch. * Book is the actual book being modified. * Settings is the page with toggles and knobs for Peruse Creator config. */ Kirigami.ApplicationWindow { id: mainWindow; property int animationDuration: 200; width: Screen.desktopAvailableWidth * 0.6; height: Screen.desktopAvailableHeight * 0.7; pageStack.initialPage: welcomePage; Peruse.Config { id: peruseConfig; } function homeDir() { return peruseConfig.homeDir(); } function openBook(bookFilename) { currentCategory = ""; peruseConfig.bookOpened(bookFilename); mainWindow.pageStack.clear(); mainWindow.pageStack.push(bookPage, { filename: bookFilename }); } header: Kirigami.ApplicationHeader {} globalDrawer: Kirigami.GlobalDrawer { /// FIXME This causes the text to get cut off on the phone, however if the text is shorter /// it fails to expand the sidebar sufficiently to see all the action labels fully. Revisit /// this when switching to Kirigami title: i18nc("application title for the sidebar", "Peruse Creator"); titleIcon: "peruse-creator"; drawerOpen: true; modal: false; actions: [ Kirigami.Action { - text: "Welcome"; + text: i18nc("Switch to the welcome page", "Welcome"); iconName: "start-over"; checked: mainWindow.currentCategory === "welcomePage"; checkable: true; onTriggered: { changeCategory(welcomePage); } }, Kirigami.Action { }, Kirigami.Action { - text: i18nc("Create a book", "Create a new book..."); + text: i18nc("Create a book", "Create a New Book..."); iconName: "document-new"; onTriggered: changeCategory(createNewBookPage); }, Kirigami.Action { - text: i18nc("Open a book from somewhere on disk (uses the open dialog, or a drilldown on touch devices)", "Open other..."); + text: i18nc("Open a book from somewhere on disk (uses the open dialog, or a drilldown on touch devices)", "Open Other..."); iconName: "document-open"; onTriggered: openOther(); }, Kirigami.Action { }, Kirigami.Action { text: i18nc("Open the settings page", "Settings"); iconName: "configure" checked: mainWindow.currentCategory === "settingsPage"; checkable: true; onTriggered: changeCategory(settingsPage); } ] } Component { id: welcomePage; WelcomePage { } } Component { id: createNewBookPage; CreateNewBook { } } Component { id: bookPage; Book { } } Component { id: settingsPage; Settings { } } property string currentCategory: "welcomePage"; function changeCategory(categoryItem) { // Clear all the way to the welcome page if we change the category... mainWindow.pageStack.clear(); mainWindow.pageStack.push(categoryItem); currentCategory = mainWindow.pageStack.currentItem.categoryName; } function openOther() { openDlg.open(); } FileDialog { id: openDlg; - title: i18nc("Title of a standard file open dialog used to open a book not in the collection", "Please choose a book to open"); + title: i18nc("@title:window standard file open dialog used to open a book not in the collection", "Please Choose a Book to Open"); folder: mainWindow.homeDir(); nameFilters: [ "Comic Book Archive zip format (*.cbz)", "All files (*)" ] property int splitPos: osIsWindows ? 8 : 7; onAccepted: { if(openDlg.fileUrl.toString().substring(0, 7) === "file://") { mainWindow.openBook(openDlg.fileUrl.toString().substring(splitPos), 0); } } onRejected: { // Just do nothing, we don't really care... } } } diff --git a/src/creator/qml/WelcomePage.qml b/src/creator/qml/WelcomePage.qml index 1de87d8..0fd60fb 100644 --- a/src/creator/qml/WelcomePage.qml +++ b/src/creator/qml/WelcomePage.qml @@ -1,183 +1,183 @@ /* * Copyright (C) 2015 Dan Leinir Turthra Jensen * * 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 . * */ import QtQuick 2.2 import QtQuick.Layouts 1.1 import QtQuick.Controls 2.2 as QtControls import org.kde.kirigami 2.1 as Kirigami /** * @brief The page on which Peruse Creator opens. * * This page gives an introduction to peruse and has options for: * - Opening the last opened archive by either Peruse or Peruse Creator. * - Opening an existing comic. * - Creating a blank comic. * - Creating a comic archive from a selection of images. */ Kirigami.Page { id: root; property string categoryName: "welcomePage"; title: i18nc("title of the welcome page", "Welcome"); Item { width: root.width - (root.leftPadding + root.rightPadding); height: root.height - root.topPadding; Item { id: titleContainer; anchors { top: parent.top; left: parent.left; right: parent.right; } height: appNameLabel.height + appDescriptionLabel.height + Kirigami.Units.largeSpacing; Kirigami.Heading { id: appNameLabel; anchors { left: parent.left; right: parent.right; bottom: parent.verticalCenter; } text: "Peruse Creator"; horizontalAlignment: Text.AlignHCenter; } QtControls.Label { id: appDescriptionLabel; anchors { top: parent.verticalCenter; left: parent.left; right: parent.right; } text: i18nc("application subtitle", "Comic Book Creation Tool"); horizontalAlignment: Text.AlignHCenter; } Rectangle { anchors.centerIn: parent; height: 1; color: Kirigami.Theme.textColor; width: appDescriptionLabel.paintedWidth; } } Item { id: actionsContainer; anchors { top: titleContainer.bottom; left: parent.left; right: parent.right; bottom: parent.bottom; } Item { anchors { top: parent.top; left: parent.left; right: parent.right; bottom: parent.verticalCenter; margins: Kirigami.Units.largeSpacing; } QtControls.Label { anchors.fill: parent; wrapMode: Text.WrapAtWordBoundaryOrAnywhere; horizontalAlignment: Text.AlignHCenter; verticalAlignment: Text.AlignVCenter; text: i18nc("Longer introduction text used on the welcome page", "Welcome to Peruse Creator, a tool designed to assist you in creating comic book archives which can be read with any cbz capable comic book reader app. You can either create entirely new comic book archives from scratch, create one from a set of pictures, or editing existing archives. Once you have created them, you can even publish them directly to the online comic book archive at the KDE Store from within the application, or just share the files with your friends."); } } Item { anchors { top: parent.verticalCenter; left: parent.left; right: parent.right; bottom: parent.bottom; } Item { id: continueLast; anchors { top: parent.top; left: parent.left; right: parent.horizontalCenter; bottom: parent.verticalCenter; } QtControls.Button { anchors.centerIn: parent; // iconName: "go-next"; - text: i18nc("Button to continue working on the most recently opened comic book archive", "Continue %1", continueLast.mostRecentBook.split('/').pop()); + text: i18nc("@action:button continue working on the most recently opened comic book archive", "Continue %1", continueLast.mostRecentBook.split('/').pop()); onClicked: mainWindow.openBook(continueLast.mostRecentBook); } property string mostRecentBook: ""; Component.onCompleted: { if(peruseConfig.recentlyOpened.length > 0) { for(var i = 0; i < peruseConfig.recentlyOpened.length; ++i) { if(peruseConfig.recentlyOpened[i].toLowerCase().slice(-4) === ".cbz") { continueLast.mostRecentBook = peruseConfig.recentlyOpened[i]; break; } } } } visible: mostRecentBook.length > 0; } Item { anchors { top: parent.top; left: continueLast.visible ? parent.horizontalCenter : parent.left; right: parent.right; bottom: parent.verticalCenter; } QtControls.Button { anchors.centerIn: parent; // iconName: "document-open"; - text: i18nc("Button to open existing comic book archive", "Open Existing..."); + text: i18nc("@action:button open existing comic book archive", "Open Existing..."); onClicked: mainWindow.openOther(); } } Item { anchors { top: parent.verticalCenter; left: parent.left; right: parent.horizontalCenter; bottom: parent.bottom; } QtControls.Button { anchors.centerIn: parent; // iconName: "document-new"; - text: i18nc("Button to create a new, empty comic book archive", "Create Blank"); + text: i18nc("@action:button create a new, empty comic book archive", "Create Blank"); onClicked: mainWindow.createNew(); } } Item { anchors { top: parent.verticalCenter; left: parent.horizontalCenter; right: parent.right; bottom: parent.bottom; } QtControls.Button { anchors.centerIn: parent; // iconName: "folder-open"; - text: i18nc("Button to create a new comic book archive by copying in a bunch of pictures", "Create From Images..."); + text: i18nc("@action:button create a new comic book archive by copying in a bunch of pictures", "Create from Images..."); } } } } } } diff --git a/src/creator/qml/metainfoeditors/AuthorEntryEditor.qml b/src/creator/qml/metainfoeditors/AuthorEntryEditor.qml index 4a8ceb0..ac78943 100644 --- a/src/creator/qml/metainfoeditors/AuthorEntryEditor.qml +++ b/src/creator/qml/metainfoeditors/AuthorEntryEditor.qml @@ -1,297 +1,297 @@ /* * Copyright (C) 2015 Dan Leinir Turthra Jensen * * 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 . * */ import QtQuick 2.2 import org.kde.kirigami 2.1 as Kirigami import QtQuick.Controls 2.2 as QtControls /** * @brief a special overlay sheet for editing the author information. * * Authors can have the full names, nicknames and some contact information * like email adress and homepage. They can also be assigned a role * from a list of predefined author activities. * * Author is used in acbf for both the actual authors as well as the people * who handled generating the acbf document, which is why this is * a dedicated form. * * TODO: Support input for multiple homepage and email adresses. */ Kirigami.OverlaySheet { id: root; property int index: -1; property QtObject bookinfo: null; signal save(); onIndexChanged: { root.author = bookinfo.getAuthor(index); activityField.model = root.author.availableActivities(); activityField.currentIndex = activityField.find(author.activity()); languageField.text = root.author.language(); firstNameField.text = root.author.firstName(); middleNameField.text = root.author.middleName(); lastNameField.text = root.author.lastName(); nickNameField.text = root.author.nickName(); } property QtObject author: null; property alias activity: activityField.currentText; property alias language: languageField.text; property alias firstName: firstNameField.text; property alias middleName: middleNameField.text; property alias lastName: lastNameField.text; property alias nickName: nickNameField.text; property var homePage: root.author ? root.author.homePages : ""; property var email: root.author ? root.author.emails : ""; Column { height: childrenRect.height; spacing: Kirigami.Units.smallSpacing; Kirigami.Heading { width: parent.width; height: paintedHeight; text: i18nc("title text for the edit author sheet", "Edit Author"); QtControls.Button { id: saveButton; anchors { right: parent.right; leftMargin: Kirigami.Units.smallSpacing; } contentItem: Kirigami.Icon { source: "dialog-ok"; } height: parent.height; width: height; Keys.onReturnPressed: { root.save(); root.close(); } onClicked: { root.save(); root.close(); } } } QtControls.Label { width: parent.width; height: paintedHeight; - text: i18nc("help text for the edit author sheet", "Please coplete the information for this author."); + text: i18nc("help text for the edit author sheet", "Please complete the information for this author."); wrapMode: Text.WrapAtWordBoundaryOrAnywhere; } Item { width: parent.width; height: Kirigami.Units.largeSpacing; } QtControls.Label { width: parent.width; height: paintedHeight; text: i18nc("label for the activity field", "Activity:"); wrapMode: Text.WrapAtWordBoundaryOrAnywhere; } QtControls.ComboBox { //enabled: activity; id: activityField; width: parent.width - Kirigami.Units.smallSpacing; } Item { width: parent.width; height: Kirigami.Units.smallSpacing; } QtControls.Label { width: parent.width; height: paintedHeight; text: i18nc("label for the language field", "Language:"); wrapMode: Text.WrapAtWordBoundaryOrAnywhere; } QtControls.TextField { id: languageField; width: parent.width - Kirigami.Units.smallSpacing; placeholderText: i18nc("placeholder text for the language field", "Language"); } Item { width: parent.width; height: Kirigami.Units.smallSpacing; } QtControls.Label { width: parent.width; height: paintedHeight; text: i18nc("label for the first name field", "First name:"); wrapMode: Text.WrapAtWordBoundaryOrAnywhere; } QtControls.TextField { id: firstNameField; width: parent.width - Kirigami.Units.smallSpacing; placeholderText: i18nc("placeholder text for the first name field", "First Name"); } Item { width: parent.width; height: Kirigami.Units.smallSpacing; } QtControls.Label { width: parent.width; height: paintedHeight; text: i18nc("label for the middle name field", "Middle name:"); wrapMode: Text.WrapAtWordBoundaryOrAnywhere; } QtControls.TextField { id: middleNameField; width: parent.width - Kirigami.Units.smallSpacing; placeholderText: i18nc("placeholder text for the middle name field", "Middle Name"); } Item { width: parent.width; height: Kirigami.Units.smallSpacing; } QtControls.Label { width: parent.width; height: paintedHeight; text: i18nc("label for the last name field", "Last name:"); wrapMode: Text.WrapAtWordBoundaryOrAnywhere; } QtControls.TextField { id: lastNameField; width: parent.width - Kirigami.Units.smallSpacing; placeholderText: i18nc("placeholder text for the last name field", "Last Name"); } Item { width: parent.width; height: Kirigami.Units.smallSpacing; } QtControls.Label { width: parent.width; height: paintedHeight; text: i18nc("label for the nickname field", "Nickname:"); wrapMode: Text.WrapAtWordBoundaryOrAnywhere; } QtControls.TextField { id: nickNameField; width: parent.width - Kirigami.Units.smallSpacing; placeholderText: i18nc("placeholder text for the nickname field", "Nickname"); } Item { width: parent.width; height: Kirigami.Units.smallSpacing; } QtControls.Label { width: parent.width; height: paintedHeight; text: i18nc("label for the homepage field", "Homepage addresses:"); wrapMode: Text.WrapAtWordBoundaryOrAnywhere; } Repeater { model: root.author ? root.author.homePages : 0; QtControls.TextField { width: parent.width - removeHomePageButton.width - Kirigami.Units.smallSpacing; text: modelData; onEditingFinished: root.author.homePages[index] = text; QtControls.Button { id: removeHomePageButton; anchors { left: parent.right; leftMargin: Kirigami.Units.smallSpacing; } contentItem: Kirigami.Icon { source: "list-remove"; } height: parent.height; width: height; onClicked: root.author.removeHomePage(index); } } } QtControls.TextField { id: homePageField; width: parent.width - addHomepageButton.width - Kirigami.Units.smallSpacing; placeholderText: i18nc("placeholder text for the homepage field", "Add Homepage"); Keys.onReturnPressed: addEntry(); function addEntry() { if (text!=="") { root.author.addHomePage(text); text=""; } } QtControls.Button { id: addHomepageButton; anchors { left: parent.right; leftMargin: Kirigami.Units.smallSpacing; } contentItem: Kirigami.Icon { source: "list-add"; } height: parent.height; width: height; onClicked: parent.addEntry(); } } Item { width: parent.width; height: Kirigami.Units.smallSpacing; } QtControls.Label { width: parent.width; height: paintedHeight; text: i18nc("label for the email field", "Email addresses:"); wrapMode: Text.WrapAtWordBoundaryOrAnywhere; } Repeater { model: root.author ? root.author.emails : 0; QtControls.TextField { width: parent.width - addEmailButton.width - Kirigami.Units.smallSpacing; text: modelData; onEditingFinished: root.author.emails[index] = text; QtControls.Button { id: removeEmailButton; anchors { left: parent.right; leftMargin: Kirigami.Units.smallSpacing; } contentItem: Kirigami.Icon { source: "list-remove"; } height: parent.height; width: height; onClicked: root.author.removeEmail(index); } } } QtControls.TextField { id: emailField; width: parent.width - addEmailButton.width - Kirigami.Units.smallSpacing; placeholderText: i18nc("placeholder text for the email field", "Add Email address"); Keys.onReturnPressed: addEntry(); function addEntry() { if (text!=="") { root.author.addEmail(text); text = ""; } } QtControls.Button { id: addEmailButton; anchors { left: parent.right; leftMargin: Kirigami.Units.smallSpacing; } contentItem: Kirigami.Icon { source: "list-add"; } height: parent.height; width: height; onClicked: parent.addEntry(); } } Item { width: parent.width; height: Kirigami.Units.smallSpacing; } } }