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 }