diff --git a/CMakeLists.txt b/CMakeLists.txt index 23340ae1..a0c54a3b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,560 +1,537 @@ cmake_minimum_required(VERSION 3.2.0) project(kphotoalbum LANGUAGES CXX VERSION 5.6.1) 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 -DQT_DISABLE_DEPRECATED_BEFORE=0x050900 -DQT_DISABLE_Q_FOREACH ) set(CMAKE_CXX_STANDARD 14) set(CMAKE_CXX_REQUIRED TRUE) ########### dependencies ############### find_package(Qt5 5.9 REQUIRED COMPONENTS Sql Xml Widgets) 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(Marble) set_package_properties(Marble PROPERTIES TYPE OPTIONAL PURPOSE "Enables support for geographic map location using embedded GPS information." ) set(HAVE_MARBLE ${Marble_FOUND}) if(Marble_FOUND) include(MarbleChecks) endif() 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 ) option(KPA_ENABLE_REMOTECONTROL "Build with support for companion Android/QML app." OFF) set(libRemoteControl_SRCS) set(libRemoteControl_LIBS) if(KPA_ENABLE_REMOTECONTROL) find_package(Qt5 5.9 REQUIRED COMPONENTS Network) # requires cmake 3.12 add_compile_definitions(KPA_ENABLE_REMOTECONTROL) set(libRemoteControl_LIBS Qt5::Network) 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 ) endif() set(libMap_SRCS) if(Marble_FOUND) set(libMap_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/Browser/GeoPositionPage.cpp ${CMAKE_CURRENT_SOURCE_DIR}/Map/GeoCluster.cpp ${CMAKE_CURRENT_SOURCE_DIR}/Map/MapView.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_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 ${libRemoteControl_LIBS} 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(Marble_FOUND) target_link_libraries(kphotoalbum Marble) 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-plugins.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config-kpa-plugins.h) configure_file(config-kpa-marble.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config-kpa-marble.h) feature_summary(WHAT ALL FATAL_ON_MISSING_REQUIRED_PACKAGES) # vi:expandtab:tabstop=4 shiftwidth=4: diff --git a/MainWindow/Window.cpp b/MainWindow/Window.cpp index eb56306c..4025fd21 100644 --- a/MainWindow/Window.cpp +++ b/MainWindow/Window.cpp @@ -1,1933 +1,1827 @@ /* Copyright (C) 2003-2020 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 "Window.h" #include #include #ifdef HAVE_STDLIB_H #include #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // for #if KIO_VERSION... #include -#ifdef HASKIPI -#include -#include -#endif - #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include -#ifdef HASKIPI -#include -#endif #ifdef KF5Purpose_FOUND #include #endif #include "AutoStackImages.h" #include "BreadcrumbViewer.h" #include "CopyPopup.h" #include "DeleteDialog.h" #include "DirtyIndicator.h" #include "DuplicateMerger/DuplicateMerger.h" #include "ExternalPopup.h" #include "FeatureDialog.h" #include "ImageCounter.h" #include "InvalidDateFinder.h" #include "Logging.h" #include "Options.h" #include "SearchBar.h" #include "SplashScreen.h" #include "StatisticsDialog.h" #include "StatusBar.h" #include "TokenEditor.h" #include "UpdateVideoThumbnail.h" #include "WelcomeDialog.h" #ifdef HAVE_MARBLE #include #endif #ifdef KPA_ENABLE_REMOTECONTROL #include #endif #include #include #include #include #include #include #include #include #include #include #include using namespace DB; MainWindow::Window *MainWindow::Window::s_instance = nullptr; MainWindow::Window::Window(QWidget *parent) : KXmlGuiWindow(parent) , m_annotationDialog(nullptr) , m_deleteDialog(nullptr) , m_htmlDialog(nullptr) , m_tokenEditor(nullptr) #ifdef HAVE_MARBLE , m_positionBrowser(nullptr) #endif { qCDebug(MainWindowLog) << "Using icon theme: " << QIcon::themeName(); qCDebug(MainWindowLog) << "Icon search paths: " << QIcon::themeSearchPaths(); QElapsedTimer timer; timer.start(); SplashScreen::instance()->message(i18n("Loading Database")); s_instance = this; bool gotConfigFile = load(); if (!gotConfigFile) throw 0; qCInfo(TimingLog) << "MainWindow: Loading Database: " << timer.restart() << "ms."; SplashScreen::instance()->message(i18n("Loading Main Window")); QWidget *top = new QWidget(this); QVBoxLayout *lay = new QVBoxLayout(top); lay->setSpacing(2); lay->setContentsMargins(2, 2, 2, 2); setCentralWidget(top); m_stack = new QStackedWidget(top); lay->addWidget(m_stack, 1); m_dateBar = new DateBar::DateBarWidget(top); lay->addWidget(m_dateBar); m_dateBarLine = new QFrame(top); m_dateBarLine->setFrameStyle(QFrame::HLine | QFrame::Plain); m_dateBarLine->setLineWidth(0); m_dateBarLine->setMidLineWidth(0); QPalette pal = m_dateBarLine->palette(); pal.setColor(QPalette::WindowText, palette().window().color()); m_dateBarLine->setPalette(pal); lay->addWidget(m_dateBarLine); setHistogramVisibilty(Settings::SettingsData::instance()->showHistogram()); m_browser = new Browser::BrowserWidget(m_stack); m_thumbnailView = new ThumbnailView::ThumbnailFacade(); m_stack->addWidget(m_browser); m_stack->addWidget(m_thumbnailView->gui()); m_stack->setCurrentWidget(m_browser); m_settingsDialog = nullptr; qCInfo(TimingLog) << "MainWindow: Loading MainWindow: " << timer.restart() << "ms."; setupMenuBar(); qCInfo(TimingLog) << "MainWindow: setupMenuBar: " << timer.restart() << "ms."; createSearchBar(); qCInfo(TimingLog) << "MainWindow: createSearchBar: " << timer.restart() << "ms."; setupStatusBar(); qCInfo(TimingLog) << "MainWindow: setupStatusBar: " << timer.restart() << "ms."; // Misc m_autoSaveTimer = new QTimer(this); connect(m_autoSaveTimer, &QTimer::timeout, this, &Window::slotAutoSave); startAutoSaveTimer(); connect(m_browser, &Browser::BrowserWidget::showingOverview, this, &Window::showBrowser); connect(m_browser, &Browser::BrowserWidget::pathChanged, m_statusBar->mp_pathIndicator, &BreadcrumbViewer::setBreadcrumbs); connect(m_statusBar->mp_pathIndicator, &BreadcrumbViewer::widenToBreadcrumb, m_browser, &Browser::BrowserWidget::widenToBreadcrumb); connect(m_browser, &Browser::BrowserWidget::pathChanged, this, QOverload::of(&Window::updateDateBar)); connect(m_dateBar, &DateBar::DateBarWidget::dateSelected, m_thumbnailView, &ThumbnailView::ThumbnailFacade::gotoDate); connect(m_dateBar, &DateBar::DateBarWidget::toolTipInfo, this, &Window::showDateBarTip); connect(Settings::SettingsData::instance(), &Settings::SettingsData::histogramSizeChanged, m_dateBar, &DateBar::DateBarWidget::setHistogramBarSize); connect(Settings::SettingsData::instance(), &Settings::SettingsData::actualThumbnailSizeChanged, this, &Window::slotThumbnailSizeChanged); connect(m_dateBar, &DateBar::DateBarWidget::dateRangeChange, this, &Window::setDateRange); connect(m_dateBar, &DateBar::DateBarWidget::dateRangeCleared, this, &Window::clearDateRange); connect(m_thumbnailView, &ThumbnailView::ThumbnailFacade::currentDateChanged, m_dateBar, &DateBar::DateBarWidget::setDate); connect(m_thumbnailView, &ThumbnailView::ThumbnailFacade::showImage, this, &Window::showImage); connect(m_thumbnailView, &ThumbnailView::ThumbnailFacade::showSelection, this, QOverload<>::of(&Window::slotView)); connect(m_thumbnailView, &ThumbnailView::ThumbnailFacade::fileIdUnderCursorChanged, this, &Window::slotSetFileName); connect(DB::ImageDB::instance(), &DB::ImageDB::totalChanged, this, QOverload<>::of(&Window::updateDateBar)); connect(DB::ImageDB::instance()->categoryCollection(), &DB::CategoryCollection::categoryCollectionChanged, this, &Window::slotOptionGroupChanged); connect(m_browser, &Browser::BrowserWidget::imageCount, m_statusBar->mp_partial, &ImageCounter::showBrowserMatches); connect(m_thumbnailView, &ThumbnailView::ThumbnailFacade::selectionChanged, this, &Window::updateContextMenuFromSelectionSize); checkIfVideoThumbnailerIsInstalled(); executeStartupActions(); qCInfo(TimingLog) << "MainWindow: executeStartupActions " << timer.restart() << "ms."; QTimer::singleShot(0, this, &Window::delayedInit); updateContextMenuFromSelectionSize(0); // Automatically save toolbar settings setAutoSaveSettings(); qCInfo(TimingLog) << "MainWindow: misc setup time: " << timer.restart() << "ms."; } MainWindow::Window::~Window() { DB::ImageDB::deleteInstance(); ImageManager::ThumbnailCache::deleteInstance(); Exif::Database::deleteInstance(); } void MainWindow::Window::delayedInit() { QElapsedTimer timer; timer.start(); SplashScreen *splash = SplashScreen::instance(); setupPluginMenu(); qCInfo(TimingLog) << "MainWindow: setupPluginMenu: " << timer.restart() << "ms."; if (Settings::SettingsData::instance()->searchForImagesOnStart() || Options::the()->searchForImagesOnStart()) { splash->message(i18n("Searching for New Files")); qApp->processEvents(); DB::ImageDB::instance()->slotRescan(); qCInfo(TimingLog) << "MainWindow: Search for New Files: " << timer.restart() << "ms."; } - if (!Settings::SettingsData::instance()->delayLoadingPlugins()) { - splash->message(i18n("Loading Plug-ins")); - loadKipiPlugins(); - qCInfo(TimingLog) << "MainWindow: Loading Plug-ins: " << timer.restart() << "ms."; - } - splash->done(); show(); updateDateBar(); qCInfo(TimingLog) << "MainWindow: MainWindow.show():" << timer.restart() << "ms."; QUrl importUrl = Options::the()->importFile(); if (importUrl.isValid()) { // I need to do this in delayed init to get the import window on top of the normal window ImportExport::Import::imageImport(importUrl); qCInfo(TimingLog) << "MainWindow: imageImport:" << timer.restart() << "ms."; } else { // I need to postpone this otherwise the tip dialog will not get focus on start up KTipDialog::showTip(this); } Exif::Database::instance(); // Load the database qCInfo(TimingLog) << "MainWindow: Loading Exif DB:" << timer.restart() << "ms."; #ifdef KPA_ENABLE_REMOTECONTROL if (!Options::the()->listen().isNull()) RemoteControl::RemoteInterface::instance().listen(Options::the()->listen()); else if (Settings::SettingsData::instance()->listenForAndroidDevicesOnStartup()) RemoteControl::RemoteInterface::instance().listen(); #endif } bool MainWindow::Window::slotExit() { if (Options::the()->demoMode()) { QString txt = i18n("

Delete Your Temporary Demo Database

" "

I hope you enjoyed the KPhotoAlbum demo. The demo database was copied to " "/tmp, should it be deleted now? If you do not delete it, it will waste disk space; " "on the other hand, if you want to come back and try the demo again, you " "might want to keep it around with the changes you made through this session.

