diff --git a/CMakeLists.txt b/CMakeLists.txt index b8c9cb0d..3207979f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,552 +1,553 @@ cmake_minimum_required(VERSION 3.2.0) project(kphotoalbum VERSION 5.5) if(POLICY CMP0063) cmake_policy(SET CMP0063 NEW) endif() # provide drop-down menu for build-type in cmake-gui: set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS ";Debug;Release;RelWithDebInfo;MinSizeRel") list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules) find_package(ECM REQUIRED NO_MODULE) list(APPEND CMAKE_MODULE_PATH ${ECM_MODULE_PATH} ) include(KDEInstallDirs) include(KDECompilerSettings) include(KDECMakeSettings) include(FeatureSummary) # enable exceptions: kde_enable_exceptions() add_definitions( -DQT_NO_CAST_FROM_ASCII -DQT_NO_CAST_TO_ASCII -DQT_NO_URL_CAST_FROM_STRING -DQT_NO_CAST_FROM_BYTEARRAY -DQT_DEPRECATED_WARNINGS -DQT_STRICT_ITERATORS ) set(CMAKE_CXX_STANDARD 11) set(CMAKE_CXX_REQUIRED TRUE) ########### dependencies ############### find_package(Qt5 5.9 REQUIRED COMPONENTS Sql Xml Widgets Network) find_package(Phonon4Qt5 REQUIRED) find_package(KF5 5.44 REQUIRED COMPONENTS Archive Completion Config CoreAddons DocTools I18n IconThemes JobWidgets KIO TextWidgets XmlGui WidgetsAddons) find_package(JPEG REQUIRED) if(JPEG_FOUND) include_directories(${JPEG_INCLUDE_DIR}) endif() ### 2018-12-30 jzarl # When Exiv2 0.26 can be deprecated, FindExiv2.cmake should be removed # and only find_package(exiv2) should be used find_package(exiv2 CONFIG QUIET) if(exiv2_FOUND) # search againg with REQUIRED, so that the feature summary correctly shows exiv as required dependency find_package(exiv2 CONFIG REQUIRED) set(EXIV2_LIBRARIES exiv2lib) else() find_package(Exiv2 REQUIRED) endif() find_package(KF5Kipi 5.1.0) set_package_properties(KF5Kipi PROPERTIES TYPE RECOMMENDED PURPOSE "Enables integration of KDE image plugin interface (shared functionality between KPhotoAlbum and other apps like gwenview or digikam)" ) set(HASKIPI ${KF5Kipi_FOUND}) find_package(KF5Purpose) set_package_properties(KF5Purpose PROPERTIES TYPE RECOMMENDED PURPOSE "Enables integration with KDE Purpose plugins, which provide image sharing and similar functionality." ) find_package(KF5KDcraw) set_package_properties(KF5KDcraw PROPERTIES TYPE OPTIONAL PURPOSE "Enables RAW image support" ) set(HAVE_KDCRAW ${KF5KDcraw_FOUND} ) find_package(KF5KGeoMap) set_package_properties(KF5KGeoMap PROPERTIES TYPE OPTIONAL PURPOSE "Enables support for geographic map location using embedded GPS information." ) set(HAVE_KGEOMAP ${KF5KGeoMap_FOUND}) add_custom_target( UpdateVersion ALL COMMAND ${CMAKE_COMMAND} -DBASE_DIR=${CMAKE_CURRENT_SOURCE_DIR} -DPROJECT_NAME=KPA -DPROJECT_VERSION="${PROJECT_VERSION}" -DCMAKE_BUILD_TYPE="${CMAKE_BUILD_TYPE}" -P "${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules/UpdateVersion.cmake" COMMENT "Updating version header." BYPRODUCTS "${CMAKE_CURRENT_SOURCE_DIR}/version.h" ) # For config-kpa-*.h include_directories(${CMAKE_CURRENT_BINARY_DIR}) set(libdatebar_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/DateBar/documentation.h ${CMAKE_CURRENT_SOURCE_DIR}/DateBar/DateBarWidget.cpp ${CMAKE_CURRENT_SOURCE_DIR}/DateBar/ViewHandler.cpp ${CMAKE_CURRENT_SOURCE_DIR}/DateBar/MouseHandler.cpp ${CMAKE_CURRENT_SOURCE_DIR}/DateBar/MouseHandler.cpp ) set(libSettings_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/Settings/documentation.h ${CMAKE_CURRENT_SOURCE_DIR}/Settings/SettingsData.cpp ${CMAKE_CURRENT_SOURCE_DIR}/Settings/SettingsDialog.cpp ${CMAKE_CURRENT_SOURCE_DIR}/Settings/ViewerSizeConfig.cpp ${CMAKE_CURRENT_SOURCE_DIR}/Settings/CategoryItem.cpp ${CMAKE_CURRENT_SOURCE_DIR}/Settings/CategoryPage.cpp ${CMAKE_CURRENT_SOURCE_DIR}/Settings/TagGroupsPage.cpp ${CMAKE_CURRENT_SOURCE_DIR}/Settings/GeneralPage.cpp ${CMAKE_CURRENT_SOURCE_DIR}/Settings/FileVersionDetectionPage.cpp ${CMAKE_CURRENT_SOURCE_DIR}/Settings/ThumbnailsPage.cpp ${CMAKE_CURRENT_SOURCE_DIR}/Settings/ViewerPage.cpp ${CMAKE_CURRENT_SOURCE_DIR}/Settings/DatabaseBackendPage.cpp ${CMAKE_CURRENT_SOURCE_DIR}/Settings/UntaggedGroupBox.cpp ${CMAKE_CURRENT_SOURCE_DIR}/Settings/CategoriesGroupsWidget.cpp ${CMAKE_CURRENT_SOURCE_DIR}/Settings/BirthdayPage.cpp ${CMAKE_CURRENT_SOURCE_DIR}/Settings/DateTableWidgetItem.cpp ${CMAKE_CURRENT_SOURCE_DIR}/Settings/Logging.cpp ) set(libxmldb_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/XMLDB/documentation.h ${CMAKE_CURRENT_SOURCE_DIR}/XMLDB/Database.cpp ${CMAKE_CURRENT_SOURCE_DIR}/XMLDB/XMLCategoryCollection.cpp ${CMAKE_CURRENT_SOURCE_DIR}/XMLDB/XMLCategory.cpp ${CMAKE_CURRENT_SOURCE_DIR}/XMLDB/XMLImageDateCollection.cpp ${CMAKE_CURRENT_SOURCE_DIR}/XMLDB/NumberedBackup.cpp ${CMAKE_CURRENT_SOURCE_DIR}/XMLDB/FileReader.cpp ${CMAKE_CURRENT_SOURCE_DIR}/XMLDB/FileWriter.cpp ${CMAKE_CURRENT_SOURCE_DIR}/XMLDB/ElementWriter.cpp ${CMAKE_CURRENT_SOURCE_DIR}/XMLDB/XmlReader.cpp ${CMAKE_CURRENT_SOURCE_DIR}/XMLDB/CompressFileInfo.cpp ${CMAKE_CURRENT_SOURCE_DIR}/XMLDB/Logging.cpp ) set(libThumbnailView_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/ThumbnailView/documentation.h ${CMAKE_CURRENT_SOURCE_DIR}/ThumbnailView/FilterWidget.cpp ${CMAKE_CURRENT_SOURCE_DIR}/ThumbnailView/ThumbnailRequest.cpp ${CMAKE_CURRENT_SOURCE_DIR}/ThumbnailView/ThumbnailToolTip.cpp ${CMAKE_CURRENT_SOURCE_DIR}/ThumbnailView/ThumbnailWidget.cpp ${CMAKE_CURRENT_SOURCE_DIR}/ThumbnailView/GridResizeInteraction.cpp ${CMAKE_CURRENT_SOURCE_DIR}/ThumbnailView/GridResizeSlider.cpp ${CMAKE_CURRENT_SOURCE_DIR}/ThumbnailView/SelectionInteraction.cpp ${CMAKE_CURRENT_SOURCE_DIR}/ThumbnailView/MouseTrackingInteraction.cpp ${CMAKE_CURRENT_SOURCE_DIR}/ThumbnailView/CellGeometry.cpp ${CMAKE_CURRENT_SOURCE_DIR}/ThumbnailView/ThumbnailModel.cpp ${CMAKE_CURRENT_SOURCE_DIR}/ThumbnailView/ThumbnailFacade.cpp ${CMAKE_CURRENT_SOURCE_DIR}/ThumbnailView/ThumbnailComponent.cpp ${CMAKE_CURRENT_SOURCE_DIR}/ThumbnailView/KeyboardEventHandler.cpp ${CMAKE_CURRENT_SOURCE_DIR}/ThumbnailView/ThumbnailDND.cpp ${CMAKE_CURRENT_SOURCE_DIR}/ThumbnailView/Delegate.cpp ${CMAKE_CURRENT_SOURCE_DIR}/ThumbnailView/SelectionMaintainer.cpp ${CMAKE_CURRENT_SOURCE_DIR}/ThumbnailView/VideoThumbnailCycler.cpp ${CMAKE_CURRENT_SOURCE_DIR}/ThumbnailView/Logging.cpp ${CMAKE_CURRENT_SOURCE_DIR}/ThumbnailView/MouseInteraction.cpp ${CMAKE_CURRENT_SOURCE_DIR}/ThumbnailView/ThumbnailFactory.cpp ${CMAKE_CURRENT_SOURCE_DIR}/ThumbnailView/enums.cpp ) set(libPlugins_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/Plugins/documentation.h ${CMAKE_CURRENT_SOURCE_DIR}/Plugins/Logging.cpp ) if(KF5Kipi_FOUND) set(libPlugins_SRCS ${libPlugins_SRCS} ${CMAKE_CURRENT_SOURCE_DIR}/Plugins/Interface.cpp ${CMAKE_CURRENT_SOURCE_DIR}/Plugins/ImageCollection.cpp ${CMAKE_CURRENT_SOURCE_DIR}/Plugins/ImageInfo.cpp ${CMAKE_CURRENT_SOURCE_DIR}/Plugins/CategoryImageCollection.cpp ${CMAKE_CURRENT_SOURCE_DIR}/Plugins/ImageCollectionSelector.cpp ${CMAKE_CURRENT_SOURCE_DIR}/Settings/PluginsPage.cpp ${CMAKE_CURRENT_SOURCE_DIR}/Plugins/UploadWidget.cpp ${CMAKE_CURRENT_SOURCE_DIR}/Plugins/UploadImageCollection.cpp ) endif() if(KF5Purpose_FOUND) set(libPlugins_SRCS ${libPlugins_SRCS} ${CMAKE_CURRENT_SOURCE_DIR}/Plugins/PurposeMenu.cpp ) endif() set(libViewer_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/Viewer/documentation.h ${CMAKE_CURRENT_SOURCE_DIR}/Viewer/ViewerWidget.cpp ${CMAKE_CURRENT_SOURCE_DIR}/Viewer/ImageDisplay.cpp ${CMAKE_CURRENT_SOURCE_DIR}/Viewer/ViewHandler.cpp ${CMAKE_CURRENT_SOURCE_DIR}/Viewer/SpeedDisplay.cpp ${CMAKE_CURRENT_SOURCE_DIR}/Viewer/InfoBox.cpp ${CMAKE_CURRENT_SOURCE_DIR}/Viewer/CategoryImageConfig.cpp ${CMAKE_CURRENT_SOURCE_DIR}/Viewer/AbstractDisplay.cpp ${CMAKE_CURRENT_SOURCE_DIR}/Viewer/VideoDisplay.cpp ${CMAKE_CURRENT_SOURCE_DIR}/Viewer/TextDisplay.cpp ${CMAKE_CURRENT_SOURCE_DIR}/Viewer/InfoBoxResizer.cpp ${CMAKE_CURRENT_SOURCE_DIR}/Viewer/VisibleOptionsMenu.cpp ${CMAKE_CURRENT_SOURCE_DIR}/Viewer/VideoShooter.cpp ${CMAKE_CURRENT_SOURCE_DIR}/Viewer/TaggedArea.cpp ${CMAKE_CURRENT_SOURCE_DIR}/Viewer/Logging.cpp ) set(libCategoryListView_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/CategoryListView/documentation.h ${CMAKE_CURRENT_SOURCE_DIR}/CategoryListView/DragableTreeWidget.cpp ${CMAKE_CURRENT_SOURCE_DIR}/CategoryListView/CheckDropItem.cpp ${CMAKE_CURRENT_SOURCE_DIR}/CategoryListView/DragItemInfo.cpp ${CMAKE_CURRENT_SOURCE_DIR}/CategoryListView/Logging.cpp ) set(libHTMLGenerator_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/HTMLGenerator/documentation.h ${CMAKE_CURRENT_SOURCE_DIR}/HTMLGenerator/HTMLDialog.cpp ${CMAKE_CURRENT_SOURCE_DIR}/HTMLGenerator/Generator.cpp ${CMAKE_CURRENT_SOURCE_DIR}/HTMLGenerator/Setup.cpp ${CMAKE_CURRENT_SOURCE_DIR}/HTMLGenerator/ImageSizeCheckBox.h ${CMAKE_CURRENT_SOURCE_DIR}/HTMLGenerator/Logging.cpp ${CMAKE_CURRENT_SOURCE_DIR}/HTMLGenerator/ImageSizeCheckBox.cpp ) set(libUtilities_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/Utilities/documentation.h ${CMAKE_CURRENT_SOURCE_DIR}/Utilities/AlgorithmHelper.cpp ${CMAKE_CURRENT_SOURCE_DIR}/Utilities/ShowBusyCursor.cpp ${CMAKE_CURRENT_SOURCE_DIR}/Utilities/List.cpp ${CMAKE_CURRENT_SOURCE_DIR}/Utilities/UniqFilenameMapper.cpp ${CMAKE_CURRENT_SOURCE_DIR}/Utilities/FileUtil.cpp ${CMAKE_CURRENT_SOURCE_DIR}/Utilities/BooleanGuard.cpp ${CMAKE_CURRENT_SOURCE_DIR}/Utilities/Process.cpp ${CMAKE_CURRENT_SOURCE_DIR}/Utilities/DeleteFiles.cpp ${CMAKE_CURRENT_SOURCE_DIR}/Utilities/ToolTip.cpp ${CMAKE_CURRENT_SOURCE_DIR}/Utilities/Logging.cpp ${CMAKE_CURRENT_SOURCE_DIR}/Utilities/JpeglibWithFix.cpp ${CMAKE_CURRENT_SOURCE_DIR}/Utilities/StringSet.cpp ${CMAKE_CURRENT_SOURCE_DIR}/Utilities/FastJpeg.cpp ${CMAKE_CURRENT_SOURCE_DIR}/Utilities/DemoUtil.cpp ${CMAKE_CURRENT_SOURCE_DIR}/Utilities/DescriptionUtil.cpp ${CMAKE_CURRENT_SOURCE_DIR}/Utilities/FileNameUtil.cpp ${CMAKE_CURRENT_SOURCE_DIR}/Utilities/VideoUtil.cpp ${CMAKE_CURRENT_SOURCE_DIR}/Utilities/ImageUtil.cpp ) set(libMainWindow_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/MainWindow/documentation.h ${CMAKE_CURRENT_SOURCE_DIR}/MainWindow/DeleteDialog.cpp ${CMAKE_CURRENT_SOURCE_DIR}/MainWindow/RunDialog.cpp ${CMAKE_CURRENT_SOURCE_DIR}/MainWindow/FeatureDialog.cpp ${CMAKE_CURRENT_SOURCE_DIR}/MainWindow/InvalidDateFinder.cpp ${CMAKE_CURRENT_SOURCE_DIR}/MainWindow/AutoStackImages.cpp ${CMAKE_CURRENT_SOURCE_DIR}/MainWindow/TokenEditor.cpp ${CMAKE_CURRENT_SOURCE_DIR}/MainWindow/WelcomeDialog.cpp ${CMAKE_CURRENT_SOURCE_DIR}/MainWindow/Window.cpp ${CMAKE_CURRENT_SOURCE_DIR}/MainWindow/SplashScreen.cpp ${CMAKE_CURRENT_SOURCE_DIR}/MainWindow/ExternalPopup.cpp ${CMAKE_CURRENT_SOURCE_DIR}/MainWindow/CategoryImagePopup.cpp ${CMAKE_CURRENT_SOURCE_DIR}/MainWindow/SearchBar.cpp ${CMAKE_CURRENT_SOURCE_DIR}/MainWindow/ImageCounter.cpp ${CMAKE_CURRENT_SOURCE_DIR}/MainWindow/DirtyIndicator.cpp ${CMAKE_CURRENT_SOURCE_DIR}/MainWindow/StatisticsDialog.cpp ${CMAKE_CURRENT_SOURCE_DIR}/MainWindow/BreadcrumbViewer.cpp ${CMAKE_CURRENT_SOURCE_DIR}/MainWindow/StatusBar.cpp ${CMAKE_CURRENT_SOURCE_DIR}/MainWindow/UpdateVideoThumbnail.cpp ${CMAKE_CURRENT_SOURCE_DIR}/MainWindow/DuplicateMerger/DuplicateMerger.cpp ${CMAKE_CURRENT_SOURCE_DIR}/MainWindow/DuplicateMerger/DuplicateMatch.cpp ${CMAKE_CURRENT_SOURCE_DIR}/MainWindow/DuplicateMerger/MergeToolTip.cpp ${CMAKE_CURRENT_SOURCE_DIR}/MainWindow/CopyPopup.cpp ${CMAKE_CURRENT_SOURCE_DIR}/MainWindow/Options.cpp ${CMAKE_CURRENT_SOURCE_DIR}/MainWindow/Logging.cpp ) set(libImageManager_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/ImageManager/documentation.h ${CMAKE_CURRENT_SOURCE_DIR}/ImageManager/ImageLoaderThread.cpp ${CMAKE_CURRENT_SOURCE_DIR}/ImageManager/AsyncLoader.cpp ${CMAKE_CURRENT_SOURCE_DIR}/ImageManager/ImageRequest.cpp ${CMAKE_CURRENT_SOURCE_DIR}/ImageManager/ImageClientInterface.cpp ${CMAKE_CURRENT_SOURCE_DIR}/ImageManager/ImageDecoder.cpp ${CMAKE_CURRENT_SOURCE_DIR}/ImageManager/RawImageDecoder.cpp ${CMAKE_CURRENT_SOURCE_DIR}/ImageManager/RequestQueue.cpp ${CMAKE_CURRENT_SOURCE_DIR}/ImageManager/ThumbnailCache.cpp ${CMAKE_CURRENT_SOURCE_DIR}/ImageManager/ImageEvent.cpp ${CMAKE_CURRENT_SOURCE_DIR}/ImageManager/ThumbnailBuilder.cpp ${CMAKE_CURRENT_SOURCE_DIR}/ImageManager/PreloadRequest.cpp ${CMAKE_CURRENT_SOURCE_DIR}/ImageManager/CancelEvent.cpp ${CMAKE_CURRENT_SOURCE_DIR}/ImageManager/VideoImageRescaleRequest.cpp ${CMAKE_CURRENT_SOURCE_DIR}/ImageManager/VideoThumbnails.cpp ${CMAKE_CURRENT_SOURCE_DIR}/ImageManager/VideoLengthExtractor.cpp ${CMAKE_CURRENT_SOURCE_DIR}/ImageManager/ExtractOneVideoFrame.cpp ${CMAKE_CURRENT_SOURCE_DIR}/ImageManager/Logging.cpp ${CMAKE_CURRENT_SOURCE_DIR}/ImageManager/CacheFileInfo.cpp ${CMAKE_CURRENT_SOURCE_DIR}/ImageManager/enums.cpp ) set(libDB_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/DB/documentation.h ${CMAKE_CURRENT_SOURCE_DIR}/DB/ImageInfo.cpp ${CMAKE_CURRENT_SOURCE_DIR}/DB/Category.cpp ${CMAKE_CURRENT_SOURCE_DIR}/DB/CategoryCollection.cpp ${CMAKE_CURRENT_SOURCE_DIR}/DB/ExactCategoryMatcher.cpp ${CMAKE_CURRENT_SOURCE_DIR}/DB/ImageDate.cpp ${CMAKE_CURRENT_SOURCE_DIR}/DB/MD5Map.cpp ${CMAKE_CURRENT_SOURCE_DIR}/DB/MemberMap.cpp ${CMAKE_CURRENT_SOURCE_DIR}/DB/ImageInfoList.cpp ${CMAKE_CURRENT_SOURCE_DIR}/DB/ImageDB.cpp ${CMAKE_CURRENT_SOURCE_DIR}/DB/FileInfo.cpp ${CMAKE_CURRENT_SOURCE_DIR}/DB/NegationCategoryMatcher.cpp ${CMAKE_CURRENT_SOURCE_DIR}/DB/NewImageFinder.cpp ${CMAKE_CURRENT_SOURCE_DIR}/DB/ImageScout.cpp ${CMAKE_CURRENT_SOURCE_DIR}/DB/NoTagCategoryMatcher.cpp ${CMAKE_CURRENT_SOURCE_DIR}/DB/GroupCounter.cpp ${CMAKE_CURRENT_SOURCE_DIR}/DB/CategoryMatcher.cpp ${CMAKE_CURRENT_SOURCE_DIR}/DB/ImageSearchInfo.cpp ${CMAKE_CURRENT_SOURCE_DIR}/DB/CategoryItem.cpp ${CMAKE_CURRENT_SOURCE_DIR}/DB/ContainerCategoryMatcher.cpp ${CMAKE_CURRENT_SOURCE_DIR}/DB/ValueCategoryMatcher.cpp ${CMAKE_CURRENT_SOURCE_DIR}/DB/OrCategoryMatcher.cpp ${CMAKE_CURRENT_SOURCE_DIR}/DB/AndCategoryMatcher.cpp ${CMAKE_CURRENT_SOURCE_DIR}/DB/FastDir.cpp ${CMAKE_CURRENT_SOURCE_DIR}/DB/OptimizedFileList.cpp ${CMAKE_CURRENT_SOURCE_DIR}/DB/FileName.cpp ${CMAKE_CURRENT_SOURCE_DIR}/DB/FileNameList.cpp ${CMAKE_CURRENT_SOURCE_DIR}/DB/Logging.cpp ${CMAKE_CURRENT_SOURCE_DIR}/DB/CategoryPtr.cpp ${CMAKE_CURRENT_SOURCE_DIR}/DB/ExifMode.cpp ${CMAKE_CURRENT_SOURCE_DIR}/DB/ImageDateCollection.cpp ${CMAKE_CURRENT_SOURCE_DIR}/DB/ImageInfoPtr.cpp ${CMAKE_CURRENT_SOURCE_DIR}/DB/MD5.cpp ${CMAKE_CURRENT_SOURCE_DIR}/DB/MediaCount.cpp ${CMAKE_CURRENT_SOURCE_DIR}/DB/RawId.cpp ${CMAKE_CURRENT_SOURCE_DIR}/DB/SimpleCategoryMatcher.cpp ${CMAKE_CURRENT_SOURCE_DIR}/DB/UIDelegate.cpp ) set(libImportExport_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/ImportExport/documentation.h ${CMAKE_CURRENT_SOURCE_DIR}/ImportExport/Export.cpp ${CMAKE_CURRENT_SOURCE_DIR}/ImportExport/Import.cpp ${CMAKE_CURRENT_SOURCE_DIR}/ImportExport/ImportMatcher.cpp ${CMAKE_CURRENT_SOURCE_DIR}/ImportExport/XMLHandler.cpp ${CMAKE_CURRENT_SOURCE_DIR}/ImportExport/MiniViewer.cpp ${CMAKE_CURRENT_SOURCE_DIR}/ImportExport/ImportHandler.cpp ${CMAKE_CURRENT_SOURCE_DIR}/ImportExport/ImageRow.cpp ${CMAKE_CURRENT_SOURCE_DIR}/ImportExport/ImportDialog.cpp ${CMAKE_CURRENT_SOURCE_DIR}/ImportExport/ImportSettings.cpp ${CMAKE_CURRENT_SOURCE_DIR}/ImportExport/KimFileReader.cpp ${CMAKE_CURRENT_SOURCE_DIR}/ImportExport/MD5CheckPage.cpp ${CMAKE_CURRENT_SOURCE_DIR}/ImportExport/Logging.cpp ) set(libAnnotationDialog_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/AnnotationDialog/documentation.h ${CMAKE_CURRENT_SOURCE_DIR}/AnnotationDialog/Dialog.cpp ${CMAKE_CURRENT_SOURCE_DIR}/AnnotationDialog/ListSelect.cpp ${CMAKE_CURRENT_SOURCE_DIR}/AnnotationDialog/ImagePreview.cpp ${CMAKE_CURRENT_SOURCE_DIR}/AnnotationDialog/ImagePreviewWidget.cpp ${CMAKE_CURRENT_SOURCE_DIR}/AnnotationDialog/DateEdit.cpp ${CMAKE_CURRENT_SOURCE_DIR}/AnnotationDialog/CompletableLineEdit.cpp ${CMAKE_CURRENT_SOURCE_DIR}/AnnotationDialog/ListViewItemHider.cpp ${CMAKE_CURRENT_SOURCE_DIR}/AnnotationDialog/ShowSelectionOnlyManager.cpp ${CMAKE_CURRENT_SOURCE_DIR}/AnnotationDialog/ShortCutManager.cpp ${CMAKE_CURRENT_SOURCE_DIR}/AnnotationDialog/ResizableFrame.cpp ${CMAKE_CURRENT_SOURCE_DIR}/AnnotationDialog/DescriptionEdit.cpp ${CMAKE_CURRENT_SOURCE_DIR}/AnnotationDialog/AreaTagSelectDialog.cpp ${CMAKE_CURRENT_SOURCE_DIR}/AnnotationDialog/Logging.cpp ${CMAKE_CURRENT_SOURCE_DIR}/AnnotationDialog/enums.cpp ) set(libBrowser_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/Browser/documentation.h ${CMAKE_CURRENT_SOURCE_DIR}/Browser/BrowserWidget.cpp ${CMAKE_CURRENT_SOURCE_DIR}/Browser/BrowserPage.cpp ${CMAKE_CURRENT_SOURCE_DIR}/Browser/OverviewPage.cpp ${CMAKE_CURRENT_SOURCE_DIR}/Browser/CategoryPage.cpp ${CMAKE_CURRENT_SOURCE_DIR}/Browser/ImageViewPage.cpp ${CMAKE_CURRENT_SOURCE_DIR}/Browser/TreeFilter.cpp ${CMAKE_CURRENT_SOURCE_DIR}/Browser/Breadcrumb.cpp ${CMAKE_CURRENT_SOURCE_DIR}/Browser/BreadcrumbList.cpp ${CMAKE_CURRENT_SOURCE_DIR}/Browser/AbstractCategoryModel.cpp ${CMAKE_CURRENT_SOURCE_DIR}/Browser/FlatCategoryModel.cpp ${CMAKE_CURRENT_SOURCE_DIR}/Browser/TreeCategoryModel.cpp ${CMAKE_CURRENT_SOURCE_DIR}/Browser/CenteringIconView.cpp ${CMAKE_CURRENT_SOURCE_DIR}/Browser/Logging.cpp ${CMAKE_CURRENT_SOURCE_DIR}/Browser/enums.cpp ) set(libExif_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/Exif/documentation.h ${CMAKE_CURRENT_SOURCE_DIR}/Exif/Database.cpp ${CMAKE_CURRENT_SOURCE_DIR}/Exif/InfoDialog.cpp ${CMAKE_CURRENT_SOURCE_DIR}/Exif/SearchDialog.cpp ${CMAKE_CURRENT_SOURCE_DIR}/Exif/SearchInfo.cpp ${CMAKE_CURRENT_SOURCE_DIR}/Exif/TreeView.cpp ${CMAKE_CURRENT_SOURCE_DIR}/Exif/Info.cpp ${CMAKE_CURRENT_SOURCE_DIR}/Exif/RangeWidget.cpp ${CMAKE_CURRENT_SOURCE_DIR}/Exif/DatabaseElement.cpp ${CMAKE_CURRENT_SOURCE_DIR}/Exif/ReReadDialog.cpp ${CMAKE_CURRENT_SOURCE_DIR}/Exif/Grid.cpp ${CMAKE_CURRENT_SOURCE_DIR}/Exif/Logging.cpp ${CMAKE_CURRENT_SOURCE_DIR}/Exif/SearchDialogSettings.cpp ${CMAKE_CURRENT_SOURCE_DIR}/Settings/ExifPage.cpp ) set(libBackgroundTaskManager_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/BackgroundTaskManager/JobInterface.cpp ${CMAKE_CURRENT_SOURCE_DIR}/BackgroundTaskManager/JobManager.cpp ${CMAKE_CURRENT_SOURCE_DIR}/BackgroundTaskManager/StatusIndicator.cpp ${CMAKE_CURRENT_SOURCE_DIR}/BackgroundTaskManager/JobViewer.cpp ${CMAKE_CURRENT_SOURCE_DIR}/BackgroundTaskManager/JobModel.cpp ${CMAKE_CURRENT_SOURCE_DIR}/BackgroundTaskManager/JobInfo.cpp ${CMAKE_CURRENT_SOURCE_DIR}/BackgroundTaskManager/CompletedJobInfo.cpp ${CMAKE_CURRENT_SOURCE_DIR}/BackgroundTaskManager/Priority.cpp ${CMAKE_CURRENT_SOURCE_DIR}/BackgroundTaskManager/PriorityQueue.cpp ${CMAKE_CURRENT_SOURCE_DIR}/BackgroundTaskManager/Logging.cpp ) set(libBackgroundJobs_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/BackgroundJobs/SearchForVideosWithoutLengthInfo.cpp ${CMAKE_CURRENT_SOURCE_DIR}/BackgroundJobs/ReadVideoLengthJob.cpp ${CMAKE_CURRENT_SOURCE_DIR}/BackgroundJobs/SearchForVideosWithoutVideoThumbnailsJob.cpp ${CMAKE_CURRENT_SOURCE_DIR}/BackgroundJobs/HandleVideoThumbnailRequestJob.cpp ${CMAKE_CURRENT_SOURCE_DIR}/BackgroundJobs/ExtractOneThumbnailJob.cpp ) set(libRemoteControl_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/RemoteControl/RemoteCommand.cpp ${CMAKE_CURRENT_SOURCE_DIR}/RemoteControl/RemoteConnection.cpp ${CMAKE_CURRENT_SOURCE_DIR}/RemoteControl/Server.cpp ${CMAKE_CURRENT_SOURCE_DIR}/RemoteControl/RemoteInterface.cpp ${CMAKE_CURRENT_SOURCE_DIR}/RemoteControl/SearchInfo.cpp ${CMAKE_CURRENT_SOURCE_DIR}/RemoteControl/RemoteImageRequest.cpp ${CMAKE_CURRENT_SOURCE_DIR}/RemoteControl/ImageNameStore.cpp ${CMAKE_CURRENT_SOURCE_DIR}/RemoteControl/ConnectionIndicator.cpp ${CMAKE_CURRENT_SOURCE_DIR}/RemoteControl/Logging.cpp ) set(libMap_SRCS) if(KF5KGeoMap_FOUND) set(libMap_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/Browser/GeoPositionPage.cpp ${CMAKE_CURRENT_SOURCE_DIR}/Browser/PositionBrowserWidget.cpp ${CMAKE_CURRENT_SOURCE_DIR}/Map/MapView.cpp ${CMAKE_CURRENT_SOURCE_DIR}/Map/MapMarkerModelHelper.cpp ${CMAKE_CURRENT_SOURCE_DIR}/Map/SearchMarkerTiler.cpp ${CMAKE_CURRENT_SOURCE_DIR}/Map/Logging.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/Map/GeoCoordinates.cpp ) endif() add_subdirectory(images) add_subdirectory(icons) add_subdirectory(demo) add_subdirectory(themes) add_subdirectory(scripts) add_subdirectory(doc) ########### next target ############### set(kphotoalbum_SRCS main.cpp ${libdatebar_SRCS} ${libSettings_SRCS} ${libsurvey_SRCS} ${libxmldb_SRCS} ${libThumbnailView_SRCS} ${libPlugins_SRCS} ${libViewer_SRCS} ${libCategoryListView_SRCS} ${libHTMLGenerator_SRCS} ${libMainWindow_SRCS} ${libImageManager_SRCS} ${libDB_SRCS} ${libImportExport_SRCS} ${libAnnotationDialog_SRCS} ${libExif_SRCS} ${libBrowser_SRCS} ${libBackgroundTaskManager_SRCS} ${libBackgroundJobs_SRCS} ${libRemoteControl_SRCS} ${libMap_SRCS} ${libUtilities_SRCS} # add doxygen headers so that they get visibiltiy in IDEs: ${CMAKE_CURRENT_SOURCE_DIR}/documentation/coding-standards.h ${CMAKE_CURRENT_SOURCE_DIR}/documentation/mainpage.h ${CMAKE_CURRENT_SOURCE_DIR}/documentation/phrase-book.h ${CMAKE_CURRENT_SOURCE_DIR}/documentation/videothumbnails.h ) add_executable(kphotoalbum ${kphotoalbum_SRCS}) add_dependencies(kphotoalbum UpdateVersion) # External components target_link_libraries(kphotoalbum ${JPEG_LIBRARY} ${EXIV2_LIBRARIES} KF5::Archive KF5::Completion KF5::ConfigCore KF5::ConfigGui KF5::CoreAddons KF5::I18n KF5::IconThemes KF5::JobWidgets KF5::KIOCore KF5::KIOWidgets KF5::TextWidgets KF5::XmlGui KF5::WidgetsAddons Phonon::phonon4qt5 Qt5::Network Qt5::Sql ) if(KF5Kipi_FOUND) target_link_libraries(kphotoalbum KF5::Kipi) endif() if(KF5Purpose_FOUND) target_link_libraries(kphotoalbum KF5::Purpose KF5::PurposeWidgets) endif() if(KF5KDcraw_FOUND) target_link_libraries(kphotoalbum KF5::KDcraw) endif() if(KF5KGeoMap_FOUND) target_link_libraries(kphotoalbum KF5::KGeoMap ) endif() install(TARGETS kphotoalbum ${KDE_INSTALL_TARGETS_DEFAULT_ARGS}) ########### install files ############### install(PROGRAMS org.kde.kphotoalbum.desktop org.kde.kphotoalbum-import.desktop DESTINATION ${KDE_INSTALL_APPDIR}) install(FILES kphotoalbumrc DESTINATION ${KDE_INSTALL_CONFDIR}) install(FILES tips default-setup DESTINATION ${KDE_INSTALL_DATADIR}/kphotoalbum) install(FILES kphotoalbumui.rc DESTINATION ${KDE_INSTALL_KXMLGUI5DIR}/kphotoalbum) install(FILES org.kde.kphotoalbum.appdata.xml DESTINATION ${KDE_INSTALL_METAINFODIR}) configure_file(config-kpa-kdcraw.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config-kpa-kdcraw.h) configure_file(config-kpa-kipi.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config-kpa-kipi.h) configure_file(config-kpa-kgeomap.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config-kpa-kgeomap.h) feature_summary(WHAT ALL FATAL_ON_MISSING_REQUIRED_PACKAGES) # vi:expandtab:tabstop=4 shiftwidth=4: diff --git a/DB/ImageInfo.cpp b/DB/ImageInfo.cpp index a69ea050..d737b4fa 100644 --- a/DB/ImageInfo.cpp +++ b/DB/ImageInfo.cpp @@ -1,799 +1,799 @@ /* Copyright (C) 2003-2019 The KPhotoAlbum Development Team This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; see the file COPYING. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "ImageInfo.h" #include "CategoryCollection.h" #include "FileInfo.h" #include "ImageDB.h" #include "Logging.h" #include "MemberMap.h" #include #include #include #include #include #include #include #include using namespace DB; ImageInfo::ImageInfo() : m_null(true) , m_rating(-1) , m_stackId(0) , m_stackOrder(0) , m_videoLength(-1) , m_isMatched(false) , m_matchGeneration(-1) , m_locked(false) , m_dirty(false) { } ImageInfo::ImageInfo(const DB::FileName &fileName, MediaType type, bool readExifInfo, bool storeExifInfo) : m_imageOnDisk(YesOnDisk) , m_null(false) , m_size(-1, -1) , m_type(type) , m_rating(-1) , m_stackId(0) , m_stackOrder(0) , m_videoLength(-1) , m_isMatched(false) , m_matchGeneration(-1) , m_locked(false) { QFileInfo fi(fileName.absolute()); m_label = fi.completeBaseName(); m_angle = 0; setFileName(fileName); // Read Exif information if (readExifInfo) { ExifMode mode = EXIFMODE_INIT; if (!storeExifInfo) mode &= ~EXIFMODE_DATABASE_UPDATE; readExif(fileName, mode); } m_dirty = false; } ImageInfo::ImageInfo(const ImageInfo &other) { *this = other; } void ImageInfo::setIsMatched(bool isMatched) { m_isMatched = isMatched; } bool ImageInfo::isMatched() const { return m_isMatched; } void ImageInfo::setMatchGeneration(int matchGeneration) { m_matchGeneration = matchGeneration; } int ImageInfo::matchGeneration() const { return m_matchGeneration; } void ImageInfo::setLabel(const QString &desc) { if (desc != m_label) m_dirty = true; m_label = desc; } QString ImageInfo::label() const { return m_label; } void ImageInfo::setDescription(const QString &desc) { if (desc != m_description) m_dirty = true; m_description = desc.trimmed(); } QString ImageInfo::description() const { return m_description; } void ImageInfo::setCategoryInfo(const QString &key, const StringSet &value) { // Don't check if really changed, because it's too slow. m_dirty = true; m_categoryInfomation[key] = value; } bool ImageInfo::hasCategoryInfo(const QString &key, const QString &value) const { return m_categoryInfomation[key].contains(value); } bool DB::ImageInfo::hasCategoryInfo(const QString &key, const StringSet &values) const { return values.intersects(m_categoryInfomation[key]); } StringSet ImageInfo::itemsOfCategory(const QString &key) const { return m_categoryInfomation[key]; } void ImageInfo::renameItem(const QString &category, const QString &oldValue, const QString &newValue) { if (m_taggedAreas.contains(category)) { if (m_taggedAreas[category].contains(oldValue)) { m_taggedAreas[category][newValue] = m_taggedAreas[category][oldValue]; m_taggedAreas[category].remove(oldValue); } } StringSet &set = m_categoryInfomation[category]; StringSet::iterator it = set.find(oldValue); if (it != set.end()) { m_dirty = true; set.erase(it); set.insert(newValue); } } DB::FileName ImageInfo::fileName() const { return m_fileName; } void ImageInfo::setFileName(const DB::FileName &fileName) { if (fileName != m_fileName) m_dirty = true; m_fileName = fileName; m_imageOnDisk = Unchecked; DB::CategoryPtr folderCategory = DB::ImageDB::instance()->categoryCollection()->categoryForSpecial(DB::Category::FolderCategory); if (folderCategory) { DB::MemberMap &map = DB::ImageDB::instance()->memberMap(); createFolderCategoryItem(folderCategory, map); //ImageDB::instance()->setMemberMap( map ); } } void ImageInfo::rotate(int degrees, RotationMode mode) { // ensure positive degrees: degrees += 360; degrees = degrees % 360; if (degrees == 0) return; m_dirty = true; m_angle = (m_angle + degrees) % 360; if (degrees == 90 || degrees == 270) { m_size.transpose(); } // the AnnotationDialog manages this by itself and sets RotateImageInfoOnly: if (mode == RotateImageInfoAndAreas) { for (auto &areasOfCategory : m_taggedAreas) { for (auto &area : areasOfCategory) { QRect rotatedArea; // parameter order for QRect::setCoords: // setCoords( left, top, right, bottom ) // keep in mind that _size is already transposed switch (degrees) { case 90: rotatedArea.setCoords( m_size.width() - area.bottom(), area.left(), m_size.width() - area.top(), area.right()); break; case 180: rotatedArea.setCoords( m_size.width() - area.right(), m_size.height() - area.bottom(), m_size.width() - area.left(), m_size.height() - area.top()); break; case 270: rotatedArea.setCoords( area.top(), m_size.height() - area.right(), area.bottom(), m_size.height() - area.left()); break; default: // degrees==0; "odd" values won't happen. rotatedArea = area; break; } // update _taggedAreas[category][tag]: area = rotatedArea; } } } } int ImageInfo::angle() const { return m_angle; } void ImageInfo::setAngle(int angle) { if (angle != m_angle) m_dirty = true; m_angle = angle; } short ImageInfo::rating() const { return m_rating; } void ImageInfo::setRating(short rating) { Q_ASSERT((rating >= 0 && rating <= 10) || rating == -1); if (rating > 10) rating = 10; if (rating < -1) rating = -1; if (m_rating != rating) m_dirty = true; m_rating = rating; } DB::StackID ImageInfo::stackId() const { return m_stackId; } void ImageInfo::setStackId(const DB::StackID stackId) { if (stackId != m_stackId) m_dirty = true; m_stackId = stackId; } unsigned int ImageInfo::stackOrder() const { return m_stackOrder; } void ImageInfo::setStackOrder(const unsigned int stackOrder) { if (stackOrder != m_stackOrder) m_dirty = true; m_stackOrder = stackOrder; } void ImageInfo::setVideoLength(int length) { if (m_videoLength != length) m_dirty = true; m_videoLength = length; } int ImageInfo::videoLength() const { return m_videoLength; } void ImageInfo::setDate(const ImageDate &date) { if (date != m_date) m_dirty = true; m_date = date; } ImageDate &ImageInfo::date() { return m_date; } ImageDate ImageInfo::date() const { return m_date; } bool ImageInfo::operator!=(const ImageInfo &other) const { return !(*this == other); } bool ImageInfo::operator==(const ImageInfo &other) const { bool changed = (m_fileName != other.m_fileName || m_label != other.m_label || (!m_description.isEmpty() && !other.m_description.isEmpty() && m_description != other.m_description) || // one might be isNull. m_date != other.m_date || m_angle != other.m_angle || m_rating != other.m_rating || (m_stackId != other.m_stackId || !((m_stackId == 0) ? true : (m_stackOrder == other.m_stackOrder)))); if (!changed) { QStringList keys = DB::ImageDB::instance()->categoryCollection()->categoryNames(); for (QStringList::ConstIterator it = keys.constBegin(); it != keys.constEnd(); ++it) changed |= m_categoryInfomation[*it] != other.m_categoryInfomation[*it]; } return !changed; } void ImageInfo::renameCategory(const QString &oldName, const QString &newName) { m_dirty = true; m_categoryInfomation[newName] = m_categoryInfomation[oldName]; m_categoryInfomation.remove(oldName); m_taggedAreas[newName] = m_taggedAreas[oldName]; m_taggedAreas.remove(oldName); } void ImageInfo::setMD5Sum(const MD5 &sum, bool storeEXIF) { if (sum != m_md5sum) { // if we make a QObject derived class out of imageinfo, we might invalidate thumbnails from here // file changed -> reload/invalidate metadata: ExifMode mode = EXIFMODE_ORIENTATION | EXIFMODE_DATABASE_UPDATE; // fuzzy dates are usually set for a reason if (!m_date.isFuzzy()) mode |= EXIFMODE_DATE; // FIXME (ZaJ): the "right" thing to do would be to update the description // - if it is currently empty (done.) // - if it has been set from the exif info and not been changed (TODO) if (m_description.isEmpty()) mode |= EXIFMODE_DESCRIPTION; if (!storeEXIF) mode &= ~EXIFMODE_DATABASE_UPDATE; readExif(fileName(), mode); // FIXME (ZaJ): it *should* make sense to set the ImageDB::md5Map() from here, but I want // to make sure I fully understand everything first... // this could also be done as signal md5Changed(old,new) // image size is invalidated by the thumbnail builder, if needed m_dirty = true; } m_md5sum = sum; } void ImageInfo::setLocked(bool locked) { m_locked = locked; } bool ImageInfo::isLocked() const { return m_locked; } void ImageInfo::readExif(const DB::FileName &fullPath, DB::ExifMode mode) { DB::FileInfo exifInfo = DB::FileInfo::read(fullPath, mode); // Date if (updateDateInformation(mode)) { const ImageDate newDate(exifInfo.dateTime()); setDate(newDate); } // Orientation if ((mode & EXIFMODE_ORIENTATION) && Settings::SettingsData::instance()->useEXIFRotate()) { setAngle(exifInfo.angle()); } // Description if ((mode & EXIFMODE_DESCRIPTION) && Settings::SettingsData::instance()->useEXIFComments()) { bool doSetDescription = true; QString desc = exifInfo.description(); if (Settings::SettingsData::instance()->stripEXIFComments()) { for (const auto &ignoredComment : Settings::SettingsData::instance()->EXIFCommentsToStrip()) { if (desc == ignoredComment) { doSetDescription = false; break; } } } if (doSetDescription) { setDescription(desc); } } // Database update if (mode & EXIFMODE_DATABASE_UPDATE) { Exif::Database::instance()->add(exifInfo); #ifdef HAVE_KGEOMAP // GPS coords might have changed... m_coordsIsSet = false; #endif } } QStringList ImageInfo::availableCategories() const { return m_categoryInfomation.keys(); } QSize ImageInfo::size() const { return m_size; } void ImageInfo::setSize(const QSize &size) { if (size != m_size) m_dirty = true; m_size = size; } bool ImageInfo::imageOnDisk(const DB::FileName &fileName) { return fileName.exists(); } ImageInfo::ImageInfo(const DB::FileName &fileName, const QString &label, const QString &description, const ImageDate &date, int angle, const MD5 &md5sum, const QSize &size, MediaType type, short rating, unsigned int stackId, unsigned int stackOrder) { m_fileName = fileName; m_label = label; m_description = description; m_date = date; m_angle = angle; m_md5sum = md5sum; m_size = size; m_imageOnDisk = Unchecked; m_locked = false; m_null = false; m_type = type; m_dirty = true; if (rating > 10) rating = 10; if (rating < -1) rating = -1; m_rating = rating; m_stackId = stackId; m_stackOrder = stackOrder; m_videoLength = -1; } // Note: we need this operator because the base class QSharedData hides // its copy operator to make exclude the reference counting from being // copied. ImageInfo &ImageInfo::operator=(const ImageInfo &other) { m_fileName = other.m_fileName; m_label = other.m_label; m_description = other.m_description; m_date = other.m_date; m_categoryInfomation = other.m_categoryInfomation; m_taggedAreas = other.m_taggedAreas; m_angle = other.m_angle; m_imageOnDisk = other.m_imageOnDisk; m_md5sum = other.m_md5sum; m_null = other.m_null; m_size = other.m_size; m_type = other.m_type; m_rating = other.m_rating; m_stackId = other.m_stackId; m_stackOrder = other.m_stackOrder; m_videoLength = other.m_videoLength; m_isMatched = other.m_isMatched; m_matchGeneration = other.m_matchGeneration; #ifdef HAVE_KGEOMAP m_coordinates = other.m_coordinates; m_coordsIsSet = other.m_coordsIsSet; #endif m_locked = other.m_locked; m_dirty = other.m_dirty; return *this; } MediaType DB::ImageInfo::mediaType() const { return m_type; } bool ImageInfo::isVideo() const { return m_type == Video; } void DB::ImageInfo::createFolderCategoryItem(DB::CategoryPtr folderCategory, DB::MemberMap &memberMap) { QString folderName = Utilities::relativeFolderName(m_fileName.relative()); if (folderName.isEmpty()) return; if (!memberMap.contains(folderCategory->name(), folderName)) { QStringList directories = folderName.split(QString::fromLatin1("/")); QString curPath; for (QStringList::ConstIterator directoryIt = directories.constBegin(); directoryIt != directories.constEnd(); ++directoryIt) { if (curPath.isEmpty()) curPath = *directoryIt; else { QString oldPath = curPath; curPath = curPath + QString::fromLatin1("/") + *directoryIt; memberMap.addMemberToGroup(folderCategory->name(), oldPath, curPath); } } folderCategory->addItem(folderName); } m_categoryInfomation.insert(folderCategory->name(), StringSet() << folderName); } void DB::ImageInfo::copyExtraData(const DB::ImageInfo &from, bool copyAngle) { m_categoryInfomation = from.m_categoryInfomation; m_description = from.m_description; // Hmm... what should the date be? orig or modified? // _date = from._date; if (copyAngle) m_angle = from.m_angle; m_rating = from.m_rating; } void DB::ImageInfo::removeExtraData() { m_categoryInfomation.clear(); m_description.clear(); m_rating = -1; } void ImageInfo::merge(const ImageInfo &other) { // Merge date if (other.date() != m_date) { // a fuzzy date has been set by the user and therefore "wins" over an exact date. // two fuzzy dates can be merged // two exact dates should ideally be cross-checked with Exif information in the file. // Nevertheless, we merge them into a fuzzy date to avoid the complexity of checking the file. if (other.date().isFuzzy()) { if (m_date.isFuzzy()) m_date.extendTo(other.date()); else m_date = other.date(); } else if (!m_date.isFuzzy()) { m_date.extendTo(other.date()); } // else: keep m_date } // Merge description if (!other.description().isEmpty()) { if (m_description.isEmpty()) m_description = other.description(); else if (m_description != other.description()) m_description += QString::fromUtf8("\n-----------\n") + other.m_description; } // Clear untagged tag if only one of the images was untagged const QString untaggedCategory = Settings::SettingsData::instance()->untaggedCategory(); const QString untaggedTag = Settings::SettingsData::instance()->untaggedTag(); const bool isCompleted = !m_categoryInfomation[untaggedCategory].contains(untaggedTag) || !other.m_categoryInfomation[untaggedCategory].contains(untaggedTag); // Merge tags QSet keys = QSet::fromList(m_categoryInfomation.keys()); keys.unite(QSet::fromList(other.m_categoryInfomation.keys())); for (const QString &key : keys) { m_categoryInfomation[key].unite(other.m_categoryInfomation[key]); } // Clear untagged tag if only one of the images was untagged if (isCompleted) m_categoryInfomation[untaggedCategory].remove(untaggedTag); // merge stacks: if (isStacked() || other.isStacked()) { DB::FileNameList stackImages; if (!isStacked()) stackImages.append(fileName()); else stackImages.append(DB::ImageDB::instance()->getStackFor(fileName())); stackImages.append(DB::ImageDB::instance()->getStackFor(other.fileName())); DB::ImageDB::instance()->unstack(stackImages); if (!DB::ImageDB::instance()->stack(stackImages)) qCWarning(DBLog, "Could not merge stacks!"); } } void DB::ImageInfo::addCategoryInfo(const QString &category, const StringSet &values) { for (StringSet::const_iterator valueIt = values.constBegin(); valueIt != values.constEnd(); ++valueIt) { if (!m_categoryInfomation[category].contains(*valueIt)) { m_dirty = true; m_categoryInfomation[category].insert(*valueIt); } } } void DB::ImageInfo::clearAllCategoryInfo() { m_categoryInfomation.clear(); m_taggedAreas.clear(); } void DB::ImageInfo::removeCategoryInfo(const QString &category, const StringSet &values) { for (StringSet::const_iterator valueIt = values.constBegin(); valueIt != values.constEnd(); ++valueIt) { if (m_categoryInfomation[category].contains(*valueIt)) { m_dirty = true; m_categoryInfomation[category].remove(*valueIt); m_taggedAreas[category].remove(*valueIt); } } } void DB::ImageInfo::addCategoryInfo(const QString &category, const QString &value, const QRect &area) { if (!m_categoryInfomation[category].contains(value)) { m_dirty = true; m_categoryInfomation[category].insert(value); if (area.isValid()) { m_taggedAreas[category][value] = area; } } } void DB::ImageInfo::removeCategoryInfo(const QString &category, const QString &value) { if (m_categoryInfomation[category].contains(value)) { m_dirty = true; m_categoryInfomation[category].remove(value); m_taggedAreas[category].remove(value); } } void DB::ImageInfo::setPositionedTags(const QString &category, const QMap &positionedTags) { m_dirty = true; m_taggedAreas[category] = positionedTags; } bool DB::ImageInfo::updateDateInformation(int mode) const { if ((mode & EXIFMODE_DATE) == 0) return false; if ((mode & EXIFMODE_FORCE) != 0) return true; return true; } QMap> DB::ImageInfo::taggedAreas() const { return m_taggedAreas; } QRect DB::ImageInfo::areaForTag(QString category, QString tag) const { // QMap::value returns a default constructed value if the key is not found: return m_taggedAreas.value(category).value(tag); } #ifdef HAVE_KGEOMAP -KGeoMap::GeoCoordinates DB::ImageInfo::coordinates() const +Map::GeoCoordinates DB::ImageInfo::coordinates() const { if (m_coordsIsSet) { return m_coordinates; } static const int EXIF_GPS_VERSIONID = 0; static const int EXIF_GPS_LATREF = 1; static const int EXIF_GPS_LAT = 2; static const int EXIF_GPS_LONREF = 3; static const int EXIF_GPS_LON = 4; static const int EXIF_GPS_ALTREF = 5; static const int EXIF_GPS_ALT = 6; static const QString S = QString::fromUtf8("S"); static const QString W = QString::fromUtf8("W"); static QList fields; if (fields.isEmpty()) { // the order here matters! we use the named int constants afterwards to refer to them: fields.append(new Exif::IntExifElement("Exif.GPSInfo.GPSVersionID")); // actually a byte value fields.append(new Exif::StringExifElement("Exif.GPSInfo.GPSLatitudeRef")); fields.append(new Exif::RationalExifElement("Exif.GPSInfo.GPSLatitude")); fields.append(new Exif::StringExifElement("Exif.GPSInfo.GPSLongitudeRef")); fields.append(new Exif::RationalExifElement("Exif.GPSInfo.GPSLongitude")); fields.append(new Exif::IntExifElement("Exif.GPSInfo.GPSAltitudeRef")); // actually a byte value fields.append(new Exif::RationalExifElement("Exif.GPSInfo.GPSAltitude")); } // read field values from database: bool foundIt = Exif::Database::instance()->readFields(m_fileName, fields); // if the Database query result doesn't contain exif GPS info (-> upgraded exifdb from DBVersion < 2), it is null // if the result is int 0, then there's no exif gps information in the image // otherwise we can proceed to parse the information if (foundIt && fields[EXIF_GPS_VERSIONID]->value().isNull()) { // update exif DB and repeat the search: Exif::Database::instance()->remove(fileName()); Exif::Database::instance()->add(fileName()); Exif::Database::instance()->readFields(m_fileName, fields); Q_ASSERT(!fields[EXIF_GPS_VERSIONID]->value().isNull()); } - KGeoMap::GeoCoordinates coords; + Map::GeoCoordinates coords; // gps info set? // don't use the versionid field here, because some cameras use 0 as its value if (foundIt && fields[EXIF_GPS_LAT]->value().toInt() != -1.0 && fields[EXIF_GPS_LON]->value().toInt() != -1.0) { // lat/lon/alt reference determines sign of float: double latr = (fields[EXIF_GPS_LATREF]->value().toString() == S) ? -1.0 : 1.0; double lat = fields[EXIF_GPS_LAT]->value().toFloat(); double lonr = (fields[EXIF_GPS_LONREF]->value().toString() == W) ? -1.0 : 1.0; double lon = fields[EXIF_GPS_LON]->value().toFloat(); double altr = (fields[EXIF_GPS_ALTREF]->value().toInt() == 1) ? -1.0 : 1.0; double alt = fields[EXIF_GPS_ALT]->value().toFloat(); if (lat != -1.0 && lon != -1.0) { coords.setLatLon(latr * lat, lonr * lon); if (alt != 0.0f) { coords.setAlt(altr * alt); } } } m_coordinates = coords; m_coordsIsSet = true; return m_coordinates; } #endif // vi:expandtab:tabstop=4 shiftwidth=4: diff --git a/DB/ImageInfo.h b/DB/ImageInfo.h index 37af49ab..74e63b61 100644 --- a/DB/ImageInfo.h +++ b/DB/ImageInfo.h @@ -1,247 +1,247 @@ /* Copyright (C) 2003-2019 The KPhotoAlbum Development Team This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; see the file COPYING. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef IMAGEINFO_H #define IMAGEINFO_H #include "config-kpa-kgeomap.h" #include "CategoryPtr.h" #include "ExifMode.h" #include "FileName.h" #include "ImageDate.h" #include "MD5.h" #include #include #include #include #include #include #ifdef HAVE_KGEOMAP -#include +#include "Map/GeoCoordinates.h" #endif namespace Plugins { class ImageInfo; } namespace XMLDB { class Database; } namespace DB { enum PathType { RelativeToImageRoot, AbsolutePath }; enum RotationMode { RotateImageInfoAndAreas, RotateImageInfoOnly }; using Utilities::StringSet; class MemberMap; enum MediaType { Image = 0x01, Video = 0x02 }; const MediaType anyMediaType = MediaType(Image | Video); typedef unsigned int StackID; class ImageInfo : public QSharedData { public: ImageInfo(); explicit ImageInfo(const DB::FileName &fileName, MediaType type = Image, bool readExifInfo = true, bool storeExifInfo = true); ImageInfo(const DB::FileName &fileName, const QString &label, const QString &description, const ImageDate &date, int angle, const MD5 &md5sum, const QSize &size, MediaType type, short rating = -1, StackID stackId = 0, unsigned int stackOrder = 0); ImageInfo(const ImageInfo &other); FileName fileName() const; void setFileName(const DB::FileName &relativeFileName); void setLabel(const QString &); QString label() const; void setDescription(const QString &); QString description() const; void setDate(const ImageDate &); ImageDate date() const; ImageDate &date(); void readExif(const DB::FileName &fullPath, DB::ExifMode mode); void rotate(int degrees, RotationMode mode = RotateImageInfoAndAreas); int angle() const; void setAngle(int angle); short rating() const; void setRating(short rating); bool isStacked() const { return m_stackId != 0; } StackID stackId() const; unsigned int stackOrder() const; void setStackOrder(const unsigned int stackOrder); void setVideoLength(int seconds); int videoLength() const; void setCategoryInfo(const QString &key, const StringSet &value); void addCategoryInfo(const QString &category, const StringSet &values); /** * Enable a tag within a category for this image. * Optionally, the tag's position can be given (for positionable categories). * @param category the category name * @param value the tag name * @param area the image region that the tag applies to. */ void addCategoryInfo(const QString &category, const QString &value, const QRect &area = QRect()); void clearAllCategoryInfo(); void removeCategoryInfo(const QString &category, const StringSet &values); void removeCategoryInfo(const QString &category, const QString &value); /** * Set the tagged areas for the image. * It is assumed that the positioned tags have already been set to the ImageInfo * using one of the functions setCategoryInfo or addCategoryInfo. * * @param category the category name. * @param positionedTags a mapping of tag names to image areas. */ void setPositionedTags(const QString &category, const QMap &positionedTags); bool hasCategoryInfo(const QString &key, const QString &value) const; bool hasCategoryInfo(const QString &key, const StringSet &values) const; QStringList availableCategories() const; StringSet itemsOfCategory(const QString &category) const; void renameItem(const QString &key, const QString &oldValue, const QString &newValue); void renameCategory(const QString &oldName, const QString &newName); bool operator!=(const ImageInfo &other) const; bool operator==(const ImageInfo &other) const; ImageInfo &operator=(const ImageInfo &other); static bool imageOnDisk(const DB::FileName &fileName); const MD5 &MD5Sum() const { return m_md5sum; } void setMD5Sum(const MD5 &sum, bool storeEXIF = true); void setLocked(bool); bool isLocked() const; bool isNull() const { return m_null; } QSize size() const; void setSize(const QSize &size); MediaType mediaType() const; void setMediaType(MediaType type) { if (type != m_type) m_dirty = true; m_type = type; } bool isVideo() const; void createFolderCategoryItem(DB::CategoryPtr, DB::MemberMap &memberMap); void copyExtraData(const ImageInfo &from, bool copyAngle = true); void removeExtraData(); /** * Merge another ImageInfo into this one. * The other ImageInfo is not altered in any way or removed. */ void merge(const ImageInfo &other); QMap> taggedAreas() const; /** * Return the area associated with a tag. * @param category the category name * @param tag the tag name * @return the associated area, or QRect() if no association exists. */ QRect areaForTag(QString category, QString tag) const; void setIsMatched(bool isMatched); bool isMatched() const; void setMatchGeneration(int matchGeneration); int matchGeneration() const; #ifdef HAVE_KGEOMAP - KGeoMap::GeoCoordinates coordinates() const; + Map::GeoCoordinates coordinates() const; #endif protected: void setIsNull(bool b) { m_null = b; } bool isDirty() const { return m_dirty; } void setIsDirty(bool b) { m_dirty = b; } bool updateDateInformation(int mode) const; void setStackId(const StackID stackId); friend class XMLDB::Database; private: DB::FileName m_fileName; QString m_label; QString m_description; ImageDate m_date; QMap m_categoryInfomation; QMap> m_taggedAreas; int m_angle; enum OnDisk { YesOnDisk, NoNotOnDisk, Unchecked }; mutable OnDisk m_imageOnDisk; MD5 m_md5sum; bool m_null; QSize m_size; MediaType m_type; short m_rating; StackID m_stackId; unsigned int m_stackOrder; int m_videoLength; bool m_isMatched; int m_matchGeneration; #ifdef HAVE_KGEOMAP - mutable KGeoMap::GeoCoordinates m_coordinates; + mutable Map::GeoCoordinates m_coordinates; mutable bool m_coordsIsSet = false; #endif // Cache information bool m_locked; // Will be set to true after every change bool m_dirty; }; } #endif /* IMAGEINFO_H */ // vi:expandtab:tabstop=4 shiftwidth=4: diff --git a/Map/GeoCoordinates.cpp b/Map/GeoCoordinates.cpp new file mode 100644 index 00000000..721be0f7 --- /dev/null +++ b/Map/GeoCoordinates.cpp @@ -0,0 +1,71 @@ +/* Copyright (C) 2018 Tobias Leupold + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of + the License or (at your option) version 3 or any later version + accepted by the membership of KDE e.V. (or its successor approved + by the membership of KDE e.V.), which shall act as a proxy + defined in Section 14 of version 3 of the license. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "GeoCoordinates.h" + +bool Map::GeoCoordinates::hasCoordinates() +{ + return m_kgeomapCoordinates.hasCoordinates(); +} + +double Map::GeoCoordinates::lon() +{ + return m_kgeomapCoordinates.lon(); +} + +double Map::GeoCoordinates::lat() +{ + return m_kgeomapCoordinates.lat(); +} + +double Map::GeoCoordinates::alt() +{ + return m_kgeomapCoordinates.alt(); +} + +bool Map::GeoCoordinates::hasAltitude() +{ + return m_kgeomapCoordinates.hasAltitude(); +} + +KGeoMap::GeoCoordinates Map::GeoCoordinates::kgeomapCoordinates() const +{ + return m_kgeomapCoordinates; +} + +void Map::GeoCoordinates::setLatLon(double lat, double lon) +{ + m_kgeomapCoordinates.setLatLon(lat, lon); +} + +void Map::GeoCoordinates::setAlt(double alt) +{ + m_kgeomapCoordinates.setAlt(alt); +} + +Map::GeoCoordinates::Pair Map::GeoCoordinates::makePair(const qreal lat1, const qreal lon1, const qreal lat2, const qreal lon2) +{ + Map::GeoCoordinates coordinates1; + coordinates1.setLatLon(lat1, lon1); + Map::GeoCoordinates coordinates2; + coordinates2.setLatLon(lat2, lon2); + return Pair(coordinates1, coordinates2); +} + +// vi:expandtab:tabstop=4 shiftwidth=4: diff --git a/Map/GeoCoordinates.h b/Map/GeoCoordinates.h new file mode 100644 index 00000000..dcf396bd --- /dev/null +++ b/Map/GeoCoordinates.h @@ -0,0 +1,55 @@ +/* Copyright (C) 2018 Tobias Leupold + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of + the License or (at your option) version 3 or any later version + accepted by the membership of KDE e.V. (or its successor approved + by the membership of KDE e.V.), which shall act as a proxy + defined in Section 14 of version 3 of the license. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#ifndef GEOCOORDINATES_H +#define GEOCOORDINATES_H + +// Libkgeomap includes +#include + +namespace Map +{ + +class GeoCoordinates +{ + +public: + bool hasCoordinates(); + qreal lon(); + qreal lat(); + qreal alt(); + bool hasAltitude(); + KGeoMap::GeoCoordinates kgeomapCoordinates() const; + void setLatLon(qreal lat, qreal lon); + void setAlt(qreal alt); + typedef QPair Pair; + static Pair makePair(const qreal lat1, const qreal lon1, const qreal lat2, const qreal lon2); + +private: // Variables + KGeoMap::GeoCoordinates m_kgeomapCoordinates; + +}; + +} + +Q_DECLARE_METATYPE(Map::GeoCoordinates::Pair) + +#endif // GEOCOORDINATES_H + +// vi:expandtab:tabstop=4 shiftwidth=4: diff --git a/Map/MapMarkerModelHelper.cpp b/Map/MapMarkerModelHelper.cpp index 4696083d..9ce12beb 100644 --- a/Map/MapMarkerModelHelper.cpp +++ b/Map/MapMarkerModelHelper.cpp @@ -1,131 +1,131 @@ -/* Copyright (C) 2014 Johannes Zarl +/* Copyright (C) 2014-2018 Johannes Zarl-Zierl This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; see the file COPYING. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "MapMarkerModelHelper.h" #include "Logging.h" // Qt includes #include #include #include // Local includes #include const int FileNameRole = Qt::UserRole + 1; Map::MapMarkerModelHelper::MapMarkerModelHelper() : m_itemModel(0) , m_itemSelectionModel(0) { m_itemModel = new QStandardItemModel(1, 1); m_itemSelectionModel = new QItemSelectionModel(m_itemModel); connect(m_itemModel, SIGNAL(dataChanged(QModelIndex, QModelIndex)), this, SLOT(slotDataChanged(QModelIndex, QModelIndex))); } Map::MapMarkerModelHelper::~MapMarkerModelHelper() { delete m_itemSelectionModel; delete m_itemModel; } void Map::MapMarkerModelHelper::clearItems() { m_itemModel->clear(); } void Map::MapMarkerModelHelper::addImage(const DB::ImageInfo &image) { qCDebug(MapLog) << "Adding marker for image " << image.label(); QStandardItem *const newItem = new QStandardItem(image.label()); newItem->setToolTip(image.label()); newItem->setData(QVariant::fromValue(image.fileName()), FileNameRole); m_itemModel->appendRow(newItem); } void Map::MapMarkerModelHelper::addImage(const DB::ImageInfoPtr image) { addImage(*image); } void Map::MapMarkerModelHelper::slotDataChanged(const QModelIndex &, const QModelIndex &) { emit(signalModelChangedDrastically()); } bool Map::MapMarkerModelHelper::itemCoordinates(const QModelIndex &index, KGeoMap::GeoCoordinates *const coordinates) const { if (!index.data(FileNameRole).canConvert()) { return false; } if (coordinates) { const DB::FileName filename = index.data(FileNameRole).value(); - *coordinates = filename.info()->coordinates(); + *coordinates = filename.info()->coordinates().kgeomapCoordinates(); } return true; } QAbstractItemModel *Map::MapMarkerModelHelper::model() const { return m_itemModel; } QItemSelectionModel *Map::MapMarkerModelHelper::selectionModel() const { return m_itemSelectionModel; } KGeoMap::ModelHelper::Flags Map::MapMarkerModelHelper::modelFlags() const { return FlagVisible; } KGeoMap::ModelHelper::Flags Map::MapMarkerModelHelper::itemFlags(const QModelIndex &index) const { if (!index.data(FileNameRole).canConvert()) { return FlagNull; } return FlagVisible; } // FIXME: for some reason, itemIcon is never called -> no thumbnails bool Map::MapMarkerModelHelper::itemIcon(const QModelIndex &index, QPoint *const offset, QSize *const, QPixmap *const pixmap, QUrl *const) const { if (!index.data(FileNameRole).canConvert()) { return false; } const DB::FileName filename = index.data(FileNameRole).value(); *pixmap = ImageManager::ThumbnailCache::instance()->lookup(filename); *offset = QPoint(pixmap->width() / 2, pixmap->height() / 2); qCDebug(MapLog) << "Map icon for " << filename.relative() << (pixmap->isNull() ? " missing." : " found."); return !pixmap->isNull(); } // vi:expandtab:tabstop=4 shiftwidth=4: diff --git a/Map/MapView.cpp b/Map/MapView.cpp index b0ac2e07..1824e049 100644 --- a/Map/MapView.cpp +++ b/Map/MapView.cpp @@ -1,239 +1,242 @@ -/* Copyright (C) 2014-2015 Tobias Leupold +/* Copyright (C) 2014-2018 Tobias Leupold This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; see the file COPYING. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "MapView.h" #include "Logging.h" // Qt includes #include #include #include #include #include // KDE includes #include #include #include #include #include // Libkgeomap includes #include // Local includes #include "MapMarkerModelHelper.h" #include "SearchMarkerTiler.h" Map::MapView::MapView(QWidget *parent, UsageType type) : QWidget(parent) { if (type == MapViewWindow) { setWindowFlags(Qt::Window); setAttribute(Qt::WA_DeleteOnClose); } QVBoxLayout *layout = new QVBoxLayout(this); m_statusLabel = new QLabel; m_statusLabel->setAlignment(Qt::AlignCenter); m_statusLabel->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); m_statusLabel->hide(); layout->addWidget(m_statusLabel); m_mapWidget = new KGeoMap::MapWidget(this); layout->addWidget(m_mapWidget); QWidget *controlWidget = m_mapWidget->getControlWidget(); controlWidget->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Maximum); layout->addWidget(controlWidget); m_mapWidget->setActive(true); QPushButton *saveButton = new QPushButton; saveButton->setIcon(QPixmap(SmallIcon(QString::fromUtf8("media-floppy")))); saveButton->setToolTip(i18n("Save the current map settings")); m_mapWidget->addWidgetToControlWidget(saveButton); connect(saveButton, &QPushButton::clicked, this, &MapView::saveSettings); m_setLastCenterButton = new QPushButton; m_setLastCenterButton->setIcon(QPixmap(SmallIcon(QString::fromUtf8("go-first")))); m_setLastCenterButton->setToolTip(i18n("Go to last map position")); m_mapWidget->addWidgetToControlWidget(m_setLastCenterButton); connect(m_setLastCenterButton, &QPushButton::clicked, this, &MapView::setLastCenter); // We first try set the default backend "marble" or the first one available ... const QString defaultBackend = QString::fromUtf8("marble"); auto backends = m_mapWidget->availableBackends(); if (backends.contains(defaultBackend)) { m_mapWidget->setBackend(defaultBackend); } else { qCDebug(MapLog) << "AnnotationMap: using backend " << backends[0]; m_mapWidget->setBackend(backends[0]); } // ... then we try to set the (probably) saved settings KConfigGroup configGroup = KSharedConfig::openConfig()->group(QString::fromUtf8("MapView")); m_mapWidget->readSettingsFromGroup(&configGroup); // Add the item model for the coordinates display m_modelHelper = new MapMarkerModelHelper(); m_itemMarkerTiler = new SearchMarkerTiler(m_modelHelper, this); m_mapWidget->setGroupedModel(m_itemMarkerTiler); connect(m_mapWidget, &KGeoMap::MapWidget::signalRegionSelectionChanged, this, &MapView::signalRegionSelectionChanged); } Map::MapView::~MapView() { delete m_modelHelper; delete m_itemMarkerTiler; } void Map::MapView::clear() { m_modelHelper->clearItems(); } void Map::MapView::addImage(const DB::ImageInfo &image) { m_modelHelper->addImage(image); } void Map::MapView::addImage(const DB::ImageInfoPtr image) { m_modelHelper->addImage(image); } void Map::MapView::zoomToMarkers() { if (m_modelHelper->model()->rowCount() > 0) { m_mapWidget->adjustBoundariesToGroupedMarkers(); } - m_lastCenter = m_mapWidget->getCenter(); + KGeoMap::GeoCoordinates kgeomapCenter = m_mapWidget->getCenter(); + Map::GeoCoordinates center; + center.setLatLon(kgeomapCenter.lat(), kgeomapCenter.lon()); + m_lastCenter = center; } void Map::MapView::setCenter(const DB::ImageInfo &image) { m_lastCenter = image.coordinates(); - m_mapWidget->setCenter(m_lastCenter); + m_mapWidget->setCenter(m_lastCenter.kgeomapCoordinates()); } void Map::MapView::setCenter(const DB::ImageInfoPtr image) { m_lastCenter = image->coordinates(); - m_mapWidget->setCenter(m_lastCenter); + m_mapWidget->setCenter(m_lastCenter.kgeomapCoordinates()); } void Map::MapView::saveSettings() { KSharedConfigPtr config = KSharedConfig::openConfig(); KConfigGroup configGroup = config->group(QString::fromUtf8("MapView")); m_mapWidget->saveSettingsToGroup(&configGroup); config->sync(); KMessageBox::information(this, i18n("Settings saved"), i18n("Map view")); } void Map::MapView::setShowThumbnails(bool state) { m_mapWidget->setShowThumbnails(state); } void Map::MapView::displayStatus(MapStatus status) { switch (status) { case MapStatus::Loading: m_statusLabel->setText(i18n("Loading coordinates from the images ...")); m_statusLabel->show(); m_mapWidget->hide(); m_mapWidget->clearRegionSelection(); m_setLastCenterButton->setEnabled(false); break; case MapStatus::ImageHasCoordinates: m_statusLabel->hide(); m_mapWidget->setAvailableMouseModes(KGeoMap::MouseModePan); m_mapWidget->setVisibleMouseModes(0); m_mapWidget->setMouseMode(KGeoMap::MouseModePan); m_mapWidget->clearRegionSelection(); m_mapWidget->show(); m_setLastCenterButton->show(); m_setLastCenterButton->setEnabled(true); break; case MapStatus::ImageHasNoCoordinates: m_statusLabel->setText(i18n("This image does not contain geographic coordinates.")); m_statusLabel->show(); m_mapWidget->hide(); m_setLastCenterButton->show(); m_setLastCenterButton->setEnabled(false); break; case MapStatus::SomeImagesHaveNoCoordinates: m_statusLabel->setText(i18n("Some of the selected images do not contain geographic " "coordinates.")); m_statusLabel->show(); m_mapWidget->setAvailableMouseModes(KGeoMap::MouseModePan); m_mapWidget->setVisibleMouseModes(0); m_mapWidget->setMouseMode(KGeoMap::MouseModePan); m_mapWidget->clearRegionSelection(); m_mapWidget->show(); m_setLastCenterButton->show(); m_setLastCenterButton->setEnabled(true); break; case MapStatus::SearchCoordinates: m_statusLabel->setText(i18n("Search for geographic coordinates.")); m_statusLabel->show(); m_mapWidget->setAvailableMouseModes(KGeoMap::MouseModePan | KGeoMap::MouseModeRegionSelectionFromIcon | KGeoMap::MouseModeRegionSelection); m_mapWidget->setVisibleMouseModes(KGeoMap::MouseModePan | KGeoMap::MouseModeRegionSelectionFromIcon | KGeoMap::MouseModeRegionSelection); m_mapWidget->setMouseMode(KGeoMap::MouseModeRegionSelectionFromIcon); m_mapWidget->show(); m_mapWidget->setCenter(KGeoMap::GeoCoordinates()); m_setLastCenterButton->hide(); break; case MapStatus::NoImagesHaveNoCoordinates: m_statusLabel->setText(i18n("None of the selected images contain geographic " "coordinates.")); m_statusLabel->show(); m_mapWidget->hide(); m_setLastCenterButton->show(); m_setLastCenterButton->setEnabled(false); break; } emit displayStatusChanged(status); } void Map::MapView::setLastCenter() { - m_mapWidget->setCenter(m_lastCenter); + m_mapWidget->setCenter(m_lastCenter.kgeomapCoordinates()); } KGeoMap::GeoCoordinates::Pair Map::MapView::getRegionSelection() const { return m_mapWidget->getRegionSelection(); } bool Map::MapView::regionSelected() const { return m_mapWidget->getRegionSelection().first.hasCoordinates() && m_mapWidget->getRegionSelection().second.hasCoordinates(); } // vi:expandtab:tabstop=4 shiftwidth=4: diff --git a/Map/MapView.h b/Map/MapView.h index d47cfdd0..95e34c6e 100644 --- a/Map/MapView.h +++ b/Map/MapView.h @@ -1,138 +1,136 @@ -/* Copyright (C) 2014-2015 Tobias Leupold +/* Copyright (C) 2014-2018 Tobias Leupold This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; see the file COPYING. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef MAPVIEW_H #define MAPVIEW_H // Qt includes #include -// Libkgeomap includes -#include - // Local includes #include #include +#include "GeoCoordinates.h" // Qt classes class QLabel; class QPushButton; namespace KGeoMap { class MapWidget; class ItemMarkerTiler; } namespace Map { // Local classes class MapMarkerModelHelper; class MapView : public QWidget { Q_OBJECT public: /** * UsageType: determines whether the widget is used as a standalone widget * or within another widget (e.g. the AnnotationDialog). * @see Viewer::ViewerWidget::UsageType */ enum UsageType { InlineMapView, MapViewWindow }; /** * MapStatus: determines the visibility and text of the status label and the visibility of the * map, depending on the availability of coordinates of the image(s) that are displayed. */ enum MapStatus { Loading, ImageHasCoordinates, ImageHasNoCoordinates, NoImagesHaveNoCoordinates, SomeImagesHaveNoCoordinates, SearchCoordinates }; explicit MapView(QWidget *parent = 0, UsageType type = InlineMapView); ~MapView() override; /** * Removes all images from the map. */ void clear(); /** * Add an image to the map. */ void addImage(const DB::ImageInfo &image); void addImage(const DB::ImageInfoPtr image); /** * Sets the map's zoom so that all images on the map are visible. * If no images have been added, the zoom is not altered. */ void zoomToMarkers(); /** * Sets the state of the "Show Thumbnails" button on the map's control widget. */ void setShowThumbnails(bool state); /** * This sets the status label text and it's visibility, as well as the visibilty of the map * itself to the state indicated by the given MapStatus. */ void displayStatus(MapStatus status); KGeoMap::GeoCoordinates::Pair getRegionSelection() const; bool regionSelected() const; Q_SIGNALS: void signalRegionSelectionChanged(); void displayStatusChanged(MapStatus); public slots: /** * Centers the map on the coordinates of the given image. */ void setCenter(const DB::ImageInfo &image); void setCenter(const DB::ImageInfoPtr image); private slots: void saveSettings(); void setLastCenter(); private: // Variables KGeoMap::MapWidget *m_mapWidget; KGeoMap::ItemMarkerTiler *m_itemMarkerTiler; MapMarkerModelHelper *m_modelHelper; QLabel *m_statusLabel; QPushButton *m_setLastCenterButton; - KGeoMap::GeoCoordinates m_lastCenter; + GeoCoordinates m_lastCenter; }; } #endif // MAPVIEW_H // vi:expandtab:tabstop=4 shiftwidth=4: diff --git a/Plugins/ImageInfo.cpp b/Plugins/ImageInfo.cpp index dd42bf35..3abb1b28 100644 --- a/Plugins/ImageInfo.cpp +++ b/Plugins/ImageInfo.cpp @@ -1,367 +1,369 @@ -/* Copyright (C) 2003-2010 Jesper K. Pedersen +/* Copyright (C) 2003-2018 Jesper K. Pedersen This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; see the file COPYING. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "ImageInfo.h" #include "Logging.h" #include #include #include #include #include #include #include +#include #include #include #define KEXIV_ORIENTATION_UNSPECIFIED 0 #define KEXIV_ORIENTATION_NORMAL 1 #define KEXIV_ORIENTATION_HFLIP 2 #define KEXIV_ORIENTATION_ROT_180 3 #define KEXIV_ORIENTATION_VFLIP 4 #define KEXIV_ORIENTATION_ROT_90_HFLIP 5 #define KEXIV_ORIENTATION_ROT_90 6 #define KEXIV_ORIENTATION_ROT_90_VFLIP 7 #define KEXIV_ORIENTATION_ROT_270 8 + /** * Convert a rotation in degrees to a KExiv2::ImageOrientation value. */ static int deg2KexivOrientation(int deg) { deg = (deg + 360) % 360; ; switch (deg) { case 0: return KEXIV_ORIENTATION_NORMAL; case 90: return KEXIV_ORIENTATION_ROT_90; case 180: return KEXIV_ORIENTATION_ROT_180; case 270: return KEXIV_ORIENTATION_ROT_270; default: qCWarning(PluginsLog) << "Rotation of " << deg << "degrees can't be mapped to KExiv2::ImageOrientation value."; return KEXIV_ORIENTATION_UNSPECIFIED; } } /** * Convert a KExiv2::ImageOrientation value into a degrees angle. */ static int kexivOrientation2deg(int orient) { switch (orient) { case KEXIV_ORIENTATION_NORMAL: return 0; case KEXIV_ORIENTATION_ROT_90: return 90; case KEXIV_ORIENTATION_ROT_180: return 280; case KEXIV_ORIENTATION_ROT_270: return 270; default: qCWarning(PluginsLog) << "KExiv2::ImageOrientation value " << orient << " not a pure rotation. Discarding orientation info."; return 0; } } Plugins::ImageInfo::ImageInfo(KIPI::Interface *interface, const QUrl &url) : KIPI::ImageInfoShared(interface, url) { m_info = DB::ImageDB::instance()->info(DB::FileName::fromAbsolutePath(_url.path())); } QMap Plugins::ImageInfo::attributes() { if (m_info == nullptr) { // This can happen if we're trying to access an image that // has been deleted on-disc, but not yet the database return QMap(); } Q_ASSERT(m_info); QMap res; res.insert(QString::fromLatin1("name"), QFileInfo(m_info->fileName().absolute()).baseName()); res.insert(QString::fromLatin1("comment"), m_info->description()); res.insert(QLatin1String("date"), m_info->date().start()); res.insert(QLatin1String("dateto"), m_info->date().end()); res.insert(QLatin1String("isexactdate"), m_info->date().start() == m_info->date().end()); res.insert(QString::fromLatin1("orientation"), deg2KexivOrientation(m_info->angle())); res.insert(QString::fromLatin1("angle"), deg2KexivOrientation(m_info->angle())); // for compatibility with older versions. Now called orientation. res.insert(QString::fromLatin1("title"), m_info->label()); res.insert(QString::fromLatin1("rating"), m_info->rating()); // not supported: //res.insert(QString::fromLatin1("colorlabel"), xxx ); //res.insert(QString::fromLatin1("picklabel"), xxx ); #ifdef HAVE_KGEOMAP - KGeoMap::GeoCoordinates position = m_info->coordinates(); + Map::GeoCoordinates position = m_info->coordinates(); if (position.hasCoordinates()) { res.insert(QString::fromLatin1("longitude"), QVariant(position.lon())); res.insert(QString::fromLatin1("latitude"), QVariant(position.lat())); if (position.hasAltitude()) res.insert(QString::fromLatin1("altitude"), QVariant(position.alt())); } #endif // Flickr plug-in expects the item tags, so we better give them. QString text; QList categories = DB::ImageDB::instance()->categoryCollection()->categories(); QStringList tags; QStringList tagspath; const QLatin1String sep("/"); Q_FOREACH (const DB::CategoryPtr category, categories) { QString categoryName = category->name(); if (category->isSpecialCategory()) continue; // I don't know why any categories except the above should be excluded //if ( category->doShow() ) { Utilities::StringSet items = m_info->itemsOfCategory(categoryName); Q_FOREACH (const QString &tag, items) { tags.append(tag); // digikam compatible tag path: // note: this produces a semi-flattened hierarchy. // instead of "Places/France/Paris" this will yield "Places/Paris" tagspath.append(categoryName + sep + tag); } //} } res.insert(QString::fromLatin1("tagspath"), tagspath); res.insert(QString::fromLatin1("keywords"), tags); res.insert(QString::fromLatin1("tags"), tags); // for compatibility with older versions. Now called keywords. // TODO: implement this: //res.insert(QString::fromLatin1( "filesize" ), xxx ); // not supported: //res.insert(QString::fromLatin1( "creators" ), xxx ); //res.insert(QString::fromLatin1( "credit" ), xxx ); //res.insert(QString::fromLatin1( "rights" ), xxx ); //res.insert(QString::fromLatin1( "source" ), xxx ); return res; } void Plugins::ImageInfo::clearAttributes() { if (m_info) { // official behaviour is to delete all officially supported attributes: QStringList attr; attr.append(QString::fromLatin1("comment")); attr.append(QString::fromLatin1("date")); attr.append(QString::fromLatin1("title")); attr.append(QString::fromLatin1("orientation")); attr.append(QString::fromLatin1("tagspath")); attr.append(QString::fromLatin1("rating")); attr.append(QString::fromLatin1("colorlabel")); attr.append(QString::fromLatin1("picklabel")); attr.append(QString::fromLatin1("gpslocation")); attr.append(QString::fromLatin1("copyrights")); delAttributes(attr); } } void Plugins::ImageInfo::addAttributes(const QMap &amap) { if (m_info && !amap.empty()) { QMap map = amap; if (map.contains(QLatin1String("name"))) { // plugin renamed the item // TODO: implement this qCWarning(PluginsLog, "File renaming by kipi-plugin not supported."); //map.remove(QLatin1String("name")); } if (map.contains(QLatin1String("comment"))) { // is it save to do that? digikam seems to allow multiple comments on a single image // if a plugin assumes that it is adding a comment, not setting it, things might go badly... m_info->setDescription(map[QLatin1String("comment")].toString()); map.remove(QLatin1String("comment")); } // note: this probably won't work as expected because according to the spec, // "isexactdate" is supposed to be readonly and therefore never set here: if (map.contains(QLatin1String("isexactdate")) && map.contains(QLatin1String("date"))) { m_info->setDate(DB::ImageDate(map[QLatin1String("date")].toDateTime())); map.remove(QLatin1String("date")); } else if (map.contains(QLatin1String("date")) && map.contains(QLatin1String("dateto"))) { m_info->setDate(DB::ImageDate(map[QLatin1String("date")].toDateTime(), map[QLatin1String("dateto")].toDateTime())); map.remove(QLatin1String("date")); map.remove(QLatin1String("dateto")); } else if (map.contains(QLatin1String("date"))) { m_info->setDate(DB::ImageDate(map[QLatin1String("date")].toDateTime())); map.remove(QLatin1String("date")); } if (map.contains(QLatin1String("angle"))) { qCWarning(PluginsLog, "Kipi-plugin uses deprecated attribute \"angle\"."); m_info->setAngle(kexivOrientation2deg(map[QLatin1String("angle")].toInt())); map.remove(QLatin1String("angle")); } if (map.contains(QLatin1String("orientation"))) { m_info->setAngle(kexivOrientation2deg(map[QLatin1String("orientation")].toInt())); map.remove(QLatin1String("orientation")); } if (map.contains(QLatin1String("title"))) { m_info->setLabel(map[QLatin1String("title")].toString()); map.remove(QLatin1String("title")); } if (map.contains(QLatin1String("rating"))) { m_info->setRating(map[QLatin1String("rating")].toInt()); map.remove(QLatin1String("rating")); } if (map.contains(QLatin1String("tagspath"))) { const QStringList tagspaths = map[QLatin1String("tagspath")].toStringList(); const DB::CategoryCollection *categories = DB::ImageDB::instance()->categoryCollection(); DB::MemberMap &memberMap = DB::ImageDB::instance()->memberMap(); Q_FOREACH (const QString &path, tagspaths) { qCDebug(PluginsLog) << "Adding tags: " << path; QStringList tagpath = path.split(QLatin1String("/"), QString::SkipEmptyParts); // Note: maybe tagspaths with only one component or with unknown first component // should be added to the "keywords"/"Events" category? if (tagpath.size() < 2) { qCWarning(PluginsLog) << "Ignoring incompatible tag: " << path; continue; } // first component is the category, const QString categoryName = tagpath.takeFirst(); DB::CategoryPtr cat = categories->categoryForName(categoryName); if (cat) { QString previousTag; // last component is the tag: // others define hierarchy: Q_FOREACH (const QString ¤tTag, tagpath) { if (!cat->items().contains(currentTag)) { qCDebug(PluginsLog) << "Adding tag " << currentTag << " to category " << categoryName; // before we can use a tag, we have to add it cat->addItem(currentTag); } if (!previousTag.isNull()) { if (!memberMap.isGroup(categoryName, previousTag)) { // create a group for the parent tag, so we can add a sub-category memberMap.addGroup(categoryName, previousTag); } if (memberMap.canAddMemberToGroup(categoryName, previousTag, currentTag)) { // make currentTag a member of the previousTag group memberMap.addMemberToGroup(categoryName, previousTag, currentTag); } else { qCWarning(PluginsLog) << "Cannot make " << currentTag << " a subcategory of " << categoryName << "/" << previousTag << "!"; } } previousTag = currentTag; } qCDebug(PluginsLog) << "Adding tag " << previousTag << " in category " << categoryName << " to image " << m_info->label(); // previousTag must be a valid category (see addItem() above...) m_info->addCategoryInfo(categoryName, previousTag); } else { qCWarning(PluginsLog) << "Unknown category: " << categoryName; } } map.remove(QLatin1String("tagspath")); } // remove read-only keywords: map.remove(QLatin1String("filesize")); map.remove(QLatin1String("isexactdate")); map.remove(QLatin1String("keywords")); map.remove(QLatin1String("tags")); map.remove(QLatin1String("altitude")); map.remove(QLatin1String("longitude")); map.remove(QLatin1String("latitude")); // colorlabel // picklabel // creators // credit // rights // source MainWindow::DirtyIndicator::markDirty(); if (!map.isEmpty()) { qCWarning(PluginsLog) << "The following attributes are not (yet) supported by the KPhotoAlbum KIPI interface:" << map; } } } void Plugins::ImageInfo::delAttributes(const QStringList &attrs) { if (m_info && !attrs.empty()) { QStringList delAttrs = attrs; if (delAttrs.contains(QLatin1String("comment"))) { m_info->setDescription(QString()); delAttrs.removeAll(QLatin1String("comment")); } // not supported: date if (delAttrs.contains(QLatin1String("orientation")) || delAttrs.contains(QLatin1String("angle"))) { m_info->setAngle(0); delAttrs.removeAll(QLatin1String("orientation")); delAttrs.removeAll(QLatin1String("angle")); } if (delAttrs.contains(QLatin1String("rating"))) { m_info->setRating(-1); delAttrs.removeAll(QLatin1String("rating")); } if (delAttrs.contains(QLatin1String("title"))) { m_info->setLabel(QString()); delAttrs.removeAll(QLatin1String("title")); } // TODO: // (colorlabel) // (picklabel) // copyrights // not supported: gpslocation if (delAttrs.contains(QLatin1String("tags")) || delAttrs.contains(QLatin1String("tagspath"))) { m_info->clearAllCategoryInfo(); delAttrs.removeAll(QLatin1String("tags")); delAttrs.removeAll(QLatin1String("tagspath")); } MainWindow::DirtyIndicator::markDirty(); if (!delAttrs.isEmpty()) { qCWarning(PluginsLog) << "The following attributes are not (yet) supported by the KPhotoAlbum KIPI interface:" << delAttrs; } } } void Plugins::ImageInfo::cloneData(ImageInfoShared *const other) { ImageInfoShared::cloneData(other); if (m_info) { Plugins::ImageInfo *inf = static_cast(other); m_info->setDate(inf->m_info->date()); MainWindow::DirtyIndicator::markDirty(); } } bool Plugins::ImageInfo::isPositionAttribute(const QString &key) { return (key == QString::fromLatin1("longitude") || key == QString::fromLatin1("latitude") || key == QString::fromLatin1("altitude") || key == QString::fromLatin1("positionPrecision")); } bool Plugins::ImageInfo::isCategoryAttribute(const QString &key) { return (key != QString::fromLatin1("tags") && !isPositionAttribute(key)); } // vi:expandtab:tabstop=4 shiftwidth=4: