Warning, /graphics/peruse/src/app/qml/Book.qml is written in an unsupported language. File is not indexed.

0001 /*
0002  * Copyright (C) 2015 Dan Leinir Turthra Jensen <admin@leinir.dk>
0003  *
0004  * This library is free software; you can redistribute it and/or
0005  * modify it under the terms of the GNU Lesser General Public
0006  * License as published by the Free Software Foundation; either
0007  * version 2.1 of the License, or (at your option) version 3, or any
0008  * later version accepted by the membership of KDE e.V. (or its
0009  * successor approved by the membership of KDE e.V.), which shall
0010  * act as a proxy defined in Section 6 of version 3 of the license.
0011  *
0012  * This library is distributed in the hope that it will be useful,
0013  * but WITHOUT ANY WARRANTY; without even the implied warranty of
0014  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
0015  * Lesser General Public License for more details.
0016  *
0017  * You should have received a copy of the GNU Lesser General Public
0018  * License along with this library.  If not, see <http://www.gnu.org/licenses/>.
0019  *
0020  */
0021 
0022 import QtQuick 2.12
0023 import QtQuick.Controls 2.12 as QtControls
0024 import QtQuick.Window 2.12
0025 
0026 import org.kde.kirigami 2.16 as Kirigami
0027 
0028 import org.kde.peruse 0.1 as Peruse
0029 import "listcomponents" as ListComponents
0030 /**
0031  * @brief Page that handles reading the book.
0032  * 
0033  * 
0034  */
0035 Kirigami.Page {
0036     id: root;
0037     objectName: "bookViewer";
0038     clip: true;
0039     property bool isCurrentContext: isCurrentPage && applicationWindow().bookOpen
0040 
0041     // Remove all the padding when we've hidden controls. Content is king!
0042     topPadding: applicationWindow().controlsVisible ? (applicationWindow() && applicationWindow().header ? applicationWindow().header.height : 0) : 0;
0043     leftPadding: applicationWindow().controlsVisible ? Kirigami.Units.gridUnit : 0;
0044     rightPadding: applicationWindow().controlsVisible ? Kirigami.Units.gridUnit : 0;
0045     bottomPadding: applicationWindow().controlsVisible ? Kirigami.Units.gridUnit * 2 : 0;
0046 
0047     background: Rectangle {
0048         anchors.fill: parent;
0049         opacity: applicationWindow().controlsVisible ? 0 : 1;
0050         Behavior on opacity { NumberAnimation { duration: applicationWindow().animationDuration; } }
0051         color: "black";
0052     }
0053 
0054     // Perhaps we should store and restore this?
0055     property bool showControls: true;
0056     property Item pageStackItem: applicationWindow().pageStack.layers.currentItem;
0057     onPageStackItemChanged: {
0058         if(root.isCurrentContext) {
0059             applicationWindow().controlsVisible = root.showControls;
0060         }
0061         else {
0062             root.showControls = applicationWindow().controlsVisible;
0063             applicationWindow().controlsVisible = true;
0064         }
0065     }
0066 
0067     property bool rtlMode: false;
0068     /**
0069      * zoomMode: Peruse.Config.ZoomMode
0070      */
0071     property int zoomMode: Peruse.Config.ZoomFull;
0072 
0073     property string file;
0074     property int currentPage;
0075     property int totalPages;
0076     onCurrentPageChanged: {
0077         // set off a timer to slightly postpone saving the current page, so it doesn't happen during animations etc
0078         updateCurrent.start();
0079     }
0080 
0081     function nextFrame() {
0082         // If there is a next frame to go to, or whether it is supported at all
0083         if(viewLoader.item.hasFrames === true) {
0084             viewLoader.item.nextFrame();
0085         }
0086         else {
0087             nextPage();
0088         }
0089     }
0090     function previousFrame() {
0091         // If there is a next frame to go to, or whether it is supported at all
0092         if(viewLoader.item.hasFrames === true) {
0093             viewLoader.item.previousFrame();
0094         }
0095         else {
0096             previousPage();
0097         }
0098     }
0099     function nextPage() {
0100         if(viewLoader.item.currentPage < viewLoader.item.pageCount - 1) {
0101             viewLoader.item.currentPage++;
0102         } else {
0103             bookInfo.showBookInfo(file);
0104         }
0105     }
0106     function previousPage() {
0107         if(viewLoader.item.currentPage > 0) {
0108             viewLoader.item.currentPage--;
0109         } else {
0110             bookInfo.showBookInfo(file);
0111         }
0112     }
0113     function setCurrentPage(pageNumber) {
0114         viewLoader.item.currentPage = pageNumber;
0115     }
0116     function closeBook() {
0117         applicationWindow().contextDrawer.close();
0118         // also for storing current page (otherwise postponed a bit after page change, done here as well to ensure it really happens)
0119         applicationWindow().controlsVisible = true;
0120         applicationWindow().pageStack.layers.pop();
0121         applicationWindow().globalDrawer.open();
0122     }
0123 
0124     property Item contextualTopItems: ListView {
0125         id: thumbnailNavigator;
0126         anchors.fill: parent;
0127         clip: true;
0128         delegate: thumbnailComponent;
0129     }
0130     Component {
0131         id: thumbnailComponent;
0132         Item {
0133             width: parent !== null ? parent.width : height;
0134             height: Kirigami.Units.gridUnit * 6;
0135             MouseArea {
0136                 anchors.fill: parent;
0137                 onClicked: viewLoader.item.currentPage = model.index;
0138             }
0139             Rectangle {
0140                 anchors.fill: parent;
0141                 color: Kirigami.Theme.highlightColor;
0142                 opacity: root.currentPage === model.index ? 1 : 0;
0143                 Behavior on opacity { NumberAnimation { duration: Kirigami.Units.shortDuration; } }
0144             }
0145             Image {
0146                 anchors {
0147                     top: parent.top;
0148                     left: parent.left;
0149                     right: parent.right;
0150                     margins: Kirigami.Units.smallSpacing;
0151                 }
0152                 height: parent.height - pageTitle.height - Kirigami.Units.smallSpacing * 2;
0153                 asynchronous: true;
0154                 fillMode: Image.PreserveAspectFit;
0155                 source: model.url;
0156             }
0157             QtControls.Label {
0158                 id: pageTitle;
0159                 anchors {
0160                     left: parent.left;
0161                     right: parent.right;
0162                     bottom: parent.bottom;
0163                 }
0164                 height: paintedHeight;
0165                 text: model.title;
0166                 elide: Text.ElideMiddle;
0167                 horizontalAlignment: Text.AlignHCenter;
0168             }
0169         }
0170     }
0171 
0172     function toggleFullscreen() {
0173         applicationWindow().contextDrawer.close();
0174         if(applicationWindow().visibility !== Window.FullScreen) {
0175             applicationWindow().visibility = Window.FullScreen;
0176             applicationWindow().controlsVisible = false;
0177         }
0178         else {
0179             applicationWindow().visibility = Window.AutomaticVisibility;
0180             applicationWindow().controlsVisible = true;
0181         }
0182     }
0183 
0184     property list<QtObject> mobileActions: [
0185         Kirigami.Action {
0186             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");
0187             iconName: "view-fullscreen";
0188             onTriggered: toggleFullscreen();
0189             enabled: root.isCurrentContext && Kirigami.Settings.isMobile
0190         },
0191         Kirigami.Action {
0192             text: i18nc("Action used on touch devices to close the currently open book and return to whatever page was most recently shown", "Close Book");
0193             shortcut: bookInfo.sheetOpen ? "" : "Esc";
0194             iconName: "dialog-close";
0195             onTriggered: closeBook();
0196             enabled: root.isCurrentContext && Kirigami.Settings.isMobile
0197         }
0198     ]
0199     property list<QtObject> desktopActions: [
0200         Kirigami.Action {
0201             text: i18nc("Top level entry leading to a submenu with options for the book display", "View Options");
0202             iconName: "configure";
0203             Kirigami.Action {
0204                 text: i18nc("Header title for the section in which the direction the book will be navigated can be picked", "Reading Direction")
0205             }
0206             Kirigami.Action {
0207                 text: i18nc("Title for the option which will make the book navigate from left to right", "Left to Right")
0208                 iconName: "format-text-direction-ltr";
0209                 shortcut: rtlMode ? "r" : "";
0210                 enabled: root.isCurrentContext && !Kirigami.Settings.isMobile && root.rtlMode;
0211                 onTriggered: { root.rtlMode = false; }
0212             }
0213             Kirigami.Action {
0214                 text: i18nc("Title for the option which will make the book navigate from right to left", "Right to Left")
0215                 iconName: "format-text-direction-rtl";
0216                 shortcut: rtlMode ? "" : "r";
0217                 enabled: root.isCurrentContext && !Kirigami.Settings.isMobile && !root.rtlMode;
0218                 onTriggered: { root.rtlMode = true; }
0219             }
0220 //             QtObject {
0221 //                 property string text: "Zoom"
0222 //             }
0223 //             Kirigami.Action {
0224 //                 text: "Fit full page"
0225 //                 iconName: "zoom-fit-best";
0226 //                 enabled: root.isCurrentContext && !Kirigami.Settings.isMobile && root.zoomMode !== Peruse.Config.ZoomFull;
0227 //                 onTriggered: { root.zoomMode = Peruse.Config.ZoomFull; }
0228 //             }
0229 //             Kirigami.Action {
0230 //                 text: "Fit width"
0231 //                 iconName: "zoom-fit-width";
0232 //                 enabled: root.isCurrentContext && !Kirigami.Settings.isMobile && root.zoomMode !== Peruse.Config.ZoomFitWidth;
0233 //                 onTriggered: { root.zoomMode = Peruse.Config.ZoomFitWidth; }
0234 //             }
0235 //             Kirigami.Action {
0236 //                 text: "Fit height"
0237 //                 iconName: "zoom-fit-height";
0238 //                 enabled: root.isCurrentContext && !Kirigami.Settings.isMobile && root.zoomMode !== Peruse.Config.ZoomFitHeight;
0239 //                 onTriggered: { root.zoomMode = Peruse.Config.ZoomFitHeight; }
0240 //             }
0241 //             QtObject {}
0242         },
0243         Kirigami.Action {
0244             text: i18nc("Go to the previous frame on the current page", "Previous Frame");
0245             shortcut: root.isCurrentContext && bookInfo.sheetOpen ? "" : StandardKey.MoveToPreviousChar;
0246             iconName: "go-previous";
0247             onTriggered: previousFrame();
0248             enabled: root.isCurrentContext && !Kirigami.Settings.isMobile
0249         },
0250         Kirigami.Action {
0251             text: i18nc("Go to the next frame on the current page", "Next Frame");
0252             shortcut: root.isCurrentContext && bookInfo.sheetOpen ? "" : StandardKey.MoveToNextChar;
0253             iconName: "go-next";
0254             onTriggered: nextFrame();
0255             enabled: root.isCurrentContext && !Kirigami.Settings.isMobile
0256         },
0257         Kirigami.Action {
0258             text: i18nc("Go to the previous page in the book", "Previous Page");
0259             shortcut: root.isCurrentContext && bookInfo.sheetOpen ? "" : StandardKey.MoveToNextPage;
0260             iconName: "go-previous";
0261             onTriggered: previousPage();
0262             enabled: root.isCurrentContext && !Kirigami.Settings.isMobile;
0263         },
0264         Kirigami.Action {
0265             text: i18nc("Go to the next page in the book", "Next Page");
0266             shortcut: bookInfo.sheetOpen ? "" : StandardKey.MoveToNextPage;
0267             iconName: "go-next";
0268             onTriggered: nextPage();
0269             enabled: root.isCurrentContext && !Kirigami.Settings.isMobile;
0270         },
0271         Kirigami.Action {
0272             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");
0273             shortcut: (applicationWindow().visibility === Window.FullScreen) ? (bookInfo.sheetOpen ? "" : "Esc") : "f";
0274             iconName: "view-fullscreen";
0275             onTriggered: toggleFullscreen();
0276             enabled: root.isCurrentContext && !Kirigami.Settings.isMobile;
0277         },
0278         Kirigami.Action {
0279             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");
0280             shortcut: (applicationWindow().visibility === Window.FullScreen) ? "" : (bookInfo.sheetOpen ? "" : "Esc");
0281             iconName: "dialog-close";
0282             onTriggered: closeBook();
0283             enabled: root.isCurrentContext && !Kirigami.Settings.isMobile;
0284         },
0285 
0286         // Invisible actions, for use in bookInfo
0287         Kirigami.Action {
0288             visible: false;
0289             shortcut: bookInfo.sheetOpen ? StandardKey.MoveToPreviousChar : "";
0290             onTriggered: bookInfo.previousBook();
0291             enabled: root.isCurrentContext && !Kirigami.Settings.isMobile;
0292         },
0293         Kirigami.Action {
0294             visible: false;
0295             shortcut: bookInfo.sheetOpen ? StandardKey.MoveToNextChar : "";
0296             onTriggered: bookInfo.nextBook();
0297             enabled: root.isCurrentContext && !Kirigami.Settings.isMobile;
0298         },
0299         Kirigami.Action {
0300             visible: false;
0301             shortcut: bookInfo.sheetOpen ? "Return" : "";
0302             onTriggered: bookInfo.openSelected();
0303             enabled: root.isCurrentContext && !Kirigami.Settings.isMobile;
0304         }
0305     ]
0306     actions {
0307         contextualActions: Kirigami.Settings.isMobile ? mobileActions : desktopActions;
0308         main: bookInfo.sheetOpen ? bookInfoAction : mainBookAction;
0309     }
0310 
0311     function updateContextualActions() {
0312         actions.contextualActions.length = 0;
0313         var newList = Kirigami.Settings.isMobile ? mobileActions : desktopActions;
0314         for(var i = 0; i < viewLoader.item.viewerActions.length; ++i) {
0315             var action = viewLoader.item.viewerActions[i];
0316             newList.push(action);
0317         }
0318         actions.contextualActions = newList;
0319     }
0320 
0321     Kirigami.Action {
0322         id: bookInfoAction;
0323         text: i18nc("Closes the book information drawer", "Close");
0324         shortcut: bookInfo.sheetOpen ? "Esc" : "";
0325         iconName: "dialog-cancel";
0326         onTriggered: bookInfo.close();
0327         enabled: root.isCurrentContext;
0328     }
0329 
0330     /**
0331      * This holds an instance of ViewerBase, which can either be the
0332      * Okular viewer(the fallback one), or one of the type specific
0333      * ones(ImageBrowser based).
0334      */
0335     Item {
0336         width: root.width - (root.leftPadding + root.rightPadding);
0337         height: root.height - (root.topPadding + root.bottomPadding);
0338         Timer {
0339             id: updateCurrent;
0340             interval: applicationWindow().animationDuration;
0341             running: false;
0342             repeat: false;
0343             onTriggered: {
0344                 if(viewLoader.item && viewLoader.item.pagesModel && viewLoader.item.pagesModel.currentPage !== undefined) {
0345                     viewLoader.item.pagesModel.currentPage = root.currentPage;
0346                 }
0347             }
0348         }
0349         NumberAnimation { id: thumbnailMovementAnimation; target: thumbnailNavigator; property: "contentY"; duration: applicationWindow().animationDuration; easing.type: Easing.InOutQuad; }
0350         Loader {
0351             id: viewLoader;
0352             anchors.fill: parent;
0353             property bool loadingCompleted: false;
0354             onStatusChanged: {
0355                 if (status === Loader.Error) {
0356                     console.debug("Error loading up the reader...");
0357                 }
0358             }
0359             onLoaded: item.file = root.file;
0360             Binding {
0361                 target: viewLoader.item;
0362                 property: "rtlMode";
0363                 value: root.rtlMode;
0364             }
0365             Binding {
0366                 target: viewLoader.item;
0367                 property: "zoomMode";
0368                 value: root.zoomMode;
0369             }
0370             Connections {
0371                 target: viewLoader.item;
0372                 function onLoadingCompleted(success) {
0373                     if(success) {
0374                         thumbnailNavigator.model = viewLoader.item.pagesModel;
0375                         if(viewLoader.item.thumbnailComponent) {
0376                             thumbnailNavigator.delegate = viewLoader.item.thumbnailComponent;
0377                         }
0378                         else {
0379                             thumbnailNavigator.delegate = thumbnailComponent;
0380                         }
0381                         peruseConfig.setFilesystemProperty(root.file, "totalPages", viewLoader.item.pageCount);
0382                         if(root.totalPages !== viewLoader.item.pageCount) {
0383                             root.totalPages = viewLoader.item.pageCount;
0384                         }
0385                         viewLoader.item.currentPage = root.currentPage;
0386                         viewLoader.loadingCompleted = true;
0387                         root.updateContextualActions();
0388                         applicationWindow().globalDrawer.close();
0389                     }
0390                 }
0391                 function onTitleChanged() { root.title = viewLoader.item.title; }
0392                 function onCurrentPageChanged() {
0393                     if(root.currentPage !== viewLoader.item.currentPage && viewLoader.loadingCompleted) {
0394                         root.currentPage = viewLoader.item.currentPage;
0395                     }
0396                     thumbnailMovementAnimation.running = false;
0397                     var currentPos = thumbnailNavigator.contentY;
0398                     var newPos;
0399                     thumbnailNavigator.positionViewAtIndex(viewLoader.item.currentPage, ListView.Center);
0400                     newPos = thumbnailNavigator.contentY;
0401                     thumbnailMovementAnimation.from = currentPos;
0402                     thumbnailMovementAnimation.to = newPos;
0403                     thumbnailMovementAnimation.running = true;
0404                 }
0405                 function onViewerActionsChanged() { root.updateContextualActions(); }
0406                 function onGoNextPage() { root.nextPage(); }
0407                 function onGoPreviousPage() { root.previousPage(); }
0408                 function onGoPage() { root.setCurrentPage(pageNumber); }
0409             }
0410         }
0411         Kirigami.PlaceholderMessage {
0412             anchors.centerIn: parent
0413             width: parent.width - (Kirigami.Units.largeSpacing * 8)
0414 
0415             visible: viewLoader.status === Loader.Error;
0416 
0417             icon.name: "emblem-error"
0418             text: i18nc("Message shown on the book reader view when there is an issue loading any reader at all (usually when Okular's qml components are not installed for some reason)", "Failed to load the reader component")
0419             explanation: i18nc("Message shown on the book reader view when there is an issue loading any reader at all (usually when Okular's qml components are not installed for some reason)", "This is generally caused by broken packaging. Contact whomever you got this package from and inform them of this error.");
0420         }
0421     }
0422     /**
0423      * Overlay with book information and a series selection.
0424      */
0425     Kirigami.OverlaySheet {
0426         id: bookInfo;
0427         function setNewCurrentIndex(newIndex) {
0428             seriesListAnimation.running = false;
0429             var currentPos = seriesListView.contentX;
0430             var newPos;
0431             seriesListView.positionViewAtIndex(newIndex, ListView.Center);
0432             newPos = seriesListView.contentX;
0433             seriesListAnimation.from = currentPos;
0434             seriesListAnimation.to = newPos;
0435             seriesListAnimation.running = true;
0436             seriesListView.currentIndex = newIndex;
0437         }
0438         function nextBook() {
0439             if(seriesListView.model && seriesListView.currentIndex < seriesListView.model.rowCount() - 1) {
0440                 setNewCurrentIndex(seriesListView.currentIndex + 1);
0441             }
0442         }
0443         function previousBook() {
0444             if(seriesListView.currentIndex > 0) {
0445                 setNewCurrentIndex(seriesListView.currentIndex - 1);
0446             }
0447         }
0448         function openSelected() {
0449             if (detailsTile.filename!==root.file) {
0450                 closeBook();
0451                 applicationWindow().showBook(detailsTile.filename, detailsTile.currentPage);
0452             }
0453         }
0454         function showBookInfo(filename) {
0455             if(sheetOpen) {
0456                 return;
0457             }
0458             seriesListView.model = contentList.seriesModelForEntry(filename);
0459             if (seriesListView.model) {
0460                 setNewCurrentIndex(seriesListView.model.indexOfFile(filename));
0461             }
0462             open();
0463         }
0464         onSheetOpenChanged: {
0465             if(sheetOpen === false) {
0466                 applicationWindow().controlsVisible = controlsShown;
0467             }
0468             else {
0469                 controlsShown = applicationWindow().controlsVisible;
0470                 applicationWindow().controlsVisible = true;
0471             }
0472         }
0473         property bool controlsShown;
0474         property QtObject currentBook: fakeBook;
0475         property QtObject fakeBook: Peruse.PropertyContainer {
0476             property var author: [""];
0477             property string title: "";
0478             property string filename: "";
0479             property string publisher: "";
0480             property string thumbnail: "";
0481             property string currentPage: "0";
0482             property string totalPages: "0";
0483             property string comment: "";
0484             property var tags: [""];
0485             property var description: [""];
0486             property string rating: "0";
0487         }
0488         Column {
0489             clip: true;
0490             width: root.width - Kirigami.Units.largeSpacing * 2;
0491             height: childrenRect.height + Kirigami.Units.largeSpacing * 2;
0492             spacing: Kirigami.Units.largeSpacing;
0493             ListComponents.BookTile {
0494                 id: detailsTile;
0495                 height: neededHeight;
0496                 width: parent.width;
0497                 author: bookInfo.currentBook.readProperty("author");
0498                 publisher: bookInfo.currentBook.readProperty("publisher");
0499                 title: bookInfo.currentBook.readProperty("title");
0500                 filename: bookInfo.currentBook.readProperty("filename");
0501                 thumbnail: bookInfo.currentBook.readProperty("thumbnail");
0502                 categoryEntriesCount: 0;
0503                 currentPage: bookInfo.currentBook.readProperty("currentPage");
0504                 totalPages: bookInfo.currentBook.readProperty("totalPages");
0505                 description: bookInfo.currentBook.readProperty("description");
0506                 onBookSelected: {
0507                     if(root.file !== fileSelected) {
0508                         bookInfo.openSelected();
0509                     }
0510                 }
0511                 onBookDeleteRequested: {
0512                     // Not strictly needed for the listview itself, but it's kind of
0513                     // nice for making sure the details tile is right
0514                     var oldIndex = seriesListView.currentIndex;
0515                     seriesListView.currentIndex = -1;
0516                     contentList.removeBook(fileSelected, true);
0517                     seriesListView.currentIndex = oldIndex;
0518                 }
0519             }
0520             // tags and ratings, comment by self
0521             // store hook for known series with more content
0522             ListView {
0523                 id: seriesListView;
0524                 width: parent.width;
0525                 height: Kirigami.Units.gridUnit * 12;
0526                 orientation: ListView.Horizontal;
0527                 NumberAnimation { id: seriesListAnimation; target: seriesListView; property: "contentX"; duration: applicationWindow().animationDuration; easing.type: Easing.InOutQuad; }
0528                 delegate: ListComponents.BookTileTall {
0529                     height: model.filename !== "" ? seriesListView.height : 1;
0530                     width: seriesListView.width / 3;
0531                     author: model.author;
0532                     title: model.title;
0533                     filename: model.filename;
0534                     thumbnail: model.thumbnail;
0535                     categoryEntriesCount: 0;
0536                     currentPage: model.currentPage;
0537                     totalPages: model.totalPages;
0538                     onBookSelected:{
0539                         if (seriesListView.currentIndex !== model.index) {
0540                             bookInfo.setNewCurrentIndex(model.index);
0541                         } else {
0542                             bookInfo.openSelected();
0543                         }
0544                     }
0545                     selected: seriesListView.currentIndex === model.index;
0546                 }
0547                 onCurrentIndexChanged: {
0548                     if (model) {
0549                         bookInfo.currentBook = model.get(currentIndex);
0550                     }
0551                 }
0552             }
0553         }
0554     }
0555 
0556     onFileChanged: {
0557         // Let's set the page title to something useful
0558         var book = contentList.bookFromFile(file);
0559         root.title = book.readProperty("title");
0560 
0561         // The idea is to have a number of specialised options as relevant to various
0562         // types of comic books, and then finally fall back to Okular as a catch-all
0563         // but generic viewer component.
0564         var attemptFallback = true;
0565 
0566         var mimetype = contentList.contentModel.getMimetype(file);
0567         console.debug("Mimetype is " + mimetype);
0568         if(mimetype == "application/x-cbz" || mimetype == "application/x-cbr" || mimetype == "application/vnd.comicbook+zip" || mimetype == "application/vnd.comicbook+rar") {
0569             viewLoader.source = "viewers/cbr.qml";
0570             attemptFallback = false;
0571         }
0572         if(mimetype == "inode/directory" || mimetype == "image/jpeg" || mimetype == "image/png") {
0573             viewLoader.source = "viewers/folderofimages.qml";
0574             attemptFallback = false;
0575         }
0576 
0577         if(attemptFallback) {
0578             viewLoader.source = "viewers/okular.qml";
0579         }
0580     }
0581 }