"); int ret = KMessageBox::questionYesNoCancel(this, txt, i18n("Delete Demo Database"), KStandardGuiItem::yes(), KStandardGuiItem::no(), KStandardGuiItem::cancel(), QString::fromLatin1("deleteDemoDatabase")); if (ret == KMessageBox::Cancel) return false; else if (ret == KMessageBox::Yes) { Utilities::deleteDemo(); goto doQuit; } else { // pass through to the check for dirtyness. } } if (m_statusBar->mp_dirtyIndicator->isSaveDirty()) { int ret = KMessageBox::warningYesNoCancel(this, i18n("Do you want to save the changes?"), i18n("Save Changes?")); if (ret == KMessageBox::Cancel) { return false; } if (ret == KMessageBox::Yes) { slotSave(); } if (ret == KMessageBox::No) { QDir().remove(Settings::SettingsData::instance()->imageDirectory() + QString::fromLatin1(".#index.xml")); } } // Flush any remaining thumbnails ImageManager::ThumbnailCache::instance()->save(); doQuit: ImageManager::AsyncLoader::instance()->requestExit(); qApp->quit(); return true; } void MainWindow::Window::slotOptions() { if (!m_settingsDialog) { m_settingsDialog = new Settings::SettingsDialog(this); // lambda expression because because reloadThumbnails has default parameters: connect(m_settingsDialog, &Settings::SettingsDialog::changed, this, [=]() { this->reloadThumbnails(); }); connect(m_settingsDialog, &Settings::SettingsDialog::changed, this, &Window::startAutoSaveTimer); connect(m_settingsDialog, &Settings::SettingsDialog::changed, m_browser, &Browser::BrowserWidget::reload); } m_settingsDialog->show(); } void MainWindow::Window::slotCreateImageStack() { const DB::FileNameList list = selected(); if (list.size() < 2) { // it doesn't make sense to make a stack from one image, does it? return; } bool ok = DB::ImageDB::instance()->stack(list); if (!ok) { if (KMessageBox::questionYesNo(this, i18n("Some of the selected images already belong to a stack. " "Do you want to remove them from their stacks and create a " "completely new one?"), i18n("Stacking Error")) == KMessageBox::Yes) { DB::ImageDB::instance()->unstack(list); if (!DB::ImageDB::instance()->stack(list)) { KMessageBox::sorry(this, i18n("Unknown error, stack creation failed."), i18n("Stacking Error")); return; } } else { return; } } DirtyIndicator::markDirty(); // The current item might have just became invisible m_thumbnailView->setCurrentItem(list.at(0)); m_thumbnailView->updateDisplayModel(); } /** @short Make the selected image the head of a stack * * The whole point of image stacking is to group images together and then select * one of them as the "most important". This function is (maybe just a * temporary) way of promoting a selected image to the "head" of a stack it * belongs to. In future, it might get replaced by a Ligtroom-like interface. * */ void MainWindow::Window::slotSetStackHead() { const DB::FileNameList list = selected(); if (list.size() != 1) { // this should be checked by enabling/disabling of QActions return; } setStackHead(*list.begin()); } void MainWindow::Window::setStackHead(const DB::FileName &image) { if (!image.info()->isStacked()) return; unsigned int oldOrder = image.info()->stackOrder(); const DB::FileNameList others = DB::ImageDB::instance()->getStackFor(image); for (const DB::FileName ¤t : others) { if (current == image) { current.info()->setStackOrder(1); } else if (current.info()->stackOrder() < oldOrder) { current.info()->setStackOrder(current.info()->stackOrder() + 1); } } DirtyIndicator::markDirty(); m_thumbnailView->updateDisplayModel(); } void MainWindow::Window::slotUnStackImages() { const DB::FileNameList &list = selected(); if (list.isEmpty()) return; DB::ImageDB::instance()->unstack(list); DirtyIndicator::markDirty(); m_thumbnailView->updateDisplayModel(); } void MainWindow::Window::slotConfigureAllImages() { configureImages(false); } void MainWindow::Window::slotConfigureImagesOneAtATime() { configureImages(true); } void MainWindow::Window::configureImages(bool oneAtATime) { const DB::FileNameList &list = selected(); if (list.isEmpty()) { KMessageBox::sorry(this, i18n("No item is selected."), i18n("No Selection")); } else { DB::ImageInfoList images; for (const DB::FileName &fileName : list) { images.append(fileName.info()); } configureImages(images, oneAtATime); } } void MainWindow::Window::configureImages(const DB::ImageInfoList &list, bool oneAtATime) { s_instance->configImages(list, oneAtATime); } void MainWindow::Window::configImages(const DB::ImageInfoList &list, bool oneAtATime) { createAnnotationDialog(); if (m_annotationDialog->configure(list, oneAtATime) == QDialog::Rejected) return; reloadThumbnails(ThumbnailView::MaintainSelection); } void MainWindow::Window::slotSearch() { createAnnotationDialog(); DB::ImageSearchInfo searchInfo = m_annotationDialog->search(); if (!searchInfo.isNull()) m_browser->addSearch(searchInfo); } void MainWindow::Window::createAnnotationDialog() { Utilities::ShowBusyCursor dummy; if (!m_annotationDialog.isNull()) return; m_annotationDialog = new AnnotationDialog::Dialog(nullptr); connect(m_annotationDialog.data(), &AnnotationDialog::Dialog::imageRotated, this, &Window::slotImageRotated); } void MainWindow::Window::slotSave() { Utilities::ShowBusyCursor dummy; m_statusBar->showMessage(i18n("Saving..."), 5000); DB::ImageDB::instance()->save(Settings::SettingsData::instance()->imageDirectory() + QString::fromLatin1("index.xml"), false); ImageManager::ThumbnailCache::instance()->save(); m_statusBar->mp_dirtyIndicator->saved(); QDir().remove(Settings::SettingsData::instance()->imageDirectory() + QString::fromLatin1(".#index.xml")); m_statusBar->showMessage(i18n("Saving... Done"), 5000); } void MainWindow::Window::slotDeleteSelected() { if (!m_deleteDialog) m_deleteDialog = new DeleteDialog(this); if (m_deleteDialog->exec(selected()) != QDialog::Accepted) return; DirtyIndicator::markDirty(); } void MainWindow::Window::slotCopySelectedURLs() { QList urls; int urlcount = 0; const auto selectedFiles = selected(); for (const DB::FileName &fileName : selectedFiles) { urls.append(QUrl::fromLocalFile(fileName.absolute())); urlcount++; } if (urlcount == 1) m_paste->setEnabled(true); else m_paste->setEnabled(false); QMimeData *mimeData = new QMimeData; mimeData->setUrls(urls); QApplication::clipboard()->setMimeData(mimeData); } void MainWindow::Window::slotPasteInformation() { const QMimeData *mimeData = QApplication::clipboard()->mimeData(); // Idealy this would look like // QList urls; // urls.fromMimeData(mimeData); // if ( urls.count() != 1 ) return; // const QString string = urls.first().path(); QString string = mimeData->text(); // fail silent if more than one image is in clipboard. if (string.count(QString::fromLatin1("\n")) != 0) return; const QString urlHead = QLatin1String("file://"); if (string.startsWith(urlHead)) { string = string.right(string.size() - urlHead.size()); } const DB::FileName fileName = DB::FileName::fromAbsolutePath(string); // fail silent if there is no file. if (fileName.isNull()) return; MD5 originalSum = MD5Sum(fileName); ImageInfoPtr originalInfo; if (DB::ImageDB::instance()->md5Map()->contains(originalSum)) { originalInfo = DB::ImageDB::instance()->info(fileName); } else { originalInfo = fileName.info(); } // fail silent if there is no info for the file. if (!originalInfo) return; const auto selectedFiles = selected(); for (const DB::FileName &newFile : selectedFiles) { newFile.info()->copyExtraData(*originalInfo, false); } DirtyIndicator::markDirty(); } void MainWindow::Window::slotReReadExifInfo() { DB::FileNameList files = selectedOnDisk(); static Exif::ReReadDialog *dialog = nullptr; if (!dialog) dialog = new Exif::ReReadDialog(this); if (dialog->exec(files) == QDialog::Accepted) DirtyIndicator::markDirty(); } void MainWindow::Window::slotAutoStackImages() { const DB::FileNameList list = selected(); if (list.isEmpty()) { KMessageBox::sorry(this, i18n("No item is selected."), i18n("No Selection")); return; } QPointer stacker = new AutoStackImages(this, list); if (stacker->exec() == QDialog::Accepted) showThumbNails(); delete stacker; } /** * In thumbnail mode, return a list of files that are selected. * Otherwise, return all images in the current scope/context. */ DB::FileNameList MainWindow::Window::selected(ThumbnailView::SelectionMode mode) const { if (m_thumbnailView->gui() == m_stack->currentWidget()) return m_thumbnailView->selection(mode); else // return all images in the current scope (parameter false: include images not on disk) return DB::ImageDB::instance()->currentScope(false); } void MainWindow::Window::slotViewNewWindow() { slotView(false, false); } /* * Returns a list of files that are both selected and on disk. If there are no * selected files, returns all files form current context that are on disk. * Note: On some setups (NFS), this can be a very time-consuming method! * */ DB::FileNameList MainWindow::Window::selectedOnDisk() { const DB::FileNameList list = selected(ThumbnailView::NoExpandCollapsedStacks); DB::FileNameList listOnDisk; for (const DB::FileName &fileName : list) { if (DB::ImageInfo::imageOnDisk(fileName)) listOnDisk.append(fileName); } return listOnDisk; } void MainWindow::Window::slotView(bool reuse, bool slideShow, bool random) { launchViewer(selected(ThumbnailView::NoExpandCollapsedStacks), reuse, slideShow, random); } void MainWindow::Window::slotView() { slotView(true, false, false); } void MainWindow::Window::launchViewer(const DB::FileNameList &inputMediaList, bool reuse, bool slideShow, bool random) { DB::FileNameList mediaList = inputMediaList; int seek = -1; if (mediaList.isEmpty()) { mediaList = m_thumbnailView->imageList(ThumbnailView::ViewOrder); } else if (mediaList.size() == 1) { // we fake it so it appears the user has selected all images // and magically scrolls to the originally selected one const DB::FileName first = mediaList.at(0); mediaList = m_thumbnailView->imageList(ThumbnailView::ViewOrder); seek = mediaList.indexOf(first); } if (mediaList.isEmpty()) mediaList = DB::ImageDB::instance()->currentScope(false); if (mediaList.isEmpty()) { KMessageBox::sorry(this, i18n("There are no images to be shown.")); return; } if (random) { mediaList = DB::FileNameList(Utilities::shuffleList(mediaList)); } Viewer::ViewerWidget *viewer; if (reuse && Viewer::ViewerWidget::latest()) { viewer = Viewer::ViewerWidget::latest(); viewer->raise(); viewer->activateWindow(); } else viewer = new Viewer::ViewerWidget(Viewer::ViewerWidget::ViewerWindow, &m_viewerInputMacros); connect(viewer, &Viewer::ViewerWidget::soughtTo, m_thumbnailView, &ThumbnailView::ThumbnailFacade::changeSingleSelection); connect(viewer, &Viewer::ViewerWidget::imageRotated, this, &Window::slotImageRotated); viewer->show(slideShow); viewer->load(mediaList, seek < 0 ? 0 : seek); viewer->raise(); } void MainWindow::Window::slotSortByDateAndTime() { DB::ImageDB::instance()->sortAndMergeBackIn(selected()); showThumbNails(DB::ImageDB::instance()->search(Browser::BrowserWidget::instance()->currentContext())); DirtyIndicator::markDirty(); } void MainWindow::Window::slotSortAllByDateAndTime() { DB::ImageDB::instance()->sortAndMergeBackIn(DB::ImageDB::instance()->images()); if (m_thumbnailView->gui() == m_stack->currentWidget()) showThumbNails(DB::ImageDB::instance()->search(Browser::BrowserWidget::instance()->currentContext())); DirtyIndicator::markDirty(); } QString MainWindow::Window::welcome() { QString configFileName; QPointer dialog = new WelcomeDialog(this); // exit if the user dismissed the welcome dialog if (!dialog->exec()) { qApp->quit(); } configFileName = dialog->configFileName(); delete dialog; return configFileName; } bool MainWindow::Window::event(QEvent *event) { if (event->type() == QEvent::PaletteChange) { // KColorSchemeManager sets a dynamic property when activating a scheme: const QString schemePath = qApp->property("KDE_COLOR_SCHEME_PATH").toString(); qCInfo(MainWindowLog) << "Color Scheme changed to " << (schemePath.isEmpty() ? QString::fromLatin1("system default") : schemePath); Settings::SettingsData::instance()->setColorScheme(schemePath); return QWidget::event(event); } return QWidget::event(event); } void MainWindow::Window::closeEvent(QCloseEvent *e) { bool quit = true; quit = slotExit(); // If I made it here, then the user canceled if (!quit) e->ignore(); else e->setAccepted(true); } void MainWindow::Window::slotLimitToSelected() { Utilities::ShowBusyCursor dummy; showThumbNails(selected()); } void MainWindow::Window::setupMenuBar() { // File menu KStandardAction::save(this, &Window::slotSave, actionCollection()); KStandardAction::quit(this, &Window::slotExit, actionCollection()); m_generateHtml = actionCollection()->addAction(QString::fromLatin1("exportHTML")); m_generateHtml->setText(i18n("Generate HTML...")); connect(m_generateHtml, &QAction::triggered, this, &Window::slotExportToHTML); QAction *a = actionCollection()->addAction(QString::fromLatin1("import"), this, &Window::slotImport); a->setText(i18n("Import...")); a = actionCollection()->addAction(QString::fromLatin1("export"), this, &Window::slotExport); a->setText(i18n("Export/Copy Images...")); // Go menu a = KStandardAction::back(m_browser, &Browser::BrowserWidget::back, actionCollection()); connect(m_browser, &Browser::BrowserWidget::canGoBack, a, &QAction::setEnabled); a->setEnabled(false); a = KStandardAction::forward(m_browser, &Browser::BrowserWidget::forward, actionCollection()); connect(m_browser, &Browser::BrowserWidget::canGoForward, a, &QAction::setEnabled); a->setEnabled(false); a = KStandardAction::home(m_browser, &Browser::BrowserWidget::home, actionCollection()); actionCollection()->setDefaultShortcut(a, Qt::CTRL + Qt::Key_Home); connect(a, &QAction::triggered, m_dateBar, &DateBar::DateBarWidget::clearSelection); KStandardAction::redisplay(m_browser, &Browser::BrowserWidget::go, actionCollection()); // The Edit menu m_copy = KStandardAction::copy(this, &Window::slotCopySelectedURLs, actionCollection()); m_paste = KStandardAction::paste(this, &Window::slotPasteInformation, actionCollection()); m_paste->setEnabled(false); m_selectAll = KStandardAction::selectAll(m_thumbnailView, &ThumbnailView::ThumbnailFacade::selectAll, actionCollection()); m_clearSelection = KStandardAction::deselect(m_thumbnailView, &ThumbnailView::ThumbnailFacade::clearSelection, actionCollection()); m_clearSelection->setEnabled(false); KStandardAction::find(this, &Window::slotSearch, actionCollection()); m_deleteSelected = actionCollection()->addAction(QString::fromLatin1("deleteSelected")); m_deleteSelected->setText(i18nc("Delete selected images", "Delete Selected")); m_deleteSelected->setIcon(QIcon::fromTheme(QString::fromLatin1("edit-delete"))); actionCollection()->setDefaultShortcut(m_deleteSelected, Qt::Key_Delete); connect(m_deleteSelected, &QAction::triggered, this, &Window::slotDeleteSelected); a = actionCollection()->addAction(QString::fromLatin1("removeTokens"), this, &Window::slotRemoveTokens); a->setText(i18n("Remove Tokens...")); a = actionCollection()->addAction(QString::fromLatin1("showListOfFiles"), this, &Window::slotShowListOfFiles); a->setText(i18n("Open List of Files...")); m_configOneAtATime = actionCollection()->addAction(QString::fromLatin1("oneProp"), this, &Window::slotConfigureImagesOneAtATime); m_configOneAtATime->setText(i18n("Annotate Individual Items")); actionCollection()->setDefaultShortcut(m_configOneAtATime, Qt::CTRL + Qt::Key_1); m_configAllSimultaniously = actionCollection()->addAction(QString::fromLatin1("allProp"), this, &Window::slotConfigureAllImages); m_configAllSimultaniously->setText(i18n("Annotate Multiple Items at a Time")); actionCollection()->setDefaultShortcut(m_configAllSimultaniously, Qt::CTRL + Qt::Key_2); m_createImageStack = actionCollection()->addAction(QString::fromLatin1("createImageStack"), this, &Window::slotCreateImageStack); m_createImageStack->setText(i18n("Merge Images into a Stack")); actionCollection()->setDefaultShortcut(m_createImageStack, Qt::CTRL + Qt::Key_3); m_unStackImages = actionCollection()->addAction(QString::fromLatin1("unStackImages"), this, &Window::slotUnStackImages); m_unStackImages->setText(i18n("Remove Images from Stack")); m_setStackHead = actionCollection()->addAction(QString::fromLatin1("setStackHead"), this, &Window::slotSetStackHead); m_setStackHead->setText(i18n("Set as First Image in Stack")); actionCollection()->setDefaultShortcut(m_setStackHead, Qt::CTRL + Qt::Key_4); m_rotLeft = actionCollection()->addAction(QString::fromLatin1("rotateLeft"), this, &Window::slotRotateSelectedLeft); m_rotLeft->setText(i18n("Rotate counterclockwise")); actionCollection()->setDefaultShortcut(m_rotLeft, Qt::Key_7); m_rotRight = actionCollection()->addAction(QString::fromLatin1("rotateRight"), this, &Window::slotRotateSelectedRight); m_rotRight->setText(i18n("Rotate clockwise")); actionCollection()->setDefaultShortcut(m_rotRight, Qt::Key_9); // The Images menu m_view = actionCollection()->addAction(QString::fromLatin1("viewImages"), this, qOverload<>(&Window::slotView)); m_view->setText(i18n("View")); actionCollection()->setDefaultShortcut(m_view, Qt::CTRL + Qt::Key_I); m_viewInNewWindow = actionCollection()->addAction(QString::fromLatin1("viewImagesNewWindow"), this, &Window::slotViewNewWindow); m_viewInNewWindow->setText(i18n("View (In New Window)")); m_runSlideShow = actionCollection()->addAction(QString::fromLatin1("runSlideShow"), this, &Window::slotRunSlideShow); m_runSlideShow->setText(i18n("Run Slide Show")); m_runSlideShow->setIcon(QIcon::fromTheme(QString::fromLatin1("view-presentation"))); actionCollection()->setDefaultShortcut(m_runSlideShow, Qt::CTRL + Qt::Key_R); m_runRandomSlideShow = actionCollection()->addAction(QString::fromLatin1("runRandomizedSlideShow"), this, &Window::slotRunRandomizedSlideShow); m_runRandomSlideShow->setText(i18n("Run Randomized Slide Show")); a = actionCollection()->addAction(QString::fromLatin1("collapseAllStacks"), m_thumbnailView, &ThumbnailView::ThumbnailFacade::collapseAllStacks); connect(m_thumbnailView, &ThumbnailView::ThumbnailFacade::collapseAllStacksEnabled, a, &QAction::setEnabled); a->setEnabled(false); a->setText(i18n("Collapse all stacks")); a = actionCollection()->addAction(QString::fromLatin1("expandAllStacks"), m_thumbnailView, &ThumbnailView::ThumbnailFacade::expandAllStacks); connect(m_thumbnailView, &ThumbnailView::ThumbnailFacade::expandAllStacksEnabled, a, &QAction::setEnabled); a->setEnabled(false); a->setText(i18n("Expand all stacks")); QActionGroup *grp = new QActionGroup(this); a = actionCollection()->add(QString::fromLatin1("orderIncr"), this, &Window::slotOrderIncr); a->setText(i18n("Show &Oldest First")); a->setActionGroup(grp); a->setChecked(!Settings::SettingsData::instance()->showNewestThumbnailFirst()); a = actionCollection()->add(QString::fromLatin1("orderDecr"), this, &Window::slotOrderDecr); a->setText(i18n("Show &Newest First")); a->setActionGroup(grp); a->setChecked(Settings::SettingsData::instance()->showNewestThumbnailFirst()); m_sortByDateAndTime = actionCollection()->addAction(QString::fromLatin1("sortImages"), this, &Window::slotSortByDateAndTime); m_sortByDateAndTime->setText(i18n("Sort Selected by Date && Time")); m_limitToMarked = actionCollection()->addAction(QString::fromLatin1("limitToMarked"), this, &Window::slotLimitToSelected); m_limitToMarked->setText(i18n("Limit View to Selection")); m_jumpToContext = actionCollection()->addAction(QString::fromLatin1("jumpToContext"), this, &Window::slotJumpToContext); m_jumpToContext->setText(i18n("Jump to Context")); actionCollection()->setDefaultShortcut(m_jumpToContext, Qt::CTRL + Qt::Key_J); m_jumpToContext->setIcon(QIcon::fromTheme(QString::fromLatin1("kphotoalbum"))); // icon suggestion: go-jump (don't know the exact meaning though, so I didn't replace it right away m_lock = actionCollection()->addAction(QString::fromLatin1("lockToDefaultScope"), this, &Window::lockToDefaultScope); m_lock->setIcon(QIcon::fromTheme(QLatin1String("lock"))); m_lock->setText(i18n("Lock Images")); m_unlock = actionCollection()->addAction(QString::fromLatin1("unlockFromDefaultScope"), this, &Window::unlockFromDefaultScope); m_unlock->setIcon(QIcon::fromTheme(QLatin1String("unlock"))); m_unlock->setText(i18n("Unlock")); m_setDefaultPos = actionCollection()->addAction(QString::fromLatin1("setDefaultScopePositive"), this, &Window::setDefaultScopePositive); m_setDefaultPos->setText(i18n("Lock Away All Other Items")); m_setDefaultNeg = actionCollection()->addAction(QString::fromLatin1("setDefaultScopeNegative"), this, &Window::setDefaultScopeNegative); m_setDefaultNeg->setText(i18n("Lock Away Current Set of Items")); // Maintenance a = actionCollection()->addAction(QString::fromLatin1("findUnavailableImages"), this, &Window::slotShowNotOnDisk); a->setText(i18n("Display Images and Videos Not on Disk")); a = actionCollection()->addAction(QString::fromLatin1("findImagesWithInvalidDate"), this, &Window::slotShowImagesWithInvalidDate); a->setText(i18n("Display Images and Videos with Incomplete Dates...")); #ifdef DOES_STILL_NOT_WORK_IN_KPA4 a = actionCollection()->addAction(QString::fromLatin1("findImagesWithChangedMD5Sum"), this, SLOT(slotShowImagesWithChangedMD5Sum())); a->setText(i18n("Display Images and Videos with Changed MD5 Sum")); #endif //DOES_STILL_NOT_WORK_IN_KPA4 a = actionCollection()->addAction(QLatin1String("mergeDuplicates"), this, &Window::mergeDuplicates); a->setText(i18n("Merge duplicates")); a = actionCollection()->addAction(QString::fromLatin1("rebuildMD5s"), this, &Window::slotRecalcCheckSums); a->setText(i18n("Recalculate Checksum")); a = actionCollection()->addAction(QString::fromLatin1("rescan"), DB::ImageDB::instance(), &DB::ImageDB::slotRescan); a->setIcon(QIcon::fromTheme(QString::fromLatin1("document-import"))); a->setText(i18n("Rescan for Images and Videos")); QAction *recreateExif = actionCollection()->addAction(QString::fromLatin1("recreateExifDB"), this, &Window::slotRecreateExifDB); recreateExif->setText(i18n("Recreate Exif Search Database")); QAction *rereadExif = actionCollection()->addAction(QString::fromLatin1("reReadExifInfo"), this, &Window::slotReReadExifInfo); rereadExif->setText(i18n("Read Exif Info from Files...")); m_sortAllByDateAndTime = actionCollection()->addAction(QString::fromLatin1("sortAllImages"), this, &Window::slotSortAllByDateAndTime); m_sortAllByDateAndTime->setText(i18n("Sort All by Date && Time")); m_sortAllByDateAndTime->setEnabled(true); m_AutoStackImages = actionCollection()->addAction(QString::fromLatin1("autoStack"), this, &Window::slotAutoStackImages); m_AutoStackImages->setText(i18n("Automatically Stack Selected Images...")); a = actionCollection()->addAction(QString::fromLatin1("buildThumbs"), this, &Window::slotBuildThumbnails); a->setText(i18n("Build Thumbnails")); a = actionCollection()->addAction(QString::fromLatin1("statistics"), this, &Window::slotStatistics); a->setText(i18n("Statistics...")); m_markUntagged = actionCollection()->addAction(QString::fromUtf8("markUntagged"), this, &Window::slotMarkUntagged); m_markUntagged->setText(i18n("Mark As Untagged")); // The Settings menu KStandardAction::preferences(this, &Window::slotOptions, actionCollection()); // the default configureShortcuts impl in XMLGuiFactory that is available via setupGUI // does not work for us because we need to add our own (non-XMLGui) actionCollections: KStandardAction::keyBindings(this, &Window::configureShortcuts, actionCollection()); a = actionCollection()->addAction(QString::fromLatin1("readdAllMessages"), this, &Window::slotReenableMessages); a->setText(i18n("Enable All Messages")); m_viewMenu = actionCollection()->add(QString::fromLatin1("configureView")); m_viewMenu->setText(i18n("Configure Current View")); m_viewMenu->setIcon(QIcon::fromTheme(QString::fromLatin1("view-list-details"))); m_viewMenu->setDelayed(false); QActionGroup *viewGrp = new QActionGroup(this); viewGrp->setExclusive(true); m_smallListView = actionCollection()->add(QString::fromLatin1("smallListView"), m_browser, &Browser::BrowserWidget::slotSmallListView); m_smallListView->setText(i18n("Tree")); m_viewMenu->addAction(m_smallListView); m_smallListView->setActionGroup(viewGrp); m_largeListView = actionCollection()->add(QString::fromLatin1("largelistview"), m_browser, &Browser::BrowserWidget::slotLargeListView); m_largeListView->setText(i18n("Tree with User Icons")); m_viewMenu->addAction(m_largeListView); m_largeListView->setActionGroup(viewGrp); m_largeIconView = actionCollection()->add(QString::fromLatin1("largeiconview"), m_browser, &Browser::BrowserWidget::slotLargeIconView); m_largeIconView->setText(i18n("Icons")); m_viewMenu->addAction(m_largeIconView); m_largeIconView->setActionGroup(viewGrp); connect(m_browser, &Browser::BrowserWidget::isViewChangeable, viewGrp, &QActionGroup::setEnabled); connect(m_browser, &Browser::BrowserWidget::currentViewTypeChanged, this, &Window::slotUpdateViewMenu); a = actionCollection()->add(QString::fromLatin1("showToolTipOnImages")); a->setText(i18n("Show Tooltips in Thumbnails Window")); actionCollection()->setDefaultShortcut(a, Qt::CTRL + Qt::Key_T); connect(a, &QAction::toggled, m_thumbnailView, &ThumbnailView::ThumbnailFacade::showToolTipsOnImages); KColorSchemeManager *schemes = new KColorSchemeManager(this); const QString schemePath = Settings::SettingsData::instance()->colorScheme(); const auto schemeCfg = KSharedConfig::openConfig(schemePath); const QString activeSchemeName = schemeCfg->group("General").readEntry("Name", QFileInfo(schemePath).baseName()); m_colorSchemeMenu = schemes->createSchemeSelectionMenu(activeSchemeName, this); m_colorSchemeMenu->setText(i18n("Choose Color Scheme")); m_colorSchemeMenu->setIcon(QIcon::fromTheme(QString::fromLatin1("color"))); m_colorSchemeMenu->setDelayed(false); actionCollection()->addAction(QString::fromLatin1("colorScheme"), m_colorSchemeMenu); // The help menu KStandardAction::tipOfDay(this, &Window::showTipOfDay, actionCollection()); a = actionCollection()->addAction(QString::fromLatin1("runDemo"), this, &Window::runDemo); a->setText(i18n("Run KPhotoAlbum Demo")); a = actionCollection()->addAction(QString::fromLatin1("features"), this, &Window::showFeatures); a->setText(i18n("KPhotoAlbum Feature Status")); a = actionCollection()->addAction(QString::fromLatin1("showVideo"), this, &Window::showVideos); a->setText(i18n("Show Demo Videos")); // Context menu actions m_showExifDialog = actionCollection()->addAction(QString::fromLatin1("showExifInfo"), this, &Window::slotShowExifInfo); m_showExifDialog->setText(i18n("Show Exif Info")); m_recreateThumbnails = actionCollection()->addAction(QString::fromLatin1("recreateThumbnails"), m_thumbnailView, &ThumbnailView::ThumbnailFacade::slotRecreateThumbnail); m_recreateThumbnails->setText(i18n("Recreate Selected Thumbnails")); m_useNextVideoThumbnail = actionCollection()->addAction(QString::fromLatin1("useNextVideoThumbnail"), this, &Window::useNextVideoThumbnail); m_useNextVideoThumbnail->setText(i18n("Use next video thumbnail")); actionCollection()->setDefaultShortcut(m_useNextVideoThumbnail, Qt::CTRL + Qt::Key_Plus); m_usePreviousVideoThumbnail = actionCollection()->addAction(QString::fromLatin1("usePreviousVideoThumbnail"), this, &Window::usePreviousVideoThumbnail); m_usePreviousVideoThumbnail->setText(i18n("Use previous video thumbnail")); actionCollection()->setDefaultShortcut(m_usePreviousVideoThumbnail, Qt::CTRL + Qt::Key_Minus); setupGUI(KXmlGuiWindow::ToolBar | Create | Save); } void MainWindow::Window::slotExportToHTML() { if (!m_htmlDialog) m_htmlDialog = new HTMLGenerator::HTMLDialog(this); m_htmlDialog->exec(selectedOnDisk()); } void MainWindow::Window::startAutoSaveTimer() { int i = Settings::SettingsData::instance()->autoSave(); m_autoSaveTimer->stop(); if (i != 0) { m_autoSaveTimer->start(i * 1000 * 60); } } void MainWindow::Window::slotAutoSave() { if (m_statusBar->mp_dirtyIndicator->isAutoSaveDirty()) { Utilities::ShowBusyCursor dummy; m_statusBar->showMessage(i18n("Auto saving....")); DB::ImageDB::instance()->save(Settings::SettingsData::instance()->imageDirectory() + QString::fromLatin1(".#index.xml"), true); ImageManager::ThumbnailCache::instance()->save(); m_statusBar->showMessage(i18n("Auto saving.... Done"), 5000); m_statusBar->mp_dirtyIndicator->autoSaved(); } } void MainWindow::Window::showThumbNails() { m_statusBar->showThumbnailSlider(); reloadThumbnails(ThumbnailView::ClearSelection); m_stack->setCurrentWidget(m_thumbnailView->gui()); m_thumbnailView->gui()->setFocus(); updateStates(true); } void MainWindow::Window::showBrowser() { m_statusBar->clearMessage(); m_statusBar->hideThumbnailSlider(); m_stack->setCurrentWidget(m_browser); m_browser->setFocus(); updateContextMenuFromSelectionSize(0); updateStates(false); } void MainWindow::Window::slotOptionGroupChanged() { // FIXME: What if annotation dialog is open? (if that's possible) delete m_annotationDialog; m_annotationDialog = nullptr; DirtyIndicator::markDirty(); } void MainWindow::Window::showTipOfDay() { KTipDialog::showTip(this, QString(), true); } void MainWindow::Window::runDemo() { KProcess *process = new KProcess; *process << qApp->applicationFilePath() << QLatin1String("--demo"); process->startDetached(); } bool MainWindow::Window::load() { // Let first try to find a config file. QString configFile; QUrl dbFileUrl = Options::the()->dbFile(); if (!dbFileUrl.isEmpty() && dbFileUrl.isLocalFile()) { configFile = dbFileUrl.toLocalFile(); } else if (Options::the()->demoMode()) { configFile = Utilities::setupDemo(); } else { bool showWelcome = false; KConfigGroup config = KSharedConfig::openConfig()->group(QString::fromUtf8("General")); if (config.hasKey(QString::fromLatin1("imageDBFile"))) { configFile = config.readEntry(QString::fromLatin1("imageDBFile"), QString()); if (!QFileInfo(configFile).exists()) showWelcome = true; } else showWelcome = true; if (showWelcome) { SplashScreen::instance()->hide(); configFile = welcome(); } } if (configFile.isNull()) return false; if (configFile.startsWith(QString::fromLatin1("~"))) configFile = QDir::home().path() + QString::fromLatin1("/") + configFile.mid(1); // To avoid a race conditions where both the image loader thread creates an instance of // Settings, and where the main thread crates an instance, we better get it created now. Settings::SettingsData::setup(QFileInfo(configFile).absolutePath()); if (Settings::SettingsData::instance()->showSplashScreen()) { SplashScreen::instance()->show(); qApp->processEvents(); } // Doing some validation on user provided index file if (Options::the()->dbFile().isValid()) { QFileInfo fi(configFile); if (!fi.dir().exists()) { KMessageBox::error(this, i18n("

