diff --git a/CMakeLists.txt b/CMakeLists.txt index 2fdbb2c4..87ad2b5b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,520 +1,521 @@ cmake_minimum_required(VERSION 3.2.0) project(kphotoalbum VERSION 5.4) 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 ) set(CMAKE_CXX_STANDARD 11) set(CMAKE_CXX_REQUIRED TRUE) ########### dependencies ############### find_package(Qt5 REQUIRED COMPONENTS Sql Xml Widgets Network) find_package(Phonon4Qt5 REQUIRED) find_package(KF5 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() find_package(Exiv2 REQUIRED) 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(KF5KDcraw) set_package_properties(KF5KDcraw PROPERTIES TYPE OPTIONAL PURPOSE "Enables RAW image support" ) set(HAVE_KDCRAW ${KF5KDcraw_FOUND} ) find_package(KF5KGeoMap) set_package_properties(KF5KGeoMap PROPERTIES TYPE OPTIONAL PURPOSE "Enables support for geographic map location using embedded GPS information." ) set(HAVE_KGEOMAP ${KF5KGeoMap_FOUND}) add_custom_target( UpdateVersion ALL COMMAND ${CMAKE_COMMAND} -DBASE_DIR=${CMAKE_CURRENT_SOURCE_DIR} -DPROJECT_NAME=KPA -DPROJECT_VERSION="${PROJECT_VERSION}" -DCMAKE_BUILD_TYPE="${CMAKE_BUILD_TYPE}" -P "${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules/UpdateVersion.cmake" COMMENT "Updating version header." BYPRODUCTS "${CMAKE_CURRENT_SOURCE_DIR}/version.h" ) # For config-kpa-*.h include_directories(${CMAKE_CURRENT_BINARY_DIR}) set(libdatebar_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/DateBar/documentation.h ${CMAKE_CURRENT_SOURCE_DIR}/DateBar/DateBarWidget.cpp ${CMAKE_CURRENT_SOURCE_DIR}/DateBar/ViewHandler.cpp ${CMAKE_CURRENT_SOURCE_DIR}/DateBar/MouseHandler.cpp ${CMAKE_CURRENT_SOURCE_DIR}/DateBar/MouseHandler.cpp ) set(libSettings_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/Settings/documentation.h ${CMAKE_CURRENT_SOURCE_DIR}/Settings/SettingsData.cpp ${CMAKE_CURRENT_SOURCE_DIR}/Settings/SettingsDialog.cpp ${CMAKE_CURRENT_SOURCE_DIR}/Settings/ViewerSizeConfig.cpp ${CMAKE_CURRENT_SOURCE_DIR}/Settings/CategoryItem.cpp ${CMAKE_CURRENT_SOURCE_DIR}/Settings/CategoryPage.cpp ${CMAKE_CURRENT_SOURCE_DIR}/Settings/TagGroupsPage.cpp ${CMAKE_CURRENT_SOURCE_DIR}/Settings/GeneralPage.cpp ${CMAKE_CURRENT_SOURCE_DIR}/Settings/FileVersionDetectionPage.cpp ${CMAKE_CURRENT_SOURCE_DIR}/Settings/ThumbnailsPage.cpp ${CMAKE_CURRENT_SOURCE_DIR}/Settings/ViewerPage.cpp ${CMAKE_CURRENT_SOURCE_DIR}/Settings/DatabaseBackendPage.cpp ${CMAKE_CURRENT_SOURCE_DIR}/Settings/UntaggedGroupBox.cpp ${CMAKE_CURRENT_SOURCE_DIR}/Settings/CategoriesGroupsWidget.cpp ${CMAKE_CURRENT_SOURCE_DIR}/Settings/BirthdayPage.cpp ${CMAKE_CURRENT_SOURCE_DIR}/Settings/DateTableWidgetItem.cpp ${CMAKE_CURRENT_SOURCE_DIR}/Settings/Logging.cpp ) set(libxmldb_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/XMLDB/documentation.h ${CMAKE_CURRENT_SOURCE_DIR}/XMLDB/Database.cpp ${CMAKE_CURRENT_SOURCE_DIR}/XMLDB/XMLCategoryCollection.cpp ${CMAKE_CURRENT_SOURCE_DIR}/XMLDB/XMLCategory.cpp ${CMAKE_CURRENT_SOURCE_DIR}/XMLDB/XMLImageDateCollection.cpp ${CMAKE_CURRENT_SOURCE_DIR}/XMLDB/NumberedBackup.cpp ${CMAKE_CURRENT_SOURCE_DIR}/XMLDB/FileReader.cpp ${CMAKE_CURRENT_SOURCE_DIR}/XMLDB/FileWriter.cpp ${CMAKE_CURRENT_SOURCE_DIR}/XMLDB/ElementWriter.cpp ${CMAKE_CURRENT_SOURCE_DIR}/XMLDB/XmlReader.cpp ${CMAKE_CURRENT_SOURCE_DIR}/XMLDB/CompressFileInfo.cpp ${CMAKE_CURRENT_SOURCE_DIR}/XMLDB/Logging.cpp ) set(libThumbnailView_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/ThumbnailView/documentation.h ${CMAKE_CURRENT_SOURCE_DIR}/ThumbnailView/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) if(KF5Kipi_FOUND) set(libPlugins_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/Plugins/documentation.h ${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 ${CMAKE_CURRENT_SOURCE_DIR}/Plugins/Logging.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/Util.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/QStr.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 ) 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 ) set(libImportExport_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/ImportExport/documentation.h ${CMAKE_CURRENT_SOURCE_DIR}/ImportExport/Export.cpp ${CMAKE_CURRENT_SOURCE_DIR}/ImportExport/Import.cpp ${CMAKE_CURRENT_SOURCE_DIR}/ImportExport/ImportMatcher.cpp ${CMAKE_CURRENT_SOURCE_DIR}/ImportExport/XMLHandler.cpp ${CMAKE_CURRENT_SOURCE_DIR}/ImportExport/MiniViewer.cpp ${CMAKE_CURRENT_SOURCE_DIR}/ImportExport/ImportHandler.cpp ${CMAKE_CURRENT_SOURCE_DIR}/ImportExport/ImageRow.cpp ${CMAKE_CURRENT_SOURCE_DIR}/ImportExport/ImportDialog.cpp ${CMAKE_CURRENT_SOURCE_DIR}/ImportExport/ImportSettings.cpp ${CMAKE_CURRENT_SOURCE_DIR}/ImportExport/KimFileReader.cpp ${CMAKE_CURRENT_SOURCE_DIR}/ImportExport/MD5CheckPage.cpp ${CMAKE_CURRENT_SOURCE_DIR}/ImportExport/Logging.cpp ) set(libAnnotationDialog_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/AnnotationDialog/documentation.h ${CMAKE_CURRENT_SOURCE_DIR}/AnnotationDialog/Dialog.cpp ${CMAKE_CURRENT_SOURCE_DIR}/AnnotationDialog/ListSelect.cpp ${CMAKE_CURRENT_SOURCE_DIR}/AnnotationDialog/ImagePreview.cpp ${CMAKE_CURRENT_SOURCE_DIR}/AnnotationDialog/ImagePreviewWidget.cpp ${CMAKE_CURRENT_SOURCE_DIR}/AnnotationDialog/DateEdit.cpp ${CMAKE_CURRENT_SOURCE_DIR}/AnnotationDialog/CompletableLineEdit.cpp ${CMAKE_CURRENT_SOURCE_DIR}/AnnotationDialog/ListViewItemHider.cpp ${CMAKE_CURRENT_SOURCE_DIR}/AnnotationDialog/ShowSelectionOnlyManager.cpp ${CMAKE_CURRENT_SOURCE_DIR}/AnnotationDialog/ShortCutManager.cpp ${CMAKE_CURRENT_SOURCE_DIR}/AnnotationDialog/ResizableFrame.cpp ${CMAKE_CURRENT_SOURCE_DIR}/AnnotationDialog/DescriptionEdit.cpp ${CMAKE_CURRENT_SOURCE_DIR}/AnnotationDialog/AreaTagSelectDialog.cpp ${CMAKE_CURRENT_SOURCE_DIR}/AnnotationDialog/Logging.cpp ${CMAKE_CURRENT_SOURCE_DIR}/AnnotationDialog/enums.cpp ) set(libBrowser_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/Browser/documentation.h ${CMAKE_CURRENT_SOURCE_DIR}/Browser/BrowserWidget.cpp ${CMAKE_CURRENT_SOURCE_DIR}/Browser/BrowserPage.cpp ${CMAKE_CURRENT_SOURCE_DIR}/Browser/OverviewPage.cpp ${CMAKE_CURRENT_SOURCE_DIR}/Browser/CategoryPage.cpp ${CMAKE_CURRENT_SOURCE_DIR}/Browser/ImageViewPage.cpp ${CMAKE_CURRENT_SOURCE_DIR}/Browser/TreeFilter.cpp ${CMAKE_CURRENT_SOURCE_DIR}/Browser/Breadcrumb.cpp ${CMAKE_CURRENT_SOURCE_DIR}/Browser/BreadcrumbList.cpp ${CMAKE_CURRENT_SOURCE_DIR}/Browser/AbstractCategoryModel.cpp ${CMAKE_CURRENT_SOURCE_DIR}/Browser/FlatCategoryModel.cpp ${CMAKE_CURRENT_SOURCE_DIR}/Browser/TreeCategoryModel.cpp ${CMAKE_CURRENT_SOURCE_DIR}/Browser/CenteringIconView.cpp ${CMAKE_CURRENT_SOURCE_DIR}/Browser/Logging.cpp ${CMAKE_CURRENT_SOURCE_DIR}/Browser/enums.cpp ) set(libExif_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/Exif/documentation.h ${CMAKE_CURRENT_SOURCE_DIR}/Exif/Database.cpp ${CMAKE_CURRENT_SOURCE_DIR}/Exif/InfoDialog.cpp ${CMAKE_CURRENT_SOURCE_DIR}/Exif/SearchDialog.cpp ${CMAKE_CURRENT_SOURCE_DIR}/Exif/SearchInfo.cpp ${CMAKE_CURRENT_SOURCE_DIR}/Exif/TreeView.cpp ${CMAKE_CURRENT_SOURCE_DIR}/Exif/Info.cpp ${CMAKE_CURRENT_SOURCE_DIR}/Exif/RangeWidget.cpp ${CMAKE_CURRENT_SOURCE_DIR}/Exif/DatabaseElement.cpp ${CMAKE_CURRENT_SOURCE_DIR}/Exif/ReReadDialog.cpp ${CMAKE_CURRENT_SOURCE_DIR}/Exif/Grid.cpp ${CMAKE_CURRENT_SOURCE_DIR}/Exif/Logging.cpp ${CMAKE_CURRENT_SOURCE_DIR}/Exif/SearchDialogSettings.cpp ${CMAKE_CURRENT_SOURCE_DIR}/Settings/ExifPage.cpp ) set(libBackgroundTaskManager_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/BackgroundTaskManager/JobInterface.cpp ${CMAKE_CURRENT_SOURCE_DIR}/BackgroundTaskManager/JobManager.cpp ${CMAKE_CURRENT_SOURCE_DIR}/BackgroundTaskManager/StatusIndicator.cpp ${CMAKE_CURRENT_SOURCE_DIR}/BackgroundTaskManager/JobViewer.cpp ${CMAKE_CURRENT_SOURCE_DIR}/BackgroundTaskManager/JobModel.cpp ${CMAKE_CURRENT_SOURCE_DIR}/BackgroundTaskManager/JobInfo.cpp ${CMAKE_CURRENT_SOURCE_DIR}/BackgroundTaskManager/CompletedJobInfo.cpp ${CMAKE_CURRENT_SOURCE_DIR}/BackgroundTaskManager/Priority.cpp ${CMAKE_CURRENT_SOURCE_DIR}/BackgroundTaskManager/PriorityQueue.cpp ${CMAKE_CURRENT_SOURCE_DIR}/BackgroundTaskManager/Logging.cpp ) set(libBackgroundJobs_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/BackgroundJobs/SearchForVideosWithoutLengthInfo.cpp ${CMAKE_CURRENT_SOURCE_DIR}/BackgroundJobs/ReadVideoLengthJob.cpp ${CMAKE_CURRENT_SOURCE_DIR}/BackgroundJobs/SearchForVideosWithoutVideoThumbnailsJob.cpp ${CMAKE_CURRENT_SOURCE_DIR}/BackgroundJobs/HandleVideoThumbnailRequestJob.cpp ${CMAKE_CURRENT_SOURCE_DIR}/BackgroundJobs/ExtractOneThumbnailJob.cpp ) set(libRemoteControl_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/RemoteControl/RemoteCommand.cpp ${CMAKE_CURRENT_SOURCE_DIR}/RemoteControl/RemoteConnection.cpp ${CMAKE_CURRENT_SOURCE_DIR}/RemoteControl/Server.cpp ${CMAKE_CURRENT_SOURCE_DIR}/RemoteControl/RemoteInterface.cpp ${CMAKE_CURRENT_SOURCE_DIR}/RemoteControl/SearchInfo.cpp ${CMAKE_CURRENT_SOURCE_DIR}/RemoteControl/RemoteImageRequest.cpp ${CMAKE_CURRENT_SOURCE_DIR}/RemoteControl/ImageNameStore.cpp ${CMAKE_CURRENT_SOURCE_DIR}/RemoteControl/ConnectionIndicator.cpp ${CMAKE_CURRENT_SOURCE_DIR}/RemoteControl/Logging.cpp ) set(libMap_SRCS) if(KF5KGeoMap_FOUND) set(libMap_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/Browser/GeoPositionPage.cpp ${CMAKE_CURRENT_SOURCE_DIR}/Browser/PositionBrowserWidget.cpp ${CMAKE_CURRENT_SOURCE_DIR}/Map/MapView.cpp ${CMAKE_CURRENT_SOURCE_DIR}/Map/MapMarkerModelHelper.cpp ${CMAKE_CURRENT_SOURCE_DIR}/Map/SearchMarkerTiler.cpp ${CMAKE_CURRENT_SOURCE_DIR}/Map/Logging.cpp ) endif() add_subdirectory(images) add_subdirectory(icons) add_subdirectory(demo) add_subdirectory(themes) add_subdirectory(scripts) add_subdirectory(doc) ########### next target ############### set(kphotoalbum_SRCS main.cpp ${libdatebar_SRCS} ${libSettings_SRCS} ${libsurvey_SRCS} ${libxmldb_SRCS} ${libThumbnailView_SRCS} ${libPlugins_SRCS} ${libViewer_SRCS} ${libCategoryListView_SRCS} ${libHTMLGenerator_SRCS} ${libMainWindow_SRCS} ${libImageManager_SRCS} ${libDB_SRCS} ${libImportExport_SRCS} ${libAnnotationDialog_SRCS} ${libExif_SRCS} ${libBrowser_SRCS} ${libBackgroundTaskManager_SRCS} ${libBackgroundJobs_SRCS} ${libRemoteControl_SRCS} ${libMap_SRCS} ${libUtilities_SRCS} # add doxygen headers so that they get visibiltiy in IDEs: ${CMAKE_CURRENT_SOURCE_DIR}/documentation/coding-standards.h ${CMAKE_CURRENT_SOURCE_DIR}/documentation/mainpage.h ${CMAKE_CURRENT_SOURCE_DIR}/documentation/phrase-book.h ${CMAKE_CURRENT_SOURCE_DIR}/documentation/videothumbnails.h ) add_executable(kphotoalbum ${kphotoalbum_SRCS}) add_dependencies(kphotoalbum UpdateVersion) # External components target_link_libraries(kphotoalbum ${JPEG_LIBRARY} ${EXIV2_LIBRARIES} KF5::Archive KF5::Completion KF5::ConfigCore KF5::ConfigGui KF5::CoreAddons KF5::I18n KF5::IconThemes KF5::JobWidgets KF5::KIOCore KF5::KIOWidgets KF5::TextWidgets KF5::XmlGui KF5::WidgetsAddons Phonon::phonon4qt5 Qt5::Network Qt5::Sql ) if(KF5Kipi_FOUND) target_link_libraries(kphotoalbum KF5::Kipi) endif() if(KF5KDcraw_FOUND) target_link_libraries(kphotoalbum KF5::KDcraw) endif() if(KF5KGeoMap_FOUND) target_link_libraries(kphotoalbum KF5::KGeoMap ) endif() install(TARGETS kphotoalbum ${KDE_INSTALL_TARGETS_DEFAULT_ARGS}) ########### install files ############### install(PROGRAMS org.kde.kphotoalbum.desktop org.kde.kphotoalbum-import.desktop DESTINATION ${KDE_INSTALL_APPDIR}) install(FILES kphotoalbumrc DESTINATION ${KDE_INSTALL_CONFDIR}) install(FILES tips default-setup DESTINATION ${KDE_INSTALL_DATADIR}/kphotoalbum) install(FILES kphotoalbumui.rc DESTINATION ${KDE_INSTALL_KXMLGUI5DIR}/kphotoalbum) install(FILES org.kde.kphotoalbum.appdata.xml DESTINATION ${KDE_INSTALL_METAINFODIR}) configure_file(config-kpa-kdcraw.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config-kpa-kdcraw.h) configure_file(config-kpa-kipi.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config-kpa-kipi.h) configure_file(config-kpa-kgeomap.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config-kpa-kgeomap.h) feature_summary(WHAT ALL FATAL_ON_MISSING_REQUIRED_PACKAGES) # vi:expandtab:tabstop=4 shiftwidth=4: diff --git a/DB/ImageInfoPtr.h b/DB/ImageInfoPtr.h index 0ab9b1e1..6d694060 100644 --- a/DB/ImageInfoPtr.h +++ b/DB/ImageInfoPtr.h @@ -1,30 +1,30 @@ /* 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 IMAGEINFOPTR_H #define IMAGEINFOPTR_H #include namespace DB { class ImageInfo; - typedef QExplicitlySharedDataPointer ImageInfoPtr; + using ImageInfoPtr = QExplicitlySharedDataPointer; } #endif /* IMAGEINFOPTR_H */ // vi:expandtab:tabstop=4 shiftwidth=4: diff --git a/RemoteControl/RemoteInterface.cpp b/RemoteControl/RemoteInterface.cpp index a19631ea..df071ea1 100644 --- a/RemoteControl/RemoteInterface.cpp +++ b/RemoteControl/RemoteInterface.cpp @@ -1,274 +1,274 @@ /* Copyright (C) 2014 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 "RemoteInterface.h" #include #include #include #include #include #include #include #include #include #include "Browser/FlatCategoryModel.h" #include "DB/Category.h" #include "DB/CategoryCollection.h" #include "DB/CategoryPtr.h" #include "DB/ImageDB.h" #include "DB/ImageInfo.h" #include "DB/ImageInfoPtr.h" #include "DB/ImageSearchInfo.h" #include "ImageManager/AsyncLoader.h" #include "MainWindow/DirtyIndicator.h" -#include "Utilities/Util.h" +#include "Utilities/DescriptionUtil.h" #include "RemoteCommand.h" #include "RemoteImageRequest.h" #include "Server.h" #include "Types.h" using namespace RemoteControl; RemoteInterface& RemoteInterface::instance() { static RemoteInterface instance; return instance; } RemoteInterface::RemoteInterface(QObject *parent) : QObject(parent), m_connection(new Server(this)) { connect(m_connection, SIGNAL(gotCommand(RemoteCommand)), this, SLOT(handleCommand(RemoteCommand))); connect(m_connection, SIGNAL(connected()), this, SIGNAL(connected())); connect(m_connection, SIGNAL(disConnected()), this, SIGNAL(disConnected())); connect(m_connection, SIGNAL(stoppedListening()), this, SIGNAL(stoppedListening())); } DB::ImageSearchInfo RemoteInterface::convert(const SearchInfo& searchInfo) const { DB::ImageSearchInfo dbSearchInfo; QString category; QString value; for (auto item : searchInfo.values()) { std::tie(category, value) = item; dbSearchInfo.addAnd(category, value); } return dbSearchInfo; } void RemoteInterface::pixmapLoaded(ImageManager::ImageRequest* request, const QImage& image) { m_connection->sendCommand(ThumbnailResult(m_imageNameStore[request->databaseFileName()], QString(), image, static_cast(request)->type())); } bool RemoteInterface::requestStillNeeded(const DB::FileName& fileName) { return m_activeReuqest.contains(fileName); } void RemoteInterface::listen(QHostAddress address) { m_connection->listen(address); emit listening(); } void RemoteInterface::stopListening() { m_connection->stopListening(); } void RemoteInterface::connectTo(const QHostAddress& address) { m_connection->connectToTcpServer(address); } void RemoteInterface::handleCommand(const RemoteCommand& command) { if (command.commandType() == CommandType::SearchRequest) { const SearchRequest& searchCommand = static_cast(command); if (searchCommand.type == SearchType::Categories) sendCategoryNames(searchCommand); else if (searchCommand.type == SearchType::CategoryItems) sendCategoryValues(searchCommand); else sendImageSearchResult(searchCommand.searchInfo); } else if (command.commandType() == CommandType::ThumbnailRequest) requestThumbnail(static_cast(command)); else if (command.commandType() == CommandType::ThumbnailCancelRequest) cancelRequest(static_cast(command)); else if (command.commandType() == CommandType::ImageDetailsRequest) sendImageDetails(static_cast(command)); else if (command.commandType() == CommandType::StaticImageRequest) sendHomePageImages(static_cast(command)); else if (command.commandType() == CommandType::ToggleTokenRequest) setToken(static_cast(command)); } void RemoteInterface::sendCategoryNames(const SearchRequest& search) { const DB::ImageSearchInfo dbSearchInfo = convert(search.searchInfo); CategoryListResult command; for (const DB::CategoryPtr& category : DB::ImageDB::instance()->categoryCollection()->categories()) { if (category->type() == DB::Category::MediaTypeCategory) continue; QMap images = DB::ImageDB::instance()->classify( dbSearchInfo, category->name(), DB::Image ); QMap videos = DB::ImageDB::instance()->classify( dbSearchInfo, category->name(), DB::Video ); const bool enabled = (images.count() /*+ videos.count()*/ > 1); CategoryViewType type = (category->viewType() == DB::Category::IconView || category->viewType() == DB::Category::ThumbedIconView) ? Types::CategoryIconView : Types::CategoryListView; const QImage icon = category->icon(search.size, enabled ? KIconLoader::DefaultState : KIconLoader::DisabledState).toImage(); command.categories.append({category->name(), icon, enabled, type}); } m_connection->sendCommand(command); } void RemoteInterface::sendCategoryValues(const SearchRequest& search) { const DB::ImageSearchInfo dbSearchInfo = convert(search.searchInfo); const QString categoryName = search.searchInfo.currentCategory(); const DB::CategoryPtr category = DB::ImageDB::instance()->categoryCollection()->categoryForName(search.searchInfo.currentCategory()); Browser::FlatCategoryModel model(category, dbSearchInfo); if (category->viewType() == DB::Category::IconView || category->viewType() == DB::Category::ThumbedIconView) { QList result; std::transform( model.m_items.begin(), model.m_items.end(), std::back_inserter(result), [this,categoryName] (const QString itemName) { return m_imageNameStore.idForCategory(categoryName,itemName); }); m_connection->sendCommand(SearchResult(SearchType::CategoryItems, result)); } else { m_connection->sendCommand(CategoryItemsResult(model.m_items)); } } void RemoteInterface::sendImageSearchResult(const SearchInfo& search) { const DB::FileNameList files = DB::ImageDB::instance()->search(convert(search), true /* Require on disk */); DB::FileNameList stacksRemoved; QList result; std::remove_copy_if(files.begin(), files.end(), std::back_inserter(stacksRemoved), [] (const DB::FileName& file) { // Only include unstacked images, and the top of stacked images. // And also exclude videos return DB::ImageDB::instance()->info(file)->stackOrder() > 1 || DB::ImageDB::instance()->info(file)->isVideo(); }); std::transform(stacksRemoved.begin(), stacksRemoved.end(), std::back_inserter(result), [this](const DB::FileName& fileName) { return m_imageNameStore[fileName]; }); m_connection->sendCommand(SearchResult(SearchType::Images, result)); } void RemoteInterface::requestThumbnail(const ThumbnailRequest& command) { if (command.type == ViewType::CategoryItems) { auto tuple = m_imageNameStore.categoryForId(command.imageId); QString categoryName = tuple.first; QString itemName = tuple.second; const DB::CategoryPtr category = DB::ImageDB::instance()->categoryCollection()->categoryForName(categoryName); QImage image = category->categoryImage( categoryName, itemName, command.size.width(), command.size.height()).toImage(); m_connection->sendCommand(ThumbnailResult(command.imageId, itemName, image,ViewType::CategoryItems)); } else { const DB::FileName fileName = m_imageNameStore[command.imageId]; const DB::ImageInfoPtr info = DB::ImageDB::instance()->info(fileName); const int angle = info->angle(); m_activeReuqest.insert(fileName); QSize size = command.size; if (!size.isValid()) { // Request for full screen image. size = info->size(); } RemoteImageRequest* request = new RemoteImageRequest(fileName, size, angle, command.type, this); ImageManager::AsyncLoader::instance()->load(request); } } void RemoteInterface::cancelRequest(const ThumbnailCancelRequest& command) { m_activeReuqest.remove(m_imageNameStore[command.imageId]); } void RemoteInterface::sendImageDetails(const ImageDetailsRequest& command) { const DB::FileName fileName = m_imageNameStore[command.imageId]; const DB::ImageInfoPtr info = DB::ImageDB::instance()->info(fileName); ImageDetailsResult result; result.fileName = fileName.relative(); result.date = info->date().toString(); result.description = info->description(); result.categories.clear(); for (const QString& categoryName : info->availableCategories()) { DB::CategoryPtr category = DB::ImageDB::instance()->categoryCollection()->categoryForName(categoryName); CategoryItemDetailsList list; for ( const QString& item : info->itemsOfCategory(categoryName) ) { const QString age = Utilities::formatAge(category, item, info); list.append(CategoryItemDetails(item, age)); } result.categories[categoryName] = list; } m_connection->sendCommand(result); } void RemoteInterface::sendHomePageImages(const StaticImageRequest& command) { const int size = command.size; QPixmap homeIcon = KIconLoader::global()->loadIcon( QString::fromUtf8("go-home"), KIconLoader::Desktop, size); QPixmap kphotoalbumIcon = KIconLoader::global()->loadIcon( QString::fromUtf8("kphotoalbum"), KIconLoader::Desktop, size); QPixmap discoverIcon = KIconLoader::global()->loadIcon( QString::fromUtf8("edit-find"), KIconLoader::Desktop, size); m_connection->sendCommand(StaticImageResult(homeIcon.toImage(), kphotoalbumIcon.toImage(), discoverIcon.toImage())); } void RemoteInterface::setToken(const ToggleTokenRequest& command) { const DB::FileName fileName = m_imageNameStore[command.imageId]; DB::ImageInfoPtr info = DB::ImageDB::instance()->info(fileName); DB::CategoryPtr tokensCategory = DB::ImageDB::instance()->categoryCollection()->categoryForSpecial(DB::Category::TokensCategory); if (command.state == ToggleTokenRequest::On) info->addCategoryInfo(tokensCategory->name(), command.token); else info->removeCategoryInfo(tokensCategory->name(), command.token); MainWindow::DirtyIndicator::markDirty(); } diff --git a/Utilities/Util.cpp b/Utilities/DescriptionUtil.cpp similarity index 56% copy from Utilities/Util.cpp copy to Utilities/DescriptionUtil.cpp index 66b67d71..c64e20aa 100644 --- a/Utilities/Util.cpp +++ b/Utilities/DescriptionUtil.cpp @@ -1,551 +1,285 @@ /* 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 "Util.h" +#include "DescriptionUtil.h" #include "Logging.h" #include #include #include -#include -#include -#include #include #include -#include -#include -#include -#include -#include +#include #include -#include -#include -#include #include #include -extern "C" { -#include -} - /** * Add a line label + info text to the result text if info is not empty. * If the result already contains something, a HTML newline is added first. * To be used in createInfoText(). */ static void AddNonEmptyInfo(const QString &label, const QString &info, QString *result) { if (info.isEmpty()) return; if (!result->isEmpty()) *result += QString::fromLatin1("
"); result->append(label).append(info); } /** * Given an ImageInfoPtr this function will create an HTML blob about the * image. The blob is used in the viewer and in the tool tip box from the * thumbnail view. * * As the HTML text is created, the parameter linkMap is filled with * information about hyperlinks. The map maps from an index to a pair of * (categoryName, categoryItem). This linkMap is used when the user selects * one of the hyberlinks. */ QString Utilities::createInfoText( DB::ImageInfoPtr info, QMap< int,QPair >* linkMap ) { Q_ASSERT( info ); QString result; if ( Settings::SettingsData::instance()->showFilename() ) { AddNonEmptyInfo(i18n("File Name: "), info->fileName().relative(), &result); } if ( Settings::SettingsData::instance()->showDate() ) { AddNonEmptyInfo(i18n("Date: "), info->date().toString( Settings::SettingsData::instance()->showTime() ? true : false ), &result); } /* XXX */ if ( Settings::SettingsData::instance()->showImageSize() && info->mediaType() == DB::Image) { const QSize imageSize = info->size(); // Do not add -1 x -1 text if (imageSize.width() >= 0 && imageSize.height() >= 0) { const double megapix = imageSize.width() * imageSize.height() / 1000000.0; QString info = i18nc("width x height","%1x%2" ,QString::number(imageSize.width()) ,QString::number(imageSize.height())); if (megapix > 0.05) { info += i18nc("short for: x megapixels"," (%1MP)" ,QString::number(megapix, 'f', 1)); } const double aspect = (double) imageSize.width() / (double) imageSize.height(); if (aspect > 1) info += i18nc("aspect ratio"," (%1:1)" ,QLocale::system().toString(aspect, 'f', 2)); else if (aspect >= 0.995 && aspect < 1.005) info += i18nc("aspect ratio"," (1:1)"); else info += i18nc("aspect ratio"," (1:%1)" ,QLocale::system().toString(1.0/aspect, 'f', 2)); AddNonEmptyInfo(i18n("Image Size: "), info, &result); } } if ( Settings::SettingsData::instance()->showRating() ) { if ( info->rating() != -1 ) { if ( ! result.isEmpty() ) result += QString::fromLatin1("
"); QUrl rating; rating.setScheme(QString::fromLatin1("kratingwidget")); // we don't use the host part, but if we don't set it, we can't use port: rating.setHost(QString::fromLatin1("int")); rating.setPort(qMin( qMax( static_cast(0), info->rating() ), static_cast(10))); result += QString::fromLatin1("").arg( rating.toString(QUrl::None) ); } } QList categories = DB::ImageDB::instance()->categoryCollection()->categories(); int link = 0; Q_FOREACH( const DB::CategoryPtr category, categories ) { const QString categoryName = category->name(); if ( category->doShow() ) { StringSet items = info->itemsOfCategory( categoryName ); if (Settings::SettingsData::instance()->hasUntaggedCategoryFeatureConfigured() && ! Settings::SettingsData::instance()->untaggedImagesTagVisible()) { if (categoryName == Settings::SettingsData::instance()->untaggedCategory()) { if (items.contains(Settings::SettingsData::instance()->untaggedTag())) { items.remove(Settings::SettingsData::instance()->untaggedTag()); } } } if (!items.empty()) { QString title = QString::fromUtf8("%1: ").arg(category->name()); QString infoText; bool first = true; Q_FOREACH( const QString &item, items) { if ( first ) first = false; else infoText += QString::fromLatin1( ", " ); if ( linkMap ) { ++link; (*linkMap)[link] = QPair( categoryName, item ); infoText += QString::fromLatin1( "%2").arg( link ).arg( item ); infoText += formatAge(category, item, info); } else infoText += item; } AddNonEmptyInfo(title, infoText, &result); } } } if ( Settings::SettingsData::instance()->showLabel()) { AddNonEmptyInfo(i18n("Label: "), info->label(), &result); } if ( Settings::SettingsData::instance()->showDescription() && !info->description().trimmed().isEmpty() ) { AddNonEmptyInfo(i18n("Description: "), info->description(), &result); } QString exifText; if ( Settings::SettingsData::instance()->showEXIF() ) { typedef QMap ExifMap; typedef ExifMap::const_iterator ExifMapIterator; ExifMap exifMap = Exif::Info::instance()->infoForViewer( info->fileName(), Settings::SettingsData::instance()->iptcCharset() ); for( ExifMapIterator exifIt = exifMap.constBegin(); exifIt != exifMap.constEnd(); ++exifIt ) { if ( exifIt.key().startsWith( QString::fromLatin1( "Exif." ) ) ) for ( QStringList::const_iterator valuesIt = exifIt.value().constBegin(); valuesIt != exifIt.value().constEnd(); ++valuesIt ) { QString exifName = exifIt.key().split( QChar::fromLatin1('.') ).last(); AddNonEmptyInfo(QString::fromLatin1( "%1: ").arg(exifName), *valuesIt, &exifText); } } QString iptcText; for( ExifMapIterator exifIt = exifMap.constBegin(); exifIt != exifMap.constEnd(); ++exifIt ) { if ( !exifIt.key().startsWith( QString::fromLatin1( "Exif." ) ) ) for ( QStringList::const_iterator valuesIt = exifIt.value().constBegin(); valuesIt != exifIt.value().constEnd(); ++valuesIt ) { QString iptcName = exifIt.key().split( QChar::fromLatin1('.') ).last(); AddNonEmptyInfo(QString::fromLatin1( "%1: ").arg(iptcName), *valuesIt, &iptcText); } } if ( !iptcText.isEmpty() ) { if ( exifText.isEmpty() ) exifText = iptcText; else exifText += QString::fromLatin1( "
" ) + iptcText; } } if ( !result.isEmpty() && !exifText.isEmpty() ) result += QString::fromLatin1( "
" ); result += exifText; return result; } using DateSpec = QPair; DateSpec dateDiff(const QDate& birthDate, const QDate& imageDate) { const int bday = birthDate.day(); const int iday = imageDate.day(); const int bmonth = birthDate.month(); const int imonth = imageDate.month(); const int byear = birthDate.year(); const int iyear = imageDate.year(); // Image before birth const int diff = birthDate.daysTo(imageDate); if (diff < 0) return qMakePair(0, 'I'); if (diff < 31) return qMakePair(diff, 'D'); int months = (iyear-byear)*12; months += (imonth-bmonth); months += (iday >= bday) ? 0 : -1; if ( months < 24) return qMakePair(months, 'M'); else return qMakePair(months/12, 'Y'); } QString formatDate(const DateSpec& date) { if (date.second == 'I') return {}; else if (date.second == 'D') return i18np("1 day", "%1 days", date.first); else if (date.second == 'M') return i18np("1 month", "%1 months", date.first); else return i18np("1 year", "%1 years", date.first); } -void test() { +void test() +{ Q_ASSERT(formatDate(dateDiff(QDate(1971,7,11), QDate(1971,7,11))) == QString::fromLatin1("0 days")); Q_ASSERT(formatDate(dateDiff(QDate(1971,7,11), QDate(1971,8,10))) == QString::fromLatin1("30 days")); Q_ASSERT(formatDate(dateDiff(QDate(1971,7,11), QDate(1971,8,11))) == QString::fromLatin1("1 month")); Q_ASSERT(formatDate(dateDiff(QDate(1971,7,11), QDate(1971,8,12))) == QString::fromLatin1("1 month")); Q_ASSERT(formatDate(dateDiff(QDate(1971,7,11), QDate(1971,9,10))) == QString::fromLatin1("1 month")); Q_ASSERT(formatDate(dateDiff(QDate(1971,7,11), QDate(1971,9,11))) == QString::fromLatin1("2 month")); Q_ASSERT(formatDate(dateDiff(QDate(1971,7,11), QDate(1972,6,10))) == QString::fromLatin1("10 month")); Q_ASSERT(formatDate(dateDiff(QDate(1971,7,11), QDate(1972,6,11))) == QString::fromLatin1("11 month")); Q_ASSERT(formatDate(dateDiff(QDate(1971,7,11), QDate(1972,6,12))) == QString::fromLatin1("11 month")); Q_ASSERT(formatDate(dateDiff(QDate(1971,7,11), QDate(1972,7,10))) == QString::fromLatin1("11 month")); Q_ASSERT(formatDate(dateDiff(QDate(1971,7,11), QDate(1972,7,11))) == QString::fromLatin1("12 month")); Q_ASSERT(formatDate(dateDiff(QDate(1971,7,11), QDate(1972,7,12))) == QString::fromLatin1("12 month")); Q_ASSERT(formatDate(dateDiff(QDate(1971,7,11), QDate(1972,12,11))) == QString::fromLatin1("17 month")); Q_ASSERT(formatDate(dateDiff(QDate(1971,7,11), QDate(1973,7,11))) == QString::fromLatin1("2 years")); } QString Utilities::formatAge(DB::CategoryPtr category, const QString &item, DB::ImageInfoPtr info) { // test(); // I wish I could get my act together to set up a test suite. const QDate birthDate = category->birthDate(item); const QDate start = info->date().start().date(); const QDate end = info->date().end().date(); if (birthDate.isNull() || start.isNull()) return {}; if ( start == end) return QString::fromUtf8(" (%1)").arg(formatDate(dateDiff(birthDate, start))); else { DateSpec lower = dateDiff(birthDate,start); DateSpec upper = dateDiff(birthDate,end); if (lower == upper) return QString::fromUtf8(" (%1)").arg(formatDate(lower)); else if (lower.second == 'I') return QString::fromUtf8(" (< %1)").arg(formatDate(upper)); else { if (lower.second == upper.second) return QString::fromUtf8(" (%1-%2)").arg(lower.first).arg(formatDate(upper)); else return QString::fromUtf8(" (%1-%2)").arg(formatDate(lower)).arg(formatDate(upper)); } } } - -void Utilities::checkForBackupFile( const QString& fileName, const QString& message ) -{ - QString backupName = QFileInfo( fileName ).absolutePath() + QString::fromLatin1("/.#") + QFileInfo( fileName ).fileName(); - QFileInfo backUpFile( backupName); - QFileInfo indexFile( fileName ); - - if ( !backUpFile.exists() || indexFile.lastModified() > backUpFile.lastModified() || backUpFile.size() == 0 ) - if ( !( backUpFile.exists() && !message.isNull() ) ) - return; - - int code; - if ( message.isNull() ) - code = KMessageBox::questionYesNo( nullptr, i18n("Autosave file '%1' exists (size %3 KB) and is newer than '%2'. " - "Should the autosave file be used?", backupName, fileName, backUpFile.size() >> 10 ), - i18n("Found Autosave File") ); - else if ( backUpFile.size() > 0 ) - code = KMessageBox::warningYesNo( nullptr,i18n( "

