diff --git a/CMakeLists.txt b/CMakeLists.txt index 9da77c6..491bf44 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,35 +1,35 @@ project(Koko) cmake_minimum_required(VERSION 2.8.12) find_package(ECM 0.0.9 REQUIRED NO_MODULE) set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake" ${ECM_MODULE_PATH}) find_package(Qt5 REQUIRED NO_MODULE COMPONENTS Qml Quick Widgets Test Sql Positioning) -find_package(KF5 5.5 REQUIRED COMPONENTS I18n Declarative Config DBusAddons KIO GuiAddons CoreAddons Notifications) +find_package(KF5 5.5 REQUIRED COMPONENTS I18n Declarative Config DBusAddons KIO GuiAddons CoreAddons Notifications IconThemes) find_package(Exiv2 0.21 REQUIRED) include(FeatureSummary) include(ECMAddTests) include(KDEInstallDirs) include(KDECMakeSettings) include(KDECompilerSettings) include(GenerateExportHeader) include(ECMInstallIcons) include_directories( ${CMAKE_SOURCE_DIR} ${CMAKE_SOURCE_DIR}/src ${CMAKE_BINARY_DIR}/src ${EXIV2_INCLUDE_DIR} ) kde_enable_exceptions() add_subdirectory(src) add_subdirectory(autotests) add_subdirectory(qml) add_subdirectory(icons) install(FILES org.kde.koko.appdata.xml DESTINATION ${KDE_INSTALL_METAINFODIR}) feature_summary(WHAT ALL FATAL_ON_MISSING_REQUIRED_PACKAGES) diff --git a/qml/AlbumDelegate.qml b/qml/AlbumDelegate.qml index 217a977..9c62b04 100644 --- a/qml/AlbumDelegate.qml +++ b/qml/AlbumDelegate.qml @@ -1,167 +1,171 @@ /* * Copyright (C) 2017 Atul Sharma * * 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.7 import QtQuick.Controls 2.1 as Controls import org.kde.kquickcontrolsaddons 2.0 as KQA import org.kde.kirigami 2.1 as Kirigami import org.kde.koko 0.1 as Koko Item { id: albumDelegate width: gridView.cellWidth height: gridView.cellHeight Rectangle { - anchors.fill: image + anchors { + fill: image + margins: -1 + } + radius: 2 color: Kirigami.Theme.textColor opacity: 0.2 - visible: model.itemType != Koko.Types.Folder && model.itemType != Koko.Types.Album + visible: model.itemType != Koko.Types.Folder } KQA.QImageItem { id: image anchors.centerIn: parent - width: gridView.cellWidth - (Kirigami.Units.smallSpacing * 2 ) + width: kokoConfig.iconSize height: width smooth: true image: model.thumbnail fillMode: KQA.QImageItem.PreserveAspectCrop } Rectangle { anchors { top: image.top left: image.left right: image.right } visible: textLabel.visible width: image.width height: textLabel.contentHeight + (Kirigami.Units.smallSpacing * 2) color: Kirigami.Theme.viewBackgroundColor opacity: 0.8 } Controls.Label { id: textLabel anchors { left: image.left right: image.right top: image.top bottom: countRect.visible ? countRect.top : image.bottom } visible: model.itemType == Koko.Types.Folder || model.itemType == Koko.Types.Album verticalAlignment: Text.AlignTop padding: Kirigami.Units.smallSpacing elide: Text.ElideRight maximumLineCount: 4 wrapMode: Text.WordWrap color: Kirigami.Theme.textColor text: model.display } Rectangle { id: countRect anchors { bottom: image.bottom left: image.left right: image.right } visible: model.fileCount && model.itemType == Koko.Types.Folder || model.itemType == Koko.Types.Album height: countLabel.contentHeight + (Kirigami.Units.smallSpacing * 2) color: Kirigami.Theme.viewBackgroundColor opacity: 0.8 Controls.Label { id: countLabel padding: Kirigami.Units.smallSpacing elide: Text.ElideRight maximumLineCount: 4 wrapMode: Text.WordWrap color: Kirigami.Theme.textColor text: i18np("1 Image", "%1 Images", model.fileCount) } } SelectionButton { id: selectionButton visible: ( albumThumbnailMouseArea.containsMouse || iconMouseArea.containsMouse || page.state == "selecting") && !(model.itemType == Koko.Types.Folder || model.itemType == Koko.Types.Album) } SelectionDelegateHighlight { id: selectionHighlight visible: model.selected } MouseArea { id: albumThumbnailMouseArea anchors.fill: parent hoverEnabled: true onPressAndHold: { gridView.model.toggleSelected(model.index) } onClicked: { if (page.state == "selecting" || (mouse.modifiers & Qt.ControlModifier ) && (model.itemType == Koko.Types.Image)) { gridView.model.toggleSelected(model.index) } else { activate(); } } } Keys.onPressed: { switch (event.key) { case Qt.Key_Enter: case Qt.Key_Return: case Qt.Key_Space: activate(); break; default: break; } } function activate( ) { gridView.model.clearSelections() gridView.currentIndex = model.index; switch( model.itemType) { case Koko.Types.Album: { imageListModel.query = imageListModel.queryForIndex( model.sourceIndex) sortedListModel.sourceModel = imageListModel collectionSelected( sortedListModel, model.display) break; } case Koko.Types.Folder: { imageFolderModel.url = model.imageurl sortedListModel.sourceModel = imageFolderModel folderSelected( sortedListModel, model.display) break; } case Koko.Types.Image: { imageSelected( model.sourceIndex) break; } default: { console.log("Unknown") break; } } } } diff --git a/qml/AlbumView.qml b/qml/AlbumView.qml index c72e6f4..2e255ce 100644 --- a/qml/AlbumView.qml +++ b/qml/AlbumView.qml @@ -1,172 +1,179 @@ /* * Copyright (C) 2017 Atul Sharma * * 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.7 import QtQuick.Controls 2.1 as Controls import org.kde.kirigami 2.1 as Kirigami import org.kde.koko 0.1 as Koko Kirigami.ScrollablePage { id: page property alias model: gridView.model signal collectionSelected(QtObject selectedModel, string cover) signal imageSelected(int currentIndex) signal folderSelected(QtObject selectedModel, string cover) keyboardNavigationEnabled: true focus: true states: [ State { name: "browsing" when: !model.hasSelectedImages }, State { name: "selecting" when: model.hasSelectedImages && Kirigami.Settings.isMobile } ] actions { main: Kirigami.Action { iconName: "edit-select-none" text: i18n("Deselect All") tooltip: i18n("De-selects all the selected images") enabled: model.hasSelectedImages visible: model.hasSelectedImages && Kirigami.Settings.isMobile onTriggered: model.clearSelections() } contextualActions: [ Kirigami.Action { iconName: "edit-select-all" text: i18n("Select All") tooltip: i18n("Selects all the images in the current view") enabled: model.containImages onTriggered: model.selectAll() }, Kirigami.Action { iconName: "edit-select-none" text: i18n("Deselect All") tooltip: i18n("De-selects all the selected images") enabled: model.hasSelectedImages onTriggered: model.clearSelections() }, Kirigami.Action { iconName: "emblem-shared-symbolic" text: i18n("Share") tooltip: i18n("Share the selected images") enabled: model.hasSelectedImages onTriggered: { shareMenu.open(); shareMenu.inputData = { "urls": model.selectedImages(), "mimeType": "image/" } } }, Kirigami.Action { iconName: "group-delete" text: i18n("Delete Selection") tooltip: i18n("Move selected items to trash") enabled: model.hasSelectedImages onTriggered: model.deleteSelection() } ] } background: Rectangle { color: Kirigami.Theme.viewBackgroundColor } Keys.onPressed: { switch (event.key) { case Qt.Key_Escape: gridView.model.clearSelections() break; default: break; } } - leftPadding: (page.width - Math.floor(page.width / gridView.cellWidth) * gridView.cellWidth)/2 - rightPadding: leftPadding - ShareDialog { id: shareMenu inputData: { "urls": [], "mimeType": ["image/"] } onFinished: { if (error==0 && output.url !== "") { console.assert(output.url !== undefined); var resultUrl = output.url; console.log("Received", resultUrl) notificationManager.showNotification( true, resultUrl); clipboard.content = resultUrl; } else { notificationManager.showNotification( false); } } } GridView { id: gridView //FIXME: right now if those two objects are out of this, the whole page breaks Koko.SortModel { id: sortedListModel } Koko.ImageFolderModel { id: imageFolderModel } keyNavigationEnabled: true - property real widthToApproximate: (applicationWindow().wideScreen ? applicationWindow().pageStack.defaultColumnWidth : page.width) - (Kirigami.Settings.isMobile ? Kirigami.Units.gridUnit : 0) + property real widthToApproximate: (applicationWindow().wideScreen ? applicationWindow().pageStack.defaultColumnWidth : page.width) - (1||Kirigami.Settings.isMobile ? Kirigami.Units.gridUnit : 0) - cellWidth: widthToApproximate/Math.round(widthToApproximate/Kirigami.Units.iconSizes.huge) + cellWidth: Math.floor(width/Math.floor(width/(kokoConfig.iconSize + Kirigami.Units.largeSpacing * 2))) - cellHeight: cellWidth - - highlight: Rectangle { color: Kirigami.Theme.highlightColor} + cellHeight: kokoConfig.iconSize + Kirigami.Units.largeSpacing * 2 + + highlightMoveDuration: 0 + highlight: Item { + Rectangle { + anchors.centerIn: parent + width: Math.min(parent.width, parent.height) + height: width + color: Qt.rgba(Kirigami.Theme.highlightColor.r, Kirigami.Theme.highlightColor.g, Kirigami.Theme.highlightColor.b, 0.3) + border.color: Kirigami.Theme.highlightColor + radius: 2 + } + } delegate: AlbumDelegate {} Controls.Label { anchors.centerIn: parent text: i18n("No Images Found") visible: gridView.count == 0 font.pixelSize: Kirigami.Units.gridUnit * 1 } } onCollectionSelected: pageStack.push( Qt.resolvedUrl("AlbumView.qml"), { "model": selectedModel, "title": i18n(cover)}) onFolderSelected: pageStack.push( Qt.resolvedUrl("AlbumView.qml"), { "model": selectedModel, "title": i18n(cover)}) onImageSelected: { currentImage.model = model.sourceModel currentImage.index = currentIndex applicationWindow().pageStack.layers.push(imageViewerComponent); } } diff --git a/qml/Sidebar.qml b/qml/Sidebar.qml index b17e5ee..bd6af94 100644 --- a/qml/Sidebar.qml +++ b/qml/Sidebar.qml @@ -1,127 +1,136 @@ /* * Copyright (C) 2017 Atul Sharma * * 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.3 import QtQuick.Controls 2.1 as Controls import org.kde.kirigami 2.1 as Kirigami Kirigami.GlobalDrawer { signal filterBy(string value) property Kirigami.Action previouslySelectedAction width: Kirigami.Units.gridUnit * 10 actions: [ Kirigami.Action { text: i18n("Locations") iconName: "tag-places" enabled: false }, Kirigami.Action { id: countryAction text: i18n("By Country") checkable: true onTriggered: { filterBy("Countries") previouslySelectedAction = countryAction } }, Kirigami.Action { id: stateAction text: i18n("By State") checkable: true onTriggered: { filterBy("States") previouslySelectedAction = stateAction } }, Kirigami.Action { id: cityAction text: i18n("By City") checkable: true onTriggered: { filterBy("Cities") previouslySelectedAction = cityAction } }, Kirigami.Action { text: i18n("Time") enabled: false iconName: "view-calendar" }, Kirigami.Action { id: yearAction text: i18n("By Year") checkable: true onTriggered: { filterBy("Years") previouslySelectedAction = yearAction } }, Kirigami.Action { id: monthAction text: i18n("By month") checkable: true onTriggered: { filterBy("Months") previouslySelectedAction = monthAction } }, Kirigami.Action { id: weekAction text: i18n("By Week") checkable: true onTriggered: { filterBy("Weeks") previouslySelectedAction = weekAction } }, Kirigami.Action { id: "dayAction" text: i18n("By Day") checkable: true onTriggered: { filterBy("Days") previouslySelectedAction = dayAction } }, Kirigami.Action { text: i18n("Path") enabled: false iconName: "folder-symbolic" }, Kirigami.Action { id: folderAction text: i18n("By Folder") checkable: true onTriggered: { filterBy("Folders") previouslySelectedAction = folderAction } } ] - + + Controls.Slider { + Layout.fillWidth: true + from: Kirigami.Units.iconSizes.medium + to: Kirigami.Units.iconSizes.enormous + value: kokoConfig.iconSize + onMoved: kokoConfig.iconSize = value; + } + Component.onCompleted: { folderAction.checked = true previouslySelectedAction = folderAction } } diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 54ba121..ddf6054 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,135 +1,136 @@ # # Common Library # set(LIB_SRCS imagestorage.cpp filesystemimagefetcher.cpp ${LIB_SRCS} ) add_library(kokocommon SHARED ${LIB_SRCS}) target_link_libraries(kokocommon Qt5::Core Qt5::Positioning Qt5::Sql KF5::CoreAddons ) generate_export_header(kokocommon BASE_NAME KOKO EXPORT_FILE_NAME koko_export.h) install(TARGETS kokocommon EXPORT KokoLibraryTargets ${INSTALL_TARGETS_DEFAULT_ARGS}) # # Application # add_executable(koko main.cpp reversegeocoder.cpp filesystemtracker.cpp processor.cpp committimer.cpp imageprocessorrunnable.cpp exiv2extractor.cpp kokoconfig.cpp kdtree.c ) target_link_libraries(koko Qt5::Quick Qt5::Widgets Qt5::Qml Qt5::Positioning KF5::ConfigCore KF5::DBusAddons KF5::I18n KF5::CoreAddons KF5::KIOCore + KF5::IconThemes kokocommon ${EXIV2_LIBRARIES} ) install(TARGETS koko ${INSTALL_TARGETS_DEFAULT_ARGS}) # # QML Plugin # set (qml_plugin_SRCS qmlplugins.cpp tagmodel.cpp imagelocationmodel.cpp imagetimemodel.cpp imagefoldermodel.cpp sortmodel.cpp allimagesmodel.cpp imagelistmodel.cpp notificationmanager.cpp types.cpp roles.cpp imagedocument.cpp ) add_library (kokoqmlplugin SHARED ${qml_plugin_SRCS}) target_link_libraries (kokoqmlplugin Qt5::Qml KF5::KIOCore KF5::KIOFileWidgets KF5::KIOWidgets KF5::GuiAddons KF5::I18n KF5::Notifications kokocommon ) install (TARGETS kokoqmlplugin DESTINATION ${QML_INSTALL_DIR}/org/kde/koko) install (FILES qmldir DESTINATION ${QML_INSTALL_DIR}/org/kde/koko) install (FILES org.kde.koko.desktop DESTINATION ${XDG_APPS_INSTALL_DIR}) # # Reverse GeoLookup Data # # Packagers can download the file and put it in the tarball if (EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/cities1000.zip) file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/cities1000.zip DESTINATION ${CMAKE_CURRENT_BINARY_DIR}) endif() if (NOT EXISTS ${CMAKE_CURRENT_BINARY_DIR}/cities1000.zip) file (DOWNLOAD "http://download.geonames.org/export/dump/cities1000.zip" ${CMAKE_CURRENT_BINARY_DIR}/cities1000.zip SHOW_PROGRESS ) endif() execute_process( COMMAND ${CMAKE_COMMAND} -E tar -xzf ${CMAKE_CURRENT_BINARY_DIR}/cities1000.zip WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} ) if (EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/admin1CodesASCII.txt) file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/admin1CodesASCII.txt DESTINATION ${CMAKE_CURRENT_BINARY_DIR}) endif() if (NOT EXISTS ${CMAKE_CURRENT_BINARY_DIR}/admin1CodesASCII.txt) file (DOWNLOAD "http://download.geonames.org/export/dump/admin1CodesASCII.txt" ${CMAKE_CURRENT_BINARY_DIR}/admin1CodesASCII.txt SHOW_PROGRESS ) endif() file(RENAME ${CMAKE_CURRENT_BINARY_DIR}/admin1CodesASCII.txt ${CMAKE_CURRENT_BINARY_DIR}/admin1Codes.txt) if (EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/admin2Codes.txt) file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/admin2Codes.txt DESTINATION ${CMAKE_CURRENT_BINARY_DIR}) endif() if (NOT EXISTS ${CMAKE_CURRENT_BINARY_DIR}/admin2Codes.txt) file (DOWNLOAD "http://download.geonames.org/export/dump/admin2Codes.txt" ${CMAKE_CURRENT_BINARY_DIR}/admin2Codes.txt SHOW_PROGRESS ) endif() install (FILES ${CMAKE_CURRENT_BINARY_DIR}/cities1000.txt DESTINATION ${DATA_INSTALL_DIR}/koko) install (FILES ${CMAKE_CURRENT_BINARY_DIR}/admin1Codes.txt DESTINATION ${DATA_INSTALL_DIR}/koko) install (FILES ${CMAKE_CURRENT_BINARY_DIR}/admin2Codes.txt DESTINATION ${DATA_INSTALL_DIR}/koko) install (FILES countries.csv DESTINATION ${DATA_INSTALL_DIR}/koko) install (FILES koko.notifyrc DESTINATION ${KNOTIFYRC_INSTALL_DIR}) diff --git a/src/kokoconfig.cpp b/src/kokoconfig.cpp index 8b8b086..ebeb182 100644 --- a/src/kokoconfig.cpp +++ b/src/kokoconfig.cpp @@ -1,54 +1,68 @@ /* * Copyright (C) 2014 Vishesh Handa * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This 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, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * */ #include "kokoconfig.h" #include #include +#include + KokoConfig::KokoConfig(QObject* parent) : QObject(parent) , m_config("kokorc") { } KokoConfig::~KokoConfig() { } bool KokoConfig::initialRun() const { - return m_config.group("general").readEntry("initial run", true); + return m_config.group("general").readEntry("InitialRun", true); } void KokoConfig::setInitialRun(bool value) { - m_config.group("general").writeEntry("initial run", value); + m_config.group("general").writeEntry("InitialRun", value); + m_config.sync(); +} + +int KokoConfig::iconSize() const +{ + return m_config.group("general").readEntry("IconSize", (int)KIconLoader::SizeHuge); +} + +void KokoConfig::setIconSize(int size) +{ + m_config.group("general").writeEntry("IconSize", size); m_config.sync(); + emit iconSizeChanged(); } void KokoConfig::reset() { QString path = QStandardPaths::locate(QStandardPaths::ConfigLocation, m_config.name()); if (QFile::exists(path)) { QFile::remove(path); } m_config.reparseConfiguration(); } diff --git a/src/kokoconfig.h b/src/kokoconfig.h index 4c80d9d..1fac4ee 100644 --- a/src/kokoconfig.h +++ b/src/kokoconfig.h @@ -1,43 +1,50 @@ /* * Copyright (C) 2014 Vishesh Handa * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This 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, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * */ #ifndef JUNGLE_JUNGLECONFIG_H #define JUNGLE_JUNGLECONFIG_H #include #include #include class KokoConfig : public QObject { Q_OBJECT Q_PROPERTY(bool initialRun READ initialRun WRITE setInitialRun) + Q_PROPERTY(int iconSize READ iconSize WRITE setIconSize NOTIFY iconSizeChanged) public: KokoConfig(QObject* parent = 0); virtual ~KokoConfig(); bool initialRun() const; void setInitialRun(bool value); + int iconSize() const; + void setIconSize(int size); + void reset(); +Q_SIGNALS: + void iconSizeChanged(); + private: KConfig m_config; }; #endif // JUNGLE_JUNGLECONFIG_H