Could not open given index.xml as provided directory does not exist.
%1

", fi.absolutePath())); return false; } // We use index.xml as the XML backend, thus we want to test for exactly it fi.setFile(QString::fromLatin1("%1/index.xml").arg(fi.dir().absolutePath())); if (!fi.exists()) { int answer = KMessageBox::questionYesNo(this, i18n("

Given index file does not exist, do you want to create following?" "
%1/index.xml

", fi.absolutePath())); if (answer != KMessageBox::Yes) return false; } configFile = fi.absoluteFilePath(); } DB::ImageDB::setupXMLDB(configFile, *this); // some sanity checks: if (!Settings::SettingsData::instance()->hasUntaggedCategoryFeatureConfigured() && !(Settings::SettingsData::instance()->untaggedCategory().isEmpty() && Settings::SettingsData::instance()->untaggedTag().isEmpty()) && !Options::the()->demoMode()) { KMessageBox::error(this, i18n("

You have configured a tag for untagged images, but either the tag itself " "or its category does not exist in the database.

" "

Please review your untagged tag setting under " "Settings|Configure KPhotoAlbum...|Categories

")); } return true; } void MainWindow::Window::contextMenuEvent(QContextMenuEvent *e) { if (m_stack->currentWidget() == m_thumbnailView->gui()) { QMenu menu(this); menu.addAction(m_configOneAtATime); menu.addAction(m_configAllSimultaniously); menu.addSeparator(); menu.addAction(m_createImageStack); menu.addAction(m_unStackImages); menu.addAction(m_setStackHead); menu.addSeparator(); menu.addAction(m_runSlideShow); menu.addAction(m_runRandomSlideShow); menu.addAction(m_showExifDialog); menu.addSeparator(); menu.addAction(m_rotLeft); menu.addAction(m_rotRight); menu.addAction(m_recreateThumbnails); menu.addAction(m_useNextVideoThumbnail); menu.addAction(m_usePreviousVideoThumbnail); m_useNextVideoThumbnail->setEnabled(anyVideosSelected()); m_usePreviousVideoThumbnail->setEnabled(anyVideosSelected()); menu.addSeparator(); menu.addAction(m_view); menu.addAction(m_viewInNewWindow); // "Invoke external program" ExternalPopup externalCommands { &menu }; DB::ImageInfoPtr info = m_thumbnailView->mediaIdUnderCursor().info(); externalCommands.populate(info, selected()); QAction *action = menu.addMenu(&externalCommands); if (!info && selected().isEmpty()) action->setEnabled(false); QUrl selectedFile; if (info) selectedFile = QUrl::fromLocalFile(info->fileName().absolute()); QList allSelectedFiles; for (const QString &selectedPath : selected().toStringList(DB::AbsolutePath)) { allSelectedFiles << QUrl::fromLocalFile(selectedPath); } // "Copy image(s) to ..." CopyPopup copyMenu(&menu, selectedFile, allSelectedFiles, m_lastTarget, CopyPopup::Copy); QAction *copyAction = menu.addMenu(©Menu); if (!info && selected().isEmpty()) { copyAction->setEnabled(false); } // "Link image(s) to ..." CopyPopup linkMenu(&menu, selectedFile, allSelectedFiles, m_lastTarget, CopyPopup::Link); QAction *linkAction = menu.addMenu(&linkMenu); if (!info && selected().isEmpty()) { linkAction->setEnabled(false); } menu.exec(QCursor::pos()); } e->setAccepted(true); } void MainWindow::Window::setDefaultScopePositive() { Settings::SettingsData::instance()->setCurrentLock(m_browser->currentContext(), false); } void MainWindow::Window::setDefaultScopeNegative() { Settings::SettingsData::instance()->setCurrentLock(m_browser->currentContext(), true); } void MainWindow::Window::lockToDefaultScope() { setLocked(true, false); } void MainWindow::Window::unlockFromDefaultScope() { setLocked(false, false); } void MainWindow::Window::setLocked(bool locked, bool force, bool recount) { m_statusBar->setLocked(locked); Settings::SettingsData::instance()->setLocked(locked, force); m_lock->setEnabled(!locked); m_unlock->setEnabled(locked); m_setDefaultPos->setEnabled(!locked); m_setDefaultNeg->setEnabled(!locked); if (recount) m_browser->reload(); } void MainWindow::Window::configureShortcuts() { Viewer::ViewerWidget *viewer = new Viewer::ViewerWidget; // Do not show, this is only used to get a key configuration KShortcutsDialog *dialog = new KShortcutsDialog(); dialog->addCollection(actionCollection(), i18n("General")); dialog->addCollection(viewer->actions(), i18n("Viewer")); -#ifdef HASKIPI - loadKipiPlugins(); - for (const KIPI::PluginLoader::Info *pluginInfo : m_pluginLoader->pluginList()) { - KIPI::Plugin *plugin = pluginInfo->plugin(); - if (plugin) - dialog->addCollection(plugin->actionCollection(), - i18nc("Add 'Plugin' prefix so that KIPI plugins are obvious in KShortcutsDialog…", "Plugin: %1", pluginInfo->name())); - } -#endif - createAnnotationDialog(); dialog->addCollection(m_annotationDialog->actions(), i18n("Annotation Dialog")); dialog->addCollection(m_filterWidget->actions(), i18n("Thumbnail View")); dialog->configure(); delete dialog; delete viewer; } void MainWindow::Window::slotSetFileName(const DB::FileName &fileName) { ImageInfoPtr info; if (fileName.isNull()) m_statusBar->clearMessage(); else { info = fileName.info(); if (info != ImageInfoPtr(nullptr)) m_statusBar->showMessage(fileName.absolute(), 4000); } } void MainWindow::Window::updateContextMenuFromSelectionSize(int selectionSize) { m_configAllSimultaniously->setEnabled(selectionSize > 1); m_configOneAtATime->setEnabled(selectionSize >= 1); m_createImageStack->setEnabled(selectionSize > 1); m_unStackImages->setEnabled(selectionSize >= 1); m_setStackHead->setEnabled(selectionSize == 1); // FIXME: do we want to check if it's stacked here? m_sortByDateAndTime->setEnabled(selectionSize > 1); m_recreateThumbnails->setEnabled(selectionSize >= 1); m_rotLeft->setEnabled(selectionSize >= 1); m_rotRight->setEnabled(selectionSize >= 1); m_AutoStackImages->setEnabled(selectionSize > 1); m_markUntagged->setEnabled(selectionSize >= 1); m_statusBar->mp_selected->setSelectionCount(selectionSize); m_clearSelection->setEnabled(selectionSize > 0); } void MainWindow::Window::rotateSelected(int angle) { const DB::FileNameList list = selected(); if (list.isEmpty()) { KMessageBox::sorry(this, i18n("No item is selected."), i18n("No Selection")); } else { for (const DB::FileName &fileName : list) { fileName.info()->rotate(angle); ImageManager::ThumbnailCache::instance()->removeThumbnail(fileName); } m_statusBar->mp_dirtyIndicator->markDirty(); } } void MainWindow::Window::slotRotateSelectedLeft() { rotateSelected(-90); reloadThumbnails(); } void MainWindow::Window::slotRotateSelectedRight() { rotateSelected(90); reloadThumbnails(); } void MainWindow::Window::reloadThumbnails(ThumbnailView::SelectionUpdateMethod method) { m_thumbnailView->reload(method); updateContextMenuFromSelectionSize(m_thumbnailView->selection().size()); } void MainWindow::Window::slotUpdateViewMenu(DB::Category::ViewType type) { if (type == DB::Category::TreeView) m_smallListView->setChecked(true); else if (type == DB::Category::ThumbedTreeView) m_largeListView->setChecked(true); else if (type == DB::Category::ThumbedIconView) m_largeIconView->setChecked(true); } void MainWindow::Window::slotShowNotOnDisk() { DB::FileNameList notOnDisk; const auto allImages = DB::ImageDB::instance()->images(); for (const DB::FileName &fileName : allImages) { if (!fileName.exists()) notOnDisk.append(fileName); } showThumbNails(notOnDisk); } void MainWindow::Window::slotShowImagesWithChangedMD5Sum() { #ifdef DOES_STILL_NOT_WORK_IN_KPA4 Utilities::ShowBusyCursor dummy; StringSet changed = DB::ImageDB::instance()->imagesWithMD5Changed(); showThumbNails(changed.toList()); #else // DOES_STILL_NOT_WORK_IN_KPA4 qFatal("Code commented out in MainWindow::Window::slotShowImagesWithChangedMD5Sum"); #endif // DOES_STILL_NOT_WORK_IN_KPA4 } void MainWindow::Window::updateStates(bool thumbNailView) { m_selectAll->setEnabled(thumbNailView); m_deleteSelected->setEnabled(thumbNailView); m_limitToMarked->setEnabled(thumbNailView); m_jumpToContext->setEnabled(thumbNailView); } void MainWindow::Window::slotRunSlideShow() { slotView(true, true); } void MainWindow::Window::slotRunRandomizedSlideShow() { slotView(true, true, true); } MainWindow::Window *MainWindow::Window::theMainWindow() { Q_ASSERT(s_instance); return s_instance; } void MainWindow::Window::slotImport() { ImportExport::Import::imageImport(); } void MainWindow::Window::slotExport() { ImportExport::Export::imageExport(selectedOnDisk()); } void MainWindow::Window::slotReenableMessages() { int ret = KMessageBox::questionYesNo(this, i18n("

Really enable all message boxes where you previously " "checked the do-not-show-again check box?

")); if (ret == KMessageBox::Yes) KMessageBox::enableAllMessages(); } void MainWindow::Window::setupPluginMenu() { QMenu *menu = findChild(QString::fromLatin1("plugins")); if (!menu) { KMessageBox::error(this, i18n("

KPhotoAlbum hit an internal error (missing plug-in menu in MainWindow::Window::setupPluginMenu). This indicate that you forgot to do a make install. If you did compile KPhotoAlbum yourself, then please run make install. If not, please report this as a bug.

KPhotoAlbum will continue execution, but it is not entirely unlikely that it will crash later on due to the missing make install.