Error: Cannot use current database file '%1':

%2

" - "

Do you want to use autosave (%3 - size %4 KB) instead of exiting?

" - "

(Manually verifying and copying the file might be a good idea)

", fileName, message, backupName, backUpFile.size() >> 10 ), - i18n("Recover from Autosave?") ); - else { - KMessageBox::error( nullptr, i18n( "

Error: %1

Also autosave file is empty, check manually " - "if numbered backup files exist and can be used to restore index.xml.

", message ) ); - exit(-1); - } - - if ( code == KMessageBox::Yes ) { - QFile in( backupName ); - if ( in.open( QIODevice::ReadOnly ) ) { - QFile out( fileName ); - if (out.open( QIODevice::WriteOnly ) ) { - char data[1024]; - int len; - while ( (len = in.read( data, 1024 ) ) ) - out.write( data, len ); - } - } - } else if ( !message.isNull() ) - exit(-1); -} - -bool Utilities::ctrlKeyDown() -{ - return QApplication::keyboardModifiers() & Qt::ControlModifier; -} - -bool Utilities::copy( const QString& from, const QString& to ) -{ - if ( QFileInfo(to).exists()) - QDir().remove(to); - return QFile::copy(from,to); -} - -bool Utilities::makeHardLink( const QString& from, const QString& to ) -{ - if (link(from.toLocal8Bit().constData(), to.toLocal8Bit().constData()) != 0) - return false; - else - return true; -} - -bool Utilities::makeSymbolicLink( const QString& from, const QString& to ) -{ - if (symlink(from.toLocal8Bit().constData(), to.toLocal8Bit().constData()) != 0) - return false; - else - return true; -} - -bool Utilities::canReadImage( const DB::FileName& fileName ) -{ - bool fastMode = !Settings::SettingsData::instance()->ignoreFileExtension(); - QMimeDatabase::MatchMode mode = fastMode ? QMimeDatabase::MatchExtension : QMimeDatabase::MatchDefault; - QMimeDatabase db; - QMimeType mimeType = db.mimeTypeForFile( fileName.absolute(), mode ); - - return QImageReader::supportedMimeTypes().contains( mimeType.name().toUtf8() ) - || ImageManager::ImageDecoder::mightDecode( fileName ); -} - - -QString Utilities::locateDataFile(const QString& fileName) -{ - return QStandardPaths::locate(QStandardPaths::DataLocation, fileName); -} - -QString Utilities::readFile( const QString& fileName ) -{ - if ( fileName.isEmpty() ) { - KMessageBox::error( nullptr, i18n("