"), i18n("Internal Error")); - m_hasLoadedKipiPlugins = true; return; // This is no good, but lets try and continue. } #ifdef KF5Purpose_FOUND Plugins::PurposeMenu *purposeMenu = new Plugins::PurposeMenu(menu); connect(m_thumbnailView, &ThumbnailView::ThumbnailFacade::selectionChanged, purposeMenu, &Plugins::PurposeMenu::slotSelectionChanged); connect(purposeMenu, &Plugins::PurposeMenu::imageShared, [this](QUrl shareLocation) { QString message; if (shareLocation.isValid()) { message = i18n("Successfully shared image(s). Copying location to clipboard..."); QGuiApplication::clipboard()->setText(shareLocation.toString()); } else { message = i18n("Successfully shared image(s)."); } m_statusBar->showMessage(message); }); connect(purposeMenu, &Plugins::PurposeMenu::imageSharingFailed, [this](QString errorMessage) { QString message = i18n("Image sharing failed with message: %1", errorMessage); m_statusBar->showMessage(message); }); #endif - -#ifdef HASKIPI - connect(menu, &QMenu::aboutToShow, this, &Window::loadKipiPlugins); - m_hasLoadedKipiPlugins = false; -#else - setPluginMenuState("kipiplugins", {}); -#ifndef KF5Purpose_FOUND - menu->setEnabled(false); -#endif - m_hasLoadedKipiPlugins = true; -#endif -} - -void MainWindow::Window::loadKipiPlugins() -{ -#ifdef HASKIPI - Utilities::ShowBusyCursor dummy; - if (m_hasLoadedKipiPlugins) - return; - - m_pluginInterface = new Plugins::Interface(this, QString::fromLatin1("KPhotoAlbum kipi interface")); - connect(m_pluginInterface, &Plugins::Interface::imagesChanged, this, &Window::slotImagesChanged); - - QStringList ignores; - ignores << QString::fromLatin1("CommentsEditor") - << QString::fromLatin1("HelloWorld"); - - m_pluginLoader = new KIPI::PluginLoader(); - m_pluginLoader->setIgnoredPluginsList(ignores); - m_pluginLoader->setInterface(m_pluginInterface); - m_pluginLoader->init(); - connect(m_pluginLoader, &KIPI::PluginLoader::replug, this, &Window::plug); - m_pluginLoader->loadPlugins(); - - // Setup signals - connect(m_thumbnailView, &ThumbnailView::ThumbnailFacade::selectionChanged, this, &Window::slotSelectionChanged); - m_hasLoadedKipiPlugins = true; - - // Make sure selection is updated also when plugin loading is - // delayed. This is needed, because selection might already be - // non-empty when loading the plugins. - slotSelectionChanged(selected().size()); -#endif // HASKIPI -} - -void MainWindow::Window::plug() -{ -#ifdef HASKIPI - unplugActionList(QString::fromLatin1("kipi_actions")); - - QList kipiActions; - - KIPI::PluginLoader::PluginList list = m_pluginLoader->pluginList(); - for (const KIPI::PluginLoader::Info *pluginInfo : list) { - KIPI::Plugin *plugin = pluginInfo->plugin(); - if (!plugin || !pluginInfo->shouldLoad()) - continue; - - plugin->setup(this); - - QList actions = plugin->actions(); - for (QAction *action : actions) { - kipiActions.append(action); - } - KConfigGroup group = KSharedConfig::openConfig()->group(QString::fromLatin1("Shortcuts")); - plugin->actionCollection()->importGlobalShortcuts(&group); - } - - setPluginMenuState("kipiplugins", kipiActions); - - plugActionList(QString::fromLatin1("kipi_actions"), kipiActions); -#endif } void MainWindow::Window::setPluginMenuState(const char *name, const QList &actions) { QMenu *menu = findChild(QString::fromLatin1(name)); if (menu) menu->setEnabled(actions.count() != 0); } void MainWindow::Window::slotImagesChanged(const QList &urls) { for (QList::ConstIterator it = urls.begin(); it != urls.end(); ++it) { DB::FileName fileName = DB::FileName::fromAbsolutePath((*it).path()); if (!fileName.isNull()) { // Plugins may report images outsite of the photodatabase // This seems to be the case with the border image plugin, which reports the destination image ImageManager::ThumbnailCache::instance()->removeThumbnail(fileName); // update MD5sum: MD5 md5sum = MD5Sum(fileName); fileName.info()->setMD5Sum(md5sum); } } m_statusBar->mp_dirtyIndicator->markDirty(); reloadThumbnails(ThumbnailView::MaintainSelection); } DB::ImageSearchInfo MainWindow::Window::currentContext() { return m_browser->currentContext(); } QString MainWindow::Window::currentBrowseCategory() const { return m_browser->currentCategory(); } -void MainWindow::Window::slotSelectionChanged(int count) -{ -#ifdef HASKIPI - m_pluginInterface->slotSelectionChanged(count != 0); -#else - Q_UNUSED(count) -#endif -} - void MainWindow::Window::resizeEvent(QResizeEvent *) { if (Settings::SettingsData::ready() && isVisible()) Settings::SettingsData::instance()->setWindowGeometry(Settings::MainWindow, geometry()); } void MainWindow::Window::moveEvent(QMoveEvent *) { if (Settings::SettingsData::ready() && isVisible()) Settings::SettingsData::instance()->setWindowGeometry(Settings::MainWindow, geometry()); } void MainWindow::Window::slotRemoveTokens() { if (!m_tokenEditor) m_tokenEditor = new TokenEditor(this); m_tokenEditor->show(); connect(m_tokenEditor, &TokenEditor::finished, m_browser, &Browser::BrowserWidget::go); } void MainWindow::Window::slotShowListOfFiles() { QStringList list = QInputDialog::getMultiLineText(this, i18n("Open List of Files"), i18n("You can open a set of files from KPhotoAlbum's image root by listing the files here.")) .split(QChar::fromLatin1('\n'), QString::SkipEmptyParts); if (list.isEmpty()) return; DB::FileNameList out; for (QStringList::const_iterator it = list.constBegin(); it != list.constEnd(); ++it) { QString fileNameStr = Utilities::imageFileNameToAbsolute(*it); if (fileNameStr.isNull()) continue; const DB::FileName fileName = DB::FileName::fromAbsolutePath(fileNameStr); if (!fileName.isNull()) out.append(fileName); } if (out.isEmpty()) KMessageBox::sorry(this, i18n("No images matching your input were found."), i18n("No Matches")); else showThumbNails(out); } void MainWindow::Window::updateDateBar(const Browser::BreadcrumbList &path) { static QString lastPath = QString::fromLatin1("ThisStringShouldNeverBeSeenSoWeUseItAsInitialContent"); if (path.toString() != lastPath) updateDateBar(); lastPath = path.toString(); } void MainWindow::Window::updateDateBar() { m_dateBar->setImageDateCollection(DB::ImageDB::instance()->rangeCollection()); } void MainWindow::Window::slotShowImagesWithInvalidDate() { QPointer finder = new InvalidDateFinder(this); if (finder->exec() == QDialog::Accepted) showThumbNails(); delete finder; } void MainWindow::Window::showDateBarTip(const QString &msg) { m_statusBar->showMessage(msg, 3000); } void MainWindow::Window::slotJumpToContext() { const DB::FileName fileName = m_thumbnailView->currentItem(); if (!fileName.isNull()) { m_browser->addImageView(fileName); } } void MainWindow::Window::setDateRange(const DB::ImageDate &range) { DB::ImageDB::instance()->setDateRange(range, m_dateBar->includeFuzzyCounts()); m_statusBar->mp_partial->showBrowserMatches(this->selected().size()); m_browser->reload(); reloadThumbnails(ThumbnailView::MaintainSelection); } void MainWindow::Window::clearDateRange() { DB::ImageDB::instance()->clearDateRange(); m_browser->reload(); reloadThumbnails(ThumbnailView::MaintainSelection); } void MainWindow::Window::showThumbNails(const DB::FileNameList &items) { m_thumbnailView->setImageList(items); m_statusBar->mp_partial->setMatchCount(items.size()); showThumbNails(); } void MainWindow::Window::slotRecalcCheckSums() { DB::ImageDB::instance()->slotRecalcCheckSums(selected()); } void MainWindow::Window::slotShowExifInfo() { DB::FileNameList items = selectedOnDisk(); if (!items.isEmpty()) { Exif::InfoDialog *exifDialog = new Exif::InfoDialog(items.at(0), this); exifDialog->show(); } } void MainWindow::Window::showFeatures() { FeatureDialog dialog(this); dialog.exec(); } void MainWindow::Window::showImage(const DB::FileName &fileName) { launchViewer(DB::FileNameList() << fileName, true, false, false); } void MainWindow::Window::slotBuildThumbnails() { ImageManager::ThumbnailBuilder::instance()->buildAll(ImageManager::StartNow); } void MainWindow::Window::slotBuildThumbnailsIfWanted() { ImageManager::ThumbnailCache::instance()->flush(); if (!Settings::SettingsData::instance()->incrementalThumbnails()) ImageManager::ThumbnailBuilder::instance()->buildAll(ImageManager::StartDelayed); } void MainWindow::Window::slotOrderIncr() { m_thumbnailView->setSortDirection(ThumbnailView::OldestFirst); } void MainWindow::Window::slotOrderDecr() { m_thumbnailView->setSortDirection(ThumbnailView::NewestFirst); } void MainWindow::Window::showVideos() { QDesktopServices::openUrl(QUrl( QStringLiteral("https://www.kphotoalbum.org/documentation/videos/"))); } void MainWindow::Window::slotStatistics() { static StatisticsDialog *dialog = new StatisticsDialog(this); dialog->show(); } void MainWindow::Window::slotMarkUntagged() { if (Settings::SettingsData::instance()->hasUntaggedCategoryFeatureConfigured()) { for (const DB::FileName &newFile : selected()) { newFile.info()->addCategoryInfo(Settings::SettingsData::instance()->untaggedCategory(), Settings::SettingsData::instance()->untaggedTag()); } DirtyIndicator::markDirty(); } else { // Note: the same dialog text is used in // Browser::OverviewPage::activateUntaggedImagesAction(), // so if it is changed, be sure to also change it there! KMessageBox::information(this, i18n("

You have not yet configured which tag to use for indicating untagged images." "

" "

Please follow these steps to do so:" "

  • In the menu bar choose Settings
  • " "
  • From there choose Configure KPhotoAlbum
  • " "
  • Now choose the Categories icon
  • " "
  • Now configure section Untagged Images

"), i18n("Feature has not been configured")); } } void MainWindow::Window::setupStatusBar() { m_statusBar = new MainWindow::StatusBar; setStatusBar(m_statusBar); setLocked(Settings::SettingsData::instance()->locked(), true, false); connect(m_statusBar, &StatusBar::thumbnailSettingsRequested, [this]() { this->slotOptions(); m_settingsDialog->activatePage(Settings::SettingsPage::ThumbnailsPage); }); } void MainWindow::Window::slotRecreateExifDB() { Exif::Database::instance()->recreate(); } void MainWindow::Window::useNextVideoThumbnail() { UpdateVideoThumbnail::useNext(selected()); } void MainWindow::Window::usePreviousVideoThumbnail() { UpdateVideoThumbnail::usePrevious(selected()); } void MainWindow::Window::mergeDuplicates() { DuplicateMerger *merger = new DuplicateMerger; merger->show(); } void MainWindow::Window::slotThumbnailSizeChanged() { QString thumbnailSizeMsg = i18nc("@info:status", //xgettext:no-c-format "Thumbnail width: %1px (storage size: %2px)", Settings::SettingsData::instance()->actualThumbnailSize(), Settings::SettingsData::instance()->thumbnailSize()); m_statusBar->showMessage(thumbnailSizeMsg, 4000); } void MainWindow::Window::createSearchBar() { // Set up the search tool bar SearchBar *searchBar = new SearchBar(this); searchBar->setLineEditEnabled(false); searchBar->setObjectName(QString::fromUtf8("searchBar")); connect(searchBar, &SearchBar::textChanged, m_browser, &Browser::BrowserWidget::slotLimitToMatch); connect(searchBar, &SearchBar::returnPressed, m_browser, &Browser::BrowserWidget::slotInvokeSeleted); connect(searchBar, &SearchBar::keyPressed, m_browser, &Browser::BrowserWidget::scrollKeyPressed); connect(m_browser, &Browser::BrowserWidget::viewChanged, searchBar, &SearchBar::reset); connect(m_browser, &Browser::BrowserWidget::isSearchable, searchBar, &SearchBar::setLineEditEnabled); m_filterWidget = m_thumbnailView->createFilterWidget(this); addToolBar(m_filterWidget); m_filterWidget->setObjectName(QString::fromUtf8("filterBar")); connect(m_browser, &Browser::BrowserWidget::viewChanged, ThumbnailView::ThumbnailFacade::instance(), &ThumbnailView::ThumbnailFacade::clearFilter); connect(m_browser, &Browser::BrowserWidget::isFilterable, m_filterWidget, &ThumbnailView::FilterWidget::setEnabled); } void MainWindow::Window::executeStartupActions() { new ImageManager::ThumbnailBuilder(m_statusBar, this); if (!Settings::SettingsData::instance()->incrementalThumbnails()) ImageManager::ThumbnailBuilder::instance()->buildMissing(); connect(Settings::SettingsData::instance(), &Settings::SettingsData::thumbnailSizeChanged, this, &Window::slotBuildThumbnailsIfWanted); if (!FeatureDialog::hasVideoThumbnailer()) { BackgroundTaskManager::JobManager::instance()->addJob( new BackgroundJobs::SearchForVideosWithoutLengthInfo); BackgroundTaskManager::JobManager::instance()->addJob( new BackgroundJobs::SearchForVideosWithoutVideoThumbnailsJob); } } void MainWindow::Window::checkIfVideoThumbnailerIsInstalled() { if (Options::the()->demoMode()) return; if (!FeatureDialog::hasVideoThumbnailer()) { KMessageBox::information(this, i18n("

Unable to find ffmpeg on the system.

" "

Without it, KPhotoAlbum will not be able to display video thumbnails and video lengths. " "Please install the ffmpeg package

"), i18n("Video thumbnails are not available"), QString::fromLatin1("VideoThumbnailerNotInstalled")); } } bool MainWindow::Window::anyVideosSelected() const { const auto selectedFiles = selected(); for (const DB::FileName &fileName : selectedFiles) { if (Utilities::isVideo(fileName)) return true; } return false; } void MainWindow::Window::setHistogramVisibilty(bool visible) const { if (visible) { m_dateBar->show(); m_dateBarLine->show(); } else { m_dateBar->hide(); m_dateBarLine->hide(); } } void MainWindow::Window::slotImageRotated(const DB::FileName &fileName) { // An image has been rotated by the annotation dialog or the viewer. // We have to reload the respective thumbnail to get it in the right angle ImageManager::ThumbnailCache::instance()->removeThumbnail(fileName); } bool MainWindow::Window::dbIsDirty() const { return m_statusBar->mp_dirtyIndicator->isSaveDirty(); } #ifdef HAVE_MARBLE void MainWindow::Window::showPositionBrowser() { auto positionBrowser = positionBrowserWidget(); m_stack->setCurrentWidget(positionBrowser); updateStates(false); } Map::MapView *MainWindow::Window::positionBrowserWidget() { if (!m_positionBrowser) { m_positionBrowser = new Map::MapView(m_stack, Map::UsageType::InlineMapView); m_stack->addWidget(m_positionBrowser); } return m_positionBrowser; } #endif UserFeedback MainWindow::Window::askWarningContinueCancel(const QString &msg, const QString &title, const QString &dialogId) { auto answer = KMessageBox::warningContinueCancel(this, msg, title, KStandardGuiItem::cont(), KStandardGuiItem::cancel(), dialogId); return (answer == KMessageBox::Continue) ? UserFeedback::Confirm : UserFeedback::Deny; } UserFeedback MainWindow::Window::askQuestionYesNo(const QString &msg, const QString &title, const QString &dialogId) { auto answer = KMessageBox::questionYesNo(this, msg, title, KStandardGuiItem::yes(), KStandardGuiItem::no(), dialogId); return (answer == KMessageBox::Yes) ? UserFeedback::Confirm : UserFeedback::Deny; } void MainWindow::Window::showInformation(const QString &msg, const QString &title, const QString &dialogId) { KMessageBox::information(this, msg, title, dialogId); } void MainWindow::Window::showSorry(const QString &msg, const QString &title, const QString &) { KMessageBox::sorry(this, msg, title); } void MainWindow::Window::showError(const QString &msg, const QString &title, const QString &) { KMessageBox::error(this, msg, title); } bool MainWindow::Window::isDialogDisabled(const QString &dialogId) { // Note(jzarl): there are different methods for different kinds of dialogs. // However, all these methods share exactly the same code in KMessageBox. // If that ever changes, we can still update our implementation - until then I won't just copy a stupid API... return !KMessageBox::shouldBeShownContinue(dialogId); } // vi:expandtab:tabstop=4 shiftwidth=4: diff --git a/MainWindow/Window.h b/MainWindow/Window.h index 5c46f349..f8f861e2 100644 --- a/MainWindow/Window.h +++ b/MainWindow/Window.h @@ -1,312 +1,299 @@ /* Copyright (C) 2003-2020 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 MAINWINDOW_WINDOW_H #define MAINWINDOW_WINDOW_H #include #include #include #include #include #include #include #include #include #include -#include +#include class QAction; class QCloseEvent; class QContextMenuEvent; class QFrame; class QLabel; class QMoveEvent; class QResizeEvent; class QStackedWidget; class QTimer; class KActionMenu; class KTipDialog; class KToggleAction; -#ifdef HASKIPI -namespace KIPI -{ -class PluginLoader; -} -#endif #ifdef HAVE_MARBLE namespace Map { class MapView; } #endif namespace AnnotationDialog { class Dialog; } namespace Browser { class BrowserWidget; class BreadcrumbList; } namespace DateBar { class DateBarWidget; } namespace DB { class ImageInfoList; } namespace HTMLGenerator { class HTMLDialog; } namespace Plugins { class Interface; } namespace Settings { class SettingsDialog; } namespace ThumbnailView { class ThumbnailFacade; class FilterWidget; } class BreadcrumbViewer; namespace MainWindow { class DeleteDialog; class StatusBar; class TokenEditor; class Window : public KXmlGuiWindow, public DB::UIDelegate { Q_OBJECT public: explicit Window(QWidget *parent); ~Window() override; static void configureImages(const DB::ImageInfoList &list, bool oneAtATime); static Window *theMainWindow(); DB::FileNameList selected(ThumbnailView::SelectionMode mode = ThumbnailView::ExpandCollapsedStacks) const; DB::ImageSearchInfo currentContext(); QString currentBrowseCategory() const; void setStackHead(const DB::FileName &image); void setHistogramVisibilty(bool visible) const; bool dbIsDirty() const; #ifdef HAVE_MARBLE void showPositionBrowser(); Map::MapView *positionBrowserWidget(); #endif // implement UI delegate interface // Note(jzarl): we just could create a UIDelegate class that takes a QWidget, // implementing the same messageParent approach that we took before. // For now, I don't see anything wrong with directly implementing the interface instead. // I may change my mind later and I'm ready to convinced of the errors of my way, though... DB::UserFeedback askWarningContinueCancel(const QString &msg, const QString &title, const QString &dialogId) override; DB::UserFeedback askQuestionYesNo(const QString &msg, const QString &title, const QString &dialogId) override; void showInformation(const QString &msg, const QString &title, const QString &dialogId) override; void showSorry(const QString &msg, const QString &title, const QString &) override; void showError(const QString &msg, const QString &title, const QString &) override; bool isDialogDisabled(const QString &dialogId) override; public slots: void showThumbNails(const DB::FileNameList &items); - void loadKipiPlugins(); void reloadThumbnails(ThumbnailView::SelectionUpdateMethod method = ThumbnailView::MaintainSelection); void runDemo(); void slotImageRotated(const DB::FileName &fileName); void slotSave(); protected slots: void showThumbNails(); bool slotExit(); void slotOptions(); void slotConfigureAllImages(); void slotConfigureImagesOneAtATime(); void slotCreateImageStack(); void slotUnStackImages(); void slotSetStackHead(); void slotCopySelectedURLs(); void slotPasteInformation(); void slotDeleteSelected(); void slotReReadExifInfo(); void slotAutoStackImages(); void slotSearch(); // FIXME(jzarl): improve this function signature: void slotView(bool reuse, bool slideShow = false, bool random = false); void slotView(); void slotViewNewWindow(); void slotSortByDateAndTime(); void slotSortAllByDateAndTime(); void slotLimitToSelected(); void slotExportToHTML(); void slotAutoSave(); void showBrowser(); void slotOptionGroupChanged(); void showTipOfDay(); void lockToDefaultScope(); void setDefaultScopePositive(); void setDefaultScopeNegative(); void unlockFromDefaultScope(); void configureShortcuts(); void slotSetFileName(const DB::FileName &); void updateContextMenuFromSelectionSize(int selectionSize); void slotUpdateViewMenu(DB::Category::ViewType); void slotShowNotOnDisk(); void slotBuildThumbnails(); void slotBuildThumbnailsIfWanted(); void slotRunSlideShow(); void slotRunRandomizedSlideShow(); void slotImport(); void slotExport(); void delayedInit(); void slotReenableMessages(); void slotImagesChanged(const QList &); - void slotSelectionChanged(int count); - void plug(); void slotRemoveTokens(); void slotShowListOfFiles(); void updateDateBar(const Browser::BreadcrumbList &); void updateDateBar(); void slotShowImagesWithInvalidDate(); void slotShowImagesWithChangedMD5Sum(); void showDateBarTip(const QString &); void slotJumpToContext(); void setDateRange(const DB::ImageDate &); void clearDateRange(); void startAutoSaveTimer(); void slotRecalcCheckSums(); void slotShowExifInfo(); void showFeatures(); void showImage(const DB::FileName &fileName); void slotOrderIncr(); void slotOrderDecr(); void slotRotateSelectedLeft(); void slotRotateSelectedRight(); void rotateSelected(int angle); void showVideos(); void slotStatistics(); void slotRecreateExifDB(); void useNextVideoThumbnail(); void usePreviousVideoThumbnail(); void mergeDuplicates(); void slotThumbnailSizeChanged(); void slotMarkUntagged(); protected: void configureImages(bool oneAtATime); QString welcome(); bool event(QEvent *event) override; void closeEvent(QCloseEvent *e) override; void resizeEvent(QResizeEvent *) override; void moveEvent(QMoveEvent *) override; void setupMenuBar(); void createAnnotationDialog(); bool load(); void contextMenuEvent(QContextMenuEvent *e) override; void setLocked(bool b, bool force, bool recount = true); void configImages(const DB::ImageInfoList &list, bool oneAtATime); void updateStates(bool thumbNailView); DB::FileNameList selectedOnDisk(); void setupPluginMenu(); void launchViewer(const DB::FileNameList &mediaList, bool reuse, bool slideShow, bool random); void setupStatusBar(); void setPluginMenuState(const char *name, const QList &actions); void createSearchBar(); void executeStartupActions(); void checkIfVideoThumbnailerIsInstalled(); bool anyVideosSelected() const; private: static Window *s_instance; ThumbnailView::ThumbnailFacade *m_thumbnailView; Settings::SettingsDialog *m_settingsDialog; QPointer m_annotationDialog; QStackedWidget *m_stack; QTimer *m_autoSaveTimer; Browser::BrowserWidget *m_browser; DeleteDialog *m_deleteDialog; QAction *m_lock; QAction *m_unlock; QAction *m_setDefaultPos; QAction *m_setDefaultNeg; QAction *m_jumpToContext; HTMLGenerator::HTMLDialog *m_htmlDialog; QAction *m_configOneAtATime; QAction *m_configAllSimultaniously; QAction *m_createImageStack; QAction *m_unStackImages; QAction *m_setStackHead; QAction *m_view; QAction *m_rotLeft; QAction *m_rotRight; QAction *m_sortByDateAndTime; QAction *m_sortAllByDateAndTime; QAction *m_AutoStackImages; QAction *m_viewInNewWindow; KActionMenu *m_viewMenu; KToggleAction *m_smallListView; KToggleAction *m_largeListView; KToggleAction *m_largeIconView; KActionMenu *m_colorSchemeMenu; QAction *m_generateHtml; QAction *m_copy; QAction *m_paste; QAction *m_deleteSelected; QAction *m_limitToMarked; QAction *m_selectAll; QAction *m_clearSelection; QAction *m_runSlideShow; QAction *m_runRandomSlideShow; Plugins::Interface *m_pluginInterface; QAction *m_showExifDialog; -#ifdef HASKIPI - KIPI::PluginLoader *m_pluginLoader; -#endif QAction *m_recreateThumbnails; QAction *m_useNextVideoThumbnail; QAction *m_usePreviousVideoThumbnail; QAction *m_markUntagged; TokenEditor *m_tokenEditor; DateBar::DateBarWidget *m_dateBar; QFrame *m_dateBarLine; - bool m_hasLoadedKipiPlugins; QMap> m_viewerInputMacros; MainWindow::StatusBar *m_statusBar; QString m_lastTarget; #ifdef HAVE_MARBLE Map::MapView *m_positionBrowser; #endif ThumbnailView::FilterWidget *m_filterWidget; }; } #endif /* MAINWINDOW_WINDOW_H */ // vi:expandtab:tabstop=4 shiftwidth=4: diff --git a/Plugins/CategoryImageCollection.cpp b/Plugins/CategoryImageCollection.cpp deleted file mode 100644 index 4f172fa2..00000000 --- a/Plugins/CategoryImageCollection.cpp +++ /dev/null @@ -1,48 +0,0 @@ -/* Copyright (C) 2003-2010 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 "CategoryImageCollection.h" - -#include - -#include -Plugins::CategoryImageCollection::CategoryImageCollection(const DB::ImageSearchInfo &context, const QString &category, - const QString &value) - : Plugins::ImageCollection(CategoryImageCollection::SubClass) - , m_context(context) - , m_category(category) - , m_value(value) -{ -} - -QString Plugins::CategoryImageCollection::name() -{ - if (m_value == QString::fromLatin1("**NONE**")) - return i18nc("The 'name' of an unnamed image collection.", "None"); - else - return m_value; -} - -QList Plugins::CategoryImageCollection::images() -{ - DB::ImageSearchInfo context(m_context); - context.addAnd(m_category, m_value); - QStringList list = DB::ImageDB::instance()->search(context, true).toStringList(DB::AbsolutePath); - return stringListToUrlList(list); -} -// vi:expandtab:tabstop=4 shiftwidth=4: diff --git a/Plugins/CategoryImageCollection.h b/Plugins/CategoryImageCollection.h deleted file mode 100644 index 9d5fb995..00000000 --- a/Plugins/CategoryImageCollection.h +++ /dev/null @@ -1,48 +0,0 @@ -/* Copyright (C) 2003-2010 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. -*/ - -#ifndef CATEGORYIMAGECOLLECTION_H -#define CATEGORYIMAGECOLLECTION_H - -#include "ImageCollection.h" - -#include - -#include -namespace Plugins -{ - -class CategoryImageCollection : public Plugins::ImageCollection -{ - -public: - CategoryImageCollection(const DB::ImageSearchInfo &context, const QString &category, const QString &value); - - QString name() override; - QList images() override; - -private: - DB::ImageSearchInfo m_context; - const QString m_category; - const QString m_value; -}; -} - -#endif /* CATEGORYIMAGECOLLECTION_H */ - -// vi:expandtab:tabstop=4 shiftwidth=4: diff --git a/Plugins/ImageCollection.cpp b/Plugins/ImageCollection.cpp deleted file mode 100644 index 5998dac3..00000000 --- a/Plugins/ImageCollection.cpp +++ /dev/null @@ -1,154 +0,0 @@ -/* Copyright (C) 2003-2010 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 "ImageCollection.h" - -#include "Logging.h" - -#include -#include -#include -#include -#include - -#include -#include - -Plugins::ImageCollection::ImageCollection(Type tp) - : m_type(tp) -{ -} - -QString Plugins::ImageCollection::name() -{ - QString res; - switch (m_type) { - case CurrentAlbum: - res = MainWindow::Window::theMainWindow()->currentContext().toString(); - break; - case CurrentSelection: - res = MainWindow::Window::theMainWindow()->currentContext().toString(); - if (res.isEmpty()) { - res = i18nc("As in 'an unknown set of images, created from the selection'.", "Unknown (Selection)"); - } else { - res += i18nc("As in 'A selection of [a generated context description]'", " (Selection)"); - } - break; - case SubClass: - qCWarning(PluginsLog, "Subclass of ImageCollection should overwrite ImageCollection::name()"); - res = i18nc("A set of images with no description.", "Unknown"); - break; - } - if (res.isEmpty()) { - // at least html export plugin needs a non-empty name: - res = i18nc("The 'name' of an unnamed image collection.", "None"); - } - return res; -} - -QList Plugins::ImageCollection::images() -{ - switch (m_type) { - case CurrentAlbum: - return stringListToUrlList(DB::ImageDB::instance()->currentScope(false).toStringList(DB::AbsolutePath)); - - case CurrentSelection: - return stringListToUrlList(MainWindow::Window::theMainWindow()->selected(ThumbnailView::NoExpandCollapsedStacks).toStringList(DB::AbsolutePath)); - - case SubClass: - qFatal("The subclass should implement images()"); - return QList(); - } - return QList(); -} - -QList Plugins::ImageCollection::imageListToUrlList(const DB::ImageInfoList &imageList) -{ - QList urlList; - for (DB::ImageInfoListConstIterator it = imageList.constBegin(); it != imageList.constEnd(); ++it) { - QUrl url; - url.setPath((*it)->fileName().absolute()); - urlList.append(url); - } - return urlList; -} - -QList Plugins::ImageCollection::stringListToUrlList(const QStringList &list) -{ - QList urlList; - for (QStringList::ConstIterator it = list.begin(); it != list.end(); ++it) { - QUrl url; - url.setPath(*it); - urlList.append(url); - } - return urlList; -} - -QUrl Plugins::ImageCollection::url() -{ - return commonRoot(); -} - -QUrl Plugins::ImageCollection::commonRoot() -{ - QString imgRoot = Settings::SettingsData::instance()->imageDirectory(); - const QList imgs = images(); - if (imgs.count() == 0) - return QUrl::fromLocalFile(imgRoot); - - QStringList res = QFileInfo(imgs[0].path()).absolutePath().split(QLatin1String("/")); - - for (QList::ConstIterator it = imgs.begin(); it != imgs.end(); ++it) { - QStringList newRes; - - QStringList path = QFileInfo((*it).path()).absolutePath().split(QLatin1String("/")); - int i = 0; - for (; i < qMin(path.size(), res.size()); ++i) { - if (path[i] == res[i]) - newRes.append(res[i]); - else - break; - } - res = newRes; - } - - QString result = res.join(QString::fromLatin1("/")); - if (result.left(imgRoot.length()) != imgRoot) { - result = imgRoot; - } - - return QUrl::fromLocalFile(result); -} - -QUrl Plugins::ImageCollection::uploadUrl() -{ - return commonRoot(); -} - -QUrl Plugins::ImageCollection::uploadRootUrl() -{ - QUrl url = QUrl::fromLocalFile(Settings::SettingsData::instance()->imageDirectory()); - return url; -} - -QString Plugins::ImageCollection::uploadRootName() -{ - return i18nc("'Name' of the image directory", "Image/Video root directory"); -} - -// vi:expandtab:tabstop=4 shiftwidth=4: diff --git a/Plugins/ImageCollection.h b/Plugins/ImageCollection.h deleted file mode 100644 index e140d39f..00000000 --- a/Plugins/ImageCollection.h +++ /dev/null @@ -1,63 +0,0 @@ -/* Copyright (C) 2003-2010 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. -*/ - -#ifndef MYIMAGECOLLECTION_H -#define MYIMAGECOLLECTION_H - -#include - -#include -#include - -namespace Plugins -{ - -class ImageCollection : public KIPI::ImageCollectionShared -{ -public: - enum Type { CurrentAlbum, - CurrentSelection, - SubClass }; - - explicit ImageCollection(Type tp); - - QString name() override; - QList images() override; - - // FIXME: url() should not called unless isDirectory() is true - // therefore, we should also to implement isDirectory - QUrl url() override; - QUrl uploadUrl() override; - QUrl uploadRootUrl() override; - QString uploadRootName() override; - // isDirectory - -protected: - QList imageListToUrlList(const DB::ImageInfoList &list); - QList stringListToUrlList(const QStringList &list); - QUrl commonRoot(); - -private: - Type m_type; -}; - -} - -#endif /* MYIMAGECOLLECTION_H */ - -// vi:expandtab:tabstop=4 shiftwidth=4: diff --git a/Plugins/ImageCollectionSelector.cpp b/Plugins/ImageCollectionSelector.cpp deleted file mode 100644 index 011a9e07..00000000 --- a/Plugins/ImageCollectionSelector.cpp +++ /dev/null @@ -1,55 +0,0 @@ -/* Copyright (C) 2003-2010 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 "ImageCollectionSelector.h" - -Plugins::ImageCollectionSelector::ImageCollectionSelector(QWidget *parent, Interface *interface) - : KIPI::ImageCollectionSelector(parent) -{ - m_interface = interface; - m_firstTimeVisible = true; -} - -QList Plugins::ImageCollectionSelector::selectedImageCollections() const -{ - if (m_interface) { - KIPI::ImageCollection collection = m_interface->currentSelection(); - if (!collection.isValid()) { - collection = m_interface->currentAlbum(); - } - if (collection.isValid()) { - QList res; - res.append(collection); - return res; - } - // probably never happens: - return m_interface->allAlbums(); - } - return QList(); -} - -void Plugins::ImageCollectionSelector::showEvent(QShowEvent *event) -{ - KIPI::ImageCollectionSelector::showEvent(event); - if (m_firstTimeVisible) { - // fake one selection change to make HTML Export Plugin believe there really is a selection: - emit selectionChanged(); - m_firstTimeVisible = false; - } -} - -// vi:expandtab:tabstop=4 shiftwidth=4: diff --git a/Plugins/ImageCollectionSelector.h b/Plugins/ImageCollectionSelector.h deleted file mode 100644 index d843959c..00000000 --- a/Plugins/ImageCollectionSelector.h +++ /dev/null @@ -1,60 +0,0 @@ -/* Copyright (C) 2003-2010 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. -*/ -#ifndef MYIMAGECOLLECTIONSELECTOR_H -#define MYIMAGECOLLECTIONSELECTOR_H - -#include "ImageCollection.h" -#include "Interface.h" - -#include - -namespace Plugins -{ -/** This class should provide a widget for selecting one ore more image collection for plugins that want the user to - * select images. - * - * Since selecting images is all kphotoalbum is about ;-), this implementation just passes the images that are (or - * would be) currently visible in thumbnail view - if some of them are selected, only selected ones, otherwise all. - * - * The widget shown is currently empty. - * - * Possible improvements: - * * show some description of the currently selected images instead of just nothing - * * give the user the possibility to group the selected images into image collections by some category: this would be - * useful as i.e. html export plugin uses the names of image collections as headlines and groups the images visually by - * image collection. - */ -class ImageCollectionSelector : public KIPI::ImageCollectionSelector -{ -public: - ImageCollectionSelector(QWidget *parent, Interface *interface); - QList selectedImageCollections() const override; - -protected: - // just fake a selectionChanged event when first shown to make export plugin happy: - void showEvent(QShowEvent *event) override; - -private: - Interface *m_interface; - bool m_firstTimeVisible; -}; - -} - -#endif /* MYIMAGECOLLECTIONSELECTOR_H */ -// vi:expandtab:tabstop=4 shiftwidth=4: diff --git a/Plugins/ImageInfo.cpp b/Plugins/ImageInfo.cpp deleted file mode 100644 index ba32adfa..00000000 --- a/Plugins/ImageInfo.cpp +++ /dev/null @@ -1,369 +0,0 @@ -/* Copyright (C) 2003-2020 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_MARBLE - 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; - const QList categories = DB::ImageDB::instance()->categoryCollection()->categories(); - QStringList tags; - QStringList tagspath; - const QLatin1String sep("/"); - for (const DB::CategoryPtr &category : categories) { - const QString categoryName = category->name(); - if (category->isSpecialCategory()) - continue; - // I don't know why any categories except the above should be excluded - //if ( category->doShow() ) { - const Utilities::StringSet items = m_info->itemsOfCategory(categoryName); - for (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(); - for (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: - for (const QString ¤tTag : qAsConst(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: diff --git a/Plugins/ImageInfo.h b/Plugins/ImageInfo.h deleted file mode 100644 index d2831b2c..00000000 --- a/Plugins/ImageInfo.h +++ /dev/null @@ -1,58 +0,0 @@ -/* Copyright (C) 2003-2010 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. -*/ - -#ifndef MYIMAGEINFO_H -#define MYIMAGEINFO_H - -#include - -#include -#include - -namespace DB -{ -class ImageInfo; -} - -namespace Plugins -{ - -class ImageInfo : public KIPI::ImageInfoShared -{ -public: - ImageInfo(KIPI::Interface *interface, const QUrl &url); - - QMap attributes() override; - void clearAttributes() override; - void addAttributes(const QMap &) override; - void delAttributes(const QStringList &) override; - - void cloneData(ImageInfoShared *const other) override; - -private: - DB::ImageInfoPtr m_info; - - bool isPositionAttribute(const QString &key); - bool isCategoryAttribute(const QString &key); -}; - -} - -#endif /* MYIMAGEINFO_H */ - -// vi:expandtab:tabstop=4 shiftwidth=4: diff --git a/Plugins/Interface.cpp b/Plugins/Interface.cpp deleted file mode 100644 index 7694b8c1..00000000 --- a/Plugins/Interface.cpp +++ /dev/null @@ -1,212 +0,0 @@ -/* 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 "Interface.h" - -#include "CategoryImageCollection.h" -#include "ImageCollection.h" -#include "ImageCollectionSelector.h" -#include "ImageInfo.h" -#include "UploadWidget.h" - -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -namespace KIPI -{ -class UploadWidget; -} - -Plugins::Interface::Interface(QObject *parent, QString name) - : KIPI::Interface(parent, name) -{ - connect(Browser::BrowserWidget::instance(), SIGNAL(pathChanged(Browser::BreadcrumbList)), this, SLOT(pathChanged(Browser::BreadcrumbList))); -} - -KIPI::ImageCollection Plugins::Interface::currentAlbum() -{ - return KIPI::ImageCollection(new Plugins::ImageCollection(Plugins::ImageCollection::CurrentAlbum)); -} - -KIPI::ImageCollection Plugins::Interface::currentSelection() -{ - if (!MainWindow::Window::theMainWindow()->selected().isEmpty()) - return KIPI::ImageCollection(new Plugins::ImageCollection(Plugins::ImageCollection::CurrentSelection)); - else - return KIPI::ImageCollection(nullptr); -} - -QList Plugins::Interface::allAlbums() -{ - QList result; - DB::ImageSearchInfo context = MainWindow::Window::theMainWindow()->currentContext(); - QString category = MainWindow::Window::theMainWindow()->currentBrowseCategory(); - if (category.isNull()) - category = Settings::SettingsData::instance()->albumCategory(); - - QMap categories = DB::ImageDB::instance()->classify(context, category, DB::Image); - - for (auto it = categories.constBegin(); it != categories.constEnd(); ++it) { - auto *col = new CategoryImageCollection(context, category, it.key()); - result.append(KIPI::ImageCollection(col)); - } - - return result; -} - -KIPI::ImageInfo Plugins::Interface::info(const QUrl &url) -{ - return KIPI::ImageInfo(new Plugins::ImageInfo(this, url)); -} - -void Plugins::Interface::refreshImages(const QList &urls) -{ - emit imagesChanged(urls); -} - -int Plugins::Interface::features() const -{ - return KIPI::ImagesHasComments | KIPI::ImagesHasTime | KIPI::HostSupportsDateRanges | KIPI::HostAcceptNewImages | KIPI::ImagesHasTitlesWritable | KIPI::HostSupportsTags | KIPI::HostSupportsRating | KIPI::HostSupportsThumbnails; -} - -QAbstractItemModel *Plugins::Interface::getTagTree() const -{ - DB::ImageSearchInfo matchAll; - DB::CategoryPtr rootCategory; - - // since this is currently used by the geolocation plugin only, try the (localized) "Places" category first: - rootCategory = DB::ImageDB::instance()->categoryCollection()->categoryForName(i18n("Places")); - - // ... if that's not available, return a category that exists: - if (!rootCategory) - rootCategory = DB::ImageDB::instance()->categoryCollection()->categoryForSpecial(DB::Category::TokensCategory); - - return new Browser::TreeCategoryModel(rootCategory, matchAll); -} - -bool Plugins::Interface::addImage(const QUrl &url, QString &errmsg) -{ - const QString dir = url.path(); - const QString root = Settings::SettingsData::instance()->imageDirectory(); - if (!dir.startsWith(root)) { - errmsg = i18n("

Image needs to be placed in a sub directory of your photo album, " - "which is rooted at %1. Image path was %2