No file name given!

") ); - return QString(); - } - - QFile file( fileName ); - if ( !file.open( QIODevice::ReadOnly ) ) { - //KMessageBox::error( nullptr, i18n("Could not open file %1").arg( fileName ) ); - return QString(); - } - - QTextStream stream( &file ); - QString content = stream.readAll(); - file.close(); - - return content; -} - -namespace Utilities -{ -QString normalizedFileName( const QString& fileName ) -{ - return QFileInfo(fileName).absoluteFilePath(); -} - -QString dereferenceSymLinks( const QString& fileName ) -{ - QFileInfo fi(fileName); - int rounds = 256; - while (fi.isSymLink() && --rounds > 0) - fi = QFileInfo(fi.readLink()); - if (rounds == 0) - return QString(); - return fi.filePath(); -} -} - -QString Utilities::stripEndingForwardSlash( const QString& fileName ) -{ - static QString slash = QString::fromLatin1("/"); - if ( fileName.endsWith( slash ) ) - return fileName.left( fileName.length()-1); - else - return fileName; -} - -QString Utilities::relativeFolderName( const QString& fileName) -{ - int index= fileName.lastIndexOf( QChar::fromLatin1('/'), -1); - if (index == -1) - return QString(); - else - return fileName.left( index ); -} - -QString Utilities::absoluteImageFileName( const QString& relativeName ) -{ - return stripEndingForwardSlash( Settings::SettingsData::instance()->imageDirectory() ) + QString::fromLatin1( "/" ) + relativeName; -} - -QString Utilities::imageFileNameToAbsolute( const QString& fileName ) -{ - if ( fileName.startsWith( Settings::SettingsData::instance()->imageDirectory() ) ) - return fileName; - else if ( fileName.startsWith( QString::fromLatin1("file://") ) ) - return imageFileNameToAbsolute( fileName.mid( 7 ) ); // 7 == length("file://") - else if ( fileName.startsWith( QString::fromLatin1("/") ) ) - return QString(); // Not within our image root - else - return absoluteImageFileName( fileName ); -} - -bool operator>( const QPoint& p1, const QPoint& p2) -{ - return p1.y() > p2.y() || (p1.y() == p2.y() && p1.x() > p2.x() ); -} - -bool operator<( const QPoint& p1, const QPoint& p2) -{ - return p1.y() < p2.y() || ( p1.y() == p2.y() && p1.x() < p2.x() ); -} - -const QSet& Utilities::supportedVideoExtensions() -{ - static QSet videoExtensions; - if ( videoExtensions.empty() ) { - videoExtensions.insert( QString::fromLatin1( "3gp" ) ); - videoExtensions.insert( QString::fromLatin1( "avi" ) ); - videoExtensions.insert( QString::fromLatin1( "mp4" ) ); - videoExtensions.insert( QString::fromLatin1( "m4v" ) ); - videoExtensions.insert( QString::fromLatin1( "mpeg" ) ); - videoExtensions.insert( QString::fromLatin1( "mpg" ) ); - videoExtensions.insert( QString::fromLatin1( "qt" ) ); - videoExtensions.insert( QString::fromLatin1( "mov" ) ); - videoExtensions.insert( QString::fromLatin1( "moov" ) ); - videoExtensions.insert( QString::fromLatin1( "qtvr" ) ); - videoExtensions.insert( QString::fromLatin1( "rv" ) ); - videoExtensions.insert( QString::fromLatin1( "3g2" ) ); - videoExtensions.insert( QString::fromLatin1( "fli" ) ); - videoExtensions.insert( QString::fromLatin1( "flc" ) ); - videoExtensions.insert( QString::fromLatin1( "mkv" ) ); - videoExtensions.insert( QString::fromLatin1( "mng" ) ); - videoExtensions.insert( QString::fromLatin1( "asf" ) ); - videoExtensions.insert( QString::fromLatin1( "asx" ) ); - videoExtensions.insert( QString::fromLatin1( "wmp" ) ); - videoExtensions.insert( QString::fromLatin1( "wmv" ) ); - videoExtensions.insert( QString::fromLatin1( "ogm" ) ); - videoExtensions.insert( QString::fromLatin1( "rm" ) ); - videoExtensions.insert( QString::fromLatin1( "flv" ) ); - videoExtensions.insert( QString::fromLatin1( "webm" ) ); - videoExtensions.insert( QString::fromLatin1( "mts" ) ); - videoExtensions.insert( QString::fromLatin1( "ogg" ) ); - videoExtensions.insert( QString::fromLatin1( "ogv" ) ); - videoExtensions.insert( QString::fromLatin1( "m2ts" ) ); - } - return videoExtensions; -} -bool Utilities::isVideo( const DB::FileName& fileName ) -{ - QFileInfo fi( fileName.relative() ); - QString ext = fi.suffix().toLower(); - return supportedVideoExtensions().contains( ext ); -} - -bool Utilities::isRAW( const DB::FileName& fileName ) -{ - return ImageManager::RAWImageDecoder::isRAW( fileName ); -} - - -QImage Utilities::scaleImage(const QImage &image, int w, int h, Qt::AspectRatioMode mode ) -{ - return image.scaled( w, h, mode, Settings::SettingsData::instance()->smoothScale() ? Qt::SmoothTransformation : Qt::FastTransformation ); -} - -QImage Utilities::scaleImage(const QImage &image, const QSize& s, Qt::AspectRatioMode mode ) -{ - return scaleImage( image, s.width(), s.height(), mode ); -} - -QString Utilities::cStringWithEncoding( const char *c_str, const QString& charset ) -{ - QTextCodec* codec = QTextCodec::codecForName( charset.toLatin1() ); - if (!codec) - codec = QTextCodec::codecForLocale(); - return codec->toUnicode( c_str ); -} - -QColor Utilities::contrastColor( const QColor& col ) -{ - if ( col.red() < 127 && col.green() < 127 && col.blue() < 127 ) - return Qt::white; - else - return Qt::black; -} - -void Utilities::saveImage( const DB::FileName& fileName, const QImage& image, const char* format ) -{ - const QFileInfo info(fileName.absolute()); - QDir().mkpath(info.path()); - const bool ok = image.save(fileName.absolute(),format); - Q_ASSERT(ok); Q_UNUSED(ok); -} -// vi:expandtab:tabstop=4 shiftwidth=4: diff --git a/DB/ImageInfoPtr.h b/Utilities/DescriptionUtil.h similarity index 69% copy from DB/ImageInfoPtr.h copy to Utilities/DescriptionUtil.h index 0ab9b1e1..a6d6bce7 100644 --- a/DB/ImageInfoPtr.h +++ b/Utilities/DescriptionUtil.h @@ -1,30 +1,36 @@ /* 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 IMAGEINFOPTR_H -#define IMAGEINFOPTR_H -#include -namespace DB +#ifndef TEXTUTIL_H +#define TEXTUTIL_H +#include "DB/CategoryPtr.h" +#include "DB/ImageInfoPtr.h" + +#include +#include +#include + +namespace Utilities { - class ImageInfo; - typedef QExplicitlySharedDataPointer ImageInfoPtr; +QString createInfoText( DB::ImageInfoPtr info, QMap >* ); +QString formatAge(DB::CategoryPtr category,const QString& item, DB::ImageInfoPtr info); } -#endif /* IMAGEINFOPTR_H */ +#endif /* TEXTUTIL_H */ // vi:expandtab:tabstop=4 shiftwidth=4: diff --git a/Utilities/ToolTip.cpp b/Utilities/ToolTip.cpp index ddb94e33..35c301cd 100644 --- a/Utilities/ToolTip.cpp +++ b/Utilities/ToolTip.cpp @@ -1,101 +1,101 @@ /* 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 "ToolTip.h" #include "Settings/SettingsData.h" #include "DB/ImageDB.h" #include "ImageManager/ImageRequest.h" #include "ImageManager/AsyncLoader.h" #include -#include "Utilities/Util.h" +#include "Utilities/DescriptionUtil.h" namespace Utilities { ToolTip::ToolTip(QWidget *parent, Qt::WindowFlags f) : QLabel(parent, f), m_tmpFileForThumbnailView(nullptr) { setAlignment( Qt::AlignLeft | Qt::AlignTop ); setLineWidth(1); setMargin(1); setWindowOpacity(0.8); setAutoFillBackground(true); QPalette p = palette(); p.setColor(QPalette::Background, QColor(0,0,0,170)); // r,g,b,A p.setColor(QPalette::WindowText, Qt::white ); setPalette(p); } void ToolTip::requestImage( const DB::FileName& fileName ) { int size = Settings::SettingsData::instance()->previewSize(); DB::ImageInfoPtr info = DB::ImageDB::instance()->info( fileName ); if ( size != 0 ) { ImageManager::ImageRequest* request = new ImageManager::ImageRequest( fileName, QSize( size, size ), info->angle(), this ); request->setPriority( ImageManager::Viewer ); ImageManager::AsyncLoader::instance()->load( request ); } else renderToolTip(); } void ToolTip::pixmapLoaded(ImageManager::ImageRequest* request, const QImage& image) { const DB::FileName fileName = request->databaseFileName(); delete m_tmpFileForThumbnailView; m_tmpFileForThumbnailView = new QTemporaryFile(this); m_tmpFileForThumbnailView->open(); image.save(m_tmpFileForThumbnailView, "PNG" ); if ( fileName == m_currentFileName ) renderToolTip(); } void ToolTip::requestToolTip(const DB::FileName &fileName) { if ( fileName.isNull() || fileName == m_currentFileName) return; m_currentFileName = fileName; requestImage( fileName ); } void ToolTip::renderToolTip() { const int size = Settings::SettingsData::instance()->previewSize(); if ( size != 0 ) { setText( QString::fromLatin1("") .arg(m_tmpFileForThumbnailView->fileName()). arg(Utilities::createInfoText( DB::ImageDB::instance()->info( m_currentFileName ), nullptr ) ) ); } else setText( QString::fromLatin1("