", - root, dir); - return false; - } - - DB::ImageInfoPtr info(new DB::ImageInfo(DB::FileName::fromAbsolutePath(dir))); - DB::ImageInfoList list; - list.append(info); - DB::ImageDB::instance()->addImages(list); - return true; -} - -void Plugins::Interface::delImage(const QUrl &url) -{ - DB::ImageInfoPtr info = DB::ImageDB::instance()->info(DB::FileName::fromAbsolutePath(url.path())); - if (info) - DB::ImageDB::instance()->deleteList(DB::FileNameList() << info->fileName()); -} - -void Plugins::Interface::slotSelectionChanged(bool b) -{ - emit selectionChanged(b); -} - -void Plugins::Interface::pathChanged(const Browser::BreadcrumbList &path) -{ - static Browser::BreadcrumbList _path; - if (_path != path) { - emit currentAlbumChanged(true); - _path = path; - } -} - -KIPI::ImageCollectionSelector *Plugins::Interface::imageCollectionSelector(QWidget *parent) -{ - return new ImageCollectionSelector(parent, this); -} - -KIPI::UploadWidget *Plugins::Interface::uploadWidget(QWidget *parent) -{ - return new Plugins::UploadWidget(parent); -} - -void Plugins::Interface::thumbnail(const QUrl &url, int size) -{ - DB::FileName file = DB::FileName::fromAbsolutePath(url.path()); - if (size <= Settings::SettingsData::instance()->thumbnailSize() - && ImageManager::ThumbnailCache::instance()->contains(file)) { - // look up in the cache - QPixmap thumb = ImageManager::ThumbnailCache::instance()->lookup(file); - emit gotThumbnail(url, thumb); - } else { - // for bigger thumbnails, fall back to previewJob: - KFileItem f { url }; - f.setDelayedMimeTypes(true); - KFileItemList fl; - fl.append(f); - KIO::PreviewJob *job = KIO::filePreview(fl, QSize(size, size)); - - connect(job, &KIO::PreviewJob::gotPreview, this, &Interface::gotKDEPreview); - - connect(job, &KIO::PreviewJob::failed, this, &Interface::failedKDEPreview); - } -} - -void Plugins::Interface::thumbnails(const QList &list, int size) -{ - for (const QUrl url : list) - thumbnail(url, size); -} - -KIPI::FileReadWriteLock *Plugins::Interface::createReadWriteLock(const QUrl &) const -{ - return nullptr; -} - -KIPI::MetadataProcessor *Plugins::Interface::createMetadataProcessor() const -{ - return nullptr; -} - -void Plugins::Interface::gotKDEPreview(const KFileItem &item, const QPixmap &pix) -{ - emit gotThumbnail(item.url(), pix); -} - -void Plugins::Interface::failedKDEPreview(const KFileItem &item) -{ - emit gotThumbnail(item.url(), QPixmap()); -} - -// vi:expandtab:tabstop=4 shiftwidth=4: diff --git a/Plugins/Interface.h b/Plugins/Interface.h deleted file mode 100644 index bf68e310..00000000 --- a/Plugins/Interface.h +++ /dev/null @@ -1,90 +0,0 @@ -/* Copyright (C) 2003-2010 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. -*/ - -#ifndef KPHOTOALBUM_PLUGININTERFACE_H -#define KPHOTOALBUM_PLUGININTERFACE_H - -#include -#include -#include -#include -#include -#include -#include -#include - -class QPixmap; -class KFileItem; - -namespace Browser -{ -class BreadcrumbList; -} - -namespace Plugins -{ - -class Interface : public KIPI::Interface -{ - Q_OBJECT - -public: - explicit Interface(QObject *parent, QString name = QString()); - - KIPI::ImageCollection currentAlbum() override; - KIPI::ImageCollection currentSelection() override; - QList allAlbums() override; - - KIPI::ImageInfo info(const QUrl &) override; - bool addImage(const QUrl &, QString &errmsg) override; - void delImage(const QUrl &) override; - void refreshImages(const QList &urls) override; - - void thumbnail(const QUrl &url, int size) override; - void thumbnails(const QList &list, int size) override; - - KIPI::ImageCollectionSelector *imageCollectionSelector(QWidget *parent) override; - KIPI::UploadWidget *uploadWidget(QWidget *parent) override; - QAbstractItemModel *getTagTree() const override; - - // these two methods are only here because of a libkipi api error - // either remove them when they are no longer pure virtual in KIPI::Interface, - // or implement them and update features() accordingly: - // FIXME: this can be safely removed if/when libkipi 5.1.0 is no longer supported - KIPI::FileReadWriteLock *createReadWriteLock(const QUrl &) const override; - KIPI::MetadataProcessor *createMetadataProcessor() const override; - - int features() const override; - -public slots: - void slotSelectionChanged(bool); - void pathChanged(const Browser::BreadcrumbList &path); - -private slots: - void gotKDEPreview(const KFileItem &item, const QPixmap &pix); - void failedKDEPreview(const KFileItem &item); - -signals: - void imagesChanged(const QList &); -}; - -} - -#endif /* PLUGININTERFACE_H */ - -// vi:expandtab:tabstop=4 shiftwidth=4: diff --git a/Plugins/PurposeMenu.h b/Plugins/PurposeMenu.h index e2df01fb..7d465e73 100644 --- a/Plugins/PurposeMenu.h +++ b/Plugins/PurposeMenu.h @@ -1,74 +1,74 @@ /* Copyright (C) 2019-2020 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) 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 KPHOTOALBUM_PURPOSEMENU_H #define KPHOTOALBUM_PURPOSEMENU_H #include #include #include -#include +#include class QMenu; namespace Purpose { class Menu; } namespace Plugins { class PurposeMenu : public Purpose::Menu { Q_OBJECT public: explicit PurposeMenu(QMenu *parent); public slots: void slotSelectionChanged(); signals: /** * @brief imageShared is emitted when an image was shared successfully. * The url contains the optional location of the shared data * (e.g. for plugins that upload to a remote location). */ void imageShared(QUrl); void imageSharingFailed(QString message); private: QMenu *m_parentMenu; bool m_menuUpdateNeeded; ///< Keeps track of changed image selection /** * @brief Load the Purpose::Menu, add it to the parent menu, and set up connections. */ void loadPurposeMenu(); /** * @brief Load Purpose menu items into the Purpose::Menu. * This is dependent on the current set of images. */ void loadPurposeItems(); }; } #endif /* PURPOSEMENU_H */ // vi:expandtab:tabstop=4 shiftwidth=4: diff --git a/Plugins/UploadImageCollection.cpp b/Plugins/UploadImageCollection.cpp deleted file mode 100644 index 236b056e..00000000 --- a/Plugins/UploadImageCollection.cpp +++ /dev/null @@ -1,61 +0,0 @@ -/* Copyright 2012 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) 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 "UploadImageCollection.h" - -#include - -#include - -namespace Plugins -{ - -UploadImageCollection::UploadImageCollection(const QString &path) - : m_path(path) -{ -} - -QList UploadImageCollection::images() -{ - return QList(); -} - -QString UploadImageCollection::name() -{ - return QString(); -} - -QUrl UploadImageCollection::uploadUrl() -{ - return QUrl::fromLocalFile(m_path); -} - -QUrl UploadImageCollection::uploadRootUrl() -{ - QUrl url = QUrl::fromLocalFile(Settings::SettingsData::instance()->imageDirectory()); - return url; -} - -QString UploadImageCollection::uploadRootName() -{ - return i18nc("'Name' of the image directory", "Image/Video root directory"); -} - -} // namespace Plugins -// vi:expandtab:tabstop=4 shiftwidth=4: diff --git a/Plugins/UploadImageCollection.h b/Plugins/UploadImageCollection.h deleted file mode 100644 index 34a27048..00000000 --- a/Plugins/UploadImageCollection.h +++ /dev/null @@ -1,47 +0,0 @@ -/* Copyright 2012 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) 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 PLUGINS_UPLOADIMAGECOLLECTION_H -#define PLUGINS_UPLOADIMAGECOLLECTION_H - -#include - -namespace Plugins -{ - -class UploadImageCollection : public KIPI::ImageCollectionShared -{ -public: - explicit UploadImageCollection(const QString &path); - - QList images() override; - QString name() override; - - QUrl uploadUrl() override; - QUrl uploadRootUrl() override; - QString uploadRootName() override; - -private: - QString m_path; -}; - -} // namespace Plugins - -#endif // PLUGINS_UPLOADIMAGECOLLECTION_H -// vi:expandtab:tabstop=4 shiftwidth=4: diff --git a/Plugins/UploadWidget.cpp b/Plugins/UploadWidget.cpp deleted file mode 100644 index 57a13f40..00000000 --- a/Plugins/UploadWidget.cpp +++ /dev/null @@ -1,61 +0,0 @@ -/* Copyright 2012 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) 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 "UploadWidget.h" - -#include "ImageCollection.h" -#include "UploadImageCollection.h" - -#include - -#include -#include -#include - -namespace Plugins -{ - -UploadWidget::UploadWidget(QWidget *parent) - : KIPI::UploadWidget(parent) -{ - QTreeView *listView = new QTreeView(this); - QHBoxLayout *layout = new QHBoxLayout(this); - layout->addWidget(listView); - - m_model = new QFileSystemModel(this); - m_model->setFilter(QDir::Dirs | QDir::NoDotDot); - listView->setModel(m_model); - m_path = Settings::SettingsData::instance()->imageDirectory(); - const QModelIndex index = m_model->setRootPath(m_path); - listView->setRootIndex(index); - connect(listView, &QTreeView::activated, this, &UploadWidget::newIndexSelected); -} - -KIPI::ImageCollection UploadWidget::selectedImageCollection() const -{ - return KIPI::ImageCollection(new Plugins::UploadImageCollection(m_path)); -} - -void UploadWidget::newIndexSelected(const QModelIndex &index) -{ - m_path = m_model->filePath(index); -} - -} // namespace Plugins -// vi:expandtab:tabstop=4 shiftwidth=4: diff --git a/Plugins/UploadWidget.h b/Plugins/UploadWidget.h deleted file mode 100644 index 7e0de6fb..00000000 --- a/Plugins/UploadWidget.h +++ /dev/null @@ -1,51 +0,0 @@ -/* Copyright 2012 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) 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 PLUGINS_UPLOADWIDGET_H -#define PLUGINS_UPLOADWIDGET_H - -#include -#include - -class QFileSystemModel; -class QModelIndex; - -namespace Plugins -{ - -class UploadWidget : public KIPI::UploadWidget -{ - Q_OBJECT - -public: - explicit UploadWidget(QWidget *parent); - KIPI::ImageCollection selectedImageCollection() const override; - -private slots: - void newIndexSelected(const QModelIndex &index); - -private: - QFileSystemModel *m_model; - QString m_path; -}; - -} // namespace Plugins - -#endif // PLUGINS_UPLOADWIDGET_H -// vi:expandtab:tabstop=4 shiftwidth=4: diff --git a/Plugins/documentation.h b/Plugins/documentation.h index 3d47d990..f7a1fc26 100644 --- a/Plugins/documentation.h +++ b/Plugins/documentation.h @@ -1,21 +1,13 @@ //krazy:skip /** \namespace Plugins - \brief Implementation of the KIPI and Purpose interfaces. - - As of this writing, KIPI is no longer maintained and will probably phase out of distributions soon. - Purpose, on the other hand, is a replacement for some, but not (yet) all things that KIPI plugins could do. + \brief Implementation of the Purpose interface. ## KIPI Status: -
    -
  • Last checked against libkipi version 5.1.0 (v16.07.80).
  • -
  • Implemented features are described by Plugins::Interface::features()
  • -
  • As far as implemented, everything should work.
  • -
  • Some concepts, such as image orientation, have no exact match between libkipi and kphotoalbum.
  • -