%1

").arg( Utilities::createInfoText( DB::ImageDB::instance()->info( m_currentFileName ), nullptr ) ) ); setWordWrap( true ); resize( sizeHint() ); // m_view->setFocus(); show(); placeWindow(); } } // namespace Utilities // vi:expandtab:tabstop=4 shiftwidth=4: diff --git a/Utilities/Util.cpp b/Utilities/Util.cpp index 66b67d71..a578bd44 100644 --- a/Utilities/Util.cpp +++ b/Utilities/Util.cpp @@ -1,551 +1,299 @@ /* 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 "Util.h" #include "Logging.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include extern "C" { #include } -/** - * Add a line label + info text to the result text if info is not empty. - * If the result already contains something, a HTML newline is added first. - * To be used in createInfoText(). - */ -static void AddNonEmptyInfo(const QString &label, const QString &info, - QString *result) { - if (info.isEmpty()) - return; - if (!result->isEmpty()) - *result += QString::fromLatin1("
"); - result->append(label).append(info); -} - -/** - * Given an ImageInfoPtr this function will create an HTML blob about the - * image. The blob is used in the viewer and in the tool tip box from the - * thumbnail view. - * - * As the HTML text is created, the parameter linkMap is filled with - * information about hyperlinks. The map maps from an index to a pair of - * (categoryName, categoryItem). This linkMap is used when the user selects - * one of the hyberlinks. - */ -QString Utilities::createInfoText( DB::ImageInfoPtr info, QMap< int,QPair >* linkMap ) -{ - Q_ASSERT( info ); - - QString result; - if ( Settings::SettingsData::instance()->showFilename() ) { - AddNonEmptyInfo(i18n("File Name: "), info->fileName().relative(), &result); - } - - if ( Settings::SettingsData::instance()->showDate() ) { - AddNonEmptyInfo(i18n("Date: "), info->date().toString( Settings::SettingsData::instance()->showTime() ? true : false ), - &result); - } - - /* XXX */ - if ( Settings::SettingsData::instance()->showImageSize() && info->mediaType() == DB::Image) { - const QSize imageSize = info->size(); - // Do not add -1 x -1 text - if (imageSize.width() >= 0 && imageSize.height() >= 0) { - const double megapix = imageSize.width() * imageSize.height() / 1000000.0; - QString info = i18nc("width x height","%1x%2" - ,QString::number(imageSize.width()) - ,QString::number(imageSize.height())); - if (megapix > 0.05) { - info += i18nc("short for: x megapixels"," (%1MP)" - ,QString::number(megapix, 'f', 1)); - } - const double aspect = (double) imageSize.width() / (double) imageSize.height(); - if (aspect > 1) - info += i18nc("aspect ratio"," (%1:1)" - ,QLocale::system().toString(aspect, 'f', 2)); - else if (aspect >= 0.995 && aspect < 1.005) - info += i18nc("aspect ratio"," (1:1)"); - else - info += i18nc("aspect ratio"," (1:%1)" - ,QLocale::system().toString(1.0/aspect, 'f', 2)); - AddNonEmptyInfo(i18n("Image Size: "), info, &result); - } - } - - if ( Settings::SettingsData::instance()->showRating() ) { - if ( info->rating() != -1 ) { - if ( ! result.isEmpty() ) - result += QString::fromLatin1("
"); - QUrl rating; - rating.setScheme(QString::fromLatin1("kratingwidget")); - // we don't use the host part, but if we don't set it, we can't use port: - rating.setHost(QString::fromLatin1("int")); - rating.setPort(qMin( qMax( static_cast(0), info->rating() ), static_cast(10))); - result += QString::fromLatin1("").arg( rating.toString(QUrl::None) ); - } - } - - QList categories = DB::ImageDB::instance()->categoryCollection()->categories(); - int link = 0; - Q_FOREACH( const DB::CategoryPtr category, categories ) { - const QString categoryName = category->name(); - if ( category->doShow() ) { - StringSet items = info->itemsOfCategory( categoryName ); - - if (Settings::SettingsData::instance()->hasUntaggedCategoryFeatureConfigured() - && ! Settings::SettingsData::instance()->untaggedImagesTagVisible()) { - - if (categoryName == Settings::SettingsData::instance()->untaggedCategory()) { - if (items.contains(Settings::SettingsData::instance()->untaggedTag())) { - items.remove(Settings::SettingsData::instance()->untaggedTag()); - } - } - } - - if (!items.empty()) { - QString title = QString::fromUtf8("%1: ").arg(category->name()); - QString infoText; - bool first = true; - Q_FOREACH( const QString &item, items) { - if ( first ) - first = false; - else - infoText += QString::fromLatin1( ", " ); - - if ( linkMap ) { - ++link; - (*linkMap)[link] = QPair( categoryName, item ); - infoText += QString::fromLatin1( "%2").arg( link ).arg( item ); - infoText += formatAge(category, item, info); - } - else - infoText += item; - } - AddNonEmptyInfo(title, infoText, &result); - } - } - } - - if ( Settings::SettingsData::instance()->showLabel()) { - AddNonEmptyInfo(i18n("Label: "), info->label(), &result); - } - - if ( Settings::SettingsData::instance()->showDescription() && !info->description().trimmed().isEmpty() ) { - AddNonEmptyInfo(i18n("Description: "), info->description(), - &result); - } - - QString exifText; - if ( Settings::SettingsData::instance()->showEXIF() ) { - typedef QMap ExifMap; - typedef ExifMap::const_iterator ExifMapIterator; - ExifMap exifMap = Exif::Info::instance()->infoForViewer( info->fileName(), Settings::SettingsData::instance()->iptcCharset() ); - - for( ExifMapIterator exifIt = exifMap.constBegin(); exifIt != exifMap.constEnd(); ++exifIt ) { - if ( exifIt.key().startsWith( QString::fromLatin1( "Exif." ) ) ) - for ( QStringList::const_iterator valuesIt = exifIt.value().constBegin(); valuesIt != exifIt.value().constEnd(); ++valuesIt ) { - QString exifName = exifIt.key().split( QChar::fromLatin1('.') ).last(); - AddNonEmptyInfo(QString::fromLatin1( "%1: ").arg(exifName), - *valuesIt, &exifText); - } - } - - QString iptcText; - for( ExifMapIterator exifIt = exifMap.constBegin(); exifIt != exifMap.constEnd(); ++exifIt ) { - if ( !exifIt.key().startsWith( QString::fromLatin1( "Exif." ) ) ) - for ( QStringList::const_iterator valuesIt = exifIt.value().constBegin(); valuesIt != exifIt.value().constEnd(); ++valuesIt ) { - QString iptcName = exifIt.key().split( QChar::fromLatin1('.') ).last(); - AddNonEmptyInfo(QString::fromLatin1( "%1: ").arg(iptcName), - *valuesIt, &iptcText); - } - } - - if ( !iptcText.isEmpty() ) { - if ( exifText.isEmpty() ) - exifText = iptcText; - else - exifText += QString::fromLatin1( "
" ) + iptcText; - } - } - - if ( !result.isEmpty() && !exifText.isEmpty() ) - result += QString::fromLatin1( "
" ); - result += exifText; - - return result; -} - -using DateSpec = QPair; -DateSpec dateDiff(const QDate& birthDate, const QDate& imageDate) -{ - const int bday = birthDate.day(); - const int iday = imageDate.day(); - const int bmonth = birthDate.month(); - const int imonth = imageDate.month(); - const int byear = birthDate.year(); - const int iyear = imageDate.year(); - - // Image before birth - const int diff = birthDate.daysTo(imageDate); - if (diff < 0) - return qMakePair(0, 'I'); - - if (diff < 31) - return qMakePair(diff, 'D'); - - int months = (iyear-byear)*12; - months += (imonth-bmonth); - months += (iday >= bday) ? 0 : -1; - - if ( months < 24) - return qMakePair(months, 'M'); - else - return qMakePair(months/12, 'Y'); -} - -QString formatDate(const DateSpec& date) -{ - if (date.second == 'I') - return {}; - else if (date.second == 'D') - return i18np("1 day", "%1 days", date.first); - else if (date.second == 'M') - return i18np("1 month", "%1 months", date.first); - else - return i18np("1 year", "%1 years", date.first); -} - -void test() { - Q_ASSERT(formatDate(dateDiff(QDate(1971,7,11), QDate(1971,7,11))) == QString::fromLatin1("0 days")); - Q_ASSERT(formatDate(dateDiff(QDate(1971,7,11), QDate(1971,8,10))) == QString::fromLatin1("30 days")); - Q_ASSERT(formatDate(dateDiff(QDate(1971,7,11), QDate(1971,8,11))) == QString::fromLatin1("1 month")); - Q_ASSERT(formatDate(dateDiff(QDate(1971,7,11), QDate(1971,8,12))) == QString::fromLatin1("1 month")); - Q_ASSERT(formatDate(dateDiff(QDate(1971,7,11), QDate(1971,9,10))) == QString::fromLatin1("1 month")); - Q_ASSERT(formatDate(dateDiff(QDate(1971,7,11), QDate(1971,9,11))) == QString::fromLatin1("2 month")); - Q_ASSERT(formatDate(dateDiff(QDate(1971,7,11), QDate(1972,6,10))) == QString::fromLatin1("10 month")); - Q_ASSERT(formatDate(dateDiff(QDate(1971,7,11), QDate(1972,6,11))) == QString::fromLatin1("11 month")); - Q_ASSERT(formatDate(dateDiff(QDate(1971,7,11), QDate(1972,6,12))) == QString::fromLatin1("11 month")); - Q_ASSERT(formatDate(dateDiff(QDate(1971,7,11), QDate(1972,7,10))) == QString::fromLatin1("11 month")); - Q_ASSERT(formatDate(dateDiff(QDate(1971,7,11), QDate(1972,7,11))) == QString::fromLatin1("12 month")); - Q_ASSERT(formatDate(dateDiff(QDate(1971,7,11), QDate(1972,7,12))) == QString::fromLatin1("12 month")); - Q_ASSERT(formatDate(dateDiff(QDate(1971,7,11), QDate(1972,12,11))) == QString::fromLatin1("17 month")); - Q_ASSERT(formatDate(dateDiff(QDate(1971,7,11), QDate(1973,7,11))) == QString::fromLatin1("2 years")); -} - -QString Utilities::formatAge(DB::CategoryPtr category, const QString &item, DB::ImageInfoPtr info) -{ - // test(); // I wish I could get my act together to set up a test suite. - const QDate birthDate = category->birthDate(item); - const QDate start = info->date().start().date(); - const QDate end = info->date().end().date(); - - if (birthDate.isNull() || start.isNull()) - return {}; - - if ( start == end) - return QString::fromUtf8(" (%1)").arg(formatDate(dateDiff(birthDate, start))); - else { - DateSpec lower = dateDiff(birthDate,start); - DateSpec upper = dateDiff(birthDate,end); - if (lower == upper) - return QString::fromUtf8(" (%1)").arg(formatDate(lower)); - else if (lower.second == 'I') - return QString::fromUtf8(" (< %1)").arg(formatDate(upper)); - else { - if (lower.second == upper.second) - return QString::fromUtf8(" (%1-%2)").arg(lower.first).arg(formatDate(upper)); - else - return QString::fromUtf8(" (%1-%2)").arg(formatDate(lower)).arg(formatDate(upper)); - } - } -} - void Utilities::checkForBackupFile( const QString& fileName, const QString& message ) { QString backupName = QFileInfo( fileName ).absolutePath() + QString::fromLatin1("/.#") + QFileInfo( fileName ).fileName(); QFileInfo backUpFile( backupName); QFileInfo indexFile( fileName ); if ( !backUpFile.exists() || indexFile.lastModified() > backUpFile.lastModified() || backUpFile.size() == 0 ) if ( !( backUpFile.exists() && !message.isNull() ) ) return; int code; if ( message.isNull() ) code = KMessageBox::questionYesNo( nullptr, i18n("Autosave file '%1' exists (size %3 KB) and is newer than '%2'. " "Should the autosave file be used?", backupName, fileName, backUpFile.size() >> 10 ), i18n("Found Autosave File") ); else if ( backUpFile.size() > 0 ) code = KMessageBox::warningYesNo( nullptr,i18n( "

Error: Cannot use current database file '%1':

%2

" "

Do you want to use autosave (%3 - size %4 KB) instead of exiting?

" "

(Manually verifying and copying the file might be a good idea)

", fileName, message, backupName, backUpFile.size() >> 10 ), i18n("Recover from Autosave?") ); else { KMessageBox::error( nullptr, i18n( "

Error: %1

Also autosave file is empty, check manually " "if numbered backup files exist and can be used to restore index.xml.

", message ) ); exit(-1); } if ( code == KMessageBox::Yes ) { QFile in( backupName ); if ( in.open( QIODevice::ReadOnly ) ) { QFile out( fileName ); if (out.open( QIODevice::WriteOnly ) ) { char data[1024]; int len; while ( (len = in.read( data, 1024 ) ) ) out.write( data, len ); } } } else if ( !message.isNull() ) exit(-1); } bool Utilities::ctrlKeyDown() { return QApplication::keyboardModifiers() & Qt::ControlModifier; } bool Utilities::copy( const QString& from, const QString& to ) { if ( QFileInfo(to).exists()) QDir().remove(to); return QFile::copy(from,to); } bool Utilities::makeHardLink( const QString& from, const QString& to ) { if (link(from.toLocal8Bit().constData(), to.toLocal8Bit().constData()) != 0) return false; else return true; } bool Utilities::makeSymbolicLink( const QString& from, const QString& to ) { if (symlink(from.toLocal8Bit().constData(), to.toLocal8Bit().constData()) != 0) return false; else return true; } bool Utilities::canReadImage( const DB::FileName& fileName ) { bool fastMode = !Settings::SettingsData::instance()->ignoreFileExtension(); QMimeDatabase::MatchMode mode = fastMode ? QMimeDatabase::MatchExtension : QMimeDatabase::MatchDefault; QMimeDatabase db; QMimeType mimeType = db.mimeTypeForFile( fileName.absolute(), mode ); return QImageReader::supportedMimeTypes().contains( mimeType.name().toUtf8() ) || ImageManager::ImageDecoder::mightDecode( fileName ); } QString Utilities::locateDataFile(const QString& fileName) { return QStandardPaths::locate(QStandardPaths::DataLocation, fileName); } QString Utilities::readFile( const QString& fileName ) { if ( fileName.isEmpty() ) { KMessageBox::error( nullptr, i18n("

No file name given!

") ); return QString(); } QFile file( fileName ); if ( !file.open( QIODevice::ReadOnly ) ) { //KMessageBox::error( nullptr, i18n("Could not open file %1").arg( fileName ) ); return QString(); } QTextStream stream( &file ); QString content = stream.readAll(); file.close(); return content; } namespace Utilities { QString normalizedFileName( const QString& fileName ) { return QFileInfo(fileName).absoluteFilePath(); } QString dereferenceSymLinks( const QString& fileName ) { QFileInfo fi(fileName); int rounds = 256; while (fi.isSymLink() && --rounds > 0) fi = QFileInfo(fi.readLink()); if (rounds == 0) return QString(); return fi.filePath(); } } QString Utilities::stripEndingForwardSlash( const QString& fileName ) { static QString slash = QString::fromLatin1("/"); if ( fileName.endsWith( slash ) ) return fileName.left( fileName.length()-1); else return fileName; } QString Utilities::relativeFolderName( const QString& fileName) { int index= fileName.lastIndexOf( QChar::fromLatin1('/'), -1); if (index == -1) return QString(); else return fileName.left( index ); } QString Utilities::absoluteImageFileName( const QString& relativeName ) { return stripEndingForwardSlash( Settings::SettingsData::instance()->imageDirectory() ) + QString::fromLatin1( "/" ) + relativeName; } QString Utilities::imageFileNameToAbsolute( const QString& fileName ) { if ( fileName.startsWith( Settings::SettingsData::instance()->imageDirectory() ) ) return fileName; else if ( fileName.startsWith( QString::fromLatin1("file://") ) ) return imageFileNameToAbsolute( fileName.mid( 7 ) ); // 7 == length("file://") else if ( fileName.startsWith( QString::fromLatin1("/") ) ) return QString(); // Not within our image root else return absoluteImageFileName( fileName ); } bool operator>( const QPoint& p1, const QPoint& p2) { return p1.y() > p2.y() || (p1.y() == p2.y() && p1.x() > p2.x() ); } bool operator<( const QPoint& p1, const QPoint& p2) { return p1.y() < p2.y() || ( p1.y() == p2.y() && p1.x() < p2.x() ); } const QSet& Utilities::supportedVideoExtensions() { static QSet videoExtensions; if ( videoExtensions.empty() ) { videoExtensions.insert( QString::fromLatin1( "3gp" ) ); videoExtensions.insert( QString::fromLatin1( "avi" ) ); videoExtensions.insert( QString::fromLatin1( "mp4" ) ); videoExtensions.insert( QString::fromLatin1( "m4v" ) ); videoExtensions.insert( QString::fromLatin1( "mpeg" ) ); videoExtensions.insert( QString::fromLatin1( "mpg" ) ); videoExtensions.insert( QString::fromLatin1( "qt" ) ); videoExtensions.insert( QString::fromLatin1( "mov" ) ); videoExtensions.insert( QString::fromLatin1( "moov" ) ); videoExtensions.insert( QString::fromLatin1( "qtvr" ) ); videoExtensions.insert( QString::fromLatin1( "rv" ) ); videoExtensions.insert( QString::fromLatin1( "3g2" ) ); videoExtensions.insert( QString::fromLatin1( "fli" ) ); videoExtensions.insert( QString::fromLatin1( "flc" ) ); videoExtensions.insert( QString::fromLatin1( "mkv" ) ); videoExtensions.insert( QString::fromLatin1( "mng" ) ); videoExtensions.insert( QString::fromLatin1( "asf" ) ); videoExtensions.insert( QString::fromLatin1( "asx" ) ); videoExtensions.insert( QString::fromLatin1( "wmp" ) ); videoExtensions.insert( QString::fromLatin1( "wmv" ) ); videoExtensions.insert( QString::fromLatin1( "ogm" ) ); videoExtensions.insert( QString::fromLatin1( "rm" ) ); videoExtensions.insert( QString::fromLatin1( "flv" ) ); videoExtensions.insert( QString::fromLatin1( "webm" ) ); videoExtensions.insert( QString::fromLatin1( "mts" ) ); videoExtensions.insert( QString::fromLatin1( "ogg" ) ); videoExtensions.insert( QString::fromLatin1( "ogv" ) ); videoExtensions.insert( QString::fromLatin1( "m2ts" ) ); } return videoExtensions; } bool Utilities::isVideo( const DB::FileName& fileName ) { QFileInfo fi( fileName.relative() ); QString ext = fi.suffix().toLower(); return supportedVideoExtensions().contains( ext ); } bool Utilities::isRAW( const DB::FileName& fileName ) { return ImageManager::RAWImageDecoder::isRAW( fileName ); } QImage Utilities::scaleImage(const QImage &image, int w, int h, Qt::AspectRatioMode mode ) { return image.scaled( w, h, mode, Settings::SettingsData::instance()->smoothScale() ? Qt::SmoothTransformation : Qt::FastTransformation ); } QImage Utilities::scaleImage(const QImage &image, const QSize& s, Qt::AspectRatioMode mode ) { return scaleImage( image, s.width(), s.height(), mode ); } QString Utilities::cStringWithEncoding( const char *c_str, const QString& charset ) { QTextCodec* codec = QTextCodec::codecForName( charset.toLatin1() ); if (!codec) codec = QTextCodec::codecForLocale(); return codec->toUnicode( c_str ); } QColor Utilities::contrastColor( const QColor& col ) { if ( col.red() < 127 && col.green() < 127 && col.blue() < 127 ) return Qt::white; else return Qt::black; } void Utilities::saveImage( const DB::FileName& fileName, const QImage& image, const char* format ) { const QFileInfo info(fileName.absolute()); QDir().mkpath(info.path()); const bool ok = image.save(fileName.absolute(),format); Q_ASSERT(ok); Q_UNUSED(ok); } // vi:expandtab:tabstop=4 shiftwidth=4: diff --git a/Utilities/Util.h b/Utilities/Util.h index 178a18d1..89239321 100644 --- a/Utilities/Util.h +++ b/Utilities/Util.h @@ -1,69 +1,67 @@ /* 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 UTIL_H #define UTIL_H #include "DB/CategoryPtr.h" #include "DB/FileName.h" #include "DB/ImageInfoPtr.h" #include #include #include #include #include namespace Utilities { -QString createInfoText( DB::ImageInfoPtr info, QMap >* ); -QString formatAge(DB::CategoryPtr category,const QString& item, DB::ImageInfoPtr info); void checkForBackupFile( const QString& fileName, const QString& message = QString() ); bool ctrlKeyDown(); bool copy( const QString& from, const QString& to ); bool makeSymbolicLink( const QString& from, const QString& to ); bool makeHardLink( const QString& from, const QString& to ); bool canReadImage( const DB::FileName& fileName ); const QSet& supportedVideoExtensions(); bool isVideo( const DB::FileName& fileName ); bool isRAW( const DB::FileName& fileName ); QString locateDataFile(const QString& fileName); QString readFile( const QString& fileName ); QString stripEndingForwardSlash( const QString& fileName ); QString absoluteImageFileName( const QString& relativeName ); QString imageFileNameToAbsolute( const QString& fileName ); QString relativeFolderName( const QString& fileName); QImage scaleImage(const QImage &image, int w, int h, Qt::AspectRatioMode mode=Qt::IgnoreAspectRatio ); QImage scaleImage(const QImage &image, const QSize& s, Qt::AspectRatioMode mode=Qt::IgnoreAspectRatio ); QString cStringWithEncoding( const char *c_str, const QString& charset ); QColor contrastColor( const QColor& ); void saveImage( const DB::FileName& fileName, const QImage& image, const char* format ); } bool operator>( const QPoint&, const QPoint& ); bool operator<( const QPoint&, const QPoint& ); #endif /* UTIL_H */ // vi:expandtab:tabstop=4 shiftwidth=4: diff --git a/Viewer/ViewerWidget.cpp b/Viewer/ViewerWidget.cpp index 0a518408..5d7027fa 100644 --- a/Viewer/ViewerWidget.cpp +++ b/Viewer/ViewerWidget.cpp @@ -1,1531 +1,1532 @@ /* Copyright (C) 2003-2018 Jesper K. Pedersen This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; see the file COPYING. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "ViewerWidget.h" #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 "CategoryImageConfig.h" #include "ImageDisplay.h" #include "InfoBox.h" #include "SpeedDisplay.h" #include "TaggedArea.h" #include "TextDisplay.h" #include "VideoDisplay.h" #include "VideoShooter.h" #include "VisibleOptionsMenu.h" Viewer::ViewerWidget* Viewer::ViewerWidget::s_latest = nullptr; Viewer::ViewerWidget* Viewer::ViewerWidget::latest() { return s_latest; } // Notice the parent is zero to allow other windows to come on top of it. Viewer::ViewerWidget::ViewerWidget( UsageType type, QMap > *macroStore ) :QStackedWidget( nullptr ) , m_current(0), m_popup(nullptr), m_showingFullScreen( false ), m_forward( true ) , m_isRunningSlideShow( false ), m_videoPlayerStoppedManually(false), m_type(type) , m_currentCategory(DB::ImageDB::instance()->categoryCollection()->categoryForSpecial(DB::Category::TokensCategory)->name()) , m_inputMacros(macroStore), m_myInputMacros(nullptr) { if ( type == ViewerWindow ) { setWindowFlags( Qt::Window ); setAttribute( Qt::WA_DeleteOnClose ); s_latest = this; } if (! m_inputMacros) { m_myInputMacros = m_inputMacros = new QMap >; } m_screenSaverCookie = -1; m_currentInputMode = InACategory; m_display = m_imageDisplay = new ImageDisplay( this ); addWidget( m_imageDisplay ); m_textDisplay = new TextDisplay( this ); addWidget( m_textDisplay ); createVideoViewer(); connect(m_imageDisplay, &ImageDisplay::possibleChange, this, &ViewerWidget::updateCategoryConfig); connect(m_imageDisplay, &ImageDisplay::imageReady, this, &ViewerWidget::updateInfoBox); connect(m_imageDisplay, &ImageDisplay::setCaptionInfo, this, &ViewerWidget::setCaptionWithDetail); connect(m_imageDisplay, &ImageDisplay::viewGeometryChanged, this, &ViewerWidget::remapAreas); // This must not be added to the layout, as it is standing on top of // the ImageDisplay m_infoBox = new InfoBox( this ); m_infoBox->hide(); setupContextMenu(); m_slideShowTimer = new QTimer( this ); m_slideShowTimer->setSingleShot( true ); m_slideShowPause = Settings::SettingsData::instance()->slideShowInterval() * 1000; connect(m_slideShowTimer, &QTimer::timeout, this, &ViewerWidget::slotSlideShowNextFromTimer); m_speedDisplay = new SpeedDisplay( this ); m_speedDisplay->hide(); setFocusPolicy( Qt::StrongFocus ); QTimer::singleShot( 2000, this, SLOT(test()) ); } void Viewer::ViewerWidget::setupContextMenu() { m_popup = new QMenu( this ); m_actions = new KActionCollection( this ); createSlideShowMenu(); createZoomMenu(); createRotateMenu(); createSkipMenu(); createShowContextMenu(); createWallPaperMenu(); createInvokeExternalMenu(); createVideoMenu(); createCategoryImageMenu(); createFilterMenu(); QAction * action = m_actions->addAction( QString::fromLatin1("viewer-edit-image-properties"), this, SLOT(editImage()) ); action->setText( i18nc("@action:inmenu","Annotate...") ); action->setShortcut( Qt::CTRL+Qt::Key_1 ); m_popup->addAction( action ); m_setStackHead = m_actions->addAction( QString::fromLatin1("viewer-set-stack-head"), this, SLOT(slotSetStackHead()) ); m_setStackHead->setText( i18nc("@action:inmenu","Set as First Image in Stack") ); m_setStackHead->setShortcut( Qt::CTRL+Qt::Key_4 ); m_popup->addAction( m_setStackHead ); m_showExifViewer = m_actions->addAction( QString::fromLatin1("viewer-show-exif-viewer"), this, SLOT(showExifViewer()) ); m_showExifViewer->setText( i18nc("@action:inmenu","Show Exif Viewer") ); m_popup->addAction( m_showExifViewer ); m_copyTo = m_actions->addAction( QString::fromLatin1("viewer-copy-to"), this, SLOT(copyTo()) ); m_copyTo->setText( i18nc("@action:inmenu","Copy Image to...") ); m_copyTo->setShortcut( Qt::Key_F7 ); m_popup->addAction( m_copyTo ); if ( m_type == ViewerWindow ) { action = m_actions->addAction( QString::fromLatin1("viewer-close"), this, SLOT(close()) ); action->setText( i18nc("@action:inmenu","Close") ); action->setShortcut( Qt::Key_Escape ); } m_popup->addAction( action ); m_actions->readSettings(); Q_FOREACH( QAction* action, m_actions->actions() ) { action->setShortcutContext(Qt::WindowShortcut); addAction(action); } } void Viewer::ViewerWidget::createShowContextMenu() { VisibleOptionsMenu* menu = new VisibleOptionsMenu( this, m_actions ); connect(menu, &VisibleOptionsMenu::visibleOptionsChanged, this, &ViewerWidget::updateInfoBox); m_popup->addMenu( menu ); } void Viewer::ViewerWidget::createWallPaperMenu() { // Setting wallpaper has still not yet been ported to KPA4 #ifndef DOES_STILL_NOT_WORK_IN_KPA4 m_wallpaperMenu = new QMenu( m_popup ); m_wallpaperMenu->setTitle( i18nc("@title:inmenu","Set as Wallpaper") ); QAction * action = m_actions->addAction( QString::fromLatin1("viewer-centered"), this, SLOT(slotSetWallpaperC()) ); action->setText( i18nc("@action:inmenu","Centered") ); m_wallpaperMenu->addAction(action); action = m_actions->addAction( QString::fromLatin1("viewer-tiled"), this, SLOT(slotSetWallpaperT()) ); action->setText( i18nc("@action:inmenu","Tiled") ); m_wallpaperMenu->addAction( action ); action = m_actions->addAction( QString::fromLatin1("viewer-center-tiled"), this, SLOT(slotSetWallpaperCT()) ); action->setText( i18nc("@action:inmenu","Center Tiled") ); m_wallpaperMenu->addAction( action ); action = m_actions->addAction( QString::fromLatin1("viewer-centered-maxspect"), this, SLOT(slotSetWallpaperCM()) ); action->setText( i18nc("@action:inmenu","Centered Maxpect") ); m_wallpaperMenu->addAction( action ); action = m_actions->addAction( QString::fromLatin1("viewer-tiled-maxpect"), this, SLOT(slotSetWallpaperTM()) ); action->setText( i18nc("@action:inmenu","Tiled Maxpect") ); m_wallpaperMenu->addAction( action ); action = m_actions->addAction( QString::fromLatin1("viewer-scaled"), this, SLOT(slotSetWallpaperS()) ); action->setText( i18nc("@action:inmenu","Scaled") ); m_wallpaperMenu->addAction( action ); action = m_actions->addAction( QString::fromLatin1("viewer-centered-auto-fit"), this, SLOT(slotSetWallpaperCAF()) ); action->setText( i18nc("@action:inmenu","Centered Auto Fit") ); m_wallpaperMenu->addAction( action ); m_popup->addMenu( m_wallpaperMenu ); #endif // DOES_STILL_NOT_WORK_IN_KPA4 } void Viewer::ViewerWidget::inhibitScreenSaver( bool inhibit ) { QDBusMessage message; if (inhibit) { message = QDBusMessage::createMethodCall( QString::fromLatin1("org.freedesktop.ScreenSaver"), QString::fromLatin1("/ScreenSaver"), QString::fromLatin1("org.freedesktop.ScreenSaver"), QString::fromLatin1("Inhibit") ); message << QString( QString::fromLatin1("KPhotoAlbum") ); message << QString( QString::fromLatin1("Giving a slideshow") ); QDBusMessage reply = QDBusConnection::sessionBus().call( message ); if ( reply.type() == QDBusMessage::ReplyMessage ) m_screenSaverCookie = reply.arguments().first().toInt(); } else { if ( m_screenSaverCookie != -1 ) { message = QDBusMessage::createMethodCall( QString::fromLatin1("org.freedesktop.ScreenSaver"), QString::fromLatin1("/ScreenSaver"), QString::fromLatin1("org.freedesktop.ScreenSaver"), QString::fromLatin1("UnInhibit") ); message << (uint)m_screenSaverCookie; QDBusConnection::sessionBus().send( message ); m_screenSaverCookie = -1; } } } void Viewer::ViewerWidget::createInvokeExternalMenu() { m_externalPopup = new MainWindow::ExternalPopup( m_popup ); m_popup->addMenu( m_externalPopup ); connect(m_externalPopup, &MainWindow::ExternalPopup::aboutToShow, this, &ViewerWidget::populateExternalPopup); } void Viewer::ViewerWidget::createRotateMenu() { m_rotateMenu = new QMenu( m_popup ); m_rotateMenu->setTitle( i18nc("@title:inmenu","Rotate") ); QAction * action = m_actions->addAction( QString::fromLatin1("viewer-rotate90"), this, SLOT(rotate90()) ); action->setText( i18nc("@action:inmenu","Rotate clockwise") ); action->setShortcut( Qt::Key_9 ); m_rotateMenu->addAction( action ); action = m_actions->addAction( QString::fromLatin1("viewer-rotate180"), this, SLOT(rotate180()) ); action->setText( i18nc("@action:inmenu","Flip Over") ); action->setShortcut( Qt::Key_8 ); m_rotateMenu->addAction( action ); action = m_actions->addAction( QString::fromLatin1("viewer-rotare270"), this, SLOT(rotate270()) ); // ^ this is a typo, isn't it?! action->setText( i18nc("@action:inmenu","Rotate counterclockwise") ); action->setShortcut( Qt::Key_7 ); m_rotateMenu->addAction( action ); m_popup->addMenu( m_rotateMenu ); } void Viewer::ViewerWidget::createSkipMenu() { QMenu *popup = new QMenu( m_popup ); popup->setTitle( i18nc("@title:inmenu As in 'skip 2 images'","Skip") ); QAction * action = m_actions->addAction( QString::fromLatin1("viewer-home"), this, SLOT(showFirst()) ); action->setText( i18nc("@action:inmenu Go to first image","First") ); action->setShortcut( Qt::Key_Home ); popup->addAction( action ); m_backwardActions.append(action); action = m_actions->addAction( QString::fromLatin1("viewer-end"), this, SLOT(showLast()) ); action->setText( i18nc("@action:inmenu Go to last image","Last") ); action->setShortcut( Qt::Key_End ); popup->addAction( action ); m_forwardActions.append(action); action = m_actions->addAction( QString::fromLatin1("viewer-next"), this, SLOT(showNext()) ); action->setText( i18nc("@action:inmenu","Show Next") ); action->setShortcuts(QList() << Qt::Key_PageDown << Qt::Key_Space); popup->addAction( action ); m_forwardActions.append(action); action = m_actions->addAction( QString::fromLatin1("viewer-next-10"), this, SLOT(showNext10()) ); action->setText( i18nc("@action:inmenu","Skip 10 Forward") ); action->setShortcut( Qt::CTRL+Qt::Key_PageDown ); popup->addAction( action ); m_forwardActions.append(action); action = m_actions->addAction( QString::fromLatin1("viewer-next-100"), this, SLOT(showNext100()) ); action->setText( i18nc("@action:inmenu","Skip 100 Forward") ); action->setShortcut( Qt::SHIFT+Qt::Key_PageDown ); popup->addAction( action ); m_forwardActions.append(action); action = m_actions->addAction( QString::fromLatin1("viewer-next-1000"), this, SLOT(showNext1000()) ); action->setText( i18nc("@action:inmenu","Skip 1000 Forward") ); action->setShortcut( Qt::CTRL+Qt::SHIFT+Qt::Key_PageDown ); popup->addAction( action ); m_forwardActions.append(action); action = m_actions->addAction( QString::fromLatin1("viewer-prev"), this, SLOT(showPrev()) ); action->setText( i18nc("@action:inmenu","Show Previous") ); action->setShortcuts(QList() << Qt::Key_PageUp << Qt::Key_Backspace); popup->addAction( action ); m_backwardActions.append(action); action = m_actions->addAction( QString::fromLatin1("viewer-prev-10"), this, SLOT(showPrev10()) ); action->setText( i18nc("@action:inmenu","Skip 10 Backward") ); action->setShortcut( Qt::CTRL+Qt::Key_PageUp ); popup->addAction( action ); m_backwardActions.append(action); action = m_actions->addAction( QString::fromLatin1("viewer-prev-100"), this, SLOT(showPrev100()) ); action->setText( i18nc("@action:inmenu","Skip 100 Backward") ); action->setShortcut( Qt::SHIFT+Qt::Key_PageUp ); popup->addAction( action ); m_backwardActions.append(action); action = m_actions->addAction( QString::fromLatin1("viewer-prev-1000"), this, SLOT(showPrev1000()) ); action->setText( i18nc("@action:inmenu","Skip 1000 Backward") ); action->setShortcut( Qt::CTRL+Qt::SHIFT+Qt::Key_PageUp ); popup->addAction( action ); m_backwardActions.append(action); action = m_actions->addAction( QString::fromLatin1("viewer-delete-current"), this, SLOT(deleteCurrent()) ); action->setText( i18nc("@action:inmenu","Delete Image") ); action->setShortcut( Qt::CTRL + Qt::Key_Delete ); popup->addAction( action ); action = m_actions->addAction( QString::fromLatin1("viewer-remove-current"), this, SLOT(removeCurrent()) ); action->setText( i18nc("@action:inmenu","Remove Image from Display List") ); action->setShortcut( Qt::Key_Delete ); popup->addAction( action ); m_popup->addMenu( popup ); } void Viewer::ViewerWidget::createZoomMenu() { QMenu *popup = new QMenu( m_popup ); popup->setTitle( i18nc("@action:inmenu","Zoom") ); // PENDING(blackie) Only for image display? QAction * action = m_actions->addAction( QString::fromLatin1("viewer-zoom-in"), this, SLOT(zoomIn()) ); action->setText( i18nc("@action:inmenu","Zoom In") ); action->setShortcut( Qt::Key_Plus ); popup->addAction( action ); action = m_actions->addAction( QString::fromLatin1("viewer-zoom-out"), this, SLOT(zoomOut()) ); action->setText( i18nc("@action:inmenu","Zoom Out") ); action->setShortcut( Qt::Key_Minus ); popup->addAction( action ); action = m_actions->addAction( QString::fromLatin1("viewer-zoom-full"), this, SLOT(zoomFull()) ); action->setText( i18nc("@action:inmenu","Full View") ); action->setShortcut( Qt::Key_Period ); popup->addAction( action ); action = m_actions->addAction( QString::fromLatin1("viewer-zoom-pixel"), this, SLOT(zoomPixelForPixel()) ); action->setText( i18nc("@action:inmenu","Pixel for Pixel View") ); action->setShortcut( Qt::Key_Equal ); popup->addAction( action ); action = m_actions->addAction( QString::fromLatin1("viewer-toggle-fullscreen"), this, SLOT(toggleFullScreen()) ); action->setText( i18nc("@action:inmenu","Toggle Full Screen") ); action->setShortcuts(QList() << Qt::Key_F11 << Qt::Key_Return); popup->addAction( action ); m_popup->addMenu( popup ); } void Viewer::ViewerWidget::createSlideShowMenu() { QMenu *popup = new QMenu( m_popup ); popup->setTitle( i18nc("@title:inmenu","Slideshow") ); m_startStopSlideShow = m_actions->addAction( QString::fromLatin1("viewer-start-stop-slideshow"), this, SLOT(slotStartStopSlideShow()) ); m_startStopSlideShow->setText( i18nc("@action:inmenu","Run Slideshow") ); m_startStopSlideShow->setShortcut( Qt::CTRL+Qt::Key_R ); popup->addAction( m_startStopSlideShow ); m_slideShowRunFaster = m_actions->addAction( QString::fromLatin1("viewer-run-faster"), this, SLOT(slotSlideShowFaster()) ); m_slideShowRunFaster->setText( i18nc("@action:inmenu","Run Faster") ); m_slideShowRunFaster->setShortcut( Qt::CTRL + Qt::Key_Plus ); // if you change this, please update the info in Viewer::SpeedDisplay popup->addAction( m_slideShowRunFaster ); m_slideShowRunSlower = m_actions->addAction( QString::fromLatin1("viewer-run-slower"), this, SLOT(slotSlideShowSlower()) ); m_slideShowRunSlower->setText( i18nc("@action:inmenu","Run Slower") ); m_slideShowRunSlower->setShortcut( Qt::CTRL+Qt::Key_Minus ); // if you change this, please update the info in Viewer::SpeedDisplay popup->addAction( m_slideShowRunSlower ); m_popup->addMenu( popup ); } void Viewer::ViewerWidget::load( const DB::FileNameList& list, int index ) { m_list = list; m_imageDisplay->setImageList( list ); m_current = index; load(); bool on = ( list.count() > 1 ); m_startStopSlideShow->setEnabled(on); m_slideShowRunFaster->setEnabled(on); m_slideShowRunSlower->setEnabled(on); } void Viewer::ViewerWidget::load() { const bool isReadable = QFileInfo( currentInfo()->fileName().absolute() ).isReadable(); const bool isVideo = isReadable && Utilities::isVideo( currentInfo()->fileName() ); if ( isReadable ) { if ( isVideo ) { m_display = m_videoDisplay; } else m_display = m_imageDisplay; } else { m_display = m_textDisplay; m_textDisplay->setText( i18n("File not available") ); updateInfoBox(); } setCurrentWidget( m_display ); m_infoBox->raise(); m_rotateMenu->setEnabled( !isVideo ); m_wallpaperMenu->setEnabled( !isVideo ); m_categoryImagePopup->setEnabled( !isVideo ); m_filterMenu->setEnabled( !isVideo ); m_showExifViewer->setEnabled( !isVideo ); if ( m_exifViewer ) m_exifViewer->setImage( currentInfo()->fileName() ); Q_FOREACH( QAction* videoAction, m_videoActions ) { videoAction->setVisible( isVideo ); } emit soughtTo( m_list[ m_current ]); bool ok = m_display->setImage( currentInfo(), m_forward ); if ( !ok ) { close( false ); return; } setCaptionWithDetail( QString() ); // PENDING(blackie) This needs to be improved, so that it shows the actions only if there are that many images to jump. for( QList::const_iterator it = m_forwardActions.constBegin(); it != m_forwardActions.constEnd(); ++it ) (*it)->setEnabled( m_current +1 < (int) m_list.count() ); for( QList::const_iterator it = m_backwardActions.constBegin(); it != m_backwardActions.constEnd(); ++it ) (*it)->setEnabled( m_current > 0 ); m_setStackHead->setEnabled( currentInfo()->isStacked() ); if ( isVideo ) updateCategoryConfig(); if ( m_isRunningSlideShow ) m_slideShowTimer->start( m_slideShowPause ); if ( m_display == m_textDisplay ) updateInfoBox(); // Add all tagged areas addTaggedAreas(); } void Viewer::ViewerWidget::setCaptionWithDetail( const QString& detail ) { setWindowTitle( i18nc("@title:window %1 is the filename, %2 its detail info", "%1 %2", currentInfo()->fileName().absolute(), detail ) ); } void Viewer::ViewerWidget::contextMenuEvent( QContextMenuEvent * e ) { if ( m_videoDisplay ) { if ( m_videoDisplay->isPaused() ) m_playPause->setText(i18nc("@action:inmenu Start video playback","Play")); else m_playPause->setText(i18nc("@action:inmenu Pause video playback","Pause")); m_stop->setEnabled( m_videoDisplay->isPlaying() ); } m_popup->exec( e->globalPos() ); e->setAccepted(true); } void Viewer::ViewerWidget::showNextN(int n) { filterNone(); if ( m_display == m_videoDisplay ) { m_videoPlayerStoppedManually = true; m_videoDisplay->stop(); } if ( m_current + n < (int) m_list.count() ) { m_current += n; if (m_current >= (int) m_list.count()) m_current = (int) m_list.count() - 1; m_forward = true; load(); } } void Viewer::ViewerWidget::showNext() { showNextN(1); } void Viewer::ViewerWidget::removeCurrent() { removeOrDeleteCurrent(OnlyRemoveFromViewer); } void Viewer::ViewerWidget::deleteCurrent() { removeOrDeleteCurrent( RemoveImageFromDatabase ); } void Viewer::ViewerWidget::removeOrDeleteCurrent( RemoveAction action ) { const DB::FileName fileName = m_list[m_current]; if ( action == RemoveImageFromDatabase ) m_removed.append(fileName); m_list.removeAll(fileName); if ( m_list.isEmpty() ) close(); if ( m_current == m_list.count() ) showPrev(); else showNextN(0); } void Viewer::ViewerWidget::showNext10() { showNextN(10); } void Viewer::ViewerWidget::showNext100() { showNextN(100); } void Viewer::ViewerWidget::showNext1000() { showNextN(1000); } void Viewer::ViewerWidget::showPrevN(int n) { if ( m_display == m_videoDisplay ) m_videoDisplay->stop(); if ( m_current > 0 ) { m_current -= n; if (m_current < 0) m_current = 0; m_forward = false; load(); } } void Viewer::ViewerWidget::showPrev() { showPrevN(1); } void Viewer::ViewerWidget::showPrev10() { showPrevN(10); } void Viewer::ViewerWidget::showPrev100() { showPrevN(100); } void Viewer::ViewerWidget::showPrev1000() { showPrevN(1000); } void Viewer::ViewerWidget::rotate90() { currentInfo()->rotate( 90 ); load(); invalidateThumbnail(); MainWindow::DirtyIndicator::markDirty(); emit imageRotated(m_list[ m_current]); } void Viewer::ViewerWidget::rotate180() { currentInfo()->rotate( 180 ); load(); invalidateThumbnail(); MainWindow::DirtyIndicator::markDirty(); emit imageRotated(m_list[ m_current]); } void Viewer::ViewerWidget::rotate270() { currentInfo()->rotate( 270 ); load(); invalidateThumbnail(); MainWindow::DirtyIndicator::markDirty(); emit imageRotated(m_list[ m_current]); } void Viewer::ViewerWidget::showFirst() { showPrevN(m_list.count()); } void Viewer::ViewerWidget::showLast() { showNextN(m_list.count()); } void Viewer::ViewerWidget::slotSetWallpaperC() { setAsWallpaper(1); } void Viewer::ViewerWidget::slotSetWallpaperT() { setAsWallpaper(2); } void Viewer::ViewerWidget::slotSetWallpaperCT() { setAsWallpaper(3); } void Viewer::ViewerWidget::slotSetWallpaperCM() { setAsWallpaper(4); } void Viewer::ViewerWidget::slotSetWallpaperTM() { setAsWallpaper(5); } void Viewer::ViewerWidget::slotSetWallpaperS() { setAsWallpaper(6); } void Viewer::ViewerWidget::slotSetWallpaperCAF() { setAsWallpaper(7); } void Viewer::ViewerWidget::setAsWallpaper(int /*mode*/) { #ifdef DOES_STILL_NOT_WORK_IN_KPA4 if(mode>7 || mode<1) return; DCOPRef kdesktop("kdesktop","KBackgroundIface"); kdesktop.send("setWallpaper(QString,int)",currentInfo()->fileName(0),mode); #endif } bool Viewer::ViewerWidget::close( bool alsoDelete) { if ( !m_removed.isEmpty() ) { MainWindow::DeleteDialog dialog( this ); dialog.exec( m_removed ); } m_slideShowTimer->stop(); m_isRunningSlideShow = false; return QWidget::close(); if ( alsoDelete ) deleteLater(); } DB::ImageInfoPtr Viewer::ViewerWidget::currentInfo() const { return DB::ImageDB::instance()->info(m_list[ m_current]); // PENDING(blackie) can we postpone this lookup? } void Viewer::ViewerWidget::infoBoxMove() { QPoint p = mapFromGlobal( QCursor::pos() ); Settings::Position oldPos = Settings::SettingsData::instance()->infoBoxPosition(); Settings::Position pos = oldPos; int x = m_display->mapFromParent( p ).x(); int y = m_display->mapFromParent( p ).y(); int w = m_display->width(); int h = m_display->height(); if ( x < w/3 ) { if ( y < h/3 ) pos = Settings::TopLeft; else if ( y > h*2/3 ) pos = Settings::BottomLeft; else pos = Settings::Left; } else if ( x > w*2/3 ) { if ( y < h/3 ) pos = Settings::TopRight; else if ( y > h*2/3 ) pos = Settings::BottomRight; else pos = Settings::Right; } else { if ( y < h/3 ) pos = Settings::Top; else if ( y > h*2/3 ) pos = Settings::Bottom; } if ( pos != oldPos ) { Settings::SettingsData::instance()->setInfoBoxPosition( pos ); updateInfoBox(); } } void Viewer::ViewerWidget::moveInfoBox() { m_infoBox->setSize(); Settings::Position pos = Settings::SettingsData::instance()->infoBoxPosition(); int lx = m_display->pos().x(); int ly = m_display->pos().y(); int lw = m_display->width(); int lh = m_display->height(); int bw = m_infoBox->width(); int bh = m_infoBox->height(); int bx, by; // x-coordinate if ( pos == Settings::TopRight || pos == Settings::BottomRight || pos == Settings::Right ) bx = lx+lw-5-bw; else if ( pos == Settings::TopLeft || pos == Settings::BottomLeft || pos == Settings::Left ) bx = lx+5; else bx = lx+lw/2-bw/2; // Y-coordinate if ( pos == Settings::TopLeft || pos == Settings::TopRight || pos == Settings::Top ) by = ly+5; else if ( pos == Settings::BottomLeft || pos == Settings::BottomRight || pos == Settings::Bottom ) by = ly+lh-5-bh; else by = ly+lh/2-bh/2; m_infoBox->move(bx,by); } void Viewer::ViewerWidget::resizeEvent( QResizeEvent* e ) { moveInfoBox(); QWidget::resizeEvent( e ); } void Viewer::ViewerWidget::updateInfoBox() { QString tokensCategory = DB::ImageDB::instance()->categoryCollection()->categoryForSpecial(DB::Category::TokensCategory)->name(); if ( currentInfo() || !m_currentInput.isEmpty() || (!m_currentCategory.isEmpty() && m_currentCategory != tokensCategory)) { QMap > map; QString text = Utilities::createInfoText( currentInfo(), &map ); QString selecttext = QString::fromLatin1(""); if (m_currentCategory.isEmpty()) { selecttext = i18nc("Basically 'enter a category name'","Setting Category: ") + m_currentInput; if (m_currentInputList.length() > 0) { selecttext += QString::fromLatin1("{") + m_currentInputList + QString::fromLatin1("}"); } } else if ( ( !m_currentInput.isEmpty() && m_currentCategory != tokensCategory)) { selecttext = i18nc("Basically 'enter a tag name'","Assigning: ") + m_currentCategory + QString::fromLatin1("/") + m_currentInput; if (m_currentInputList.length() > 0) { selecttext += QString::fromLatin1("{") + m_currentInputList + QString::fromLatin1("}"); } } else if ( !m_currentInput.isEmpty() && m_currentCategory == tokensCategory) { m_currentInput = QString::fromLatin1(""); } if (!selecttext.isEmpty()) text = selecttext + QString::fromLatin1("
") + text; if ( Settings::SettingsData::instance()->showInfoBox() && !text.isNull() && ( m_type != InlineViewer ) ) { m_infoBox->setInfo( text, map ); m_infoBox->show(); } else m_infoBox->hide(); moveInfoBox(); } } Viewer::ViewerWidget::~ViewerWidget() { inhibitScreenSaver(false); if ( s_latest == this ) s_latest = nullptr; if ( m_myInputMacros ) delete m_myInputMacros; } void Viewer::ViewerWidget::toggleFullScreen() { setShowFullScreen( !m_showingFullScreen ); } void Viewer::ViewerWidget::slotStartStopSlideShow() { bool wasRunningSlideShow = m_isRunningSlideShow; m_isRunningSlideShow = !m_isRunningSlideShow && m_list.count() != 1; if ( wasRunningSlideShow ) { m_startStopSlideShow->setText( i18nc("@action:inmenu","Run Slideshow") ); m_slideShowTimer->stop(); if ( m_list.count() != 1 ) m_speedDisplay->end(); inhibitScreenSaver(false); } else { m_startStopSlideShow->setText( i18nc("@action:inmenu","Stop Slideshow") ); if ( currentInfo()->mediaType() != DB::Video ) m_slideShowTimer->start( m_slideShowPause ); m_speedDisplay->start(); inhibitScreenSaver(true); } } void Viewer::ViewerWidget::slotSlideShowNextFromTimer() { // Load the next images. QTime timer; timer.start(); if ( m_display == m_imageDisplay ) slotSlideShowNext(); // ensure that there is a few milliseconds pause, so that an end slideshow keypress // can get through immediately, we don't want it to queue up behind a bunch of timer events, // which loaded a number of new images before the slideshow stops int ms = qMax( 200, m_slideShowPause - timer.elapsed() ); m_slideShowTimer->start( ms ); } void Viewer::ViewerWidget::slotSlideShowNext() { m_forward = true; if ( m_current +1 < (int) m_list.count() ) m_current++; else m_current = 0; load(); } void Viewer::ViewerWidget::slotSlideShowFaster() { changeSlideShowInterval(-500); } void Viewer::ViewerWidget::slotSlideShowSlower() { changeSlideShowInterval(+500); } void Viewer::ViewerWidget::changeSlideShowInterval( int delta ) { if ( m_list.count() == 1 ) return; m_slideShowPause += delta; m_slideShowPause = qMax( m_slideShowPause, 500 ); m_speedDisplay->display( m_slideShowPause ); if (m_slideShowTimer->isActive() ) m_slideShowTimer->start( m_slideShowPause ); } void Viewer::ViewerWidget::editImage() { DB::ImageInfoList list; list.append( currentInfo() ); MainWindow::Window::configureImages( list, true ); } void Viewer::ViewerWidget::filterNone() { if ( m_display == m_imageDisplay ) { m_imageDisplay->filterNone(); m_filterMono->setChecked( false ); m_filterBW->setChecked( false ); m_filterContrastStretch->setChecked( false ); m_filterHistogramEqualization->setChecked( false ); } } void Viewer::ViewerWidget::filterSelected() { // The filters that drop bit depth below 32 should be the last ones // so that filters requiring more bit depth are processed first if ( m_display == m_imageDisplay ) { m_imageDisplay->filterNone(); if (m_filterBW->isChecked()) m_imageDisplay->filterBW(); if (m_filterContrastStretch->isChecked()) m_imageDisplay->filterContrastStretch(); if (m_filterHistogramEqualization->isChecked()) m_imageDisplay->filterHistogramEqualization(); if (m_filterMono->isChecked()) m_imageDisplay->filterMono(); } } void Viewer::ViewerWidget::filterBW() { if ( m_display == m_imageDisplay ) { if ( m_filterBW->isChecked() ) m_filterBW->setChecked( m_imageDisplay->filterBW()); else filterSelected(); } } void Viewer::ViewerWidget::filterContrastStretch() { if ( m_display == m_imageDisplay ) { if (m_filterContrastStretch->isChecked()) m_filterContrastStretch->setChecked( m_imageDisplay->filterContrastStretch() ); else filterSelected(); } } void Viewer::ViewerWidget::filterHistogramEqualization() { if ( m_display == m_imageDisplay ) { if ( m_filterHistogramEqualization->isChecked() ) m_filterHistogramEqualization->setChecked( m_imageDisplay->filterHistogramEqualization() ); else filterSelected(); } } void Viewer::ViewerWidget::filterMono() { if ( m_display == m_imageDisplay ) { if ( m_filterMono->isChecked() ) m_filterMono->setChecked( m_imageDisplay->filterMono() ); else filterSelected(); } } void Viewer::ViewerWidget::slotSetStackHead() { MainWindow::Window::theMainWindow()->setStackHead(m_list[ m_current ]); } bool Viewer::ViewerWidget::showingFullScreen() const { return m_showingFullScreen; } void Viewer::ViewerWidget::setShowFullScreen( bool on ) { if ( on ) { setWindowState( windowState() | Qt::WindowFullScreen ); // set moveInfoBox(); } else { // We need to size the image when going out of full screen, in case we started directly in full screen // setWindowState( windowState() & ~Qt::WindowFullScreen ); // reset resize( Settings::SettingsData::instance()->viewerSize() ); } m_showingFullScreen = on; } void Viewer::ViewerWidget::updateCategoryConfig() { if ( !CategoryImageConfig::instance()->isVisible() ) return; CategoryImageConfig::instance()->setCurrentImage( m_imageDisplay->currentViewAsThumbnail(), currentInfo() ); } void Viewer::ViewerWidget::populateExternalPopup() { m_externalPopup->populate( currentInfo(), m_list ); } void Viewer::ViewerWidget::populateCategoryImagePopup() { m_categoryImagePopup->populate( m_imageDisplay->currentViewAsThumbnail(), m_list[m_current] ); } void Viewer::ViewerWidget::show( bool slideShow ) { QSize size; bool fullScreen; if ( slideShow ) { fullScreen = Settings::SettingsData::instance()->launchSlideShowFullScreen(); size = Settings::SettingsData::instance()->slideShowSize(); } else { fullScreen = Settings::SettingsData::instance()->launchViewerFullScreen(); size = Settings::SettingsData::instance()->viewerSize(); } if ( fullScreen ) setShowFullScreen( true ); else resize( size ); QWidget::show(); if ( slideShow != m_isRunningSlideShow) { // The info dialog will show up at the wrong place if we call this function directly // don't ask me why - 4 Sep. 2004 15:13 -- Jesper K. Pedersen QTimer::singleShot(0, this, SLOT(slotStartStopSlideShow()) ); } } KActionCollection* Viewer::ViewerWidget::actions() { return m_actions; } int Viewer::ViewerWidget::find_tag_in_list(const QStringList &list, QString &namefound) { int found = 0; m_currentInputList = QString::fromLatin1(""); for( QStringList::ConstIterator listIter = list.constBegin(); listIter != list.constEnd(); ++listIter ) { if (listIter->startsWith(m_currentInput, Qt::CaseInsensitive)) { found++; if (m_currentInputList.length() > 0) m_currentInputList = m_currentInputList + QString::fromLatin1(","); m_currentInputList =m_currentInputList + listIter->right(listIter->length() - m_currentInput.length()); if (found > 1 && m_currentInputList.length() > 20) { // already found more than we want to display // bail here for now // XXX: non-ideal? display more? certainly config 20 return found; } else { namefound = *listIter; } } } return found; } void Viewer::ViewerWidget::keyPressEvent( QKeyEvent* event ) { if (event->key() == Qt::Key_Backspace) { // remove stuff from the current input string m_currentInput.remove( m_currentInput.length()-1, 1 ); updateInfoBox(); MainWindow::DirtyIndicator::markDirty(); m_currentInputList = QString::fromLatin1(""); // } else if (event->modifier & (Qt::AltModifier | Qt::MetaModifier) && // event->key() == Qt::Key_Enter) { return; // we've handled it } else if (event->key() == Qt::Key_Comma) { // force set the "new" token if (!m_currentCategory.isEmpty()) { if (m_currentInput.left(1) == QString::fromLatin1("\"") || // allow a starting ' or " to signal a brand new category // this bypasses the auto-selection of matching characters m_currentInput.left(1) == QString::fromLatin1("\'")) { m_currentInput = m_currentInput.right(m_currentInput.length()-1); } if (m_currentInput.isEmpty()) return; currentInfo()->addCategoryInfo(m_currentCategory, m_currentInput); DB::CategoryPtr category = DB::ImageDB::instance()->categoryCollection()->categoryForName(m_currentCategory); category->addItem(m_currentInput); } m_currentInput = QString::fromLatin1(""); updateInfoBox(); MainWindow::DirtyIndicator::markDirty(); return; // we've handled it } else if ( event->modifiers() == 0 && event->key() >= Qt::Key_0 && event->key() <= Qt::Key_5 ) { bool ok; short rating = event->text().left(1).toShort(&ok, 10); if (ok) { currentInfo()->setRating(rating * 2); updateInfoBox(); MainWindow::DirtyIndicator::markDirty(); } } else if (event->modifiers() == 0 || event->modifiers() == Qt::ShiftModifier) { // search the category for matches QString namefound; QString incomingKey = event->text().left(1); // start searching for a new category name if (incomingKey == QString::fromLatin1("/")) { if (m_currentInput.isEmpty() && m_currentCategory.isEmpty()) { if (m_currentInputMode == InACategory) { m_currentInputMode = AlwaysStartWithCategory; } else { m_currentInputMode = InACategory; } } else { // reset the category to search through m_currentInput = QString::fromLatin1(""); m_currentCategory = QString::fromLatin1(""); } // use an assigned key or map to a given key for future reference } else if (m_currentInput.isEmpty() && // can map to function keys event->key() >= Qt::Key_F1 && event->key() <= Qt::Key_F35) { // we have a request to assign a macro key or use one Qt::Key key = (Qt::Key) event->key(); if (m_inputMacros->contains(key)) { // Use the requested toggle if ( event->modifiers() == Qt::ShiftModifier ) { if ( currentInfo()->hasCategoryInfo( (*m_inputMacros)[key].first, (*m_inputMacros)[key].second ) ) { currentInfo()->removeCategoryInfo( (*m_inputMacros)[key].first, (*m_inputMacros)[key].second ); } } else { currentInfo()->addCategoryInfo( (*m_inputMacros)[key].first, (*m_inputMacros)[key].second ); } } else { (*m_inputMacros)[key] = qMakePair(m_lastCategory, m_lastFound); } updateInfoBox(); MainWindow::DirtyIndicator::markDirty(); // handled it return; } else if (m_currentCategory.isEmpty()) { // still searching for a category to lock to m_currentInput += incomingKey; QStringList categorynames = DB::ImageDB::instance()->categoryCollection()->categoryTexts(); if (find_tag_in_list(categorynames, namefound) == 1) { // yay, we have exactly one! m_currentCategory = namefound; m_currentInput = QString::fromLatin1(""); m_currentInputList = QString::fromLatin1(""); } } else { m_currentInput += incomingKey; DB::CategoryPtr category = DB::ImageDB::instance()->categoryCollection() ->categoryForName(m_currentCategory); QStringList items = category->items(); if (find_tag_in_list(items, namefound) == 1) { // yay, we have exactly one! if ( currentInfo()->hasCategoryInfo( category->name(), namefound ) ) currentInfo()->removeCategoryInfo( category->name(), namefound ); else currentInfo()->addCategoryInfo( category->name(), namefound ); m_lastFound = namefound; m_lastCategory = m_currentCategory; m_currentInput = QString::fromLatin1(""); m_currentInputList = QString::fromLatin1(""); if (m_currentInputMode == AlwaysStartWithCategory) m_currentCategory = QString::fromLatin1(""); } } updateInfoBox(); MainWindow::DirtyIndicator::markDirty(); } QWidget::keyPressEvent( event ); return; } void Viewer::ViewerWidget::videoStopped() { if ( !m_videoPlayerStoppedManually && m_isRunningSlideShow ) slotSlideShowNext(); m_videoPlayerStoppedManually=false; } void Viewer::ViewerWidget::wheelEvent( QWheelEvent* event ) { if ( event->delta() < 0) { showNext(); } else { showPrev(); } } void Viewer::ViewerWidget::showExifViewer() { m_exifViewer = new Exif::InfoDialog( currentInfo()->fileName(), this ); m_exifViewer->show(); } void Viewer::ViewerWidget::zoomIn() { m_display->zoomIn(); } void Viewer::ViewerWidget::zoomOut() { m_display->zoomOut(); } void Viewer::ViewerWidget::zoomFull() { m_display->zoomFull(); } void Viewer::ViewerWidget::zoomPixelForPixel() { m_display->zoomPixelForPixel(); } void Viewer::ViewerWidget::makeThumbnailImage() { VideoShooter::go(currentInfo(), this); } struct SeekInfo { SeekInfo( const QString& title, const char* name, int value, const QKeySequence& key ) : title( title ), name(name), value(value), key(key) {} QString title; const char* name; int value; QKeySequence key; }; void Viewer::ViewerWidget::createVideoMenu() { QMenu* menu = new QMenu(m_popup); menu->setTitle(i18nc("@title:inmenu","Seek")); m_videoActions.append( m_popup->addMenu( menu ) ); QList list; list << SeekInfo( i18nc("@action:inmenu","10 minutes backward"), "seek-10-minute", -600000, QKeySequence(QString::fromLatin1("Ctrl+Left"))) << SeekInfo( i18nc("@action:inmenu","1 minute backward"), "seek-1-minute", -60000, QKeySequence(QString::fromLatin1( "Shift+Left"))) << SeekInfo( i18nc("@action:inmenu","10 seconds backward"), "seek-10-second", -10000, QKeySequence(QString::fromLatin1( "Left"))) << SeekInfo( i18nc("@action:inmenu","1 seconds backward"), "seek-1-second", -1000, QKeySequence(QString::fromLatin1( "Up"))) << SeekInfo( i18nc("@action:inmenu","100 milliseconds backward"), "seek-100-millisecond", -100, QKeySequence(QString::fromLatin1( "Shift+Up"))) << SeekInfo( i18nc("@action:inmenu","100 milliseconds forward"), "seek+100-millisecond", 100, QKeySequence(QString::fromLatin1( "Shift+Down"))) << SeekInfo( i18nc("@action:inmenu","1 seconds forward"), "seek+1-second", 1000, QKeySequence(QString::fromLatin1( "Down"))) << SeekInfo( i18nc("@action:inmenu","10 seconds forward"), "seek+10-second", 10000, QKeySequence(QString::fromLatin1( "Right"))) << SeekInfo( i18nc("@action:inmenu","1 minute forward"), "seek+1-minute", 60000, QKeySequence(QString::fromLatin1( "Shift+Right"))) << SeekInfo( i18nc("@action:inmenu","10 minutes forward"), "seek+10-minute", 600000, QKeySequence(QString::fromLatin1( "Ctrl+Right"))); int count=0; Q_FOREACH( const SeekInfo& info, list ) { if ( count++ == 5 ) { QAction* sep = new QAction( menu ); sep->setSeparator(true); menu->addAction(sep); } QAction * seek = m_actions->addAction( QString::fromLatin1(info.name), m_videoDisplay, SLOT(seek())); seek->setText(info.title); seek->setData(info.value); seek->setShortcut( info.key ); menu->addAction(seek); } QAction* sep = new QAction(m_popup); sep->setSeparator(true); m_popup->addAction( sep ); m_videoActions.append( sep ); m_stop = m_actions->addAction( QString::fromLatin1("viewer-video-stop"), m_videoDisplay, SLOT(stop()) ); m_stop->setText( i18nc("@action:inmenu Stop video playback","Stop") ); m_popup->addAction( m_stop ); m_videoActions.append(m_stop); m_playPause = m_actions->addAction( QString::fromLatin1("viewer-video-pause"), m_videoDisplay, SLOT(playPause()) ); // text set in contextMenuEvent() m_playPause->setShortcut( Qt::Key_P ); m_popup->addAction( m_playPause ); m_videoActions.append( m_playPause ); m_makeThumbnailImage = m_actions->addAction( QString::fromLatin1("make-thumbnail-image"), this, SLOT(makeThumbnailImage())); m_makeThumbnailImage->setShortcut(Qt::ControlModifier + Qt::Key_S); m_makeThumbnailImage->setText( i18nc("@action:inmenu","Use current frame in thumbnail view") ); m_popup->addAction(m_makeThumbnailImage); m_videoActions.append(m_makeThumbnailImage); QAction * restart = m_actions->addAction( QString::fromLatin1("viewer-video-restart"), m_videoDisplay, SLOT(restart()) ); restart->setText( i18nc("@action:inmenu Restart video playback.","Restart") ); m_popup->addAction( restart ); m_videoActions.append( restart ); } void Viewer::ViewerWidget::createCategoryImageMenu() { m_categoryImagePopup = new MainWindow::CategoryImagePopup( m_popup ); m_popup->addMenu( m_categoryImagePopup ); connect(m_categoryImagePopup, &MainWindow::CategoryImagePopup::aboutToShow, this, &ViewerWidget::populateCategoryImagePopup); } void Viewer::ViewerWidget::createFilterMenu() { m_filterMenu = new QMenu( m_popup ); m_filterMenu->setTitle( i18nc("@title:inmenu","Filters") ); m_filterNone = m_actions->addAction( QString::fromLatin1("filter-empty"), this, SLOT(filterNone()) ); m_filterNone->setText( i18nc("@action:inmenu","Remove All Filters") ); m_filterMenu->addAction( m_filterNone ); m_filterBW = m_actions->addAction( QString::fromLatin1("filter-bw"), this, SLOT(filterBW()) ); m_filterBW->setText( i18nc("@action:inmenu","Apply Grayscale Filter") ); m_filterBW->setCheckable( true ); m_filterMenu->addAction( m_filterBW ); m_filterContrastStretch = m_actions->addAction( QString::fromLatin1("filter-cs"), this, SLOT(filterContrastStretch()) ); m_filterContrastStretch->setText( i18nc("@action:inmenu","Apply Contrast Stretching Filter") ); m_filterContrastStretch->setCheckable( true ); m_filterMenu->addAction( m_filterContrastStretch ); m_filterHistogramEqualization = m_actions->addAction( QString::fromLatin1("filter-he"), this, SLOT(filterHistogramEqualization()) ); m_filterHistogramEqualization->setText( i18nc("@action:inmenu","Apply Histogram Equalization Filter") ); m_filterHistogramEqualization->setCheckable( true ); m_filterMenu->addAction( m_filterHistogramEqualization ); m_filterMono = m_actions->addAction( QString::fromLatin1("filter-mono"), this, SLOT(filterMono()) ); m_filterMono->setText( i18nc("@action:inmenu","Apply Monochrome Filter") ); m_filterMono->setCheckable( true ); m_filterMenu->addAction( m_filterMono ); m_popup->addMenu( m_filterMenu ); } void Viewer::ViewerWidget::test() { #ifdef TESTING QTimeLine* timeline = new QTimeLine; timeline->setStartFrame( _infoBox->y() ); timeline->setEndFrame( height() ); connect(timeline, &QTimeLine::frameChanged, this, &ViewerWidget::moveInfoBox); timeline->start(); #endif // TESTING } void Viewer::ViewerWidget::moveInfoBox( int y) { m_infoBox->move( m_infoBox->x(), y ); } void Viewer::ViewerWidget::createVideoViewer() { m_videoDisplay = new VideoDisplay( this ); addWidget( m_videoDisplay ); connect(m_videoDisplay, &VideoDisplay::stopped, this, &ViewerWidget::videoStopped); } void Viewer::ViewerWidget::stopPlayback() { m_videoDisplay->stop(); } void Viewer::ViewerWidget::invalidateThumbnail() const { ImageManager::ThumbnailCache::instance()->removeThumbnail( currentInfo()->fileName() ); } void Viewer::ViewerWidget::addTaggedAreas() { // Clean all areas we probably already have foreach (TaggedArea *area, findChildren()) { area->deleteLater(); } QMap> taggedAreas = currentInfo()->taggedAreas(); QMapIterator> areasInCategory(taggedAreas); QString category; QString tag; while (areasInCategory.hasNext()) { areasInCategory.next(); category = areasInCategory.key(); QMapIterator areaData(areasInCategory.value()); while (areaData.hasNext()) { areaData.next(); tag = areaData.key(); // Add a new frame for the area TaggedArea *newArea = new TaggedArea(this); newArea->setTagInfo(category, category, tag); newArea->setActualGeometry(areaData.value()); newArea->show(); connect(m_infoBox, &InfoBox::tagHovered, newArea, &TaggedArea::checkShowArea); connect(m_infoBox, &InfoBox::noTagHovered, newArea, &TaggedArea::resetViewStyle); } } // Be sure to display the areas, as viewGeometryChanged is not always emitted on load QSize imageSize = currentInfo()->size(); QSize windowSize = this->size(); // On load, the image is never zoomed, so it's a bit easier ;-) double scaleWidth = double(imageSize.width()) / windowSize.width(); double scaleHeight = double(imageSize.height()) / windowSize.height(); int offsetTop = 0; int offsetLeft = 0; if (scaleWidth > scaleHeight) { offsetTop = (windowSize.height() - imageSize.height() / scaleWidth); } else { offsetLeft = (windowSize.width() - imageSize.width() / scaleHeight); } remapAreas( QSize(windowSize.width() - offsetLeft, windowSize.height() - offsetTop), QRect(QPoint(0, 0), QPoint(imageSize.width(), imageSize.height())), 1 ); } void Viewer::ViewerWidget::remapAreas(QSize viewSize, QRect zoomWindow, double sizeRatio) { QSize currentWindowSize = this->size(); int outerOffsetLeft = (currentWindowSize.width() - viewSize.width()) / 2; int outerOffsetTop = (currentWindowSize.height() - viewSize.height()) / 2; if (sizeRatio != 1) { zoomWindow = QRect( QPoint( double(zoomWindow.left()) * sizeRatio, double(zoomWindow.top()) * sizeRatio ), QPoint( double(zoomWindow.left() + zoomWindow.width()) * sizeRatio, double(zoomWindow.top() + zoomWindow.height()) * sizeRatio ) ); } double scaleHeight = double(viewSize.height()) / zoomWindow.height(); double scaleWidth = double(viewSize.width()) / zoomWindow.width(); int innerOffsetLeft = -zoomWindow.left() * scaleWidth; int innerOffsetTop = -zoomWindow.top() * scaleHeight; Q_FOREACH(TaggedArea *area, findChildren()) { QRect actualGeometry = area->actualGeometry(); QRect screenGeometry; screenGeometry.setWidth(actualGeometry.width() * scaleWidth); screenGeometry.setHeight(actualGeometry.height() * scaleHeight); screenGeometry.moveTo( actualGeometry.left() * scaleWidth + outerOffsetLeft + innerOffsetLeft, actualGeometry.top() * scaleHeight + outerOffsetTop + innerOffsetTop ); area->setGeometry(screenGeometry); } } void Viewer::ViewerWidget::copyTo() { QUrl src = QUrl::fromLocalFile(currentInfo()->fileName().absolute()); if (m_lastCopyToTarget.isNull()) { // get directory of src file m_lastCopyToTarget = QFileInfo(src.path()).path(); } QFileDialog dialog( this ); dialog.setWindowTitle( i18nc("@title:window", "Copy Image to...") ); // use directory of src as start-location: dialog.setDirectory(m_lastCopyToTarget); dialog.selectFile(src.fileName()); dialog.setAcceptMode(QFileDialog::AcceptSave); dialog.setLabelText(QFileDialog::Accept, i18nc("@action:button", "Copy")); if (dialog.exec()) { QUrl dst = dialog.selectedUrls().first(); KIO::CopyJob *job = KIO::copy(src, dst); connect(job, &KIO::CopyJob::finished, job, &QObject::deleteLater); // get directory of dst file m_lastCopyToTarget = QFileInfo(dst.path()).path(); } } // vi:expandtab:tabstop=4 shiftwidth=4:
%2