+ The old KIPI interface is no longer supported by KPhotoAlbum. ## Purpose Status: - Sharing images is possible - Success/error feedback does not yet work correctly **/ // vi:expandtab:tabstop=4 shiftwidth=4: diff --git a/Settings/PluginsPage.cpp b/Settings/PluginsPage.cpp deleted file mode 100644 index a42bd334..00000000 --- a/Settings/PluginsPage.cpp +++ /dev/null @@ -1,62 +0,0 @@ -/* Copyright (C) 2003-2020 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 "PluginsPage.h" - -#include -#include -#include -#include -#include - -#ifdef HASKIPI -#include -#include -#endif - -#include "SettingsData.h" - -#include - -Settings::PluginsPage::PluginsPage(QWidget *parent) - : QWidget(parent) -{ - // TODO: DEPENDENCY: the circular dependency on mainwindow is unfortunate. - ::MainWindow::Window::theMainWindow()->loadKipiPlugins(); - QVBoxLayout *lay1 = new QVBoxLayout(this); - - QLabel *label = new QLabel(i18n("Choose Plugins to load:"), this); - lay1->addWidget(label); - - m_pluginConfig = KIPI::PluginLoader::instance()->configWidget(this); - lay1->addWidget(m_pluginConfig); - - m_delayLoadingPlugins = new QCheckBox(i18n("Delay loading plugins until the plugin menu is opened"), this); - lay1->addWidget(m_delayLoadingPlugins); -} - -void Settings::PluginsPage::saveSettings(Settings::SettingsData *opt) -{ - m_pluginConfig->apply(); - opt->setDelayLoadingPlugins(m_delayLoadingPlugins->isChecked()); -} - -void Settings::PluginsPage::loadSettings(Settings::SettingsData *opt) -{ - m_delayLoadingPlugins->setChecked(opt->delayLoadingPlugins()); -} -// vi:expandtab:tabstop=4 shiftwidth=4: diff --git a/Settings/PluginsPage.h b/Settings/PluginsPage.h deleted file mode 100644 index 0ced0f4e..00000000 --- a/Settings/PluginsPage.h +++ /dev/null @@ -1,48 +0,0 @@ -/* Copyright (C) 2003-2010 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. -*/ -#ifndef PLUGINSPAGE_H -#define PLUGINSPAGE_H -#include - -namespace KIPI -{ -class ConfigWidget; -} - -class QCheckBox; -namespace Settings -{ -class SettingsData; - -class PluginsPage : public QWidget -{ -public: - explicit PluginsPage(QWidget *parent); - void loadSettings(Settings::SettingsData *); - void saveSettings(Settings::SettingsData *); - -private: - KIPI::ConfigWidget *m_pluginConfig; - QCheckBox *m_delayLoadingPlugins; -}; - -} - -#endif /* PLUGINSPAGE_H */ - -// vi:expandtab:tabstop=4 shiftwidth=4: diff --git a/Settings/SettingsData.cpp b/Settings/SettingsData.cpp index 3ece941f..89fc0f44 100644 --- a/Settings/SettingsData.cpp +++ b/Settings/SettingsData.cpp @@ -1,620 +1,618 @@ /* Copyright (C) 2003-2020 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) 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 "SettingsData.h" #include #include #include #include #include #include #include #include #include #include #include #include #define STR(x) QString::fromLatin1(x) #define value(GROUP, OPTION, DEFAULT) \ KSharedConfig::openConfig()->group(GROUP).readEntry(OPTION, DEFAULT) #define setValue(GROUP, OPTION, VALUE) \ { \ KConfigGroup group = KSharedConfig::openConfig()->group(GROUP); \ group.writeEntry(OPTION, VALUE); \ group.sync(); \ } #define getValueFunc_(TYPE, FUNC, GROUP, OPTION, DEFAULT) \ TYPE SettingsData::FUNC() const \ { \ return (TYPE)value(GROUP, OPTION, DEFAULT); \ } #define setValueFunc_(FUNC, TYPE, GROUP, OPTION, VALUE) \ void SettingsData::FUNC(const TYPE v) \ { \ setValue(GROUP, OPTION, VALUE); \ } #define getValueFunc(TYPE, FUNC, GROUP, DEFAULT) getValueFunc_(TYPE, FUNC, #GROUP, #FUNC, DEFAULT) #define setValueFunc(FUNC, TYPE, GROUP, OPTION) setValueFunc_(FUNC, TYPE, #GROUP, #OPTION, v) // TODO(mfwitten): document parameters. #define property_(GET_TYPE, GET_FUNC, GET_VALUE, SET_FUNC, SET_TYPE, SET_VALUE, GROUP, OPTION, GET_DEFAULT_1, GET_DEFAULT_2, GET_DEFAULT_2_TYPE) \ GET_TYPE SettingsData::GET_FUNC() const \ { \ KConfigGroup g = KSharedConfig::openConfig()->group(GROUP); \ \ if (!g.hasKey(OPTION)) \ return GET_DEFAULT_1; \ \ GET_DEFAULT_2_TYPE v = g.readEntry(OPTION, (GET_DEFAULT_2_TYPE)GET_DEFAULT_2); \ return (GET_TYPE)GET_VALUE; \ } \ setValueFunc_(SET_FUNC, SET_TYPE, GROUP, OPTION, SET_VALUE) #define property(GET_TYPE, GET_FUNC, SET_FUNC, SET_TYPE, SET_VALUE, GROUP, OPTION, GET_DEFAULT) \ getValueFunc_(GET_TYPE, GET_FUNC, GROUP, OPTION, GET_DEFAULT) \ setValueFunc_(SET_FUNC, SET_TYPE, GROUP, OPTION, SET_VALUE) #define property_copy(GET_FUNC, SET_FUNC, TYPE, GROUP, GET_DEFAULT) \ property(TYPE, GET_FUNC, SET_FUNC, TYPE, v, #GROUP, #GET_FUNC, GET_DEFAULT) #define property_ref_(GET_FUNC, SET_FUNC, TYPE, GROUP, GET_DEFAULT) \ property(TYPE, GET_FUNC, SET_FUNC, TYPE &, v, GROUP, #GET_FUNC, GET_DEFAULT) #define property_ref(GET_FUNC, SET_FUNC, TYPE, GROUP, GET_DEFAULT) \ property(TYPE, GET_FUNC, SET_FUNC, TYPE &, v, #GROUP, #GET_FUNC, GET_DEFAULT) #define property_enum(GET_FUNC, SET_FUNC, TYPE, GROUP, GET_DEFAULT) \ property(TYPE, GET_FUNC, SET_FUNC, TYPE, (int)v, #GROUP, #GET_FUNC, (int)GET_DEFAULT) #define property_sset(GET_FUNC, SET_FUNC, GROUP, GET_DEFAULT) \ property_(StringSet, GET_FUNC, v.toSet(), SET_FUNC, StringSet &, v.toList(), #GROUP, #GET_FUNC, GET_DEFAULT, QStringList(), QStringList) /** * smoothScale() is called from the image loading thread, therefore we need * to cache it this way, rather than going to KConfig. */ static bool _smoothScale = true; using namespace Settings; const WindowType Settings::MainWindow = "MainWindow"; const WindowType Settings::AnnotationDialog = "AnnotationDialog"; SettingsData *SettingsData::s_instance = nullptr; SettingsData *SettingsData::instance() { if (!s_instance) qFatal("instance called before loading a setup!"); return s_instance; } bool SettingsData::ready() { return s_instance; } void SettingsData::setup(const QString &imageDirectory) { if (!s_instance) s_instance = new SettingsData(imageDirectory); } SettingsData::SettingsData(const QString &imageDirectory) { m_hasAskedAboutTimeStamps = false; QString s = STR("/"); m_imageDirectory = imageDirectory.endsWith(s) ? imageDirectory : imageDirectory + s; _smoothScale = value("Viewer", "smoothScale", true); // Split the list of Exif comments that should be stripped automatically to a list QStringList commentsToStrip = value("General", "commentsToStrip", QString::fromLatin1("Exif_JPEG_PICTURE-,-OLYMPUS DIGITAL CAMERA-,-JENOPTIK DIGITAL CAMERA-,-")).split(QString::fromLatin1("-,-"), QString::SkipEmptyParts); for (QString &comment : commentsToStrip) comment.replace(QString::fromLatin1(",,"), QString::fromLatin1(",")); m_EXIFCommentsToStrip = commentsToStrip; } ///////////////// //// General //// ///////////////// // clang-format off property_copy(useEXIFRotate, setUseEXIFRotate, bool, General, true) property_copy(useEXIFComments, setUseEXIFComments, bool, General, true) property_copy(stripEXIFComments, setStripEXIFComments, bool, General, true) property_copy(commentsToStrip, setCommentsToStrip, QString, General, "" /* see constructor */) property_copy(searchForImagesOnStart, setSearchForImagesOnStart, bool, General, true) property_copy(ignoreFileExtension, setIgnoreFileExtension, bool, General, false) property_copy(skipSymlinks, setSkipSymlinks, bool, General, false) property_copy(skipRawIfOtherMatches, setSkipRawIfOtherMatches, bool, General, false) property_copy(useRawThumbnail, setUseRawThumbnail, bool, General, true) property_copy(useRawThumbnailSize, setUseRawThumbnailSize, QSize, General, QSize(1024, 768)) property_copy(useCompressedIndexXML, setUseCompressedIndexXML, bool, General, true) property_copy(compressBackup, setCompressBackup, bool, General, true) property_copy(showSplashScreen, setShowSplashScreen, bool, General, true) property_copy(showHistogram, setShowHistogram, bool, General, true) property_copy(autoSave, setAutoSave, int, General, 5) property_copy(backupCount, setBackupCount, int, General, 5) property_enum(tTimeStamps, setTTimeStamps, TimeStampTrust, General, Always) property_copy(excludeDirectories, setExcludeDirectories, QString, General, QString::fromLatin1("xml,ThumbNails,.thumbs")) #ifdef KPA_ENABLE_REMOTECONTROL property_copy(recentAndroidAddress, setRecentAndroidAddress, QString, General, QString()) property_copy(listenForAndroidDevicesOnStartup, setListenForAndroidDevicesOnStartup, bool, General, false) #endif getValueFunc(QString, colorScheme, General, QString()) void SettingsData::setColorScheme(const QString &path) { if (path != colorScheme()) { setValue("General", "colorScheme", path); emit colorSchemeChanged(); } } getValueFunc(QSize, histogramSize, General, QSize(15, 30)) getValueFunc(ViewSortType, viewSortType, General, (int)SortLastUse) getValueFunc(AnnotationDialog::MatchType, matchType, General, (int)AnnotationDialog::MatchFromWordStart) getValueFunc(bool, histogramUseLinearScale, General, false) // clang-format on void SettingsData::setHistogramUseLinearScale(const bool useLinearScale) { if (useLinearScale == histogramUseLinearScale()) return; setValue("General", "histogramUseLinearScale", useLinearScale); emit histogramScaleChanged(); } void SettingsData::setHistogramSize(const QSize &size) { if (size == histogramSize()) return; setValue("General", "histogramSize", size); emit histogramSizeChanged(size); } void SettingsData::setViewSortType(const ViewSortType tp) { if (tp == viewSortType()) return; setValue("General", "viewSortType", (int)tp); emit viewSortTypeChanged(tp); } void SettingsData::setMatchType(const AnnotationDialog::MatchType mt) { if (mt == matchType()) return; setValue("General", "matchType", (int)mt); emit matchTypeChanged(mt); } bool SettingsData::trustTimeStamps() { if (tTimeStamps() == Always) return true; else if (tTimeStamps() == Never) return false; else { if (!m_hasAskedAboutTimeStamps) { QApplication::setOverrideCursor(Qt::ArrowCursor); QString txt = i18n("When reading time information of images, their Exif info is used. " "Exif info may, however, not be supported by your KPhotoAlbum installation, " "or no valid information may be in the file. " "As a backup, KPhotoAlbum may use the timestamp of the image - this may, " "however, not be valid in case the image is scanned in. " "So the question is, should KPhotoAlbum trust the time stamp on your images?"); int answer = KMessageBox::questionYesNo(nullptr, txt, i18n("Trust Time Stamps?")); QApplication::restoreOverrideCursor(); if (answer == KMessageBox::Yes) m_trustTimeStamps = true; else m_trustTimeStamps = false; m_hasAskedAboutTimeStamps = true; } return m_trustTimeStamps; } } //////////////////////////////// //// File Version Detection //// //////////////////////////////// // clang-format off property_copy(detectModifiedFiles, setDetectModifiedFiles, bool, FileVersionDetection, true) property_copy(modifiedFileComponent, setModifiedFileComponent, QString, FileVersionDetection, "^(.*)-edited.([^.]+)$") property_copy(originalFileComponent, setOriginalFileComponent, QString, FileVersionDetection, "\\1.\\2") property_copy(moveOriginalContents, setMoveOriginalContents, bool, FileVersionDetection, false) property_copy(autoStackNewFiles, setAutoStackNewFiles, bool, FileVersionDetection, true) property_copy(copyFileComponent, setCopyFileComponent, QString, FileVersionDetection, "(.[^.]+)$") property_copy(copyFileReplacementComponent, setCopyFileReplacementComponent, QString, FileVersionDetection, "-edited\\1") property_copy(loadOptimizationPreset, setLoadOptimizationPreset, int, FileVersionDetection, 0) property_copy(overlapLoadMD5, setOverlapLoadMD5, bool, FileVersionDetection, false) property_copy(preloadThreadCount, setPreloadThreadCount, int, FileVersionDetection, 1) property_copy(thumbnailPreloadThreadCount, setThumbnailPreloadThreadCount, int, FileVersionDetection, 1) property_copy(thumbnailBuilderThreadCount, setThumbnailBuilderThreadCount, int, FileVersionDetection, 0) // clang-format on //////////////////// //// Thumbnails //// //////////////////// // clang-format off property_copy(displayLabels, setDisplayLabels, bool, Thumbnails, true) property_copy(displayCategories, setDisplayCategories, bool, Thumbnails, false) property_copy(autoShowThumbnailView, setAutoShowThumbnailView, int, Thumbnails, 20) property_copy(showNewestThumbnailFirst, setShowNewestFirst, bool, Thumbnails, false) property_copy(thumbnailDisplayGrid, setThumbnailDisplayGrid, bool, Thumbnails, false) property_copy(previewSize, setPreviewSize, int, Thumbnails, 256) property_copy(thumbnailSpace, setThumbnailSpace, int, Thumbnails, 4) // not available via GUI, but should be consistent (and maybe confgurable for powerusers): property_copy(minimumThumbnailSize, setMinimumThumbnailSize, int, Thumbnails, 32) property_copy(maximumThumbnailSize, setMaximumThumbnailSize, int, Thumbnails, 4096) property_enum(thumbnailAspectRatio, setThumbnailAspectRatio, ThumbnailAspectRatio, Thumbnails, Aspect_3_2) property_copy(incrementalThumbnails, setIncrementalThumbnails, bool, Thumbnails, true) // database specific so that changing it doesn't invalidate the thumbnail cache for other databases: getValueFunc_(int, thumbnailSize, groupForDatabase("Thumbnails"), "thumbSize", 256) // clang-format on void SettingsData::setThumbnailSize(int value) { // enforce limits: value = qBound(minimumThumbnailSize(), value, maximumThumbnailSize()); if (value != thumbnailSize()) emit thumbnailSizeChanged(value); setValue(groupForDatabase("Thumbnails"), "thumbSize", value); } int SettingsData::actualThumbnailSize() const { // this is database specific since it's a derived value of thumbnailSize int retval = value(groupForDatabase("Thumbnails"), "actualThumbSize", 0); // if no value has been set, use thumbnailSize if (retval == 0) retval = thumbnailSize(); return retval; } void SettingsData::setActualThumbnailSize(int value) { QPixmapCache::clear(); // enforce limits: value = qBound(minimumThumbnailSize(), value, thumbnailSize()); if (value != actualThumbnailSize()) { setValue(groupForDatabase("Thumbnails"), "actualThumbSize", value); emit actualThumbnailSizeChanged(value); } } //////////////// //// Viewer //// //////////////// // clang-format off property_ref(viewerSize, setViewerSize, QSize, Viewer, QSize(1024, 768)) property_ref(slideShowSize, setSlideShowSize, QSize, Viewer, QSize(1024, 768)) property_copy(launchViewerFullScreen, setLaunchViewerFullScreen, bool, Viewer, false) property_copy(launchSlideShowFullScreen, setLaunchSlideShowFullScreen, bool, Viewer, true) property_copy(showInfoBox, setShowInfoBox, bool, Viewer, true) property_copy(showLabel, setShowLabel, bool, Viewer, true) property_copy(showDescription, setShowDescription, bool, Viewer, true) property_copy(showDate, setShowDate, bool, Viewer, true) property_copy(showImageSize, setShowImageSize, bool, Viewer, true) property_copy(showRating, setShowRating, bool, Viewer, true) property_copy(showTime, setShowTime, bool, Viewer, true) property_copy(showFilename, setShowFilename, bool, Viewer, false) property_copy(showEXIF, setShowEXIF, bool, Viewer, true) property_copy(slideShowInterval, setSlideShowInterval, int, Viewer, 5) property_copy(viewerCacheSize, setViewerCacheSize, int, Viewer, 195) property_copy(infoBoxWidth, setInfoBoxWidth, int, Viewer, 400) property_copy(infoBoxHeight, setInfoBoxHeight, int, Viewer, 300) property_enum(infoBoxPosition, setInfoBoxPosition, Position, Viewer, Bottom) property_enum(viewerStandardSize, setViewerStandardSize, StandardViewSize, Viewer, FullSize) // clang-format on bool SettingsData::smoothScale() const { return _smoothScale; } void SettingsData::setSmoothScale(bool b) { _smoothScale = b; setValue("Viewer", "smoothScale", b); } //////////////////// //// Categories //// //////////////////// setValueFunc(setAlbumCategory, QString &, General, albumCategory) QString SettingsData::albumCategory() const { QString category = value("General", "albumCategory", STR("")); if (!DB::ImageDB::instance()->categoryCollection()->categoryNames().contains(category)) { category = DB::ImageDB::instance()->categoryCollection()->categoryNames()[0]; const_cast(this)->setAlbumCategory(category); } return category; } // clang-format off property_ref(untaggedCategory, setUntaggedCategory, QString, General, i18n("Events")) property_ref(untaggedTag, setUntaggedTag, QString, General, i18n("untagged")) property_copy(untaggedImagesTagVisible, setUntaggedImagesTagVisible, bool, General, false) // clang-format on ////////////// //// Exif //// ////////////// // clang-format off property_sset(exifForViewer, setExifForViewer, Exif, StringSet()) property_sset(exifForDialog, setExifForDialog, Exif, Exif::Info::instance()->standardKeys()) property_ref(iptcCharset, setIptcCharset, QString, Exif, QString()) // clang-format on ///////////////////// //// Exif Import //// ///////////////////// // clang-format off property_copy(updateExifData, setUpdateExifData, bool, ExifImport, true) property_copy(updateImageDate, setUpdateImageDate, bool, ExifImport, false) property_copy(useModDateIfNoExif, setUseModDateIfNoExif, bool, ExifImport, true) property_copy(updateOrientation, setUpdateOrientation, bool, ExifImport, false) property_copy(updateDescription, setUpdateDescription, bool, ExifImport, false) // clang-format on /////////////////////// //// Miscellaneous //// /////////////////////// // clang-format off -property_copy(delayLoadingPlugins, setDelayLoadingPlugins, bool, Plug - ins, true) - property_ref_(HTMLBaseDir, setHTMLBaseDir, QString, groupForDatabase("HTML Settings"), QString::fromLocal8Bit(qgetenv("HOME")) + STR("/public_html")) property_ref_(HTMLBaseURL, setHTMLBaseURL, QString, groupForDatabase("HTML Settings"), STR("file://") + HTMLBaseDir()) property_ref_(HTMLDestURL, setHTMLDestURL, QString, groupForDatabase("HTML Settings"), STR("file://") + HTMLBaseDir()) property_ref_(HTMLCopyright, setHTMLCopyright, QString, groupForDatabase("HTML Settings"), STR("")) property_ref_(HTMLDate, setHTMLDate, int, groupForDatabase("HTML Settings"), true) property_ref_(HTMLTheme, setHTMLTheme, int, groupForDatabase("HTML Settings"), -1) property_ref_(HTMLKimFile, setHTMLKimFile, int, groupForDatabase("HTML Settings"), true) property_ref_(HTMLInlineMovies, setHTMLInlineMovies, int, groupForDatabase("HTML Settings"), true) property_ref_(HTML5Video, setHTML5Video, int, groupForDatabase("HTML Settings"), true) property_ref_(HTML5VideoGenerate, setHTML5VideoGenerate, int, groupForDatabase("HTML Settings"), true) property_ref_(HTMLThumbSize, setHTMLThumbSize, int, groupForDatabase("HTML Settings"), 128) property_ref_(HTMLNumOfCols, setHTMLNumOfCols, int, groupForDatabase("HTML Settings"), 5) property_ref_(HTMLSizes, setHTMLSizes, QString, groupForDatabase("HTML Settings"), STR("")) property_ref_(HTMLIncludeSelections, setHTMLIncludeSelections, QString, groupForDatabase("HTML Settings"), STR("")) // clang-format on QDate SettingsData::fromDate() const { QString date = value("Miscellaneous", "fromDate", STR("")); return date.isEmpty() ? QDate(QDate::currentDate().year(), 1, 1) : QDate::fromString(date, Qt::ISODate); } void SettingsData::setFromDate(const QDate &date) { if (date.isValid()) setValue("Miscellaneous", "fromDate", date.toString(Qt::ISODate)); } QDate SettingsData::toDate() const { QString date = value("Miscellaneous", "toDate", STR("")); return date.isEmpty() ? QDate(QDate::currentDate().year() + 1, 1, 1) : QDate::fromString(date, Qt::ISODate); } void SettingsData::setToDate(const QDate &date) { if (date.isValid()) setValue("Miscellaneous", "toDate", date.toString(Qt::ISODate)); } QString SettingsData::imageDirectory() const { return m_imageDirectory; } QString SettingsData::groupForDatabase(const char *setting) const { return STR("%1 - %2").arg(STR(setting)).arg(imageDirectory()); } DB::ImageSearchInfo SettingsData::currentLock() const { return DB::ImageSearchInfo::loadLock(); } void SettingsData::setCurrentLock(const DB::ImageSearchInfo &info, bool exclude) { info.saveLock(); setValue(groupForDatabase("Privacy Settings"), "exclude", exclude); } bool SettingsData::lockExcludes() const { return value(groupForDatabase("Privacy Settings"), "exclude", false); } getValueFunc_(bool, locked, groupForDatabase("Privacy Settings"), "locked", false) void SettingsData::setLocked(bool lock, bool force) { if (lock == locked() && !force) return; setValue(groupForDatabase("Privacy Settings"), "locked", lock); emit locked(lock, lockExcludes()); } void SettingsData::setWindowGeometry(WindowType win, const QRect &geometry) { setValue("Window Geometry", win, geometry); } QRect SettingsData::windowGeometry(WindowType win) const { return value("Window Geometry", win, QRect(0, 0, 800, 600)); } bool Settings::SettingsData::hasUntaggedCategoryFeatureConfigured() const { return DB::ImageDB::instance()->categoryCollection()->categoryNames().contains(untaggedCategory()) && DB::ImageDB::instance()->categoryCollection()->categoryForName(untaggedCategory())->items().contains(untaggedTag()); } double Settings::SettingsData::getThumbnailAspectRatio() const { double ratio = 1.0; switch (Settings::SettingsData::instance()->thumbnailAspectRatio()) { case Settings::Aspect_16_9: ratio = 9.0 / 16; break; case Settings::Aspect_4_3: ratio = 3.0 / 4; break; case Settings::Aspect_3_2: ratio = 2.0 / 3; break; case Settings::Aspect_9_16: ratio = 16 / 9.0; break; case Settings::Aspect_3_4: ratio = 4 / 3.0; break; case Settings::Aspect_2_3: ratio = 3 / 2.0; break; case Settings::Aspect_1_1: ratio = 1.0; break; } return ratio; } QStringList Settings::SettingsData::EXIFCommentsToStrip() { return m_EXIFCommentsToStrip; } void Settings::SettingsData::setEXIFCommentsToStrip(QStringList EXIFCommentsToStrip) { m_EXIFCommentsToStrip = EXIFCommentsToStrip; } bool Settings::SettingsData::getOverlapLoadMD5() const { switch (Settings::SettingsData::instance()->loadOptimizationPreset()) { case Settings::LoadOptimizationSlowNVME: case Settings::LoadOptimizationFastNVME: return true; break; case Settings::LoadOptimizationManual: return Settings::SettingsData::instance()->overlapLoadMD5(); break; case Settings::LoadOptimizationHardDisk: case Settings::LoadOptimizationNetwork: case Settings::LoadOptimizationSataSSD: default: return false; break; } } int Settings::SettingsData::getPreloadThreadCount() const { switch (Settings::SettingsData::instance()->loadOptimizationPreset()) { case Settings::LoadOptimizationManual: return Settings::SettingsData::instance()->preloadThreadCount(); break; case Settings::LoadOptimizationSlowNVME: case Settings::LoadOptimizationFastNVME: case Settings::LoadOptimizationSataSSD: return qMax(1, qMin(16, QThread::idealThreadCount())); break; case Settings::LoadOptimizationHardDisk: case Settings::LoadOptimizationNetwork: default: return 1; break; } } int Settings::SettingsData::getThumbnailPreloadThreadCount() const { switch (Settings::SettingsData::instance()->loadOptimizationPreset()) { case Settings::LoadOptimizationManual: return Settings::SettingsData::instance()->thumbnailPreloadThreadCount(); break; case Settings::LoadOptimizationSlowNVME: case Settings::LoadOptimizationFastNVME: case Settings::LoadOptimizationSataSSD: return qMax(1, qMin(16, QThread::idealThreadCount() / 2)); break; case Settings::LoadOptimizationHardDisk: case Settings::LoadOptimizationNetwork: default: return 1; break; } } int Settings::SettingsData::getThumbnailBuilderThreadCount() const { switch (Settings::SettingsData::instance()->loadOptimizationPreset()) { case Settings::LoadOptimizationManual: return Settings::SettingsData::instance()->thumbnailBuilderThreadCount(); break; case Settings::LoadOptimizationSlowNVME: case Settings::LoadOptimizationFastNVME: case Settings::LoadOptimizationSataSSD: case Settings::LoadOptimizationHardDisk: case Settings::LoadOptimizationNetwork: default: return qMax(1, qMin(16, QThread::idealThreadCount() - 1)); break; } } // vi:expandtab:tabstop=4 shiftwidth=4: diff --git a/Settings/SettingsData.h b/Settings/SettingsData.h index 637a37ac..cf4d8e2d 100644 --- a/Settings/SettingsData.h +++ b/Settings/SettingsData.h @@ -1,288 +1,286 @@ /* Copyright (C) 2003-2020 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 SETTINGS_SETTINGSDATA_H #define SETTINGS_SETTINGSDATA_H #include #include #include #include #include #define property(GET_TYPE, GET_FUNC, SET_FUNC, SET_TYPE) \ GET_TYPE GET_FUNC() const; \ void SET_FUNC(const SET_TYPE) #define property_copy(GET_FUNC, SET_FUNC, TYPE) property(TYPE, GET_FUNC, SET_FUNC, TYPE) #define property_ref(GET_FUNC, SET_FUNC, TYPE) property(TYPE, GET_FUNC, SET_FUNC, TYPE &) namespace DB { class CategoryCollection; } namespace Settings { using Utilities::StringSet; enum Position { Bottom, Top, Left, Right, TopLeft, TopRight, BottomLeft, BottomRight }; enum ViewSortType { SortLastUse, SortAlphaTree, SortAlphaFlat }; enum TimeStampTrust { Always, Ask, Never }; enum StandardViewSize { FullSize, NaturalSize, NaturalSizeIfFits }; enum ThumbnailAspectRatio { Aspect_1_1, Aspect_4_3, Aspect_3_2, Aspect_16_9, Aspect_3_4, Aspect_2_3, Aspect_9_16 }; enum LoadOptimizationPreset { LoadOptimizationHardDisk, LoadOptimizationNetwork, LoadOptimizationSataSSD, LoadOptimizationSlowNVME, LoadOptimizationFastNVME, LoadOptimizationManual }; typedef const char *WindowType; extern const WindowType MainWindow, AnnotationDialog; class SettingsData : public QObject { Q_OBJECT public: static SettingsData *instance(); static bool ready(); static void setup(const QString &imageDirectory); ///////////////// //// General //// ///////////////// property_ref(histogramSize, setHistogramSize, QSize); property_copy(useEXIFRotate, setUseEXIFRotate, bool); property_copy(useEXIFComments, setUseEXIFComments, bool); property_copy(stripEXIFComments, setStripEXIFComments, bool); property_copy(commentsToStrip, setCommentsToStrip, QString); property_copy(searchForImagesOnStart, setSearchForImagesOnStart, bool); property_copy(ignoreFileExtension, setIgnoreFileExtension, bool); property_copy(skipSymlinks, setSkipSymlinks, bool); property_copy(skipRawIfOtherMatches, setSkipRawIfOtherMatches, bool); property_copy(useRawThumbnail, setUseRawThumbnail, bool); property_copy(useRawThumbnailSize, setUseRawThumbnailSize, QSize); property_copy(useCompressedIndexXML, setUseCompressedIndexXML, bool); property_copy(compressBackup, setCompressBackup, bool); property_copy(showSplashScreen, setShowSplashScreen, bool); property_copy(showHistogram, setShowHistogram, bool); property_copy(histogramUseLinearScale, setHistogramUseLinearScale, bool); property_copy(autoSave, setAutoSave, int); property_copy(backupCount, setBackupCount, int); property_copy(viewSortType, setViewSortType, ViewSortType); property_copy(matchType, setMatchType, AnnotationDialog::MatchType); property_copy(tTimeStamps, setTTimeStamps, TimeStampTrust); property_copy(excludeDirectories, setExcludeDirectories, QString); #ifdef KPA_ENABLE_REMOTECONTROL property_copy(recentAndroidAddress, setRecentAndroidAddress, QString); property_copy(listenForAndroidDevicesOnStartup, setListenForAndroidDevicesOnStartup, bool); #endif //////////////////////////////// //// File Version Detection //// //////////////////////////////// property_copy(detectModifiedFiles, setDetectModifiedFiles, bool); property_copy(modifiedFileComponent, setModifiedFileComponent, QString); property_copy(originalFileComponent, setOriginalFileComponent, QString); property_copy(moveOriginalContents, setMoveOriginalContents, bool); property_copy(autoStackNewFiles, setAutoStackNewFiles, bool); property_copy(copyFileComponent, setCopyFileComponent, QString); property_copy(copyFileReplacementComponent, setCopyFileReplacementComponent, QString); property_copy(loadOptimizationPreset, setLoadOptimizationPreset, int); property_copy(overlapLoadMD5, setOverlapLoadMD5, bool); property_copy(preloadThreadCount, setPreloadThreadCount, int); property_copy(thumbnailPreloadThreadCount, setThumbnailPreloadThreadCount, int); property_copy(thumbnailBuilderThreadCount, setThumbnailBuilderThreadCount, int); bool trustTimeStamps(); //////////////////// //// Thumbnails //// //////////////////// property_copy(displayLabels, setDisplayLabels, bool); property_copy(displayCategories, setDisplayCategories, bool); property_copy(autoShowThumbnailView, setAutoShowThumbnailView, int); property_copy(showNewestThumbnailFirst, setShowNewestFirst, bool); property_copy(thumbnailDisplayGrid, setThumbnailDisplayGrid, bool); property_copy(previewSize, setPreviewSize, int); property_ref(colorScheme, setColorScheme, QString); property_copy(incrementalThumbnails, setIncrementalThumbnails, bool); // Border space around thumbnails. property_copy(thumbnailSpace, setThumbnailSpace, int); property_copy(thumbnailSize, setThumbnailSize, int); property_copy(minimumThumbnailSize, setMinimumThumbnailSize, int); property_copy(maximumThumbnailSize, setMaximumThumbnailSize, int); property_copy(actualThumbnailSize, setActualThumbnailSize, int); property_copy(thumbnailAspectRatio, setThumbnailAspectRatio, ThumbnailAspectRatio); //////////////// //// Viewer //// //////////////// property_ref(viewerSize, setViewerSize, QSize); property_ref(slideShowSize, setSlideShowSize, QSize); property_copy(launchViewerFullScreen, setLaunchViewerFullScreen, bool); property_copy(launchSlideShowFullScreen, setLaunchSlideShowFullScreen, bool); property_copy(showInfoBox, setShowInfoBox, bool); property_copy(showLabel, setShowLabel, bool); property_copy(showDescription, setShowDescription, bool); property_copy(showDate, setShowDate, bool); property_copy(showImageSize, setShowImageSize, bool); property_copy(showRating, setShowRating, bool); property_copy(showTime, setShowTime, bool); property_copy(showFilename, setShowFilename, bool); property_copy(showEXIF, setShowEXIF, bool); property_copy(smoothScale, setSmoothScale, bool); property_copy(slideShowInterval, setSlideShowInterval, int); property_copy(viewerCacheSize, setViewerCacheSize, int); property_copy(infoBoxWidth, setInfoBoxWidth, int); property_copy(infoBoxHeight, setInfoBoxHeight, int); property_copy(infoBoxPosition, setInfoBoxPosition, Position); property_copy(viewerStandardSize, setViewerStandardSize, StandardViewSize); //////////////////// //// Categories //// //////////////////// property_ref(albumCategory, setAlbumCategory, QString); property_ref(untaggedCategory, setUntaggedCategory, QString); property_ref(untaggedTag, setUntaggedTag, QString); bool hasUntaggedCategoryFeatureConfigured() const; property_copy(untaggedImagesTagVisible, setUntaggedImagesTagVisible, bool); ////////////// //// Exif //// ////////////// property_ref(exifForViewer, setExifForViewer, StringSet); property_ref(exifForDialog, setExifForDialog, StringSet); property_ref(iptcCharset, setIptcCharset, QString); ///////////////////// //// Exif Import //// ///////////////////// property_copy(updateExifData, setUpdateExifData, bool); property_copy(updateImageDate, setUpdateImageDate, bool); property_copy(useModDateIfNoExif, setUseModDateIfNoExif, bool); property_copy(updateOrientation, setUpdateOrientation, bool); property_copy(updateDescription, setUpdateDescription, bool); /////////////////////// //// Miscellaneous //// /////////////////////// - property_copy(delayLoadingPlugins, setDelayLoadingPlugins, bool); - property_ref(HTMLBaseDir, setHTMLBaseDir, QString); property_ref(HTMLBaseURL, setHTMLBaseURL, QString); property_ref(HTMLDestURL, setHTMLDestURL, QString); property_ref(HTMLCopyright, setHTMLCopyright, QString); property_ref(HTMLDate, setHTMLDate, int); property_ref(HTMLTheme, setHTMLTheme, int); property_ref(HTMLKimFile, setHTMLKimFile, int); property_ref(HTMLInlineMovies, setHTMLInlineMovies, int); property_ref(HTML5Video, setHTML5Video, int); property_ref(HTML5VideoGenerate, setHTML5VideoGenerate, int); property_ref(HTMLThumbSize, setHTMLThumbSize, int); property_ref(HTMLNumOfCols, setHTMLNumOfCols, int); property_ref(HTMLSizes, setHTMLSizes, QString); property_ref(HTMLIncludeSelections, setHTMLIncludeSelections, QString); property_ref(fromDate, setFromDate, QDate); property_ref(toDate, setToDate, QDate); QString imageDirectory() const; QString groupForDatabase(const char *setting) const; DB::ImageSearchInfo currentLock() const; void setCurrentLock(const DB::ImageSearchInfo &, bool exclude); bool lockExcludes() const; bool locked() const; void setLocked(bool locked, bool force); void setWindowGeometry(WindowType, const QRect &geometry); QRect windowGeometry(WindowType) const; double getThumbnailAspectRatio() const; QStringList EXIFCommentsToStrip(); void setEXIFCommentsToStrip(QStringList EXIFCommentsToStrip); bool getOverlapLoadMD5() const; int getPreloadThreadCount() const; int getThumbnailPreloadThreadCount() const; int getThumbnailBuilderThreadCount() const; signals: void locked(bool lock, bool exclude); void viewSortTypeChanged(Settings::ViewSortType); void matchTypeChanged(AnnotationDialog::MatchType); void histogramSizeChanged(const QSize &); void thumbnailSizeChanged(int); void actualThumbnailSizeChanged(int); void histogramScaleChanged(); void colorSchemeChanged(); private: SettingsData(const QString &imageDirectory); bool m_trustTimeStamps; bool m_hasAskedAboutTimeStamps; QString m_imageDirectory; static SettingsData *s_instance; friend class DB::CategoryCollection; QStringList m_EXIFCommentsToStrip; }; } // end of namespace #undef property #undef property_copy #undef property_ref #endif /* SETTINGS_SETTINGSDATA_H */ // vi:expandtab:tabstop=4 shiftwidth=4: diff --git a/Settings/SettingsDialog.cpp b/Settings/SettingsDialog.cpp index f499556f..ef826d73 100644 --- a/Settings/SettingsDialog.cpp +++ b/Settings/SettingsDialog.cpp @@ -1,179 +1,149 @@ /* Copyright (C) 2003-2020 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 "config-kpa-plugins.h" - #include "SettingsDialog.h" #include "BirthdayPage.h" #include "CategoryPage.h" #include "DatabaseBackendPage.h" #include "ExifPage.h" #include "FileVersionDetectionPage.h" #include "GeneralPage.h" -#include "PluginsPage.h" #include "TagGroupsPage.h" #include "ThumbnailsPage.h" #include "ViewerPage.h" #include #include #include #include #include #include struct Data { Settings::SettingsPage page; QString title; const char *icon; QWidget *widget; }; Settings::SettingsDialog::SettingsDialog(QWidget *parent) : KPageDialog(parent) { m_generalPage = new Settings::GeneralPage(this); m_fileVersionDetectionPage = new Settings::FileVersionDetectionPage(this); m_thumbnailsPage = new Settings::ThumbnailsPage(this); m_categoryPage = new Settings::CategoryPage(this); m_tagGroupsPage = new Settings::TagGroupsPage(this); m_viewerPage = new Settings::ViewerPage(this); - -#ifdef HASKIPI - m_pluginsPage = new Settings::PluginsPage(this); -#endif - m_exifPage = new Settings::ExifPage(this); - m_birthdayPage = new Settings::BirthdayPage(this); - m_databaseBackendPage = new Settings::DatabaseBackendPage(this); Data data[] = { { SettingsPage::GeneralPage, i18n("General"), "configure-shortcuts", m_generalPage }, { SettingsPage::FileVersionDetectionPage, i18n("File Searching & Versions"), "system-search", m_fileVersionDetectionPage }, { SettingsPage::ThumbnailsPage, i18n("Thumbnail View"), "view-preview", m_thumbnailsPage }, { SettingsPage::CategoryPage, i18n("Categories"), "edit-group", m_categoryPage }, { SettingsPage::BirthdayPage, i18n("Birthdays"), "view-calendar-birthday", m_birthdayPage }, { SettingsPage::TagGroupsPage, i18n("Tag Groups"), "view-group", m_tagGroupsPage }, { SettingsPage::ViewerPage, i18n("Viewer"), "document-preview", m_viewerPage }, -#ifdef HASKIPI - { SettingsPage::PluginsPage, i18n("Plugins"), "plugins", m_pluginsPage }, -#endif - { SettingsPage::ExifPage, i18n("Exif/IPTC Information"), "document-properties", m_exifPage }, { SettingsPage::DatabaseBackendPage, i18n("Database Backend"), "document-save", m_databaseBackendPage }, { SettingsPage::GeneralPage, QString(), "", 0 } }; int i = 0; while (data[i].widget != 0) { KPageWidgetItem *page = new KPageWidgetItem(data[i].widget, data[i].title); page->setHeader(data[i].title); page->setIcon(QIcon::fromTheme(QString::fromLatin1(data[i].icon))); addPage(page); m_pages[data[i].page] = page; ++i; } setStandardButtons(QDialogButtonBox::Ok | QDialogButtonBox::Cancel | QDialogButtonBox::Apply); button(QDialogButtonBox::Ok)->setShortcut(Qt::CTRL | Qt::Key_Return); connect(this, &QDialog::accepted, this, &SettingsDialog::slotMyOK); connect(button(QDialogButtonBox::Apply), &QPushButton::clicked, this, &SettingsDialog::slotMyOK); connect(this, &QDialog::rejected, m_birthdayPage, &Settings::BirthdayPage::discardChanges); setWindowTitle(i18nc("@title:window", "Settings")); connect(m_categoryPage, &Settings::CategoryPage::categoryChangesPending, m_tagGroupsPage, &Settings::TagGroupsPage::categoryChangesPending); connect(this, &SettingsDialog::currentPageChanged, m_tagGroupsPage, &Settings::TagGroupsPage::slotPageChange); connect(this, &SettingsDialog::currentPageChanged, m_birthdayPage, &Settings::BirthdayPage::pageChange); connect(this, &SettingsDialog::rejected, m_categoryPage, &Settings::CategoryPage::resetCategoryLabel); } void Settings::SettingsDialog::show() { Settings::SettingsData *opt = Settings::SettingsData::instance(); m_generalPage->loadSettings(opt); m_fileVersionDetectionPage->loadSettings(opt); m_thumbnailsPage->loadSettings(opt); m_tagGroupsPage->loadSettings(); m_databaseBackendPage->loadSettings(opt); m_viewerPage->loadSettings(opt); - -#ifdef HASKIPI - m_pluginsPage->loadSettings(opt); -#endif - m_categoryPage->loadSettings(opt); - m_exifPage->loadSettings(opt); - m_categoryPage->enableDisable(false); - m_birthdayPage->reload(); m_categoryPage->resetCategoryNamesChanged(); QDialog::show(); } void Settings::SettingsDialog::activatePage(Settings::SettingsPage pageId) { auto page = m_pages.value(pageId, nullptr); if (page) setCurrentPage(page); } // QDialog has a slotOK which we do not want to override. void Settings::SettingsDialog::slotMyOK() { Utilities::ShowBusyCursor dummy; Settings::SettingsData *opt = Settings::SettingsData::instance(); m_categoryPage->resetInterface(); m_generalPage->saveSettings(opt); m_fileVersionDetectionPage->saveSettings(opt); m_thumbnailsPage->saveSettings(opt); - m_birthdayPage->saveSettings(); m_tagGroupsPage->saveSettings(); m_categoryPage->saveSettings(opt, m_tagGroupsPage->memberMap()); - m_viewerPage->saveSettings(opt); - -#ifdef HASKIPI - m_pluginsPage->saveSettings(opt); -#endif - m_exifPage->saveSettings(opt); - m_databaseBackendPage->saveSettings(opt); emit changed(); KSharedConfig::openConfig()->sync(); } void Settings::SettingsDialog::keyPressEvent(QKeyEvent *) { // This prevents the dialog to be closed if the ENTER key is pressed anywhere } // vi:expandtab:tabstop=4 shiftwidth=4: diff --git a/Settings/SettingsDialog.h b/Settings/SettingsDialog.h index dd1faed8..f31b94f1 100644 --- a/Settings/SettingsDialog.h +++ b/Settings/SettingsDialog.h @@ -1,103 +1,99 @@ -/* Copyright (C) 2003-2010 Jesper K. Pedersen +/* Copyright (C) 2003-2020 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 SETTINGSDIALOG_H #define SETTINGSDIALOG_H // KDE includes #include -namespace KIPI -{ -class ConfigWidget; -} namespace Exif { class TreeView; } namespace Settings { class ViewerSizeConfig; class CategoryItem; class CategoryPage; class TagGroupsPage; class GeneralPage; class ThumbnailsPage; class ViewerPage; class FileVersionDetectionPage; class PluginsPage; class ExifPage; class DatabaseBackendPage; class BirthdayPage; /** * @brief The SettingsPage enum has a value for every settings sub-page. * It is used for SettingsDialog::setPage(). */ enum class SettingsPage { BirthdayPage, CategoryPage, DatabaseBackendPage, ExifPage, FileVersionDetectionPage, GeneralPage, PluginsPage, TagGroupsPage, ThumbnailsPage, ViewerPage }; class SettingsDialog : public KPageDialog { Q_OBJECT public: explicit SettingsDialog(QWidget *parent); virtual void show(); public slots: void activatePage(SettingsPage pageId); signals: void changed(); void thumbnailSizeChanged(); protected slots: void slotMyOK(); private: Settings::GeneralPage *m_generalPage; Settings::FileVersionDetectionPage *m_fileVersionDetectionPage; Settings::ThumbnailsPage *m_thumbnailsPage; Settings::CategoryPage *m_categoryPage; Settings::TagGroupsPage *m_tagGroupsPage; Settings::ViewerPage *m_viewerPage; Settings::PluginsPage *m_pluginsPage; Settings::ExifPage *m_exifPage; Settings::DatabaseBackendPage *m_databaseBackendPage; Settings::BirthdayPage *m_birthdayPage; QMap m_pages; void keyPressEvent(QKeyEvent *) override; }; } #endif /* SETTINGSDIALOG_H */ // vi:expandtab:tabstop=4 shiftwidth=4: diff --git a/config-kpa-plugins.h.cmake b/config-kpa-plugins.h.cmake index 2052186c..854e21c0 100644 --- a/config-kpa-plugins.h.cmake +++ b/config-kpa-plugins.h.cmake @@ -1,3 +1 @@ -/* Define to 1 if you have KIPI installed */ -#cmakedefine HASKIPI 1 #cmakedefine KF5Purpose_FOUND 1 diff --git a/dev/documentation/mainpage.h b/dev/documentation/mainpage.h index df203604..47b63061 100644 --- a/dev/documentation/mainpage.h +++ b/dev/documentation/mainpage.h @@ -1,43 +1,43 @@ //krazy:skip /** \mainpage Welcome to the KPhotoAlbum source code documentation, which is generated with doxygen. To generate your own copy, simply run doxygen in the kphotoalbum source directory. A few related pages that you should read: \li \ref coding-standards \li \ref phrase-book \li \ref videothumbnails \li \ref debug-output - Logging categories used by KPhotoAlbum \li \ref database-layout - Documentation for on-disk file formats (index.xml, thumbnail storage, etc.) KPhotoAlbum is split into a number of modules, each module is a directory of its own on the hard disk and a namespace in the source code. The following is a list of modules:

Main GUI component

\li \ref MainWindow - The main window and associated dialogs. \li \ref Browser - This is the browser where you narrow your way to the image you want to see. \li \ref ThumbnailView - The thumbnail viewer. \li \ref Viewer - The image/video viewer. \li \ref AnnotationDialog - This is the dialog where you tag your images (the one you get to using Ctrl+1 and Ctrl+2). \li \ref DateBar - The date bar at the bottom of the main screen.

Other GUI components

\li \ref ImportExport - Import/Export dialog and asscociated classes. \li \ref Settings - The Settings dialog and backend classes. \li \ref HTMLGenerator - The builtin HTML generator. \li \ref CategoryListView - This is the tree view used in the annotation dialog. \li \ref Exif - Exif related dialog and backend classes

Database backend

\li \ref DB - The abstract interface for the database backend. \li \ref XMLDB - The XML based database backend.

Backend

\li \ref ImageManager - Thumbnail loader - \li \ref Plugins - KIPI plug-in management + \li \ref Plugins - Purpose plugin management \li \ref Utilities - Miscellaneous utility classes \li \ref BackgroundTaskManager \li \ref BackgroundJobs - Jobs for the BackgroundTaskManager */ // vi:expandtab:tabstop=4 shiftwidth=4: diff --git a/kphotoalbumui.rc b/kphotoalbumui.rc index 7889961c..0b05fda6 100644 --- a/kphotoalbumui.rc +++ b/kphotoalbumui.rc @@ -1,105 +1,101 @@ &View Privacy &Maintenance Plugins - - Legacy Plugins (KIPI) - - Main Toolbar