diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 2ee930a1de..ed419a09a1 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,5 +1,4 @@ include: - https://invent.kde.org/sysadmin/ci-tooling/raw/master/invent/ci-before.yml - https://invent.kde.org/sysadmin/ci-tooling/raw/master/invent/ci-applications-linux.yml - - https://invent.kde.org/sysadmin/ci-tooling/raw/master/invent/ci-applications-freebsd.yml - - https://invent.kde.org/sysadmin/ci-tooling/raw/master/invent/ci-applications-windows.yml +# - https://invent.kde.org/sysadmin/ci-tooling/raw/master/invent/ci-applications-freebsd.yml diff --git a/Mainpage.dox b/Mainpage.dox index 592e692074..6bedb9c0ef 100644 --- a/Mainpage.dox +++ b/Mainpage.dox @@ -1,1128 +1,1127 @@ /** \mainpage digiKam API reference page. \author (c) 2001-2020 digiKam team. \brief digiKam is an advanced open-source digital photo management application that runs on Linux, Windows, and MacOS. \image html logo-digikam.png \section sourcedirs Source Code Directories digiKam is split into a number of components, each ones located to a dedicated directory. The main namespace is Digikam for the digiKam application and all sub components. A second one is ShowFoto for the stand alone version of image editor. See below the complete list of directories used by the project: | SOURCE TREE-VIEW | DETAILS | | ----------------------------------------------------------- | ----------------------------------------------------------------------- | |├── AUTHORS | List of developers and contributors to the project | |├── bootstrap.linux | Configuration script to compile under Linux | |├── bootstrap.local | Configuration script to compile a local version under Linux | |├── bootstrap.macports | Configuration script to compile under MacOS with Macports | |├── bootstrap.mxe | Configuration script to cross-compile for Windows under Linux with MXE | |├── build | Temporary directory created by bootstrap script to host compiled files | |├── ChangeLog | Complete list of source code changes since the project origin | |├── CMakeLists.txt | Main Cmake script including lead compilation rules for the project | |├── COPYING | Main project license | |├── COPYING-CMAKE-SCRIPTS | Cmake files license | |├── COPYING.LIB | Internal shared libraries license | |├── download-repos | Script to download all source code including extra repositories | |├── gits | Script to perform git in recursive mode over multiple repositories | |├── gitup | Script to perform git pull --rebase in reccursive mode | |├── Mainpage.dox | API documentation main page based on Doxygen | |├── Messages.sh | Script to extract strings for translators | |├── NEWS | Notice to resume all project changes done at release time | |├── README.DEVEL | Read me file for developers | |├── README.md | First start helper documentation | |├── README.BUNDLES | Read me for Linux, MacOs, and Windows bundles support | |├── build | Directory to store compiled files and binary targets | |├── core | All source code are hosted in this directory | |│ ├── app | Lead application component | |│ │ ├── date | All date relevant views | |│ │ ├── dragdrop | Drag and drop helper classes | |│ │ ├── filters | Tags filter widgets | |│ │ ├── items | Item management classes | |│ │ │ ├── delegate | Item view delegate | |│ │ │ ├── overlays | Item overlays | |│ │ │ ├── thumbbar | Item thumbbar widget | |│ │ │ ├── utils | Item utility classes | |│ │ │ └── views | Item view classes | |│ │ ├── main | Main digiKam application | |│ │ ├── utils | Generic utility classes | |│ │ └── views | Views classes | |│ │ ├── preview | Item preview classes | |│ │ ├── sidebar | Left sidebar contents | |│ │ ├── stack | Stacked-view show in central place of main digiKam window | |│ │ ├── tableview | Table-view classes | |│ │ └── utils | View utility classes | |│ ├── cmake | Extra Cmake scripts will be hosted here | |│ │ ├── modules | Cmake scripts to find extra dependencies | |│ │ ├── templates | Cmake template files used at configuration time | |│ │ ├── about | Welcome page files (HTML + CSS) | |│ │ ├── colorschemes | GUI Color scheme files | |│ │ ├── database | Database XML configuration files | |│ │ ├── facesengine | Face detection and recognition data files | |│ │ ├── filters | Image filters data files | |│ │ ├── geolocation | Geolocation tool data files | |│ │ ├── hotplug | Hotplug Linux integration files | |│ │ ├── htmlgallery | HTML gallery tool data files | |│ │ ├── icons | Application icons | |│ │ ├── metadata | Metadata tool data files | |│ │ ├── pics | Application pictures | |│ │ ├── printcreator | Print Creator tool data files | |│ │ ├── profiles | Basis open source ICC color profiles | |│ │ └── scripts | Miscs maintenance scripts | |│ ├── dplugins | All digiKam plugins will be hosted in this directory | |│ │ ├── bqm | All Batch Queue Manager plugins | |│ │ │ ├── colors | All color adjustments plugins | |│ │ │ ├── convert | All file convert plugins | |│ │ │ ├── custom | All user-custom processing plugins | |│ │ │ ├── decorate | All decorate item plugins | |│ │ │ ├── enhance | All enhance item plugins | |│ │ │ ├── filters | All filter item plugins | |│ │ │ ├── metadata | All metadata edit plugins | |│ │ │ └── transform | All tranform item plugins | |│ │ ├── editor | All Image Editor plugins | |│ │ │ ├── colors | All color adjustments plugins | |│ │ │ ├── decorate | All decorate item plugins | |│ │ │ ├── enhance | All enhance item plugins | |│ │ │ ├── file | All file processing plugins | |│ │ │ ├── filters | All filter item plugins | |│ │ │ └── transform | All tranform item plugins | |│ │ ├── generic | All generic plugins | |│ │ │ ├── import | Tools to import items | |│ │ │ ├── metadata | Plugins to change items metadata | |│ │ │ ├── tools | Plugins hosted in Tools main menu | |│ │ │ ├── view | Plugins to display items | |│ │ │ └── webservices | All plugins to import and export items to remote web-services | |│ │ ├── rawimport | All Raw import plugins | |│ │ └── dimg | All DImg image loader plugins | |│ ├── libs | digiKam core sub-components (few are shared with Showfoto) | |│ │ ├── album | All classes use to manage digiKam albums operations and properties | |│ │ ├── database | All low level database interface is here | |│ │ │ ├── collection | All classes relevant of collections management | |│ │ │ ├── coredb | The core database interface used to host all image properties | |│ │ │ ├── dbjobs | All database multi-threaded jobs | |│ │ │ ├── engine | The low level database engine classes | |│ │ │ ├── haar | The similarity low level algorithms to compute image finger-prints | |│ │ │ ├── history | The item history classes for the database | |│ │ │ ├── item | The database item classes, including containers, lister, and scanner | |│ │ │ ├── models | The databse model classes | |│ │ │ ├── server | The Mysql internal server | |│ │ │ ├── similaritydb | The similarity database | |│ │ │ ├── tags | The database tags management classes | |│ │ │ ├── thumbsdb | The thumbnails database | |│ │ │ └── utils | Miscs tools and widgets used with database | |│ │ ├── dialogs | Common dialogs | |│ │ ├── dimg | The Qt digiKam image data container support ICC and 16 bits color depth | |│ │ │ ├── filters | All image filters will be hosted here. All support 16 bits color depth | |│ │ │ │ ├── auto | Auto colors correction filters | |│ │ │ │ ├── bcg | Brightness-Contrast-Gamma filter | |│ │ │ │ ├── bw | Black and White image converter, including infrared filter | |│ │ │ │ ├── cb | Colors balance filter | |│ │ │ │ ├── curves | Colors curves filter | |│ │ │ │ ├── decorate | Decorate filters | |│ │ │ │ ├── film | Analog film emulation filters | |│ │ │ │ ├── fx | Special effect filters | |│ │ │ │ ├── greycstoration | Cimg based restoration filter | |│ │ │ │ ├── hsl | Hue-Saturation-Lightness filter | |│ │ │ │ ├── icc | Icc color profile filters | |│ │ │ │ ├── imgqsort | The image quality sort algorithms | |│ │ │ │ ├── lc | Local contrast filter (pseudo HDR) | |│ │ │ │ ├── lens | Lens corrections filters, including Qt Lensfun interface | |│ │ │ │ ├── levels | Color levels filter | |│ │ │ │ ├── nr | Wavelets noise reduction filter | |│ │ │ │ ├── redeye | Red-eyes parser and fixer | |│ │ │ │ ├── sharp | Image sharp filter, including Unsharped-mask and Refocus | |│ │ │ │ ├── transform | All image transformation filters | |│ │ │ │ └── wb | White balance filter | |│ │ │ ├── imagehistory | Image history interface for image container | |│ │ │ └── loaders | All DImg image loaders interface | |│ │ ├── metadataengine | The metadata wrapper based on Exiv2 for image and FFMpeg for video | |│ │ ├── dngwriter | Qt classes to convert RAW files to DNG format | |│ │ │ └── extra | DNG and XMP sdks from Adobe | |│ │ ├── dplugins | All shared dplugins classes are hosted here | |│ │ │ ├── core | Low level classes for plugins definitions | |│ │ │ ├── iface | Low level classes for host interface definitions | |│ │ │ ├── setup | Classes to setup plugins in configuration pannel | |│ │ │ ├── webservices | Common classes for Webservices tools | |│ │ │ └── widgets | Common widget sfor plugins | |│ │ ├── dtrash | digiKam trash manager full independant of desktop trash | |│ │ ├── facesengine | Face detection and recognition engine + Faces database implementations | |│ │ │ ├── alignment-congealing | Face alignment based on congealing method | |│ │ │ ├── alignment-flandmark | Face alignment based on flandmark method | |│ │ │ ├── detection | Face detection algorithms | |│ │ │ ├── dnnface | Deep-learning face algorithms | |│ │ │ ├── facedb | Faces database classes | |│ │ │ ├── opencv3-face | OpenCV version 3 face management classes | |│ │ │ ├── preprocessing-tantriggs | Face pre-processing based on tantriggs method | |│ │ │ ├── recognition-dlib-dnn | Deep-learning faces recognition module | |│ │ │ ├── recognition-opencv-eigenfaces | Eigen faces recognition mudule | |│ │ │ ├── recognition-opencv-fisherfaces | Fisher faces recognition module | |│ │ │ ├── recognition-opencv-lbph | LBPH bases faces recognition module | |│ │ │ └── shape-predictor | Shape predictor algorithms | |│ │ ├── fileactionmanager | Classes to connect database and metadata actions to file operations | |│ │ ├── filters | Widgets to filter items by metadata properties | |│ │ ├── imageproperties | All widgets used in right side-bar from all main views | |│ │ ├── iojobs | Multithreaded jobs manager used with files operations | |│ │ ├── jpegutils | Utilities to process JPEG files | |│ │ │ └── libjpeg | JPEG loss-less transform private implementations from libjpeg | |│ │ ├── kmemoryinfo | Qt backend to analyze system memory information | |│ │ ├── models | Qt models used with item views | |│ │ ├── notificationmanager | Multi-desktop notifications wrapper | |│ │ ├── pgfutils | Qt Classes to work with PGF image format | |│ │ ├── progressmanager | Multi-level operations progress widget | |│ │ ├── rawengine | Qt classes to work with libraw decoder | |│ │ │ └── libraw | Internal Libraw sdk | |│ │ ├── settings | digiKam settings manager | |│ │ ├── tags | Classes to play with tags | |│ │ │ └── tagsmanager | Tags manager view | |│ │ ├── template | Metadata template support | |│ │ ├── threadimageio | Classes to process thumbs and preview extraction including video support| |│ │ ├── threads | Classes to manage and chain threads using multi-core | |│ │ ├── timeadjust | Common classes time adjustements tools | |│ │ ├── transitionmngr | Frames transitions manager | |│ │ ├── versionmanager | Classes to manage versionning operations | |│ │ ├── video | Classes to play with video contents | |│ │ └── widgets | To host plenty of widgets used everywhere | |│ │ ├── colors | Colors relevant views | |│ │ ├── combo | Combo-box helper classes | |│ │ ├── common | Uncategorized widgets | |│ │ ├── files | File operation classes | |│ │ ├── fonts | Font management classes | |│ │ ├── graphicsview | Graphics-view implementation (model-view) | |│ │ ├── iccprofiles | ICC color profiles widgets | |│ │ ├── imagehistory | Image history widgets | |│ │ ├── itemview | Item-view implementations (model-view) | |│ │ ├── layout | Layout helper classes | |│ │ ├── mainview | Common top-level view implementations | |│ │ ├── metadata | Metadata widgets | |│ │ └── range | Range helper classes | |│ ├── showfoto | Stand alone image editor | |│ │ ├── main | Main Showfoto application | |│ │ ├── setup | Showfoto Setup views | |│ │ └── thumbbar | Showfoto thumb-bar views | |│ ├── tests | Unit tests | |│ └── utilities | digiKam utilities and advanced tools (few are shared with showfoto) | |│ ├── advancedrename | Advance rename tool | |│ ├── extrasupport | Extra desktop features support as Baloo search engine | |│ ├── facemanagement | Face management classes and tools | |│ ├── firstrun | First-run assistant to configure lead digiKam settings | |│ ├── fuzzysearch | Similarity search tools | |│ ├── geolocation | All geo-location tools are located here | |│ │ ├── editor | Tool to edit items geo-location | |│ │ ├── geoiface | All shared classes used by geo-location tools | |│ │ ├── geomapwrapper | Legacy helper classes for geo-location support | |│ │ └── mapsearches | Tool to perform map searches | |│ ├── imageeditor | The famous digiKam image editor, a lots of classes shared with Showfoto | |│ │ ├── core | Core implementation including canvas and tools interface | |│ │ ├── dialogs | Image editor dialogs | |│ │ ├── editor | The core image editors classes | |│ │ ├── main | The main digiKam image editor view, not shared with Showfoto | |│ │ └── widgets | All common widgets | |│ ├── import | The import tools, including USB MAss Storage, Gphoto2, and Sane support | |│ │ ├── backend | Camera backends | |│ │ ├── dialogs | Import tools dialogs | |│ │ ├── items | Import item classes | |│ │ ├── main | Import tool main view | |│ │ ├── models | Import model classes | |│ │ ├── views | Import view classes | |│ │ └── widgets | Import common widgets | |│ ├── lighttable | The Light-table tool to compare images side by side | |│ ├── maintenance | The digiKam tool to maintain the database contents | |│ ├── queuemanager | The famous Batch Queue Manager tool | |│ │ ├── main | The main BQM view | |│ │ ├── manager | The multi-core manager to run tools in background | |│ │ ├── tools | All BQM tools classed by functions | |│ │ └── views | The BQM internal views | |│ ├── searchwindow | The powerfull advanced search tool | |│ ├── setup | All digiKam setup panel, with few ones shared with Showfoto | |│ │ ├── album | Album configuration views | |│ │ ├── camera | Camera configuration views | |│ │ ├── collections | Collection configuration views | |│ │ ├── editor | Image Editor configuration views | |│ │ └── metadata | Metadata configuration views | |│ └── slideshow | The simple slideshow tool | |├── doc | Handbooks directory | |│ ├── digikam | digiKam manual | |│ ├── project | Extra files for the documentation | |│ └── showfoto | Showfoto manual | |├── extra | Optional extra libraries to compile advanced features | |└── project | Extra project parts | | ├── bundles | Bundles build scripts | | │ ├── 3rdparty | External components required to build bundles | | │ ├── appimage | Linux AppImage | | │ ├── macports | MacOs package | | │ └── mxe | Windows installer | | ├── documents | Project documentations | | ├── release | Release scripts | | ├── reports | Static analyzers report scripts | | └── scripts | 3rdparty source code management scripts | \section externaldeps External Dependencies \subsection depscheckout Dependencies To Checkout All Source Code - Perl http://www.perl.org - Git http://git-scm.com \subsection depstrans Dependencies To Process Translations Files (optional) - Ruby http://www.ruby-lang.org (to extract translations files from remote repositories) - Subversion https://subversion.apache.org/ (to extract translations files from remote repositories) - Gettext https://www.gnu.org/software/gettext (including Msgfmt to compile po files to mo files) \subsection depscomplink Dependencies To Compile And Link Source Code The full list of mandatory (X) and optional (opt) external dependencies required to compile and link digiKam source code is listed below. | Dependency | Status | Version | Project URL | Remarks | TODO | |----------------------|----------|-------------|---------------------------------------------------|--------------------------------------------------------|---------------------------------------------------------------------------------------| | CMake | X | >= 3.3.2 | http://www.cmake.org | | | | ECM | X | >= 1.7.0 | https://cgit.kde.org/extra-cmake-modules.git/ | | | | Qt5::Core | X | >= 5.9 | http://code.qt.io/cgit/qt/qtbase.git/ | | | | Qt5::Gui | X | >= 5.9 | http://code.qt.io/cgit/qt/qtbase.git/ | | | | Qt5::Widgets | X | >= 5.9 | http://code.qt.io/cgit/qt/qtbase.git/ | | | | Qt5::Network | X | >= 5.9 | http://code.qt.io/cgit/qt/qtbase.git/ | | | | Qt5::Sql | X | >= 5.9 | http://code.qt.io/cgit/qt/qtbase.git/ | Including Qt5::Sqlite and Qt5::Mysql plugins | | | Qt5::Xml | X | >= 5.9 | http://code.qt.io/cgit/qt/qtbase.git/ | | | | Qt5::Concurrent | X | >= 5.9 | http://code.qt.io/cgit/qt/qtbase.git/ | | | | Qt5::PrintSupport | X | >= 5.9 | http://code.qt.io/cgit/qt/qtbase.git/ | | | | Qt5::Svg | X | >= 5.9 | http://code.qt.io/cgit/qt/qtsvg.git/ | | | | Qt5::XmlPatterns | X | >= 5.9 | http://code.qt.io/cgit/qt/qtxmlpatterns.git/ | | | | Qt5::WebKitWidgets | X | >= 5.9 | http://code.qt.io/cgit/qt/qtwebkit.git/ | To render web contents | Used with MXE only as WebEngine do not compile with Mingw | | Qt5::WebEngine | opt | >= 5.9 | http://code.qt.io/cgit/qt/qtwebengine.git/ | To render web contents (ENABLE_QWEBENGINE=on) | To replace QWebKitWidgets everywhere, excepted for MXE | | Qt5::X11Extras | opt | >= 5.9 | http://code.qt.io/cgit/qt/qtx11extras.git/ | For color management support under Linux | | | Qt5::DBus | opt | >= 5.9 | http://code.qt.io/cgit/qt/qtbase.git/ | Optional: only for Linux Desktop | | | Qt5::OpenGL | opt | >= 5.9 | http://code.qt.io/cgit/qt/qtbase.git/ | For Presentation tool | | | Qt5::Test | opt | >= 5.9 | http://code.qt.io/cgit/qt/qtbase.git/ | To compile test codes (BUILD_TESTING=on) | | | Qt5::Qml | opt | >= 5.9 | http://code.qt.io/cgit/qt/qtbase.git/ | To compile test codes (BUILD_TESTING=on) O2 unit tests | | | Qt5::WebView | opt | >= 5.9 | http://code.qt.io/cgit/qt/qtbase.git/ | To compile test codes (BUILD_TESTING=on) O2 unit tests | | | KF5::Config | X | >= 5.5.0 | https://cgit.kde.org/kconfig.git/about/ | | | | KF5::XmlGui | X | >= 5.5.0 | https://cgit.kde.org/kxmlgui.git/about/ | | | | KF5::I18n | X | >= 5.5.0 | https://cgit.kde.org/ki18n.git/about/ | | | | KF5::WindowSystem | X | >= 5.5.0 | https://cgit.kde.org/kwindowsystem.git/about/ | | | | KF5::Service | X | >= 5.5.0 | https://cgit.kde.org/kservice.git/about/ | TODO: make optional for Linux desktop (DFileOperations)| | | KF5::Solid | X | >= 5.5.0 | https://cgit.kde.org/solid.git/about/ | | | | KF5::CoreAddons | X | >= 5.5.0 | https://cgit.kde.org/kcoreaddons.git/about/ | Needs for KAboutData | | | KF5::NotifyConfig | opt | >= 5.5.0 | https://cgit.kde.org/knotifyconfig.git/about/ | For Linux desktop application notify configuration | | | KF5::Notifications | opt | >= 5.5.0 | https://cgit.kde.org/knotifications.git/about/ | For Linux desktop notifications integrations | | | KF5::ThreadWeaver | opt | >= 5.5.0 | https://cgit.kde.org/threadweaver.git/about/ | For panorama tool | | | KF5::IconThemes | opt | >= 5.5.0 | https://cgit.kde.org/kiconthemes.git/about/ | Optional: only for Linux Desktop (KIconDialog) | | | KF5::FileMetaData | opt | >= 5.5.0 | https://cgit.kde.org/kfilemetadata.git/about/ | KDE files indexer (ENABLE_KFILEMETADATASUPPORT=on) | Still experimental, disabled by default. Implementation from KDE side very unstable | | KF5::CalendarCore | opt | >= 5.5.0 | https://cgit.kde.org/kcalendarcore.git/about/ | For calendar tool to setup ical special events | | | KF5::KIO | opt | >= 5.5.0 | https://cgit.kde.org/kio.git/about/ | Optional: only for Linux Desktop | | | KF5::AkonadiContact | opt | >= 4.89.0 | https://cgit.kde.org/akonadi-contacts.git/about/ | KDE Mail contacts (ENABLE_AKONADICONTACTSUPPORT=on) | Still experimental, disabled by default. Need testing | | libopencv | X | >= 3.3 | https://opencv.org/ | OpenCV 4 supported with digiKam 6.1.0 | DNN module required for face management | | libpthread | X | >= 2.0.0 | http://www.gnu.org/software/hurd/libpthread.html | For DNG converter | | | libtiff | X | >= 4.0 | http://www.remotesensing.org/libtiff | For DImg image loader | | | libpng | X | >= 1.6 | http://www.libpng.org/pub/png/libpng.html | For DImg image loader | | | libjpeg | X | >= 6b | http://www.ijg.org | jpeglib >= 8.0 hightly recommended for RawEngine | | | libboost | X | >= 1.55.0 | http://www.boost.org/doc/libs | For Versionning support | | | liblcms | X | >= 2.x | http://www.littlecms.com | For Color Management support | | | libexpat | X | >= 2.1.0 | http://expat.sourceforge.net | For DNG converter | | | libexiv2 | X | >= 0.27.0 | http://www.exiv2.org | Metadata low level management. | | | libxml2 | opt | >= 2.7.0 | http://xmlsoft.org | For HtmlGallery tool | | | libxslt | opt | >= 1.1.0 | http://xmlsoft.org/XSLT | For HtmlGallery tool | | | libqtav | opt | >= 1.12.0 | http://www.qtav.org | To play video and audio (ENABLE_MEDIAPLAYER=on) | | | libffmpeg | opt | >= 3.3.x | http://www.ffmpeg.org | To play video and audio (ENABLE_MEDIAPLAYER=on) | libavformat, libavutil, libavcodec used to extract video metadata | | Flex | opt | >= 2.5.0 | http://flex.sourceforge.net | For Panorama tool | | | Bison | opt | >= 2.5.0 | http://www.gnu.org/software/bison/bison.html | For Panorama tool | | | libmesa | opt | >= 11.0 | http://www.mesa3d.org | For Presentation tools (Linux only) | | -| libkvkontakte | opt | >= 5.0.0 | https://cgit.kde.org/libkvkontatke.git/about/ | For Vkontakte tool support | | | libksane | opt | >= 5.0.0 | https://cgit.kde.org/libksane.git/about/ | Digital scanner support | | | libjpasper | opt | >= 1.900.1 | http://www.ece.uvic.ca/~mdadams/jasper | For JPEG-2000 support | | | libmarble | opt | >= 0.22 | https://cgit.kde.org/marble.git/about/ | For geolocation support | | | libeigen3 | opt | >= 3.2 | http://eigen.tuxfamily.org | For Refocus tool | See if Clapack from OpenCV can be used instead | | liblensfun | opt | >= 0.2.8 | https://github.com/lensfun/lensfun | For LensCorrection tool | | | liblqr-1 | opt | >= 0.4.2 | http://liblqr.wikidot.com | For Liquid rescale tool | | | libgphoto2 | opt | >= 2.5 | http://www.gphoto.org | Digital camera drivers support. Need libusb-1 | | | libgomp | opt | >= 5.0 | https://gcc.gnu.org/onlinedocs/libgomp | OpenMP support for RawEngine | | | libmagick++ | opt | >= 6.7.0 | https://www.imagemagick.org | ImageMagick codecs support for DImg image loader | Version >= 7.0 recommended | | DrMingw | opt | >= 0.8.2 | https://github.com/jrfonseca/drmingw | Mingw crash handler (run-time deps only for windows) | | | libx265 | opt | >= 2.2 | http://x265.org/ | For HEIC encoding support | | \section getsourcecode Get Source Code \subsection softcomponents Software Components digiKam project use a single git repository from GitLab to host whole source code base. The project page is given below: https://invent.kde.org/graphics/digikam The digikam handbook source code is hosted in a separate GitLab repository: https://invent.kde.org/documentation/digikam-doc \section develenv Development Envirronnement If you are a developer with push access to the git repositories, it is strongly recommended to use the "kde:" prefix and let git use the read-only mirrors for pulling. If you did not clone this repository from "kde:", do it again: \code git config --global url.git://anongit.kde.org/.insteadof kde: git config --global url.ssh://git@git.kde.org/.pushinsteadof kde: git clone kde:digikam \endcode See below an example of \b .gitconfig file working with a developer account : \code [url "git://anongit.kde.org/"] insteadof = kde:// [url "git@git.kde.org:"] pushinsteadof = kde:// [url "ssh://git@git.kde.org/"] pushinsteadof = kde:// [alias] up = pull --rebase -v --stat ci = commit -a -v [core] editor = mcedit [user] name = my name email = my email [push] default = tracking [color] # turn on color diff = auto status = auto branch = auto interactive = auto ui = auto [color "branch"] current = green bold local = green remote = red bold [color "diff"] meta = yellow bold frag = magenta bold old = red bold new = green bold [color "status"] added = green bold changed = yellow bold untracked = red [color "sh"] branch = yellow [color "sh"] \endcode \section cmakeoptions Cmake Configuration Options To configure the project with CMake, use dedicated "bootstrap" script for your platform where all available configuration options are present with default values. There are two configuration sections : the top level and the core. \subsection topleveloptions Top Level Configuration - Packaging options: - \b DIGIKAMSC_COMPILE_DIGIKAM : Build digiKam core (default=ON). - \b DIGIKAMSC_CHECKOUT_PO : Extract application translation files from remote repositories (default=ON). - \b DIGIKAMSC_CHECKOUT_DOC : Extract documentation translation files from remote repositories (default=ON). - \b DIGIKAMSC_COMPILE_DOC : Build handbook i18n files. It require to checkout documentation translations files before from remote repositories (default=ON). - \b DIGIKAMSC_COMPILE_PO : Build GUI i18n files. It require to checkout application translations files before from remote repositories (default=ON). - Developers only options: - \b BUILD_TESTING=ON : Build tests code (default=ON). \subsection coreoptions Core Configuration - Extra feature support options: - \b ENABLE_KFILEMETADATASUPPORT : Build digiKam with KDE files indexer support (default=OFF). - \b ENABLE_AKONADICONTACTSUPPORT : Build digiKam with KDE Mail Contacts support (default=OFF). - \b ENABLE_MEDIAPLAYER : Build digiKam with Media Player support (default=OFF). - \b ENABLE_DBUS : Build digiKam with DBUS support (default=ON). - \b ENABLE_APPSTYLES : Build digiKam with support for changing the widget application style (default=OFF). - \b ENABLE_QWEBENGINE : Build digiKam with QWebEngine instead of QWebKit (default=ON). - Experimental support options - \b ENABLE_MYSQLSUPPORT : Build digiKam with MySQL dabatase support (default=ON). - \b ENABLE_INTERNALMYSQL : Build digiKam with internal MySQL server executable (default=ON). - Developers only options: - \b ENABLE_DIGIKAM_MODELTEST : Enable ModelTest on some models for debugging (default=OFF). - Packaging options - \b ENABLE_DRMINGW : Enable the Dr. Mingw crash handler for windows (default=ON). - \b ENABLE_MINGW_HARDENING_LINKER : Enable DEP (NX), ASLR, and high-entropy ASLR linker flags for MinGW (default ON). \section setuplocaldev Setup Local Compilation and Run-Time This section describes how to install digiKam from the git repository, while keeping a system-wide digiKam install. This procedure is based on the configure script \b bootstrap.local 1. Set the root directory for your git install in bootstrap.local (DIGIKAM_INSTALL_PREFIX variable) 2. If you want a clean build directory, set CLEANROOT to 1 3. Type the following command in your terminal: \code $ ./bootstrap.local # or "./bootstrap.local --eclipse" if you intend to use Eclipse $ cd build $ make $ make install $ KDESYCOCA="/your/root/directory/var/tmp/kde-$USER/ksycoca5" kbuildsycoca5 \endcode To run digikam, use the following commands: \code $ export KDESYCOCA=/your/root/directory/var/tmp/kde-$USER/ksycoca5 $ export QT_PLUGIN_PATH=/your/root/directory/lib64/plugins:/your/root/directory/lib/plugins:$QT_PLUGIN_PATH $ export XDG_DATA_DIRS=/your/root/directory/share:$XDG_DATA_DIRS $ /your/root/directory/bin/digikam \endcode The same applies for all binaries in /your/root/directory/bin/ If your shell is bash, you can edit your \b .bashrc file (in $HOME) and add the follwing alias: \code DIGIKAMROOT="/your/root/directory" alias digikam-dev="KDESYCOCA=\$DIGIKAMROOT/var/tmp/kde-$USER/ksycoca5 XDG_DATA_DIRS=\$DIGIKAMROOT/share:\$XDG_DATA_DIRS QT_PLUGIN_PATH=\$DIGIKAMROOT/lib64/plugins:\$DIGIKAMROOT/lib/plugins:\$QT_PLUGIN_PATH \$DIGIKAMROOT/bin/digikam" \endcode then you can start your newly installed digikam with \code $ digikam-dev \endcode \section debugtraces Debug Traces At Run-Time digiKam uses categorized logging at run-time. By default, all debug messages are printed on the console. To disable output, you can either fine-grained control by using one or more logging categories listed below. Note: under Windows, to catch all debug messages you need to install an extra Microsoft application named DebugView available at this url: http://technet.microsoft.com/en-us/sysinternals/bb896647.aspx \subsection Enable Logging Using an Environment Variable You can set the environment variable \b QT_LOGGING_RULES. Rules are divided by semicolons. E.g. you can start digiKam like this on the commandline with thumbnails and core database messages disabled: \code export QT_LOGGING_RULES='digikam.thumbsdb=false:digikam.coredb=false' digikam \endcode \subsection digiKamloggincat Logging Categories in digiKam All loggin categories are listed in \ref digikam_debug.cpp source code. \subsection moreaboutloggincat Further Reading For more details see the Qt framework documentation about loggin categories available at this url: https://doc.qt.io/qt-5/qloggingcategory.html#details \section cmakecompilrules Cmake compilation rules \subsection cmakeintro Introduction The whole project is written mostly in C++/Qt and the Cmake framework is used to compile under Linux, MacOs, and Windows. The Cmake rules have been configured to reduce the linking overhead and improve CPU utilization with modular design. Independent Cmake configuration is presents in following folders: - root source dir - core - doc The Cmake rules will build the folowwing targets: - digikamcore shared lib - digikamdatabase shared lib - digikamgui shared lib - digikam executable - showfoto executable - plugin shared libraries (dplugins) - various test executables - if testing is enabled - various unit-tests - if testing is enabled Each of them depend on various sources which must be compiled before. A complete description of source code direction is given to the sourcedirs section. \subsection cmakeimpldetails CMake Implementation Details \subsubsection includedirs Include Directories Local include directories are all managed by this snippet of code: \code set(DK_INCLUDES_ALL "") HEADER_DIRECTORIES(DK_LOCAL_INCLUDES_RAW) \endcode The libjpeg- folders are all included, so we need to delete them all and include the correct one only: \code # This macro will set all paths which do not containt libjpeg- # We will add later the directory we need FOREACH(var ${DK_LOCAL_INCLUDES_RAW}) STRING(REGEX MATCH "libjpeg-" item ${var}) IF(item STREQUAL "") LIST(APPEND DK_LOCAL_INCLUDES ${var}) ENDIF(item) ENDFOREACH(var) set(DK_LOCAL_INCLUDES ${DK_LOCAL_INCLUDES} libs/jpegutils/${DIGIKAM_LIBJPEG_DIR}) include_directories(${DK_LOCAL_INCLUDES}) \endcode There is no need for manual intervention to add new includes, even if you add a new folder, just keep in mind to use: \code #include "tagmngrlistitem.h" \endcode instead of : \code #include "models/tagmngrlistitem.h" \endcode \subsubsection sharedlibs Shared Libraries To avoid linking overhead and make a better use of sources there are some dynamic libs as these one: - digikamcore : core components used by almost all executables as digiKam and Showfoto. - digikamdatabase : database components, also used together with digikamcore but only for digiKam Please add sources to digikam core or digikam database only if they don't depend on any big component from digikam main executable. These two shared libs must be kept small because they link in a lot of places \subsubsection staticlibs Static Libraries Currently cmake configuration features a lots of shared libraries as: - metadataedit - geolocationedit - digikamfaceengine This libraries are linked in digikam main executable and some tests tools. Avoid making static libraries if possible, and use OBJECT libraries instead. Only make STATIC libraries which does not depend on other digikam code. Also make sure you put the PRIVATE parameter when setting the target_link_libraries. \code target_link_libraries(digikamcore PRIVATE Qt5::Core Qt5::Gui Qt5::Widgets ) \endcode \subsubsection objlibs Object Libraries While static libraries are still collection of objects, CMake offer a better approach by allowing to specify an OBJECT library: \code set(libslideshow_SRCS slidetoolbar.cpp slideosd.cpp slideproperties.cpp slideimage.cpp slideerror.cpp slideend.cpp slideshow.cpp slidehelp.cpp slideshowsettings.cpp ) add_library(slideshow_src OBJECT ${libslideshow_SRCS}) \endcode OBJECT library is a cmake internal implementation feature and allow to easily manage sources. Here is an example of how to make a shared lib using OBJECT libraries: \code add_library(digikamcore SHARED $ # the lib we made few lines above $ $ .... ) \endcode \section codecontrib Contribute To The Code This section's purpose is to guide contributors and developers to help on the digiKam project. \subsection startopensource Starting With Open-Source Before to contribute to digiKam project, please take a look to this link which provide 10 golden rules for starting with open source project: http://schlitt.info/opensource/blog/0541_10_golden_rules_for_starting_with_open_source.html \subsection codeformat Source Code Formatting Adhere to this style guide strictly while adding new code to digiKam or working on existing code. \subsubsection indentlength Indentation length Indent with 4 spaces exactly. for eg: \code void function() { ....int a; // 4 spaces from beginning ....for (int i = 0 ; i < 10 ; ++i) // 4 spaces from beginning ....{ // 4 spaces from beginning ........a = i; // 4 spaces from previous indent block \endcode Emacs by default will indent to 4 spaces vim users add this to you .vimrc set tabstop=4 \subsubsection tabsspaces Tabs vs Spaces Absolutely no tabs. Use a sensible editor which will convert tabs to spaces. This will reduce unnecessary changes in your cvs commits. Emacs by default will convert tab to spaces. For vim users, add this to your .vimrc set expandtab \subsubsection linelength Line length Line length should never exceed 80 chars (unless really necessary - these cases are rare). Having long lines greatly reduces readability of code \subsubsection bracketing Bracketing In all cases, {} brackets should start on a newline and should be aligned with previous line (follow the indentation spaces). for eg. \code class A { //new line ... for (int i = 0 ; i < 10 ; ++i) { //new line if (a == foobar) { //new line ... } else { // new line .. } \endcode \subsubsection accessmod Positioning of Access modifiers public, private, protected, public slots, ... should be aligned to the beginning of the line with no margin \code class A { public: // aligned to left ... private Q_SLOTS: // aligned to left \endcode Follow a consistent order in defining these attributes. The recommended order is public, protected (functions), private (functions), signals, public slots, protected slots, private slots, private (variables) \subsection itemsnames Class, file and Variable names \subsubsection classfilenames Class and filenames - filenames should always be in lower-case - class names should match the filenames. Capitalize the first letter and other letters logically to improve readability \subsubsection protectvars Protected Member variables - protected member variable names should always be of the form m_varName. - Capitalize logically so that it becomes easy to read it. Do not capitalize the first letter after _ (Use m_varName not m_VarName) - variable names should be indicative of their functionality and also of the type they belong too if they are instances of qt widgets. for eg, QCheckBox* m_autoRotateCheckBox; \subsubsection nomembvars Non-Member variables - non-member variables should follow the same naming convention as the member variables, except for the leading m_ \subsubsection privmembvars Private Member variables - private member variables must be stored in a d private container to reduce compilation time and improve binary compatibility between digiKam components. See more information how to use a 'd' private class at this url: https://community.kde.org/Policies/Library_Code_Policy \subsection commentsspaces Comments and Whitespace Use whitespaces liberally to improve readability. Add blank lines between logical sections of the code. Comment as much as possible. Position comments at the beginning of the section/line you want to comment, NEVER at the end of the line, excepted for special cases for ex to describe enum values. \code // put your comments here a = (b == foobar) ? 1 : -1; a = (b == foobar) ? 1 : -1; // you are asking for trouble by putting comments here \endcode \subsection headerfiles Header Files - Add copyright to top of every file. Use the same header than others digiKam source code. - Double inclusion protection defines are all upper case letters and are composed of the classname and a H suffix separated by underscore \code #ifndef DIGIKAM_ANOTHER_NICE_CLASS_H #define DIGIKAM_ANOTHER_NICE_CLASS_H class AnotherNiceClass { ... } #endif // DIGIKAM_ANOTHER_NICE_CLASS_H \endcode - Use forward declarations as much as possible. \code class QFileInfo; class A { ....QFileInfo* m_fileInfo; \endcode \subsection autocodeformat Automatic source code formatting The above coding style guidelines can be automatically applied with astyle (http://astyle.sourceforge.net/). Run it in the directory where the files are located that should be formatted. To apply the coding guidelines with astyle is to use the fileformatter.py script in project/scripts directory. This script will also clean up the source tree and remove backup files that had been created by astyle, if the appropriate command line argument is given. To handle the command easier, create a bash function in ~/.bashrc, e.g. \code dkfrmcode() { astyle --style=allman \ --indent=spaces=4 \ --convert-tabs \ --indent-switches \ --break-blocks \ --break-closing-brackets \ --pad-header \ --align-pointer=type \ --indent-col1-comments \ --add-brackets \ --min-conditional-indent=0 \ `find $1 -type f -name '*.cpp'` `find $1 -type f -name '*.c'` `find $1 -type f -name '*.h'` } \endcode You can pass a parameter to the function, in this case the first parameter is the directory, where files should be formatted. Examples: 1. Run astyle in the current directory \code $> dkfrmcode \endcode 2. Run astyle in a different directory \code $> dkfrmcode /home/user/code/git/digikam/ \endcode \subsection generalrecommend General recommendations Please take a look into this contrib page tips before to write code/patches for digiKam project : http://techbase.kde.org/Contribute Use the same .cpp/.h header than the rest of digiKam project. Use a decent editor which does auto-indentation/syntax-highlighting for you, as Kate or QtCreator There are excellent initializer scripts in the kdesdk package for xemacs and vim which can substantially increase your productivity. Just to give a taste of what i can do with emacs (and kdesdk): * automatically insert copyright (and ifdefs) in new files. * insertion of class function definitions for declared class functions in header with one keystroke * switch between header and declaration files with one keystroke * go to corresponding definition/declaration with one keystroke * tab completion of variable/function names already declared. \subsection gdbbacktrace GDB Backtrace If you found a context to crash digiKam, you can provide a backtrace using GDB debugger. digiKam need to be compiled with all debug info else the backtrace will not suitable. There is a configure option for that: \code $> cmake . -DCMAKE_BUILD_TYPE=debugfull $> make $> su $> make install/fast \endcode To make a backtrace with GDB use following command: \code $ gdb digikam > catch throw > run > ... > _crash here_ > ... > bt > _the backtrace is here_ > quit \endcode Post this backtrace at the right place (Bugzilla or developement mailing list) for investigations by developers. For Windows users, take a look on this tutorial : http://techbase.kde.org/Development/Tutorials/Debugging/Debugging_on_MS_Windows \subsection memleak Memory Leak To check any memory leak problem in digiKam, valgrind is your friend (http://valgrind.org) Try this command line to use with valgrind : \code valgrind --tool=memcheck --leak-check=full --error-limit=no --suppressions=project/reports/digikam.supp digikam \endcode NOTE: digikam.supp file is available in digikam/project sub-folder. \subsection profilingcode Profiling With Cachegrind Valgrind also includes a tool to find out in which parts of your code time is spent. \code valgrind --tool=callgrind digikam \endcode Profiling can be disabled at startup to limit the output to the code you are interested in. Start with \code valgrind --tool=callgrind --instr-atstart=no digikam \endcode and prepare the situation you want to profile. Then, in another console, start profiling with "callgrind_control -i on" and, after the situation has passed, request a profile dump with "callgrind_control -d". The resulting callgrind.out files need to be viewed with the kcachegrind program, e.g.: \code kcachegrind callgrind.out.16693.1 \endcode \subsection unittests Unit Testing / Automated Testing Unit Testing is great way to ensure that software units (in OOP this normally means classes) work as expected. Wikipedia gives a good introduction to Unit Testing: http://en.wikipedia.org/wiki/Unit_testing It is also worth to follow most of QTTest API rules with digiKam: http://doc.qt.io/qt-5/qtest-tutorial.html The digiKam test suite is located under tests and will be compiled if BUILD_TESTING is turned ON at cmake configuration time. After compiling the source code the tests can be executed via \code make test \endcode The console output while running the tests is stored in Testing/Temporary/LastTest.log in the CMake binary dir. All tests are simple binaries that can be executed separately if needed. \subsection corruptedsignalslot Checking For Corrupt Qt Signal Slot Connection Use this alias for running digikam: \code alias digikamdbg="digikam 2>&1 | tee - /tmp/digikam.out; echo -e \"\n\n\nPossible connection errors:\n\n\"; cat /tmp/digikam.out | grep -A2 'Object::connect'" \endcode It will print a list of connection warnings after terminating the program. Moreover the complete console log of the last session is stored in /tmp/digikam.out. \subsection dupcodes Finding Duplicated Code Code duplication should be avoided as bugs have to be fixed for every piece of duplicated code. The current duplication can be analyzed eg. with Simian: http://www.redhillconsulting.com.au/products/simian/ In the digikam checkout directory run: \code java -jar simian.jar `find . -regex '.*\.cpp\|.*\.h' | grep -v 3rdparty` \endcode This prints out a list of duplicated code segments. \subsection docvalidcodecheck API Documentation Validation, User Documentation Validation, Source Code Checking The following site check on a daily basis for the a.m. errors: www.englishbreakfastnetwork.org/krazy/ It can be very useful, in particular before major releases. Don't trust it blindly! Sometimes they propose too advanced modifications that are no compatible with the prevailant include files. \subsection appusability Usability Issues OpenUsability project has define default menu structure and keyboard shortcuts: http://wiki.openusability.org/guidelines/index.php/Appendices:Keyboard_Shortcuts \subsection apidoc Generate API Documentation To generate API documentation, you need to install: - Doxygen program (http://www.doxygen.org) - Dot program (http://www.graphviz.org) After cmake generated a Makefile you can call 'make doc'. A new subfolder named 'html' will be created. Warning, this can take a while. For finding documentation errors, doxygen generates a warning log file at the cmake binary dir called 'doxygen-warn.log'. \subsection speedupcompil Speed Up The Code-Compile-Test Cycle Assuming you have setup your environment in ~/.bashrc as is suggested for development, you can add something like this to your ~/.bashrc: \code function digikam_start { LD_LIBRARY_PATH=${KDE_BUILD}/extragear/graphics/lib:${LD_LIBRARY_PATH} ${KDE_BUILD}/extragear/graphics/digikam/digikam/digikam } function digikam_start_gdb { LD_LIBRARY_PATH=${KDE_BUILD}/extragear/graphics/lib:${LD_LIBRARY_PATH} gdb ${KDE_BUILD}/extragear/graphics/digikam/digikam/digikam } \endcode This allows you to run digikam after compiling without the need of a "make install", even if you changed code in the libraries. \subsection gitbranches Working With Branches From Git Repository Example to create a local 'dplugins' development branch based on master: \code git checkout master git checkout -b development/dplugins \endcode Example to delete the local 'dplugins' developement branch: \code git checkout master git branch -d development/dplugins \endcode Example to create a remote 'dplugins' development branch from the local branch: \code git push -u origin development/dplugins \endcode Example to delete the remote 'dplugins' development branch: \code git push origin :development/dplugins \endcode \subsection syncgitbranches Sync a Branch With Master From Git Repository It typical to use a dedicated developement branch in Git to keep the master code stable for production. To synchronize branches with master, use these commands in your local branch checkout: \code $>git checkout master $>git pull --rebase $>git checkout -b MY_DEVEL_BRANCH GIT_REMOTE_PATH Branch 'MY_DEVEL_BRANCH' set up to track remote branch path 'GIT_REMOTE_PATH' from origin. To list GIT_REMOTE_PATH, use 'git branch -a' command Switched to a new branch 'MY_DEVEL_BRANCH' $>git merge master Merge made by the 'recursive' strategy. ... $>git push ... \endcode The first 2 lines make sure that your local master repository is up to date. The 3rd line creates the local development branch "MY_DEVEL_BRANCH". If you have already created this branch, just run "git checkout MY_DEVEL_BRANCH". Merging between master and "MY_DEVEL_BRANCH" branch is done in the 4th line. Git might ask you to resolve conflicts here. When it's done, it will ask you to provide a commit message. Finally you push your merge into the remote repository. */ diff --git a/NEWS b/NEWS index 3467055a2f..9a01a48659 100644 --- a/NEWS +++ b/NEWS @@ -1,756 +1,764 @@ digiKam 7.0.0-rc - Release date: 2020-05-31 ***************************************************************************************************** NEW FEATURES: FaceManagement: New Neural Network engine based on OpenCV Deep Learning module to detect and recognize faces. FaceManagement: Face Scan dialog contents is now simplified and embeded into left side-bar tab. SlideShow : Add new shuffle mode. HTMLGallery : Add new theme "Html5Responsive". -General : All bundles have switched to last Qt 5.14.2. Linux and MacOS use QtWebEngine instead QtWebKit. +General : Code compile with Qt5.15 framework. +General : All bundles have switched to last Qt 5.14.2 LTS. Linux and MacOS use QtWebEngine instead QtWebKit. General : All bundles have switched to last KF5 5.70.0. Fix support for Gimp XCF files >= 2.10. General : New FlatPak bundle processed by the KDE continuous integration stream. General : Add Microsoft Visual C++ support and a Continuous Integration workflow to check code with this compiler. General : Table-view is now able to show digiKam Tag-Paths properties. Metadata : New option to write geolocation data in file metadata. General : Internal libheif updated to last stable 1.6.2 and internal libde265 updated to last stable 1.0.5. General : Internal libraw updated to 0.20-beta1 (https://www.libraw.org/news/libraw-0-20-beta1). New camera supported: Canon CR3, PowerShot G5 X Mark II, G7 X Mark III, SX70 HS, EOS R, EOS RP, EOS 90D, EOS 250D, EOS M6 Mark II, EOS M50, EOS M200, EOS 1DX Mark III (lossless files only) DJI Mavic Air, Air2, Osmo Action, FujiFilm Fujifilm compressed/16bit, GFX 100, X-A7, X-Pro3, X100V, X-T4, X-T200, GoPro Fusion, HERO5, HERO6, HERO7, HERO8, Hasselblad L1D-20c, X1D II 50C, Leica D-LUX7, Q-P, Q2, V-LUX5, C-Lux / CAM-DC25, SL2, M10 Monochrom, Nikon D780, Z50, P950, Olympus TG-6, E-M5 Mark III, E-PL10, E-M1 Mark III, Panasonic Panasonic 14-bit, DC-FZ1000 II, DC-G90, DC-S1, DC-S1R, DC-S1H, DC-TZ95, PhaseOne IQ4 150MP, Rapsberry Pi RAW+JPEG format, Ricoh GR III, Sony A7R IV, A9 II, ILCE-6100, ILCE-6600, RX0 II, RX100 VII, Zenit M, also multiple smartphones. ***************************************************************************************************** BUGFIXES: 001 ==> 384401 - Various recognition algorithm improvements for face detection. 002 ==> 413701 - digiKam 6.3.0 does not build against current plasma. 003 ==> 413748 - Broken theme on Windows 10 after Digikam update. 004 ==> 413738 - Video Preview changes VLC volume. 005 ==> 382311 - Photos in collapsed groups are incorrectly excluded if first photo in group does not match filter. 006 ==> 396337 - In tag filter view (or any other filitered view), grouped photos should not always have "group" thumbnail 007 ==> 413704 - Filters do not work on hidden grouped images. 007 ==> 413233 - Application crashes when click in Albums left panel. 008 ==> 413759 - Tarball is missing translations. 009 ==> 413879 - Buttons without theme colors. 010 ==> 392015 - Show "Unknown" faces in a more visible and preeminent place in the "People" list. 011 ==> 413837 - Reverse geolocation deletes previous tags from the picture. 012 ==> 376629 - Face tagging dropdown: "Add in persons" adds person not label. 013 ==> 413923 - The peoples list shows just one line of height (one name) when expanded for the first time. 014 ==> 413924 - The face rectangle behavior when show face tags is OFF. 015 ==> 341111 - MYSQL : deleting an image involves "too many" queries. 016 ==> 413938 - Metadata is not written to all pictures in a tag hierarchy. 017 ==> 413916 - Compile Error with lqr on neon. 018 ==> 413972 - align_image_stack and enfuse use only 1 CPU core if called by digiKam plugin, 4 CPU core on command line. 019 ==> 413981 - Refresh has no effect in Preview mode. 020 ==> 413985 - Can't move an album too far down - no treeview scrolling. 021 ==> 412678 - Renaming folder leads to an error. 022 ==> 303239 - GROUP : grouped images are found, but do not show in searches. 023 ==> 321339 - GROUP : suppress display of albums containing only grouped images. 024 ==> 289911 - 23HQ should be split from flickr uploader as a new export tool. 025 ==> 414112 - Rounding problems in Resize Image function. 026 ==> 414052 - Packaging error of digikam-6.4.0.tar.xz, archive file contains a huge number of unnecessary files. 027 ==> 404667 - Bug of proofing for illuminant A and color cast. 028 ==> 414247 - digikam: error while loading shared libraries: libcudart.so.8.0. 029 ==> 392758 - Slideshow with random pictures [patch]. 030 ==> 414300 - Buttons on either side completely unresponsive. 031 ==> 414320 - Performance regression when scanning directories with many sub-directories. 032 ==> 414284 - Improve Setup/Slideshow View Options Layout. 033 ==> 414420 - Date/time in sidecar files for Videos seems to be ignored by digikam. 034 ==> 414340 - RawTherapee plugin. 035 ==> 414473 - Importing problem with M2Ts video files. 036 ==> 414521 - File management is broken. 037 ==> 205406 - Add Javascript to scaling images in html gallery [patch]. 038 ==> 334680 - Allow to limit exported files by type-mime in html gallery. 039 ==> 092462 - Add a new option to limit amount of thumbnails per index page. 040 ==> 136389 - Add new option to show image date and time in html gallery. 041 ==> 305167 - Add back filenames in html gallery. 042 ==> 114216 - Add a new option to export and merge more than one album in html gallery. 043 ==> 414516 - digikam crash when double-clicking on Google Maps when editing geolocation. 044 ==> 414603 - Pictures sort by freehand - drag & drop. 045 ==> 414630 - Shortcut bug in caption (tag). 046 ==> 414484 - Cannot use Rawtherapee or Darktable for raw image import on Windows. 047 ==> 166577 - List of available cameras in search. 048 ==> 414637 - New option for search. 049 ==> 414401 - RawTherapee does not work as Raw Import Tool if both are AppImages. 050 ==> 412067 - I get a error message: "Error while opening the database". 051 ==> 414902 - After using the "healing clone" tool only preview is available from other tools. 052 ==> 402894 - Display of original image. 053 ==> 380345 - Batch converting RAW to JPEG saves as new version. 054 ==> 383716 - "Save Changes" button same as "Save As New Version" Button. 055 ==> 307374 - Wrong image count in album view with versioning. 056 ==> 412961 - All image versions remain visible. 057 ==> 399923 - Segmentation fault during face detection. 058 ==> 392651 - Digikam crashes when Face scanning reaches 29%. 059 ==> 397919 - Segmentation fault during maintenance. 060 ==> 365354 - MYSQL : Application crash on scanning for faces in large picture set. 061 ==> 391014 - Crashes on close. 062 ==> 387821 - Face Recognition and Finding crashs after reaching 50%. 063 ==> 414749 - Export google photos: error transmission date creation. 064 ==> 377127 - Wrong item count on years in Dates View. 065 ==> 350350 - Moving images notifies me moving finished when it did not finish yet. 066 ==> 342191 - digiKam keeps crashing after crash with face-detection and manual tagging at the same time. 067 ==> 309769 - Crash on startup: 'Program received signal SIGILL, Illegal instruction.' (libopencv_nonfree). 068 ==> 286418 - digikam crashed during face recognition. 069 ==> 402470 - Crashed While I Walked Away During Face Detection. 070 ==> 415046 - Facial Recognition crash during scan. 071 ==> 360477 - Crash when adding a new face tag. 072 ==> 351638 - digiKam crash after facemarking. 073 ==> 350599 - Crash after maintenance. 074 ==> 350549 - Massive memory usage when assigning a tag to a recognized face. 075 ==> 312243 - digiKam crash when detected face moved or resized. 076 ==> 329108 - Crash when running Scanning faces/Clear and rebuild all training data. 077 ==> 280958 - Removing non-faces crashes digiKam. 078 ==> 312289 - Face Scan Crash. 079 ==> 285444 - Crash when adding face tag. 080 ==> 328413 - Crash while tagging faces - no background detection in process. 081 ==> 303328 - digiKam crashes repeatedly while tagging faces. 082 ==> 307554 - digiKam crash when scanning faces. 083 ==> 301506 - Crash while Tagging and Grouping Faces. 084 ==> 309306 - Crash during face-recognition. 085 ==> 284137 - digiKam crashed while moving a face tag after rotating a picture. 086 ==> 299173 - Crash during face tagging. 087 ==> 328560 - Crash when attempting to exit digiKam after aborted face recognition. 088 ==> 323828 - Crash when assigning a name to a single unknown face. 089 ==> 344735 - Crash if I name a face. 090 ==> 275688 - Crash during face detection, whilst tagging. 091 ==> 322022 - Faces and crash. 092 ==> 326750 - digiKam sometimes crashes when tagging recogniced faces with names. 093 ==> 314877 - Face Scan Crash. 094 ==> 271791 - digiKam crashes when removing a "face image" from the current list when the list is not yet loaded completely. 095 ==> 270410 - digiKam crashes when confirming faces. 096 ==> 283197 - Crash during tagging faces. 097 ==> 326689 - digiKam crashes while tagging faces. 098 ==> 284398 - Crash while tagging persons. 099 ==> 334158 - digiKam crash while tagging faces. 100 ==> 321851 - Face scan crash but only on multithread. 101 ==> 308393 - Crash while scanning faces. 102 ==> 326570 - digiKam crashes while recognizing/tagging faces. 103 ==> 343014 - Crashed when select another tag (face tag management). 104 ==> 327699 - Crashing while face detection. 105 ==> 308645 - digiKam crashed when I clicked scan faces while another scan was already running. 106 ==> 287961 - Crash while face-tagging pictures and doing import. 107 ==> 317863 - digiKam crashes as detected faces are being assigned to people or being removed while face detection is running. 108 ==> 302354 - Crash finding again faces (probably a duplicate of 262596). 109 ==> 320861 - Crash when adding tag to photo of the face scan result. 110 ==> 326794 - Tagging faces crashed digiKam. 111 ==> 274850 - digiKam crash on face scanning. 112 ==> 283165 - Crash happened while running face recognition. 113 ==> 280521 - digiKam crashes when deselecting two pictures in in quick succession from the face view. 114 ==> 303304 - Crash during face detection. 115 ==> 329596 - digiKam crashes during face recognition. 116 ==> 298599 - digiKam crashed while scaning for faces in the background. 117 ==> 304360 - Crash during face tagging. 118 ==> 280620 - digiKam crashes when tagging multiple non-faces. 119 ==> 302437 - Crash when re-searching for faces. 120 ==> 274727 - Crash on scan of faces. 121 ==> 333582 - Scanning for faces crashes. 122 ==> 290826 - digiKam crashed while tagging faces. 123 ==> 289003 - Crash when trying to face-scan all images. 124 ==> 280618 - digiKam crashes when tagging multiple faces. 125 ==> 329651 - Face detection crashes when reaches 3 GB of Memory, the computer has 6GB and 2 are still free. 126 ==> 294452 - digiKam crash in "Scan collection for faces". 127 ==> 268102 - Crash when finding faces. 128 ==> 323823 - Crash while first face detection after update. 129 ==> 336236 - Crash while tagging faces on more than one image at same time. 130 ==> 279266 - digiKam crashes while trying to tag faces. 131 ==> 312442 - digiKam crashed when editing face tags. 132 ==> 275827 - digiKam crash whilst scanning for faces, starting to enter tag. 133 ==> 330828 - Crash while detecting faces. 134 ==> 324711 - Crash when scaning faces. 135 ==> 323654 - digiKam crashed while i was tagging faces. 136 ==> 283540 - digiKam crashes on entering people tages (face recognition). 137 ==> 277099 - digiKam crash when adding a tag to a face. 138 ==> 296281 - digiKam crashed while manually adding a face. 139 ==> 325526 - Crash while tagging faces. 140 ==> 275637 - Performing multiple scans and recognises causes crash. 141 ==> 289228 - Crash when detecting faces. 142 ==> 317413 - digiKam crashed while tagging faces. 143 ==> 334580 - Face detection crashes digiKam. 144 ==> 301781 - digiKam crashed while scanning photos for faces. 145 ==> 321273 - Crash during face scan. 146 ==> 296784 - digiKam crashes while tagging faces. 147 ==> 285517 - digiKam crashed when tagging faces. 148 ==> 290818 - digiKam crash during face scanning. 149 ==> 271375 - digiKam crashed when unchecking face. 150 ==> 317290 - Crashes during face scan. 151 ==> 308575 - digiKam crashes while scanning for faces. 152 ==> 301832 - digiKam crashes a while after starting face detection scan. 153 ==> 300357 - digiKam crashes on Face Recognition scan. 154 ==> 309142 - Face Crash. 155 ==> 279781 - digiKam crashes while using face detection tool. 156 ==> 275387 - digiKam crashes on face recognition. 157 ==> 311934 - digiKam was crashing when I was face-tagging photos. 158 ==> 323428 - Crash while tagging a lot of faces at the same time. 159 ==> 262873 - digiKam crashes on scanning for faces. 160 ==> 280520 - Crash while running a face detection, and marking some of the faces. 161 ==> 322187 - Crash while 'detect and recognize faces'. 162 ==> 334337 - Crash when tagging faces in RAW picture files. 163 ==> 301856 - digiKam crashes during face tagging. 164 ==> 275541 - digiKam crashed while doing face recognition. 165 ==> 324093 - digiKam crashed when I clicked the check mark to assign an existing name to several selected face images. No background tasks were running. 166 ==> 286071 - Crash while adding face tags. 167 ==> 280901 - Crash when rejecting faces in different albums. 168 ==> 290891 - digiKam Crash while Face Recognition. 169 ==> 293418 - Crashes when scanning for faces. 170 ==> 273161 - digiKam: malloc(): smallbin double linked list corrupted. 171 ==> 268046 - Switch foto with face recognition. 172 ==> 277163 - digikam valgrind issues during face recognition testing. 173 ==> 302359 - Crash creating a tag while scanning faces. 174 ==> 307110 - digiKam crashes when manually adding face tags, then moving to next image. 175 ==> 339263 - digiKam crash. 176 ==> 379470 - Crash when performing face detection or recognition on large collection. 177 ==> 284154 - Crash upon exit. 178 ==> 392142 - Use Tensorflow for face and object recognition. 179 ==> 303501 - Double free or corruption during face tagging. 180 ==> 314646 - digikam crash when (tagging) scanning collection for faces. 181 ==> 317450 - Attempting to name a person while face tagging is in progress. 182 ==> 318640 - Face detection crash. 183 ==> 325385 - Face scan and using all processor cores. 184 ==> 325712 - Face tagging has massive memory leak. 185 ==> 326323 - Running Face Scan. 186 ==> 329164 - Changing face tags in an image causes digiKam to eat up all the virtual memory. 187 ==> 331912 - Face detection and recognition dos not work, tags and factags counts for one person are different, memory goes up and up without any result. 188 ==> 334509 - Person Detection. 189 ==> 337936 - Assigning a new person. 190 ==> 342144 - Face detection. 191 ==> 344661 - Face recognition makes digikam fill all the available memory. 192 ==> 345395 - Face Management the Memory and Swap growed to max. 193 ==> 365669 - Face Recognition improvement suggestion. 194 ==> 376901 - Face Recognition Algorithm Improvements. 195 ==> 338072 - digiKam face detection. 196 ==> 387870 - undefined reference to typeinfo for cv::face::FaceRecognizer. 197 ==> 327197 - When add one face tag in mainview/albums/one pictures view digikam and/or opencv fullfill the memory. 198 ==> 353859 - FaceEngine multi threaded ? 199 ==> 342004 - FacesEngine header and pkgconfig files not installed. 200 ==> 406838 - digiKam crashes while labeling unconfirmed faces. 201 ==> 409437 - Crash when doing any face detection. 202 ==> 404853 - digiKam faces engine fails to compile on PowerPC. 203 ==> 405625 - digiKam faces engine fails to compile on PowerPC with AltiVec enabled. 204 ==> 194401 - Face detection / recognition for digiKam for tags. 205 ==> 339823 - Detect and recognice faces crashes. 206 ==> 351077 - Crash during facial detection and recognition. 207 ==> 375317 - MYSQL: digikam crashes during face recognition. 208 ==> 299066 - digiKam crashed while tagging a large amount of people. 209 ==> 268761 - Segfault when tagging people without existing tag in preview mode. 210 ==> 374165 - Tag Change in Menu Person search crashes. 211 ==> 347753 - Surface freezes after some faces tagged. 212 ==> 375945 - Face detection not scanning my images. 213 ==> 343314 - Mass face tagging pictures when writing the tags into the files often crashes at the end of the writing. 214 ==> 345909 - digiKam crashed when face taging multiple photos. 215 ==> 401306 - digiKam doesn't compile with Opencv 4. 216 ==> 376766 - Face detection is using all CPU cores regardless of the checked option. 217 ==> 402320 - Memory hole (>1.5GB resident after 2 minutes) when detecting faces. 218 ==> 389031 - Scanning collection for faces causes produces lots of OpenCV errors. 219 ==> 325331 - MySQL : when adding a new face, tag is created with a new _Digikam_root_ tag in database. 220 ==> 262577 - Scanning collection for new faces (skip already scanned images) does not do anything. 221 ==> 330143 - "Detect and recognize faces" does no detection. 222 ==> 392527 - "Add A Face Tag" Dialogue Is Too Small. 223 ==> 372761 - Face Tag selection needs extra confirm now. 224 ==> 375418 - Glitch in face selection in album view. 225 ==> 281792 - When tagging with 'Return', too many faces are tagged. 226 ==> 282592 - Rejecting multiple faces at a time doesn't work properly. 227 ==> 376681 - Region Coordinates Are Sometimes inf / Large Numbers. 228 ==> 380251 - "Show only face tags..." not working properly. 229 ==> 326538 - When a picture is in portrait, face thumbnails are not rotated. 230 ==> 279208 - After the upgrade to Plasma and digiKam Faces Are Not Detected Anymore. 231 ==> 316856 - I suggest to extend face recognition to text-recognition system. 232 ==> 381378 - Face rectangle from XMP sidecar drawn incorrectly for EXIF rotated images. 233 ==> 326035 - Stepping through previews with "show face tags" enabled does not always show the tags. 234 ==> 326033 - Adding or changing face tag in preview window causes all tags on that preview to "hide". 235 ==> 265022 - UI's handling of People tags is confusing. 236 ==> 262180 - The toggle button to show/hide faces is difficult to see. 237 ==> 316161 - Reuse face tags from another picture. 238 ==> 412999 - Some photos downloaded in previous version of digikam no more recognized as already downloaded. 239 ==> 334215 - Cannot open RAW files from Sony A7R. 240 ==> 340595 - Convertion Sony A7 raw file turns to red the result. 241 ==> 307313 - digiKam uses wrong darkness/saturation values. 242 ==> 315156 - CR2 files from Canon PowerShot G1 X the colours are messed up. 243 ==> 332126 - LibRaw_r_LIBRARIES CMake Error: The following variables are used in this project, but they are set to NOTFOUND. 244 ==> 352996 - digiKam crashes while scanning for new files in Collection. 245 ==> 362779 - Can't decode PEF image from Pentax K-1 (36mp). 246 ==> 364063 - Crash on startup (related to crashing on conversion to DNG?). 247 ==> 221984 - Choosing manual WB does not allow user to tweak the previously selected WB setting. 248 ==> 182611 - Segfault with Samsung S85 RAW images. 249 ==> 178760 - Conversion of Canon .cr2 files broken as used in Darkroom. 250 ==> 364230 - Crash on startup. 251 ==> 244142 - digiKam crash. 252 ==> 362418 - Crashes on launch. 253 ==> 253091 - digiKam will not launch. 254 ==> 338075 - Tagging RAW images for Canon EOS-1Ds corrupts them. 255 ==> 205006 - On first run, digikam crashes while creating the index. 256 ==> 409148 - Sony A7r3 ARW files are displayed blurred in preview. 257 ==> 388339 - Crash when importing Pentax DNG file. 258 ==> 329230 - Crash when tuning a raw image. 259 ==> 253877 - digiKam Crash when loading. 260 ==> 134700 - CR2 thumbnails are not rotated. 261 ==> 090875 - Preview of RAW-Files in album. 262 ==> 143681 - Can't edit/view NEF anymore. 263 ==> 341024 - digiKam crash in a dir with only Canon Raw and JPEG files. 264 ==> 132695 - CR2 RAW files do not open. 265 ==> 339924 - RAW preview using embedded JPG. 266 ==> 142057 - Speed to view raw pictures. 267 ==> 187015 - Libraw breaks RW2 file handling. 268 ==> 126151 - Raw (NEF) thumbnail not correctly showed. 269 ==> 143244 - Error processing RAW files. 270 ==> 235321 - Tried to open X3F file. Was showing up in preview but crashed while opening in editor. 271 ==> 306843 - Raw-pics rotating. 272 ==> 149328 - Let RawEngine use raw decoding options when generating thumbnails. 273 ==> 150872 - Crash during during RAW conversion. 274 ==> 182013 - Sensitivity is not shown for rw2 files. 275 ==> 157619 - Let digiKam use preview images from raw files for more speed. 276 ==> 220322 - Crash when reading nef data: Warning: Exif tag Exif.NikonPreview.JPEGInterchangeFormatLength not encoded. 277 ==> 165176 - Previews of raw pics are not created when "create all previews new" is selected. 278 ==> 149086 - RAF-picture finepix not show in Imageeditor (thumbnails are displayed). 279 ==> 123950 - Crashing on raw files (.nef). 280 ==> 320049 - RawEngine is crashing on decoding some sigma raw files. 281 ==> 277707 - Easy way to convert Raw, to get same image as seen in preview. 282 ==> 146738 - Fuji *.RAF files aren't recognized as RAW files. 283 ==> 331397 - Preview and thumbnails colors are wrong for cr2. 284 ==> 210659 - Inconsistent Save / Save As handling in image editor after raw import. 285 ==> 155950 - Raw file open with action causes two instances. 286 ==> 337601 - digiKam crashed on closing after interrupted raw processing. 287 ==> 219748 - Image editor doesn't import raw images twice. 288 ==> 272725 - Some PEF/jpeg files not recognized as raw/non raw 289 ==> 154922 - Start to use libopenraw instead of libraw. 290 ==> 359949 - Bad canon raw file makes digikam to crash at startup. 291 ==> 101281 - EXIF info from RAW images not woking properly. 292 ==> 342233 - Crash when decoding pentax k-r raw photo. 293 ==> 139550 - Autocorrect levels for raw photos (which are shown too dark). 294 ==> 099437 - Incorrect Libraw option. 295 ==> 155156 - Raw file conversion crash single/batch mode. 296 ==> 146259 - Raw Converter won't convert to 16bit PNG. 297 ==> 151523 - RAW converter crashes on startup. 298 ==> 242479 - Crashes on appling refocus tool to raw image. 299 ==> 361678 - Dng Converter crashes when opening a raw file (*.NEF in my case). 300 ==> 382576 - DNG Image Converter crashed trying to convert a RAW image. 301 ==> 326268 - SONY SLT A58. 302 ==> 140087 - Sony Alpha Super SteadyShot Meta Info. 303 ==> 282116 - DNG Converter fails to convert Nikon NEF to DNG. 304 ==> 270457 - Crash when opening a NIKON-jpg with 1.3 MB. 305 ==> 135011 - Sort images by EXIF date seems buggy for Nikon (or not just Nikon?). 306 ==> 141249 - CANON EOS 5D not support. 307 ==> 240750 - DNG Converter produces black files (Canon 5D mkII) 308 ==> 361660 - DNG Image Converter crashes when converting Canon cr2 file. 309 ==> 211908 - TIF (RAW from Phase One and old Canon, not TIFF/EP) opens as thumbnail. 310 ==> 388222 - high RAM memory consumption of digiKam. 311 ==> 338249 - digiKam uses all free memory and gets terminated. 312 ==> 131277 - Memory leak in image editor. 313 ==> 252443 - digiKam leaves a zombie after quit. doesn't free memory. 314 ==> 321784 - Recreating fingerprints leaks memory. 315 ==> 381877 - digiKam start allocating all memory when scans a new collection. 316 ==> 098227 - Huge memory leak when downloading from camera. 317 ==> 330227 - Image quality sorter leaks memory. 318 ==> 412893 - Application windows are incorrectly magnified in macOS. 319 ==> 413656 - Manage Tags window appears with messed up double size graphics. 320 ==> 092783 - Usabilitiy of RAW file support. 321 ==> 158911 - digiKam crashes before RAW convert. 322 ==> 362870 - DNG Convert crash on convert NEF file. 323 ==> 367859 - DNG Converter crashed after trying to nconvert CR2 file. 324 ==> 369289 - DNG converter crashes when converting Olympus (.ORF) files. 325 ==> 407203 - Crash on trying to convert a .NEF file. 326 ==> 370623 - digiKam converter to DNG report an error while to process NEF image. 327 ==> 354364 - Crash of DNG converter. 328 ==> 338842 - Moving DNG images to another album corrupts DNG properties. 329 ==> 401849 - Cannot Save Current Search. 330 ==> 372972 - Find duplicates "search in drop down" only shows 1 item. 331 ==> 351521 - digiKam crashes when searching by date. 332 ==> 335978 - digiKam crashes when using timeline. 333 ==> 335052 - Crash on looking for duplicate. 334 ==> 333952 - Crashed when trying to search for duplicates. 335 ==> 218022 - Albums are sorted alphabetically within the month. 336 ==> 326495 - Calendar: higher flexibility in the layout. 337 ==> 281848 - Search for images with no goelocation. 338 ==> 283045 - Crash when searching with timeline. 339 ==> 281895 - digiKam crashed the selection date in the calendar. 340 ==> 280760 - Find duplicates should handle raw files more intelligently. 341 ==> 182029 - Duplicate item count not changed after duplicates deleted. 342 ==> 182043 - Duplicates search result in an inconsistent/broken state. 343 ==> 182492 - No photos are displayed when selecting a timeframe in the timeline. 344 ==> 140732 - Show all pictures of year when selecting year in date-tree. 345 ==> 240738 - Batch deletion of duplicate or similar files. 346 ==> 241536 - Reproducible crash when bilding fingerprint. 347 ==> 242438 - Crashes while creating fingerprints. 348 ==> 246500 - Fingerprint. 349 ==> 246635 - Crash when rebuilding fingerprints. 350 ==> 253382 - digiKam crashs creating fingerprints. 351 ==> 265670 - digiKam crashes when generating thumbnails and fingerprints in parallel. 352 ==> 265245 - Crash upon selecting certain month in timeline. 353 ==> 261418 - Improved handling of duplicate images. 354 ==> 263002 - Crash during browsing timeline. 355 ==> 147981 - Ability to move images to different albums from seach results. 356 ==> 149025 - Do not sort images in Search by album. 357 ==> 147407 - Root folders/years should display all child images. 358 ==> 155286 - Crash during duplicate search. 359 ==> 169404 - Color selector in fuzzy search is black. 360 ==> 214665 - Crash when cleaning duplicated images. 361 ==> 243136 - Fingerprints. 362 ==> 247550 - digiKam does not quit gracefully. 363 ==> 406228 - Getting random unextpected 'Database is locked' events. 364 ==> 409884 - digiKam Crash. 365 ==> 413944 - digiKam crashes while album browsing. 366 ==> 134817 - Introduce symlinks for album collection. 367 ==> 149983 - Show recursively sub-album images. 268 ==> 226770 - digiKam edit photo shift+space. 369 ==> 237161 - digiKam crashes on album viewing. 370 ==> 240436 - digiKam crashes when selecting a photo in an album. 371 ==> 291514 - Suggestions for improving face recognition performance. 372 ==> 271679 - digiKam detects but does not recognize faces. 373 ==> 292248 - Recognize faces does nothing. 374 ==> 321297 - Name not set on face recognition run. 375 ==> 314744 - Face do not recognize. 376 ==> 402021 - Face recognition not working. 377 ==> 392518 - Face recognition using the deep learning algo, dont move faces to Unconfirmed. 378 ==> 391671 - Face recognition fails giving a lot of addition effort to correct the errors. 379 ==> 392010 - Face recognition assigns faces to people without confirmation, leading to false positives. 380 ==> 277620 - Face recognition should propose the tagged name as default in the face identification page. 381 ==> 414308 - On a person, be able to view only the news found. 382 ==> 404167 - Improve confirmation process. 383 ==> 247571 - Crash while browsing in album view. 384 ==> 263209 - New Album crash. 385 ==> 275684 - Crash during tagging. 386 ==> 297044 - digiKam crashes when moving images to a new Album. 387 ==> 305108 - Crash during import of images. 388 ==> 317440 - digiKam crashes after deleting a tag. 389 ==> 335708 - Crash on image import. 390 ==> 337839 - I experience many crashes, without precise action. 391 ==> 339720 - Very slow tagging when missing Plasma icons have been used for tags. 392 ==> 343026 - Crashes when downloading. 393 ==> 361084 - Crash while importing. 394 ==> 117561 - Should store hashes of files if file is moved. 395 ==> 186920 - Too many open files. 396 ==> 135051 - digiKam crashes when building database from existing directory. 397 ==> 135689 - New folders not visible. 398 ==> 150181 - digiKam wants to delete the whole database (24.000Images) at startup when USB-Disk with the Photos is not connected. 399 ==> 194630 - digiKam fails to start ? after Plasma update. 400 ==> 202956 - Crashing on start up. 401 ==> 204071 - Just started digiKam and it crashed. 402 ==> 218726 - digiKam always crashes at startup with segmentation fault. 403 ==> 218860 - digiKam crashes when starting from krunner. 404 ==> 220172 - digiKam crash on scanning directories [mem2chunk_check, free_check, QHashData::free_helper]. 405 ==> 242305 - digiKam Crash. 406 ==> 242818 - digiKam crash at startup. 407 ==> 246065 - digiKam crashes at startup. 408 ==> 246534 - digiKam crashes at every start. 409 ==> 250418 - digiKam crashes on startup. 410 ==> 261624 - Moving an album from one collection to another doesn't update the source collection. 411 ==> 338171 - When automatic recognition guesses the same name for multiple faces in one picture, assigning a different name deletes all the face boxes except one. 412 ==> 392016 - Confirmed and unconfirmed faces look the same in a person's face list. 413 ==> 286452 - No way to scan untagged photos for faces, or scan ALL folders. 414 ==> 411732 - UI for assigning people tag is very fragile. 415 ==> 365668 - Face tagger allows text input when hovered over, then does crazy stuff. 416 ==> 413923 - The peoples list shows just one line of height (one name) when expanded for the first time. 417 ==> 326034 - Allow to add "unknown" face tags in Preview mode. 418 ==> 388649 - Face tag rectangle cursor sometimes disappear. 419 ==> 415560 - No default selection for Scan Collection. 420 ==> 415460 - JFIF files have APP0 marker after SOI where there should be APP1. 421 ==> 415582 - People Unknown faces does not decrease to zero. 422 ==> 415599 - Add date to help DNN face recognition for baby/kid/adult distinction. 423 ==> 415602 - How to erase faces rectangles for an album. 424 ==> 316897 - Face Detection improvements by colors filtering and using EXIF orientation. 425 ==> 088895 - Stops after a few percent with: Unknown event: 3. 426 ==> 091548 - Find duplicate images claims no album is selected although one is 427 ==> 101958 - Can not select duplicate images by clicking on a line instead of the select box of 6 pix square. 428 ==> 107095 - Double image removal: Use trashcan. 429 ==> 113557 - digiKam crashes during "Find Duplicate Images" with SIGSEGV. 430 ==> 117578 - Bad UI fpr searching duplicate images. 431 ==> 181698 - Cannot delete fuzzy searches. 432 ==> 181720 - Can't rename fuzzy searches. 433 ==> 183008 - Fuzzy image search requires two clicks to navigate back. 434 ==> 199045 - Find duplicates in a directory/directories. 435 ==> 199732 - Deleting duplicated images crashes digiKam. 436 ==> 222273 - Run fingerprint scan on new images. 437 ==> 231047 - Crash when updating fingerprints on a large photo collection (100GB). 438 ==> 235763 - Reproducible crash, when attempting to rebuild all fingerprints. 439 ==> 167168 - Timeline view shows redundant extra sections. 440 ==> 168004 - Timeline gives zero height column for dates with a single entry. 441 ==> 279674 - Incorporate "event view". 442 ==> 261216 - Debugging doesn´t work with installed. 443 ==> 109022 - Iphoto like calendar search. 444 ==> 109703 - Updating of calendar view in Date panel. 445 ==> 109705 - When quit date panel with day selected no images in other panels. 446 ==> 130230 - Wrong date in header using date-view. 447 ==> 213619 - Add a new view of albums based on due date. 448 ==> 375306 - Add 'Go to album' in search results. 449 ==> 128101 - Crash when selecting "Comments&Tags" tab, after selecting "Search" tab, with no search results selected. 450 ==> 146091 - Displaying raw photos by Tag or Date takes very long to load. 451 ==> 091372 - Make searching for multiple tags possible. 452 ==> 098846 - Searching photos with no tag. 453 ==> 113806 - Is it possible to have the size of the quick search window saved? 454 ==> 114848 - Search dialog: images not rotated correctly. 455 ==> 115536 - Quick search dialog box: wrong attached tips. 456 ==> 133294 - Advanced search dialog - combo box with tag names (when tag is to be matched) doesn't stretch with the dialog. 457 ==> 149555 - Always present search box instead of search by right-clicking and selecting simple or advanced search. 458 ==> 185106 - Advanced search is not saved/restored correctly. 459 ==> 120922 - Clicking on Advanced search crashes application. 460 ==> 141035 - Advanced Search gives error when trying to search by rating. 461 ==> 147429 - "and not" option in search function. 462 ==> 415489 - Win32 7.0.0 Beta 1 Crash On Opening Preview. 463 ==> 407540 - Missing EXIF parameter in DNG when converting Panasonic RW2 images. 464 ==> 399159 - Lenses name lost on converting to DNG. 465 ==> 118396 - Ability to search through image comments. 466 ==> 139283 - IPTC Caption comment in search function 467 ==> 155735 - Make it possible to seach on IPTC-text. 468 ==> 095584 - No indication of the end of download from camera. 469 ==> 179712 - Open image in embeded editor lead to digikam crash. 470 ==> 297293 - Option to use libnotify for non-blocking errors/message. 471 ==> 316928 - Servicing: statut of each servicing option should be searchable. 472 ==> 375521 - Progress bars waste useful space. 473 ==> 218583 - Showfoto customizing toolbar freerotation. 474 ==> 228879 - Geolocation Editor: Missing progress bar. 475 ==> 272158 - More user feedback about background operations. 476 ==> 301064 - Async task UI persists on leaving geolocation UI. 477 ==> 164600 - No picture in view pane. 478 ==> 297295 - Zooming: full picture thumbnail to show position. 479 ==> 160894 - Add new picLens like 3D view mode. 480 ==> 325530 - Preview and Editor return "Failed to Load Image" error when viewing JPEG (only) files. 481 ==> 256309 - Wish to save settings in batch queue manager. 482 ==> 271198 - Make queues saveable and use them as single action. 483 ==> 287407 - Save tools with settings in BQM. 484 ==> 318771 - Saved process don't remember settings in BQM. 485 ==> 320358 - Files randomly fail to process. 486 ==> 342433 - Crashes after tagging a few batches of photos. 487 ==> 171073 - Statistic of database, all pictures? 488 ==> 119228 - Storage of images on removable media. 489 ==> 144724 - Freezing by using NAS. 490 ==> 189362 - After moving the collection on a new HD, Digikam doesn't find it. 491 ==> 249375 - Crash when device with open window is removed. 492 ==> 272918 - digiKam crash after scanning ssh network drive. 493 ==> 275559 - digiKam crashes while browsing over NFS. 494 ==> 415679 - Faces are not recognized. 495 ==> 220903 - Duplicate records in ImageComments table after migration. 496 ==> 267800 - Keywords from IPTC-Metadata are not imported. 497 ==> 280678 - MIGRATION : cannot migrate from sqlite database. 498 ==> 301075 - MySQL : external database empty after migration. 499 ==> 328729 - digiKam crash while switching MySQL to SQLite 500 ==> 329849 - MIGRATION : db conversion fails. 501 ==> 361809 - Migration failed from SQLite to MySQL. 502 ==> 415700 - "Welcome to" screen still mentions v5.0. 503 ==> 415566 - Use existing face rectangles to improve recognition. 504 ==> 415603 - Add "Face Recognition" optional item in toolbar. 505 ==> 415592 - When renaming a tag, if I say no to updating, clicking Save won't trigger update again. 506 ==> 415766 - HEIF thumbnails are not shown in Timeline view. 507 ==> 396734 - Error while executing DBAction [ "UpdateSchemaFromV7ToV9" ] Statement [ "DROP TRIGGER IF EXISTS delete_image;" ]. 508 ==> 268204 - MYSQL : file-names are case-INsensitive. 509 ==> 383927 - OpenSuse digikam.coredb: Core database: schema update to V 8 failed! 510 ==> 319420 - Failed to update the database schema from version 5 to version 6. 511 ==> 288599 - Schema update to V6 failed. 512 ==> 218571 - Schema update version 4 to 5 failed. 513 ==> 230606 - digiKam doesn’t display images imported from database. 514 ==> 288839 - Failed to update the database schema from version 5 to version 6. 515 ==> 392179 - Database Upgrade to v9 fails. 516 ==> 396765 - Schema update to V 9 failed. 517 ==> 190411 - Metadata are invisible on older pictures. 518 ==> 110066 - Md5 Checksums to identify pictures. 519 ==> 415767 - Thumbnails for Portrait oriented images aren't rotated properly. 520 ==> 415791 - TimeAdjust can not adjust time by 1 unit - only by 2. 521 ==> 415557 - Face detection - Advanced settings : restricted folder scope not taken into account. 522 ==> 415702 - Cannot abort or stop find Duplicate process. 523 ==> 415796 - Face thumbnail zoom level too wide. 524 ==> 366551 - Unclear icon-item overlay when associating people to faces. 525 ==> 415535 - Import from a local/remote folder. 526 ==> 415643 - digiKam crashes Face detection. 527 ==> 415685 - digiKam crashes when scanning faces. 528 ==> 415877 - "Reset" button in Tag Manager not always working to reset icon. 529 ==> 415561 - Face detection based on deep learning leads to no result under Windows. 530 ==> 415920 - The unconfirmed faces count in the left sidebar is missing. 531 ==> 415944 - 7.0.0beta2 removed all my Geolocation bookmarks. 532 ==> 414016 - Log file /var/log/syslog grows in size very fast. 533 ==> 414028 - digiKam spamming journal with details of almost everything it does... 534 ==> 415882 - digiKam 6.4 crashes on processing a large number of new HEIC/HEIF files. 535 ==> 414115 - G'mic plugin not available on Mac. 536 ==> 416018 - Face detection: specified list of albums not taken into account. 537 ==> 416028 - "Search in Tags" selection for Face detection/recognition unduly changes. 538 ==> 416120 - Error transfering. 539 ==> 392304 - digiKam crash on exit (accessing stale QScreen instance during global destruction). 540 ==> 397694 - Missing text on selected tabs from QTabWidget. 541 ==> 380969 - Copy and paste text to comment field. 542 ==> 402914 - Crash when selecting custom icon for tag. 543 ==> 406507 - digiKam crashes when trying to select custom icon in the tag manager a second time. 544 ==> 401912 - Crash on adding GPS coordinates to multiple images. 545 ==> 412679 - Scrolling to a illogical place in the main view. 546 ==> 397761 - Tags are not written. 547 ==> 416231 - Exif date not written to sidecar file when modified, nor is it updated in the metadata sidebar. 548 ==> 402620 - AppImage digiKam does not respect desktop default application setting. 549 ==> 385953 - Reduce installed footprint under Windows. 550 ==> 416289 - 7.0.0-beta2 crash when closing Geolocation Editor. 551 ==> 413855 - digiKam crashes when deleting a letter in the login window (QtWebkit + libicu). 552 ==> 411619 - Crash when tagging, switching to map view (QtWebKit + libicu). 553 ==> 416345 - Proper OpenCV minimum version must be specified. 554 ==> 416289 - 7.0.0-beta2 crash when closing Geolocation Editor. 555 ==> 406809 - digikam-6.2.0-git-20190421T121202-qtwebengine-x86-64.appimage crashes on startup. 556 ==> 411891 - digiKam crashes if compiled with QWebEngine. 557 ==> 409906 - Crash at Startup in geolocation about QtWebEngine. 558 ==> 416371 - 7.0.0-beta bug in Album tree view sorting order. 559 ==> 404901 - Google auth dialog => firefox google auth window not closed. 560 ==> 372340 - Tagged face areas on portait (vertical) oriented images are mispositioned. 561 ==> 415941 - Face identification in wrong place. 562 ==> 415550 - Faces setup by Picasa are not displayed correctly on rotated/portrait pictures. 563 ==> 413926 - Incompatibility between software, for face regions on auto-rotated portrait position photos. 564 ==> 406971 - Face frames are misaligned on Digikam when face framing was done on Picasa. 565 ==> 395243 - Incorrect face regions on vertical images. 566 ==> 378456 - When previewing faces the thumbnail preview is sometime sideways or not aligned to the face in question. 567 ==> 377628 - CR2 file face detection misplaced in portrait. 568 ==> 412920 - Image Editor displays incorrect coordinates for selections on scaled HiDPI screens. 569 ==> 407914 - Crash when tagging, finding duplicates, moving between albums. 570 ==> 392607 - Crash sometimes when applying Tags to images. 571 ==> 406612 - Pictures are not placed on map when the've got GPS metadata. 572 ==> 406611 - There is no GPS icon over the thumbnail. 573 ==> 406941 - Geo-filter on the right pane does not work. 574 ==> 413081 - German user interface? 575 ==> 398869 - Tabs text gone when digiKam window active. 576 ==> 204479 - Unable to use digiKam on Windows. 577 ==> 414176 - Recognition of people. 578 ==> 415521 - digiKam is crashing very oftenly while loading preview of an image on Windows 10. 579 ==> 412453 - Crash after adding a new album-network drive. 580 ==> 412950 - While processing new collection, any (mouse) action will result in crash of digiKam. 581 ==> 398674 - digiKam duplicates an album when the folder name has been renamed to lowercase or uppercase. 582 ==> 392109 - Renamed tags are not written to metadata. 583 ==> 391839 - Grouped pictures no longer appear in albums, tags, searches or timeline. 584 ==> 391544 - Metadata not written to image after renaming Tag. 585 ==> 326870 - Pop up window for file attributes with bad colors. 586 ==> 376640 - Changing tag name does not update sidecar metadata. 587 ==> 240224 - Foreground and background of the Settings menu item have the same color when the Dark Theme is selected. 588 ==> 290072 - XMP sidecars available for all file types user wants to. 589 ==> 403349 - digiKam window offset on high resolution display. 590 ==> 415353 - FEATURE REQUEST: hotkey-mode to assign photos to album categories, quickly. 591 ==> 414212 - Slow response after tagging. 592 ==> 386098 - Crash when adding pictures to a running digiKam. 593 ==> 416526 - Exporting no longer works at all on the 20/01 version. 594 ==> 416551 - Create an utility to allow to create external soft links for tagged files from database. 595 ==> 372230 - Searches tool : "Current Searches" virtual album appears more than one. 596 ==> 416492 - Panorama tool fails to make panoramas. 597 ==> 416756 - When searching for duplicates, only one photo is shown if the other one is in a group (and not the first of the group). 598 ==> 416759 - In duplicate tab, it should be possible to have reference image always at first/last. 599 ==> 416802 - Add confirmation dialog for "Delete all" action. 600 ==> 416755 - Tag with "." not processed correctly, likely to result in data loss. 601 ==> 416984 - Build failure with opencv4-4.2.0. 602 ==> 406066 - Cannot parse date string YYYY-MM-DDTHH:MM:SS.ssss and YYYY:MM:DD HH:MM:SS in EXIF and XMP. 603 ==> 417008 - Flickr export ERR_CERT_AUTHORITY_INVALID. 604 ==> 417100 - kbuildsycoca5 cannot be started under MacOS 10.11.6 when digiKam PKG is installed. 605 ==> 417136 - Export pictures by creating symlinks (e.g. for a best-of-selection on filesystem). 606 ==> 417178 - Export pictures by creating "relative" symlinks. 607 ==> 417221 - "Tags Filter" not honouring "AND" modifier. 608 ==> 417255 - People > Search In > Albums/Tags requires scrollbar. 609 ==> 417257 - When navigating various TreeViews with keyboard, selected item is not always in focus. 610 ==> 412539 - digiKam crash when starting. 611 ==> 417317 - digikam git master crash. 612 ==> 417322 - Unable to move album because tree view does not scroll. 613 ==> 417333 - Advanced slideshow is blocking the desktop. 614 ==> 417633 - The Google Photos transfer no longer works. 615 ==> 409324 - digiKam freezes when exiting image editor from edit on image from filtered view. 616 ==> 417696 - Using the button "Determine difference from clock photo" without any result. 617 ==> 386141 - Export to Google Photos not responding under Windows with QtWebKit. 618 ==> 417786 - Renaming files in batch processor fails. 619 ==> 318516 - SCAN : Improve digiKam loading time [patch]. 620 ==> 417708 - Video export => geolocation is not exported. 621 ==> 397819 - digiKam not showing date taken. 622 ==> 327466 - digiKam crashes after launching alternate image processing software. 623 ==> 332903 - Altering Geo-Data. 624 ==> 417887 - Need Windows admin rights in order to see selected file list in Geolocation editor. 625 ==> 417797 - Batch video timestamp changing fails. 626 ==> 418056 - Not possible to filter for "no labels". 627 ==> 418022 - Difficulty in organizing tags if tags not visible in tag manager window. 628 ==> 418061 - Position of faces copied when rotating an image. 629 ==> 417946 - Tool not implemented. 630 ==> 418199 - Would it be possible to display file sizes in Megabytes? 631 ==> 418200 - 'Use native dialogs from system' option seems to do nothing on Win 10. 632 ==> 418342 - Tag shortcuts does not support single key shortcut. 633 ==> 418213 - When I add a Person to tagged Faces and delete it, it already exist. No recreation possible. 634 ==> 418138 - Export to local storage not working on Windows machine. 635 ==> 418057 - Icons missing in xubuntu. 636 ==> 418446 - Move Filters menu from right to left side of main window. 637 ==> 418594 - Should warn the user immediately when database not writable. 638 ==> 418536 - Editing date used as picture-taking date. 639 ==> 229010 - No support to resync GPS data from digikams database into into jpg files. 640 ==> 418658 - GPS data not exported on metadata export to sidecar files. 641 ==> 381347 - Non expected geolocation error message. 642 ==> 418760 - Suggested people tag does not appear on Unconfirmed thumbnails until you click on the thumbnail. 643 ==> 418554 - In face tagging, when manually entering a name, all other suggestions will default to that name. 644 ==> 418786 - Metadata Editors Missing. 645 ==> 418641 - Saves Mysql password in plain text. 646 ==> 406586 - Item count in "People" is not updated until you click or hover a tag. 647 ==> 414055 - Port Slideshow tool to plugin architecture [patch]. 648 ==> 419119 - Download limited to 100 photos. 649 ==> 419171 - Tagging multiple images at once broken with commit "modified CoreDB to perform recordChangeSet operation on tags". 650 ==> 419409 - Exif dates shown are wrong in some images. 651 ==> 419410 - Fuji S3Pro Image Preview Rotation is wrong. 652 ==> 419294 - Open with does not work with collection on network shares (on Windows). 653 ==> 418120 - Uninstaller recursively deleted C:/ drive. 654 ==> 419510 - Digikam crashed on exit after importing collection. 655 ==> 380838 - Metadata only written to DNG when "write metadata to Raw" selected. 656 ==> 402938 - Rename album an within album property fails when a subalbum exists. 657 ==> 417737 - File operations locked under Windows. 658 ==> 419667 - Preview in file opener. 659 ==> 419744 - 7.0.0-beta3 Geolocation Editor does not save new location. 660 ==> 419793 - Presentation shuffle not working. 661 ==> 419904 - Editor plugin icons same size or plugin list. 662 ==> 405995 - Large blank spaces between icons in "Tools" panel. 663 ==> 419771 - A possible configuration for automatically scanning for new added pictures? 664 ==> 420023 - RAW-editor integration with RawTherapee. 665 ==> 322117 - Open directory in File Manager and Open directory In Terminal not working under Windows and OSX. 666 ==> 279372 - Show combined file sizes when selection is done into icon-view. 667 ==> 420252 - Thumbnails of MPO images appear corrupted. 668 ==> 420254 - Preview of MPO stereo files displays a black image. 669 ==> 419927 - Filter by favorites. 670 ==> 415771 - Segmentation fault (11) crash when loading corrupt file with ImageMagick codec. 671 ==> 420398 - Install icon elongated. 672 ==> 420430 - Inconsistent behavior of View tab of Metadata dialog in digiKam Configure menu. 673 ==> 272730 - Sidebar does not adjust scroll area size automatically. 674 ==> 420400 - Emptying Trash does not provide any feedback that it is active/running. 675 ==> 420634 - Face thumbnails not zoomed in and have wrong orientation on network shares. 676 ==> 309598 - "Recent Tags" menu show existing tags added and shouldn't be. 677 ==> 420724 - A space at the end of the name of folder causes problem in Windows Explorer. 678 ==> 418688 - Face tagging in Thumbnails View? 679 ==> 405980 - The area of a "face rectangle" in the picture is different than the face thumbnail. 680 ==> 402940 - No thumbnails with windows client using network share. 681 ==> 420727 - digiKam-Window and all Sub-Windows are enlarged too much. 682 ==> 420642 - Crash on face detection stop button. 683 ==> 420773 - New pictures copied with shell in albums are not picked up from digikam. 684 ==> 420661 - Wishlist: option to clear/delete specific date field in a picture selection. 685 ==> 420865 - "Labels" tab in left menu shows unexpected behavior. 686 ==> 328291 - BQM Metadata Tool : be able to customize tags or namespaces to drop. 687 ==> 419852 - Seg fault (double free or corruption) while detecting faces, 0-length JPG files. 688 ==> 420875 - Assigning Tag/Label makes thumbnail disappear. 689 ==> 420564 - Google SSO auth broken. 690 ==> 420986 - External picture rating can not be read. 691 ==> 420978 - Error on new install Failed to update the database schema from version 9 to version 10. 692 ==> 421188 - Buttons in left and right sidebars are not visible except the active one. 693 ==> 309465 - Failed to load image for XCF files. 694 ==> 186641 - digiKam: doesn't open XCF files with GIMP 695 ==> 224697 - Crash with gimp xcf files with metadata. 696 ==> 250003 - XCF support is needed. 697 ==> 339152 - Crash when adding XCF file to Digikam collection. 698 ==> 368444 - Not supporting XCF anymore. 699 ==> 412339 - Bug report "Gimp 2.10 XCF serious issue". 700 ==> 406461 - Can't update from V9 to V10. 701 ==> 421361 - Removing a keyboard shortcut crashes Digikam. 702 ==> 421387 - Camera Leica V-Lux (Typ 114) not supported. 703 ==> 421023 - Face tagging quirky in digiKam 7.0.0-beta3. 704 ==> 421560 - Undo not working in ImageEditor RC. 705 ==> 421559 - HEIC: Failed to open the data source: Too many open files (errno = 24). 706 ==> 421464 - XPkeywords not being updated. 707 ==> 421521 - Wrong EXIF photograph properties shown when there is no EXIF data at all. 708 ==> 421646 - Impossible to add new tag. 709 ==> 418806 - digiKam:colorlabel in xmp sidecar files not compatible with external programs. 710 ==> 421693 - digiKam only read xmp:Label during first import then prioritizes its digiKam:ColorLabel over the xmp:Label. 711 ==> 421712 - Sidebar text turns white or disappear. 712 ==> 421791 - Single tag present in Xmp.dc.subject in XMP sidecar when multiple tags present in Xmp.lr.hierachicalSubject and database. 713 ==> 419472 - Flatpak in distribute.kde.org is outdated. 714 ==> 404642 - digiKam flatpak: Please include mysql driver (for using an external mysql db). 715 ==> 421801 - Feature Request - Please Add "Open With Default Application" to "Thumbnail Click Action" in Conf > Views > Icons section. 716 ==> 388422 - DBVersion setting does not exist. 717 ==> 421808 - Ignored directory does not clean up database. 718 ==> 421853 - Single button to refresh collection. -719 ==> +719 ==> 421817 - digiKam fails to compile with Qt 5.15. +720 ==> 421941 - Slideshow option does not respect sort order across collections. +721 ==> 421943 - Wishlist: Search for items without a tag in Advanced search. +722 ==> 420819 - Error while opening database digikam will try to automatically reconnect to the database. +723 ==> 421210 - digiKam 5.9.0-1+b1 refuses to start since LMDE4 Debbie upgrade. +724 ==> 421945 - Existing face tags not rotated with image. +725 ==> 422015 - Canon THM files (and likely others) are ignored by Sidecar management unless named very specifically. +726 ==> diff --git a/README.md b/README.md index c5fe0a5fbd..4735fc32c8 100644 --- a/README.md +++ b/README.md @@ -1,146 +1,140 @@ ![](https://c1.staticflickr.com/1/822/26082076957_5ff0796a99_o.png) digiKam - Professional Photo Management with the Power of Open Source | CI Job | Status | |---------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | Linux Build | [![Linux Build Status](https://build.kde.org/job/Extragear/job/digikam/job/kf5-qt5%20SUSEQt5.14/badge/icon)](https://build.kde.org/job/Extragear/job/digikam/job/kf5-qt5%20SUSEQt5.14/) | | FreeBSD Build | [![FreeBSD Build Status](https://build.kde.org/job/Extragear/job/digikam/job/kf5-qt5%20FreeBSDQt5.14/badge/icon)](https://build.kde.org/job/Extragear/job/digikam/job/kf5-qt5%20FreeBSDQt5.14/) | | Windows Build | [![Windows build Status](https://build.kde.org/job/Extragear/job/digikam/job/kf5-qt5%20WindowsMSVCQt5.14/badge/icon)](https://build.kde.org/job/Extragear/job/digikam/job/kf5-qt5%20WindowsMSVCQt5.14/) | | Flatpak Build | [![FlatPak Build Status](https://binary-factory.kde.org/view/Flatpak/job/Digikam_flatpak/badge/icon)](https://binary-factory.kde.org/view/Flatpak/job/Digikam_flatpak/) | | Coverity Scan | [![Coverity Scan Status](https://scan.coverity.com/projects/285/badge.svg)](https://scan.coverity.com/projects/digikam) | If you are reading this on Github, be aware that this is just a mirror. Our real code repository [is located here](https://invent.kde.org/graphics/digikam) Developers, if you want to contribute, see the online [API documentation here](https://www.digikam.org/api) # About digiKam is an advanced open-source digital photo management application that runs on Linux, Windows, and MacOS. The application provides a comprehensive set of tools for importing, managing, editing, and sharing photos and RAW files. [![](https://c1.staticflickr.com/5/4216/35354951072_a034561b5e_c.jpg "Albums View and Image Editor")](https://www.flickr.com/photos/digikam/35354951072/) You can use digiKam's import capabilities to easily transfer photos, RAW files, and videos directly from your camera and external storage devices (SD cards, USB disks, etc.). The application allows you to configure import settings and rules that process and organize imported items on-the-fly. [![](https://c1.staticflickr.com/1/703/32558229094_3d7ec01d3a_c.jpg "Map View displaying rated items and Batch Queue Manager in action")](https://www.flickr.com/photos/digikam/32558229094) digiKam organizes photos, RAW files, and videos into albums. But the application also features powerful tagging tools that allow you to assign tags, ratings, and labels to photos and raw files. You can then use filtering functionality to quickly find items that match specific criteria. [![](https://c2.staticflickr.com/4/3726/32557269024_ae870b0466_c.jpg "Search items by date rage and Geolocation editor")](https://www.flickr.com/photos/digikam/32557269024) In addition to filtering functionality, digiKam features powerful searching capabilities that let you search the photo library by a wide range of criteria. You can search photos by tags, labels, rating, data, location, and even specific EXIF, IPTC, or XMP metadata. [![](https://c1.staticflickr.com/1/306/32217007615_db6f9d116a_c.jpg "Search by Tags with preview mode and Metadata Editor in action")](https://www.flickr.com/photos/digikam/32217007615) You can also combine several criteria for more advanced searches. digiKam rely on Exiv2 library to handle metadata tag contents from files to populate the photo library. [![](https://c1.staticflickr.com/5/4795/40743725771_0b69dca743_c.jpg "Advanced search tool and video file result played as preview")](https://www.flickr.com/photos/digikam/40743725771) digiKam can handle RAW files, and the application uses the excellent LibRaw library for decoding raw files. The library is actively maintained and regularly updated to include support for the latest camera models. [![](https://c1.staticflickr.com/1/300/31407487553_a14abd0418_c.jpg "Find by items similarity and Light Table in cation to compare side by side")](https://www.flickr.com/photos/digikam/31407487553) The application provides a comprehensive set of editing tools. This includes basic tools for adjusting colors, cropping, and sharpening as well as advanced tools for, curves adjustment, panorama stitching, and much more. A special tool based on Lensfun library permit to apply lens corrections automatically on images. [![](https://c1.staticflickr.com/5/4649/40430534662_097b46a270_c.jpg "Panorama tool stiching photo")](https://www.flickr.com/photos/digikam/40474079731) Extended functionality in digiKam is implemented via a set of tools, dedicated especially to import and export contents to remote web-services. digiKam is based in part on the work of the Independent JPEG Group. # Authors See [AUTHORS](AUTHORS) file for details. # Related URLs http://www.digikam.org # Contact -If you have questions, comments, suggestions to make do email at : +If you have questions, comments, and suggestions, write an email to: digikam-users@kde.org -If you want contribute to digiKam developments do email at : +If you want contribute to digiKam developments write an email to: digikam-devel@kde.org IRC channel from irc.freenode.org server: [#digikam](http://webchat.freenode.net/?channels=digikam) # Bug reports IMPORTANT : the bug reports and wishlist entries are hosted by the Bugzilla system which can be reached from the standard Help menu of digiKam. A mail will automatically be sent to the digiKam development mailing list. There is no need to contact directly the digiKam mailing list for a bug report or a devel wish. -The current bugs and devel wishes reported to the bugzilla servers can be seen at these urls : +The current bugs and devel wishes reported to the bugzilla servers can be seen at this url: * [digiKam](http://bugs.kde.org/buglist.cgi?product=digikam&bug_status=UNCONFIRMED&bug_status=NEW&bug_status=ASSIGNED&bug_status=REOPENED) Extra Bugzilla servers for shared libs used by digiKam : * [LibRaw library](https://github.com/LibRaw/LibRaw/issues) * [Lensfun library](https://github.com/lensfun/lensfun/issues) * [GPhoto2 library](http://gphoto.org/bugs) * [Exiv2 library](https://github.com/Exiv2/exiv2/issues) * [QtAV library](https://github.com/wang-bin/QtAV/issues) # External Dependencies See the [online Dependencies list](https://www.digikam.org/api/index.html#externaldeps) for details. # Cmake Configuration See the [online Configuration rules](https://www.digikam.org/api/index.html#cmakeoptions) for details. # Installation In order to compile, just use something like that: export VERBOSE=1 export QTDIR=/usr/lib/qt5 export PATH=$QTDIR/bin:$PATH cmake . - make + make -j4 sudo make install Usual CMake options : -* `-DCMAKE_INSTALL_PREFIX` : decide where the program will be install on your computer. -* `-DCMAKE_BUILD_TYPE` : decide which type of build you want. You can chose between: - * `debugfull`. : for hacking. Include all debug information. - * `debug`. - * `profile`. - * `relwithdebinfo` : default. use gcc `-O2` `-g` options. - * `release` : generate stripped and optimized bin files. For packaging. - -Compared to old autoconf options: - -* `cmake . -DCMAKE_BUILD_TYPE=debugfull` is equivalent to `./configure --enable-debug=full` -* `cmake . -DCMAKE_INSTALL_PREFIX=/usr` is equivalent to `./configure --prefix=/usr` +* `-DCMAKE_INSTALL_PREFIX` : decide where the program will be install on your computer. +* `-DCMAKE_BUILD_TYPE` : decide which type of build you want. You can chose between: + * `debugfull`. : for hacking. Include all debug information. + * `relwithdebinfo` : default. use gcc `-O2` `-g` options. + * `release` : generate stripped and optimized bin files. For packaging. +* `-DCMAKE_INSTALL_PREFIX=/usr`: install program in /usr. More details can be found [at this url](http://techbase.kde.org/Development/Tutorials/CMake#Environment_Variables) Note: To know KDE install path on your computer, use `kf5-config --prefix` command line like this (with full debug object enabled): * `cmake . -DCMAKE_BUILD_TYPE=debugfull -DCMAKE_INSTALL_PREFIX=$(kf5-config --prefix)` # Donate Money If you love digiKam, you can help developers to buy new photo devices to test and implement new features. Thanks in advance for your generous donations. For more information, look [at this url](https://www.digikam.org/donate/) diff --git a/core/CMakeLists.txt b/core/CMakeLists.txt index 68e473d608..12979fdf75 100644 --- a/core/CMakeLists.txt +++ b/core/CMakeLists.txt @@ -1,731 +1,732 @@ # # Copyright (c) 2010-2020 by Gilles Caulier, # Copyright (c) 2015 by Veaceslav Munteanu, # Copyright (c) 2015 by Mohamed_Anwer, # # Redistribution and use is allowed according to the terms of the BSD license. # For details see the accompanying COPYING-CMAKE-SCRIPTS file. APPLY_COMMON_POLICIES() # ============================================================================== message(STATUS "----------------------------------------------------------------------------------") message(STATUS "Check dependencies for ${PROJECT_NAME} version ${DIGIKAM_VERSION_STRING}") message(STATUS "") set(KF5_MIN_VERSION "5.5.0") set(QT_MIN_VERSION "5.9.0") set(EXIV2_MIN_VERSION "0.27.0") set(OPENCV_MIN_VERSION "3.3.0") set(LENSFUN_MIN_VERSION "0.2.6.0") # For Lens auto-correction plugin set(QTAV_MIN_VERSION "1.12.0") # For video support. set(KSANE_MIN_VERSION "5.5.0") # For digital scanner support. set(CALENDAR_MIN_VERSION "5.5.0") # Calendar Core dependency for plugin. set(AKONADI_MIN_VERSION "4.89.0") # Akonadi Contact dependency. -set(VKONTAKTE_MIN_VERSION "5.0.0") # VKontakte library dependency for plugin. cmake_minimum_required(VERSION ${CMAKE_MIN_VERSION}) # ============================================================================== option(ENABLE_KFILEMETADATASUPPORT "Build digiKam with KDE files indexer support (default=OFF)" OFF) option(ENABLE_AKONADICONTACTSUPPORT "Build digiKam with KDE Mail Contacts support (default=OFF)" OFF) option(ENABLE_MEDIAPLAYER "Build digiKam with Media Player support (default=OFF)" OFF) option(ENABLE_DBUS "Build digiKam with DBUS support (default=ON)" ON) option(ENABLE_APPSTYLES "Build digiKam with support for changing the widget application style (default=OFF)" OFF) option(ENABLE_QWEBENGINE "Build digiKam with QWebEngine instead of QWebKit (default=ON)" ON) # Mysql support options (experimental): option(ENABLE_MYSQLSUPPORT "Build digiKam with MySQL dabatase support (default=ON)" ON) option(ENABLE_INTERNALMYSQL "Build digiKam with internal MySQL server executable (default=ON)" ON) # Developer options: option(ENABLE_DIGIKAM_MODELTEST "Enable ModelTest on some models for debugging (default=OFF)" OFF) # Packaging options: if(MINGW) option(ENABLE_DRMINGW "Enable the Dr. MinGW crash handler support for windows (default ON)" ON) option(ENABLE_MINGW_HARDENING_LINKER "Enable DEP (NX), ASLR, and high-entropy ASLR linker flags for MinGW (default ON)" ON) endif() ############## Find Packages ################### find_package(Qt5 ${QT_MIN_VERSION} REQUIRED NO_MODULE COMPONENTS Core Concurrent Widgets Gui Sql Xml XmlPatterns PrintSupport Network ) if(ENABLE_QWEBENGINE) find_package(Qt5 ${QT_MIN_VERSION} REQUIRED NO_MODULE COMPONENTS WebEngineWidgets) else() find_package(Qt5 ${QT_MIN_VERSION} REQUIRED NO_MODULE COMPONENTS WebKitWidgets) endif() find_package(Qt5 ${QT_MIN_VERSION} OPTIONAL_COMPONENTS DBus OpenGL ) if(ENABLE_DBUS) if(NOT Qt5DBus_FOUND) set(ENABLE_DBUS OFF) endif() endif() # Dependencies For unit tests and CLI test tools if(BUILD_TESTING) find_package(Qt5 ${QT_MIN_VERSION} REQUIRED NO_MODULE COMPONENTS Test ) find_package(Qt5 ${QT_MIN_VERSION} QUIET OPTIONAL_COMPONENTS Qml WebView # Optional, for 'sialis' O2 library test tool. ) endif() find_package(KF5 ${KF5_MIN_VERSION} REQUIRED COMPONENTS XmlGui CoreAddons Config Service WindowSystem Solid I18n ) find_package(KF5 ${KF5_MIN_VERSION} QUIET OPTIONAL_COMPONENTS KIO # For Desktop integration (Widgets only). IconThemes # For Desktop integration. FileMetaData # For KDE file indexer support. ThreadWeaver # For Panorama tool. NotifyConfig # KDE desktop application notify configuration. Notifications # KDE desktop notifications integration. ) find_package(KF5 ${AKONADI_MIN_VERSION} QUIET OPTIONAL_COMPONENTS AkonadiContact # For KDE Mail Contacts support. Contacts # API for contacts/address book data. ) find_package(KF5 ${KSANE_MIN_VERSION} QUIET OPTIONAL_COMPONENTS Sane # For digital scanner support. ) -find_package(KF5 ${VKONTAKTE_MIN_VERSION} QUIET - OPTIONAL_COMPONENTS - Vkontakte # For VKontakte webservice support. -) - find_package(KF5 ${CALENDAR_MIN_VERSION} QUIET OPTIONAL_COMPONENTS CalendarCore # For Calendar tool. ) if ("${KF5CalendarCore_VERSION}" VERSION_GREATER 5.6.40) set(HAVE_KCALENDAR_QDATETIME TRUE) endif() if(ENABLE_AKONADICONTACTSUPPORT AND (NOT KF5AkonadiContact_FOUND OR NOT KF5Contacts_FOUND)) set(ENABLE_AKONADICONTACTSUPPORT OFF) endif() if(ENABLE_KFILEMETADATASUPPORT AND NOT KF5FileMetaData_FOUND) set(ENABLE_KFILEMETADATASUPPORT OFF) endif() # Check if KIO have been compiled with KIOWidgets. digiKam only needs this one. if(KF5KIO_FOUND) get_target_property(KIOWidgets_INCLUDE_DIRS KF5::KIOWidgets INTERFACE_INCLUDE_DIRECTORIES) message(STATUS "KF5::KIOWidgets include dirs: ${KIOWidgets_INCLUDE_DIRS}") if(NOT KIOWidgets_INCLUDE_DIRS) message(STATUS "KF5::KIOWidgets not available in shared KIO library. KIO support disabled.") set(KF5KIO_FOUND FALSE) endif() endif() # ============================================================================== # Dependencies Rules # mandatory DETECT_JPEG() set(DIGIKAM_LIBJPEG_DIR libjpeg/${JPEG_LIB_VERSION}) message(STATUS "Using libjpeg sub-directory: ${DIGIKAM_LIBJPEG_DIR}") find_package(TIFF) find_package(PNG) find_package(Boost) find_package(LCMS2) find_package(EXPAT) # For DNGWriter: XMP SDK need Expat library to compile. find_package(Threads) # For DNGWriter and LibRaw which needs native threads support. find_package(X265) # For HEIF encoding support. find_package(exiv2 REQUIRED) set_package_properties("exiv2" PROPERTIES DESCRIPTION "Required to build digiKam" URL "https://www.exiv2.org" TYPE RECOMMENDED PURPOSE "Library to manage image metadata" ) if("${exiv2_VERSION}" VERSION_LESS ${EXIV2_MIN_VERSION}) message(FATAL_ERROR "Exiv2 version is too old (${exiv2_VERSION})! Minimal version required:${EXIV2_MIN_VERSION}.") endif() # -- check Media player -------------------------------------------------------- find_package(FFmpeg COMPONENTS AVCODEC AVDEVICE AVFILTER AVFORMAT AVUTIL SWSCALE) find_package(QtAV) if(ENABLE_MEDIAPLAYER) if(${AVCODEC_FOUND} AND ${AVDEVICE_FOUND} AND ${AVFILTER_FOUND} AND ${AVFORMAT_FOUND} AND ${AVUTIL_FOUND} AND ${SWSCALE_FOUND}) include_directories(${FFMPEG_INCLUDE_DIRS}) else() set(ENABLE_MEDIAPLAYER OFF) set(FFMPEG_FOUND OFF) message(STATUS "ENABLE_MEDIAPLAYER option is enabled but FFMpeg cannot be found. Media player support is disabled.") endif() if(NOT ${QtAV_FOUND} OR "${QTAV_VERSION_STRING}" VERSION_LESS ${QTAV_MIN_VERSION}) set(ENABLE_MEDIAPLAYER OFF) set(QtAV_FOUND OFF) message(STATUS "ENABLE_MEDIAPLAYER option is enabled but QtAV cannot be found. Media player support is disabled.") else() include_directories(${QTAV_INCLUDE_DIRS}) endif() if (${QtAV_FOUND} AND ${FFMPEG_FOUND}) message(STATUS "Media player support is enabled.") endif() endif() # -- check OpenCV -------------------------------------------------------------- DETECT_OPENCV(${OPENCV_MIN_VERSION} core objdetect imgproc imgcodecs dnn flann) if(${OpenCV_FOUND}) if(${OpenCV_VERSION} VERSION_LESS ${OPENCV_MIN_VERSION}) message(STATUS "OpenCV < ${OPENCV_MIN_VERSION} have been found. Please use a more recent version.") set(OpenCV_FOUND FALSE) endif() endif() configure_file(${CMAKE_CURRENT_SOURCE_DIR}/app/utils/digikam_opencv.h.cmake.in ${CMAKE_CURRENT_BINARY_DIR}/app/utils/digikam_opencv.h) # -- optionals ----------------------------------------------------------------- find_package(FLEX) # For Panorama tool. find_package(BISON) # For Panorama tool. find_package(LibXslt) # For HTMLGallery tool. find_package(LibXml2) # For HTMLGallery tool. find_package(Marble) # For geolocation support. find_package(PkgConfig) find_package(Jasper) # For JPEG 2000 support. find_package(Eigen3) # For Refocus tool. -find_package(OpenGL) # For Presentation tool. + +# -- ImageMagick rules -------------------------------------------------------- find_package(ImageMagick COMPONENTS Magick++ MagickCore) if(ImageMagick_Magick++_FOUND) add_definitions(${ImageMagick_Magick++_DEFINITIONS}) endif() +# -- X11 rules ---------------------------------------------------------------- + # For Monitor Profiles management with LCMS find_package(X11) if(X11_FOUND) find_package(Qt5 ${QT_MIN_VERSION} NO_MODULE COMPONENTS X11Extras) set(HAVE_X11 TRUE) else() set(HAVE_X11 FALSE) endif() +# -- OpenGL rules ------------------------------------------------------------- + +if(POLICY CMP0072) + # Prefers Legacy OpenGL by default when available introduced in CMake version 3.11 + # Details: https://cmake.org/cmake/help/git-stage/policy/CMP0072.html + cmake_policy(SET CMP0072 OLD) +endif() + +find_package(OpenGL) # For Presentation tool. + +message(STATUS "OpenGL found; ${OPENGL_FOUND}") +message(STATUS "OpenGL GLU extension found; ${OPENGL_GLU_FOUND}") +message(STATUS "OpenGL Qt5 found; ${Qt5OpenGL_FOUND}") + # decide if Presentation tool can be built with OpenGL if(OPENGL_FOUND AND OPENGL_GLU_FOUND AND Qt5OpenGL_FOUND) set(HAVE_OPENGL TRUE) elseif() set(HAVE_OPENGL FALSE) endif() # -- Windows rules ------------------------------------------------------------ # For LibRaw if(WIN32) find_library(WSOCK32_LIBRARY wsock32) find_library(WS2_32_LIBRARY ws2_32) endif() if(MINGW) # NOTE: inspired from https://phabricator.kde.org/T3917 if(ENABLE_MINGW_HARDENING_LINKER) set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--dynamicbase -Wl,--nxcompat -Wl,--disable-auto-image-base") set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,--dynamicbase -Wl,--nxcompat -Wl,--disable-auto-image-base") set(CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} -Wl,--dynamicbase -Wl,--nxcompat -Wl,--disable-auto-image-base") if ("${CMAKE_SIZEOF_VOID_P}" EQUAL "8") # Enable high-entropy ASLR for 64-bit # The image base has to be >4GB for HEASLR to be enabled. # The values used here are kind of arbitrary. set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--high-entropy-va -Wl,--image-base,0x140000000") set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,--high-entropy-va -Wl,--image-base,0x180000000") set(CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} -Wl,--high-entropy-va -Wl,--image-base,0x180000000") endif() message(STATUS "MinGW linker Security Flags enabled") else() message(WARNING "MinGW Linker Security Flags not enabled!") endif () # Enable C++11 support set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11") endif() # -- To link under Solaris (see bug #274484) ----------------------------------- if(NOT WIN32) find_library(MATH_LIBRARY m) endif() if(CMAKE_SYSTEM_NAME STREQUAL FreeBSD) find_library(KVM_LIBRARY kvm) endif() # ============================================================================== # More Optional Dependencies find_package(Doxygen) if(NOT CMAKE_SYSTEM_NAME STREQUAL FreeBSD) find_package(Lqr-1) endif() # -- libgphoto2 rules ---------------------------------------------------------- if(NOT WIN32) find_package(Gphoto2) if(Gphoto2_FOUND) if("${GPHOTO2_VERSION_STRING}" VERSION_GREATER "2.4.0") set(VERSION_GPHOTO2 true) else() set(VERSION_GPHOTO2 false) endif() if("${GPHOTO2_VERSION_STRING}" VERSION_GREATER "2.5.0") set(VERSION_GPHOTO25 true) message(STATUS "libgphoto2 API version >= 2.5") else() set(VERSION_GPHOTO25 false) message(STATUS "libgphoto2 API version < 2.5") endif() if(VERSION_GPHOTO25) set(HAVE_GPHOTO25 1) else() set(HAVE_GPHOTO25 0) endif() endif() endif() # -- Check LensFun library for Lens auto-correction tool ----------------------- find_package(LensFun) if(LENSFUN_VERSION) message(STATUS "liblensfun: Found version ${LENSFUN_VERSION} (required: ${LENSFUN_MIN_VERSION})") if(${LENSFUN_VERSION} VERSION_LESS ${LENSFUN_MIN_VERSION}) set(LensFun_FOUND FALSE) endif() else() message(STATUS "liblensfun: Version information not found, your version is probably too old.") set(LensFun_FOUND FALSE) endif() # -- Check dependencies for Panorama tool -------------------------------------- if(FLEX_FOUND AND BISON_FOUND AND KF5ThreadWeaver_FOUND) set(HAVE_PANORAMA 1) else() set(HAVE_PANORAMA 0) endif() # -- Check dependencies for HTMLGallery tool ----------------------------------- if(LibXml2_FOUND AND LibXslt_FOUND) set(HAVE_HTMLGALLERY 1) else() set(HAVE_HTMLGALLERY 0) endif() # -- Check dependencies for libraw --------------------------------------------- message(STATUS "") message(STATUS "--------------------------------------------------") message(STATUS "digiKam RawEngine dependencies checks:") message(STATUS "") include(MacroOpenMP) DETECT_OPENMP() if(OPENMP_FOUND) message(STATUS "RawEngine will be compiled with OpenMP support") else() message(STATUS "RawEngine will not be compiled with OpenMP support") endif() # Flag to use libjasper with LibRaw RedCine codec if(Jasper_FOUND) add_definitions(-DUSE_JASPER) include_directories(${JASPER_INCLUDE_DIR}) message(STATUS "RawEngine will be compiled with RedCine codec") else() message(STATUS "RawEngine will not be compiled with RedCine codec") endif() # JPEG library check message(STATUS "Looking for LibJpeg") if(JPEG_FOUND) # JPEG lib version is done on top level through MacroJPEG.cmake if (${JPEG_LIB_VERSION} LESS 80) set(JPEG8_FOUND FALSE) else() set(JPEG8_FOUND TRUE) endif() endif() # Flag to use libjpeg with LibRaw DNG lossy codec if(JPEG8_FOUND) add_definitions(-DUSE_JPEG) add_definitions(-DUSE_JPEG8) include_directories(${JPEG_INCLUDE_DIR}) message(STATUS "RawEngine will be compiled with DNG lossy codec") else() message(STATUS "RawEngine will not be compiled with DNG lossy codec") endif() message(STATUS "Looking for PThreads") set(PTHREADS_FOUND (CMAKE_USE_PTHREADS_INIT OR CMAKE_USE_WIN32_THREADS_INIT)) # Registration of Libraw configuration to a dedicated header MACRO_BOOL_TO_01(JPEG8_FOUND LIBRAW_USE_DNGLOSSYCODEC) MACRO_BOOL_TO_01(OPENMP_FOUND LIBRAW_USE_OPENMP) MACRO_BOOL_TO_01(Jasper_FOUND LIBRAW_USE_REDCINECODEC) configure_file(${CMAKE_CURRENT_SOURCE_DIR}/libs/rawengine/libraw/libraw_config.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/libs/rawengine/libraw/libraw_config.h) message(STATUS "--------------------------------------------------") message(STATUS "") # -- compilation options definitions ------------------------------------------- MACRO_BOOL_TO_01(KF5Sane_FOUND HAVE_KSANE) MACRO_BOOL_TO_01(KF5FileMetaData_FOUND HAVE_KFILEMETADATA) MACRO_BOOL_TO_01(KF5CalendarCore_FOUND HAVE_KCALENDAR) MACRO_BOOL_TO_01(KF5Notifications_FOUND HAVE_KNOTIFICATIONS) MACRO_BOOL_TO_01(KF5NotifyConfig_FOUND HAVE_KNOTIFYCONFIG) MACRO_BOOL_TO_01(KF5KIO_FOUND HAVE_KIO) MACRO_BOOL_TO_01(KF5IconThemes_FOUND HAVE_KICONTHEMES) -MACRO_BOOL_TO_01(KF5Vkontakte_FOUND HAVE_VKONTAKTE) MACRO_BOOL_TO_01(LensFun_FOUND HAVE_LENSFUN) MACRO_BOOL_TO_01(Lqr-1_FOUND HAVE_LIBLQR_1) MACRO_BOOL_TO_01(Gphoto2_FOUND HAVE_GPHOTO2) MACRO_BOOL_TO_01(Jasper_FOUND HAVE_JASPER) MACRO_BOOL_TO_01(Eigen3_FOUND HAVE_EIGEN3) MACRO_BOOL_TO_01(Marble_FOUND HAVE_MARBLE) MACRO_BOOL_TO_01(ENABLE_AKONADICONTACTSUPPORT HAVE_AKONADICONTACT) MACRO_BOOL_TO_01(ENABLE_MYSQLSUPPORT HAVE_MYSQLSUPPORT) MACRO_BOOL_TO_01(ENABLE_INTERNALMYSQL HAVE_INTERNALMYSQL) MACRO_BOOL_TO_01(ENABLE_MEDIAPLAYER HAVE_MEDIAPLAYER) MACRO_BOOL_TO_01(ENABLE_DBUS HAVE_DBUS) MACRO_BOOL_TO_01(ENABLE_APPSTYLES HAVE_APPSTYLE_SUPPORT) MACRO_BOOL_TO_01(ENABLE_QWEBENGINE HAVE_QWEBENGINE) MACRO_BOOL_TO_01(ENABLE_DRMINGW HAVE_DRMINGW) MACRO_BOOL_TO_01(ImageMagick_Magick++_FOUND HAVE_IMAGE_MAGICK) MACRO_BOOL_TO_01(X265_FOUND HAVE_X265) # Set config files accordingly with optional dependencies configure_file(app/utils/digikam_config.h.cmake.in ${CMAKE_CURRENT_BINARY_DIR}/app/utils/digikam_config.h) # ============================================================================== # Log messages message(STATUS "") message(STATUS "----------------------------------------------------------------------------------") message(STATUS " digiKam ${DIGIKAM_VERSION_STRING} dependencies results ") message(STATUS "") PRINT_COMPONENT_COMPILE_STATUS("MySQL Database Support" ENABLE_MYSQLSUPPORT) PRINT_COMPONENT_COMPILE_STATUS("MySQL Internal Support" ENABLE_INTERNALMYSQL) PRINT_COMPONENT_COMPILE_STATUS("DBUS Support" ENABLE_DBUS) PRINT_COMPONENT_COMPILE_STATUS("App. Style Support" ENABLE_APPSTYLES) PRINT_COMPONENT_COMPILE_STATUS("QWebEngine Support" ENABLE_QWEBENGINE) # ============================================================================== PRINT_LIBRARY_STATUS("libboostgraph" "https://www.boost.org/doc/libs" "(version >= 1.43.0)" Boost_FOUND) PRINT_LIBRARY_STATUS("libexiv2" "https://www.exiv2.org" "(version >= ${EXIV2_MIN_VERSION}" exiv2_FOUND) PRINT_LIBRARY_STATUS("libexpat" "https://libexpat.github.io" "(version >= 2.0.0)" EXPAT_FOUND) PRINT_LIBRARY_STATUS("libjpeg" "https://github.com/libjpeg-turbo/libjpeg-turbo" "(version >= 6b)" JPEG_FOUND) PRINT_LIBRARY_STATUS("libkde" "https://www.kde.org" "(version >= ${KF5_MIN_VERSION})" KF5_FOUND) PRINT_LIBRARY_STATUS("liblcms" "https://github.com/mm2/Little-CMS" "(version >= 2.0.0)" LCMS2_FOUND) PRINT_LIBRARY_STATUS("libopencv" "https://opencv.org" "(version >= ${OPENCV_MIN_VERSION})" OpenCV_FOUND) PRINT_LIBRARY_STATUS("libpng" "https://libpng.sourceforge.io/" "(version >= 1.2.7)" PNG_FOUND) PRINT_LIBRARY_STATUS("libpthread" "https://www.gnu.org/software/hurd/libpthread.html" "(version >= 2.0.0)" CMAKE_USE_PTHREADS_INIT OR CMAKE_USE_WIN32_THREADS_INIT) PRINT_LIBRARY_STATUS("libqt" "https://www.qt.io" "(version >= ${QT_MIN_VERSION})" Qt5_FOUND) PRINT_LIBRARY_STATUS("libtiff" "https://gitlab.com/libtiff/libtiff/" "(version >= 3.8.2)" TIFF_FOUND) # ============================================================================== PRINT_OPTIONAL_LIBRARY_STATUS("bison" "https://www.gnu.org/software/bison/" "(version >= 2.5.0)" "digiKam will be compiled without Panorama support." BISON_FOUND) PRINT_OPTIONAL_LIBRARY_STATUS("doxygen" "https://github.com/doxygen/doxygen" "(version >= 1.8.0)" "digiKam will be compiled without API documentation building support." Doxygen_FOUND) PRINT_OPTIONAL_LIBRARY_STATUS("flex" "https://github.com/westes/flex" "(version >= 2.5.0)" "digiKam will be compiled without Panorama support." FLEX_FOUND) PRINT_OPTIONAL_LIBRARY_STATUS("libakonadicontact" "https://cgit.kde.org/akonadi-contacts.git/about/" "(version >= ${AKONADI_MIN_VERSION})" "digiKam will be compiled without KDE desktop address book support." KF5AkonadiContact_FOUND) PRINT_OPTIONAL_LIBRARY_STATUS("libmagick++" "https://www.imagemagick.org" "(version >= 6.7.0)" "digiKam will be compiled without ImageMagick codecs." HAVE_IMAGE_MAGICK) PRINT_OPTIONAL_LIBRARY_STATUS("libeigen3" "https://github.com/eigenteam/eigen-git-mirror" "(version >= 3.0.0)" "digiKam will be compiled without Refocus tool support." Eigen3_FOUND) PRINT_OPTIONAL_LIBRARY_STATUS("libgphoto2" "https://github.com/gphoto" "(version >= 2.4.0)" "digiKam will be compiled without GPhoto2 camera drivers support." Gphoto2_FOUND) PRINT_OPTIONAL_LIBRARY_STATUS("libjasper" "https://github.com/mdadams/jasper" "(version >= 1.7.0)" "digiKam will be compiled without JPEG2000 support." Jasper_FOUND) PRINT_OPTIONAL_LIBRARY_STATUS("libkcalendarcore" "https://cgit.kde.org/kcalendarcore.git/about/" "(version >= ${CALENDAR_MIN_VERSION})" "digiKam will be compiled without advanced calendar support." KF5CalendarCore_FOUND) -PRINT_OPTIONAL_LIBRARY_STATUS("libkvkontakte" "https://cgit.kde.org/libkvkontatke.git/about/" "(version >= ${VKONTAKTE_MIN_VERSION})" "digiKam will be compiled without libkvkontakte support." KF5Vkontakte_FOUND) PRINT_OPTIONAL_LIBRARY_STATUS("libkfilemetadata" "https://cgit.kde.org/kfilemetadata.git/about/" "(version >= ${KF5_MIN_VERSION})" "digiKam will be compiled without KDE desktop file metadata support." KF5FileMetaData_FOUND) PRINT_OPTIONAL_LIBRARY_STATUS("libkiconthemes" "https://cgit.kde.org/kiconthemes.git/about/" "(version >= ${KF5_MIN_VERSION})" "digiKam will be compiled without KDE desktop icon themes support." KF5IconThemes_FOUND) PRINT_OPTIONAL_LIBRARY_STATUS("libkio" "https://cgit.kde.org/kio.git/about/" "(version >= ${KF5_MIN_VERSION})" "digiKam will be compiled without KDE desktop integration support." KF5KIO_FOUND) PRINT_OPTIONAL_LIBRARY_STATUS("libknotifications" "https://cgit.kde.org/knotifyconfig.git/about/" "(version >= ${KF5_MIN_VERSION})" "digiKam will be compiled without KDE desktop notifications support." KF5Notifications_FOUND) PRINT_OPTIONAL_LIBRARY_STATUS("libknotifyconfig" "https://cgit.kde.org/knotifications.git/about/" "(version >= ${KF5_MIN_VERSION})" "digiKam will be compiled without KDE desktop notify configuration support." KF5NotifyConfig_FOUND) PRINT_OPTIONAL_LIBRARY_STATUS("libksane" "https://cgit.kde.org/libksane.git/about/" "(version >= ${KSANE_MIN_VERSION})" "digiKam will be compiled without flat scanners support." KF5Sane_FOUND) PRINT_OPTIONAL_LIBRARY_STATUS("liblensfun" "https://lensfun.sourceforge.net" "(version >= 0.2.6)" "digiKam will be compiled without Lens Auto Correction tool support." LensFun_FOUND) PRINT_OPTIONAL_LIBRARY_STATUS("liblqr-1" "https://liblqr.wikidot.com" "(version >= 0.4.1)" "digiKam will be compiled without Contents Aware Resizer tool support." Lqr-1_FOUND) PRINT_OPTIONAL_LIBRARY_STATUS("libmarble" "https://cgit.kde.org/marble.git/about/" "(version >= 0.22.0)" "digiKam will be compiled without geolocation maps support." Marble_FOUND) PRINT_OPTIONAL_LIBRARY_STATUS("libqtav" "https://www.qtav.org/" "(version >= ${QTAV_MIN_VERSION})" "digiKam will be compiled without Media Player support." QtAV_FOUND) PRINT_OPTIONAL_LIBRARY_STATUS("libthreadweaver" "https://cgit.kde.org/threadweaver.git/about/" "(version >= ${KF5_MIN_VERSION})" "digiKam will be compiled without Panorama support." KF5ThreadWeaver_FOUND) PRINT_OPTIONAL_LIBRARY_STATUS("libxml2" "https://xmlsoft.org" "(version >= 2.7.0)" "digiKam will be compiled without HTMLGallery support." LibXml2_FOUND) PRINT_OPTIONAL_LIBRARY_STATUS("libxslt" "https://xmlsoft.org/XSLT" "(version >= 1.1.0)" "digiKam will be compiled without HTMLGallery support." LibXslt_FOUND) PRINT_OPTIONAL_LIBRARY_STATUS("libx265" "https://x265.org" "(version >= 2.2)" "digiKam will be compiled without HEIF encoding support." X265_FOUND) PRINT_OPTIONAL_LIBRARY_STATUS("OpenGL" "https://www.mesa3d.org" "(version >= 11.0.0)" "digiKam will be compiled without OpenGL support." HAVE_OPENGL) # ============================================================================== if(Boost_FOUND AND exiv2_FOUND AND EXPAT_FOUND AND JPEG_FOUND AND KF5_FOUND AND LCMS2_FOUND AND OpenCV_FOUND AND PNG_FOUND AND Qt5_FOUND AND TIFF_FOUND AND Threads_FOUND AND (Qt5Test_FOUND OR NOT BUILD_TESTING) AND (Qt5X11Extras_FOUND OR NOT HAVE_X11) AND (CMAKE_USE_PTHREADS_INIT OR CMAKE_USE_WIN32_THREADS_INIT) ) message(STATUS " digiKam can be compiled.................. YES") set(DIGIKAM_CAN_BE_COMPILED true) else() message(FATAL_ERROR " digiKam will be compiled.................. NO (See the README file for more details about dependencies)") set(DIGIKAM_CAN_BE_COMPILED false) endif() message(STATUS "----------------------------------------------------------------------------------") message(STATUS "") if(DIGIKAM_CAN_BE_COMPILED) # ========================================================================== # Create git version header # We only do this IF we are in a .git dir find_file(GIT_MARKER entries PATHS ${CMAKE_SOURCE_DIR}/.git) if(NOT GIT_MARKER) set(GIT_MARKER ${CMAKE_SOURCE_DIR}/CMakeLists.txt) # Dummy file endif() # Add a custom command to drive the git script whenever the git entries # file changes. configure_file("${CMAKE_CURRENT_SOURCE_DIR}/cmake/templates/gitscript.cmake.in" "${CMAKE_CURRENT_BINARY_DIR}/gitscript.cmake" @ONLY) # Add a custom target to drive the custom command. add_custom_target(digikam-gitversion ALL COMMAND ${CMAKE_COMMAND} -P "${CMAKE_CURRENT_BINARY_DIR}/gitscript.cmake") # ========================================================================== # To prevent warnings from M$ compiler if(WIN32 AND MSVC) add_definitions(-D_CRT_SECURE_NO_WARNINGS) add_definitions(-D_ATL_SECURE_NO_WARNINGS) add_definitions(-D_AFX_SECURE_NO_WARNINGS) endif() # ========================================================================== # Definitions rules # Remove pedantic GCC flag which generate a lots of warnings on the console # with qCDebug() REMOVE_GCC_COMPILER_WARNINGS("-pedantic") # Remove date-time GCC flag which generate a lots of compile warnings REMOVE_GCC_COMPILER_WARNINGS("-Wdate-time") # Remove suggest-override GCC flag which generate a lots of compile warnings REMOVE_GCC_COMPILER_WARNINGS("-Wsuggest-override") # Remove zero-as-null-pointer-constant GCC and CLang flag which generate a lots of compile warnings REMOVE_COMPILER_WARNINGS("-Wzero-as-null-pointer-constant") # Remove deprecated-copy GCC warnings which generate a lots of compile warnings DISABLE_GCC_COMPILER_WARNINGS("8.99.99" "-Wno-deprecated-copy") # Remove date-time Clang warnings which generate a lots of compile warnings DISABLE_CLANG_COMPILER_WARNINGS("7.99.99" "-Wdate-time") # Remove inconsistent-missing-override Clang warnings which generate a lots of compile warnings DISABLE_CLANG_COMPILER_WARNINGS("7.99.99" "-Wno-inconsistent-missing-override") # translations catalog add_definitions(-DTRANSLATION_DOMAIN=\"digikam\") # NOTE: with libpgf 6.11.24 OpenMP is not very well supported. We disable # it to be safe. See B.K.O #273765 for details. add_definitions(-DLIBPGF_DISABLE_OPENMP) # Enable C++ Exceptions support, require by Greycstoration algorithm # (CImg.h) and PGF codec kde_enable_exceptions() # ========================================================================== # Includes rules # Recursively get all directories which contain header files set(DK_INCLUDES_ALL "") HEADER_DIRECTORIES(DK_LOCAL_INCLUDES_RAW) # This macro will set all paths which do not contain libjpeg- # We will add later the directory we need foreach(var ${DK_LOCAL_INCLUDES_RAW}) string(REGEX MATCH "libjpeg/" item ${var}) if(item STREQUAL "") list(APPEND DK_LOCAL_INCLUDES ${var}) endif() endforeach() set(DK_LOCAL_INCLUDES ${DK_LOCAL_INCLUDES} libs/jpegutils/${DIGIKAM_LIBJPEG_DIR}) include_directories(${DK_LOCAL_INCLUDES}) # for config headers digikam_version.h gitversion.h digikam_config.h # digikam_dbconfig.h digikam_opencv.h include_directories(${CMAKE_CURRENT_BINARY_DIR}/app/utils) include_directories(${OpenCV_INCLUDE_DIRS}) - if(KF5Vkontakte_FOUND) - include_directories(${LIBKVKONTAKTE_INCLUDES}) - endif() - - if(LensFun_FOUND) - include_directories(${LENSFUN_INCLUDE_DIRS}) - endif() - if(ImageMagick_FOUND) include_directories(${ImageMagick_INCLUDE_DIRS}) endif() # ========================================================================== # Common targets parts add_subdirectory(data) add_subdirectory(libs) add_subdirectory(utilities) add_subdirectory(app) add_subdirectory(dplugins) add_subdirectory(showfoto) if(BUILD_TESTING) add_subdirectory(tests) endif() endif() # ============================================================================== # API documentation generation if(Doxygen_FOUND) message(STATUS " ==> Doxygen is found: ${Doxygen_FOUND}") message(STATUS " ==> Graphviz Dot is found: ${DOXYGEN_DOT_FOUND}") if(Doxygen_FOUND AND DOXYGEN_DOT_FOUND) message(STATUS " To build API documentation use 'make doc'") add_custom_target(doc ${DOXYGEN_EXECUTABLE} WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}) endif() endif() diff --git a/core/app/CMakeLists.txt b/core/app/CMakeLists.txt index cc9228fec3..b328a54280 100644 --- a/core/app/CMakeLists.txt +++ b/core/app/CMakeLists.txt @@ -1,66 +1,66 @@ # # Copyright (c) 2010-2020 by Gilles Caulier, # Copyright (c) 2015 by Veaceslav Munteanu, # # Redistribution and use is allowed according to the terms of the BSD license. # For details see the accompanying COPYING-CMAKE-SCRIPTS file. APPLY_COMMON_POLICIES() # ----------------------------------------------------------------------------------- # Headers to configure configure_file(${CMAKE_CURRENT_SOURCE_DIR}/utils/digikam_version.h.cmake.in ${CMAKE_CURRENT_BINARY_DIR}/utils/digikam_version.h) configure_file(${CMAKE_CURRENT_SOURCE_DIR}/utils/digikam_dbconfig.h.cmake.in ${CMAKE_CURRENT_BINARY_DIR}/utils/digikam_dbconfig.h) # ----------------------------------------------------------------------------------- # Common includes rules include_directories($ $ $ $ $ $ $ $ $ $ $ $) if(ENABLE_QWEBENGINE) include_directories($) else() include_directories($) endif() if(KF5KIO_FOUND) include_directories($) endif() if(Gphoto2_FOUND) include_directories(${GPHOTO2_INCLUDE_DIRS}) endif() # ----------------------------------------------------------------------------------- # Targets to build include(DigikamCoreTarget.cmake) include(DigikamDatabaseTarget.cmake) include(DigikamGuiTarget.cmake) include(DigikamTarget.cmake) # ----------------------------------------------------------------------------------- # Others Files to install -install(PROGRAMS main/org.kde.digikam.desktop DESTINATION ${XDG_APPS_INSTALL_DIR}) -install(FILES main/org.kde.digikam.appdata.xml DESTINATION ${KDE_INSTALL_METAINFODIR}) -install(FILES main/digikamui5.rc DESTINATION ${KXMLGUI_INSTALL_DIR}/digikam) -install(FILES main/digikam.notifyrc DESTINATION ${KNOTIFYRC_INSTALL_DIR}) +install(PROGRAMS ${CMAKE_CURRENT_SOURCE_DIR}/main/org.kde.digikam.desktop DESTINATION ${XDG_APPS_INSTALL_DIR}) +install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/main/org.kde.digikam.appdata.xml DESTINATION ${KDE_INSTALL_METAINFODIR}) +install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/main/digikamui5.rc DESTINATION ${KXMLGUI_INSTALL_DIR}/digikam) +install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/main/digikam.notifyrc DESTINATION ${KNOTIFYRC_INSTALL_DIR}) include(DigikamExportAPI.cmake) diff --git a/core/app/DigikamCoreTarget.cmake b/core/app/DigikamCoreTarget.cmake index 984968ed66..5d45abd299 100644 --- a/core/app/DigikamCoreTarget.cmake +++ b/core/app/DigikamCoreTarget.cmake @@ -1,351 +1,351 @@ # # Copyright (c) 2010-2020 by Gilles Caulier, # Copyright (c) 2015 by Veaceslav Munteanu, # # Redistribution and use is allowed according to the terms of the BSD license. # For details see the accompanying COPYING-CMAKE-SCRIPTS file. ### digiKam core object library ################################################################################################# set(DIGIKAMCORE_OBJECTS - utils/digikam_debug.cpp - utils/digikam_version.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/utils/digikam_debug.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/utils/digikam_version.cpp $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ # widgets $ $ $ $ $ # utilities $ $ $ $ $ $ ) if(ENABLE_MEDIAPLAYER) set(DIGIKAMCORE_OBJECTS ${DIGIKAMCORE_OBJECTS} $ ) endif() if(Marble_FOUND) set(DIGIKAMCORE_OBJECTS ${DIGIKAMCORE_OBJECTS} $ $ ) endif() if(KF5FileMetaData_FOUND) set(DIGIKAMCORE_OBJECTS ${DIGIKAMCORE_OBJECTS} $ ) endif() if(KF5AkonadiContact_FOUND) set(DIGIKAMCORE_OBJECTS ${DIGIKAMCORE_OBJECTS} $ ) endif() add_library(digikamcore SHARED ${DIGIKAMCORE_OBJECTS} ) set_target_properties(digikamcore PROPERTIES VERSION ${DIGIKAM_VERSION_SHORT} SOVERSION ${DIGIKAM_VERSION_SHORT} ) target_compile_definitions(digikamcore PRIVATE digikamcore_EXPORTS ) add_dependencies(digikamcore digikam-gitversion) # All codes from this target are exported with digikam_core_export.h header and DIGIKAM_EXPORT macro. generate_export_header(digikamcore BASE_NAME digikam EXPORT_FILE_NAME "${CMAKE_CURRENT_BINARY_DIR}/utils/digikam_core_export.h" ) # NOTE: all this target dependencies must be private and not exported to prevent inherited dependencies on external plugins. target_link_libraries(digikamcore PRIVATE Qt5::Core Qt5::Gui Qt5::Xml Qt5::XmlPatterns Qt5::Widgets Qt5::Sql Qt5::PrintSupport Qt5::Concurrent KF5::Solid KF5::WindowSystem KF5::ConfigGui KF5::XmlGui KF5::I18n KF5::Service # Required by CImg which use pthread internally. ${CMAKE_THREAD_LIBS_INIT} ${EXPAT_LIBRARY} ${LCMS2_LIBRARIES} # filters ${TIFF_LIBRARIES} PNG::PNG ${JPEG_LIBRARIES} exiv2lib ${FFMPEG_LIBRARIES} ${OPENMP_LDFLAGS} opencv_core opencv_objdetect opencv_imgproc opencv_imgcodecs opencv_dnn opencv_flann ) if(ENABLE_QWEBENGINE) target_link_libraries(digikamcore PRIVATE Qt5::WebEngineWidgets ) else() target_link_libraries(digikamcore PRIVATE Qt5::WebKitWidgets ) endif() if(ENABLE_DBUS) target_link_libraries(digikamcore PRIVATE Qt5::DBus ) endif() if(KF5IconThemes_FOUND) target_link_libraries(digikamcore PRIVATE KF5::IconThemes ) endif() if(KF5KIO_FOUND) target_link_libraries(digikamcore PRIVATE KF5::KIOCore KF5::KIOWidgets ) endif() if(KF5Notifications_FOUND) target_link_libraries(digikamcore PRIVATE KF5::Notifications ) endif() if(KF5NotifyConfig_FOUND) target_link_libraries(digikamcore PRIVATE KF5::NotifyConfig ) endif() if(Marble_FOUND) target_link_libraries(digikamcore PRIVATE ${MARBLE_LIBRARIES} ) endif() if(X11_FOUND) target_link_libraries(digikamcore PRIVATE Qt5::X11Extras ${X11_LIBRARIES} ) endif() if(Jasper_FOUND) target_link_libraries(digikamcore PRIVATE ${JASPER_LIBRARIES} ) endif() # LibLqr-1 library rules for content-aware filter if(Lqr-1_FOUND) target_link_libraries(digikamcore PRIVATE ${LQR-1_LIBRARIES} ) endif() if(LensFun_FOUND) target_link_libraries(digikamcore PRIVATE ${LENSFUN_LIBRARIES} ) endif() if(ImageMagick_FOUND) target_link_libraries(digikamcore PRIVATE ${ImageMagick_LIBRARIES} ) endif() # for nrfilter if(OpenCV_FOUND) target_link_libraries(digikamcore PRIVATE ${OpenCV_LIBRARIES} ) endif() if(KF5FileMetaData_FOUND) target_link_libraries(digikamcore PRIVATE KF5::FileMetaData ) endif() if(KF5AkonadiContact_FOUND) target_link_libraries(digikamcore PRIVATE KF5::AkonadiContact ) endif() if(APPLE) target_link_libraries(digikamcore PRIVATE /System/Library/Frameworks/AppKit.framework ) endif() if(WIN32) target_link_libraries(digikamcore PRIVATE wsock32 ws2_32 ) endif() if(CMAKE_SYSTEM_NAME STREQUAL FreeBSD) target_link_libraries(digikamcore PRIVATE ${KVM_LIBRARY} ) endif() if(ENABLE_MEDIAPLAYER) target_link_libraries(digikamcore PRIVATE ${QTAV_LIBRARIES} ) endif() ### Install Rules ############################################################################################################### install(TARGETS digikamcore EXPORT DigikamCoreConfig ${INSTALL_TARGETS_DEFAULT_ARGS}) install(EXPORT DigikamCoreConfig DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/DigikamCore" NAMESPACE Digikam::) write_basic_package_version_file(${CMAKE_CURRENT_BINARY_DIR}/DigikamCoreConfigVersion.cmake VERSION ${DIGIKAM_VERSION_SHORT} COMPATIBILITY SameMajorVersion) install(FILES ${CMAKE_CURRENT_BINARY_DIR}/DigikamCoreConfigVersion.cmake DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/DigikamCore") diff --git a/core/app/DigikamGuiTarget.cmake b/core/app/DigikamGuiTarget.cmake index 4ab872aa29..44d02d6efd 100644 --- a/core/app/DigikamGuiTarget.cmake +++ b/core/app/DigikamGuiTarget.cmake @@ -1,349 +1,349 @@ # # Copyright (c) 2010-2020 by Gilles Caulier, # Copyright (c) 2015 by Veaceslav Munteanu, # # Redistribution and use is allowed according to the terms of the BSD license. # For details see the accompanying COPYING-CMAKE-SCRIPTS file. ### digiKam GUI object library ################################################################################################## if(ENABLE_DBUS) qt5_add_dbus_adaptor(digikamadaptor_SRCS - main/org.kde.digikam.xml - main/digikamapp.h + ${CMAKE_CURRENT_SOURCE_DIR}/main/org.kde.digikam.xml + ${CMAKE_CURRENT_SOURCE_DIR}/main/digikamapp.h Digikam::DigikamApp) endif() set(libdigikamgui_SRCS - main/digikamapp.cpp - main/digikamapp_solid.cpp - main/digikamapp_camera.cpp - main/digikamapp_import.cpp - main/digikamapp_config.cpp - main/digikamapp_tools.cpp - main/digikamapp_setup.cpp - - date/dpopupframe.cpp - date/ddateedit.cpp - date/ddatetable.cpp - date/ddatetable_p.cpp - date/ddatepicker.cpp - date/ddatepicker_p.cpp - date/ddatetimeedit.cpp - date/ddatepickerpopup.cpp - date/datefolderview.cpp - date/monthwidget.cpp - date/timelinewidget.cpp - - dragdrop/importdragdrop.cpp - dragdrop/albumdragdrop.cpp - dragdrop/ddragobjects.cpp - dragdrop/itemdragdrop.cpp - dragdrop/tagdragdrop.cpp - - filters/filtersidebarwidget.cpp - filters/tagfilterview.cpp - - items/delegate/digikamitemdelegate.cpp - items/delegate/itemdelegate.cpp - items/delegate/itemfacedelegate.cpp - items/views/digikamitemview.cpp - items/views/digikamitemview_p.cpp - items/views/itemcategorizedview.cpp - items/thumbbar/itemthumbnailbar.cpp - items/thumbbar/itemthumbnaildelegate.cpp - items/overlays/assignnameoverlay.cpp - items/overlays/facerejectionoverlay.cpp - items/overlays/groupindicatoroverlay.cpp - items/overlays/itemfullscreenoverlay.cpp - items/overlays/itemratingoverlay.cpp - items/overlays/itemrotationoverlay.cpp - items/overlays/itemcoordinatesoverlay.cpp - items/overlays/itemselectionoverlay.cpp - items/utils/itemviewutilities.cpp - items/utils/tooltipfiller.cpp - items/utils/contextmenuhelper.cpp - items/utils/groupingviewimplementation.cpp - items/utils/itemcategorydrawer.cpp - - views/tableview/tableview.cpp - views/tableview/tableview_model.cpp - views/tableview/tableview_shared.cpp - views/tableview/tableview_treeview.cpp - views/tableview/tableview_treeview_delegate.cpp - views/tableview/tableview_column_configuration_dialog.cpp - views/tableview/tableview_column_audiovideo.cpp - views/tableview/tableview_column_file.cpp - views/tableview/tableview_column_geo.cpp - views/tableview/tableview_column_digikam.cpp - views/tableview/tableview_column_item.cpp - views/tableview/tableview_column_photo.cpp - views/tableview/tableview_column_thumbnail.cpp - views/tableview/tableview_columnfactory.cpp - views/tableview/tableview_selection_model_syncer.cpp - - views/stack/itemiconview.cpp - views/stack/itemiconview_albums.cpp - views/stack/itemiconview_groups.cpp - views/stack/itemiconview_items.cpp - views/stack/itemiconview_search.cpp - views/stack/itemiconview_sidebars.cpp - views/stack/itemiconview_tags.cpp - views/stack/itemiconview_tools.cpp - views/stack/itemiconview_views.cpp - views/stack/itemiconview_zoom.cpp - views/stack/trashview.cpp - views/stack/stackedview.cpp - views/stack/welcomepageview.cpp - views/preview/itempreviewcanvas.cpp - views/preview/itempreviewview.cpp - - views/sidebar/albumfolderviewsidebarwidget.cpp - views/sidebar/datefolderviewsidebarwidget.cpp - views/sidebar/timelinesidebarwidget.cpp - views/sidebar/searchsidebarwidget.cpp - views/sidebar/fuzzysearchsidebarwidget.cpp - views/sidebar/labelssidebarwidget.cpp - views/sidebar/peoplesidebarwidget.cpp - views/sidebar/tagviewsidebarwidget.cpp - views/sidebar/sidebarwidget.cpp - - views/utils/dmodelfactory.cpp - views/utils/componentsinfodlg.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/main/digikamapp.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/main/digikamapp_solid.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/main/digikamapp_camera.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/main/digikamapp_import.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/main/digikamapp_config.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/main/digikamapp_tools.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/main/digikamapp_setup.cpp + + ${CMAKE_CURRENT_SOURCE_DIR}/date/dpopupframe.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/date/ddateedit.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/date/ddatetable.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/date/ddatetable_p.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/date/ddatepicker.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/date/ddatepicker_p.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/date/ddatetimeedit.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/date/ddatepickerpopup.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/date/datefolderview.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/date/monthwidget.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/date/timelinewidget.cpp + + ${CMAKE_CURRENT_SOURCE_DIR}/dragdrop/importdragdrop.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/dragdrop/albumdragdrop.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/dragdrop/ddragobjects.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/dragdrop/itemdragdrop.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/dragdrop/tagdragdrop.cpp + + ${CMAKE_CURRENT_SOURCE_DIR}/filters/filtersidebarwidget.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/filters/tagfilterview.cpp + + ${CMAKE_CURRENT_SOURCE_DIR}/items/delegate/digikamitemdelegate.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/items/delegate/itemdelegate.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/items/delegate/itemfacedelegate.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/items/views/digikamitemview.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/items/views/digikamitemview_p.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/items/views/itemcategorizedview.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/items/thumbbar/itemthumbnailbar.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/items/thumbbar/itemthumbnaildelegate.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/items/overlays/assignnameoverlay.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/items/overlays/facerejectionoverlay.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/items/overlays/groupindicatoroverlay.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/items/overlays/itemfullscreenoverlay.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/items/overlays/itemratingoverlay.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/items/overlays/itemrotationoverlay.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/items/overlays/itemcoordinatesoverlay.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/items/overlays/itemselectionoverlay.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/items/utils/itemviewutilities.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/items/utils/tooltipfiller.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/items/utils/contextmenuhelper.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/items/utils/groupingviewimplementation.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/items/utils/itemcategorydrawer.cpp + + ${CMAKE_CURRENT_SOURCE_DIR}/views/tableview/tableview.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/views/tableview/tableview_model.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/views/tableview/tableview_shared.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/views/tableview/tableview_treeview.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/views/tableview/tableview_treeview_delegate.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/views/tableview/tableview_column_configuration_dialog.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/views/tableview/tableview_column_audiovideo.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/views/tableview/tableview_column_file.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/views/tableview/tableview_column_geo.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/views/tableview/tableview_column_digikam.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/views/tableview/tableview_column_item.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/views/tableview/tableview_column_photo.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/views/tableview/tableview_column_thumbnail.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/views/tableview/tableview_columnfactory.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/views/tableview/tableview_selection_model_syncer.cpp + + ${CMAKE_CURRENT_SOURCE_DIR}/views/stack/itemiconview.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/views/stack/itemiconview_albums.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/views/stack/itemiconview_groups.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/views/stack/itemiconview_items.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/views/stack/itemiconview_search.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/views/stack/itemiconview_sidebars.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/views/stack/itemiconview_tags.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/views/stack/itemiconview_tools.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/views/stack/itemiconview_views.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/views/stack/itemiconview_zoom.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/views/stack/trashview.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/views/stack/stackedview.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/views/stack/welcomepageview.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/views/preview/itempreviewcanvas.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/views/preview/itempreviewview.cpp + + ${CMAKE_CURRENT_SOURCE_DIR}/views/sidebar/albumfolderviewsidebarwidget.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/views/sidebar/datefolderviewsidebarwidget.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/views/sidebar/timelinesidebarwidget.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/views/sidebar/searchsidebarwidget.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/views/sidebar/fuzzysearchsidebarwidget.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/views/sidebar/labelssidebarwidget.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/views/sidebar/peoplesidebarwidget.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/views/sidebar/tagviewsidebarwidget.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/views/sidebar/sidebarwidget.cpp + + ${CMAKE_CURRENT_SOURCE_DIR}/views/utils/dmodelfactory.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/views/utils/componentsinfodlg.cpp ${digikamadaptor_SRCS} ) if(${Marble_FOUND}) set(libdigikamgui_SRCS ${libdigikamgui_SRCS} - views/stack/mapwidgetview.cpp - views/sidebar/gpssearchsidebarwidget.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/views/stack/mapwidgetview.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/views/sidebar/gpssearchsidebarwidget.cpp ) endif() # FIXME # #if(ENABLE_DIGIKAM_MODELTEST) # message(STATUS "Modeltest enabled") # # set(libdigikamgui_SRCS ${libdigikamgui_SRCS} # modeltest/modeltest.cpp) # # add_definitions(-DENABLE_DIGIKAM_MODELTEST) #endif() add_library(gui_digikam_obj OBJECT ${libdigikamgui_SRCS} ) target_compile_definitions(gui_digikam_obj PRIVATE digikamgui_EXPORTS ) ### digiKam GUI shared library objects declaration ############################################################################## set(DIGIKAMGUI_OBJECTS $ # Libs $ $ $ $ $ $ $ $ $ $ $ $ $ $ # Utilities $ $ $ $ $ $ $ $ $ $ $ $ ) if(${Marble_FOUND}) set(DIGIKAMGUI_OBJECTS ${DIGIKAMGUI_OBJECTS} $ ) endif() ### digiKam GUI shared library target ########################################################################################### add_library(digikamgui SHARED ${DIGIKAMGUI_OBJECTS} ) set_target_properties(digikamgui PROPERTIES VERSION ${DIGIKAM_VERSION_SHORT} SOVERSION ${DIGIKAM_VERSION_SHORT} ) target_compile_definitions(digikamgui PRIVATE digikamcore_EXPORTS ) add_dependencies(digikamgui digikamcore digikamdatabase) # All codes from this target are exported with digikam_gui_export.h header and DIGIKAM_GUI_EXPORT macro. generate_export_header(digikamgui BASE_NAME digikam_gui EXPORT_FILE_NAME "${CMAKE_CURRENT_BINARY_DIR}/utils/digikam_gui_export.h" ) if(WIN32) set_target_properties(digikamgui PROPERTIES COMPILE_FLAGS -DJPEG_STATIC) endif() # NOTE: all this target dependencies must be private and not exported to prevent inherited dependencies on external plugins. target_link_libraries(digikamgui PRIVATE Qt5::Core Qt5::Gui Qt5::Widgets Qt5::Sql Qt5::PrintSupport KF5::XmlGui KF5::Solid KF5::ConfigCore KF5::ConfigGui KF5::Service KF5::WindowSystem KF5::I18n digikamcore digikamdatabase opencv_core opencv_objdetect opencv_imgproc opencv_imgcodecs opencv_dnn opencv_flann ) if(ENABLE_QWEBENGINE) target_link_libraries(digikamgui PRIVATE Qt5::WebEngineWidgets ) else() target_link_libraries(digikamgui PRIVATE Qt5::WebKitWidgets ) endif() if(ENABLE_DBUS) target_link_libraries(digikamgui PRIVATE Qt5::DBus ) endif() if(KF5IconThemes_FOUND) target_link_libraries(digikamgui PRIVATE KF5::IconThemes ) endif() if(KF5KIO_FOUND) target_link_libraries(digikamgui PRIVATE KF5::KIOWidgets ) endif() if(${Marble_FOUND}) target_link_libraries(digikamgui PRIVATE ${MARBLE_LIBRARIES} ) endif() if(APPLE) target_link_libraries(digikamgui PRIVATE /System/Library/Frameworks/AppKit.framework ) endif() if(NOT WIN32) # To link under Solaris (see bug #274484) target_link_libraries(digikamgui PRIVATE ${MATH_LIBRARY} ) endif() if(Gphoto2_FOUND) # See bug #258931: libgphoto2 library must be the last arg for linker. # See bug #268267 : digiKam need to be linked to libusb to prevent crash # at gphoto2 init if opencv is linked with libdc1394. Libusb linking rules are # add to gphoto2 linking rules by Gphoto2 cmake detection script. target_link_libraries(digikamgui PRIVATE ${GPHOTO2_LIBRARIES} ) endif() ### Install Rules ############################################################################################################### install(TARGETS digikamgui EXPORT DigikamGuiConfig ${INSTALL_TARGETS_DEFAULT_ARGS}) install(EXPORT DigikamGuiConfig DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/DigikamGui" NAMESPACE Digikam::) write_basic_package_version_file(${CMAKE_CURRENT_BINARY_DIR}/DigikamGuiConfigVersion.cmake VERSION ${DIGIKAM_VERSION_SHORT} COMPATIBILITY SameMajorVersion) install(FILES ${CMAKE_CURRENT_BINARY_DIR}/DigikamGuiConfigVersion.cmake DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/DigikamGui") diff --git a/core/app/date/dpopupframe.cpp b/core/app/date/dpopupframe.cpp index 4f06ada9b8..95e9e9c792 100644 --- a/core/app/date/dpopupframe.cpp +++ b/core/app/date/dpopupframe.cpp @@ -1,246 +1,246 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 1997-04-21 * Description : Frame with popup menu behavior. * * Copyright (C) 2011-2020 by Gilles Caulier * Copyright (C) 1997 by Tim D. Gilman * Copyright (C) 1998-2001 by Mirko Boehm * Copyright (C) 2007 by John Layt * * 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, 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. * * ============================================================ */ #include "dpopupframe.h" // Qt includes #include #include #include #include #include namespace Digikam { class Q_DECL_HIDDEN DPopupFrame::Private { public: explicit Private(DPopupFrame* const qq); ~Private(); public: DPopupFrame* q; /** * The result. It is returned from exec() when the popup window closes. */ int result; /** * The only subwidget that uses the whole dialog window. */ QWidget* main; class OutsideClickCatcher; OutsideClickCatcher* outsideClickCatcher; }; class Q_DECL_HIDDEN DPopupFrame::Private::OutsideClickCatcher : public QObject { public: explicit OutsideClickCatcher(QObject* const parent = nullptr) : QObject(parent), m_popup(nullptr) { } ~OutsideClickCatcher() { } void setPopupFrame(DPopupFrame* const popup) { m_popup = popup; popup->installEventFilter(this); } - bool eventFilter(QObject* object, QEvent* event) Q_DECL_OVERRIDE + bool eventFilter(QObject* object, QEvent* event) override { Q_UNUSED(object); // To catch outside clicks, it is sufficient to check for // hide events on Qt::Popup type widgets if (event->type() == QEvent::Hide && m_popup) { // do not set d->result here, because the popup // hides itself after leaving the event loop. emit m_popup->leaveModality(); } return false; } public: DPopupFrame* m_popup; }; DPopupFrame::Private::Private(DPopupFrame* const qq) : q(qq), result(0), // rejected main(nullptr), outsideClickCatcher(new OutsideClickCatcher) { outsideClickCatcher->setPopupFrame(q); } DPopupFrame::Private::~Private() { delete outsideClickCatcher; } DPopupFrame::DPopupFrame(QWidget* const parent) : QFrame(parent, Qt::Popup), d(new Private(this)) { setFrameStyle(QFrame::Box | QFrame::Raised); setMidLineWidth(2); } DPopupFrame::~DPopupFrame() { delete d; } void DPopupFrame::keyPressEvent(QKeyEvent* e) { if (e->key() == Qt::Key_Escape) { d->result = 0; // rejected emit leaveModality(); } } void DPopupFrame::hideEvent(QHideEvent *e) { QFrame::hideEvent(e); } void DPopupFrame::close(int r) { d->result = r; emit leaveModality(); } void DPopupFrame::setMainWidget(QWidget* const m) { d->main = m; if (d->main) { resize(d->main->width() + 2 * frameWidth(), d->main->height() + 2 * frameWidth()); } } void DPopupFrame::resizeEvent(QResizeEvent* e) { Q_UNUSED(e); if (d->main) { d->main->setGeometry(frameWidth(), frameWidth(), width() - 2 * frameWidth(), height() - 2 * frameWidth()); } } void DPopupFrame::popup(const QPoint& p) { // Make sure the whole popup is visible. QScreen* screen = qApp->primaryScreen(); if (QWidget* const widget = nativeParentWidget()) { if (QWindow* const window = widget->windowHandle()) { screen = window->screen(); } } QRect desktopGeometry = screen->geometry(); int x = p.x(); int y = p.y(); int w = width(); int h = height(); if ((x + w) > (desktopGeometry.x() + desktopGeometry.width())) { x = desktopGeometry.width() - w; } if ((y + h) > (desktopGeometry.y() + desktopGeometry.height())) { y = desktopGeometry.height() - h; } if (x < desktopGeometry.x()) { x = 0; } if (y < desktopGeometry.y()) { y = 0; } // Pop the thingy up. move(x, y); show(); d->main->setFocus(); } int DPopupFrame::exec(const QPoint& p) { popup(p); repaint(); d->result = 0; // rejected QEventLoop eventLoop; connect(this, &DPopupFrame::leaveModality, &eventLoop, &QEventLoop::quit); eventLoop.exec(); hide(); return d->result; } int DPopupFrame::exec(int x, int y) { return exec(QPoint(x, y)); } } // namespace Digikam diff --git a/core/app/main/org.kde.digikam.appdata.xml b/core/app/main/org.kde.digikam.appdata.xml index b25a8829f9..d6dc67b9a3 100644 --- a/core/app/main/org.kde.digikam.appdata.xml +++ b/core/app/main/org.kde.digikam.appdata.xml @@ -1,533 +1,543 @@ org.kde.digikam.desktop CC0-1.0 GPL-2.0+ digiKam دِجي‌كام digiKam digiKam digiKam digiKam digiKam digiKam digiKam digiKam digiKam digiKam digiKam digiKam digiKam digiKam digiKam digiKam digiKam digiKam ਡਿਜਿਕੈਮ digiKam digiKam digiKam digiKam digiKam digiKam Digikam digiKam digiKam xxdigiKamxx digiKam digiKam Photo Management Program Programa per a la gestió de les fotografies Programa per a la gestió de les fotografies Program pro správu fotografií Fotoverwaltung Πρόγραμμα διαχείρισης φωτογραφιών Photo Management Program Programa de gestión de fotos Fotohalduse rakendus Valokuvahallintaohjelma Application de gestion de photos Xestor de álbums de fotos Program Pengelolaan Foto Programma di gestione delle foto 사진 관리 프로그램 Fotobeheerprogramma Fotohandsamingsprogram ਫੋਟੋ ਇੰਤਜ਼ਾਮ ਲਈ ਪਰੋਗਰਾਮ Zarządzanie zdjęciami Programa de Gestão de Fotografias Programa de gerenciamento de fotos Программа для управления фотографиями Program za upravljanje fotografij Fotohanteringsprogram Resim Düzenleme Programı Програма для керування фотографіями xxPhoto Management Programxx 照片管理程序 圖片管理程式

digiKam is an advanced open-source digital photo management application that runs on Linux, Windows, and MacOS. The application provides a comprehensive set of tools for importing, managing, editing, and sharing photos and raw files. You can use digiKam’s import capabilities to easily transfer photos, raw files, and videos directly from your camera and external storage devices (SD cards, USB disks, etc.). The application allows you to configure import settings and rules that process and organize imported items on-the-fly. +

+

digiKam organizes photos, raw files, and videos into albums. But the application also features powerful tagging tools that allow you to assign tags, ratings, and labels to photos and raw files. You can then use filtering functionality to quickly find items that match specific criteria. +

+

In addition to filtering functionality, digiKam features powerful searching capabilities that let you search the photo library by a wide range of criteria. You can search photos by tags, labels, rating, data, location, and even specific EXIF, IPTC, or XMP metadata. You can also combine several criteria for more advanced searches. digiKam rely on Exiv2 library to handle metadata tag contents from files to populate the photo library. +

+

digiKam can handle raw files, and the application uses the excellent LibRaw library for decoding raw files. The library is actively maintained and regularly updated to include support for the latest camera models. digiKam can also manage video files for cataloging purpose, and the application uses the couple FFmpeg and QtAv libraries to extract metadata and play media. +

+

The application provides a comprehensive set of editing tools. This includes basic tools for adjusting colors, cropping, and sharpening as well as advanced tools for, curves adjustment, panorama stitching, and much more. A special tool based on Lensfun library permit to apply lens corrections automatically on images. +

+

Extended functionality in digiKam is implemented via a set of tools based of plugins mechanism (named DPlugins for digiKam Plugins). Plugins can be written to import and export contents to remote web-services, add new features to edit image, and batch process photo.

digiKam je napredna aplikacija za upravljanje digitalnim fotografijama, što čini uvoz i organizaciju digitalnih fotografija "klikom". Fotografije su organizovane u albumima koji mogu biti sortirani hronološki, po rasporedu foldera ili po korisničkim kolekcijama.

El digiKam és una avançada aplicació per a gestionar les fotografies digitals, la qual fa que la importació i organització de les fotografies digitals sigui una «tasca fàcil». Les fotografies s'organitzen en àlbums, els quals es poden ordenar cronològicament, per disposició de la carpeta o per col·leccions personalitzades.

El digiKam és una avançada aplicació per a gestionar les fotografies digitals, la qual fa que la importació i organització de les fotografies digitals siga una «tasca fàcil». Les fotografies s'organitzen en àlbums, els quals es poden ordenar cronològicament, per disposició de la carpeta o per col·leccions personalitzades.

digiKam je pokročilý správce fotografií, se kterým snadno zvládnete import a uspořádání digitálních fotografií. Obrázky jsou organizovány v albech, která je možné seřadit podle data, podle struktury složek, nebo podle uživatelských sbírek.

digiKam ist eine leistungsfähige Anwendung zur Verwaltung von Digitalbildern. Es stellt eine einfache Oberfläche zum Importieren und Organisieren von Digitalbildern zur Verfügung. Die Bilder werden in Alben organisiert, die chronologisch oder nach Ordnern oder als benutzerdefinierte Sammlungen sortiert werden können.

Το digiKam είναι μια προηγμένη εφαρμογή διαχείρισης φωτογραφιών, που κάνει πανεύκολη την εισαγωγή και οργάνωση φωτογραφιών. Οι φωτογραφίες οργανώνονται σε άλμπουμ με χρονολογική ταξινόμηση, ανάλογα με τη διάταξη του φακέλου ή τις προσαρμοσμένες συλλογές.

digiKam is an advanced digital photo management application, which makes importing and organizing digital photos a "snap". The photos are organized in albums which can be sorted chronologically, by folder layout or by custom collections.

digiKam es una aplicación avanzada de gestión de fotos digitales que facilita la importación y la organización de fotos digitales. Las fotos se organizan en álbumes, que se pueden ordenar cronológicamente, por esquemas de carpetas o por colecciones personalizadas.

digiKam on võimas digitaalfotode haldamise rakendus, mis muudab digitaalfotode importimise ja korraldamise imelihtsaks. Fotod paigutatakse albumitesse, mida saab sortida aja, kataloogide või kohandatud kogude järgi.

digiKam on edistynyt digikuvien hallintaohjelma, joka tekee digitaalikuvien tuonnista ja järjestelystä vaivatonta. Kuva järjestetään albumeiksi, jotka voi lajitella ajan tai kansioasettelun mukaan tai mukautetuiksi kokoelmiksi.

digiKam est une application de gestion de photos avancée, qui rend l'import et l'organisation des photos comme un jeu d'enfant. Les photos sont organisées dans des albums qui peuvent être triés de manière chronologique, selon l'organisation des dossiers, ou par collections personnalisées.

digiKam é unha aplicación de administración avanzada de fotografía dixital que facilita a importación e organización de fotografías dixitais. As fotos organízanse en álbums, que poden ordenarse de maneira cronolóxica, por distribución do cartafol ou por coleccións personalizadas.

digiKam adalah sebuah aplikasi pengelola foto digital dimana pengimporan dan pengorganisasian foto digital "snap". Foto yang diorganisir di album yang bisa disimpan secara kronologis, menurut tataletak folder atau berdasarkan koleksi kustom.

digiKam è un'applicazione per la gestione avanzata delle fotografie digitali, che permette di importare e organizzare le proprie foto in pochi clic. Le foto sono organizzate in album, i quali possono essere ordinati per data, per disposizione delle cartelle o per raccolte personalizzate.

digiKam은 디지털 사진을 가져오고 관리하는 고급 디지털 사진 관리 프로그램입니다. 사진은 시간순, 폴더별 등 다양한 방법으로 정리할 수 있습니다.

digiKam is een geavanceerde toepassing voor beheer van digitale foto's, die importeren en organiseren van digitale foto's een "fluitje van een cent" maakt. De foto's zijn georganiseerd in albums die chronologisch, op indeling van mappen of op eigen verzamelingen gesorteerd kunnen zijn.

digiKam er eit avansert fotohandsamingsprogram som gjer det kjapt og enkelt å importera og organisera digitale bilete. Bileta vert ordna i album som kan sorterast kronologisk, etter mappestruktur eller i eigendefinerte samlingar.

digiKam jest zaawansowanym programem do zarządzania cyfrowymi zdjęciami, który czyni importowanie i zarządzanie cyfrowymi zdjęciami "drobnostką". Zdjęcia są urządzone w albumy, które można przechowywać chronologicznie, według układu katalogów lub według własnych zbiorów.

O digiKam é uma aplicação de gestão de fotografias digitais, o que torna a importação e a organização de fotografias digitais extremamente simples. As fotos são organizadas em álbuns, os quais podem ser ordenados de forma cronológica, por disposição das pastas ou com colecções personalizadas.

DigiKam é um aplicativo para gerenciamento de fotos digitais avançado, que torna muito fácil a importação e organização das suas fotos digitais. As fotos são organizadas em álbuns e ordenadas de forma cronológica, por layout de pastas ou por coleções personalizadas.

digiKam — приложение для управления фотографиями с расширенным набором возможностей. Фотографии сохраняются в альбомах, что позволяет упорядочивать их в хронологическом порядке, по иерархии папок, а также в коллекции, определённые пользователем.

digiKam je pokročilá aplikácia na správu digitálnych fotografií, ktorá zjednodušuje import a správu digitálnych fotografií. Fotografie sú organizované v albumoch, ktoré sa dajú triediť chronologicky, podľa rozloženia priečinkov alebo podľa vlastných zbierok.

digiKam je napreden program za upravljanje digitalnih fotografij, ki močno poenostavi uvažanje in organiziranje digitalnih fotografij. Fotografije so organizirane v albumih, ki jih lahko razvrstite po času, razporedu map ali zbirkah po meri.

Digikam är ett avancerat program för hantering av digitala foton, som gör det enkelt att importera och organisera digitala foton. Fotona organiseras i album som kan sorteras kronologiskt, enligt katalogutläggning eller enligt egna samlingar.

digiKam, resimlerinizi içe aktarma ve düzenlemeyi aniden yapmanızı sağlayan bir gelişmiş resim yönetme uygulamasıdır. Resimleriniz albümlerde düzenlenir ve bu albümler tarihe göre sıralanıp, dizin düzeninde ya da özelleşmiş koleksiyonlarda gösterilebilir.

digiKam — програма з розширеними можливостями щодо керування цифровими фотографіями, яка робить імпортування і впорядкування цифрових фотографій зовсім простою справою". Програма впорядковує фотографії за альбомами, які можна розташовувати у вікні програму у хронологічному порядку, за теками або за нетиповими збірками.

xxdigiKam is an advanced digital photo management application, which makes importing and organizing digital photos a "snap". The photos are organized in albums which can be sorted chronologically, by folder layout or by custom collections.xx

digiKam 是一种先进的数字照片管理应用程序,它使导入和组织数字照片变得轻而易举。照片按照时间顺序、文件夹布局或自定义集合来排列。

Features:

الميزات:

Svojstva:

Característiques:

Característiques:

Vlastnosti:

Funktioner:

Funktionen:

Χαρακτηριστικά:

Features:

Funciones:

Omadused:

Ominaisuuksia:

Fonctionnalités :

Funcionalidades:

Fitur:

Caratteristiche:

기능:

Mogelijkheden:

Funksjonar:

ਫੀਚਰ:

Możliwości:

Funcionalidades:

Funcionalidades:

Возможности:

Funkcie:

Zmožnosti:

Funktioner:

Özellikler:

Можливості:

xxFeatures:xx

功能:

功能:

  • Organization of photos in albums and sub-albums (with tags and comments support)
  • تنظيم الصّور في ألبومات وألبومات فرعيّة (بدعم الوسوم والتّعليقات)
  • Organizacija fotografija u albume i podalbume (sa podrškom za označavanje i komentarisanje)
  • Organització de les fotografies en àlbums i subàlbums (amb implementació per a les etiquetes i comentaris)
  • Organització de les fotografies en àlbums i subàlbums (amb implementació per a les etiquetes i comentaris)
  • Organizace fotografií do alb a podřízených alb s podporou značek a komentářů
  • Organisation von Fotos in Alben und Unteralben mit Unterstützung für Stichwörter und Kommentare
  • Οργάνωση φωτογραφιών σε άλμπουμ και υπο-άλμπουμ (με υποστήριξη για ετικέτες και σχόλια)
  • Organisation of photos in albums and sub-albums (with tags and comments support)
  • Organización de fotos en álbumes y álbumes anidados (con uso de etiquetas y comentarios)
  • Fotode paigutamine albumitesse ja alamalbumitesse (toetatud on sildid ja kommentaarid)
  • Valokuvien järjestely albumeiksi ja alialbumeiksi (tunniste- ja kommenttituella)
  • Organisation de photos en albums et sous-albums (avec prise en charge des étiquettes et des commentaires)
  • Organización de fotos en álbums e subálbums (con funcionalidade de etiquetas e comentarios).
  • Organisasi foto dalam album dan sub-album (dengan dukungan tag dan komentar)
  • Organizzazione di foto in album e sotto-album (con supporto per le etichette e i commenti)
  • 앨범 및 하위 앨범으로 사진 정렬(태그와 주석 지원)
  • Organiseren van foto's in albums en sub-albums (met tags en ondersteuning van commentaar)
  • Organisering av bilete i album og underalbum (med støtte for merkelappar og kommentarar)
  • Grupowanie zdjęć w albumy i podalbumów (z obsługą znaczników i komentarzy)
  • Organização das fotografias por álbuns e sub-álbuns (com o suporte para marcas e comentários)
  • Organização de fotos em álbuns e subálbuns (com suporte a etiquetas e comentários)
  • Упорядочивание фотографий с использованием иерархических альбомов, меток и комментариев;
  • Organizácia fotiek v albumoch a podalbumoch (so značkami a komentármi)
  • Organizacija fotografij v albumih in pod-albumih (s podporo oznakam in opombam)
  • Organisation av foton i album och delalbum (med stöd för etiketter och kommentarer)
  • Resimler albüm ya da alt albümlerde düzenleme (etiket ve yorum desteğiyle)
  • упорядковування фотографій за альбомами і підальбомами з підтримкою міток та коментарів;
  • xxOrganization of photos in albums and sub-albums (with tags and comments support)xx
  • 在相册和子相册中组织照片 (支持标签和注释)
  • Support for Exif, Iptc, Xmp, Makernotes
  • Podrška za Exif, Iptc, Xmp, Makernotes
  • Implementació per a notes EXIF, IPTC, XMP i del fabricant
  • Implementació per a notes EXIF, IPTC, XMP i del fabricant
  • Podpora pro Exif, Iptc, Xmp, Makernotes
  • Unterstützung für Exif, Iptc, Xmp und Herstellerbemerkungen
  • Υποστήριξη για Exif, Iptc, Xmp, Makernotes
  • Support for Exif, Iptc, Xmp, Makernotes
  • Uso de Exif, Iptc, Xmp y notas del fabricante
  • Exif'i, Iptc, Xmp, Makernotes'i toetus
  • Exif-, IPTC-, XMP ja Makernotes-tuki
  • Prise en charge des données EXIF, IPTC, XMP, MakerNotes
  • Compatíbel con Exif, Iptc, Xmp e Makernotes.
  • Mendukung Exif. Iptc, Xmp, Makernotes
  • Supporto per Exif, Iptc, Xmp, Makernotes
  • EXIF, IPTC, XMP, 메이커 노트 지원
  • Ondersteuning voor Exif, Iptc, Xmp, Makernotes
  • Støtte for Exif, Iptc, Xmp og Makernotes
  • Obsługa Exif, Iptc, Xmp, notatek twórcy
  • Suporte para o EXIF, IPTC, XMP e notas do fabricante
  • Suporte a EXIF, IPTC, XMP e Notas do Fabricante
  • Поддержка метаданных EXIF, IPTC, XMP и MakerNote;
  • Podpora pre Exif, Iptc, Xmp, Makernotes
  • Podpora Exif, Iptc, Xmp, Makernotes
  • Stöd för EXIF, IPTC, XMP, tillverkaranteckningar
  • Exif, Iptc, Xmp, Makernotes desteği
  • підтримка Exif, Iptc, Xmp, Makernotes;
  • xxSupport for Exif, Iptc, Xmp, Makernotesxx
  • 支持 Exif、Iptc、Xmp、Makernotes
  • 支援 EXIF、IPTC、XMP 和 Makernotes
  • SQLite or Mysql powered storage for the album contents and its metadata with advanced search tools
  • Emmagatzematge basat en SQLite o Mysql per als continguts dels àlbums i les seves metadades amb eines de cerca avançades
  • Emmagatzematge basat en SQLite o Mysql per als continguts dels àlbums i les seues metadades amb eines de cerca avançades
  • Ukládání informace o albech do databáze založené na SQLite nebo MySQL s pokročilými vyhledávacími nástroji
  • Αποθηκευτικός χώρος με SQLite ή Mysql για τα άλμπουμ και τα μεταδεδομένα τους με προηγμένα εργαλεία αναζήτησης
  • SQLite or Mysql powered storage for the album contents and its metadata with advanced search tools
  • Almacenamiento basado en SQLite o Mysql para los álbumes y sus metadatos con herramientas de búsqueda avanzadas
  • SQLite'i või Mysql'i baasil salvesti albumite sisule ja metaandmetele ühes võimsa otsingutööriistaga
  • SQLite- tai MySQL-tallennustila albumien sisällölle ja matatiedoille sekä edistyneet hakutyökalut
  • Stockage du contenu des albums et des métadonnées fonctionnant avec SQLite ou MySQL et des outils de recherche avancée
  • Almacenamento mediante SQLite ou MySQL para o contido dos álbums e os seus metadatos con ferramentas de busca avanzadas.
  • Penyimpanan yang diberdayakan SQLite atau Mysql untuk konten album dan metadatanya dengan alat pencarian tingkat-lanjut
  • Memorizzazione del contenuto degli album e dei suoi metadati fornita da SQLite o Mysql, con strumenti di ricerca avanzati
  • SQLite 및 MySQL 기반 앨범과 메타데이터 저장소 및 고급 검색 도구
  • Op SQLite of Mysql gebaseerde opslag voor de inhoud van albums en zijn metagegevens met geavanceerde zoekhulpmiddellen
  • SQLite- eller MySQL-basert lagring for album og metadata, med avanserte søkjeverktøy
  • Przechowalnia dla albumów i ich metadanych z zaawansowanymi narzędziami wyszukiwania jest napędzana SQLite lub Mysql
  • Armazenamento baseado em SQLite ou MySQL para o conteúdo dos álbuns e os seus meta-dados com ferramentas de pesquisa avançadas
  • Armazenamento baseado em SQLite ou MySQL para o conteúdo dos álbuns e seus metadados com ferramentas de pesquisa avançadas
  • Использование баз данных SQLite и MySQL для хранения метаданных и информации о содержимом альбомов позволяет выполнять расширенный поиск и отбор требуемых изображений;
  • Shramba SQLite ali Mysql za albume in metapodatke ter napredna iskalna orodja
  • Lagring driven av SQLite eller MySQL för album och deras metadata med avancerade sökverktyg
  • Albüm içeriği ve meta verileri için gelişmiş arama araçlarıyla, SQLite veya Mysql destekli depolama
  • сховище даних на основі SQLite або MySQL для вмісту альбомів та метаданих із розширеними можливостями пошуку;
  • xxSQLite or Mysql powered storage for the album contents and its metadata with advanced search toolsxx
  • SQLite 或 Mysql 为相册内容及其元数据提供高级搜索工具的存储
  • Support for filtering and sorting albums
  • دعم ترشيح الألبومات وفرزها
  • Podrška za filtriranje i sortiranje albuma
  • Implementació pel filtratge i classificació dels àlbums
  • Implementació pel filtratge i classificació dels àlbums
  • Podpora řazení a filtrování alb
  • Unterstützung für das Filtern und Sortieren von Alben
  • Υποστήριξη για φιλτράρισμα και ταξινόμηση των άλμπουμ
  • Support for filtering and sorting albums
  • Implementación de filtrado y ordenación de álbumes
  • Albumite filtreerimise ja sortimise toetus
  • Albumien suodatuksen ja lajittelun tuki
  • Prise en charge du filtrage et du tri des albums
  • Permite filtrar e ordenar os álbums.
  • Mendukung pemfilteran dan pengurutan album
  • Supporto per il filtraggio e l'ordinamento degli album
  • 앨범 필터링 및 정렬
  • Ondersteuning voor filtering en sorteren van albums
  • Støtte for filtrering og sortering av album
  • Obsługa filtrowania i sortowania albumów
  • Suporte para filtrar e ordenar os álbuns
  • Suporte a filtragem e ordenação de álbuns
  • Отбор и сортировка альбомов;
  • Podpora pre filtrovanie a triedenie albumov
  • Podpora filtriranju in razvrščanju albumov
  • Stöd för filtrering och sortering av album
  • Albümleri filtreleme ve sıralama desteği
  • підтримка фільтрування та упорядковування альбомів;
  • xxSupport for filtering and sorting albumsxx
  • 支持筛选和排序相册
  • 支援篩選和排序相簿
  • Import from more than 1100 digital camera devices
  • الاستيراد من أكثر من 1100 كمرة رقميّة
  • Uvoz iz više od 1100 digitalnih kamera uređaja
  • Importa des de més de 1.100 dispositius de càmeres digitals
  • Importa des de més de 1.100 dispositius de càmeres digitals
  • Import z více než 1100 typů digitálních fotoaparátů
  • Import von mehr als 1100 digitalen Kameras
  • Εισαγωγή από περισσότερες από 1100 ψηφιακές κάμερες
  • Import from more than 1100 digital camera devices
  • Permite importar de más de 1100 cámaras digitales
  • Import enam kui 1100 digitaalkaamerast
  • Tuontituki yli 1100 eri digikameralle
  • Importe à partir de plus de 1100 appareils photos numériques
  • Permite importar desde máis de 1100 dispositivos de cámara dixital.
  • Impor dari lebih dari 110 perangkat kamera digital
  • Importazione da più di 1.100 dispositivi fotografici digitali
  • 1100종 이상의 디지털 카메라에서 사진 가져오기
  • Importeren uit meer dan 1100 digitale camera's
  • Støtte for importering av bilete frå meir enn 1 100 ulike kameramodellar
  • Importowanie z więcej niż 1100 aparatów cyfrowych
  • Importar a partir de mais de 1100 máquinas digitais
  • Importação de mais de 1.100 câmeras digitais
  • Поддержка импорта более чем с 1100 моделей цифровых камер;
  • Import z viac ako 1100 digitálnych fotoaparátov
  • Uvažanje iz več kot 1100 digitalnih fotoaparatov
  • Import från mer än 1100 digitalkameror
  • 1100den fazla kamera cihazından içe aktarma
  • можливість імпортування даних з понад 1100 цифрових фотоапаратів;
  • xxImport from more than 1100 digital camera devicesxx
  • 从1100多种数码相机设备中导入
  • 能從超過 1100 個數位相機裝置匯入
  • Support for more than 900 RAW format pictures
  • Admet més de 900 formats d'imatge RAW
  • Admet més de 900 formats d'imatge RAW
  • Podpora více než 900 obrázků ve formátu RAW
  • Unterstützung für mehr als 900 Bild-Rohformate
  • Υποστήριξη για περισσότερους από 900 τύπους RAW εικόνων
  • Support for more than 900 RAW format pictures
  • Permite imágenes en más de 900 formatos RAW
  • Enam kui 900 toorpildivormingu toetus
  • Yli 900 RAW-kuvamuodon tuki
  • Prise en charge de plus de 900 formats d'image RAW
  • Compatíbel con imaxes en máis de 900 formatos en cru (RAW).
  • Mendukung lebih dari 900 gambar format RAW
  • Supporto per più di 900 immagini in formato RAW
  • 900종 이상의 RAW 사진 형식 지원
  • Ondersteuning voor 900 RAW-format afbeeldingen
  • Støtte for meir enn 900 ulike råbiletformat
  • Obsługa dla więcej niż 900 postaci zdjęć RAW
  • Suporte para mais de 900 formatos de imagens RAW
  • Suporte a mais de 900 formatos de imagem RAW
  • Поддержка более 900 форматов цифровых негативов (RAW);
  • Podpora več kot 900 vrstam surovih slik (RAW)
  • Stöd för 900 obehandlade bildformat
  • 900'den fazla RAW biçimli resim desteği
  • підтримка понад 900 форматів цифрових негативів (RAW);
  • xxSupport for more than 900 RAW format picturesxx
  • 支持超过900种 RAW 格式的图片
  • 支援超過 900 個 RAW 格式圖片
  • Light Table to compare photo side by side
  • Taula de llum per a comparar les fotografies costat a costat
  • Taula de llum per a comparar les fotografies costat a costat
  • Prohlížecí stolek pro porovnání fotografií vedle sebe
  • Leuchttisch zum Vergleichen von Fotos nebeneinander
  • Ελαφρύς πίνακας για σύγκριση φωτογραφιών κατ' αντιπαράθεση
  • Light Table to compare photo side by side
  • Mesa de luz para comparar fotos una junto a otra
  • Valguslaud fotode vahetuks võrdlemiseks
  • Valotaulu kuvien vertailemiseksi rinnakkain
  • Table lumineuse pour comparer des photos côte-à-côte
  • Táboa de luz para comparar fotos unha ao lado da outra.
  • Table Terang untuk membandingkan foto sisi demi sisi
  • Tavolo luminoso per confrontare le foto fianco a fianco
  • 사진을 양쪽으로 비교하는 조명 테이블
  • Lichttafel om foto's naast elkaar te vergelijken
  • Lysbord for å samanlikna bilete ved sida av kvarandre
  • Podświetlarka do porównywania zdjęć jedno obok drugiego
  • Mesa de Luz para comparar as fotografias lado-a-lado
  • Mesa de Luz para comparação de fotos lado a lado
  • Световой стол для сравнения изображений;
  • Osvetlitvena miza za primerjavo fotografij
  • Ljusbord för att jämföra foton sida vid sida
  • Fotoğrafı yan yana karşılaştırmak için Tabloyu Işıklandır
  • стіл із підсвічуванням для порівняння фотографій;
  • xxLight Table to compare photo side by sidexx
  • 用于并排比较照片的灯桌
  • Extended features using extra tools
  • Característiques ampliades usant eines addicionals
  • Característiques ampliades usant eines addicionals
  • Rozšířené vlastnosti pomocí extra nástrojů
  • Επεκτάσεις χαρακτηριστικών με χρήση επιπλέον εργαλείων
  • Extended features using extra tools
  • Funciones extendidas mediante herramientas adicionales
  • Avarad võimalused lisatööriistade abil
  • Lisää ominaisuuksia lisätyökaluilla
  • Fonctionnalités étendues par des outils externes
  • Funcionalidades estendidas usando ferramentas adicionais.
  • Fitur yang diperluas menggunakan peralatan extra
  • Funzionalità estese tramite strumenti aggiuntivi
  • 추가 도구로 기능 확장
  • Uitgebreide mogelijkheden met extra hulpmiddelen
  • Programtillegg som gjev ekstrafunksjonar
  • Rozszerzone funkcje używające dodatkowych narzędzi
  • Funcionalidades estendidas com ferramentas extra
  • Funcionalidades estendidas com ferramentas extras
  • Расширенные возможности дополнительных инструментов;
  • Razširjene zmožnosti preko dodatnih orodij
  • Utökade funktioner med användning av extra verktyg
  • Ekstra araçlar kullanan genişletilmiş özellikler
  • розширення можливостей за допомогою додаткових інструментів;
  • xxExtended features using extra toolsxx
  • 使用额外工具的扩展功能
  • 使用延伸工具得到延伸功能
  • Share your photos using HTML or publishing them to remote web services
  • Compartiu les vostres fotografies usant HTML o publicant-les a serveis web remots
  • Compartiu les vostres fotografies usant HTML o publicant-les a serveis web remots
  • Sdílení fotografií vytvořením HTML galerie nebo přímým publikováním na vzdálených webových službách
  • Teilen Sie Ihre Fotos über HTML oder veröffentlichen Sie sie auf entfernten Webdiensten
  • Μοιραστείτε τις φωτογραφίες σας με HTML ή με δημοσίευση σε απομακρυσμένες υπηρεσίες ιστού
  • Share your photos using HTML or publishing them to remote web services
  • Compartir fotos usando HTML o publicándolas en servicios web remotos
  • Fotode jagamine HTML-i vahendusel või nende avaldamine veebiteenustes
  • Valokuvien jako HTML:ksi tai julkaisu etäverkkopalvelimiin
  • Permet de partager vos photos avec des pages HTML ou de les publier sur des services web distants
  • Permite compartir imaxes usando HTML ou publicándoas en servizos web remotos.
  • Bagikan fotomu menggunakan HTML atau mempublikasikannya ke layanan web jarak jauh
  • Condivisione delle foto tramite HTML o loro pubblicazione nei servizi web remoti
  • HTML로 사진 공유 및 웹 서비스에 올리기
  • Uw foto's delen met HTML of ze publiceren op websites op afstand
  • Kan dela bileta med HTML eller publisera dei på ulike nett-tenester
  • Udostępnianie zdjęć przy użyciu HTML lub publikowanie ich poprzez usługi sieciowe
  • Partilhar as suas fotos por HTML ou publicá-las em serviços Web remotos
  • Compartilhamento de fotos usando HTML ou publicação em serviços remotos
  • Публикация фотографий созданием галерей в формате HTML а также на различных интернет-ресурсах;
  • Delite vaše fotografije s pomočjo HTML-ja ali pa jih objavite na oddaljenih storitvah
  • Dela foton genom att använda HTML eller publicera dem på webbtjänster
  • Resimlerinizi HTML kullanarak paylaşın veya onları uzak web servislerinde yayımlayın
  • можливість оприлюднення фотографій на віддалених службах у інтернеті;
  • xxShare your photos using HTML or publishing them to remote web servicesxx
  • 使用 HTML 共享您的照片或将其发布到远程 WEB 服务
  • 使用 HTML 分享圖片或將圖片發布到遠端網路服務
  • Image editor included with advanced tools to fix photo quickly
  • Inclou un editor d'imatges amb eines avançades per esmenar ràpidament les fotografies
  • Inclou un editor d'imatges amb eines avançades per esmenar ràpidament les fotografies
  • Editor obrázků zahrnutý v pokročilých nástrojích pro rychlé opravy fotek
  • Περιλαμβάνεται επεξεργαστής εικόνων με προηγμένα εργαλεία για γρήγορη διόρθωση φωτογραφιών
  • Image editor included with advanced tools to fix photo quickly
  • Editor de imágenes incluido con herramientas avanzadas para corregir las fotos de forma rápida
  • Pildiredaktor võimsate tööriistadega fotode kiireks parandamiseks
  • Lisätyökaluissa kuvamuokkain kuvien pikakorjauksiin
  • Éditeur d'images inclus, avec des fonctionnalités pour réparer rapidement une photo
  • Inclúe un editor de imaxes con ferramentas avanzadas para arranxar fotos rapidamente.
  • Tersemat editor citra dengan alat tingkat-lanjut untuk memperbaiki foto secara cepat
  • Editor delle immagini con strumenti avanzati inclusi per la correzione rapida delle foto
  • 사진을 빠르게 편집할 수 있는 고급 기능이 있는 그림 편집기
  • Bewerker van afbeeldingen met geavanceerde hulpmiddelen om foto's snel te repareren
  • Innebygd biletredigering med avanserte verktøy for å forbetra bileta
  • Dołączony edytor obrazów o zaawansowanych narzedziach do szybkiego poprawiania zdjęć
  • Editor de imagens que inclui ferramentas avançadas para corrigir rapidamente as fotografias
  • Editor de imagens com funcionalidades avançadas para correção de fotos
  • Редактор изображений с расширенными инструментами для быстрого исправления фотографий.
  • Vgrajen urejevalnik slik z naprednimi orodji za hitre popravke fotografij
  • Bildeditor med avancerade funktioner ingår för att snabbt förbättra foton
  • Fotoğrafı hızla düzeltmek için gelişmiş araçlar içeren resim editörü
  • редактор зображень із додатковими можливостями для швидкого виправлення фотографій;
  • xxImage editor included with advanced tools to fix photo quicklyxx
  • 图像编辑器包含高级工具以快速修复照片
  • 包含進階工具的圖片編輯器以快速修復圖片
https://www.digikam.org/ https://bugs.kde.org/enter_bug.cgi?format=guided&product=digikam https://docs.kde.org/?application=digikam https://www.digikam.org/donation digiKam Batch Queue Manager xxdigiKam Batch Queue Managerxx https://www.digikam.org/img/content/about/overview_bqm.png digiKam Image Editor xxdigiKam Image Editorxx https://www.digikam.org/img/content/about/overview_imageeditor.png digiKam Import From Camera xxdigiKam Import From Cameraxx https://www.digikam.org/img/content/about/overview_import.png digiKam Light Table xxdigiKam Light Tablexx https://www.digikam.org/img/content/about/overview_lighttable.png digiKam Tags View Gestió de les fotografies al digiKam Gestió de les fotografies al digiKam Správa fotografií v digiKamu Fotoverwaltung in digiKam Photo management in digiKam Gestión de fotos en digiKam Fotohaldus digiKamis Valokuvien hallinta digiKamissä Gestion de photos dans digiKam Xestión de fotos en digiKam Pengelolaan foto dalam digiKam Gestione delle foto in digiKam digiKam으로 사진 관리하기 Fotobeheer in digiKam Fotohandsaming i digiKam ਡਿਜਿਕੈਮ ਵਿੱਚ ਫੋਟੋ ਇੰਤਜ਼ਾਮ Zarządzanie zdjęciami w digiKam Gestão de Fotografias no digiKam Gerenciamento de fotos no digiKam Управление фотографиями в digiKam Program za upravljanje zbirk fotografij Fotohantering i Digikam Керування фотографіями у digiKam xxdigiKam Tags Viewxx digiKam 的照片管理 digiKam 中的圖片管理 https://www.digikam.org/img/content/about/overview_tagsview.png digiKam Team مشروع «دِجي‌كام» digiKam Projekt Projecte digiKam Projecte digiKam Projekt digiKam digiKam-Projekt digiKam Project digiKam Project Proyecto digiKam digiKami projekt digiKam-projekti Projet digiKam Proxecto digiKam digiKam Project Progetto digiKam digiKam 프로젝트 digiKam-project digiKam-prosjektet ਡਿਜਿਕੈਮ ਪਰੋਜੈੱਕਟ Projekt digiKam Projecto digiKam Projeto digiKam Проект digiKam Projekt digiKamu Projekt digiKam Digikam-projektet digiKam Projesi Проєкт digiKam xxdigiKam Teamxx digiKam 项目 digiKam 專案 KDE digikam
diff --git a/core/cmake/modules/MacroUtils.cmake b/core/cmake/modules/MacroUtils.cmake index 71d1e47d59..d4927dcfe9 100644 --- a/core/cmake/modules/MacroUtils.cmake +++ b/core/cmake/modules/MacroUtils.cmake @@ -1,216 +1,210 @@ # Some useful macros for printing status information # # Copyright (c) 2010-2020 by Gilles Caulier, # # Redistribution and use is allowed according to the terms of the BSD license. # For details see the accompanying COPYING-CMAKE-SCRIPTS file. # # ------------------------------------------------------------------------- set(PRINT_COMPILE_LENGTH "40") macro(FILL_WITH_DOTS VAR) string(LENGTH ${${VAR}} NAME_LENGTH) math(EXPR DOT_LENGTH "${PRINT_COMPILE_LENGTH} - ${NAME_LENGTH}") if(${DOT_LENGTH} LESS 0) set(DOT_LENGTH 0) endif() foreach(COUNT RANGE ${DOT_LENGTH}) set(${VAR} "${${VAR}}.") endforeach() endmacro() # ------------------------------------------------------------------------- macro(PRINT_LIBRARY_STATUS NAME WEBSITE VERSIONHINT) set(LIB_MESSAGE "${NAME} found") FILL_WITH_DOTS(LIB_MESSAGE) if(${ARGN}) message(STATUS " ${LIB_MESSAGE} YES") else() message(STATUS " ${LIB_MESSAGE} NO") message(SEND_ERROR " ${NAME} is needed. You need to install the ${NAME} ${VERSIONHINT} development package.") message(STATUS " ${NAME} website is at ${WEBSITE}") message(STATUS "") endif() endmacro() # ------------------------------------------------------------------------- macro(PRINT_OPTIONAL_LIBRARY_STATUS NAME WEBSITE VERSIONHINT FEATUREMISSING) set(LIB_MESSAGE "${NAME} found") FILL_WITH_DOTS(LIB_MESSAGE) if(${ARGN}) message(STATUS " ${LIB_MESSAGE} YES (optional)") else() message(STATUS " ${LIB_MESSAGE} NO (optional)") message(STATUS " ${FEATUREMISSING}") message(STATUS " Please install the ${NAME} ${VERSIONHINT} development package.") if(${WEBSITE}) message(STATUS " ${NAME} website is at ${WEBSITE}") endif() message(STATUS "") endif() endmacro() # ------------------------------------------------------------------------- macro(PRINT_QTMODULE_STATUS NAME) set(LIB_MESSAGE "${NAME} module found") FILL_WITH_DOTS(LIB_MESSAGE) if(${ARGN}) message(STATUS " ${LIB_MESSAGE} YES") else() message(STATUS " ${LIB_MESSAGE} NO") message(STATUS "") message(SEND_ERROR " ${NAME} module is needed. You need to install a package containing the ${NAME} module.") message(STATUS "") endif() endmacro() # ------------------------------------------------------------------------- macro(PRINT_EXECUTABLE_STATUS NAME TECHNICAL_NAME PATH_VARIABLE) set(LIB_MESSAGE "${NAME} found") FILL_WITH_DOTS(LIB_MESSAGE) if(${ARGN}) message(STATUS " ${LIB_MESSAGE} YES") else() message(STATUS " ${LIB_MESSAGE} NO") message(STATUS "") message(STATUS " ${NAME} is needed. You need to install the package containing the \"${TECHNICAL_NAME}\" executable.") message(STATUS " If you have this executable installed, please specify the folder containing it by ${PATH_VARIABLE}") message(SEND_ERROR "") endif() endmacro() # ------------------------------------------------------------------------- macro(PRINT_COMPONENT_COMPILE_STATUS NAME) set(COMPILE_MESSAGE "${NAME} will be compiled") FILL_WITH_DOTS(COMPILE_MESSAGE) IF(${ARGN}) message(STATUS " ${COMPILE_MESSAGE} YES (optional)") else() message(STATUS " ${COMPILE_MESSAGE} NO (optional)") endif() endmacro() # ------------------------------------------------------------------------- macro(PRINT_OPTIONAL_QTMODULE_STATUS NAME) set(LIB_MESSAGE "${NAME} module found") FILL_WITH_DOTS(LIB_MESSAGE) if(${ARGN}) message(STATUS " ${LIB_MESSAGE} YES (optional)") else() message(STATUS " ${LIB_MESSAGE} NO (optional)") endif() endmacro() # ------------------------------------------------------------------------- macro(HEADER_DIRECTORIES return_list) file(GLOB_RECURSE new_list *.h) set(dir_list "") foreach(file_path ${new_list}) get_filename_component(dir_path ${file_path} PATH) set(dir_list ${dir_list} ${dir_path}) endforeach() list(REMOVE_DUPLICATES dir_list) set(${return_list} ${dir_list}) endmacro() # ------------------------------------------------------------------------- macro(APPLY_COMMON_POLICIES) if(POLICY CMP0063) # C++ symbols visibility policy introduced in CMake version 3.3 # Details: https://cmake.org/cmake/help/git-stage/policy/CMP0063.html cmake_policy(SET CMP0063 NEW) endif() if(POLICY CMP0068) # MacOS RPATH settings policy introduced in CMake version 3.9 # Details: https://cmake.org/cmake/help/git-stage/policy/CMP0068.html cmake_policy(SET CMP0068 NEW) endif() if(POLICY CMP0071) # Automoc/autouic files handling introduced in CMake version 3.10 # Details: https://cmake.org/cmake/help/git-stage/policy/CMP0071.html cmake_policy(SET CMP0071 NEW) endif() - if(POLICY CMP0072) - # Prefers GLVND by default when available introduced in CMake version 3.11 - # Details: https://cmake.org/cmake/help/git-stage/policy/CMP0072.html - cmake_policy(SET CMP0072 NEW) - endif() - if(POLICY CMP0092) # MSVC warnings flag rules introduced in CMake version 3.16 # Details: https://cmake.org/cmake/help/git-stage/policy/CMP0092.html cmake_policy(SET CMP0092 NEW) endif() endmacro() diff --git a/core/dplugins/dimg/heif/dimgheifloader_load.cpp b/core/dplugins/dimg/heif/dimgheifloader_load.cpp index 30e572a2e2..38c9ebfd35 100644 --- a/core/dplugins/dimg/heif/dimgheifloader_load.cpp +++ b/core/dplugins/dimg/heif/dimgheifloader_load.cpp @@ -1,652 +1,657 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2019-09-26 * Description : A HEIF IO file for DImg framework - load operations * * Copyright (C) 2020 by Gilles Caulier * * 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, 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. * * ============================================================ */ #include "dimgheifloader.h" // Qt includes #include #include #include #include #include #include // Local includes #include "digikam_debug.h" #include "digikam_config.h" #include "dimg.h" #include "dimgloaderobserver.h" #include "metaengine.h" namespace Digikam { bool DImgHEIFLoader::load(const QString& filePath, DImgLoaderObserver* const observer) { m_observer = observer; readMetadata(filePath); - bool loadToMemory = false; - - FILE* file = fopen(QFile::encodeName(filePath).constData(), "rb"); + FILE* file = fopen(QFile::encodeName(filePath).constData(), "rb"); #ifdef Q_OS_WIN + bool loadToMemory = false; + if (!file) { - file = _wfopen((const wchar_t*)filePath.utf16(), L"rb"); - loadToMemory = true; + file = _wfopen((const wchar_t*)filePath.utf16(), L"rb"); + loadToMemory = true; } #endif if (!file) { qWarning() << "Error: Could not open source file."; loadingFailed(); return false; } const int headerLen = 12; unsigned char header[headerLen]; if (fread(&header, headerLen, 1, file) != 1) { qWarning() << "Error: Could not parse magic identifier."; fclose(file); loadingFailed(); return false; } if ((memcmp(&header[4], "ftyp", 4) != 0) && (memcmp(&header[8], "heic", 4) != 0) && (memcmp(&header[8], "heix", 4) != 0) && (memcmp(&header[8], "mif1", 4) != 0)) { qWarning() << "Error: source file is not HEIF image."; fclose(file); loadingFailed(); return false; } fclose(file); if (observer) { observer->progressInfo(0.1F); } // ------------------------------------------------------------------- // Initialize HEIF API. heif_item_id primary_image_id; struct heif_context* const heif_context = heif_context_alloc(); struct heif_error error; +#ifdef Q_OS_WIN + if (loadToMemory) { QByteArray buffer; QFile memFile(filePath); if (!memFile.open(QIODevice::ReadOnly)) { qWarning() << "Error: Could not load into memory."; loadingFailed(); return false; } buffer = memFile.readAll(); memFile.close(); qDebug() << "HEIF loading file to memory buffer"; error = heif_context_read_from_memory(heif_context, (void*)buffer.data(), buffer.size(), nullptr); } else + +#endif + { error = heif_context_read_from_file(heif_context, QFile::encodeName(filePath).constData(), nullptr); } if (!isHeifSuccess(&error)) { qWarning() << "Error: Could not read source file."; loadingFailed(); heif_context_free(heif_context); return false; } error = heif_context_get_primary_image_ID(heif_context, &primary_image_id); if (!isHeifSuccess(&error)) { qWarning() << "Error: Could not load image data."; loadingFailed(); heif_context_free(heif_context); return false; } bool ret = readHEICImageByID(heif_context, primary_image_id); heif_context_free(heif_context); return ret; } bool DImgHEIFLoader::readHEICColorProfile(struct heif_image_handle* const image_handle) { #if LIBHEIF_NUMERIC_VERSION >= 0x01040000 switch (heif_image_handle_get_color_profile_type(image_handle)) { case heif_color_profile_type_not_present: break; case heif_color_profile_type_rICC: case heif_color_profile_type_prof: { size_t length = heif_image_handle_get_raw_color_profile_size(image_handle); if (length > 0) { // Read color profile. QByteArray profile; profile.resize((int)length); struct heif_error error = heif_image_handle_get_raw_color_profile(image_handle, profile.data()); if (error.code == 0) { qDebug() << "HEIF color profile found with size:" << length; imageSetIccProfile(IccProfile(profile)); return true; } } break; } default: // heif_color_profile_type_nclx qWarning() << "Unknown HEIF color profile type discarded"; break; } #else Q_UNUSED(image_handle); #endif // If ICC profile is null, check Exif metadata. if (checkExifWorkingColorSpace()) { return true; } return false; } bool DImgHEIFLoader::readHEICMetadata(struct heif_image_handle* const image_handle) { heif_item_id dataIds[10]; QByteArray exif; QByteArray iptc; QByteArray xmp; int count = heif_image_handle_get_list_of_metadata_block_IDs(image_handle, nullptr, dataIds, 10); qDebug() << "Found" << count << "HEIF metadata chunck"; if (count > 0) { for (int i = 0 ; i < count ; ++i) { qDebug() << "Parsing HEIF metadata chunck:" << heif_image_handle_get_metadata_type(image_handle, dataIds[i]); if (QLatin1String(heif_image_handle_get_metadata_type(image_handle, dataIds[i])) == QLatin1String("Exif")) { // Read Exif chunk. size_t length = heif_image_handle_get_metadata_size(image_handle, dataIds[i]); QByteArray exifChunk; exifChunk.resize((int)length); struct heif_error error = heif_image_handle_get_metadata(image_handle, dataIds[i], exifChunk.data()); if ((error.code == 0) && (length > 4)) { // The first 4 bytes indicate the // offset to the start of the TIFF header of the Exif data. int skip = ((exifChunk.constData()[0] << 24) | (exifChunk.constData()[1] << 16) | (exifChunk.constData()[2] << 8) | exifChunk.constData()[3]) + 4; if (exifChunk.size() > skip) { // Copy the real exif data into the byte array qDebug() << "HEIF exif container found with size:" << length - skip; exif.append((char*)(exifChunk.data() + skip), exifChunk.size() - skip); } } } if (QLatin1String(heif_image_handle_get_metadata_type(image_handle, dataIds[i])) == QLatin1String("iptc")) { // Read Iptc chunk. size_t length = heif_image_handle_get_metadata_size(image_handle, dataIds[i]); iptc.resize((int)length); struct heif_error error = heif_image_handle_get_metadata(image_handle, dataIds[i], iptc.data()); if (error.code == 0) { qDebug() << "HEIF iptc container found with size:" << length; } else { iptc = QByteArray(); } } if ( (QLatin1String(heif_image_handle_get_metadata_type(image_handle, dataIds[i])) == QLatin1String("mime")) && (QLatin1String(heif_image_handle_get_metadata_content_type(image_handle, dataIds[i])) == QLatin1String("application/rdf+xml")) ) { // Read Xmp chunk. size_t length = heif_image_handle_get_metadata_size(image_handle, dataIds[i]); xmp.resize((int)length); struct heif_error error = heif_image_handle_get_metadata(image_handle, dataIds[i], xmp.data()); if (error.code == 0) { qDebug() << "HEIF xmp container found with size:" << length; } else { xmp = QByteArray(); } } } } if (!exif.isEmpty() || !iptc.isEmpty() || !xmp.isEmpty()) { MetaEngine meta; if (!exif.isEmpty()) { meta.setExif(exif); } if (!iptc.isEmpty()) { meta.setIptc(iptc); } if (!xmp.isEmpty()) { meta.setXmp(xmp); } m_image->setMetadata(meta.data()); return true; } return false; } bool DImgHEIFLoader::readHEICImageByID(struct heif_context* const heif_context, heif_item_id image_id) { struct heif_image* heif_image = nullptr; struct heif_image_handle* image_handle = nullptr; struct heif_error error = heif_context_get_image_handle(heif_context, image_id, &image_handle); if (!isHeifSuccess(&error)) { loadingFailed(); return false; } // NOTE: An HEIC image without ICC color profile or without metadata still valid. if (m_loadFlags & LoadMetadata) { readHEICMetadata(image_handle); } if (m_loadFlags & LoadICCData) { readHEICColorProfile(image_handle); } if (m_observer) { m_observer->progressInfo(0.2F); } if (m_loadFlags & LoadPreview) { heif_item_id thumbnail_ID = 0; int nThumbnails = heif_image_handle_get_list_of_thumbnail_IDs(image_handle, &thumbnail_ID, 1); if (nThumbnails > 0) { struct heif_image_handle* thumbnail_handle = nullptr; error = heif_image_handle_get_thumbnail(image_handle, thumbnail_ID, &thumbnail_handle); if (!isHeifSuccess(&error)) { loadingFailed(); heif_image_handle_release(image_handle); return false; } heif_image_handle_release(image_handle); qDebug() << "HEIF preview found in thumbnail chunk"; return readHEICImageByHandle(thumbnail_handle, heif_image); } } if (m_loadFlags & LoadImageData) { return readHEICImageByHandle(image_handle, heif_image); } heif_image_handle_release(image_handle); return true; } bool DImgHEIFLoader::readHEICImageByHandle(struct heif_image_handle* image_handle, struct heif_image* heif_image) { // Copy HEIF image into data structures. struct heif_error error; struct heif_decoding_options* const decode_options = heif_decoding_options_alloc(); decode_options->ignore_transformations = 1; m_hasAlpha = heif_image_handle_has_alpha_channel(image_handle); heif_chroma chroma = m_hasAlpha ? heif_chroma_interleaved_RGBA : heif_chroma_interleaved_RGB; // Trace to check image size properties before decoding, as these values can be different. qDebug() << "HEIF image size: (" << heif_image_handle_get_width(image_handle) << "x" << heif_image_handle_get_height(image_handle) << ")"; error = heif_decode_image(image_handle, &heif_image, heif_colorspace_RGB, chroma, decode_options); if (!isHeifSuccess(&error)) { loadingFailed(); heif_image_handle_release(image_handle); return false; } if (m_observer) { m_observer->progressInfo(0.3F); } heif_decoding_options_free(decode_options); int colorModel = DImg::COLORMODELUNKNOWN; (void)colorModel; // prevent cppcheck warning. int colorDepth = heif_image_get_bits_per_pixel_range(heif_image, heif_channel_interleaved); imageWidth() = heif_image_get_width(heif_image, heif_channel_interleaved); imageHeight() = heif_image_get_height(heif_image, heif_channel_interleaved); qDebug() << "Decoded HEIF image properties: size(" << imageWidth() << "x" << imageHeight() << "), Alpha:" << m_hasAlpha << ", Color depth :" << colorDepth; if (!QSize(imageWidth(), imageHeight()).isValid()) { loadingFailed(); heif_image_release(heif_image); heif_image_handle_release(image_handle); return false; } int stride = 0; uint8_t* const ptr = heif_image_get_plane(heif_image, heif_channel_interleaved, &stride); qDebug() << "HEIF data container:" << ptr; qDebug() << "HEIC bytes per line:" << stride; if (!ptr || stride <= 0) { qWarning() << "HEIC data pixels information not valid!"; loadingFailed(); heif_image_release(heif_image); heif_image_handle_release(image_handle); return false; } uchar* data = nullptr; int colorMul = 1; // color multiplier colorModel = DImg::RGB; if (colorDepth == 8) { qDebug() << "Color bytes depth: 8"; m_sixteenBit = false; } else if ((colorDepth > 8) && (colorDepth <= 16)) { qDebug() << "Color bytes depth: 16"; m_sixteenBit = true; colorMul = 16 - colorDepth; } else { qWarning() << "Color bits depth: " << colorDepth << ": not supported!"; loadingFailed(); heif_image_release(heif_image); heif_image_handle_release(image_handle); return false; } qDebug() << "Color multiplier:" << colorMul; if (m_sixteenBit) { data = new_failureTolerant(imageWidth(), imageHeight(), 8); // 16 bits/color/pixel } else { data = new_failureTolerant(imageWidth(), imageHeight(), 4); // 8 bits/color/pixel } if (!data) { qWarning() << "Cannot allocate memory!"; loadingFailed(); heif_image_release(heif_image); heif_image_handle_release(image_handle); return false; } if (m_observer) { m_observer->progressInfo(0.4F); } uchar* dst = data; unsigned short* dst16 = reinterpret_cast(data); uchar* src = nullptr; unsigned short* src16 = nullptr; unsigned int checkPoint = 0; for (unsigned int y = 0 ; y < imageHeight() ; ++y) { src = reinterpret_cast(ptr + (y * stride)); src16 = reinterpret_cast(src); for (unsigned int x = 0 ; x < imageWidth() ; ++x) { if (!m_sixteenBit) // 8 bits image. { // Blue dst[0] = src[2]; // Green dst[1] = src[1]; // Red dst[2] = src[0]; // Alpha if (m_hasAlpha) { dst[3] = src[3]; src += 4; } else { dst[3] = 0xFF; src += 3; } dst += 4; } else // 16 bits image. { // Blue dst16[0] = (unsigned short)(src16[2] << colorMul); // Green dst16[1] = (unsigned short)(src16[1] << colorMul); // Red dst16[2] = (unsigned short)(src16[0] << colorMul); // Alpha if (m_hasAlpha) { dst16[3] = (unsigned short)(src16[3] << colorMul); src16 += 4; } else { dst16[3] = 0xFFFF; src16 += 3; } dst16 += 4; } } if (m_observer && y >= checkPoint) { checkPoint += granularity(m_observer, y, 0.8F); if (!m_observer->continueQuery()) { loadingFailed(); heif_image_release(heif_image); heif_image_handle_release(image_handle); return false; } m_observer->progressInfo(0.4 + (0.8 * (((float)y) / ((float)imageHeight())))); } } imageData() = data; imageSetAttribute(QLatin1String("format"), QLatin1String("HEIF")); imageSetAttribute(QLatin1String("originalColorModel"), colorModel); imageSetAttribute(QLatin1String("originalBitDepth"), m_sixteenBit ? 16 : 8); imageSetAttribute(QLatin1String("originalSize"), QSize(imageWidth(), imageHeight())); if (m_observer) { m_observer->progressInfo(0.9F); } heif_image_release(heif_image); heif_image_handle_release(image_handle); return true; } } // namespace Digikam diff --git a/core/dplugins/editor/colors/profileconversion/profileconversiontoolplugin.cpp b/core/dplugins/editor/colors/profileconversion/profileconversiontoolplugin.cpp index ab6f948108..7ea163476f 100644 --- a/core/dplugins/editor/colors/profileconversion/profileconversiontoolplugin.cpp +++ b/core/dplugins/editor/colors/profileconversion/profileconversiontoolplugin.cpp @@ -1,229 +1,229 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2018-07-30 * Description : image editor plugin to convert to color space * * Copyright (C) 2018-2020 by Gilles Caulier * * 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, 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. * * ============================================================ */ #include "profileconversiontoolplugin.h" // Qt includes #include #include #include // KDE includes #include // Local includes #include "editorwindow.h" #include "profileconversiontool.h" #include "iccsettings.h" #include "iccsettingscontainer.h" #include "icctransform.h" #include "imageiface.h" namespace DigikamEditorProfileConversionToolPlugin { ProfileConversionToolPlugin::ProfileConversionToolPlugin(QObject* const parent) : DPluginEditor(parent) { } ProfileConversionToolPlugin::~ProfileConversionToolPlugin() { } QString ProfileConversionToolPlugin::name() const { return i18n("Color Profile Conversion"); } QString ProfileConversionToolPlugin::iid() const { return QLatin1String(DPLUGIN_IID); } QIcon ProfileConversionToolPlugin::icon() const { return QIcon::fromTheme(QLatin1String("preferences-desktop-display-color")); } QString ProfileConversionToolPlugin::description() const { return i18n("A tool to convert image to a color space"); } QString ProfileConversionToolPlugin::details() const { return i18n("

This Image Editor tool can convert image to a different color space.

"); } QList ProfileConversionToolPlugin::authors() const { return QList() << DPluginAuthor(QString::fromUtf8("Marcel Wiesweg"), QString::fromUtf8("marcel dot wiesweg at gmx dot de"), QString::fromUtf8("(C) 2009-2012")) << DPluginAuthor(QString::fromUtf8("Gilles Caulier"), QString::fromUtf8("caulier dot gilles at gmail dot com"), QString::fromUtf8("(C) 2009-2020")) ; } void ProfileConversionToolPlugin::setup(QObject* const parent) { DPluginAction* const ac = new DPluginAction(parent); m_profileMenuAction = new IccProfilesMenuAction(icon(), QString(), parent); connect(m_profileMenuAction, SIGNAL(triggered(IccProfile)), this, SLOT(slotConvertToColorSpace(IccProfile))); connect(IccSettings::instance(), SIGNAL(settingsChanged()), this, SLOT(slotUpdateColorSpaceMenu())); ac->setMenu(m_profileMenuAction); ac->setText(i18n("Color Spaces")); ac->setObjectName(QLatin1String("editorwindow_colormanagement")); ac->setActionCategory(DPluginAction::EditorColors); addAction(ac); DPluginAction* const ac2 = new DPluginAction(parent); ac2->setIcon(icon()); ac2->setText(i18nc("@action", "Color Space Converter...")); ac2->setObjectName(QLatin1String("editorwindow_color_spaceconverter")); ac2->setActionCategory(DPluginAction::EditorColors); connect(ac2, SIGNAL(triggered(bool)), this, SLOT(slotProfileConversionTool())); m_colorSpaceConverter = ac2; addAction(ac2); slotUpdateColorSpaceMenu(); } void ProfileConversionToolPlugin::slotConvertToColorSpace(const IccProfile& profile) { ImageIface iface; if (iface.originalIccProfile().isNull()) { QMessageBox::critical(qApp->activeWindow(), qApp->applicationName(), i18n("This image is not color managed.")); return; } qApp->setOverrideCursor(Qt::WaitCursor); ProfileConversionTool::fastConversion(profile); qApp->restoreOverrideCursor(); } void ProfileConversionToolPlugin::slotUpdateColorSpaceMenu() { m_profileMenuAction->clear(); EditorWindow* const editor = dynamic_cast(m_profileMenuAction->parentObject()); if (!IccSettings::instance()->isEnabled()) { QAction* const action = new QAction(i18n("Color Management is disabled..."), this); m_profileMenuAction->addAction(action); if (editor) { connect(action, SIGNAL(triggered()), editor, SLOT(slotSetupICC())); } } else { ICCSettingsContainer settings = IccSettings::instance()->settings(); QList standardProfiles; QList favoriteProfiles; QSet standardProfilePaths; standardProfiles << IccProfile::sRGB() << IccProfile::adobeRGB() << IccProfile::wideGamutRGB() << IccProfile::proPhotoRGB(); foreach (IccProfile profile, standardProfiles) // krazy:exclude=foreach { m_profileMenuAction->addProfile(profile, profile.description()); standardProfilePaths << profile.filePath(); } m_profileMenuAction->addSeparator(); QStringList profileList = ProfileConversionTool::favoriteProfiles(); -#if QT_VERSION < QT_VERSION_CHECK(5, 14, 0) +#if (QT_VERSION < QT_VERSION_CHECK(5, 14, 0)) QSet favoriteProfilePaths = QSet::fromList(profileList); #else QSet favoriteProfilePaths(profileList.begin(), profileList.end()); #endif favoriteProfilePaths -= standardProfilePaths; foreach (const QString& path, favoriteProfilePaths) { favoriteProfiles << IccProfile(path); } m_profileMenuAction->addProfiles(favoriteProfiles); } m_profileMenuAction->addSeparator(); m_profileMenuAction->addAction(m_colorSpaceConverter); if (editor) { m_colorSpaceConverter->setEnabled(editor->actionEnabledState() && IccSettings::instance()->isEnabled()); } } void ProfileConversionToolPlugin::slotProfileConversionTool() { EditorWindow* const editor = dynamic_cast(sender()->parent()); if (editor) { ProfileConversionTool* const tool = new ProfileConversionTool(this); tool->setPlugin(this); connect(tool, SIGNAL(okClicked()), this, SLOT(slotUpdateColorSpaceMenu())); editor->loadTool(tool); } } } // namespace DigikamEditorProfileConversionToolPlugin diff --git a/core/dplugins/generic/view/slideshow/slideshowplugin.cpp b/core/dplugins/generic/view/slideshow/slideshowplugin.cpp index 759862e05a..1582ff4c55 100644 --- a/core/dplugins/generic/view/slideshow/slideshowplugin.cpp +++ b/core/dplugins/generic/view/slideshow/slideshowplugin.cpp @@ -1,301 +1,304 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2018-07-30 * Description : a plugin to render slideshow. * * Copyright (C) 2018-2020 by Gilles Caulier * Copyright (C) 2019-2020 by Minh Nghia Duong * * 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, 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. * * ============================================================ */ #include "slideshowplugin.h" // Qt includes #include #include #include #include #include // KDE includes #include // Local includes #include "metaenginesettings.h" #include "setupslideshow_dialog.h" #include "slideshowloader.h" using namespace Digikam; namespace DigikamGenericSlideShowPlugin { SlideShowPlugin::SlideShowPlugin(QObject* const parent) : DPluginGeneric(parent) { } SlideShowPlugin::~SlideShowPlugin() { } QString SlideShowPlugin::name() const { return i18n("SlideShow"); } QString SlideShowPlugin::iid() const { return QLatin1String(DPLUGIN_IID); } QIcon SlideShowPlugin::icon() const { return QIcon::fromTheme(QLatin1String("view-presentation")); } QString SlideShowPlugin::description() const { return i18n("A tool to render slideshow"); } QString SlideShowPlugin::details() const { return i18n("

This tool render a series of items as a simple slide-show.

" "

Plenty of items properties can be displayed as overlay while running.

" "

This tool can play album contents in recursive mode with children albums if any.

"); } QList SlideShowPlugin::authors() const { return QList() << DPluginAuthor(QString::fromUtf8("Enrico Ros"), QString::fromUtf8("eros dot kde at email dot it"), QString::fromUtf8("(C) 2004")) << DPluginAuthor(QString::fromUtf8("Renchi Raju"), QString::fromUtf8("renchi dot raju at gmail dot com"), QString::fromUtf8("(C) 2004-2005")) << DPluginAuthor(QString::fromUtf8("Gilles Caulier"), QString::fromUtf8("caulier dot gilles at gmail dot com"), QString::fromUtf8("(C) 2005-2020")) << DPluginAuthor(QString::fromUtf8("Minh Nghia Duong"), QString::fromUtf8("minhnghiaduong997 at gmail dot com"), QString::fromUtf8("(C) 2019-2020")) ; } void SlideShowPlugin::setup(QObject* const parent) { DPluginAction* const ac = new DPluginAction(parent); ac->setIcon(icon()); ac->setText(i18nc("@action", "Slideshow")); ac->setObjectName(QLatin1String("slideshow_plugin")); ac->setActionCategory(DPluginAction::GenericView); DInfoInterface* const iface = infoIface(ac); if (iface && (parent->objectName() == QLatin1String("Digikam"))) { QMenu* const slideShowActions = new QMenu(i18n("Slideshow"), nullptr); slideShowActions->setIcon(icon()); ac->setMenu(slideShowActions); // Action show all QAction* const slideShowAllAction = new QAction(i18n("All"), ac); slideShowAllAction->setObjectName(QLatin1String("slideshow_all")); slideShowAllAction->setShortcut(Qt::Key_F9); slideShowActions->addAction(slideShowAllAction); connect(slideShowAllAction, SIGNAL(triggered()), this, SLOT(slotMenuSlideShowAll())); + connect(slideShowActions->menuAction(), SIGNAL(triggered()), + slideShowAllAction, SLOT(trigger())); + // Action show selection QAction* const slideShowSelectionAction = new QAction(i18n("Selection"), ac); slideShowSelectionAction->setObjectName(QLatin1String("slideshow_selected")); slideShowSelectionAction->setShortcut(Qt::ALT + Qt::Key_F9); slideShowActions->addAction(slideShowSelectionAction); connect(slideShowSelectionAction, SIGNAL(triggered()), this, SLOT(slotMenuSlideShowSelection())); // Action show recursive QAction* const slideShowRecursiveAction = new QAction(i18n("With All Sub-Albums"), ac); slideShowRecursiveAction->setObjectName(QLatin1String("slideshow_recursive")); slideShowRecursiveAction->setShortcut(Qt::SHIFT + Qt::Key_F9); slideShowActions->addAction(slideShowRecursiveAction); connect(slideShowRecursiveAction, SIGNAL(triggered()), this, SLOT(slotMenuSlideShowRecursive())); connect(ac, SIGNAL(triggered(bool)), this, SLOT(slotShowManual())); } else { ac->setShortcut(Qt::Key_F9); connect(ac, SIGNAL(triggered(bool)), this, SLOT(slotMenuSlideShow())); } addAction(ac); } void SlideShowPlugin::addConnectionSlideEnd(QObject* const obj) { Q_UNUSED(obj); } void SlideShowPlugin::slotMenuSlideShow() { QUrl startFrom; DPluginAction* const ac = dynamic_cast(sender()); if (ac) { startFrom = ac->data().toUrl(); ac->setData(QVariant()); } SlideShowSettings* const settings = new SlideShowSettings(); settings->iface = infoIface(ac); settings->readFromConfig(); settings->exifRotate = MetaEngineSettings::instance()->settings().exifRotate; settings->fileList = settings->iface->currentAlbumItems(); slideshow(settings, true, startFrom); } void SlideShowPlugin::slotMenuSlideShowAll() { SlideShowSettings* const settings = new SlideShowSettings(); settings->iface = infoIface(sender()->parent()); settings->readFromConfig(); settings->fileList = settings->iface->currentAlbumItems(); slideshow(settings); } void SlideShowPlugin::slotMenuSlideShowSelection() { SlideShowSettings* const settings = new SlideShowSettings(); settings->iface = infoIface(sender()->parent()); settings->readFromConfig(); settings->fileList = settings->iface->currentSelectedItems(); slideshow(settings); } void SlideShowPlugin::slotMenuSlideShowRecursive() { DInfoInterface* const iface = infoIface(sender()->parent()); connect(iface, SIGNAL(signalAlbumItemsRecursiveCompleted(QList)), this, SLOT(slotShowRecursive(QList))); iface->parseAlbumItemsRecursive(); } void SlideShowPlugin::slotShowRecursive(const QList& imageList) { SlideShowSettings* const settings = new SlideShowSettings(); settings->iface = dynamic_cast(sender()); settings->readFromConfig(); settings->fileList = imageList; slideshow(settings); } void SlideShowPlugin::slotShowManual() { DPluginAction* const ac = dynamic_cast(sender()); QUrl startFrom; if (ac) { startFrom = ac->data().toUrl(); ac->setData(QVariant()); if (!startFrom.isValid()) { return; } } else { return; } SlideShowSettings* const settings = new SlideShowSettings(); settings->iface = infoIface(ac); settings->readFromConfig(); settings->exifRotate = MetaEngineSettings::instance()->settings().exifRotate; settings->fileList = settings->iface->currentAlbumItems(); slideshow(settings, false, startFrom); } void SlideShowPlugin::slideshow(SlideShowSettings* const settings, bool autoPlayEnabled, const QUrl& startFrom) { settings->autoPlayEnabled = autoPlayEnabled; settings->plugin = this; // TODO: preview settings for digikam //settings.previewSettings = ApplicationSettings::instance()->getPreviewSettings(); if (startFrom.isValid()) { settings->imageUrl = startFrom; } SlideShowLoader* const slide = new SlideShowLoader(settings); slide->setShortCutPrefixes(settings->iface->passShortcutActionsToWidget(slide)); if (settings->imageUrl.isValid()) { slide->setCurrentItem(settings->imageUrl); } else if (settings->startWithCurrent) { if (!settings->iface->currentSelectedItems().isEmpty()) { slide->setCurrentItem(settings->iface->currentSelectedItems()[0]); } else { // no current selection, do nothing. return; } } connect(slide, SIGNAL(signalLastItemUrl(QUrl)), settings->iface, SIGNAL(signalLastItemUrl(QUrl))); connect(settings->iface, SIGNAL(signalShortcutPressed(QString,int)), slide, SLOT(slotHandleShortcut(QString,int))); slide->show(); } } // namespace DigikamGenericSlideShowPlugin diff --git a/core/dplugins/generic/webservices/CMakeLists.txt b/core/dplugins/generic/webservices/CMakeLists.txt index 6a76c98cf8..4cb24252d6 100644 --- a/core/dplugins/generic/webservices/CMakeLists.txt +++ b/core/dplugins/generic/webservices/CMakeLists.txt @@ -1,45 +1,42 @@ # # Copyright (c) 2010-2020 by Gilles Caulier, # # Redistribution and use is allowed according to the terms of the BSD license. # For details see the accompanying COPYING-CMAKE-SCRIPTS file. if(ENABLE_QWEBENGINE) include_directories($) SET(QWEB_BACKEND Qt5::WebEngineWidgets) else() include_directories($) SET(QWEB_BACKEND Qt5::WebKitWidgets) endif() add_subdirectory(box) add_subdirectory(dropbox) add_subdirectory(facebook) add_subdirectory(filecopy) add_subdirectory(flickr) add_subdirectory(google) add_subdirectory(imgur) add_subdirectory(imageshack) add_subdirectory(ipfs) add_subdirectory(mediawiki) add_subdirectory(onedrive) add_subdirectory(pinterest) add_subdirectory(piwigo) add_subdirectory(rajce) add_subdirectory(smugmug) add_subdirectory(twitter) add_subdirectory(yandexfotki) -if(KF5Vkontakte_FOUND) - add_subdirectory(vkontakte) -endif() - if(KF5KIO_FOUND) + add_subdirectory(vkontakte) add_subdirectory(filetransfer) # Not yet finalized # add_subdirectory(debianscreenshots) endif() # Not yet finalized #add_subdirectory(unified) diff --git a/core/dplugins/generic/webservices/ipfs/ipfsimageslist.h b/core/dplugins/generic/webservices/ipfs/ipfsimageslist.h index 554c0ed014..5e44752f13 100644 --- a/core/dplugins/generic/webservices/ipfs/ipfsimageslist.h +++ b/core/dplugins/generic/webservices/ipfs/ipfsimageslist.h @@ -1,93 +1,93 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2012-02-12 * Description : a tool to export images to IPFS web service * * Copyright (C) 2018 by Amar Lakshya * Copyright (C) 2018 by Caulier Gilles * * 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, 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. * * ============================================================ */ #ifndef DIGIKAM_IPFS_IMAGES_LIST_H #define DIGIKAM_IPFS_IMAGES_LIST_H // Qt includes #include // Local includes #include "ditemslist.h" #include "ipfstalker.h" using namespace Digikam; namespace DigikamGenericIpfsPlugin { class IpfsImagesListViewItem; class IpfsImagesList : public DItemsList { Q_OBJECT public: /* The different columns in a list. */ enum FieldType { Title = DItemsListView::User1, Description = DItemsListView::User2, Url = DItemsListView::User3, }; public: explicit IpfsImagesList(QWidget* const parent = nullptr); - ~IpfsImagesList() Q_DECL_OVERRIDE {} + ~IpfsImagesList() override {} QList getPendingItems() const; public Q_SLOTS: void slotAddImages(const QList& list) override; void slotSuccess(const IpfsTalkerResult& result); void slotDoubleClick(QTreeWidgetItem* element, int i); }; // ------------------------------------------------------------------------- class IpfsImagesListViewItem : public DItemsListViewItem { public: explicit IpfsImagesListViewItem(DItemsListView* const view, const QUrl& url); - ~IpfsImagesListViewItem() Q_DECL_OVERRIDE {} + ~IpfsImagesListViewItem() override {} void setTitle(const QString& str); QString Title() const; void setDescription(const QString& str); QString Description() const; void setIpfsUrl(const QString& str); QString IpfsUrl() const; }; } // namespace DigikamGenericIpfsPlugin #endif // DIGIKAM_IPFS_IMAGES_LIST_H diff --git a/core/dplugins/generic/webservices/mediawiki/backend/mediawiki_edit.h b/core/dplugins/generic/webservices/mediawiki/backend/mediawiki_edit.h index a1696b6ec2..04663f01e8 100644 --- a/core/dplugins/generic/webservices/mediawiki/backend/mediawiki_edit.h +++ b/core/dplugins/generic/webservices/mediawiki/backend/mediawiki_edit.h @@ -1,325 +1,325 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2011-03-22 * Description : a Iface C++ interface * * Copyright (C) 2011-2020 by Gilles Caulier * Copyright (C) 2011 by Alexandre Mendes * Copyright (C) 2011 by Hormiere Guillaume * Copyright (C) 2011 by Manuel Campomanes * * 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, 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. * * ============================================================ */ #ifndef DIGIKAM_MEDIAWIKI_EDIT_H #define DIGIKAM_MEDIAWIKI_EDIT_H // Qt includes #include #include #include #include #include // Local includes #include "mediawiki_job.h" #include "mediawiki_queryinfo.h" namespace MediaWiki { class Iface; class EditPrivate; /** * @brief Edit job. * * Uses for create or edit a wiki. */ class Edit : public Job { Q_OBJECT Q_DECLARE_PRIVATE(Edit) public: /** * @brief Indicates all possible error conditions found during the processing of the edit. */ enum { /** * @brief Text is missing. */ TextMissing = Job::UserDefinedError+1, /** * @brief The section is invalid. */ InvalidSection, /** * @brief The page name is protected. */ TitleProtected, /** * @brief The permission for create page is missing. */ CreatePagePermissionMissing, /** * @brief The permission for create page is missing for anonymous. */ AnonymousCreatePagePermissionMissing, /** * @brief The article already exist. */ ArticleDuplication, /** * @brief The permission for create image is missing for anonymous. */ AnonymousCreateImagePermissionMissing, /** * @brief The permission for create image is missing. */ CreateImagePermissionMissing, /** * @brief The MediaWiki considers you are spamming. */ SpamDetected, /** * @brief The MediaWiki refuses your edit. */ Filtered, /** * @brief The size of the article exceed. */ ArticleSizeExceed, /** * @brief The permission for edit page is missing for anonymous. */ AnonymousEditPagePermissionMissing, /** * @brief The permission for edit page is missing. */ EditPagePermissionMissing, /** * @brief The page is deleted. */ PageDeleted, /** * @brief The page is empty. */ EmptyPage, /** * @brief The section is empty. */ EmptySection, /** * @brief Mediwiki detect an edit conflict. */ EditConflict, /** * @brief The revision isn't a valid revision. */ RevWrongPage, /** * @brief The undo failed. */ UndoFailed }; /** * @brief Specify how the watchlist is affected by this edit. */ enum Watchlist { /** * @brief Add the page to the watchlist */ watch, /** * @brief Remove the page from the watchlist */ unwatch, /** * @brief Use the preference settings */ preferences, /** * @brief Don't change the watchlist */ nochange }; public: /** * @brief Constructs an Edit job. * @param parent the QObject parent */ explicit Edit(Iface& media, QObject* const parent = nullptr); /** * @brief Destroys the Edit job. */ virtual ~Edit(); /** * @brief Starts the job asynchronously. */ - void start() Q_DECL_OVERRIDE; + void start() override; /** * @brief Specify how the watchlist is affected by this edit. * @param watchlist Specify how the watchlist is affected by this edit */ void setWatchList(Edit::Watchlist watchlist); /** * @brief If set, suppress errors about the page having been deleted in the meantime and recreate it. * @param recreate if set, suppress errors about the page having been deleted in the meantime and recreate it */ void setRecreate(bool recreate); /** * @brief If set, throw an error if the page already exists. * @param if set, throw an error if the page already exists */ void setCreateonly(bool createonly); /** * @brief If set, throw a missingtitle error if the page doesn't exist. * @param norecreate if set, throw a missingtitle error if the page doesn't exist */ void setNocreate(bool norecreate); /** * @brief If set to true, mark the edit as minor * @param minor if set to true, mark the edit as minor */ void setMinor(bool minor); /** * @brief Set the section. * @param section new section or integer */ void setSection(const QString& section); /** * @brief Set the summary. * @param summary the summary */ void setSummary(const QString& summary); /** * @brief Undo all revisions from undo to this one. If not set, just undo one revision. * @param undoafter if true set the undo after property */ void setUndoAfter(int undoafter); /** * @brief Undo this revision. Overrides text, prependtext and appendtext. * @param undo if true set the undo */ void setUndo(int undo); /** * @brief Set the text added to the beginning of the page. Overrides text. * @param prependText the text added to the beginning of the page */ void setPrependText(const QString& prependText); /** * @brief Set the text added to the end of the page. Overrides text. * @param appendText the text added to the end of the page */ void setAppendText(const QString& appendText); /** * @brief Set the page title. * @param pageName the page title */ void setPageName(const QString& pageName); /** * @brief Set the edit token. Retrieve from QueryInfo. * @param token the edit token */ void setToken(const QString& token); /** * @brief Set the timestamp of the base revision. Leave unset to ignore conflit. * @param baseTimestamp the timestamp of the base revision */ void setBaseTimestamp(const QDateTime& baseTimestamp); /** * @brief Set the timestamp when you obtained the edit token. * @param startTimestamp the timestamp when you obtained the edit token */ void setStartTimestamp(const QDateTime& startTimestamp); /** * @brief Set the page content. * @param text the page content. */ void setText(const QString& text); Q_SIGNALS: /** * @brief Emit the captcha question. * @param captcha the captcha question */ void resultCaptcha(const QVariant& captcha); private Q_SLOTS: void doWorkSendRequest(Page page); void finishedEdit(); public Q_SLOTS: /** * @brief Put the captcha answer. * @param captcha the captcha answer */ void finishedCaptcha(const QString& captcha); }; } // namespace MediaWiki #endif // DIGIKAM_MEDIAWIKI_EDIT_H diff --git a/core/dplugins/generic/webservices/mediawiki/backend/mediawiki_job.h b/core/dplugins/generic/webservices/mediawiki/backend/mediawiki_job.h index a279260bd4..046725dc11 100644 --- a/core/dplugins/generic/webservices/mediawiki/backend/mediawiki_job.h +++ b/core/dplugins/generic/webservices/mediawiki/backend/mediawiki_job.h @@ -1,101 +1,101 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2011-03-22 * Description : a Iface C++ interface * * Copyright (C) 2011-2020 by Gilles Caulier * Copyright (C) 2011 by Paolo de Vathaire * * 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, 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. * * ============================================================ */ #ifndef DIGIKAM_MEDIAWIKI_JOB_H #define DIGIKAM_MEDIAWIKI_JOB_H // KDE includes #include // Local includes namespace MediaWiki { class Iface; class JobPrivate; /** * @brief The base class for all Iface jobs. */ class Job : public KJob { Q_OBJECT Q_DECLARE_PRIVATE(Job) public: /** * @brief Indicates all possible error conditions found during the processing of the job. */ enum { NetworkError = KJob::UserDefinedError + 1, XmlError, UserRequestDefinedError = KJob::UserDefinedError + 100, MissingMandatoryParameter }; public: /** * @brief Destructs the Job. */ virtual ~Job(); /** * @brief Aborts this job quietly. */ - bool doKill() Q_DECL_OVERRIDE; + bool doKill() override; protected: /** * @brief Constructs a Job by a private class. * @param dd a private class * @param parent the QObject parent */ Job(JobPrivate& dd, QObject* const parent = nullptr); /** * @brief Connects signals of the reply object (in the private object) to * slots of this base class. */ void connectReply(); /** * @brief The private d pointer. */ JobPrivate* const d_ptr; private Q_SLOTS: void processUploadProgress(qint64 bytesReceived, qint64 bytesTotal); }; } // namespace MediaWiki #endif // DIGIKAM_MEDIAWIKI_JOB_H diff --git a/core/dplugins/generic/webservices/mediawiki/backend/mediawiki_login.h b/core/dplugins/generic/webservices/mediawiki/backend/mediawiki_login.h index c5f5bf2079..c3af9e2b0b 100644 --- a/core/dplugins/generic/webservices/mediawiki/backend/mediawiki_login.h +++ b/core/dplugins/generic/webservices/mediawiki/backend/mediawiki_login.h @@ -1,148 +1,148 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2011-03-22 * Description : a Iface C++ interface * * Copyright (C) 2011-2020 by Gilles Caulier * Copyright (C) 2011 by Alexandre Mendes * Copyright (C) 2011 by Peter Potrowl * Copyright (C) 2011 by Manuel Campomanes * * 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, 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. * * ============================================================ */ #ifndef DIGIKAM_MEDIAWIKI_LOGIN_H #define DIGIKAM_MEDIAWIKI_LOGIN_H // Qt includes #include #include // Local includes #include "mediawiki_job.h" namespace MediaWiki { class Iface; class LoginPrivate; /** * @brief Login job. * * Uses for log in a user. */ class Login : public Job { Q_OBJECT Q_DECLARE_PRIVATE(Login) public: enum { /** * @brief You didn't set the login parameter */ LoginMissing = Job::UserDefinedError + 1, /** * @brief You provided an illegal username */ IllegalUsername, /** * @brief The username you provided doesn't exist */ UsernameNotExists, /** * @brief You didn't set the lgpassword parameter or you left it empty */ PasswordMissing, /** * @brief The password you provided is incorrect */ WrongPassword, /** * @brief Same as WrongPass, returned when an authentication plugin rather than Iface itself rejected the password */ WrongPluginPassword, /** * @brief The wiki tried to automatically create a new account for you, but your IP address has been blocked from account creation */ IPAddressBlocked, /** * @brief You've logged in too many times in a short time. */ TooManyConnections, /** * @brief User is blocked */ UserBlocked, /** * @brief Either you did not provide the login token or the sessionid cookie. Request again with the token and cookie given in this response */ TokenNeeded }; public: /** * @brief Constructs a Login job. * @param MediaWiki the MediaWiki concerned by the job * @param login the QObject parent * @param password the QObject parent * @param parent the QObject parent */ explicit Login(Iface& MediaWiki, const QString& login, const QString& password, QObject* const parent = nullptr); /** * @brief Destroys the Login job. */ virtual ~Login(); /** * @brief Starts the job asynchronously. */ - void start() Q_DECL_OVERRIDE; + void start() override; private Q_SLOTS: /** * @brief Send a request to get the token and the cookie. */ void doWorkSendRequest(); /** * @brief Reads the xml * if the attribute value is equal to "NeedToken", try to log in the user * else if the attribute value is equal to "Success", the user is logged in * @param success true if the connection was completed successfully. */ void doWorkProcessReply(); }; } // namespace MediaWiki #endif // DIGIKAM_MEDIAWIKI_LOGIN_H diff --git a/core/dplugins/generic/webservices/mediawiki/backend/mediawiki_logout.h b/core/dplugins/generic/webservices/mediawiki/backend/mediawiki_logout.h index cdfe5f43c3..bf3cbb4753 100644 --- a/core/dplugins/generic/webservices/mediawiki/backend/mediawiki_logout.h +++ b/core/dplugins/generic/webservices/mediawiki/backend/mediawiki_logout.h @@ -1,74 +1,74 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2011-03-22 * Description : a Iface C++ interface * * Copyright (C) 2011-2020 by Gilles Caulier * Copyright (C) 2011 by Alexandre Mendes * * 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, 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. * * ============================================================ */ #ifndef DIGIKAM_MEDIAWIKI_LOGOUT_H #define DIGIKAM_MEDIAWIKI_LOGOUT_H // Local includes #include "mediawiki_job.h" namespace MediaWiki { class Iface; class LogoutPrivate; /** * @brief Logout job. * * Uses for log out a user. */ class Logout : public Job { Q_OBJECT Q_DECLARE_PRIVATE(Logout) public: /** * @brief Constructs a Logout job. * @param parent the QObject parent */ explicit Logout(Iface& MediaWiki, QObject* const parent = nullptr); /** * @brief Destroys the Logout job. */ virtual ~Logout(); /** * @brief Starts the job asynchronously. */ - void start() Q_DECL_OVERRIDE; + void start() override; private Q_SLOTS: void doWorkSendRequest(); void doWorkProcessReply(); }; } // namespace MediaWiki #endif // DIGIKAM_MEDIAWIKI_LOGOUT_H diff --git a/core/dplugins/generic/webservices/mediawiki/backend/mediawiki_parse.h b/core/dplugins/generic/webservices/mediawiki/backend/mediawiki_parse.h index 999e905778..736d11a95e 100644 --- a/core/dplugins/generic/webservices/mediawiki/backend/mediawiki_parse.h +++ b/core/dplugins/generic/webservices/mediawiki/backend/mediawiki_parse.h @@ -1,97 +1,97 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2011-03-22 * Description : a Iface C++ interface * * Copyright (C) 2011-2020 by Gilles Caulier * Copyright (C) 2011 by Alexandre Mendes * Copyright (C) 2011 by Vincent Garcia * * 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, 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. * * ============================================================ */ #ifndef DIGIKAM_MEDIAWIKI_PARSE_H #define DIGIKAM_MEDIAWIKI_PARSE_H // Qt includes #include #include // Local includes #include "mediawiki_job.h" namespace MediaWiki { class Iface; class ParsePrivate; class Parse : public Job { Q_OBJECT Q_DECLARE_PRIVATE(Parse) public: /** * @brief Indicates all possible error conditions found during the processing of the job. */ enum { /** * @brief An internal error occurred. */ InternalError= Job::UserDefinedError+1, /** * @brief The page parameter cannot be used together with the text and title parameters */ TooManyParams, /** * @brief The page you specified doesn't exist */ MissingPage }; explicit Parse(Iface& MediaWiki, QObject* const parent = nullptr); virtual ~Parse(); void setText(const QString& param); void setTitle(const QString& param); void setPageName(const QString& param); void setUseLang(const QString& param); - void start() Q_DECL_OVERRIDE; + void start() override; Q_SIGNALS: void result(const QString& text); private Q_SLOTS: void doWorkSendRequest(); void doWorkProcessReply(); }; } // namespace MediaWiki #endif // DIGIKAM_MEDIAWIKI_PARSE_H diff --git a/core/dplugins/generic/webservices/mediawiki/backend/mediawiki_queryimageinfo.h b/core/dplugins/generic/webservices/mediawiki/backend/mediawiki_queryimageinfo.h index c472bf51d9..edfac5cb95 100644 --- a/core/dplugins/generic/webservices/mediawiki/backend/mediawiki_queryimageinfo.h +++ b/core/dplugins/generic/webservices/mediawiki/backend/mediawiki_queryimageinfo.h @@ -1,167 +1,167 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2011-03-22 * Description : a Iface C++ interface * * Copyright (C) 2011-2020 by Gilles Caulier * Copyright (C) 2011 by Ludovic Delfau * * 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, 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. * * ============================================================ */ #ifndef DIGIKAM_MEDIAWIKI_QUERYIMAGEINFO_H #define DIGIKAM_MEDIAWIKI_QUERYIMAGEINFO_H // Qt includes #include // Local includes #include "mediawiki_imageinfo.h" #include "mediawiki_job.h" namespace MediaWiki { class Iface; class QueryImageinfoPrivate; /** * @brief Query image info job. * * Gets image information for an image. */ class QueryImageinfo : public Job { Q_OBJECT Q_DECLARE_PRIVATE(QueryImageinfo) public: /** * @brief Property. */ enum Property { Timestamp = 0x01, User = 0x02, Comment = 0x04, Url = 0x08, Size = 0x10, Sha1 = 0x20, Mime = 0x40, Metadata = 0x80 }; Q_DECLARE_FLAGS(Properties, Property) public: /** * @brief Constructs a query image info job. * * You can set parameters of the query after. * * By default, a single image info is returned. * * @param MediaWiki the MediaWiki concerned by the job * @param parent the QObject parent */ explicit QueryImageinfo(Iface& MediaWiki, QObject* const parent = nullptr); /** * @brief Destructs a query image info job. */ virtual ~QueryImageinfo(); /** * @brief Set the title of the image requested. * @param title the title of the image requested */ void setTitle(const QString& title); /** * @brief Set which properties to get. * @param properties properties to get */ void setProperties(Properties properties); /** * @brief Set how many image info to return per signal. * @param limit how many image info to return per signal */ void setLimit(unsigned int limit); /** * @brief If true stop the request to the first signal. * @param onlyOneSignal if true stop the request to the first signal */ void setOnlyOneSignal(bool onlyOneSignal); /** * @brief Set timestamp to start listing from. * @param begin timestamp to start listing from */ void setBeginTimestamp(const QDateTime& begin); /** * @brief Set timestamp to stop listing at. * @param end timestamp to stop listing at */ void setEndTimestamp(const QDateTime& end); /** * @brief Set width scale. * * Only for the first image info. The property URL must be set. * * @param width width scale */ void setWidthScale(unsigned int width); /** * @brief Set height scale. * * Only for the first image info. The property URL must be set. * * @param height height scale */ void setHeightScale(unsigned int height); /** * @brief Starts the job asynchronously. */ - void start() Q_DECL_OVERRIDE; + void start() override; Q_SIGNALS: /** * @brief Provides a list of imageinfos. * @param imageinfos a list of imageinfos */ void result(const QList& imageinfos); private Q_SLOTS: void doWorkSendRequest(); void doWorkProcessReply(); }; Q_DECLARE_OPERATORS_FOR_FLAGS(QueryImageinfo::Properties) } // namespace MediaWiki #endif // DIGIKAM_MEDIAWIKI_QUERYIMAGEINFO_H diff --git a/core/dplugins/generic/webservices/mediawiki/backend/mediawiki_queryimages.h b/core/dplugins/generic/webservices/mediawiki/backend/mediawiki_queryimages.h index 54de8289cb..b6a67c75eb 100644 --- a/core/dplugins/generic/webservices/mediawiki/backend/mediawiki_queryimages.h +++ b/core/dplugins/generic/webservices/mediawiki/backend/mediawiki_queryimages.h @@ -1,121 +1,121 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2011-03-22 * Description : a Iface C++ interface * * Copyright (C) 2011-2020 by Gilles Caulier * Copyright (C) 2011 by Ludovic Delfau * * 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, 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. * * ============================================================ */ #ifndef DIGIKAM_MEDIAWIKI_QUERYIMAGES_H #define DIGIKAM_MEDIAWIKI_QUERYIMAGES_H // KDE includes #include // Local includes #include "mediawiki_job.h" #include "mediawiki_image.h" namespace MediaWiki { class Iface; class QueryImagesPrivate; /** * @brief Query images job. * * Gets a list of all images used on pages. */ class QueryImages : public Job { Q_OBJECT Q_DECLARE_PRIVATE(QueryImages) public: /** * @brief Indicates all possible error conditions found during the processing of the job. */ enum { /** * @brief A network error has occurred. */ NetworkError = KJob::UserDefinedError + 1, /** * @brief A XML error has occurred. */ XmlError }; public: /** * @brief Constructs a query images job. * @param MediaWiki the MediaWiki concerned by the job * @param parent the QObject parent */ explicit QueryImages(Iface& MediaWiki, QObject* const parent = nullptr); /** * @brief Destroys a query images job. */ virtual ~QueryImages(); /** * @brief Set the title. * @param title the title of the page */ void setTitle(const QString& title); /** * @brief Set the limit. * @param limit number of images returned by one signal #pages() */ void setLimit(unsigned int limit); /** * @brief Starts the job asynchronously. */ - void start() Q_DECL_OVERRIDE; + void start() override; Q_SIGNALS: /** * @brief Provides a list of all images used on pages * * This signal can be emitted several times. * * @param pages list of all images used on pages */ void images(const QList & images); private Q_SLOTS: void doWorkSendRequest(); void doWorkProcessReply(); }; } // namespace MediaWiki #endif // DIGIKAM_MEDIAWIKI_QUERYIMAGES_H diff --git a/core/dplugins/generic/webservices/mediawiki/backend/mediawiki_queryinfo.h b/core/dplugins/generic/webservices/mediawiki/backend/mediawiki_queryinfo.h index 4d85892424..6792866be1 100644 --- a/core/dplugins/generic/webservices/mediawiki/backend/mediawiki_queryinfo.h +++ b/core/dplugins/generic/webservices/mediawiki/backend/mediawiki_queryinfo.h @@ -1,121 +1,121 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2011-03-22 * Description : a Iface C++ interface * * Copyright (C) 2011-2020 by Gilles Caulier * Copyright (C) 2011 by Alexandre Mendes * * 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, 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. * * ============================================================ */ #ifndef DIGIKAM_MEDIAWIKI_QUERYINFO_H #define DIGIKAM_MEDIAWIKI_QUERYINFO_H // Qt includes #include #include #include #include // Local includes #include "mediawiki_page.h" #include "mediawiki_protection.h" #include "mediawiki_job.h" namespace MediaWiki { class Iface; class QueryInfoPrivate; /** * @brief QueryInfo job. * * Uses to send a request to get basic page information. */ class QueryInfo : public Job { Q_OBJECT Q_DECLARE_PRIVATE(QueryInfo) public: /** * @brief Constructs a QueryInfo job. * @param MediaWiki the MediaWiki concerned by the job * @param parent the QObject parent */ explicit QueryInfo(Iface& MediaWiki, QObject* const parent = nullptr); /** * @brief Destroys the QuerySiteInfoGeneral job. */ virtual ~QueryInfo(); /** * @brief Starts the job asynchronously. */ - void start() Q_DECL_OVERRIDE; + void start() override; /** * @brief Set the page name. * @param title the page name */ void setPageName(const QString& title); /** * @brief Set the token to perform a data-modifying action on a page * @param token the token */ void setToken(const QString& token); /** * @brief Set the page id. * @param id the page id */ void setPageId(unsigned int id); /** * @brief Set the page revision * @param id the page revision */ void setRevisionId(unsigned int id); Q_SIGNALS: /** * @brief Provides a page * @param */ void page(const Page& p); void protection(const QVector& protect); private Q_SLOTS: /** * @brief Send a request. */ void doWorkSendRequest(); void doWorkProcessReply(); }; } // namespace MediaWiki #endif // DIGIKAM_MEDIAWIKI_QUERYINFO_H diff --git a/core/dplugins/generic/webservices/mediawiki/backend/mediawiki_queryrevision.h b/core/dplugins/generic/webservices/mediawiki/backend/mediawiki_queryrevision.h index 93df1d9435..9ee4f25993 100644 --- a/core/dplugins/generic/webservices/mediawiki/backend/mediawiki_queryrevision.h +++ b/core/dplugins/generic/webservices/mediawiki/backend/mediawiki_queryrevision.h @@ -1,276 +1,276 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2011-03-22 * Description : a Iface C++ interface * * Copyright (C) 2011-2020 by Gilles Caulier * Copyright (C) 2011 by Hormiere Guillaume * Copyright (C) 2011 by Manuel Campomanes * * 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, 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. * * ============================================================ */ #ifndef DIGIKAM_MEDIAWIKI_QUERYREVISION_H #define DIGIKAM_MEDIAWIKI_QUERYREVISION_H // Qt includes #include #include #include // Local includes #include "mediawiki_job.h" #include "mediawiki_revision.h" namespace MediaWiki { class Iface; class QueryRevisionPrivate; /** * @brief QueryRevision job. * * Uses for fetch a revision information about one pages of the wiki. */ class QueryRevision : public Job { Q_OBJECT Q_DECLARE_PRIVATE(QueryRevision) public: /** * @brief Direction to list revisions. */ enum Direction { /** * @brief List newest revisions first. */ Older, /** * @brief List oldest revisions first. */ Newer }; /** * @brief Tokens can get for each revision. */ enum Token { /** * @brief Rollback token. */ Rollback }; /** * @brief Indicates all possible error conditions found during the processing of the job. */ enum { /** * @brief The revids= parameter may not be used with the list options (limit, startid, endid, dirNewer, start, end). */ WrongRevisionId = Job::UserDefinedError + 1, /** * @brief titles, pageids or a generator was used to supply multiple pages, but the limit, startid, endid, dirNewer, user, excludeuser, start and end parameters may only be used on a single page. */ MultiPagesNotAllowed, /** * @brief The current user is not allowed to read title. */ TitleAccessDenied, /** * @brief start and startid or end and endid or user and excludeuser cannot be used together */ TooManyParams, /** * @brief There is no section section in rrevid */ SectionNotFound }; /** * @brief Property. */ enum Property { Ids = 0x01, Flags = 0x02, Timestamp = 0x04, User = 0x08, Comment = 0x10, Size = 0x20, Content = 0x40 }; Q_DECLARE_FLAGS(Properties, Property) public: /** * @brief Constructs a Revision job. * @param MediaWiki the MediaWiki concerned by the job * @param parent the QObject parent */ explicit QueryRevision(Iface& MediaWiki, QObject* const parent = nullptr); /** * @brief Destroys the QueryRevision job. */ virtual ~QueryRevision(); /** * @brief Starts the job asynchronously. */ - void start() Q_DECL_OVERRIDE; + void start() override; /** * @brief Set the page id. * @param pageId the page id */ void setPageId(unsigned int pageId); /** * @param Set the revision id. * @param revisionId the revision id */ void setRevisionId(unsigned int revisionId); /** * @brief Set the page name. * @param pageName the page name */ void setPageName(const QString& pageName); /** * @brief Which properties to get for each revision. * @param properties properties to get for each revision */ void setProperties(Properties properties); /** * @brief Set the maximum number of revisions to return. * @param limit the maximum number of revisions to return */ void setLimit(int limit); /** * @brief Set the revision ID to start listing from. * @param startId the revision ID to start listing from */ void setStartId(int startId); /** * @brief Set the revision ID to stop listing at. * @param endId the revision ID to stop listing at */ void setEndId(int endId); /** * @brief Set the timestamp to start listing from. * @param start the timestamp to start listing from */ void setStartTimestamp(const QDateTime& start); /** * @brief Set the timestamp to end listing at. * @param end the timestamp to end listing at */ void setEndTimestamp(const QDateTime& end); /** * @brief Set the user. * * Do list revisions made by this user. * * @param user the user */ void setUser(const QString& user); /** * @brief Set the user to exclude. * * Do not list revisions made by this user * * @param excludeUser the user to exclude */ void setExcludeUser(const QString& excludeUser); /** * @brief Set the direction to list revisions. * @param direction the direction to list revisions */ void setDirection(QueryRevision::Direction direction); /** * @brief Set XML generation to parse tree for revision content. * @param generateXML if true set XML generation to parse tree for revision content */ void setGenerateXML(bool generateXML); /** * @brief Set the section. * * If the property content is set, only retrieve the contents of this section. * * @param section the section */ void setSection(int section); /** * @brief Set the token to get for each revision. * @param token the token to get for each revision */ void setToken(QueryRevision::Token token); /** * @brief Set expand templates. * * Only if the property content is set. * * @param expandTemplates if true set expand templates */ void setExpandTemplates(bool expandTemplates); Q_SIGNALS: /** * @brief Provides a list of all user groups. * @param revision list of all user groups */ void revision(const QList& revision); private Q_SLOTS: void doWorkSendRequest(); void doWorkProcessReply(); }; Q_DECLARE_OPERATORS_FOR_FLAGS(QueryRevision::Properties) } // namespace MediaWiki #endif // DIGIKAM_MEDIAWIKI_QUERYREVISION_H diff --git a/core/dplugins/generic/webservices/mediawiki/backend/mediawiki_querysiteinfogeneral.h b/core/dplugins/generic/webservices/mediawiki/backend/mediawiki_querysiteinfogeneral.h index 0ab49c73b1..94923c36fe 100644 --- a/core/dplugins/generic/webservices/mediawiki/backend/mediawiki_querysiteinfogeneral.h +++ b/core/dplugins/generic/webservices/mediawiki/backend/mediawiki_querysiteinfogeneral.h @@ -1,97 +1,97 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2011-03-22 * Description : a Iface C++ interface * * Copyright (C) 2011-2020 by Gilles Caulier * Copyright (C) 2011 by Hormiere Guillaume * Copyright (C) 2011 by Manuel Campomanes * * 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, 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. * * ============================================================ */ #ifndef DIGIKAM_MEDIAWIKI_QUERYSITEINFOGENERAL_H #define DIGIKAM_MEDIAWIKI_QUERYSITEINFOGENERAL_H // Qt includes #include #include // Local includes #include "mediawiki_job.h" #include "mediawiki_generalinfo.h" namespace MediaWiki { class Iface; class QuerySiteInfoGeneralPrivate; /** * @brief QuerySiteInfoGeneral job. * * Uses for fetch a generals information about the wiki. */ class QuerySiteInfoGeneral : public Job { Q_OBJECT Q_DECLARE_PRIVATE(QuerySiteInfoGeneral) public: enum { IncludeAllDenied = Job::UserDefinedError + 1 }; public: /** * @brief Constructs a QuerySiteInfoGeneral job. * @param MediaWiki the MediaWiki concerned by the job * @param parent the QObject parent */ explicit QuerySiteInfoGeneral(Iface& MediaWiki, QObject* const parent = nullptr); /** * @brief Destroys the QuerySiteInfoGeneral job. */ virtual ~QuerySiteInfoGeneral(); /** * @brief Starts the job asynchronously. */ - void start() Q_DECL_OVERRIDE; + void start() override; Q_SIGNALS: /** * @brief Provide general info. * @param generalinfo the general info */ void result(const Generalinfo& generalinfo); private Q_SLOTS: void doWorkSendRequest(); void doWorkProcessReply(); }; } // namespace MediaWiki #endif // DIGIKAM_MEDIAWIKI_QUERYSITEINFOGENERAL_H diff --git a/core/dplugins/generic/webservices/mediawiki/backend/mediawiki_querysiteinfousergroups.h b/core/dplugins/generic/webservices/mediawiki/backend/mediawiki_querysiteinfousergroups.h index 7af0a535aa..baaf7ce00f 100644 --- a/core/dplugins/generic/webservices/mediawiki/backend/mediawiki_querysiteinfousergroups.h +++ b/core/dplugins/generic/webservices/mediawiki/backend/mediawiki_querysiteinfousergroups.h @@ -1,91 +1,91 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2011-03-22 * Description : a Iface C++ interface * * Copyright (C) 2011-2020 by Gilles Caulier * Copyright (C) 2011 by Ludovic Delfau * * 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, 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. * * ============================================================ */ #ifndef DIGIKAM_MEDIAWIKI_QUERYSITEINFOUSERGROUPS_H #define DIGIKAM_MEDIAWIKI_QUERYSITEINFOUSERGROUPS_H // Local includes #include "mediawiki_job.h" #include "mediawiki_usergroup.h" namespace MediaWiki { class Iface; class QuerySiteinfoUsergroupsPrivate; /** * @brief UserGroups job. * * Uses for fetch a list of all user groups and their permissions. */ class QuerySiteinfoUsergroups : public Job { Q_OBJECT Q_DECLARE_PRIVATE(QuerySiteinfoUsergroups) public: /** * @brief Constructs a UserGroups job. * @param MediaWiki the MediaWiki concerned by the job * @param parent the QObject parent */ explicit QuerySiteinfoUsergroups(Iface& MediaWiki, QObject* const parent = nullptr); /** * @brief Destroys the UserGroups job. */ virtual ~QuerySiteinfoUsergroups(); /** * @brief If true number of users of each user group is included * @param includeNumber if true number of users of each user group is included */ void setIncludeNumber(bool includeNumber); /** * @brief Starts the job asynchronously. */ - void start() Q_DECL_OVERRIDE; + void start() override; Q_SIGNALS: /** * @brief Provides a list of all user groups. * @param usergroups list of all user groups * @see QuerySiteinfoUsergroups::Result */ void usergroups(const QList& usergroups); private Q_SLOTS: void doWorkSendRequest(); void doWorkProcessReply(); }; } // namespace MediaWiki #endif // DIGIKAM_MEDIAWIKI_QUERYSITEINFOUSERGROUPS_H diff --git a/core/dplugins/generic/webservices/mediawiki/backend/mediawiki_upload.h b/core/dplugins/generic/webservices/mediawiki/backend/mediawiki_upload.h index 4849a6d0e0..41f4ad4581 100644 --- a/core/dplugins/generic/webservices/mediawiki/backend/mediawiki_upload.h +++ b/core/dplugins/generic/webservices/mediawiki/backend/mediawiki_upload.h @@ -1,176 +1,176 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2011-03-22 * Description : a Iface C++ interface * * Copyright (C) 2011-2020 by Gilles Caulier * Copyright (C) 2011 by Alexandre Mendes * * 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, 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. * * ============================================================ */ #ifndef DIGIKAM_MEDIAWIKI_UPLOAD_H #define DIGIKAM_MEDIAWIKI_UPLOAD_H // Qt includes #include #include #include #include #include // Local includes #include "mediawiki_job.h" #include "mediawiki_queryinfo.h" namespace MediaWiki { class Iface; class UploadPrivate; /** * @brief Upload job. * * Uses for upload files. */ class Upload : public Job { Q_OBJECT Q_DECLARE_PRIVATE(Upload) public: enum { /** * @brief An internal error occurred. */ InternalError= Job::UserDefinedError+1, /** * @brief The module is disabled. */ UploadDisabled, /** * @brief The session key is invalid. */ InvalidSessionKey, /** * @brief The current user can't upload. */ BadAccess, /** * @brief A param is missing. */ ParamMissing, /** * @brief No upload without logged in. */ MustBeLoggedIn, /** * @brief */ FetchFileError, /** * @brief No upload module set. */ NoModule, /** * @brief The file submitted was empty. */ EmptyFile, /** * @brief The file is missing an extension. */ ExtensionMissing, /** * @brief The filename is too short. */ TooShortFilename, /** * @brief Overwriting an existing file is not allowed. */ OverWriting, /** * @brief Stashing temporary file failed. */ StashFailed }; public: /** * @brief Constructs an Upload job. * @param parent the QObject parent */ Upload(Iface& MediaWiki, QObject* const parent = nullptr); /** * @brief Destroys the Upload job. */ virtual ~Upload(); /** * @brief Starts the job asynchronously. */ - void start() Q_DECL_OVERRIDE; + void start() override; /** * @brief Set the target filename. * @param filename the filename */ void setFilename(const QString&); /** * @brief Set the file. * @param file the file */ void setFile(QIODevice* const); /** * @brief Set the upload comment. Also used as the initial page text for new files if text parameter not provided. * @param comment the comment */ void setComment(const QString&); /** * @brief Set the information of the image. Use this template {{Information|Description=|Source=|Date=|Author=|Permission=|other_versions=}}. * @param text the text */ void setText(const QString&); private Q_SLOTS: void doWorkSendRequest(Page page); void doWorkProcessReply(); }; } // namespace MediaWiki #endif // UPLOAD_H diff --git a/core/dplugins/generic/webservices/mediawiki/mediawikitalker.h b/core/dplugins/generic/webservices/mediawiki/mediawikitalker.h index 1949a69bd4..9baf14ffa5 100644 --- a/core/dplugins/generic/webservices/mediawiki/mediawikitalker.h +++ b/core/dplugins/generic/webservices/mediawiki/mediawikitalker.h @@ -1,89 +1,89 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2011-02-11 * Description : a tool to export images to WikiMedia web service * * Copyright (C) 2011 by Alexandre Mendes * Copyright (C) 2011-2020 by Gilles Caulier * * 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, 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. * * ============================================================ */ #ifndef DIGIKAM_MEDIAWIKI_TALKER_H #define DIGIKAM_MEDIAWIKI_TALKER_H // Qt includes #include #include #include #include // KDE includes #include // Local includes #include "dinfointerface.h" using namespace Digikam; namespace MediaWiki { class Iface; } using namespace MediaWiki; namespace DigikamGenericMediaWikiPlugin { class MediaWikiTalker : public KJob { Q_OBJECT public: explicit MediaWikiTalker(DInfoInterface* const iface, Iface* const MediaWiki, QObject* const parent=nullptr); ~MediaWikiTalker(); public: QString buildWikiText(const QMap& info) const; void setImageMap(const QMap >& imageDesc); - void start() Q_DECL_OVERRIDE; + void start() override; Q_SIGNALS: void signalUploadProgress(int percent); void signalEndUpload(); public Q_SLOTS: void slotBegin(); void slotUploadHandle(KJob* j = nullptr); void slotUploadProgress(KJob* job, unsigned long percent); private: class Private; Private* const d; }; } // namespace DigikamGenericMediaWikiPlugin #endif // DIGIKAM_MEDIAWIKI_TALKER_H diff --git a/core/dplugins/generic/webservices/vkontakte/CMakeLists.txt b/core/dplugins/generic/webservices/vkontakte/CMakeLists.txt index c19d49096c..53ddb5c0f1 100644 --- a/core/dplugins/generic/webservices/vkontakte/CMakeLists.txt +++ b/core/dplugins/generic/webservices/vkontakte/CMakeLists.txt @@ -1,31 +1,85 @@ # # Copyright (c) 2010-2020 by Gilles Caulier, # Copyright (c) 2018 by Maik Qualmann, # # Redistribution and use is allowed according to the terms of the BSD license. # For details see the accompanying COPYING-CMAKE-SCRIPTS file. APPLY_COMMON_POLICIES() include(MacroDPlugins) include_directories($ $ $ $ - $ + $ ) +if(ENABLE_QWEBENGINE) + include_directories($) +else() + include_directories($) +endif() + +# -------------------------------------------------------------------------------- + +set(libvkontakte_SRCS + ${CMAKE_CURRENT_SOURCE_DIR}/backend/vkontakte_vkapi.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/backend/vkontakte_getapplicationpermissionsjob.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/backend/vkontakte_authenticationdialog.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/backend/vkontakte_util.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/backend/vkontakte_jobs.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/backend/vkontakte_userinfo.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/backend/vkontakte_userinfojob.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/backend/vkontakte_albuminfo.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/backend/vkontakte_albumlistjob.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/backend/vkontakte_createalbumjob.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/backend/vkontakte_editalbumjob.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/backend/vkontakte_deletealbumjob.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/backend/vkontakte_photoinfo.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/backend/vkontakte_photolistjob.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/backend/vkontakte_photojob.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/backend/vkontakte_getphotouploadserverjob.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/backend/vkontakte_photopostjob.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/backend/vkontakte_savephotojob.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/backend/vkontakte_uploadphotosjob.cpp + +# Not used (old implementation or not ported) +# ${CMAKE_CURRENT_SOURCE_DIR}/backend/attic/vkontakte_notejob.cpp +# ${CMAKE_CURRENT_SOURCE_DIR}/backend/attic/vkontakte_cidsnamesjob.cpp +# ${CMAKE_CURRENT_SOURCE_DIR}/backend/attic/vkontakte_messageinfo.cpp +# ${CMAKE_CURRENT_SOURCE_DIR}/backend/attic/vkontakte_discussionslistjob.cpp +# ${CMAKE_CURRENT_SOURCE_DIR}/backend/attic/vkontakte_messageslistjob.cpp +# ${CMAKE_CURRENT_SOURCE_DIR}/backend/attic/vkontakte_noteinfo.cpp +# ${CMAKE_CURRENT_SOURCE_DIR}/backend/attic/vkontakte_noteaddjob.cpp +# ${CMAKE_CURRENT_SOURCE_DIR}/backend/attic/vkontakte_allmessageslistjob.cpp +# ${CMAKE_CURRENT_SOURCE_DIR}/backend/attic/vkontakte_groupinfo.cpp +# ${CMAKE_CURRENT_SOURCE_DIR}/backend/attic/vkontakte_allnoteslistjob.cpp +# ${CMAKE_CURRENT_SOURCE_DIR}/backend/attic/vkontakte_noteslistjob.cpp +# ${CMAKE_CURRENT_SOURCE_DIR}/backend/attic/vkontakte_friendlistjob.cpp + +# Deprecated classes +# ${CMAKE_CURRENT_SOURCE_DIR}/backend/deprecated/vkontakte_getvariablejob.cpp +# ${CMAKE_CURRENT_SOURCE_DIR}/backend/deprecated/vkontakte_getinfojob.cpp +# ${CMAKE_CURRENT_SOURCE_DIR}/backend/deprecated/vkontakte_grouplistjob.cpp +) + +# For unit-tests and plugin +add_library(vkontaktebackend STATIC ${libvkontakte_SRCS}) + +# -------------------------------------------------------------------------------- + set(vkontakteplugin_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/vkalbumchooser.cpp ${CMAKE_CURRENT_SOURCE_DIR}/vkauthwidget.cpp ${CMAKE_CURRENT_SOURCE_DIR}/vknewalbumdlg.cpp ${CMAKE_CURRENT_SOURCE_DIR}/vkplugin.cpp ${CMAKE_CURRENT_SOURCE_DIR}/vkwindow.cpp ) DIGIKAM_ADD_GENERIC_PLUGIN(NAME VKontakte SOURCES ${vkontakteplugin_SRCS} - DEPENDS KF5::Vkontakte + DEPENDS vkontaktebackend ${QWEB_BACKEND} KF5::KIOCore ) diff --git a/core/dplugins/generic/webservices/vkontakte/backend/TODO b/core/dplugins/generic/webservices/vkontakte/backend/TODO new file mode 100644 index 0000000000..af75fa394d --- /dev/null +++ b/core/dplugins/generic/webservices/vkontakte/backend/TODO @@ -0,0 +1,7 @@ +* Private messages are truncated at around 2000 characters (vkontakte.ru issue?) +* Sending private messages +* Handle attachments in private messages +* vkontakte.ru support in Telepathy-KDE and/or Kopete (how about a messaging framework that could be used for both Kopete and Telepathy-KDE?) +* News from vkontakte.ru in Akregator (http://vkontakte.ru/developers.php?o=-1&p=newsfeed.get) +* Write to VKontakte administration via paper mail about their logo +* Fix tests when user's default language is not Russian. diff --git a/core/dplugins/generic/webservices/vkontakte/backend/attic/vkontakte_allmessageslistjob.cpp b/core/dplugins/generic/webservices/vkontakte/backend/attic/vkontakte_allmessageslistjob.cpp new file mode 100644 index 0000000000..9f982a29e9 --- /dev/null +++ b/core/dplugins/generic/webservices/vkontakte/backend/attic/vkontakte_allmessageslistjob.cpp @@ -0,0 +1,178 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * https://www.digikam.org + * + * Date : 2011-02-19 + * Description : a tool to export images to VKontakte web service + * + * Copyright (C) 2011-2015 by Alexander Potashev + * Copyright (C) 2011-2020 by Gilles Caulier + * + * 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, 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. + * + * ============================================================ */ + +#include "vkontakte_allmessageslistjob.h" + +// KDE includes + +#include + +// Local includes + +#include "vkontakte_messageslistjob.h" +#include "digikam_debug.h" + +namespace Vkontakte +{ + +class Q_DECL_HIDDEN AllMessagesListJob::Private +{ +public: + + QString accessToken; ///< Vkontakte Access token + int out; + int previewLength; + int filters; + int timeOffset; + + int totalCount[2]; + QList list; +}; + +AllMessagesListJob::AllMessagesListJob(const QString& accessToken, + int out, int previewLength, + int filters, int timeOffset) + : KJobWithSubjobs(), + d(new Private) +{ + d->accessToken = accessToken; + d->out = out; + d->previewLength = previewLength; + d->filters = filters; + d->timeOffset = timeOffset; + d->totalCount[0] = -1; // for incoming messages + d->totalCount[1] = -1; // for outgoing messages +} + +AllMessagesListJob::~AllMessagesListJob() +{ + delete d; +} + +void AllMessagesListJob::startNewJob(int offset, int count, int out) +{ + Q_ASSERT(out == 0 || out == 1); + + MessagesListJob* const job = new MessagesListJob(d->accessToken, out, + offset, count, + d->previewLength, d->filters, d->timeOffset); + connect(job, SIGNAL(result(KJob*)), + this, SLOT(jobFinished(KJob*))); + + m_jobs.append(job); + job->start(); +} + +void AllMessagesListJob::start() +{ + // out=-1 means to retrieve both incoming and outgoing messages + + if (d->out == 0 || d->out == -1) // incoming + { + startNewJob(0, 100, 0); + } + + if (d->out == 1 || d->out == -1) // outgoing + { + startNewJob(0, 100, 1); + } +} + +void AllMessagesListJob::jobFinished(KJob *kjob) +{ + MessagesListJob* const job = dynamic_cast(kjob); + Q_ASSERT(job); + + if (!job) + { + return; + } + + m_jobs.removeAll(job); + + if (job->error()) + { + setError(job->error()); + setErrorText(job->errorText()); + qCWarning(DIGIKAM_WEBSERVICES_LOG) << "Job error: " << job->errorString(); + + return; + } + + d->list.append(job->list()); + + int out = job->out(); // incoming or outgoing + Q_ASSERT(out == 0 || out == 1); + + // If this was the first job, start all others + + if (d->totalCount[out] == -1) + { + d->totalCount[out] = job->totalCount(); + + for (int offset = 100 ; offset < d->totalCount[out] ; offset += 100) + { + startNewJob(offset, qMin(100, d->totalCount[out] - offset), out); + } + } + else if (d->totalCount[out] != job->totalCount()) + { + // TODO: some new messages might have been added, what should we do then? + doKill(); + setError(KJob::UserDefinedError + 2); + + if (out == 1) + { + setErrorText(i18n("The number of outgoing messages has changed between requests.")); + } + else // out == 0 + { + setErrorText(i18n("The number of incoming messages has changed between requests.")); + } + + qCWarning(DIGIKAM_WEBSERVICES_LOG) << "Job error: " << job->errorString(); + emitResult(); + + return; + } + + // All jobs have finished + + if (m_jobs.size() == 0) + { + qSort(d->list); // sort by message ID (which should be equivalent to sorting by date) + emitResult(); + } +} + +QList AllMessagesListJob::list() const +{ + return d->list; +} + +int AllMessagesListJob::count() const +{ + return d->list.size(); +} + +} // namespace Vkontakte diff --git a/core/dplugins/generic/webservices/vkontakte/backend/attic/vkontakte_allmessageslistjob.h b/core/dplugins/generic/webservices/vkontakte/backend/attic/vkontakte_allmessageslistjob.h new file mode 100644 index 0000000000..8ca5aa219e --- /dev/null +++ b/core/dplugins/generic/webservices/vkontakte/backend/attic/vkontakte_allmessageslistjob.h @@ -0,0 +1,75 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * https://www.digikam.org + * + * Date : 2011-02-19 + * Description : a tool to export images to VKontakte web service + * + * Copyright (C) 2011-2015 by Alexander Potashev + * Copyright (C) 2011-2020 by Gilles Caulier + * + * 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, 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. + * + * ============================================================ */ + +#ifndef DIGIKAM_VKONTAKTE_ALLMESSAGESLISTJOB_H +#define DIGIKAM_VKONTAKTE_ALLMESSAGESLISTJOB_H + +// Local includes + +#include "vkontakte_messageslistjob.h" + +namespace Vkontakte +{ + +class AllMessagesListJob : public KJobWithSubjobs +{ + Q_OBJECT + +public: + + /** + * @brief ... + * + * @param accessToken ... + * @param out -1 means to retrieve both incoming and outgoing messages. Defaults to -1. + * @param previewLength ... Defaults to 0. + * @param filters ... Defaults to 0. + * @param timeOffset ... Defaults to 0. + **/ + explicit AllMessagesListJob(const QString& accessToken, + int out = -1, int previewLength = 0, + int filters = 0, int timeOffset = 0); + ~AllMessagesListJob(); + + void start() override; + + QList list() const; + int count() const; + +protected: + + void startNewJob(int offset, int count, int out); + +private Q_SLOTS: + + void jobFinished(KJob* kjob); + +private: + + class Private; + Private* const d; +}; + +} // namespace Vkontakte + +#endif // DIGIKAM_VKONTAKTE_ALLMESSAGESLISTJOB_H diff --git a/core/dplugins/generic/webservices/vkontakte/backend/attic/vkontakte_allnoteslistjob.cpp b/core/dplugins/generic/webservices/vkontakte/backend/attic/vkontakte_allnoteslistjob.cpp new file mode 100644 index 0000000000..3ff72e4a7b --- /dev/null +++ b/core/dplugins/generic/webservices/vkontakte/backend/attic/vkontakte_allnoteslistjob.cpp @@ -0,0 +1,144 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * https://www.digikam.org + * + * Date : 2011-02-19 + * Description : a tool to export images to VKontakte web service + * + * Copyright (C) 2011-2015 by Alexander Potashev + * Copyright (C) 2011-2020 by Gilles Caulier + * + * 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, 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. + * + * ============================================================ */ + +#include "vkontakte_allnoteslistjob.h" + +// KDE includes + +#include + +// Local includes + +#include "digikam_debug.h" + +namespace Vkontakte +{ + +class AllNotesListJob::Private +{ +public: + + QString accessToken; + int uid; + int totalCount; + QList list; +}; + +AllNotesListJob::AllNotesListJob(const QString& accessToken, int uid) + : KJobWithSubjobs(), + d(new Private) +{ + d->accessToken = accessToken; + d->uid = uid; + d->totalCount = -1; +} + +AllNotesListJob::~AllNotesListJob() +{ + delete d; +} + +void AllNotesListJob::startNewJob(int offset, int count) +{ + NotesListJob* const job = new NotesListJob(d->accessToken, d->uid, offset, count); + + connect(job, SIGNAL(result(KJob*)), + this, SLOT(jobFinished(KJob*))); + + m_jobs.append(job); + job->start(); +} + +void AllNotesListJob::start() +{ + startNewJob(0, 100); +} + +void AllNotesListJob::jobFinished(KJob *kjob) +{ + NotesListJob* const job = dynamic_cast(kjob); + + Q_ASSERT(job); + + if (!job) + { + return; + } + + m_jobs.removeAll(job); + + if (job->error()) + { + setError(job->error()); + setErrorText(job->errorText()); + qCWarning(DIGIKAM_WEBSERVICES_LOG) << "Job error: " << job->errorString(); + + return; + } + + d->list.append(job->list()); + + // If this was the first job, start all others + + if (d->totalCount == -1) + { + d->totalCount = job->totalCount(); + + for (int offset = 100; offset < d->totalCount; offset += 100) + { + startNewJob(offset, qMin(100, d->totalCount - offset)); + } + } + else if (d->totalCount != job->totalCount()) + { + // TODO: some new notes might have been added, what should we do then? + + doKill(); + setError(KJob::UserDefinedError + 1); + setErrorText(i18n("The number of notes has changed between requests.")); + qCWarning(DIGIKAM_WEBSERVICES_LOG) << "Job error: " << job->errorString(); + emitResult(); + + return; + } + + // All jobs have finished + + if (m_jobs.size() == 0) + { +// qSort(list); // sort by message ID (which should be equivalent to sorting by date) + emitResult(); + } +} + +QList AllNotesListJob::list() const +{ + return d->list; +} + +int AllNotesListJob::count() const +{ + return d->list.size(); +} + +} // namespace Vkontakte diff --git a/core/dplugins/generic/webservices/vkontakte/backend/attic/vkontakte_allnoteslistjob.h b/core/dplugins/generic/webservices/vkontakte/backend/attic/vkontakte_allnoteslistjob.h new file mode 100644 index 0000000000..d794153a8c --- /dev/null +++ b/core/dplugins/generic/webservices/vkontakte/backend/attic/vkontakte_allnoteslistjob.h @@ -0,0 +1,64 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * https://www.digikam.org + * + * Date : 2011-02-19 + * Description : a tool to export images to VKontakte web service + * + * Copyright (C) 2011-2015 by Alexander Potashev + * Copyright (C) 2011-2020 by Gilles Caulier + * + * 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, 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. + * + * ============================================================ */ + +#ifndef DIGIKAM_VKONTAKTE_ALLNOTESLISTJOB_H +#define DIGIKAM_VKONTAKTE_ALLNOTESLISTJOB_H + +// Local includes + +#include "vkontakte_noteslistjob.h" + +namespace Vkontakte +{ + +class AllNotesListJob : public KJobWithSubjobs +{ + Q_OBJECT + +public: + + explicit AllNotesListJob(const QString& accessToken, int uid); + ~AllNotesListJob(); + + void start() override; + + QList list() const; + int count() const; + +protected: + + void startNewJob(int offset, int count); + +private Q_SLOTS: + + void jobFinished(KJob* kjob); + +private: + + class Private; + Private* const d; +}; + +} // namespace Vkontakte + +#endif // DIGIKAM_VKONTAKTE_ALLNOTESLISTJOB_H diff --git a/core/dplugins/generic/webservices/vkontakte/backend/attic/vkontakte_cidsnamesjob.cpp b/core/dplugins/generic/webservices/vkontakte/backend/attic/vkontakte_cidsnamesjob.cpp new file mode 100644 index 0000000000..6de5a06379 --- /dev/null +++ b/core/dplugins/generic/webservices/vkontakte/backend/attic/vkontakte_cidsnamesjob.cpp @@ -0,0 +1,77 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * https://www.digikam.org + * + * Date : 2011-02-19 + * Description : a tool to export images to VKontakte web service + * + * Copyright (C) 2011-2015 by Alexander Potashev + * Copyright (C) 2011-2020 by Gilles Caulier + * + * 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, 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. + * + * ============================================================ */ + +#include "vkontakte_cidsnamesjob.h" + +// Qt includes + +#include +#include + +namespace Vkontakte +{ + +class Q_DECL_HIDDEN CidsNamesJob::Private +{ +public: + + QIntList cids; + QMap names; // cid -> name +}; + +// https://vk.com/dev/database.getCountriesById +// TODO: access token is not needed for this call + +CidsNamesJob::CidsNamesJob(const QString& method, + const QString& accessToken, + const QIntList& cids) + : VkontakteJob(accessToken, method), + d(new Private) +{ + d->cids = cids; + addQueryItem("cids", cids.join()); +} + +CidsNamesJob::~CidsNamesJob() +{ + delete d; +} + +void CidsNamesJob::handleData(const QVariant& data) +{ + // We need QMap, because VK server might reorder results + // (I did not check it, but they do not guarantee the same order as in "cids") + + foreach (const QVariant& item, data.toList()) + { + QVariantMap item_map = item.toMap(); + d->names[item_map["cid"].toInt()] = item_map["name"].toString(); + } +} + +QMap CidsNamesJob::names() const +{ + return d->names; +} + +} // namespace Vkontakte diff --git a/core/dplugins/generic/webservices/vkontakte/backend/attic/vkontakte_cidsnamesjob.h b/core/dplugins/generic/webservices/vkontakte/backend/attic/vkontakte_cidsnamesjob.h new file mode 100644 index 0000000000..ce57e63221 --- /dev/null +++ b/core/dplugins/generic/webservices/vkontakte/backend/attic/vkontakte_cidsnamesjob.h @@ -0,0 +1,62 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * https://www.digikam.org + * + * Date : 2011-02-19 + * Description : a tool to export images to VKontakte web service + * + * Copyright (C) 2011-2015 by Alexander Potashev + * Copyright (C) 2011-2020 by Gilles Caulier + * + * 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, 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. + * + * ============================================================ */ + +#ifndef DIGIKAM_VKONTAKTE_CIDSNAMESJOB_H +#define DIGIKAM_VKONTAKTE_CIDSNAMESJOB_H + +// Qt includes + +#include + +// Local includes + +#include "vkontakte_jobs.h" +#include "vkontakte_qintlist.h" + +namespace Vkontakte +{ + +class CidsNamesJob : public VkontakteJob +{ +public: + + explicit CidsNamesJob(const QString& method, + const QString& accessToken, + const QIntList& cids); + ~CidsNamesJob(); + + QMap names() const; + +protected: + + void handleData(const QVariant& data) override; + +private: + + class Private; + Private* const d; +}; + +} // namespace Vkontakte + +#endif // DIGIKAM_VKONTAKTE_CIDSNAMESJOB_H diff --git a/core/dplugins/generic/webservices/vkontakte/backend/attic/vkontakte_discussionslistjob.cpp b/core/dplugins/generic/webservices/vkontakte/backend/attic/vkontakte_discussionslistjob.cpp new file mode 100644 index 0000000000..6202a8e547 --- /dev/null +++ b/core/dplugins/generic/webservices/vkontakte/backend/attic/vkontakte_discussionslistjob.cpp @@ -0,0 +1,86 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * https://www.digikam.org + * + * Date : 2011-02-19 + * Description : a tool to export images to VKontakte web service + * + * Copyright (C) 2011-2015 by Alexander Potashev + * Copyright (C) 2011-2020 by Gilles Caulier + * + * 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, 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. + * + * ============================================================ */ + +#include "vkontakte_discussionslistjob.h" + +// Qt includes + +#include + +namespace Vkontakte +{ + +class Q_DECL_HIDDEN DiscussionsListJob::Private +{ +public: + + int totalCount; ///< number of all discussions, not only discussions retrieved in this request + QList list; +}; + +DiscussionsListJob::DiscussionsListJob(const QString& accessToken, int offset, int count, int previewLength) + : VkontakteJob(accessToken, "messages.getDialogs"), + d(new Private) +{ + addQueryItem("offset", QString::number(offset)); + addQueryItem("count", QString::number(count)); + addQueryItem("preview_length", QString::number(previewLength)); +} + +DiscussionsListJob::~DiscussionsListJob() +{ + delete d; +} + +void DiscussionsListJob::handleItem(const QVariant& data) +{ + MessageInfoPtr item(new MessageInfo()); + QJson::QObjectHelper::qvariant2qobject(data.toMap(), item.data()); + d->list.append(item); +} + +void DiscussionsListJob::handleData(const QVariant& data) +{ + QVariantList list = data.toList(); + d->totalCount = list[0].toInt(); + list.pop_front(); + + foreach (const QVariant& item, list) + { + handleItem(item); + } + + qSort(d->list); // sort by message ID (which should be equivalent to sorting by date) +} + +QList DiscussionsListJob::list() const +{ + return d->list; +} + +int DiscussionsListJob::totalCount() const +{ + return d->totalCount; +} + +} // namespace Vkontakte diff --git a/core/dplugins/generic/webservices/vkontakte/backend/attic/vkontakte_discussionslistjob.h b/core/dplugins/generic/webservices/vkontakte/backend/attic/vkontakte_discussionslistjob.h new file mode 100644 index 0000000000..357e3a27dc --- /dev/null +++ b/core/dplugins/generic/webservices/vkontakte/backend/attic/vkontakte_discussionslistjob.h @@ -0,0 +1,69 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * https://www.digikam.org + * + * Date : 2011-02-19 + * Description : a tool to export images to VKontakte web service + * + * Copyright (C) 2011-2015 by Alexander Potashev + * Copyright (C) 2011-2020 by Gilles Caulier + * + * 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, 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. + * + * ============================================================ */ + +#ifndef DIGIKAM_VKONTAKTE_DISCUSSIONSLISTJOB_H +#define DIGIKAM_VKONTAKTE_DISCUSSIONSLISTJOB_H + +// Local includes + +#include "vkontakte_jobs.h" +#include "vkontakte_messageinfo.h" + +namespace Vkontakte +{ + +class DiscussionsListJob : public VkontakteJob +{ +public: + + /** + * @brief Constructs a job to retrieve an array of discussions from vk.com. + * + * @param accessToken vk.com access token. + * @param offset Offset in the list of all discussions. Defaults to 0. + * @param count Number of discussions to retrieve. Defaults to 100. + * @param previewLength 0 means to not truncate messages. Defaults to 0. + */ + explicit DiscussionsListJob(const QString& accessToken, + int offset = 0, + int count = 100, + int previewLength = 0); + ~DiscussionsListJob(); + + QList list() const; + int totalCount() const; + +protected: + + void handleData(const QVariant& data) override; + void handleItem(const QVariant& data); + +private: + + class Private; + Private* const d; +}; + +} // namespace Vkontakte + +#endif // DIGIKAM_VKONTAKTE_DISCUSSIONSLISTJOB_H diff --git a/core/dplugins/generic/webservices/vkontakte/backend/attic/vkontakte_friendlistjob.cpp b/core/dplugins/generic/webservices/vkontakte/backend/attic/vkontakte_friendlistjob.cpp new file mode 100644 index 0000000000..286c88c7d5 --- /dev/null +++ b/core/dplugins/generic/webservices/vkontakte/backend/attic/vkontakte_friendlistjob.cpp @@ -0,0 +1,73 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * https://www.digikam.org + * + * Date : 2011-02-19 + * Description : a tool to export images to VKontakte web service + * + * Copyright (C) 2011-2015 by Alexander Potashev + * Copyright (C) 2011-2020 by Gilles Caulier + * + * 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, 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. + * + * ============================================================ */ + +#include "vkontakte_friendlistjob.h" + +// Qt includes + +#include + +namespace Vkontakte +{ + +class Q_DECL_HIDDEN FriendListJob::Private +{ +public: + + QList list; +}; + +// http://vk.com/dev/friends.get +FriendListJob::FriendListJob(const QString& accessToken, int uid) + : VkontakteJob(accessToken, "friends.get"), + d(new Private) +{ + if (uid != -1) + { + addQueryItem("uid", QString::number(uid)); + } + + addQueryItem("fields", UserInfo::allQueryFields().join(",")); +} + +FriendListJob::~FriendListJob() +{ + delete d; +} + +QList FriendListJob::list() const +{ + return d->list; +} + +void FriendListJob::handleData(const QVariant& data) +{ + foreach (const QVariant& user, data.toList()) + { + UserInfoPtr userInfo(new UserInfo()); + QJson::QObjectHelper::qvariant2qobject(user.toMap(), userInfo.data()); + d->list.append(userInfo); + } +} + +} // namespace Vkontakte diff --git a/core/dplugins/generic/webservices/vkontakte/backend/attic/vkontakte_friendlistjob.h b/core/dplugins/generic/webservices/vkontakte/backend/attic/vkontakte_friendlistjob.h new file mode 100644 index 0000000000..048989e917 --- /dev/null +++ b/core/dplugins/generic/webservices/vkontakte/backend/attic/vkontakte_friendlistjob.h @@ -0,0 +1,59 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * https://www.digikam.org + * + * Date : 2011-02-19 + * Description : a tool to export images to VKontakte web service + * + * Copyright (C) 2011-2015 by Alexander Potashev + * Copyright (C) 2011-2020 by Gilles Caulier + * + * 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, 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. + * + * ============================================================ */ + +#ifndef DIGIKAM_VKONTAKTE_FRIENDLISTJOB_H +#define DIGIKAM_VKONTAKTE_FRIENDLISTJOB_H + +// Local includes + +#include "vkontakte_jobs.h" +#include "vkontakte_userinfo.h" + +namespace Vkontakte +{ + +/// Get a list of friends of the user +class FriendListJob : public VkontakteJob +{ + Q_OBJECT + +public: + + explicit FriendListJob(const QString& accessToken, int uid = -1); + ~FriendListJob(); + + QList list() const; + +protected: + + void handleData(const QVariant& data) override; + +private: + + class Private; + Private* const d; +}; + +} // namespace Vkontakte + +#endif // DIGIKAM_VKONTAKTE_FRIENDLISTJOB_H diff --git a/core/dplugins/generic/webservices/vkontakte/backend/attic/vkontakte_groupinfo.cpp b/core/dplugins/generic/webservices/vkontakte/backend/attic/vkontakte_groupinfo.cpp new file mode 100644 index 0000000000..08d900370f --- /dev/null +++ b/core/dplugins/generic/webservices/vkontakte/backend/attic/vkontakte_groupinfo.cpp @@ -0,0 +1,136 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * https://www.digikam.org + * + * Date : 2011-02-19 + * Description : a tool to export images to VKontakte web service + * + * Copyright (C) 2011-2015 by Alexander Potashev + * Copyright (C) 2011-2020 by Gilles Caulier + * + * 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, 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. + * + * ============================================================ */ + +#include "vkontakte_groupinfo.h" + +namespace Vkontakte +{ + +class Q_DECL_HIDDEN GroupInfo::Private +{ +public: + + int gid; + QString name; + QString screenName; + bool isClosed; + bool isAdmin; + QString photo; + QString photoMedium; + QString photoBig; +}; + +GroupInfo::GroupInfo() + : d(new Private) +{ + d->gid = -1; + d->isClosed = true; + d->isAdmin = false; +} + +GroupInfo::~GroupInfo() +{ + delete d; +} + +void GroupInfo::setGid(int gid) +{ + d->gid = gid; +} + +int GroupInfo::gid() const +{ + return d->gid; +} + +void GroupInfo::setName(const QString& name) +{ + d->name = name; +} + +QString GroupInfo::name() const +{ + return d->name; +} + +void GroupInfo::setScreenName(const QString& screenName) +{ + d->screenName = screenName; +} + +QString GroupInfo::screenName() const +{ + return d->screenName; +} + +void GroupInfo::setClosed(bool closed) +{ + d->isClosed = closed; +} + +bool GroupInfo::isClosed() const +{ + return d->isClosed; +} + +void GroupInfo::setAdmin(bool admin) +{ + d->isAdmin = admin; +} + +bool GroupInfo::isAdmin() const +{ + return d->isAdmin; +} + +void GroupInfo::setPhoto(const QString& photo) +{ + d->photo = photo; +} + +QString GroupInfo::photo() const +{ + return d->photo; +} + +void GroupInfo::setPhotoMedium(const QString& photoMedium) +{ + d->photoMedium = photoMedium; +} + +QString GroupInfo::photoMedium() const +{ + return d->photoMedium; +} + +void GroupInfo::setPhotoBig(const QString& photoBig) +{ + d->photoBig = photoBig; +} + +QString GroupInfo::photoBig() const +{ + return d->photoBig; +} + +} // namespace Vkontakte diff --git a/core/dplugins/generic/webservices/vkontakte/backend/attic/vkontakte_groupinfo.h b/core/dplugins/generic/webservices/vkontakte/backend/attic/vkontakte_groupinfo.h new file mode 100644 index 0000000000..2c9ff818f3 --- /dev/null +++ b/core/dplugins/generic/webservices/vkontakte/backend/attic/vkontakte_groupinfo.h @@ -0,0 +1,91 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * https://www.digikam.org + * + * Date : 2011-02-19 + * Description : a tool to export images to VKontakte web service + * + * Copyright (C) 2011-2015 by Alexander Potashev + * Copyright (C) 2011-2020 by Gilles Caulier + * + * 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, 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. + * + * ============================================================ */ + +#ifndef DIGIKAM_VKONTAKTE_GROUPINFO_H +#define DIGIKAM_VKONTAKTE_GROUPINFO_H + +// Qt includes + +#include + +namespace Vkontakte +{ + +// http://vk.com/dev/groups.get +class GroupInfo : public QObject +{ + Q_OBJECT + + // gid + Q_PROPERTY(int gid WRITE setGid READ gid) + + // If "extended" was requested, then these fields will be also available: + // name, screen_name, is_closed, is_admin, photo, photo_medium, photo_big + Q_PROPERTY(QString name WRITE setName READ name) + Q_PROPERTY(QString screen_name WRITE setScreenName READ screenName) + Q_PROPERTY(bool is_closed WRITE setClosed READ isClosed) + Q_PROPERTY(bool is_admin WRITE setAdmin READ isAdmin) + Q_PROPERTY(QString photo WRITE setPhoto READ photo) + Q_PROPERTY(QString photo_medium WRITE setPhotoMedium READ photoMedium) + Q_PROPERTY(QString photo_big WRITE setPhotoBig READ photoBig) + +public: + + GroupInfo(); + ~GroupInfo(); + + void setGid(int gid); + int gid() const; + + void setName(const QString& name); + QString name() const; + + void setScreenName(const QString& screenName); + QString screenName() const; + + void setClosed(bool closed); + bool isClosed() const; + + void setAdmin(bool admin); + bool isAdmin() const; + + void setPhoto(const QString& photo); + QString photo() const; + + void setPhotoMedium(const QString& photoMedium); + QString photoMedium() const; + + void setPhotoBig(const QString& photoBig); + QString photoBig() const; + +private: + + class Private; + Private* const d; +}; + +typedef QSharedPointer GroupInfoPtr; + +} // namespace Vkontakte + +#endif // DIGIKAM_VKONTAKTE_GROUPINFO_H diff --git a/core/dplugins/generic/webservices/vkontakte/backend/attic/vkontakte_messageinfo.cpp b/core/dplugins/generic/webservices/vkontakte/backend/attic/vkontakte_messageinfo.cpp new file mode 100644 index 0000000000..1a11db3caf --- /dev/null +++ b/core/dplugins/generic/webservices/vkontakte/backend/attic/vkontakte_messageinfo.cpp @@ -0,0 +1,193 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * https://www.digikam.org + * + * Date : 2011-02-19 + * Description : a tool to export images to VKontakte web service + * + * Copyright (C) 2011-2015 by Alexander Potashev + * Copyright (C) 2011-2020 by Gilles Caulier + * + * 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, 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. + * + * ============================================================ */ + +#include "vkontakte_messageinfo.h" +#include "vkontakte_util.h" + +// Qt includes + +#include + +namespace Vkontakte +{ + +class Q_DECL_HIDDEN MessageInfo::Private +{ +public: + + QString date; + int uid; + int mid; + QString title; + QString body; + int readState; + int out; + QString chatId; + QString chatActive; +}; + +MessageInfo::MessageInfo() + : d(new Private) +{ + d->uid = -1; + d->mid = -1; + d->readState = 0; + d->out = 0; +} + +MessageInfo::~MessageInfo() +{ + delete d; +} + +void MessageInfo::setDateString(const QString& createdDate) +{ + d->date = createdDate; +} + +QString MessageInfo::dateString() const +{ + return d->date; +} + +QDateTime MessageInfo::date() const +{ + return unixTimeToQDateTime(d->date); +} + +void MessageInfo::setUid(int uid) +{ + d->uid = uid; +} + +int MessageInfo::uid() const +{ + return d->uid; +} + +void MessageInfo::setMid(int mid) +{ + d->mid = mid; +} + +int MessageInfo::mid() const +{ + return d->mid; +} + +void MessageInfo::setTitle(const QString& title) +{ + // vk.com puts "..." into the title when the subject is not specified + QRegExp rx("(Re(\\(\\d+\\))?: )?( ?)\\.\\.\\.( ?)"); + + if (!rx.exactMatch(title)) + { + d->title = title; + } +} + +QString MessageInfo::title() const +{ + return d->title; +} + +QString MessageInfo::coreTitle() const +{ + QString res = d->title; + res.remove(QRegExp("^Re(\\(\\d+\\))?: ")); + + return res; +} + +void MessageInfo::setBody(const QString& body) +{ + d->body = body; + d->body.remove('\n'); + d->body.replace("
", "\n"); + d->body.replace("<", "<"); + d->body.replace(">", ">"); + d->body.replace(""", "\""); + d->body.replace("&", "&"); +} + +QString MessageInfo::body() const +{ + return d->body; +} + +void MessageInfo::setReadState(int readState) +{ + d->readState = readState; +} + +int MessageInfo::readState() const +{ + return d->readState; +} + +void MessageInfo::setOut(int out) +{ + d->out = out; +} + +int MessageInfo::out() const +{ + return d->out; +} + +void MessageInfo::setChatId(const QString& chatId) +{ + d->chatId = chatId; +} + +QString MessageInfo::chatId() const +{ + return d->chatId; +} + +void MessageInfo::setChatActive(const QString& chatActive) +{ + d->chatActive = chatActive; +} + +QString MessageInfo::chatActive() const +{ + return d->chatActive; +} + +QString MessageInfo::remoteId() const +{ + return QString::fromUtf8("priv_mid%1").arg(mid(), 6, 10, QLatin1Char('0')); +} + +MessageInfoPtr::MessageInfoPtr(MessageInfo* const ptr) + : QSharedPointer(ptr) +{ +} + +bool MessageInfoPtr::operator<(const MessageInfoPtr& o) const +{ + return ((*this)->mid() < o->mid()); +} + +} // namespace Vkontakte diff --git a/core/dplugins/generic/webservices/vkontakte/backend/attic/vkontakte_messageinfo.h b/core/dplugins/generic/webservices/vkontakte/backend/attic/vkontakte_messageinfo.h new file mode 100644 index 0000000000..f16db26c3c --- /dev/null +++ b/core/dplugins/generic/webservices/vkontakte/backend/attic/vkontakte_messageinfo.h @@ -0,0 +1,129 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * https://www.digikam.org + * + * Date : 2011-02-19 + * Description : a tool to export images to VKontakte web service + * + * Copyright (C) 2011-2015 by Alexander Potashev + * Copyright (C) 2011-2020 by Gilles Caulier + * + * 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, 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. + * + * ============================================================ */ + +#ifndef DIGIKAM_VKONTAKTE_MESSAGEINFO_H +#define DIGIKAM_VKONTAKTE_MESSAGEINFO_H + +// Qt includes + +#include + +namespace Vkontakte +{ + +/** + * Class that describes a private message on vk.com + */ +class MessageInfo : public QObject +{ + Q_OBJECT + + // date, uid, mid, title, body, read_state, out + Q_PROPERTY(QString date WRITE setDateString READ dateString) + Q_PROPERTY(int uid WRITE setUid READ uid) + Q_PROPERTY(int mid WRITE setMid READ mid) + Q_PROPERTY(QString title WRITE setTitle READ title) + Q_PROPERTY(QString body WRITE setBody READ body) + Q_PROPERTY(int read_state WRITE setReadState READ readState) + Q_PROPERTY(int out WRITE setOut READ out) // do non-discussion messages have this? + + // For chats: chat_id, chat_active + Q_PROPERTY(QString chat_id WRITE setChatId READ chatId) + Q_PROPERTY(QString chat_active WRITE setChatActive READ chatActive) + +public: + + MessageInfo(); + ~MessageInfo(); + + /** + * Set the creation time of the note + * @param createdTime Time as UNIX timestamp + */ + void setDateString(const QString& createdDate); + + /** + * Returns the creation date/time as a UNIX timestamp + */ + QString dateString() const; + + /** + * Returns the creation date/time in QDateTime + */ + QDateTime date() const; + + void setUid(int uid); + int uid() const; + + void setMid(int mid); + int mid() const; + + void setTitle(const QString& title); + QString title() const; + + /** + * @brief Returns the title without "Re(n): " prefix + **/ + QString coreTitle() const; + + void setBody(const QString& body); + QString body() const; + + void setReadState(int readState); + int readState() const; + + void setOut(int out); + int out() const; + + void setChatId(const QString& chatId); + QString chatId() const; + + void setChatActive(const QString& chatActive); + QString chatActive() const; + + QString remoteId() const; + +private: + + class Private; + Private* const d; +}; + +/** + * @brief This class is needed for using qSort on QLists of MessageInfoPtrs + **/ +class MessageInfoPtr : public QSharedPointer +{ +public: + + explicit MessageInfoPtr(MessageInfo* const ptr); + + /** + * @brief Compares by message ID (MessageInfo::m_mid) + **/ + bool operator<(const MessageInfoPtr& o) const; +}; + +} // namespace Vkontakte + +#endif // DIGIKAM_VKONTAKTE_MESSAGEINFO_H diff --git a/core/dplugins/generic/webservices/vkontakte/backend/attic/vkontakte_messageslistjob.cpp b/core/dplugins/generic/webservices/vkontakte/backend/attic/vkontakte_messageslistjob.cpp new file mode 100644 index 0000000000..1d364fd2ab --- /dev/null +++ b/core/dplugins/generic/webservices/vkontakte/backend/attic/vkontakte_messageslistjob.cpp @@ -0,0 +1,99 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * https://www.digikam.org + * + * Date : 2011-02-19 + * Description : a tool to export images to VKontakte web service + * + * Copyright (C) 2011-2015 by Alexander Potashev + * Copyright (C) 2011-2020 by Gilles Caulier + * + * 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, 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. + * + * ============================================================ */ + +#include "vkontakte_messageslistjob.h" + +// Qt includes + +#include + +namespace Vkontakte +{ + +class Q_DECL_HIDDEN MessagesListJob::Private +{ +public: + + int out; + int totalCount; ///< number of all messages, not only messages retrieved in this request + QList list; +}; + +MessagesListJob::MessagesListJob(const QString& accessToken, + int out, + int offset, int count, int previewLength, + int filters, int timeOffset) + : VkontakteJob(accessToken, "messages.get"), + d(new Private) +{ + d->out = out; + d->totalCount = 0; + + addQueryItem("out", QString::number(out)); + addQueryItem("offset", QString::number(offset)); + addQueryItem("count", QString::number(count)); + addQueryItem("filters", QString::number(filters)); + addQueryItem("preview_length", QString::number(previewLength)); + addQueryItem("time_offset", QString::number(timeOffset)); +} + +MessagesListJob::~MessagesListJob() +{ + delete d; +} + +void MessagesListJob::handleItem(const QVariant& data) +{ + MessageInfoPtr item(new MessageInfo()); + QJson::QObjectHelper::qvariant2qobject(data.toMap(), item.data()); + d->list.append(item); +} + +void MessagesListJob::handleData(const QVariant& data) +{ + QVariantList list = data.toList(); + d->totalCount = list[0].toInt(); + list.pop_front(); + + foreach (const QVariant& item, list) + { + handleItem(item); + } +} + +QList MessagesListJob::list() const +{ + return d->list; +} + +int MessagesListJob::totalCount() const +{ + return d->totalCount; +} + +int MessagesListJob::out() const +{ + return d->out; +} + +} // namespace Vkontakte diff --git a/core/dplugins/generic/webservices/vkontakte/backend/attic/vkontakte_messageslistjob.h b/core/dplugins/generic/webservices/vkontakte/backend/attic/vkontakte_messageslistjob.h new file mode 100644 index 0000000000..f347483058 --- /dev/null +++ b/core/dplugins/generic/webservices/vkontakte/backend/attic/vkontakte_messageslistjob.h @@ -0,0 +1,78 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * https://www.digikam.org + * + * Date : 2011-02-19 + * Description : a tool to export images to VKontakte web service + * + * Copyright (C) 2011-2015 by Alexander Potashev + * Copyright (C) 2011-2020 by Gilles Caulier + * + * 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, 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. + * + * ============================================================ */ + +#ifndef DIGIKAM_VKONTAKTE_MESSAGESLISTJOB_H +#define DIGIKAM_VKONTAKTE_MESSAGESLISTJOB_H + +// Local includes + +#include "vkontakte_jobs.h" +#include "vkontakte_messageinfo.h" + +namespace Vkontakte +{ + +// TODO: do not export in KF5 version +class MessagesListJob : public VkontakteJob +{ +public: + + /** + * @brief Constructs a job to retrieve an array of messages from vk.com. + * + * @param accessToken vk.com access token. + * @param out 1 if you want outgoing messages. Defaults to 0. + * @param offset Offset in the list of all discussions. Defaults to 0. + * @param count Number of discussions to retrieve. Defaults to 100. + * @param previewLength 0 means to not truncate messages. Defaults to 0. + * @param filters ... Defaults to 0. + * @param timeOffset ... Defaults to 0. + **/ + explicit MessagesListJob(const QString& accessToken, + int out = 0, + int offset = 0, int count = 100, int previewLength = 0, + int filters = 0, int timeOffset = 0); + ~MessagesListJob(); + + QList list() const; + int totalCount() const; + + /** + * @brief Used by AllMessagesListJob + **/ + int out() const; + +protected: + + void handleData(const QVariant& data) override; + void handleItem(const QVariant& data); + +private: + + class Private; + Private* const d; +}; + +} // namespace Vkontakte + +#endif // DIGIKAM_VKONTAKTE_MESSAGESLISTJOB_H diff --git a/core/dplugins/generic/webservices/vkontakte/backend/attic/vkontakte_noteaddjob.cpp b/core/dplugins/generic/webservices/vkontakte/backend/attic/vkontakte_noteaddjob.cpp new file mode 100644 index 0000000000..764d33c16a --- /dev/null +++ b/core/dplugins/generic/webservices/vkontakte/backend/attic/vkontakte_noteaddjob.cpp @@ -0,0 +1,66 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * https://www.digikam.org + * + * Date : 2011-02-19 + * Description : a tool to export images to VKontakte web service + * + * Copyright (C) 2011-2015 by Alexander Potashev + * Copyright (C) 2011-2020 by Gilles Caulier + * + * 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, 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. + * + * ============================================================ */ + +#include "vkontakte_noteaddjob.h" + +// Qt includes + +#include + +namespace Vkontakte +{ + +class Q_DECL_HIDDEN NoteAddJob::Private +{ +public: + + int nid; +}; + +NoteAddJob::NoteAddJob(const QString& accessToken, const QString& title, const QString& text) + : VkontakteJob(accessToken, "notes.add", true), + d(new Private) +{ + d->nid = -1; + + addQueryItem("title", title); + addQueryItem("text", text); + addQueryItem("privacy", "3"); +} + +NoteAddJob::~NoteAddJob() +{ + delete d; +} + +void NoteAddJob::handleData(const QVariant& data) +{ + d->nid = data.toMap()["nid"].toInt(); +} + +int NoteAddJob::nid() const +{ + return d->nid; +} + +} // namespace Vkontakte diff --git a/core/dplugins/generic/webservices/vkontakte/backend/attic/vkontakte_noteaddjob.h b/core/dplugins/generic/webservices/vkontakte/backend/attic/vkontakte_noteaddjob.h new file mode 100644 index 0000000000..026059c8e8 --- /dev/null +++ b/core/dplugins/generic/webservices/vkontakte/backend/attic/vkontakte_noteaddjob.h @@ -0,0 +1,61 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * https://www.digikam.org + * + * Date : 2011-02-19 + * Description : a tool to export images to VKontakte web service + * + * Copyright (C) 2011-2015 by Alexander Potashev + * Copyright (C) 2011-2020 by Gilles Caulier + * + * 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, 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. + * + * ============================================================ */ + +#ifndef DIGIKAM_VKONTAKTE_NOTEADDJOB_H +#define DIGIKAM_VKONTAKTE_NOTEADDJOB_H + +// Local includes + +#include "vkontakte_jobs.h" +#include "vkontakte_noteinfo.h" + +namespace Vkontakte +{ + +/** +* Add a note to vkontakte +*/ +class NoteAddJob : public VkontakteJob +{ + Q_OBJECT + +public: + + explicit NoteAddJob(const QString& accessToken, const QString& title, const QString& text); + ~NoteAddJob(); + + int nid() const; + +protected: + + void handleData(const QVariant& data) override; + +private: + + class Private; + Private* const d; +}; + +} // namespace Vkontakte + +#endif // DIGIKAM_VKONTAKTE_NOTEADDJOB_H diff --git a/core/dplugins/generic/webservices/vkontakte/backend/attic/vkontakte_noteinfo.cpp b/core/dplugins/generic/webservices/vkontakte/backend/attic/vkontakte_noteinfo.cpp new file mode 100644 index 0000000000..83a364b4d7 --- /dev/null +++ b/core/dplugins/generic/webservices/vkontakte/backend/attic/vkontakte_noteinfo.cpp @@ -0,0 +1,183 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * https://www.digikam.org + * + * Date : 2011-02-19 + * Description : a tool to export images to VKontakte web service + * + * Copyright (C) 2011-2015 by Alexander Potashev + * Copyright (C) 2011-2020 by Gilles Caulier + * + * 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, 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. + * + * ============================================================ */ + +#include "vkontakte_noteinfo.h" +#include "vkontakte_util.h" + +// KDE includes + +#include + +namespace Vkontakte +{ + +class Q_DECL_HIDDEN NoteInfo::Private +{ +public: + + int nid; ///< Vkontakte id of the note. + int uid; ///< Creator of the note. + QString title; ///< Subject of the note. + QString text; ///< Actual content of the note. + QString textWiki; + QString date; ///< Creation time of the note. + int ncom; ///< Number of comments. + int readNcom; ///< Number of read comments. + int privacy; + int commentPrivacy; + int canComment; +}; + +NoteInfo::NoteInfo() + : d(new Private) +{ + d->nid = -1; + d->uid = -1; + d->ncom = -1; + d->readNcom = -1; + d->privacy = -1; + d->commentPrivacy = -1; + d->canComment = false; +} + +NoteInfo::~NoteInfo() +{ + delete d; +} + +void NoteInfo::setNid(int nid) +{ + d->nid = nid; +} + +int NoteInfo::nid() const +{ + return d->nid; +} + +void NoteInfo::setUid(int uid) +{ + d->uid = uid; +} + +int NoteInfo::uid() const +{ + return d->uid; +} + +void NoteInfo::setTitle(const QString& title) +{ + d->title = title; +} + +QString NoteInfo::title() const +{ + return d->title; +} + +void NoteInfo::setText(const QString& text) +{ + d->text = text; +} + +QString NoteInfo::text() const +{ + return QString(d->text).remove(QChar::ByteOrderMark); +} + +void NoteInfo::setTextWiki(const QString& textWiki) +{ + d->textWiki = textWiki; +} + +QString NoteInfo::textWiki() const +{ + return d->textWiki; +} + +void NoteInfo::setDateString(const QString& createdDate) +{ + d->date = createdDate; +} + +QString NoteInfo::dateString() const +{ + return d->date; +} + +QDateTime NoteInfo::date() const +{ + return unixTimeToQDateTime(d->date); +} + +void NoteInfo::setNcom (int ncom) +{ + d->ncom = ncom; +} + +int NoteInfo::ncom() const +{ + return d->ncom; +} + +void NoteInfo::setReadNcom (int readNcom) +{ + d->readNcom = readNcom; +} + +int NoteInfo::readNcom() const +{ + return d->readNcom; +} + +void NoteInfo::setPrivacy(int privacy) +{ + d->privacy = privacy; +} + +int NoteInfo::privacy() const +{ + return d->privacy; +} + +void NoteInfo::setCommentPrivacy(int commentPrivacy) +{ + d->commentPrivacy = commentPrivacy; +} + +int NoteInfo::commentPrivacy() const +{ + return d->commentPrivacy; +} + +void NoteInfo::setCanComment(bool canComment) +{ + d->canComment = canComment; +} + +bool NoteInfo::canComment() const +{ + return d->canComment; +} + +} // namespace Vkontakte diff --git a/core/dplugins/generic/webservices/vkontakte/backend/attic/vkontakte_noteinfo.h b/core/dplugins/generic/webservices/vkontakte/backend/attic/vkontakte_noteinfo.h new file mode 100644 index 0000000000..e8b9568e18 --- /dev/null +++ b/core/dplugins/generic/webservices/vkontakte/backend/attic/vkontakte_noteinfo.h @@ -0,0 +1,136 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * https://www.digikam.org + * + * Date : 2011-02-19 + * Description : a tool to export images to VKontakte web service + * + * Copyright (C) 2011-2015 by Alexander Potashev + * Copyright (C) 2011-2020 by Gilles Caulier + * + * 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, 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. + * + * ============================================================ */ + +#ifndef DIGIKAM_VKONTAKTE_NOTEINFO_H +#define DIGIKAM_VKONTAKTE_NOTEINFO_H + +// Qt includes + +#include + +namespace Vkontakte +{ + +// http://vk.com/dev/notes.getById +class NoteInfo : public QObject +{ + Q_OBJECT + + // nid, uid, title, text, text_wiki, date, ncom, read_ncom, privacy, comment_privacy, can_comment + Q_PROPERTY(int nid WRITE setNid READ nid) + Q_PROPERTY(int uid WRITE setUid READ uid) + Q_PROPERTY(QString title WRITE setTitle READ title) + Q_PROPERTY(QString text WRITE setText READ text) + Q_PROPERTY(QString text_wiki WRITE setTextWiki READ textWiki) + Q_PROPERTY(QString date WRITE setDateString READ dateString) + Q_PROPERTY(int ncom WRITE setNcom READ ncom) + Q_PROPERTY(int read_ncom WRITE setReadNcom READ readNcom) + Q_PROPERTY(int privacy WRITE setPrivacy READ privacy) + Q_PROPERTY(int comment_privacy WRITE setCommentPrivacy READ commentPrivacy) + Q_PROPERTY(bool can_comment WRITE setCanComment READ canComment) + +public: + + NoteInfo(); + ~NoteInfo(); + + void setNid(int nid); + int nid() const; + + void setUid(int uid); + int uid() const; + + /** + * Set the subject of the note + * @param subject the subject + */ + void setTitle(const QString& title); + /** + * Returns the subject of the note. + */ + QString title() const; + + /** + * Set the actual content of the note + * @param text The actual content of the note + */ + void setText( const QString& text); + + /** + * Returns the content of the note. + */ + QString text() const; + + /** + * Set the actual content of the note + * @param textWiki The actual content of the note + */ + void setTextWiki( const QString& textWiki); + + /** + * Returns the content of the note. + */ + QString textWiki() const; + + /** + * Set the creation time of the note + * @param createdTime Time as UNIX timestamp + */ + void setDateString(const QString& createdDate); + + /** + * Returns the creation date/time as a UNIX timestamp + */ + QString dateString() const; + + /** + * Returns the creation date/time in QDateTime + */ + QDateTime date() const; + + void setNcom(int ncom); + int ncom() const; + + void setReadNcom(int readNcom); + int readNcom() const; + + void setPrivacy(int privacy); + int privacy() const; + + void setCommentPrivacy(int commentPrivacy); + int commentPrivacy() const; + + void setCanComment(bool canComment); + bool canComment() const; + +private: + + class Private; + Private* const d; +}; + +typedef QSharedPointer NoteInfoPtr; + +} // namespace Vkontakte + +#endif // DIGIKAM_VKONTAKTE_NOTEINFO_H diff --git a/core/dplugins/generic/webservices/vkontakte/backend/attic/vkontakte_notejob.cpp b/core/dplugins/generic/webservices/vkontakte/backend/attic/vkontakte_notejob.cpp new file mode 100644 index 0000000000..106f7a89f1 --- /dev/null +++ b/core/dplugins/generic/webservices/vkontakte/backend/attic/vkontakte_notejob.cpp @@ -0,0 +1,65 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * https://www.digikam.org + * + * Date : 2011-02-19 + * Description : a tool to export images to VKontakte web service + * + * Copyright (C) 2011-2015 by Alexander Potashev + * Copyright (C) 2011-2020 by Gilles Caulier + * + * 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, 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. + * + * ============================================================ */ + +#include "vkontakte_notejob.h" + +// Qt includes + +#include + +namespace Vkontakte +{ + +class Q_DECL_HIDDEN NoteJob::Private +{ +public: + + NoteInfoPtr noteInfo; +}; + +// http://vk.com/dev/notes.getById +NoteJob::NoteJob(const QString& accessToken, int nid) + : VkontakteJob(accessToken, "notes.getById"), + d(new Private) +{ + addQueryItem("nid", QString::number(nid)); + addQueryItem("need_wiki", "1"); // works only for current user's notes +} + +NoteJob::~NoteJob() +{ + delete d; +} + +void NoteJob::handleData(const QVariant& data) +{ + d->noteInfo = NoteInfoPtr(new NoteInfo()); + QJson::QObjectHelper::qvariant2qobject(data.toMap(), d->noteInfo.data()); +} + +NoteInfoPtr NoteJob::noteInfo() +{ + return d->noteInfo; +} + +} // namespace Vkontakte diff --git a/core/dplugins/generic/webservices/vkontakte/backend/attic/vkontakte_notejob.h b/core/dplugins/generic/webservices/vkontakte/backend/attic/vkontakte_notejob.h new file mode 100644 index 0000000000..0e2c2d3e90 --- /dev/null +++ b/core/dplugins/generic/webservices/vkontakte/backend/attic/vkontakte_notejob.h @@ -0,0 +1,58 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * https://www.digikam.org + * + * Date : 2011-02-19 + * Description : a tool to export images to VKontakte web service + * + * Copyright (C) 2011-2015 by Alexander Potashev + * Copyright (C) 2011-2020 by Gilles Caulier + * + * 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, 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. + * + * ============================================================ */ + +#ifndef DIGIKAM_VKONTAKTE_NOTEJOB_H +#define DIGIKAM_VKONTAKTE_NOTEJOB_H + +// Local includes + +#include "vkontakte_jobs.h" +#include "vkontakte_noteinfo.h" + +namespace Vkontakte +{ + +class NoteJob : public VkontakteJob +{ + Q_OBJECT + +public: + + explicit NoteJob(const QString& accessToken, int nid); + ~NoteJob(); + + NoteInfoPtr noteInfo(); + +protected: + + void handleData(const QVariant& data) override; + +private: + + class Private; + Private* const d; +}; + +} // namespace Vkontakte + +#endif // DIGIKAM_VKONTAKTE_NOTEJOB_H diff --git a/core/dplugins/generic/webservices/vkontakte/backend/attic/vkontakte_noteslistjob.cpp b/core/dplugins/generic/webservices/vkontakte/backend/attic/vkontakte_noteslistjob.cpp new file mode 100644 index 0000000000..2f22416e7b --- /dev/null +++ b/core/dplugins/generic/webservices/vkontakte/backend/attic/vkontakte_noteslistjob.cpp @@ -0,0 +1,93 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * https://www.digikam.org + * + * Date : 2011-02-19 + * Description : a tool to export images to VKontakte web service + * + * Copyright (C) 2011-2015 by Alexander Potashev + * Copyright (C) 2011-2020 by Gilles Caulier + * + * 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, 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. + * + * ============================================================ */ + +#include "vkontakte_noteslistjob.h" + +// Qt includes + +#include +#include + +namespace Vkontakte +{ + +class Q_DECL_HIDDEN NotesListJob::Private +{ +public: + + int totalCount; ///< number of all notes, not only notes retrieved in this request + QList list; +}; + +NotesListJob::NotesListJob(const QString& accessToken, + int uid, int offset, int count) + : VkontakteJob(accessToken, "notes.get"), + d(new Private) +{ + // Not passing "nids", because we want all notes. + + if (uid > 0) + { + addQueryItem("user_id", QString::number(uid)); + } + + addQueryItem("sort", "1"); // ascending by date + addQueryItem("count", QString::number(count)); + addQueryItem("offset", QString::number(offset)); +} + +NotesListJob::~NotesListJob() +{ + delete d; +} + +void NotesListJob::handleItem(const QVariant& data) +{ + NoteInfoPtr item(new NoteInfo()); + QJson::QObjectHelper::qvariant2qobject(data.toMap(), item.data()); + d->list.append(item); +} + +void NotesListJob::handleData(const QVariant& data) +{ + QVariantList list = data.toList(); + d->totalCount = list[0].toInt(); + list.pop_front(); + + foreach (const QVariant& item, list) + { + handleItem(item); + } +} + +QList NotesListJob::list() const +{ + return d->list; +} + +int NotesListJob::totalCount() const +{ + return d->totalCount; +} + +} // namespace Vkontakte diff --git a/core/dplugins/generic/webservices/vkontakte/backend/attic/vkontakte_noteslistjob.h b/core/dplugins/generic/webservices/vkontakte/backend/attic/vkontakte_noteslistjob.h new file mode 100644 index 0000000000..3f5d79b207 --- /dev/null +++ b/core/dplugins/generic/webservices/vkontakte/backend/attic/vkontakte_noteslistjob.h @@ -0,0 +1,71 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * https://www.digikam.org + * + * Date : 2011-02-19 + * Description : a tool to export images to VKontakte web service + * + * Copyright (C) 2011-2015 by Alexander Potashev + * Copyright (C) 2011-2020 by Gilles Caulier + * + * 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, 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. + * + * ============================================================ */ + +#ifndef DIGIKAM_VKONTAKTE_NOTESLISTJOB_H +#define DIGIKAM_VKONTAKTE_NOTESLISTJOB_H + +// Local includes + +#include "vkontakte_jobs.h" +#include "vkontakte_noteinfo.h" + +namespace Vkontakte +{ + +class NotesListJob : public VkontakteJob +{ + Q_OBJECT + +public: + + /** + * @brief Construct a job to retrieve the data of user with the given uid. + * @param accessToken The access token to access data on vkontakte. + * @param uid The user ID + * @param offset Offset in the list of all notes + * @param count Number of notes to retrieve (limited to 100) + */ + explicit NotesListJob(const QString& accessToken, int uid, int offset, int count); + ~NotesListJob(); + + QList list() const; + int totalCount() const; + +protected: + + /** + * @brief Handles the data returned by the VkontakteGetJob + * @param data A JSON string containing the data. + */ + void handleData(const QVariant& data) override; + void handleItem(const QVariant& data); + +private: + + class Private; + Private* const d; +}; + +} // namespace Vkontakte + +#endif // DIGIKAM_VKONTAKTE_NOTESLISTJOB_H diff --git a/core/dplugins/generic/webservices/vkontakte/backend/deprecated/vkontakte_getinfojob.cpp b/core/dplugins/generic/webservices/vkontakte/backend/deprecated/vkontakte_getinfojob.cpp new file mode 100644 index 0000000000..1a106f3edc --- /dev/null +++ b/core/dplugins/generic/webservices/vkontakte/backend/deprecated/vkontakte_getinfojob.cpp @@ -0,0 +1,58 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * https://www.digikam.org + * + * Date : 2011-02-19 + * Description : a tool to export images to VKontakte web service + * + * Copyright (C) 2011-2015 by Alexander Potashev + * Copyright (C) 2011-2020 by Gilles Caulier + * + * 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, 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. + * + * ============================================================ */ + +#include "vkontakte_getinfojob.h" + +namespace Vkontakte +{ + +class Q_DECL_HIDDEN GetInfoJob::Private +{ +public: + + QVariantMap info; +}; + +// example 'method': "messages.getLongPollServer" +GetInfoJob::GetInfoJob(const QString& accessToken, const QString& method) + : VkontakteJob(accessToken, method), + d(new Private) +{ +} + +GetInfoJob::~GetInfoJob() +{ + delete d; +} + +void GetInfoJob::handleData(const QVariant& data) +{ + d->info = data.toMap(); +} + +QVariantMap GetInfoJob::info() +{ + return d->info; +} + +} // namespace Vkontakte diff --git a/core/dplugins/generic/webservices/vkontakte/backend/deprecated/vkontakte_getinfojob.h b/core/dplugins/generic/webservices/vkontakte/backend/deprecated/vkontakte_getinfojob.h new file mode 100644 index 0000000000..ec1ec10f4f --- /dev/null +++ b/core/dplugins/generic/webservices/vkontakte/backend/deprecated/vkontakte_getinfojob.h @@ -0,0 +1,60 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * https://www.digikam.org + * + * Date : 2011-02-19 + * Description : a tool to export images to VKontakte web service + * + * Copyright (C) 2011-2015 by Alexander Potashev + * Copyright (C) 2011-2020 by Gilles Caulier + * + * 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, 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. + * + * ============================================================ */ + +#ifndef DIGIKAM_VKONTAKTE_GETINFOJOB_H +#define DIGIKAM_VKONTAKTE_GETINFOJOB_H + +// Qt includes + +#include + +// Local includes + +#include "vkontakte_jobs.h" + +namespace Vkontakte +{ + +class GetInfoJob : public VkontakteJob +{ +public: + + // This class was never used, will be removed later + Q_DECL_DEPRECATED GetInfoJob(const QString& accessToken, const QString& method); + ~GetInfoJob(); + + QVariantMap info(); + +protected: + + void handleData(const QVariant& data) override; + +private: + + class Private; + Private* const d; +}; + +} // namespace Vkontakte + +#endif // DIGIKAM_VKONTAKTE_GETINFOJOB_H diff --git a/core/dplugins/generic/webservices/vkontakte/backend/deprecated/vkontakte_getvariablejob.cpp b/core/dplugins/generic/webservices/vkontakte/backend/deprecated/vkontakte_getvariablejob.cpp new file mode 100644 index 0000000000..b969f755f9 --- /dev/null +++ b/core/dplugins/generic/webservices/vkontakte/backend/deprecated/vkontakte_getvariablejob.cpp @@ -0,0 +1,58 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * https://www.digikam.org + * + * Date : 2011-02-19 + * Description : a tool to export images to VKontakte web service + * + * Copyright (C) 2011-2015 by Alexander Potashev + * Copyright (C) 2011-2020 by Gilles Caulier + * + * 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, 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. + * + * ============================================================ */ + +#include "vkontakte_getvariablejob.h" + +namespace Vkontakte +{ + +class Q_DECL_HIDDEN GetVariableJob::Private +{ +public: + + QVariant variable; +}; + +GetVariableJob::GetVariableJob(const QString& accessToken, int index) + : VkontakteJob(accessToken, "getVariable"), + d(new Private) +{ + addQueryItem("key", QString::number(index)); +} + +GetVariableJob::~GetVariableJob() +{ + delete d; +} + +void GetVariableJob::handleData(const QVariant& data) +{ + d->variable = data; +} + +QVariant GetVariableJob::variable() const +{ + return d->variable; +} + +} // namespace Vkontakte diff --git a/core/dplugins/generic/webservices/vkontakte/backend/deprecated/vkontakte_getvariablejob.h b/core/dplugins/generic/webservices/vkontakte/backend/deprecated/vkontakte_getvariablejob.h new file mode 100644 index 0000000000..8c78dbd829 --- /dev/null +++ b/core/dplugins/generic/webservices/vkontakte/backend/deprecated/vkontakte_getvariablejob.h @@ -0,0 +1,63 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * https://www.digikam.org + * + * Date : 2011-02-19 + * Description : a tool to export images to VKontakte web service + * + * Copyright (C) 2011-2015 by Alexander Potashev + * Copyright (C) 2011-2020 by Gilles Caulier + * + * 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, 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. + * + * ============================================================ */ + +#ifndef DIGIKAM_VKONTAKTE_GETVARIABLEJOB_H +#define DIGIKAM_VKONTAKTE_GETVARIABLEJOB_H + +// Qt includes + +#include + +// Local includes + +#include "vkontakte_jobs.h" + +namespace Vkontakte +{ + +// For the info about specific variables see: +// http://vkontakte.ru/developers.php?o=-1&p=%D5%F0%E0%ED%E5%ED%E8%E5%20%E4%E0%ED%ED%FB%F5%20%ED%E0%20%F1%E5%F0%E2%E5%F0%E0%F5%20%C2%CA%EE%ED%F2%E0%EA%F2%E5 +class GetVariableJob : public VkontakteJob +{ + +public: + + // "getVariable" is deprecated, please use "users.get" with empty user_ids + Q_DECL_DEPRECATED GetVariableJob(const QString& accessToken, int index); + ~GetVariableJob(); + + QVariant variable() const; + +protected: + + void handleData(const QVariant& data) override; + +private: + + class Private; + Private* const d; +}; + +} // namespace Vkontakte + +#endif // DIGIKAM_VKONTAKTE_GETVARIABLEJOB_H diff --git a/core/dplugins/generic/webservices/vkontakte/backend/deprecated/vkontakte_grouplistjob.cpp b/core/dplugins/generic/webservices/vkontakte/backend/deprecated/vkontakte_grouplistjob.cpp new file mode 100644 index 0000000000..15d4c2eeb4 --- /dev/null +++ b/core/dplugins/generic/webservices/vkontakte/backend/deprecated/vkontakte_grouplistjob.cpp @@ -0,0 +1,101 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * https://www.digikam.org + * + * Date : 2011-02-19 + * Description : a tool to export images to VKontakte web service + * + * Copyright (C) 2011-2015 by Alexander Potashev + * Copyright (C) 2011-2020 by Gilles Caulier + * + * 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, 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. + * + * ============================================================ */ + +#include "vkontakte_grouplistjob.h" + +// Qt includes + +#include + +namespace Vkontakte +{ + +class Q_DECL_HIDDEN GroupListJob::Private +{ +public: + + QList list; + bool extended; +}; + +GroupListJob::GroupListJob(const QString& accessToken, int uid, bool extended) + : VkontakteJob(accessToken, "groups.get"), + d(new Private) +{ + d->extended = extended; + + if (uid != -1) + { + addQueryItem("uid", QString::number(uid)); + } + + if (extended) + { + addQueryItem("extended", "1"); + } +} + +GroupListJob::~GroupListJob() +{ + delete d; +} + +QList GroupListJob::list() const +{ + return d->list; +} + +GroupInfoPtr GroupListJob::handleSingleData(const QVariant& data) +{ + GroupInfoPtr info = GroupInfoPtr(new GroupInfo()); + QJson::QObjectHelper::qvariant2qobject(data.toMap(), info.data()); + + return info; +} + +void GroupListJob::handleData(const QVariant& data) +{ + if (d->extended) + { + QVariantList dataList = data.toList(); + dataList.pop_front(); // total count (unused) + + foreach (const QVariant& item, dataList) + { + d->list.append(handleSingleData(item)); + } + } + else + { + // TODO: test with both extended={true, false} + + foreach (const QVariant& item, data.toList()) + { + GroupInfoPtr group = GroupInfoPtr(new GroupInfo()); + group->setGid(item.toInt()); + d->list.append(group); + } + } +} + +} // namespace Vkontakte diff --git a/core/dplugins/generic/webservices/vkontakte/backend/deprecated/vkontakte_grouplistjob.h b/core/dplugins/generic/webservices/vkontakte/backend/deprecated/vkontakte_grouplistjob.h new file mode 100644 index 0000000000..79a809a08d --- /dev/null +++ b/core/dplugins/generic/webservices/vkontakte/backend/deprecated/vkontakte_grouplistjob.h @@ -0,0 +1,62 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * https://www.digikam.org + * + * Date : 2011-02-19 + * Description : a tool to export images to VKontakte web service + * + * Copyright (C) 2011-2015 by Alexander Potashev + * Copyright (C) 2011-2020 by Gilles Caulier + * + * 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, 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. + * + * ============================================================ */ + +#ifndef DIGIKAM_VKONTAKTE_GROUPLISTJOB_H +#define DIGIKAM_VKONTAKTE_GROUPLISTJOB_H + +// Local includes + +#include "vkontakte_jobs.h" +#include "vkontakte_groupinfo.h" + +namespace Vkontakte +{ + +// http://vk.com/dev/groups.get +// TODO: for how many groups does this method work? +class GroupListJob : public VkontakteJob +{ + Q_OBJECT + +public: + + // This class was never used, will be removed later + Q_DECL_DEPRECATED explicit GroupListJob(const QString& accessToken, int uid = -1, bool extended = true); + ~GroupListJob(); + + QList list() const; + +protected: + + GroupInfoPtr handleSingleData(const QVariant& data); + void handleData(const QVariant& data) override; + +private: + + class Private; + Private* const d; +}; + +} // namespace Vkontakte + +#endif // DIGIKAM_VKONTAKTE_GROUPLISTJOB_H diff --git a/core/dplugins/generic/webservices/vkontakte/backend/vkontakte_albuminfo.cpp b/core/dplugins/generic/webservices/vkontakte/backend/vkontakte_albuminfo.cpp new file mode 100644 index 0000000000..627f2eb798 --- /dev/null +++ b/core/dplugins/generic/webservices/vkontakte/backend/vkontakte_albuminfo.cpp @@ -0,0 +1,107 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * https://www.digikam.org + * + * Date : 2011-02-19 + * Description : a tool to export images to VKontakte web service + * + * Copyright (C) 2011-2015 by Alexander Potashev + * Copyright (C) 2011-2020 by Gilles Caulier + * + * 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, 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. + * + * ============================================================ */ + +#include "vkontakte_albuminfo.h" + +// Local includes + +#include "vkontakte_util.h" + +namespace Vkontakte +{ + +class Q_DECL_HIDDEN AlbumInfo::Private : public QSharedData +{ +public: + + QJsonObject jsonData; +}; + +// -------------------------------------------------------------------- + +AlbumInfo::AlbumInfo() + : d() +{ +} + +AlbumInfo::AlbumInfo(const QJsonObject& jsonData) + : d(new Private) +{ + d->jsonData = jsonData; +} + +AlbumInfo::AlbumInfo(const AlbumInfo &other) + : d(other.d) +{ +} + +AlbumInfo::~AlbumInfo() +{ +} + +AlbumInfo& AlbumInfo::operator=(const AlbumInfo& other) +{ + if (this != &other) + { + d = other.d; + } + + return *this; +} + +bool AlbumInfo::isEmpty() const +{ + return !d; +} + +int AlbumInfo::aid() const +{ + return d->jsonData.value(QLatin1String("aid")).toInt(-1); +} + +QString AlbumInfo::title() const +{ + return d->jsonData.value(QLatin1String("title")).toString(); +} + +QString AlbumInfo::description() const +{ + return d->jsonData.value(QLatin1String("description")).toString(); +} + +int AlbumInfo::size() const +{ + return d->jsonData.value(QLatin1String("size")).toInt(-1); +} + +int AlbumInfo::privacy() const +{ + return d->jsonData.value(QLatin1String("privacy")).toInt(PRIVACY_UNKNOWN); +} + +int AlbumInfo::commentPrivacy() const +{ + return d->jsonData.value(QLatin1String("comment_privacy")).toInt(PRIVACY_UNKNOWN); +} + +} // namespace Vkontakte diff --git a/core/dplugins/generic/webservices/vkontakte/backend/vkontakte_albuminfo.h b/core/dplugins/generic/webservices/vkontakte/backend/vkontakte_albuminfo.h new file mode 100644 index 0000000000..767edb50ac --- /dev/null +++ b/core/dplugins/generic/webservices/vkontakte/backend/vkontakte_albuminfo.h @@ -0,0 +1,80 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * https://www.digikam.org + * + * Date : 2011-02-19 + * Description : a tool to export images to VKontakte web service + * + * Copyright (C) 2011-2015 by Alexander Potashev + * Copyright (C) 2011-2020 by Gilles Caulier + * + * 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, 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. + * + * ============================================================ */ + +#ifndef DIGIKAM_VKONTAKTE_ALBUMINFO_H +#define DIGIKAM_VKONTAKTE_ALBUMINFO_H + +// Qt includes + +#include +#include + +namespace Vkontakte +{ + +// http://vk.com/dev/photos.getAlbums +class AlbumInfo +{ +public: + + enum + { + PRIVACY_UNKNOWN = -1, + PRIVACY_PUBLIC = 0, + PRIVACY_FRIENDS = 1, + PRIVACY_FRIENDS_OF_FRIENDS = 2, + PRIVACY_PRIVATE = 3 + }; + +public: + + AlbumInfo(); + AlbumInfo(const AlbumInfo& other); + explicit AlbumInfo(const QJsonObject& jsonData); + ~AlbumInfo(); + + AlbumInfo& operator=(const AlbumInfo& other); + + bool isEmpty() const; + + int aid() const; + QString title() const; + QString description() const; + + /** + * @brief Returns number of photos in the album + */ + int size() const; + + int privacy() const; + int commentPrivacy() const; + +private: + + class Private; + QSharedDataPointer d; +}; + +} // namespace Vkontakte + +#endif // DIGIKAM_VKONTAKTE_ALBUMINFO_H diff --git a/core/dplugins/generic/webservices/vkontakte/backend/vkontakte_albumlistjob.cpp b/core/dplugins/generic/webservices/vkontakte/backend/vkontakte_albumlistjob.cpp new file mode 100644 index 0000000000..0150625b9f --- /dev/null +++ b/core/dplugins/generic/webservices/vkontakte/backend/vkontakte_albumlistjob.cpp @@ -0,0 +1,91 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * https://www.digikam.org + * + * Date : 2011-02-19 + * Description : a tool to export images to VKontakte web service + * + * Copyright (C) 2011-2015 by Alexander Potashev + * Copyright (C) 2011-2020 by Gilles Caulier + * + * 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, 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. + * + * ============================================================ */ + +#include "vkontakte_albumlistjob.h" + +// Qt includes + +#include +#include + +// Local includes + +#include "vkontakte_util.h" + +namespace Vkontakte +{ + +class Q_DECL_HIDDEN AlbumListJob::Private +{ +public: + + QList list; +}; + +AlbumListJob::AlbumListJob(const QString& accessToken, int uid, const QList& aids) + : VkontakteJob(accessToken, QLatin1String("photos.getAlbums")), + d(new Private) +{ + if (uid != -1) + { + addQueryItem(QLatin1String("uid"), QString::number(uid)); + } + + if (!aids.empty()) + { + addQueryItem(QLatin1String("aids"), joinIntegers(aids)); + } +} + +AlbumListJob::~AlbumListJob() +{ + delete d; +} + +void AlbumListJob::handleData(const QJsonValue& data) +{ + if (!data.isArray()) + { + // TODO: report error!!! + return; + } + + foreach (const QJsonValue& item, data.toArray()) + { + if (!item.isObject()) + { + // TODO: report error!!! + d->list.clear(); + return; + } + + d->list.append(AlbumInfo(item.toObject())); + } +} + +QList AlbumListJob::list() const +{ + return d->list; +} + +} // namespace Vkontakte diff --git a/core/dplugins/generic/webservices/vkontakte/backend/vkontakte_albumlistjob.h b/core/dplugins/generic/webservices/vkontakte/backend/vkontakte_albumlistjob.h new file mode 100644 index 0000000000..6f561c85d2 --- /dev/null +++ b/core/dplugins/generic/webservices/vkontakte/backend/vkontakte_albumlistjob.h @@ -0,0 +1,60 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * https://www.digikam.org + * + * Date : 2011-02-19 + * Description : a tool to export images to VKontakte web service + * + * Copyright (C) 2011-2015 by Alexander Potashev + * Copyright (C) 2011-2020 by Gilles Caulier + * + * 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, 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. + * + * ============================================================ */ + +#ifndef DIGIKAM_VKONTAKTE_ALBUMLISTJOB_H +#define DIGIKAM_VKONTAKTE_ALBUMLISTJOB_H + +// Local includes + +#include "vkontakte_jobs.h" +#include "vkontakte_albuminfo.h" + +namespace Vkontakte +{ + +// http://vk.com/dev/photos.getAlbums +class AlbumListJob : public VkontakteJob +{ + Q_OBJECT + +public: + + explicit AlbumListJob(const QString& accessToken, int uid = -1, const QList& aids = QList()); + ~AlbumListJob(); + + QList list() const; + +protected: + + void handleData(const QJsonValue& data) override; + void handleItem(const QVariant& data); + +private: + + class Private; + Private* const d; +}; + +} // namespace Vkontakte + +#endif // DIGIKAM_VKONTAKTE_ALBUMLISTJOB_H diff --git a/core/dplugins/generic/webservices/vkontakte/backend/vkontakte_apppermissions.h b/core/dplugins/generic/webservices/vkontakte/backend/vkontakte_apppermissions.h new file mode 100644 index 0000000000..92dfaa848a --- /dev/null +++ b/core/dplugins/generic/webservices/vkontakte/backend/vkontakte_apppermissions.h @@ -0,0 +1,93 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * https://www.digikam.org + * + * Date : 2011-02-19 + * Description : a tool to export images to VKontakte web service + * + * Copyright (C) 2011-2015 by Alexander Potashev + * Copyright (C) 2011-2020 by Gilles Caulier + * + * 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, 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. + * + * ============================================================ */ + +#ifndef DIGIKAM_VKONTAKTE_APPPERMISSIONS_H +#define DIGIKAM_VKONTAKTE_APPPERMISSIONS_H + +namespace Vkontakte +{ + +namespace AppPermissions +{ + +enum Value +{ + NoPermissions = 0, + + // User allowed to send notifications to him/her + Notify = 1, + // Access to friends + Friends = 2, + // Access to photos + Photos = 4, + // Access to audios + Audio = 8, + // Access to videos + Video = 16, + // Access to offers (obsolete methods) + Offers = 32, + // Access to questions (obsolete methods). + Questions = 64, + // Access to wiki pages + Pages = 128, + // Addition of link to the application in the left menu + AppLink = 256, + // Access to user status + Status = 1024, + // Access to user notes + Notes = 2048, + // Access to advanced methods for messaging + Messages = 4096, + // Access to standard and advanced methods for the wall + Wall = 8192, + // Access to advanced methods for Ads API + Ads = 32768, + // Access to API at any time from a third party server + Offline = 65536, + // Access to documents + Docs = 131072, + // Access to user groups + Groups = 262144, + // Access to notifications about answers to the user + Notifications = 524288, + // Access to statistics of user's own groups and applications + Stats = 1048576, + // User e-mail access. Available only for sites. + Email = 4194304 +}; // enum Value + +inline AppPermissions::Value operator|(AppPermissions::Value a, AppPermissions::Value b) +{ + return static_cast(static_cast(a) | static_cast(b)); +} + +inline AppPermissions::Value& operator|=(AppPermissions::Value& a, AppPermissions::Value b) +{ + return (a = a | b); +} + +} // namespace AppPermissions + +} // namespace Vkontakte + +#endif // DIGIKAM_VKONTAKTE_APPPERMISSIONS_H diff --git a/core/dplugins/generic/webservices/vkontakte/backend/vkontakte_authenticationdialog.cpp b/core/dplugins/generic/webservices/vkontakte/backend/vkontakte_authenticationdialog.cpp new file mode 100644 index 0000000000..2ce707863f --- /dev/null +++ b/core/dplugins/generic/webservices/vkontakte/backend/vkontakte_authenticationdialog.cpp @@ -0,0 +1,247 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * https://www.digikam.org + * + * Date : 2011-02-19 + * Description : a tool to export images to VKontakte web service + * + * Copyright (C) 2010 by Thomas McGuire + * Copyright (C) 2011-2015 by Alexander Potashev + * Copyright (C) 2011-2020 by Gilles Caulier + * + * 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, 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. + * + * ============================================================ */ + +#include "vkontakte_authenticationdialog.h" +#include "digikam_config.h" + +// Qt includes + +#ifdef HAVE_QWEBENGINE +# include +# include +# include +# include +#else +# include +#endif + +#include +#include +#include +#include +#include +#include +#include + +// KDE incudes + +#include + +// Local includes + +#include "digikam_debug.h" +#include "vkontakte_util.h" + +namespace Vkontakte +{ + +class Q_DECL_HIDDEN AuthenticationDialog::Private +{ +public: + + QString appId; + Vkontakte::AppPermissions::Value permissions; + QString displayMode; + +#ifdef HAVE_QWEBENGINE + + QWebEngineView* webView; + +#else + + QWebView* webView; + +#endif + + QProgressBar* progressBar; + + QString error; + QString errorDescription; +}; + +AuthenticationDialog::AuthenticationDialog(QWidget* const parent) + : QDialog(parent), + d(new Private) +{ + d->displayMode = QLatin1String("page"); + + setWindowTitle(i18nc("@title:window", "Authenticate with VKontakte")); + setAttribute(Qt::WA_DeleteOnClose, true); + + QWidget* const progressWidget = new QWidget(this); + QHBoxLayout* const progressLayout = new QHBoxLayout(progressWidget); + progressLayout->setContentsMargins(QMargins()); + +#ifdef HAVE_QWEBENGINE + + d->webView = new QWebEngineView(this); + d->webView->page()->profile()->cookieStore()->deleteAllCookies(); + +#else + + d->webView = new QWebView(this); + d->webView->settings()->setAttribute(QWebSettings::LocalStorageEnabled, true); + d->webView->page()->networkAccessManager()->setCookieJar(new QNetworkCookieJar()); + +#endif + + d->progressBar = new QProgressBar(this); + d->progressBar->setRange(0, 100); + QLabel* const progressLabel = new QLabel(i18n("Loading Page:"), this); + progressLayout->addWidget(progressLabel); + progressLayout->addWidget(d->progressBar); + + // Buttons + QDialogButtonBox* const buttonBox = new QDialogButtonBox(QDialogButtonBox::Cancel, this); + + connect(buttonBox, &QDialogButtonBox::rejected, + this, &QDialog::reject); + + // Layout + QVBoxLayout* const layout = new QVBoxLayout(this); + layout->addWidget(progressWidget); + layout->addWidget(d->webView); + layout->addWidget(buttonBox); + setLayout(layout); + + connect(buttonBox, &QDialogButtonBox::rejected, + this, &AuthenticationDialog::canceled); + + connect(d->webView, SIGNAL(urlChanged(QUrl)), + this, SLOT(urlChanged(QUrl))); + + connect(d->webView, SIGNAL(loadStarted()), + progressWidget, SLOT(show())); + + connect(d->webView, SIGNAL(loadFinished(bool)), + progressWidget, SLOT(hide())); + + connect(d->webView, SIGNAL(loadProgress(int)), + d->progressBar, SLOT(setValue(int))); + + connect(d->webView, SIGNAL(loadFinished(bool)), + this, SLOT(loadFinished(bool))); +} + +AuthenticationDialog::~AuthenticationDialog() +{ + delete d; +} + +void AuthenticationDialog::setAppId(const QString& appId) +{ + d->appId = appId; +} + +void Vkontakte::AuthenticationDialog::setPermissions(Vkontakte::AppPermissions::Value permissions) +{ + d->permissions = permissions; +} + +// display= {page, popup, touch, wap} +void AuthenticationDialog::setDisplayMode(const QString& displayMode) +{ + d->displayMode = displayMode; +} + +void AuthenticationDialog::start() +{ + Q_ASSERT(!d->appId.isEmpty()); + + const QString url = QString::fromUtf8( + "http://oauth.vk.com/authorize?" + "client_id=%1&" + "scope=%2&" + "redirect_uri=http://oauth.vk.com/blank.html&" + "display=%3&" + "response_type=token") + .arg(d->appId) + .arg(appPermissionsToStringList(d->permissions).join(QLatin1String(","))) + .arg(d->displayMode); + + qCDebug(DIGIKAM_WEBSERVICES_LOG) << "Showing" << url; + d->webView->setUrl(QUrl::fromUserInput(url)); + show(); +} + +void AuthenticationDialog::showErrorDialog() +{ + hide(); + const QString details = i18n("VKontakte Error Description: %1
" + "VKontakte Error: %2
", d->errorDescription, d->error); + QMessageBox::warning(this, i18n("Authentication with VKontakte was not successful."), + details, i18nc("@title:window", "Authentication Problem")); + emit canceled(); + close(); +} + +void AuthenticationDialog::urlChanged(const QUrl& url) +{ + qCDebug(DIGIKAM_WEBSERVICES_LOG) << "Navigating to" << url; + + if ((url.host() == QLatin1String("oauth.vk.com")) && + (url.path() == QLatin1String("/blank.html"))) + { + const QUrlQuery query(url); + + d->error = query.queryItemValue(QLatin1String("error")); + d->errorDescription = query.queryItemValue(QLatin1String("error_description")).replace(QLatin1Char('+'), QLatin1Char(' ')); + + if (!d->error.isEmpty() || !d->errorDescription.isEmpty()) + { + QTimer::singleShot(0, this, SLOT(showErrorDialog())); + return; + } + + // The URL comes in the form "bla#access_token=bla&expires_in=foo", we need to convert from + // # to ? + const QUrl fixedURL = QUrl::fromUserInput(url.toString().replace(QLatin1Char('#'), QLatin1Char('?'))); + const QUrlQuery fixedQuery(fixedURL); + const QString accessToken = fixedQuery.queryItemValue(QLatin1String("access_token")); + const QString tokenExpiresIn = fixedQuery.queryItemValue(QLatin1String("expires_in")); // TODO: use this for something? + + if (!accessToken.isEmpty()) + { + emit authenticated(accessToken); + QTimer::singleShot(0, this, SLOT(close())); + } + } +} + +void AuthenticationDialog::loadFinished(bool ok) +{ + if (!ok) + { + hide(); + + QMessageBox::critical(parentWidget(), + i18n("There was a network error when trying to authenticate with VKontakte web service."), + i18nc("@title:window", "Network Error")); + + emit canceled(); + close(); + } +} + +} // namespace Vkontakte diff --git a/core/dplugins/generic/webservices/vkontakte/backend/vkontakte_authenticationdialog.h b/core/dplugins/generic/webservices/vkontakte/backend/vkontakte_authenticationdialog.h new file mode 100644 index 0000000000..0a246e6539 --- /dev/null +++ b/core/dplugins/generic/webservices/vkontakte/backend/vkontakte_authenticationdialog.h @@ -0,0 +1,73 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * https://www.digikam.org + * + * Date : 2011-02-19 + * Description : a tool to export images to VKontakte web service + * + * Copyright (C) 2010 by Thomas McGuire + * Copyright (C) 2011-2015 by Alexander Potashev + * Copyright (C) 2011-2020 by Gilles Caulier + * + * 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, 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. + * + * ============================================================ */ + +#ifndef DIGIKAM_VKONTAKTE_AUTHENTICATIONDIALOG_H +#define DIGIKAM_VKONTAKTE_AUTHENTICATIONDIALOG_H + +// Qt includes + +#include + +// Local includes + +#include "vkontakte_apppermissions.h" + +namespace Vkontakte +{ + +class AuthenticationDialog : public QDialog +{ + Q_OBJECT + +public: + + explicit AuthenticationDialog(QWidget* const parent); + ~AuthenticationDialog(); + + void setAppId(const QString& appId); + void Q_DECL_DEPRECATED setPermissions(const QStringList& permissions); + void setPermissions(Vkontakte::AppPermissions::Value permissions); + void setDisplayMode(const QString& displayMode); + void start(); + +Q_SIGNALS: + + void authenticated(const QString& accessToken); + void canceled(); + +private Q_SLOTS: + + void urlChanged(const QUrl& url); + void loadFinished(bool ok); + void showErrorDialog(); + +private: + + class Private; + Private* const d; +}; + +} // namespace Vkontakte + +#endif // DIGIKAM_VKONTAKTE_AUTHENTICATIONDIALOG_H diff --git a/core/dplugins/generic/webservices/vkontakte/backend/vkontakte_createalbumjob.cpp b/core/dplugins/generic/webservices/vkontakte/backend/vkontakte_createalbumjob.cpp new file mode 100644 index 0000000000..7d420ec5b9 --- /dev/null +++ b/core/dplugins/generic/webservices/vkontakte/backend/vkontakte_createalbumjob.cpp @@ -0,0 +1,83 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * https://www.digikam.org + * + * Date : 2011-02-19 + * Description : a tool to export images to VKontakte web service + * + * Copyright (C) 2011-2015 by Alexander Potashev + * Copyright (C) 2011-2020 by Gilles Caulier + * + * 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, 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. + * + * ============================================================ */ + +#include "vkontakte_createalbumjob.h" + +namespace Vkontakte +{ + +class Q_DECL_HIDDEN CreateAlbumJob::Private +{ +public: + + AlbumInfo album; +}; + +CreateAlbumJob::CreateAlbumJob(const QString& accessToken, + const QString& title, + const QString& description, + int privacy, + int comment_privacy) + : VkontakteJob(accessToken, QLatin1String("photos.createAlbum"), true), + d(new Private) +{ + addQueryItem(QLatin1String("title"), title); + + if (!description.isEmpty()) + { + addQueryItem(QLatin1String("description"), description); + } + + if (privacy != AlbumInfo::PRIVACY_UNKNOWN) + { + addQueryItem(QLatin1String("privacy"), QString::number(privacy)); + } + + if (comment_privacy != AlbumInfo::PRIVACY_UNKNOWN) + { + addQueryItem(QLatin1String("comment_privacy"), QString::number(comment_privacy)); + } +} + +CreateAlbumJob::~CreateAlbumJob() +{ + delete d; +} + +void CreateAlbumJob::handleData(const QJsonValue& data) +{ + if (!data.isObject()) + { + // TODO: report error!!! + return; + } + + d->album = AlbumInfo(data.toObject()); +} + +AlbumInfo CreateAlbumJob::album() const +{ + return d->album; +} + +} // namespace Vkontakte diff --git a/core/dplugins/generic/webservices/vkontakte/backend/vkontakte_createalbumjob.h b/core/dplugins/generic/webservices/vkontakte/backend/vkontakte_createalbumjob.h new file mode 100644 index 0000000000..f764e8320f --- /dev/null +++ b/core/dplugins/generic/webservices/vkontakte/backend/vkontakte_createalbumjob.h @@ -0,0 +1,60 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * https://www.digikam.org + * + * Date : 2011-02-19 + * Description : a tool to export images to VKontakte web service + * + * Copyright (C) 2011-2015 by Alexander Potashev + * Copyright (C) 2011-2020 by Gilles Caulier + * + * 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, 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. + * + * ============================================================ */ + +#ifndef DIGIKAM_VKONTAKTE_CREATEALBUMJOB_H +#define DIGIKAM_VKONTAKTE_CREATEALBUMJOB_H + +// Local includes + +#include "vkontakte_jobs.h" +#include "vkontakte_albuminfo.h" + +namespace Vkontakte +{ + +class CreateAlbumJob : public VkontakteJob +{ +public: + + explicit CreateAlbumJob(const QString& accessToken, + const QString& title, + const QString& description = QString(), + int privacy = AlbumInfo::PRIVACY_UNKNOWN, + int comment_privacy = AlbumInfo::PRIVACY_UNKNOWN); + ~CreateAlbumJob(); + + AlbumInfo album() const; + +protected: + + void handleData(const QJsonValue& data) override; + +private: + + class Private; + Private* const d; +}; + +} // namespace Vkontakte + +#endif // DIGIKAM_VKONTAKTE_CREATEALBUMJOB_H diff --git a/core/dplugins/generic/webservices/vkontakte/backend/vkontakte_deletealbumjob.cpp b/core/dplugins/generic/webservices/vkontakte/backend/vkontakte_deletealbumjob.cpp new file mode 100644 index 0000000000..6d2bfd114f --- /dev/null +++ b/core/dplugins/generic/webservices/vkontakte/backend/vkontakte_deletealbumjob.cpp @@ -0,0 +1,57 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * https://www.digikam.org + * + * Date : 2011-02-19 + * Description : a tool to export images to VKontakte web service + * + * Copyright (C) 2011-2015 by Alexander Potashev + * Copyright (C) 2011-2020 by Gilles Caulier + * + * 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, 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. + * + * ============================================================ */ + +#include "vkontakte_deletealbumjob.h" + +// Qt includes + +#include + +// KDE include + +#include + +// Local includes + +#include "digikam_debug.h" + +namespace Vkontakte +{ + +DeleteAlbumJob::DeleteAlbumJob(const QString& accessToken, int aid) + : VkontakteJob(accessToken, QLatin1String("photos.deleteAlbum"), true) +{ + addQueryItem(QLatin1String("aid"), QString::number(aid)); +} + +void DeleteAlbumJob::handleData(const QJsonValue& data) +{ + if (data.toInt(-1) != 1) + { + setError(1); + setErrorText(i18n("Failed to delete album")); + qCWarning(DIGIKAM_WEBSERVICES_LOG) << "Failed to delete album"; + } +} + +} // namespace Vkontakte diff --git a/core/dplugins/generic/webservices/vkontakte/backend/vkontakte_deletealbumjob.h b/core/dplugins/generic/webservices/vkontakte/backend/vkontakte_deletealbumjob.h new file mode 100644 index 0000000000..3c237c7869 --- /dev/null +++ b/core/dplugins/generic/webservices/vkontakte/backend/vkontakte_deletealbumjob.h @@ -0,0 +1,47 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * https://www.digikam.org + * + * Date : 2011-02-19 + * Description : a tool to export images to VKontakte web service + * + * Copyright (C) 2011-2015 by Alexander Potashev + * Copyright (C) 2011-2020 by Gilles Caulier + * + * 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, 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. + * + * ============================================================ */ + +#ifndef DIGIKAM_VKONTAKTE_DELETEALBUMJOB_H +#define DIGIKAM_VKONTAKTE_DELETEALBUMJOB_H + +// Local includes + +#include "vkontakte_jobs.h" + +namespace Vkontakte +{ + +class DeleteAlbumJob : public VkontakteJob +{ +public: + + explicit DeleteAlbumJob(const QString& accessToken, int aid); + +protected: + + void handleData(const QJsonValue& data) override; +}; + +} // namespace Vkontakte + +#endif // DIGIKAM_VKONTAKTE_DELETEALBUMJOB_H diff --git a/core/dplugins/generic/webservices/vkontakte/backend/vkontakte_editalbumjob.cpp b/core/dplugins/generic/webservices/vkontakte/backend/vkontakte_editalbumjob.cpp new file mode 100644 index 0000000000..b4e8bedd1b --- /dev/null +++ b/core/dplugins/generic/webservices/vkontakte/backend/vkontakte_editalbumjob.cpp @@ -0,0 +1,71 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * https://www.digikam.org + * + * Date : 2011-02-19 + * Description : a tool to export images to VKontakte web service + * + * Copyright (C) 2011-2015 by Alexander Potashev + * Copyright (C) 2011-2020 by Gilles Caulier + * + * 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, 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. + * + * ============================================================ */ + +#include "vkontakte_editalbumjob.h" + +#include + +#include "digikam_debug.h" +#include + +namespace Vkontakte +{ + +EditAlbumJob::EditAlbumJob(const QString& accessToken, + int aid, + const QString& title, + const QString& description, + int privacy, + int comment_privacy) + : VkontakteJob(accessToken, QLatin1String("photos.editAlbum"), true) +{ + addQueryItem(QLatin1String("aid"), QString::number(aid)); + addQueryItem(QLatin1String("title"), title); + + if (!description.isEmpty()) + { + addQueryItem(QLatin1String("description"), description); + } + + if (privacy != AlbumInfo::PRIVACY_UNKNOWN) + { + addQueryItem(QLatin1String("privacy"), QString::number(privacy)); + } + + if (comment_privacy != AlbumInfo::PRIVACY_UNKNOWN) + { + addQueryItem(QLatin1String("comment_privacy"), QString::number(comment_privacy)); + } +} + +void EditAlbumJob::handleData(const QJsonValue& data) +{ + if (data.toInt(-1) != 1) + { + setError(1); + setErrorText(i18n("Failed to edit album")); + qCWarning(DIGIKAM_WEBSERVICES_LOG) << "Failed to edit album"; + } +} + +} // namespace Vkontakte diff --git a/core/dplugins/generic/webservices/vkontakte/backend/vkontakte_editalbumjob.h b/core/dplugins/generic/webservices/vkontakte/backend/vkontakte_editalbumjob.h new file mode 100644 index 0000000000..ce8cd9763c --- /dev/null +++ b/core/dplugins/generic/webservices/vkontakte/backend/vkontakte_editalbumjob.h @@ -0,0 +1,53 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * https://www.digikam.org + * + * Date : 2011-02-19 + * Description : a tool to export images to VKontakte web service + * + * Copyright (C) 2011-2015 by Alexander Potashev + * Copyright (C) 2011-2020 by Gilles Caulier + * + * 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, 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. + * + * ============================================================ */ + +#ifndef DIGIKAM_VKONTAKTE_EDITALBUMJOB_H +#define DIGIKAM_VKONTAKTE_EDITALBUMJOB_H + +// Local includes + +#include "vkontakte_jobs.h" +#include "vkontakte_albuminfo.h" + +namespace Vkontakte +{ + +class EditAlbumJob : public VkontakteJob +{ +public: + + EditAlbumJob(const QString& accessToken, + int aid, + const QString& title, + const QString& description = QString(), + int privacy = AlbumInfo::PRIVACY_UNKNOWN, + int comment_privacy = AlbumInfo::PRIVACY_UNKNOWN); + +protected: + + void handleData(const QJsonValue& data) override; +}; + +} // namespace Vkontakte + +#endif // DIGIKAM_VKONTAKTE_EDITALBUMJOB_H diff --git a/core/dplugins/generic/webservices/vkontakte/backend/vkontakte_getapplicationpermissionsjob.cpp b/core/dplugins/generic/webservices/vkontakte/backend/vkontakte_getapplicationpermissionsjob.cpp new file mode 100644 index 0000000000..f3e09258a5 --- /dev/null +++ b/core/dplugins/generic/webservices/vkontakte/backend/vkontakte_getapplicationpermissionsjob.cpp @@ -0,0 +1,66 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * https://www.digikam.org + * + * Date : 2011-02-19 + * Description : a tool to export images to VKontakte web service + * + * Copyright (C) 2011-2015 by Alexander Potashev + * Copyright (C) 2011-2020 by Gilles Caulier + * + * 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, 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. + * + * ============================================================ */ + +#include "vkontakte_getapplicationpermissionsjob.h" + +// Qt includes + +#include +#include + +namespace Vkontakte +{ + +class Q_DECL_HIDDEN GetApplicationPermissionsJob::Private +{ +public: + + int permissions; +}; + +// http://vk.com/dev/account.getAppPermissions +// http://vk.com/dev/permissions +GetApplicationPermissionsJob::GetApplicationPermissionsJob(const QString& accessToken) + : VkontakteJob(accessToken, QLatin1String("account.getAppPermissions")), + d(new Private) +{ +} + +GetApplicationPermissionsJob::~GetApplicationPermissionsJob() +{ + delete d; +} + +void GetApplicationPermissionsJob::handleData(const QJsonValue& data) +{ + d->permissions = data.toInt(-1); + + // TODO: return error (call handleError?) when "data" is not an integer +} + +int GetApplicationPermissionsJob::permissions() const +{ + return d->permissions; +} + +} // namespace Vkontakte diff --git a/core/dplugins/generic/webservices/vkontakte/backend/vkontakte_getapplicationpermissionsjob.h b/core/dplugins/generic/webservices/vkontakte/backend/vkontakte_getapplicationpermissionsjob.h new file mode 100644 index 0000000000..6ea4354a72 --- /dev/null +++ b/core/dplugins/generic/webservices/vkontakte/backend/vkontakte_getapplicationpermissionsjob.h @@ -0,0 +1,55 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * https://www.digikam.org + * + * Date : 2011-02-19 + * Description : a tool to export images to VKontakte web service + * + * Copyright (C) 2011-2015 by Alexander Potashev + * Copyright (C) 2011-2020 by Gilles Caulier + * + * 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, 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. + * + * ============================================================ */ + +#ifndef DIGIKAM_VKONTAKTE_GETAPPLICATIONPERMISSIONSJOB_H +#define DIGIKAM_VKONTAKTE_GETAPPLICATIONPERMISSIONSJOB_H + +// Local includes + +#include "vkontakte_jobs.h" + +namespace Vkontakte +{ + +class GetApplicationPermissionsJob : public VkontakteJob +{ +public: + + explicit GetApplicationPermissionsJob(const QString& accessToken); + ~GetApplicationPermissionsJob(); + + int permissions() const; + +protected: + + void handleData(const QJsonValue& data) override; + +private: + + class Private; + Private* const d; +}; + +} // namespace Vkontakte + +#endif // DIGIKAM_VKONTAKTE_GETAPPLICATIONPERMISSIONSJOB_H diff --git a/core/dplugins/generic/webservices/vkontakte/backend/vkontakte_getphotouploadserverjob.cpp b/core/dplugins/generic/webservices/vkontakte/backend/vkontakte_getphotouploadserverjob.cpp new file mode 100644 index 0000000000..b7bb853dcb --- /dev/null +++ b/core/dplugins/generic/webservices/vkontakte/backend/vkontakte_getphotouploadserverjob.cpp @@ -0,0 +1,163 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * https://www.digikam.org + * + * Date : 2011-02-19 + * Description : a tool to export images to VKontakte web service + * + * Copyright (C) 2011-2015 by Alexander Potashev + * Copyright (C) 2011-2020 by Gilles Caulier + * + * 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, 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. + * + * ============================================================ */ + +#include "vkontakte_getphotouploadserverjob.h" + +// Qt includes + +#include + +// Local includes + +#include "vkontakte_uploadphotosjob.h" +#include "digikam_debug.h" + +namespace Vkontakte +{ + +GetPhotoUploadServerJob::GetPhotoUploadServerJob(const QString& accessToken, Vkontakte::UploadPhotosJob::Dest dest) + : VkontakteJob(accessToken, getMethod(dest)), + m_dest(dest), + m_aid(-1), + m_gid(-1), + m_uid(-1) +{ +} + +void GetPhotoUploadServerJob::initUploadAlbum(int aid, int gid) +{ + m_aid = aid; + m_gid = gid; +} + +// static +QString GetPhotoUploadServerJob::getMethod(Vkontakte::UploadPhotosJob::Dest dest) +{ + switch (dest) + { + case Vkontakte::UploadPhotosJob::DEST_ALBUM: + { + return QLatin1String("photos.getUploadServer"); + } + + case Vkontakte::UploadPhotosJob::DEST_PROFILE: + { + return QLatin1String("photos.getProfileUploadServer"); + } + + case Vkontakte::UploadPhotosJob::DEST_WALL: + { + return QLatin1String("photos.getWallUploadServer"); + } + + default: + { + return QLatin1String(""); + } + } +} + +void GetPhotoUploadServerJob::prepareQueryItems() +{ + switch (m_dest) + { + case Vkontakte::UploadPhotosJob::DEST_ALBUM: + { + if (m_aid == -1) + { + setError(KJob::UserDefinedError); + setErrorText(QLatin1String("m_aid not set.")); + qCWarning(DIGIKAM_WEBSERVICES_LOG) << "m_aid not set."; + } + + addQueryItem(QLatin1String("aid"), QString::number(m_aid)); + + if (m_gid != -1) + { + addQueryItem(QLatin1String("gid"), QString::number(m_gid)); + } + + } + break; + + case Vkontakte::UploadPhotosJob::DEST_PROFILE: + { + // photos.getProfileUploadServer has not parameters + break; + } + + case Vkontakte::UploadPhotosJob::DEST_WALL: + { + if ((m_uid != -1) && (m_gid != -1)) + { + setError(KJob::UserDefinedError); + setErrorText(QLatin1String("Only one parameter m_uid or m_gid should be set.")); + qCWarning(DIGIKAM_WEBSERVICES_LOG) << "Only one parameter m_uid or m_gid should be set."; + } + + if (m_uid != -1) + { + addQueryItem(QLatin1String("uid"), QString::number(m_uid)); + } + + if (m_gid != -1) + { + addQueryItem(QLatin1String("gid"), QString::number(m_gid)); + } + + break; + } + + default: + { + setError(KJob::UserDefinedError); + setErrorText(QLatin1String("Unsupported m_dest.")); + qCWarning(DIGIKAM_WEBSERVICES_LOG) << "Unsupported m_dest."; + break; + } + } +} + +void GetPhotoUploadServerJob::handleData(const QJsonValue& data) +{ + if (!data.isObject()) + { + return; + } + + // TODO: simplify this code + QJsonObject object = data.toObject(); + + if (object.contains(QLatin1String("upload_url")) && + object.value(QLatin1String("upload_url")).isString()) + { + m_uploadUrl = QUrl(object.value(QLatin1String("upload_url")).toString()); + } +} + +QUrl GetPhotoUploadServerJob::uploadUrl() const +{ + return m_uploadUrl; +} + +} // namespace Vkontakte diff --git a/core/dplugins/generic/webservices/vkontakte/backend/vkontakte_getphotouploadserverjob.h b/core/dplugins/generic/webservices/vkontakte/backend/vkontakte_getphotouploadserverjob.h new file mode 100644 index 0000000000..fec364d41c --- /dev/null +++ b/core/dplugins/generic/webservices/vkontakte/backend/vkontakte_getphotouploadserverjob.h @@ -0,0 +1,65 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * https://www.digikam.org + * + * Date : 2011-02-19 + * Description : a tool to export images to VKontakte web service + * + * Copyright (C) 2011-2015 by Alexander Potashev + * Copyright (C) 2011-2020 by Gilles Caulier + * + * 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, 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. + * + * ============================================================ */ + +#ifndef DIGIKAM_VKONTAKTE_GETPHOTOUPLOADSERVERJOB_H +#define DIGIKAM_VKONTAKTE_GETPHOTOUPLOADSERVERJOB_H + +// Local includes + +#include "vkontakte_jobs.h" +#include "vkontakte_uploadphotosjob.h" + +namespace Vkontakte +{ + +// This class is not exported, so: +// - we don't care about ABI of this class (not using Pimpl) +// - library's users should use class UploadPhotosJob +class GetPhotoUploadServerJob : public VkontakteJob +{ +public: + + GetPhotoUploadServerJob(const QString& accessToken, Vkontakte::UploadPhotosJob::Dest m_dest); + + void initUploadAlbum(int aid, int gid = -1); + QUrl uploadUrl() const; + +protected: + + void prepareQueryItems() override; + void handleData(const QJsonValue& data) override; + + static QString getMethod(enum UploadPhotosJob::Dest dest); + +private: + + UploadPhotosJob::Dest m_dest; + QUrl m_uploadUrl; + int m_aid; + int m_gid; + int m_uid; +}; + +} // namespace Vkontakte + +#endif // DIGIKAM_VKONTAKTE_GETPHOTOUPLOADSERVERJOB_H diff --git a/core/dplugins/generic/webservices/vkontakte/backend/vkontakte_jobs.cpp b/core/dplugins/generic/webservices/vkontakte/backend/vkontakte_jobs.cpp new file mode 100644 index 0000000000..1f29b347e9 --- /dev/null +++ b/core/dplugins/generic/webservices/vkontakte/backend/vkontakte_jobs.cpp @@ -0,0 +1,261 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * https://www.digikam.org + * + * Date : 2011-02-19 + * Description : a tool to export images to VKontakte web service + * + * Copyright (C) 2011-2015 by Alexander Potashev + * Copyright (C) 2011-2020 by Gilles Caulier + * + * 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, 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. + * + * ============================================================ */ + +#include "vkontakte_jobs.h" + +// Qt includes + +#include +#include +#include +#include +#include + +// KDE includes + +#include +#include +#include + +// Local includes + +#include "digikam_debug.h" + +namespace Vkontakte +{ + +bool KJobWithSubjob::doKill() +{ + if (m_job) + { + m_job->kill(KJob::Quietly); + } + + return KJob::doKill(); +} + +//--------------------------------------------------------------- + +bool KJobWithSubjobs::doKill() +{ + foreach (KJob* const job, m_jobs) + { + job->kill(KJob::Quietly); + } + + m_jobs.clear(); + + return KJob::doKill(); +} + +//--------------------------------------------------------------- + +/* + * VkontakteJobs base class + */ +VkontakteJob::VkontakteJob(const QString& accessToken, const QString& method, bool httpPost) + : m_accessToken(accessToken), + m_method(method), + m_httpPost(httpPost) +{ + setCapabilities(KJob::Killable); +} + +void VkontakteJob::addQueryItem(const QString& key, const QString& value) +{ + QueryItem item; + item.first = key; + item.second = value; + m_queryItems.append(item); +} + +bool VkontakteJob::handleError(const QJsonValue& data) +{ + int error_code = -1; + QString error_msg; + + if (data.isUndefined()) + { + qCWarning(DIGIKAM_WEBSERVICES_LOG) << "Response from server has unexpected format"; + } + else + { + const QVariantMap errorMap = data.toVariant().toMap(); + error_code = errorMap[QLatin1String("error_code")].toInt(); + error_msg = errorMap[QLatin1String("error_msg")].toString(); + + qCWarning(DIGIKAM_WEBSERVICES_LOG) << "An error of type" << error_code << "occurred:" << error_msg; + } + + if (error_code == 6) + { + // "Too many requests per second", we will retry after a delay. + // VK API limit the rate of requests to 3 requests per second, + // so it should be OK if we wait for 340 ms. + QTimer::singleShot(340, this, SLOT(slotRetry())); + + return true; + } + else + { + setError(KJob::UserDefinedError); + + if (data.isUndefined()) + { + setErrorText(i18n("Response from the VKontakte server has unexpected format. " + "Please report this problem against product digiKam " + "at the KDE bug tracker.", + QLatin1String("http://bugs.kde.org/"))); + } + else + { + setErrorText(i18n("The VKontakte server returned an error " + "of type %1 in reply to method %2: %3", + error_code, m_method, error_msg)); + } + + return false; + } +} + +KJob* VkontakteJob::createHttpJob() +{ + QUrl url; + url.setScheme(QLatin1String("https")); + url.setHost(QLatin1String("api.vk.com")); + url.setPath(QLatin1String("/method/") + m_method); + + // Collect query items in "query" + QUrlQuery query; + + prepareQueryItems(); + + foreach (const QueryItem& item, m_queryItems) + { + query.addQueryItem(item.first, item.second); + } + + if (!m_accessToken.isEmpty()) + { + query.addQueryItem(QLatin1String("access_token"), m_accessToken); + } + + url.setQuery(query); + + // TODO: Save QUrl to reuse it if we need to retry the HTTP request +// m_url = url; + + qCDebug(DIGIKAM_WEBSERVICES_LOG) << "Starting request" << url; + + if (m_httpPost) + { + return KIO::storedHttpPost(QByteArray(), url, KIO::HideProgressInfo); + } + else + { + return KIO::storedGet(url, KIO::Reload, KIO::HideProgressInfo); + } +} + +void VkontakteJob::start() +{ + m_job = createHttpJob(); + + connect(m_job, SIGNAL(result(KJob*)), + this, SLOT(jobFinished(KJob*))); + + m_job->start(); +} + +void VkontakteJob::slotRetry() +{ + start(); +} + +void VkontakteJob::jobFinished(KJob* kjob) +{ + KIO::StoredTransferJob* const job = dynamic_cast(kjob); + Q_ASSERT(job); + + if (job == nullptr) + { + setError(-1); + setErrorText(i18n("Internal error: No valid instance of KIO::StoredTransferJob " + "passed into VkontakteJob::jobFinished.")); + + qCWarning(DIGIKAM_WEBSERVICES_LOG) << "KIO::StoredTransferJob is null"; + } + else if (job->error()) + { + setError(job->error()); + setErrorText(KIO::buildErrorString(error(), job->errorText())); + qCWarning(DIGIKAM_WEBSERVICES_LOG) << "Job error:" << job->errorString(); + } + else + { + qCDebug(DIGIKAM_WEBSERVICES_LOG) << "Got data:" << job->data(); + + QJsonParseError parseError; + QJsonDocument data = QJsonDocument::fromJson(job->data(), &parseError); + + if (parseError.error == QJsonParseError::NoError) + { + const QJsonObject object = data.object(); + + if (!data.isObject() || + (!object.contains(QLatin1String("response")) && !object.contains(QLatin1String("error")))) + { + // Something went wrong, but there is no valid object "error" + handleError(QJsonValue::Undefined); + } + else if (object.contains(QLatin1String("error"))) + { + bool willRetry = handleError(object.value(QLatin1String("error"))); + + if (willRetry) + { + return; + } + } + else + { + handleData(object.value(QLatin1String("response"))); + } + } + else + { + qCWarning(DIGIKAM_WEBSERVICES_LOG) << "Unable to parse JSON data:" << parseError.errorString(); + qCDebug(DIGIKAM_WEBSERVICES_LOG) << "Received data:" << job->data(); + + setError(KJob::UserDefinedError); + setErrorText(i18n("Unable to parse data returned by the VKontakte server: %1", + parseError.errorString())); + } + } + + emitResult(); + + m_job = nullptr; +} + +} // namespace Vkontakte diff --git a/core/dplugins/generic/webservices/vkontakte/backend/vkontakte_jobs.h b/core/dplugins/generic/webservices/vkontakte/backend/vkontakte_jobs.h new file mode 100644 index 0000000000..aeeba79274 --- /dev/null +++ b/core/dplugins/generic/webservices/vkontakte/backend/vkontakte_jobs.h @@ -0,0 +1,140 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * https://www.digikam.org + * + * Date : 2011-02-19 + * Description : a tool to export images to VKontakte web service + * + * Copyright (C) 2011-2015 by Alexander Potashev + * Copyright (C) 2011-2020 by Gilles Caulier + * + * 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, 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. + * + * ============================================================ */ + +#ifndef DIGIKAM_VKONTAKTE_JOBS_H +#define DIGIKAM_VKONTAKTE_JOBS_H + +// Qt includes + +#include + +// KDE includes + +#include + +namespace Vkontakte +{ + +typedef QPair QueryItem; + +class KJobWithSubjob : public KJob +{ +protected: + + /** + * Kill the currentjobs and its subjobs + */ + bool doKill() override; + + /** + * Pointer to the running job + */ + QPointer m_job; +}; + +class KJobWithSubjobs : public KJob +{ +protected: + + /** + * Kill the currentjobs and its subjobs + */ + bool doKill() override; + + /** + * Pointers to running jobs + */ + QList m_jobs; +}; + +/** + * VkontakteJob base class + */ +class VkontakteJob : public KJobWithSubjob +{ + Q_OBJECT + +public: + + enum JobErrorType + { + AuthenticationProblem = KJob::UserDefinedError + 42 + }; + +public: + + /** + * Constructor that sets the path and the accesstoken + * + * @param method For the list of methods see http://vk.com/dev/methods (in English or Russian) + * @param accessToken The accessToken to access our data on vkontakte + * @param httpPost Whether to make a POST http request instead of GET ("false" by default) + * */ + VkontakteJob(const QString& accessToken, const QString& method, bool httpPost = false); + + void start() override; + +protected: + + /** + * Add a query item to the list + */ + void addQueryItem(const QString& key, const QString& value); + +private: + + /** + * Check for a return error and set the appropriate error messages + */ + + /** + * Returns "true" if we will retry the call. + */ + bool handleError(const QJsonValue& data); + + virtual void handleData(const QJsonValue& data) = 0; + + /** + * Called right before sending request to server + */ + virtual void prepareQueryItems() {} + + // TODO: cache url in a member variable + KJob* createHttpJob(); + +private Q_SLOTS: + + void jobFinished(KJob* kjob); + void slotRetry(); + +private: + + QString m_accessToken; ///< Vkontakte Access token + QString m_method; + bool m_httpPost; + QList m_queryItems; ///< The query items +}; + +} // namespace Vkontakte + +#endif // DIGIKAM_VKONTAKTE_JOBS_H diff --git a/core/dplugins/generic/webservices/vkontakte/backend/vkontakte_photoinfo.cpp b/core/dplugins/generic/webservices/vkontakte/backend/vkontakte_photoinfo.cpp new file mode 100644 index 0000000000..d442d77d9e --- /dev/null +++ b/core/dplugins/generic/webservices/vkontakte/backend/vkontakte_photoinfo.cpp @@ -0,0 +1,104 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * https://www.digikam.org + * + * Date : 2011-02-19 + * Description : a tool to export images to VKontakte web service + * + * Copyright (C) 2011-2015 by Alexander Potashev + * Copyright (C) 2011-2020 by Gilles Caulier + * + * 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, 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. + * + * ============================================================ */ + +#include "vkontakte_photoinfo.h" + +// Local includes + +#include "vkontakte_util.h" + +namespace Vkontakte +{ + +class Q_DECL_HIDDEN PhotoInfo::Private : public QSharedData +{ +public: + + QJsonObject jsonData; +}; + +PhotoInfo::PhotoInfo() + : d(new Private) +{ +} + +PhotoInfo::PhotoInfo(const QJsonObject& jsonData) + : d(new Private) +{ + d->jsonData = jsonData; +} + +PhotoInfo::PhotoInfo(const PhotoInfo& other) + : d(other.d) +{ +} + +PhotoInfo::~PhotoInfo() +{ +} + +PhotoInfo &PhotoInfo::operator=(const PhotoInfo& other) +{ + if (this != &other) + { + d = other.d; + } + + return *this; +} + +int PhotoInfo::pid() const +{ + return d->jsonData.value(QLatin1String("pid")).toInt(-1); +} + +QUrl PhotoInfo::urlMaxResolution() const +{ + // Photo URLs in the order of decresing size + QStringList srcKeys; + srcKeys.append(QLatin1String("src_xxxbig")); + srcKeys.append(QLatin1String("src_xxbig")); + srcKeys.append(QLatin1String("src_xbig")); + srcKeys.append(QLatin1String("src_big")); + srcKeys.append(QLatin1String("src")); + srcKeys.append(QLatin1String("src_small")); + + foreach (const QString& key, srcKeys) + { + if (!d->jsonData.contains(key)) + { + continue; + } + + QJsonValue value = d->jsonData.value(key); + + if (value.isString()) + { + return QUrl(value.toString()); + } + } + + return QUrl(); +} + +} // namespace Vkontakte diff --git a/core/dplugins/generic/webservices/vkontakte/backend/vkontakte_photoinfo.h b/core/dplugins/generic/webservices/vkontakte/backend/vkontakte_photoinfo.h new file mode 100644 index 0000000000..8ba01651f6 --- /dev/null +++ b/core/dplugins/generic/webservices/vkontakte/backend/vkontakte_photoinfo.h @@ -0,0 +1,66 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * https://www.digikam.org + * + * Date : 2011-02-19 + * Description : a tool to export images to VKontakte web service + * + * Copyright (C) 2011-2015 by Alexander Potashev + * Copyright (C) 2011-2020 by Gilles Caulier + * + * 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, 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. + * + * ============================================================ */ + +#ifndef DIGIKAM_VKONTAKTE_PHOTOINFO_H +#define DIGIKAM_VKONTAKTE_PHOTOINFO_H + +// Qt includes + +#include +#include +#include + +namespace Vkontakte +{ + +// http://vk.com/dev/photos.get +class PhotoInfo +{ +public: + + PhotoInfo(); + PhotoInfo(const PhotoInfo& other); + explicit PhotoInfo(const QJsonObject& jsonData); + ~PhotoInfo(); + + PhotoInfo& operator=(const PhotoInfo& other); + + /** + * @brief Returns the integer ID of the photo + */ + int pid() const; + + /** + * @brief Return URL to the image version of maximum resolution + */ + QUrl urlMaxResolution() const; + +private: + + class Private; + QSharedDataPointer d; +}; + +} // namespace Vkontakte + +#endif // DIGIKAM_VKONTAKTE_PHOTOINFO_H diff --git a/core/dplugins/generic/webservices/vkontakte/backend/vkontakte_photojob.cpp b/core/dplugins/generic/webservices/vkontakte/backend/vkontakte_photojob.cpp new file mode 100644 index 0000000000..37d63f0cd1 --- /dev/null +++ b/core/dplugins/generic/webservices/vkontakte/backend/vkontakte_photojob.cpp @@ -0,0 +1,97 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * https://www.digikam.org + * + * Date : 2011-02-19 + * Description : a tool to export images to VKontakte web service + * + * Copyright (C) 2011-2015 by Alexander Potashev + * Copyright (C) 2011-2020 by Gilles Caulier + * + * 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, 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. + * + * ============================================================ */ + +#include "vkontakte_photojob.h" + +// Qt includes + +#include + +// KDE includes + +#include + +// Local includes + +#include "digikam_debug.h" + +namespace Vkontakte +{ + +class Q_DECL_HIDDEN PhotoJob::Private +{ +public: + + QUrl url; + QImage photo; +}; + +PhotoJob::PhotoJob(const QUrl& url) + : d(new Private) +{ + d->url = url; +} + +PhotoJob::~PhotoJob() +{ + delete d; +} + +void PhotoJob::start() +{ + qCDebug(DIGIKAM_WEBSERVICES_LOG) << "Starting photo download" << d->url; + KIO::StoredTransferJob* const job = KIO::storedGet(d->url, KIO::Reload, KIO::HideProgressInfo); + m_job = job; + + connect(job, SIGNAL(result(KJob*)), + this, SLOT(jobFinished(KJob*))); + + job->start(); +} + +void PhotoJob::jobFinished(KJob* kjob) +{ + KIO::StoredTransferJob* const job = dynamic_cast(kjob); + Q_ASSERT(job); + + if (job && job->error()) + { + setError(job->error()); + setErrorText(KIO::buildErrorString(error(), job->errorText())); + qCWarning(DIGIKAM_WEBSERVICES_LOG) << "Job error:" << job->errorString(); + } + else + { + d->photo = QImage::fromData(job->data()); + } + + emitResult(); + m_job = 0; +} + +QImage PhotoJob::photo() const +{ + return d->photo; +} + +} // namespace Vkontakte diff --git a/core/dplugins/generic/webservices/vkontakte/backend/vkontakte_photojob.h b/core/dplugins/generic/webservices/vkontakte/backend/vkontakte_photojob.h new file mode 100644 index 0000000000..af76f08da9 --- /dev/null +++ b/core/dplugins/generic/webservices/vkontakte/backend/vkontakte_photojob.h @@ -0,0 +1,61 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * https://www.digikam.org + * + * Date : 2011-02-19 + * Description : a tool to export images to VKontakte web service + * + * Copyright (C) 2011-2015 by Alexander Potashev + * Copyright (C) 2011-2020 by Gilles Caulier + * + * 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, 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. + * + * ============================================================ */ + +#ifndef DIGIKAM_VKONTAKTE_PHOTOJOBBASE_H +#define DIGIKAM_VKONTAKTE_PHOTOJOBBASE_H + +#include "vkontakte_jobs.h" + +// Local includes + +#include "vkontakte_userinfo.h" + +namespace Vkontakte +{ + +// KJobWithSubjob::doKill will be inherited +class PhotoJob : public KJobWithSubjob +{ + Q_OBJECT + +public: + + explicit PhotoJob(const QUrl& url); + ~PhotoJob(); + + void start() override; + QImage photo() const; + +private Q_SLOTS: + + void jobFinished(KJob* kjob); + +private: + + class Private; + Private* const d; +}; + +} // namespace Vkontakte + +#endif // DIGIKAM_VKONTAKTE_PHOTOJOBBASE_H diff --git a/core/dplugins/generic/webservices/vkontakte/backend/vkontakte_photolistjob.cpp b/core/dplugins/generic/webservices/vkontakte/backend/vkontakte_photolistjob.cpp new file mode 100644 index 0000000000..ac66f3c70c --- /dev/null +++ b/core/dplugins/generic/webservices/vkontakte/backend/vkontakte_photolistjob.cpp @@ -0,0 +1,93 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * https://www.digikam.org + * + * Date : 2011-02-19 + * Description : a tool to export images to VKontakte web service + * + * Copyright (C) 2011-2015 by Alexander Potashev + * Copyright (C) 2011-2020 by Gilles Caulier + * + * 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, 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. + * + * ============================================================ */ + +#include "vkontakte_photolistjob.h" + +// Qt includes + +#include + +// Local includes + +#include "vkontakte_util.h" + +namespace Vkontakte +{ + +class Q_DECL_HIDDEN PhotoListJob::Private +{ +public: + + QList list; +}; + +// http://vk.com/dev/photos.get +// +// The API also allows to set "limit" and "offset", but that +// does not seem to be useful. +PhotoListJob::PhotoListJob(const QString& accessToken, + int uid, int aid, const QList& pids) + : VkontakteJob(accessToken, QLatin1String("photos.get")), + d(new Private) +{ + addQueryItem(QLatin1String("uid"), QString::number(uid)); + addQueryItem(QLatin1String("aid"), QString::number(aid)); + + if (!pids.empty()) + { + addQueryItem(QLatin1String("pids"), joinIntegers(pids)); + } +} + +PhotoListJob::~PhotoListJob() +{ + delete d; +} + +void PhotoListJob::handleData(const QJsonValue& data) +{ + if (!data.isArray()) + { + // TODO: report error!!! + return; + } + + foreach (const QJsonValue& item, data.toArray()) + { + if (!item.isObject()) + { + // TODO: report error!!! + d->list.clear(); + return; + } + + d->list.append(PhotoInfo(item.toObject())); + } +} + +QList PhotoListJob::list() const +{ + return d->list; +} + +} // namespace Vkontakte diff --git a/core/dplugins/generic/webservices/vkontakte/backend/vkontakte_photolistjob.h b/core/dplugins/generic/webservices/vkontakte/backend/vkontakte_photolistjob.h new file mode 100644 index 0000000000..d3b58e35ca --- /dev/null +++ b/core/dplugins/generic/webservices/vkontakte/backend/vkontakte_photolistjob.h @@ -0,0 +1,63 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * https://www.digikam.org + * + * Date : 2011-02-19 + * Description : a tool to export images to VKontakte web service + * + * Copyright (C) 2011-2015 by Alexander Potashev + * Copyright (C) 2011-2020 by Gilles Caulier + * + * 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, 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. + * + * ============================================================ */ + +#ifndef DIGIKAM_VKONTAKTE_PHOTOLISTJOB_H +#define DIGIKAM_VKONTAKTE_PHOTOLISTJOB_H + +// Local includes + +#include "vkontakte_jobs.h" +#include "vkontakte_photoinfo.h" + +namespace Vkontakte +{ + +class PhotoListJob : public VkontakteJob +{ + Q_OBJECT + +public: + + explicit PhotoListJob(const QString& accessToken, + int uid, int aid, const QList& pids = QList()); + ~PhotoListJob(); + + QList list() const; + +protected: + + /** + * @brief Handles the data returned by VkontakteJob + * @param data JSON value containing an array of photos. + */ + void handleData(const QJsonValue& data) override; + +private: + + class Private; + Private* const d; +}; + +} // namespace Vkontakte + +#endif // DIGIKAM_VKONTAKTE_PHOTOLISTJOB_H diff --git a/core/dplugins/generic/webservices/vkontakte/backend/vkontakte_photopostjob.cpp b/core/dplugins/generic/webservices/vkontakte/backend/vkontakte_photopostjob.cpp new file mode 100644 index 0000000000..fdb7c76709 --- /dev/null +++ b/core/dplugins/generic/webservices/vkontakte/backend/vkontakte_photopostjob.cpp @@ -0,0 +1,253 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * https://www.digikam.org + * + * Date : 2011-02-19 + * Description : a tool to export images to VKontakte web service + * + * Copyright (C) 2011-2015 by Alexander Potashev + * Copyright (C) 2011-2020 by Gilles Caulier + * + * 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, 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. + * + * ============================================================ */ + +#include "vkontakte_photopostjob.h" + +// Qt includes + +#include +#include +#include +#include +#include +#include +#include +#include + +// KDE includes + +#include +#include + +// Local includes + +#include "digikam_debug.h" + +namespace Vkontakte +{ + +PhotoPostJob::PhotoPostJob(Vkontakte::UploadPhotosJob::Dest dest, + const QUrl& url, const QStringList& files) + : m_dest(dest), + m_url(url), + m_files(files), + m_ok(true) +{ + setCapabilities(KJob::Killable); + + if ((files.size() <= 0) || (files.size() > 5)) + { + m_ok = false; + } +} + +void PhotoPostJob::handleError(const QJsonValue& data) +{ + int error_code = -1; + QString error_msg; + + if (data.isUndefined()) + { + qCWarning(DIGIKAM_WEBSERVICES_LOG) << "Response from server has unexpected format"; + } + else + { + const QVariantMap errorMap = data.toVariant().toMap(); + + error_code = errorMap[QLatin1String("error_code")].toInt(); + error_msg = errorMap[QLatin1String("error_msg")].toString(); + + qCWarning(DIGIKAM_WEBSERVICES_LOG) << "An error of type" << error_code << "occurred:" << error_msg; + } + + setError(KJob::UserDefinedError); + + if (data.isUndefined()) + { + setErrorText(i18n( + "Response from the VKontakte server has unexpected format. " + "Please report this problem against product libkvkontakte " + "at the KDE bug tracker.", + QLatin1String("http://bugs.kde.org/"))); + } + else + { + setErrorText(i18n( + "The VKontakte server returned an error " + "of type %1 in reply to uploading to URL %2: %3", + error_code, m_url.toString(), error_msg)); + } +} + +bool PhotoPostJob::appendFile(QHttpMultiPart* multiPart, const QString& header, const QString& path) +{ + QString mime = QMimeDatabase().mimeTypeForUrl(QUrl(path)).name(); + + if (mime.isEmpty()) + { + return false; + } + + QFileInfo fileInfo(path); + + QHttpPart imagePart; + imagePart.setHeader(QNetworkRequest::ContentDispositionHeader, + QVariant(QString::fromUtf8("form-data; name=\"%1\"; filename=\"%2\"") + .arg(header).arg(fileInfo.fileName()))); + imagePart.setHeader(QNetworkRequest::ContentLengthHeader, QVariant(fileInfo.size())); + imagePart.setHeader(QNetworkRequest::ContentTypeHeader, QVariant(mime)); + QFile* const file = new QFile(path); + + if (!file->open(QIODevice::ReadOnly)) + { + delete file; + return false; + } + + imagePart.setBodyDevice(file); + file->setParent(multiPart); // we cannot delete the file now, so delete it with the multiPart + + multiPart->append(imagePart); + + return true; +} + +void PhotoPostJob::start() +{ + if (!m_ok) + { + setError(UserDefinedError); + setErrorText(QLatin1String("Internal error")); + emitResult(); + } + + QHttpMultiPart* const multiPart = new QHttpMultiPart(QHttpMultiPart::FormDataType); + + switch (m_dest) + { + case Vkontakte::UploadPhotosJob::DEST_ALBUM: + { + // "file1" .. "file5" + for (int i = 0 ; i < m_files.size() ; i++) + { + if (!appendFile(multiPart, QString::fromLatin1("file%1").arg(i + 1), m_files[i])) + { + m_ok = false; + break; + } + } + + break; + } + + case Vkontakte::UploadPhotosJob::DEST_PROFILE: + case Vkontakte::UploadPhotosJob::DEST_WALL: + { + // "photo" + if (!appendFile(multiPart, QLatin1String("photo"), m_files[0])) + { + m_ok = false; + } + + break; + } + + default: + { + m_ok = false; + break; + } + } + + if (!m_ok) + { + setError(UserDefinedError); + setErrorText(QLatin1String("Could not attach file")); + emitResult(); + } + + QNetworkAccessManager* manager = new QNetworkAccessManager(this); + + connect(manager, SIGNAL(finished(QNetworkReply*)), + this, SLOT(parseNetworkResponse(QNetworkReply*))); + + qCDebug(DIGIKAM_WEBSERVICES_LOG) << "Starting POST request" << m_url; + QNetworkReply* const reply = manager->post(QNetworkRequest(m_url), multiPart); + multiPart->setParent(reply); // delete the multiPart with the reply +} + +void PhotoPostJob::parseNetworkResponse(QNetworkReply *reply) +{ + if (reply->error() != QNetworkReply::NoError) + { + // A communication error has occurred + setError(reply->error()); + setErrorText(KIO::buildErrorString(error(), QString())); +// qCWarning(DIGIKAM_WEBSERVICES_LOG) << "Network error: " << reply->errorString(); + } + else + { + QByteArray ba = reply->readAll(); + qCDebug(DIGIKAM_WEBSERVICES_LOG) << "Got data:" << ba; + + QJsonParseError parseError; + QJsonDocument data = QJsonDocument::fromJson(ba, &parseError); + + if (parseError.error == QJsonParseError::NoError) + { + const QJsonObject object = data.object(); + + if (!data.isObject()) + { + // Something went wrong, but there is no valid object "error" + handleError(QJsonValue::Undefined); + } + else if (object.contains(QLatin1String("error"))) + { + handleError(object.value(QLatin1String("error"))); + } + else + { + // Handle data + m_response = object.toVariantMap(); + } + } + else + { + qCWarning(DIGIKAM_WEBSERVICES_LOG) << "Unable to parse JSON data:" << ba; + setError(KJob::UserDefinedError); + setErrorText( + i18n("Unable to parse data returned by the VKontakte server: %1", + parseError.errorString())); + } + } + + emitResult(); +} + +QVariantMap PhotoPostJob::response() const +{ + return m_response; +} + +} // namespace Vkontakte diff --git a/core/dplugins/generic/webservices/vkontakte/backend/vkontakte_photopostjob.h b/core/dplugins/generic/webservices/vkontakte/backend/vkontakte_photopostjob.h new file mode 100644 index 0000000000..9925e8ff7a --- /dev/null +++ b/core/dplugins/generic/webservices/vkontakte/backend/vkontakte_photopostjob.h @@ -0,0 +1,84 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * https://www.digikam.org + * + * Date : 2011-02-19 + * Description : a tool to export images to VKontakte web service + * + * Copyright (C) 2011-2015 by Alexander Potashev + * Copyright (C) 2011-2020 by Gilles Caulier + * + * 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, 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. + * + * ============================================================ */ + +#ifndef DIGIKAM_VKONTAKTE_PHOTOPOSTJOB_H +#define DIGIKAM_VKONTAKTE_PHOTOPOSTJOB_H + +// Qt includes + +#include +#include + +// Local includes + +#include "vkontakte_jobs.h" +#include "vkontakte_uploadphotosjob.h" + +class QHttpMultiPart; +class QNetworkReply; + +namespace Vkontakte +{ + +// This class is not exported, so: +// - we don't care about ABI of this class (not using Pimpl) +// - library's users should use class UploadPhotosJob +class PhotoPostJob : public KJob +{ + Q_OBJECT + +public: + + // files.size <= 5 + explicit PhotoPostJob(Vkontakte::UploadPhotosJob::Dest dest, const QUrl& url, const QStringList& files); + + void start() override; + QVariantMap response() const; + +protected: + + /** + * Check for a return error and set the appropriate error messages + */ + void handleError(const QJsonValue& data); + +private Q_SLOTS: + + void parseNetworkResponse(QNetworkReply* reply); + +private: + + bool appendFile(QHttpMultiPart* multiPart, const QString& header, const QString& path); + +private: + + UploadPhotosJob::Dest m_dest; + QUrl m_url; + QStringList m_files; + bool m_ok; + QVariantMap m_response; +}; + +} // namespace Vkontakte + +#endif // DIGIKAM_VKONTAKTE_PHOTOPOSTJOB_H diff --git a/core/dplugins/generic/webservices/vkontakte/backend/vkontakte_savephotojob.cpp b/core/dplugins/generic/webservices/vkontakte/backend/vkontakte_savephotojob.cpp new file mode 100644 index 0000000000..07c4589e74 --- /dev/null +++ b/core/dplugins/generic/webservices/vkontakte/backend/vkontakte_savephotojob.cpp @@ -0,0 +1,161 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * https://www.digikam.org + * + * Date : 2011-02-19 + * Description : a tool to export images to VKontakte web service + * + * Copyright (C) 2011-2015 by Alexander Potashev + * Copyright (C) 2011-2020 by Gilles Caulier + * + * 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, 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. + * + * ============================================================ */ + +#include "vkontakte_savephotojob.h" + +// Qt includes + +#include + +namespace Vkontakte +{ + +SavePhotoJob::SavePhotoJob(const QString& accessToken, + UploadPhotosJob::Dest dest, + const QVariantMap& photoIdData, int gid) + : VkontakteJob(accessToken, getMethod(dest), true) +{ + m_dest = dest; + + switch (dest) + { + case Vkontakte::UploadPhotosJob::DEST_ALBUM: + { + addQueryItem(QLatin1String("aid"), photoIdData[QLatin1String("aid")].toString()); + addQueryItem(QLatin1String("server"), photoIdData[QLatin1String("server")].toString()); + addQueryItem(QLatin1String("photos_list"), photoIdData[QLatin1String("photos_list")].toString()); + addQueryItem(QLatin1String("hash"), photoIdData[QLatin1String("hash")].toString()); + + // TODO: may be "gid" will also be in "photoIdData", so this argument is unnecessary? + if (gid != -1) + { + addQueryItem(QLatin1String("gid"), QString::number(gid)); + } + + break; + } + + case Vkontakte::UploadPhotosJob::DEST_PROFILE: + { + addQueryItem(QLatin1String("server"), photoIdData[QLatin1String("server")].toString()); + addQueryItem(QLatin1String("photo"), photoIdData[QLatin1String("photos")].toString()); + addQueryItem(QLatin1String("hash"), photoIdData[QLatin1String("hash")].toString()); + break; + } + + case Vkontakte::UploadPhotosJob::DEST_WALL: + { + // TODO: support optional parameters "uid" and "gid" (for posting to other users' and groups' walls) + // TODO: for posting onto a wall, we must also call the "wall.post" VK method + addQueryItem(QLatin1String("server"), photoIdData[QLatin1String("server")].toString()); + addQueryItem(QLatin1String("photo"), photoIdData[QLatin1String("photo")].toString()); + addQueryItem(QLatin1String("hash"), photoIdData[QLatin1String("hash")].toString()); + break; + } + + default: + { + // TODO: handle unknown destination error + break; + } + } +} + +// static +QString SavePhotoJob::getMethod(Vkontakte::UploadPhotosJob::Dest dest) +{ + switch (dest) + { + case Vkontakte::UploadPhotosJob::DEST_ALBUM: + { + return QLatin1String("photos.save"); + } + + case Vkontakte::UploadPhotosJob::DEST_PROFILE: + { + return QLatin1String("photos.saveProfilePhoto"); + } + + case Vkontakte::UploadPhotosJob::DEST_WALL: + { + return QLatin1String("photos.saveWallPhoto"); + } + + default: + { + return QLatin1String(""); + } + } +} + +void SavePhotoJob::handleItem(const QJsonValue& item) +{ + if (!item.isObject()) + { + // TODO: report error!!! + m_list.clear(); + return; + } + + m_list.append(PhotoInfo(item.toObject())); +} + +void SavePhotoJob::handleData(const QJsonValue& data) +{ + switch (m_dest) + { + case Vkontakte::UploadPhotosJob::DEST_ALBUM: + { + if (!data.isArray()) + { + // TODO: report error!!! + return; + } + + foreach (const QJsonValue& item, data.toArray()) + { + handleItem(item); + } + break; + } + + case Vkontakte::UploadPhotosJob::DEST_PROFILE: + case Vkontakte::UploadPhotosJob::DEST_WALL: + { + handleItem(data); + break; + } + + default: + { + break; + } + } +} + +QList SavePhotoJob::list() const +{ + return m_list; +} + +} // namespace Vkontakte diff --git a/core/dplugins/generic/webservices/vkontakte/backend/vkontakte_savephotojob.h b/core/dplugins/generic/webservices/vkontakte/backend/vkontakte_savephotojob.h new file mode 100644 index 0000000000..ce9d61f35a --- /dev/null +++ b/core/dplugins/generic/webservices/vkontakte/backend/vkontakte_savephotojob.h @@ -0,0 +1,65 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * https://www.digikam.org + * + * Date : 2011-02-19 + * Description : a tool to export images to VKontakte web service + * + * Copyright (C) 2011-2015 by Alexander Potashev + * Copyright (C) 2011-2020 by Gilles Caulier + * + * 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, 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. + * + * ============================================================ */ + +#ifndef DIGIKAM_VKONTAKTE_SAVEPHOTOJOB_H +#define DIGIKAM_VKONTAKTE_SAVEPHOTOJOB_H + +// Qt includes + +#include +#include + +// Local includes + +#include "vkontakte_jobs.h" +#include "vkontakte_photoinfo.h" +#include "vkontakte_uploadphotosjob.h" + +namespace Vkontakte +{ + +// This class is not exported, so: +// - we don't care about ABI of this class (not using Pimpl) +// - library's users should use class UploadPhotosJob +class SavePhotoJob : public VkontakteJob +{ +public: + SavePhotoJob(const QString& accessToken, + UploadPhotosJob::Dest dest, + const QVariantMap& photoIdData, int gid = -1); + + QList list() const; + +protected: + QString getMethod(Vkontakte::UploadPhotosJob::Dest dest); + void handleItem(const QJsonValue& item); + void handleData(const QJsonValue& data) override; + +private: + UploadPhotosJob::Dest m_dest; + QList m_list; +}; + +} // namespace Vkontakte + +#endif // DIGIKAM_VKONTAKTE_SAVEPHOTOJOB_H diff --git a/core/dplugins/generic/webservices/vkontakte/backend/vkontakte_uploadphotosjob.cpp b/core/dplugins/generic/webservices/vkontakte/backend/vkontakte_uploadphotosjob.cpp new file mode 100644 index 0000000000..3590db80c0 --- /dev/null +++ b/core/dplugins/generic/webservices/vkontakte/backend/vkontakte_uploadphotosjob.cpp @@ -0,0 +1,267 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * https://www.digikam.org + * + * Date : 2011-02-19 + * Description : a tool to export images to VKontakte web service + * + * Copyright (C) 2011-2015 by Alexander Potashev + * Copyright (C) 2011-2020 by Gilles Caulier + * + * 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, 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. + * + * ============================================================ */ + +#include "vkontakte_uploadphotosjob.h" + +// Local includes + +#include "vkontakte_getphotouploadserverjob.h" +#include "vkontakte_photopostjob.h" +#include "vkontakte_savephotojob.h" +#include "digikam_debug.h" + +namespace Vkontakte +{ + +const int UploadPhotosJob::MAX_POST_JOBS = 2; +const int UploadPhotosJob::REQUEST_FILES_COUNT = 1; // <= 5 + +class Q_DECL_HIDDEN UploadPhotosJob::Private +{ +public: + + QString accessToken; + QStringList files; + int aid; + int gid; + bool saveBig; + + UploadPhotosJob::Dest dest; + + QUrl uploadUrl; + QList list; + + QList pendingPostJobs; + int workingPostJobs; +}; + +UploadPhotosJob::UploadPhotosJob(const QString& accessToken, + const QStringList& files, bool saveBig, int aid, int gid) + : KJobWithSubjobs(), + d(new Private) +{ + d->accessToken = accessToken; + d->files = files; + d->aid = aid; + d->gid = gid; + d->saveBig = saveBig; + d->workingPostJobs = 0; + d->dest = Vkontakte::UploadPhotosJob::DEST_ALBUM; +} + +UploadPhotosJob::~UploadPhotosJob() +{ + delete d; +} + +int UploadPhotosJob::getMaxRequestFilesCount() const +{ + return REQUEST_FILES_COUNT; +} + +void UploadPhotosJob::start() +{ + emit progress(0); + + GetPhotoUploadServerJob* const job = new GetPhotoUploadServerJob(d->accessToken, d->dest); + job->initUploadAlbum(d->aid, d->gid); + + m_jobs.append(job); + + connect(job, SIGNAL(result(KJob*)), + this, SLOT(serverJobFinished(KJob*))); + + job->start(); +} + +void UploadPhotosJob::serverJobFinished(KJob *kjob) +{ + GetPhotoUploadServerJob* const job = dynamic_cast(kjob); + Q_ASSERT(job); + + if(!job) + { + return; + } + + if (job->error()) + { + setError(job->error()); + setErrorText(job->errorText()); + qCWarning(DIGIKAM_WEBSERVICES_LOG) << "Job error: " << job->errorString(); + + // It is safe to emit result here because there are no jobs + // running in parallel with this one. + emitResult(); + + m_jobs.removeAll(job); + return; + } + + d->uploadUrl = job->uploadUrl(); + int totalCount = d->files.size(); + int requestFilesCount = getMaxRequestFilesCount(); + + for (int offset = 0 ; offset < totalCount ; offset += requestFilesCount) + { + startPostJob(offset, qMin(requestFilesCount, totalCount - offset)); + } + + // Remove as the last step to avoid the situation when m_jobs is empty but + // there is something left to do. + m_jobs.removeAll(job); +} + +bool UploadPhotosJob::mayStartPostJob() +{ + return (d->workingPostJobs < MAX_POST_JOBS); +} + +void UploadPhotosJob::startPostJob(int offset, int count) +{ + PhotoPostJob* const job = new PhotoPostJob(d->dest, d->uploadUrl, d->files.mid(offset, count)); + m_jobs.append(job); + + connect(job, SIGNAL(result(KJob*)), + this, SLOT(postJobFinished(KJob*))); + + if (mayStartPostJob()) + { + d->workingPostJobs++; + job->start(); + } + else + { + d->pendingPostJobs.append(job); + } +} + +void UploadPhotosJob::postJobFinished(KJob* kjob) +{ + PhotoPostJob* const job = dynamic_cast(kjob); + Q_ASSERT(job); + + if (!job) + { + return; + } + + // start one pending job if possible + if (mayStartPostJob() && !d->pendingPostJobs.empty()) + { + PhotoPostJob* const nextJob = d->pendingPostJobs.first(); + d->pendingPostJobs.removeAll(nextJob); + + d->workingPostJobs ++; + nextJob->start(); + } + + if (job->error()) + { + setError(job->error()); + setErrorText(job->errorText()); + qCWarning(DIGIKAM_WEBSERVICES_LOG) << "Job error: " << job->errorString(); + } + + if (error()) + { + if (m_jobs.size() == 1) + { + emitResult(); + } + + d->workingPostJobs--; + m_jobs.removeAll(job); + + return; + } + + startSaveJob(job->response()); + + d->workingPostJobs--; + m_jobs.removeAll(job); +} + +void UploadPhotosJob::startSaveJob(const QVariantMap& photoIdData) +{ + SavePhotoJob* const job = new SavePhotoJob(d->accessToken, d->dest, photoIdData); + m_jobs.append(job); + + connect(job, SIGNAL(result(KJob*)), + this, SLOT(saveJobFinished(KJob*))); + + job->start(); +} + +void UploadPhotosJob::saveJobFinished(KJob* kjob) +{ + // TODO: Try to preserve the original order of photos. + // This task might be difficult when MAX_POST_JOBS > 1 + + SavePhotoJob* const job = dynamic_cast(kjob); + Q_ASSERT(job); + + if (!job) + { + return; + } + + if (job->error()) + { + setError(job->error()); + setErrorText(job->errorText()); + qCWarning(DIGIKAM_WEBSERVICES_LOG) << "Job error: " << job->errorString(); + } + + if (error()) + { + // All subjobs have finished + + if (m_jobs.size() == 1) + { + emitResult(); + } + + m_jobs.removeAll(job); + + return; + } + + d->list.append(job->list()); + emit progress(100 * d->list.size() / d->files.size()); + + // All subjobs have finished + if (m_jobs.size() == 1) + { + emitResult(); + } + + m_jobs.removeAll(job); +} + +QList UploadPhotosJob::list() const +{ + return d->list; +} + +} // namespace Vkontakte diff --git a/core/dplugins/generic/webservices/vkontakte/backend/vkontakte_uploadphotosjob.h b/core/dplugins/generic/webservices/vkontakte/backend/vkontakte_uploadphotosjob.h new file mode 100644 index 0000000000..105d98070d --- /dev/null +++ b/core/dplugins/generic/webservices/vkontakte/backend/vkontakte_uploadphotosjob.h @@ -0,0 +1,92 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * https://www.digikam.org + * + * Date : 2011-02-19 + * Description : a tool to export images to VKontakte web service + * + * Copyright (C) 2011-2015 by Alexander Potashev + * Copyright (C) 2011-2020 by Gilles Caulier + * + * 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, 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. + * + * ============================================================ */ + +#ifndef DIGIKAM_VKONTAKTE_UPLOADPHOTOSJOB_H +#define DIGIKAM_VKONTAKTE_UPLOADPHOTOSJOB_H + +// Qt includes + +#include + +// Local includes + +#include "vkontakte_jobs.h" +#include "vkontakte_photoinfo.h" + +namespace Vkontakte +{ + +class UploadPhotosJob : public KJobWithSubjobs +{ + Q_OBJECT + +public: + + enum Dest + { + DEST_ALBUM = 1, + DEST_PROFILE, + DEST_WALL + }; + + explicit UploadPhotosJob(const QString& accessToken, + const QStringList& files, bool saveBig, int aid, int gid = -1); + ~UploadPhotosJob(); + + void start() override; + + QList list() const; + +protected: + + static const int MAX_POST_JOBS; + static const int REQUEST_FILES_COUNT; + +protected: + + int getMaxRequestFilesCount() const; + + bool mayStartPostJob(); + + void startPostJob(int offset, int count); + void startSaveJob(const QVariantMap& photoIdData); + +Q_SIGNALS: + + void progress(int); + +private Q_SLOTS: + + void serverJobFinished(KJob*); + void postJobFinished(KJob*); + void saveJobFinished(KJob*); + +private: + + class Private; + Private* const d; +}; + +} // namespace Vkontakte + +#endif // DIGIKAM_VKONTAKTE_UPLOADPHOTOSJOB_H diff --git a/core/dplugins/generic/webservices/vkontakte/backend/vkontakte_userinfo.cpp b/core/dplugins/generic/webservices/vkontakte/backend/vkontakte_userinfo.cpp new file mode 100644 index 0000000000..67c206e2ab --- /dev/null +++ b/core/dplugins/generic/webservices/vkontakte/backend/vkontakte_userinfo.cpp @@ -0,0 +1,165 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * https://www.digikam.org + * + * Date : 2011-02-19 + * Description : a tool to export images to VKontakte web service + * + * Copyright (C) 2011-2015 by Alexander Potashev + * Copyright (C) 2011-2020 by Gilles Caulier + * + * 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, 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. + * + * ============================================================ */ + +#include "vkontakte_userinfo.h" + +// Qt includes + +#include + +// Local includes + +#include "vkontakte_util.h" + +namespace Vkontakte +{ + +class Q_DECL_HIDDEN UserInfo::Private : public QSharedData +{ +public: + + QJsonObject jsonData; +}; + +UserInfo::UserInfo() + : d(new Private) +{ +} + +UserInfo::UserInfo(const QJsonObject& jsonData) + : d(new Private) +{ + d->jsonData = jsonData; +} + +UserInfo::UserInfo(const UserInfo& other) + : d(other.d) +{ +} + +UserInfo::~UserInfo() +{ +} + +UserInfo &UserInfo::operator=(const UserInfo& other) +{ + if (this != &other) + { + d = other.d; + } + + return *this; +} + +int UserInfo::userId() const +{ + return d->jsonData.value(QLatin1String("uid")).toInt(-1); +} + +QString UserInfo::firstName() const +{ + return d->jsonData.value(QLatin1String("first_name")).toString(); +} + +QString UserInfo::lastName() const +{ + return d->jsonData.value(QLatin1String("last_name")).toString(); +} + +QString UserInfo::nickName() const +{ + return d->jsonData.value(QLatin1String("nick_name")).toString(); +} + +QString UserInfo::domain() const +{ + return d->jsonData.value(QLatin1String("domain")).toString(); +} + +int UserInfo::sex() const +{ + return d->jsonData.value(QLatin1String("sex")).toInt(-1); +} + +bool UserInfo::online() const +{ + return d->jsonData.value(QLatin1String("online")).toBool(false); +} + +// static +QStringList UserInfo::allQueryFields() +{ + QStringList fields; + + fields << QLatin1String("sex") + << QLatin1String("bdate") + << QLatin1String("city") + << QLatin1String("country") + << QLatin1String("photo_50") + << QLatin1String("photo_100") + << QLatin1String("photo_200_orig") + << QLatin1String("photo_200") + << QLatin1String("photo_400_orig") + << QLatin1String("photo_max") + << QLatin1String("photo_max_orig") + << QLatin1String("photo_id") + << QLatin1String("online") + << QLatin1String("online_mobile") + << QLatin1String("domain") + << QLatin1String("has_mobile") + << QLatin1String("contacts") + << QLatin1String("connections") + << QLatin1String("site") + << QLatin1String("education") + << QLatin1String("universities") + << QLatin1String("schools") + << QLatin1String("can_post") + << QLatin1String("can_see_all_posts") + << QLatin1String("can_see_audio") + << QLatin1String("can_write_private_message") + << QLatin1String("status") + << QLatin1String("last_seen") + << QLatin1String("common_count") + << QLatin1String("relation") + << QLatin1String("relatives") + << QLatin1String("counters") + << QLatin1String("screen_name") + << QLatin1String("maiden_name") + << QLatin1String("timezone") + << QLatin1String("occupation") + << QLatin1String("activities") + << QLatin1String("interests") + << QLatin1String("music") + << QLatin1String("movies") + << QLatin1String("tv") + << QLatin1String("books") + << QLatin1String("games") + << QLatin1String("about") + << QLatin1String("quotes") + << QLatin1String("personal") + << QLatin1String("friends_status"); + + return fields; +} + +} // namespace Vkontakte diff --git a/core/dplugins/generic/webservices/vkontakte/backend/vkontakte_userinfo.h b/core/dplugins/generic/webservices/vkontakte/backend/vkontakte_userinfo.h new file mode 100644 index 0000000000..53f06011e3 --- /dev/null +++ b/core/dplugins/generic/webservices/vkontakte/backend/vkontakte_userinfo.h @@ -0,0 +1,110 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * https://www.digikam.org + * + * Date : 2011-02-19 + * Description : a tool to export images to VKontakte web service + * + * Copyright (C) 2011-2015 by Alexander Potashev + * Copyright (C) 2011-2020 by Gilles Caulier + * + * 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, 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. + * + * ============================================================ */ + +#ifndef DIGIKAM_VKONTAKTE_USERINFO_H +#define DIGIKAM_VKONTAKTE_USERINFO_H + +// Qt includes + +#include +#include +#include + +namespace Vkontakte +{ + +// http://vk.com/dev/fields +class UserInfo +{ +public: + + enum + { + INVALID_TIMEZONE = 42 + }; + +public: + + UserInfo(); + UserInfo(const UserInfo& other); + explicit UserInfo(const QJsonObject& jsonData); + ~UserInfo(); + + UserInfo &operator=(const UserInfo& other); + + /** + * @brief Returns integer user ID at VK. + * + * UserInfoJob (the "users.get" method) always returns this, regardless of + * the requested additional fields. + * + * Returns -1 for uninitialized object or on server error. + */ + int userId() const; + + /** + * @brief Returns user's first name in the default language. + * + * UserInfoJob (the "users.get" method) always returns this, regardless of + * the requested additional fields. + * + * Returns empty string for uninitialized object or on server error. + */ + QString firstName() const; + + /** + * @brief Returns user's last name in the default language. + * + * UserInfoJob (the "users.get" method) always returns this, regardless of + * the requested additional fields. + * + * Returns empty string for uninitialized object or on server error. + */ + QString lastName() const; + + QString nickName() const; + QString domain() const; + + int sex() const; + + bool online() const; + + /** + * @brief Returns the stringlist of all possible field groups + * that may be requested in VK API method "users.get". + * + * @return The list of strings to pass as "fields" argument to a method. + */ + static QStringList allQueryFields(); + +private: + + class Private; + QSharedDataPointer d; +}; + +} // namespace Vkontakte + +Q_DECLARE_METATYPE(Vkontakte::UserInfo) + +#endif // DIGIKAM_VKONTAKTE_USERINFO_H diff --git a/core/dplugins/generic/webservices/vkontakte/backend/vkontakte_userinfojob.cpp b/core/dplugins/generic/webservices/vkontakte/backend/vkontakte_userinfojob.cpp new file mode 100644 index 0000000000..05d0131622 --- /dev/null +++ b/core/dplugins/generic/webservices/vkontakte/backend/vkontakte_userinfojob.cpp @@ -0,0 +1,119 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * https://www.digikam.org + * + * Date : 2011-02-19 + * Description : a tool to export images to VKontakte web service + * + * Copyright (C) 2011-2015 by Alexander Potashev + * Copyright (C) 2011-2020 by Gilles Caulier + * + * 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, 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. + * + * ============================================================ */ + +#include "vkontakte_userinfojob.h" + +// Qt includes + +#include + +// Local includes + +#include "vkontakte_util.h" + +namespace Vkontakte +{ + +class Q_DECL_HIDDEN UserInfoJob::Private +{ +public: + + QList userInfo; + QStringList fields; +}; + +// http://vk.com/dev/users.get +UserInfoJob::UserInfoJob(const QString& accessToken) + : VkontakteJob(accessToken, QLatin1String("users.get")), + d(new Private) +{ + // The complete list of fields + setFields(UserInfo::allQueryFields()); // TODO: do not pull extra fields by default + + // TODO: support "counters" request (probably in another KJob) +} + +UserInfoJob::UserInfoJob(const QString& accessToken, int uid) + : VkontakteJob(accessToken, QLatin1String("users.get")), + d(new Private) +{ + setFields(UserInfo::allQueryFields()); // TODO: do not pull extra fields by default + addQueryItem(QLatin1String("user_ids"), QString::number(uid)); +} + +UserInfoJob::UserInfoJob(const QString& accessToken, const QList& uids) + : VkontakteJob(accessToken, QLatin1String("users.get")), + d(new Private) +{ + setFields(UserInfo::allQueryFields()); // TODO: do not pull extra fields by default + addQueryItem(QLatin1String("user_ids"), joinIntegers(uids)); + + // TODO: make this working for more than 1000 uids + // ("users.get" allows requesting only 1000 users at once) +} + +UserInfoJob::~UserInfoJob() +{ + delete d; +} + +QList UserInfoJob::userInfo() const +{ + return d->userInfo; +} + +void UserInfoJob::setFields(const QStringList& fields) +{ + d->fields = fields; +} + +void UserInfoJob::prepareQueryItems() +{ + if (!d->fields.isEmpty()) + { + addQueryItem(QLatin1String("fields"), d->fields.join(QLatin1String(","))); + } +} + +void UserInfoJob::handleData(const QJsonValue& data) +{ + if (!data.isArray()) + { + // TODO: report error!!! + return; + } + + foreach (const QJsonValue& item, data.toArray()) + { + if (!item.isObject()) + { + // TODO: report error!!! + d->userInfo.clear(); + return; + } + + d->userInfo.append(UserInfo(item.toObject())); + } +} + +} // namespace Vkontakte diff --git a/core/dplugins/generic/webservices/vkontakte/backend/vkontakte_userinfojob.h b/core/dplugins/generic/webservices/vkontakte/backend/vkontakte_userinfojob.h new file mode 100644 index 0000000000..b67c25e826 --- /dev/null +++ b/core/dplugins/generic/webservices/vkontakte/backend/vkontakte_userinfojob.h @@ -0,0 +1,97 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * https://www.digikam.org + * + * Date : 2011-02-19 + * Description : a tool to export images to VKontakte web service + * + * Copyright (C) 2011-2015 by Alexander Potashev + * Copyright (C) 2011-2020 by Gilles Caulier + * + * 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, 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. + * + * ============================================================ */ + +#ifndef DIGIKAM_VKONTAKTE_USERINFOJOB_H +#define DIGIKAM_VKONTAKTE_USERINFOJOB_H + +// Qt includes + +#include +#include +#include + +// Local includes + +#include "vkontakte_jobs.h" +#include "vkontakte_userinfo.h" + +namespace Vkontakte +{ + +/** + * Get information about multiple users + * + * Uses VKontakte API method "users.get". + */ +// TODO: support working with more than 1000 users +class UserInfoJob : public VkontakteJob +{ + Q_OBJECT + +public: + + /** + * @brief Construct a job to retrieve the data of the currently authenticaed user. + * @param accessToken The access token to access data on vkontakte. + */ + explicit UserInfoJob(const QString& accessToken); + + /** + * @brief Construct a job to retrieve the data of user with the given uid. + * @param accessToken The access token to access data on vkontakte. + * @param uid The user ID + */ + UserInfoJob(const QString& accessToken, int uid); + + UserInfoJob(const QString& accessToken, const QList& uids); + ~UserInfoJob(); + + /** + * @return A pointer to a userInfo object about the currently authenticated user. + */ + QList userInfo() const; + + void setFields(const QStringList& fields); + +protected: + + /** + * @brief Handles the data returned by the VkontakteGetJob + * @param data A JSON string containing the data. + */ + void handleData(const QJsonValue& data) override; + + /** + * @brief Prepares m_queryItems by several addQueryItem calls + */ + void prepareQueryItems() override; + +private: + + class Private; + Private* const d; +}; + +} // namespace Vkontakte + +#endif // DIGIKAM_VKONTAKTE_USERINFOJOB_H diff --git a/core/dplugins/generic/webservices/vkontakte/backend/vkontakte_util.cpp b/core/dplugins/generic/webservices/vkontakte/backend/vkontakte_util.cpp new file mode 100644 index 0000000000..bb93f4ccf3 --- /dev/null +++ b/core/dplugins/generic/webservices/vkontakte/backend/vkontakte_util.cpp @@ -0,0 +1,109 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * https://www.digikam.org + * + * Date : 2011-02-19 + * Description : a tool to export images to VKontakte web service + * + * Copyright (C) 2010 by Thomas McGuire + * Copyright (C) 2011-2015 by Alexander Potashev + * Copyright (C) 2011-2020 by Gilles Caulier + * + * 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, 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. + * + * ============================================================ */ + +#include "vkontakte_util.h" + +// Qt includes + +#include +#include + +namespace Vkontakte +{ + +QDateTime unixTimeToQDateTime(const QString& unixTime) +{ + QDateTime res; + res.setTime_t(unixTime.toLongLong()); + + return res; +} + +static QMap initAppPermissionStrings() +{ + QMap map; + + // http://vk.com/dev/permissions + map.insert(Vkontakte::AppPermissions::Notify, "notify"); + map.insert(Vkontakte::AppPermissions::Friends, "friends"); + map.insert(Vkontakte::AppPermissions::Photos, "photos"); + map.insert(Vkontakte::AppPermissions::Audio, "audio"); + map.insert(Vkontakte::AppPermissions::Video, "video"); + map.insert(Vkontakte::AppPermissions::Offers, "offers"); + map.insert(Vkontakte::AppPermissions::Questions, "questions"); + map.insert(Vkontakte::AppPermissions::Pages, "pages"); + // "applink" permission cannot be requested, I invented + // the name "applink" myself to somehow identify this permission flag only. + map.insert(Vkontakte::AppPermissions::AppLink, "applink"); + map.insert(Vkontakte::AppPermissions::Status, "status"); + map.insert(Vkontakte::AppPermissions::Notes, "notes"); + map.insert(Vkontakte::AppPermissions::Messages, "messages"); + map.insert(Vkontakte::AppPermissions::Wall, "wall"); + map.insert(Vkontakte::AppPermissions::Ads, "ads"); + map.insert(Vkontakte::AppPermissions::Offline, "offline"); + map.insert(Vkontakte::AppPermissions::Docs, "docs"); + map.insert(Vkontakte::AppPermissions::Groups, "groups"); + map.insert(Vkontakte::AppPermissions::Notifications, "notifications"); + map.insert(Vkontakte::AppPermissions::Stats, "stats"); + map.insert(Vkontakte::AppPermissions::Email, "email"); + + return map; +} + +// This could be a QList >, but with QMap we can write this shorter +static QMap appPermissionToString = initAppPermissionStrings(); + +QStringList appPermissionsToStringList(Vkontakte::AppPermissions::Value permissions) +{ + QStringList res; + + foreach (Vkontakte::AppPermissions::Value key, appPermissionToString.keys()) + { + if (permissions & key) + { + res << QString::fromUtf8(appPermissionToString.value(key)); + } + } + + return res; +} + +QString joinIntegers(const QList& list) +{ + QString res; + + foreach (int x, list) + { + if (!res.isEmpty()) + { + res += QLatin1Char(','); + } + + res += QString::number(x); + } + + return res; +} + +} // namespace Vkontakte diff --git a/core/dplugins/generic/webservices/vkontakte/backend/vkontakte_util.h b/core/dplugins/generic/webservices/vkontakte/backend/vkontakte_util.h new file mode 100644 index 0000000000..7039db3061 --- /dev/null +++ b/core/dplugins/generic/webservices/vkontakte/backend/vkontakte_util.h @@ -0,0 +1,51 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * https://www.digikam.org + * + * Date : 2011-02-19 + * Description : a tool to export images to VKontakte web service + * + * Copyright (C) 2010 by Thomas McGuire + * Copyright (C) 2011-2015 by Alexander Potashev + * Copyright (C) 2011-2020 by Gilles Caulier + * + * 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, 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. + * + * ============================================================ */ + +#ifndef DIGIKAM_VKONTAKTE_UTIL_H +#define DIGIKAM_VKONTAKTE_UTIL_H + +// Qt includes + +#include +#include + +// Local includes + +#include "vkontakte_apppermissions.h" + +namespace Vkontakte +{ + +/** + * Convert a QString containing a UNIX timestamp to a QDateTime object + */ +QDateTime unixTimeToQDateTime(const QString& unixTime); + +QStringList appPermissionsToStringList(Vkontakte::AppPermissions::Value permissions); + +QString joinIntegers(const QList& list); + +} // namespace Vkontakte + +#endif // DIGIKAM_VKONTAKTE_UTIL_H diff --git a/core/dplugins/generic/webservices/vkontakte/backend/vkontakte_vkapi.cpp b/core/dplugins/generic/webservices/vkontakte/backend/vkontakte_vkapi.cpp new file mode 100644 index 0000000000..cdb7270e07 --- /dev/null +++ b/core/dplugins/generic/webservices/vkontakte/backend/vkontakte_vkapi.cpp @@ -0,0 +1,167 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * https://www.digikam.org + * + * Date : 2011-02-19 + * Description : a tool to export images to VKontakte web service + * + * Copyright (C) 2011-2015 by Alexander Potashev + * Copyright (C) 2011-2020 by Gilles Caulier + * + * 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, 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. + * + * ============================================================ */ + +#include "vkontakte_vkapi.h" + +// Qt includes + +#include + +// Local includes + +#include "vkontakte_authenticationdialog.h" +#include "vkontakte_getapplicationpermissionsjob.h" + +namespace Vkontakte +{ + +class Q_DECL_HIDDEN VkApi::Private +{ +public: + + QWidget* parent; + QString appId; + Vkontakte::AppPermissions::Value requiredPermissions; + QString accessToken; + bool authenticated; +}; + +VkApi::VkApi(QWidget* const parent) + : d(new Private) +{ + d->parent = parent; + d->appId = QString(); + d->requiredPermissions = Vkontakte::AppPermissions::NoPermissions; + d->accessToken = QString(); + d->authenticated = false; +} + +VkApi::~VkApi() +{ +} + +void VkApi::setAppId(const QString& appId) +{ + d->appId = appId; +} + +void VkApi::setRequiredPermissions(Vkontakte::AppPermissions::Value permissions) +{ + d->requiredPermissions = permissions; +} + +void VkApi::setInitialAccessToken(const QString& accessToken) +{ + // Does nothing if m_accessToken is already set, because this function + // is only for parameter initialization from a configuration file. + + if (d->accessToken.isEmpty()) + { + d->accessToken = accessToken; + } +} + +QString VkApi::accessToken() const +{ + return d->accessToken; +} + +void VkApi::startAuthentication(bool forceLogout) +{ + if (forceLogout) + { + d->accessToken.clear(); + } + + if (!d->accessToken.isEmpty()) + { + Vkontakte::GetApplicationPermissionsJob* const job = new Vkontakte::GetApplicationPermissionsJob(d->accessToken); + + connect(job, SIGNAL(result(KJob*)), + this, SLOT(slotApplicationPermissionCheckDone(KJob*))); + + job->start(); + } + else + { + QPointer authDialog = new Vkontakte::AuthenticationDialog(d->parent); + authDialog->setAppId(d->appId); + authDialog->setPermissions(d->requiredPermissions); + + connect(authDialog, SIGNAL(authenticated(QString)), + this, SLOT(slotAuthenticationDialogDone(QString))); + + connect(authDialog, SIGNAL(canceled()), + this, SIGNAL(canceled())); + + authDialog->start(); + } +} + +void VkApi::slotApplicationPermissionCheckDone(KJob* kjob) +{ + Vkontakte::GetApplicationPermissionsJob* const job = dynamic_cast(kjob); + Q_ASSERT(job); + + bool havePermissions = true; + + if (!job || job->error()) + { + // There was some error enumerating permissions, need to start over for sure + havePermissions = false; + } + else + { + Vkontakte::AppPermissions::Value availablePermissions = static_cast(job->permissions()); + + if ((availablePermissions & d->requiredPermissions) != d->requiredPermissions) + { + // Existing permissions are not enough, need to request more permissions + havePermissions = false; + } + } + + if (havePermissions) + { + d->authenticated = true; + emit authenticated(); + } + else + { + startAuthentication(true); + } +} + +void VkApi::slotAuthenticationDialogDone(const QString& accessToken) +{ + d->accessToken = accessToken; + d->authenticated = true; + emit authenticated(); +} + +bool VkApi::isAuthenticated() +{ + return d->authenticated; +} + +} // namespace Vkontakte diff --git a/core/dplugins/generic/webservices/vkontakte/backend/vkontakte_vkapi.h b/core/dplugins/generic/webservices/vkontakte/backend/vkontakte_vkapi.h new file mode 100644 index 0000000000..7bf2a4b2f4 --- /dev/null +++ b/core/dplugins/generic/webservices/vkontakte/backend/vkontakte_vkapi.h @@ -0,0 +1,103 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * https://www.digikam.org + * + * Date : 2011-02-19 + * Description : a tool to export images to VKontakte web service + * + * Copyright (C) 2011-2015 by Alexander Potashev + * Copyright (C) 2011-2020 by Gilles Caulier + * + * 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, 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. + * + * ============================================================ */ + +#ifndef DIGIKAM_VKONTAKTE_VKAPI_H +#define DIGIKAM_VKONTAKTE_VKAPI_H + +// Qt includes + +#include + +// Local includes + +#include "vkontakte_apppermissions.h" + +class QWidget; +class KJob; + +namespace Vkontakte +{ + +/** + * @brief Authentication manager for libkvkontakte + * + * This is a handy interface on top of Vkontakte::AuthenticationDialog + * to request authentication on demand and store the API token for later use. + */ +class VkApi : public QObject +{ + Q_OBJECT + +public: + + explicit VkApi(QWidget* const parent); + ~VkApi(); + + /** + * @brief Initialize app ID. + * + * @param appId VK application ID for authentication requests. + */ + void setAppId(const QString& appId); + + /** + * @brief Set app permissions to request during authentication. + * + * @param permissions Any combination of AppPermissions::*, use bitwise OR to combine them + */ + void setRequiredPermissions(Vkontakte::AppPermissions::Value permissions); + + /** + * @brief Set initial access token. + * + * If an initial access token was set with this method, then before the + * normal authentication process VkApi::startAuthentication() will first try + * to reuse the given token in order to cut down the number of times the + * authentication dialog is opened. + */ + void setInitialAccessToken(const QString& accessToken); + + QString accessToken() const; + + void startAuthentication(bool forceLogout); + bool isAuthenticated(); + +Q_SIGNALS: + + void authenticated(); + void canceled(); + +protected Q_SLOTS: + + void slotApplicationPermissionCheckDone(KJob* kjob); + void slotAuthenticationDialogDone(const QString& accessToken); + +private: + + class Private; + Private* const d; +}; + +} // namespace Vkontakte + +#endif // DIGIKAM_VKONTAKTE_VKAPI_H diff --git a/core/dplugins/generic/webservices/vkontakte/vkalbumchooser.cpp b/core/dplugins/generic/webservices/vkontakte/vkalbumchooser.cpp index 5cf7d733cb..ed1865526d 100644 --- a/core/dplugins/generic/webservices/vkontakte/vkalbumchooser.cpp +++ b/core/dplugins/generic/webservices/vkontakte/vkalbumchooser.cpp @@ -1,447 +1,447 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2011-02-19 * Description : a tool to export images to VKontakte web service * * Copyright (C) 2011-2015 by Alexander Potashev * Copyright (C) 2011-2020 by Gilles Caulier * * 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, 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. * * ============================================================ */ #include "vkalbumchooser.h" // Qt includes #include #include #include #include #include #include // KDE includes #include // LibKvkontakte includes -#include -#include -#include -#include -#include +#include "vkontakte_albumlistjob.h" +#include "vkontakte_createalbumjob.h" +#include "vkontakte_editalbumjob.h" +#include "vkontakte_deletealbumjob.h" +#include "vkontakte_vkapi.h" // Local includes #include "vknewalbumdlg.h" namespace DigikamGenericVKontaktePlugin { class Q_DECL_HIDDEN VKAlbumChooser::Private { public: explicit Private() { albumsCombo = nullptr; newAlbumButton = nullptr; reloadAlbumsButton = nullptr; editAlbumButton = nullptr; deleteAlbumButton = nullptr; albumToSelect = -1; vkapi = nullptr; } QComboBox* albumsCombo; QPushButton* newAlbumButton; QPushButton* reloadAlbumsButton; QToolButton* editAlbumButton; QToolButton* deleteAlbumButton; QList albums; /** Album with this "aid" will * be selected in slotAlbumsReloadDone() */ int albumToSelect; Vkontakte::VkApi* vkapi; }; VKAlbumChooser::VKAlbumChooser(QWidget* const parent, Vkontakte::VkApi* const vkapi) : QGroupBox(i18nc("@title:group Header above controls for managing albums", "Album"), parent), d(new Private) { d->vkapi = vkapi; setWhatsThis(i18n("This is the VKontakte album that will be used for the transfer.")); QVBoxLayout* const albumsBoxLayout = new QVBoxLayout(this); d->albumsCombo = new QComboBox(this); d->albumsCombo->setEditable(false); d->newAlbumButton = new QPushButton(QIcon::fromTheme(QLatin1String("list-add")), i18n("New Album"), this); d->newAlbumButton->setToolTip(i18n("Create new VKontakte album")); d->reloadAlbumsButton = new QPushButton(QIcon::fromTheme(QLatin1String("view-refresh")), i18nc("reload albums list", "Reload"), this); d->reloadAlbumsButton->setToolTip(i18n("Reload albums list")); d->editAlbumButton = new QToolButton(this); d->editAlbumButton->setToolTip(i18n("Edit selected album")); d->editAlbumButton->setEnabled(false); d->editAlbumButton->setIcon(QIcon::fromTheme(QLatin1String("document-edit"))); d->deleteAlbumButton = new QToolButton(this); d->deleteAlbumButton->setToolTip(i18n("Delete selected album")); d->deleteAlbumButton->setEnabled(false); d->deleteAlbumButton->setIcon(QIcon::fromTheme(QLatin1String("edit-delete"))); QWidget* const currentAlbumWidget = new QWidget(this); QHBoxLayout* const currentAlbumWidgetLayout = new QHBoxLayout(currentAlbumWidget); currentAlbumWidgetLayout->setContentsMargins(0, 0, 0, 0); currentAlbumWidgetLayout->addWidget(d->albumsCombo); currentAlbumWidgetLayout->addWidget(d->editAlbumButton); currentAlbumWidgetLayout->addWidget(d->deleteAlbumButton); QWidget* const albumButtons = new QWidget(this); QHBoxLayout* const albumButtonsLayout = new QHBoxLayout(albumButtons); albumButtonsLayout->setContentsMargins(0, 0, 0, 0); albumButtonsLayout->addSpacerItem(new QSpacerItem(0, 0, QSizePolicy::Expanding, QSizePolicy::Minimum)); albumButtonsLayout->addWidget(d->newAlbumButton); albumButtonsLayout->addWidget(d->reloadAlbumsButton); albumsBoxLayout->addWidget(currentAlbumWidget); albumsBoxLayout->addWidget(albumButtons); connect(d->newAlbumButton, SIGNAL(clicked()), this, SLOT(slotNewAlbumRequest())); connect(d->editAlbumButton, SIGNAL(clicked()), this, SLOT(slotEditAlbumRequest())); connect(d->deleteAlbumButton, SIGNAL(clicked()), this, SLOT(slotDeleteAlbumRequest())); connect(d->reloadAlbumsButton, SIGNAL(clicked()), this, SLOT(slotReloadAlbumsRequest())); connect(d->vkapi, SIGNAL(authenticated()), this, SLOT(slotReloadAlbumsRequest())); } VKAlbumChooser::~VKAlbumChooser() { delete d; } /** * @brief Clear the list of albums **/ void VKAlbumChooser::clearList() { d->albumsCombo->clear(); } bool VKAlbumChooser::getCurrentAlbumInfo(VKNewAlbumDlg::AlbumProperties& out) { int index = d->albumsCombo->currentIndex(); if (index >= 0) { Vkontakte::AlbumInfo album = d->albums.at(index); out.title = album.title(); out.description = album.description(); out.privacy = album.privacy(); out.commentPrivacy = album.commentPrivacy(); return true; } else { return false; } } bool VKAlbumChooser::getCurrentAlbumId(int& out) { int index = d->albumsCombo->currentIndex(); if (index >= 0) { Vkontakte::AlbumInfo album = d->albums.at(index); out = album.aid(); return true; } else { return false; } } void VKAlbumChooser::selectAlbum(int aid) { /* * If the album list is not ready yet, select this album later */ d->albumToSelect = aid; for (int i = 0 ; i < d->albums.size() ; ++i) { if (d->albums.at(i).aid() == aid) { d->albumsCombo->setCurrentIndex(i); break; } } } //------------------------------ void VKAlbumChooser::slotNewAlbumRequest() { QPointer dlg = new VKNewAlbumDlg(this); if (dlg->exec() == QDialog::Accepted) { updateBusyStatus(true); slotStartAlbumCreation(dlg->album()); } delete dlg; } void VKAlbumChooser::slotStartAlbumCreation(const VKNewAlbumDlg::AlbumProperties &album) { Vkontakte::CreateAlbumJob* const job = new Vkontakte::CreateAlbumJob(d->vkapi->accessToken(), album.title, album.description, album.privacy, album.commentPrivacy); connect(job, SIGNAL(result(KJob*)), this, SLOT(slotAlbumCreationDone(KJob*))); job->start(); } void VKAlbumChooser::slotAlbumCreationDone(KJob* kjob) { Vkontakte::CreateAlbumJob* const job = dynamic_cast(kjob); Q_ASSERT(job); if (job == nullptr || job->error()) { handleVkError(job); updateBusyStatus(false); } else { // Select the newly created album in the combobox later (in "slotAlbumsReloadDone()") d->albumToSelect = job->album().aid(); slotStartAlbumsReload(); updateBusyStatus(true); } } //------------------------------ void VKAlbumChooser::slotEditAlbumRequest() { VKNewAlbumDlg::AlbumProperties album; int aid = 0; if (!getCurrentAlbumInfo(album) || !getCurrentAlbumId(aid)) { return; } QPointer dlg = new VKNewAlbumDlg(this, album); if (dlg->exec() == QDialog::Accepted) { updateBusyStatus(true); slotStartAlbumEditing(aid, dlg->album()); } delete dlg; } void VKAlbumChooser::slotStartAlbumEditing(int aid, const VKNewAlbumDlg::AlbumProperties& album) { // Select the same album again in the combobox later (in "slotAlbumsReloadDone()") d->albumToSelect = aid; Vkontakte::EditAlbumJob* const job = new Vkontakte::EditAlbumJob(d->vkapi->accessToken(), aid, album.title, album.description, album.privacy, album.commentPrivacy); connect(job, SIGNAL(result(KJob*)), this, SLOT(slotAlbumEditingDone(KJob*))); job->start(); } void VKAlbumChooser::slotAlbumEditingDone(KJob* kjob) { Vkontakte::EditAlbumJob* const job = dynamic_cast(kjob); Q_ASSERT(job); if (job && job->error()) { handleVkError(job); return; } slotStartAlbumsReload(); updateBusyStatus(true); } //------------------------------ void VKAlbumChooser::slotDeleteAlbumRequest() { VKNewAlbumDlg::AlbumProperties album; int aid = 0; if (!getCurrentAlbumInfo(album) || !getCurrentAlbumId(aid)) { return; } if (QMessageBox::question(this, i18nc("@title:window", "Confirm Album Deletion"), i18n("Are you sure you want to remove the album %1 " "including all photos in it?", album.title)) != QMessageBox::Yes) { return; } updateBusyStatus(true); slotStartAlbumDeletion(aid); } void VKAlbumChooser::slotStartAlbumDeletion(int aid) { Vkontakte::DeleteAlbumJob* const job = new Vkontakte::DeleteAlbumJob(d->vkapi->accessToken(), aid); connect(job, SIGNAL(result(KJob*)), this, SLOT(slotAlbumDeletionDone(KJob*))); job->start(); } void VKAlbumChooser::slotAlbumDeletionDone(KJob* kjob) { Vkontakte::DeleteAlbumJob* const job = dynamic_cast(kjob); Q_ASSERT(job); if (job && job->error()) { handleVkError(job); return; } slotStartAlbumsReload(); updateBusyStatus(true); } //------------------------------ void VKAlbumChooser::slotReloadAlbumsRequest() { updateBusyStatus(true); int aid = 0; if (getCurrentAlbumId(aid)) { d->albumToSelect = aid; } slotStartAlbumsReload(); } void VKAlbumChooser::slotStartAlbumsReload() { updateBusyStatus(true); Vkontakte::AlbumListJob* const job = new Vkontakte::AlbumListJob(d->vkapi->accessToken()); connect(job, SIGNAL(result(KJob*)), this, SLOT(slotAlbumsReloadDone(KJob*))); job->start(); } void VKAlbumChooser::slotAlbumsReloadDone(KJob* kjob) { Vkontakte::AlbumListJob* const job = dynamic_cast(kjob); Q_ASSERT(job); if (job && job->error()) { handleVkError(job); return; } if (!job) return; d->albumsCombo->clear(); d->albums = job->list(); foreach (const Vkontakte::AlbumInfo &album, d->albums) d->albumsCombo->addItem(QIcon::fromTheme(QLatin1String("folder-image")), album.title()); if (d->albumToSelect != -1) { selectAlbum(d->albumToSelect); d->albumToSelect = -1; } d->albumsCombo->setEnabled(true); if (!d->albums.isEmpty()) { d->editAlbumButton->setEnabled(true); d->deleteAlbumButton->setEnabled(true); } updateBusyStatus(false); } //------------------------------ void VKAlbumChooser::updateBusyStatus(bool busy) { setEnabled(!busy); } // TODO: share this code with `vkwindow.cpp` void VKAlbumChooser::handleVkError(KJob* kjob) { QMessageBox::critical(this, i18nc("@title:window", "Request to VKontakte failed"), kjob == nullptr ? i18n("Internal error: Null pointer to KJob instance.") : kjob->errorText()); } } // namespace DigikamGenericVKontaktePlugin diff --git a/core/dplugins/generic/webservices/vkontakte/vkalbumchooser.h b/core/dplugins/generic/webservices/vkontakte/vkalbumchooser.h index 2bcf9b23b1..6cf8e2288d 100644 --- a/core/dplugins/generic/webservices/vkontakte/vkalbumchooser.h +++ b/core/dplugins/generic/webservices/vkontakte/vkalbumchooser.h @@ -1,97 +1,97 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2011-02-19 * Description : a tool to export images to VKontakte web service * * Copyright (C) 2011-2015 by Alexander Potashev * Copyright (C) 2011-2020 by Gilles Caulier * * 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, 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. * * ============================================================ */ #ifndef DIGIKAM_VK_ALBUM_CHOOSER_H #define DIGIKAM_VK_ALBUM_CHOOSER_H // Qt includes #include #include // LibKvkontakte includes -#include +#include "vkontakte_albuminfo.h" // Local includes #include "vknewalbumdlg.h" class KJob; namespace Vkontakte { class VkApi; } namespace DigikamGenericVKontaktePlugin { class VKAlbumChooser : public QGroupBox { Q_OBJECT public: explicit VKAlbumChooser(QWidget* const parent, Vkontakte::VkApi* const vkapi); ~VKAlbumChooser(); public: void clearList(); bool getCurrentAlbumInfo(VKNewAlbumDlg::AlbumProperties& out); bool getCurrentAlbumId(int &out); void selectAlbum(int aid); private Q_SLOTS: void slotNewAlbumRequest(); void slotEditAlbumRequest(); void slotDeleteAlbumRequest(); void slotReloadAlbumsRequest(); void slotStartAlbumCreation(const VKNewAlbumDlg::AlbumProperties& album); void slotStartAlbumEditing(int aid, const VKNewAlbumDlg::AlbumProperties& album); void slotStartAlbumDeletion(int aid); void slotStartAlbumsReload(); void slotAlbumCreationDone(KJob* kjob); void slotAlbumEditingDone(KJob* kjob); void slotAlbumDeletionDone(KJob* kjob); void slotAlbumsReloadDone(KJob* kjob); private: void updateBusyStatus(bool busy); void handleVkError(KJob* kjob); private: class Private; Private* const d; }; } // namespace DigikamGenericVKontaktePlugin #endif // DIGIKAM_VK_ALBUM_CHOOSER_H diff --git a/core/dplugins/generic/webservices/vkontakte/vkauthwidget.cpp b/core/dplugins/generic/webservices/vkontakte/vkauthwidget.cpp index 8636e010b7..061d0d18ed 100644 --- a/core/dplugins/generic/webservices/vkontakte/vkauthwidget.cpp +++ b/core/dplugins/generic/webservices/vkontakte/vkauthwidget.cpp @@ -1,195 +1,195 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2011-02-19 * Description : a tool to export images to VKontakte web service * * Copyright (C) 2011-2015 by Alexander Potashev * Copyright (C) 2011-2020 by Gilles Caulier * * 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, 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. * * ============================================================ */ #include "vkauthwidget.h" // Qt includes #include #include #include #include // KDE includes #include // LibKvkontakte includes -#include -#include +#include "vkontakte_userinfojob.h" +#include "vkontakte_vkapi.h" namespace DigikamGenericVKontaktePlugin { class Q_DECL_HIDDEN VKAuthWidget::Private { public: explicit Private() { vkapi = nullptr; userId = -1; loginLabel = nullptr; changeUserButton = nullptr; } // VK.com interface Vkontakte::VkApi* vkapi; // Data int userId; QString userFullName; // GUI QLabel* loginLabel; QPushButton* changeUserButton; }; VKAuthWidget::VKAuthWidget(QWidget* const parent, Vkontakte::VkApi* const vkapi) : QGroupBox(i18n("Account"), parent), d(new Private) { d->vkapi = vkapi; setWhatsThis(i18n("This account is used for authentication.")); QGridLayout* const accountBoxLayout = new QGridLayout(this); QLabel* const loginDescLabel = new QLabel(this); loginDescLabel->setText(i18n("Name:")); loginDescLabel->setWhatsThis(i18n("Your VKontakte login")); d->loginLabel = new QLabel(this); d->changeUserButton = new QPushButton(QIcon::fromTheme(QLatin1String("system-switch-user")), i18n("Change Account"), this); d->changeUserButton->setToolTip(i18n("Change VKontakte account used to authenticate")); d->changeUserButton->hide(); // changing account does not work anyway accountBoxLayout->addWidget(loginDescLabel, 0, 0); accountBoxLayout->addWidget(d->loginLabel, 0, 1); accountBoxLayout->addWidget(d->changeUserButton, 1, 1); connect(d->changeUserButton, SIGNAL(clicked()), this, SLOT(slotChangeUserClicked())); connect(d->vkapi, SIGNAL(authenticated()), this, SLOT(slotStartGetUserInfo())); connect(this, SIGNAL(signalUpdateAuthInfo()), this, SLOT(slotUpdateAuthInfo())); } VKAuthWidget::~VKAuthWidget() { delete d; } //--------------------------------------------------------------------------- void VKAuthWidget::slotStartAuthentication(bool forceLogout) { d->userFullName.clear(); d->userId = -1; d->vkapi->startAuthentication(forceLogout); emit signalAuthCleared(); } void VKAuthWidget::slotChangeUserClicked() { // force authenticate window slotStartAuthentication(true); } //--------------------------------------------------------------------------- void VKAuthWidget::slotStartGetUserInfo() { // Retrieve user info with UserInfoJob Vkontakte::UserInfoJob* const job = new Vkontakte::UserInfoJob(d->vkapi->accessToken()); connect(job, SIGNAL(result(KJob*)), this, SLOT(slotGetUserInfoDone(KJob*))); job->start(); } void VKAuthWidget::slotGetUserInfoDone(KJob* kjob) { Vkontakte::UserInfoJob* const job = dynamic_cast(kjob); Q_ASSERT(job); if (job && job->error()) { handleVkError(job); return; } if (!job) return; QList res = job->userInfo(); Vkontakte::UserInfo user = res.first(); d->userId = user.userId(); d->userFullName = i18nc("Concatenation of first name (%1) and last name (%2)", "%1 %2", user.firstName(), user.lastName()); emit signalUpdateAuthInfo(); } //--------------------------------------------------------------------------- void VKAuthWidget::slotUpdateAuthInfo() { QString loginText; if (d->vkapi->isAuthenticated()) { loginText = d->userFullName; } else { loginText = i18n("Unauthorized"); } d->loginLabel->setText(QString::fromLatin1("%1").arg(loginText)); } // TODO: share this code with `vkwindow.cpp` void VKAuthWidget::handleVkError(KJob* kjob) { QMessageBox::critical(this, i18nc("@title:window", "Request to VKontakte failed"), kjob->errorText()); } //--------------------------------------------------------------------------- QString VKAuthWidget::albumsURL() const { if (d->vkapi->isAuthenticated() && d->userId != -1) return QString::fromLatin1("http://vk.com/albums%1").arg(d->userId); else return QLatin1String("http://vk.com/"); } } // namespace DigikamGenericVKontaktePlugin diff --git a/core/dplugins/generic/webservices/vkontakte/vknewalbumdlg.cpp b/core/dplugins/generic/webservices/vkontakte/vknewalbumdlg.cpp index 90b386e1c5..8b8b642209 100644 --- a/core/dplugins/generic/webservices/vkontakte/vknewalbumdlg.cpp +++ b/core/dplugins/generic/webservices/vkontakte/vknewalbumdlg.cpp @@ -1,205 +1,205 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2011-02-19 * Description : a tool to export images to VKontakte web service * * Copyright (C) 2011 by Roman Tsisyk * Copyright (C) 2011-2015 by Alexander Potashev * Copyright (C) 2011-2020 by Gilles Caulier * * 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, 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. * * ============================================================ */ #include "vknewalbumdlg.h" // Qt includes #include #include #include #include #include #include #include #include #include #include #include #include // KDE includes #include // libvkontakte includes -#include +#include "vkontakte_albuminfo.h" namespace DigikamGenericVKontaktePlugin { class Q_DECL_HIDDEN VKNewAlbumDlg::Private { public: explicit Private() { titleEdit = nullptr; summaryEdit = nullptr; albumPrivacyCombo = nullptr; commentsPrivacyCombo = nullptr; } QLineEdit* titleEdit; QTextEdit* summaryEdit; QComboBox* albumPrivacyCombo; QComboBox* commentsPrivacyCombo; AlbumProperties album; }; VKNewAlbumDlg::VKNewAlbumDlg(QWidget* const parent) : QDialog(parent), d(new Private) { initDialog(false); } VKNewAlbumDlg::VKNewAlbumDlg(QWidget* const parent, const AlbumProperties& album) : QDialog(parent), d(new Private) { d->album = album; initDialog(true); } VKNewAlbumDlg::~VKNewAlbumDlg() { delete d; } void VKNewAlbumDlg::initDialog(bool editing) { setWindowTitle(editing ? i18nc("@title:window", "Edit album") : i18nc("@title:window", "New album")); setMinimumSize(400, 300); QVBoxLayout* const mainLayout = new QVBoxLayout(this); setLayout(mainLayout); QDialogButtonBox* const buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, this); QPushButton* const okButton = buttonBox->button(QDialogButtonBox::Ok); okButton->setDefault(true); okButton->setShortcut(Qt::CTRL | Qt::Key_Return); connect(buttonBox, &QDialogButtonBox::accepted, this, &VKNewAlbumDlg::accept); connect(buttonBox, &QDialogButtonBox::rejected, this, &VKNewAlbumDlg::reject); QGroupBox* const albumBox = new QGroupBox(i18nc("@title:group Header above Title and Summary fields", "Album"), this); albumBox->setWhatsThis(i18n("These are basic settings for the new VKontakte album.")); d->titleEdit = new QLineEdit(d->album.title); d->titleEdit->setWhatsThis(i18n("Title of the album that will be created (required).")); d->summaryEdit = new QTextEdit(d->album.description); d->summaryEdit->setWhatsThis(i18n("Description of the album that will be created (optional).")); QFormLayout* const albumBoxLayout = new QFormLayout; albumBoxLayout->addRow(i18n("Title:"), d->titleEdit); albumBoxLayout->addRow(i18n("Summary:"), d->summaryEdit); albumBox->setLayout(albumBoxLayout); QGroupBox* const privacyBox = new QGroupBox(i18n("Privacy Settings"), this); QGridLayout* const privacyBoxLayout = new QGridLayout; d->albumPrivacyCombo = new QComboBox(privacyBox); d->albumPrivacyCombo->addItem(i18n("Only me"), QVariant(Vkontakte::AlbumInfo::PRIVACY_PRIVATE)); d->albumPrivacyCombo->addItem(i18n("My friends"), QVariant(Vkontakte::AlbumInfo::PRIVACY_FRIENDS)); d->albumPrivacyCombo->addItem(i18n("Friends of my friends"), QVariant(Vkontakte::AlbumInfo::PRIVACY_FRIENDS_OF_FRIENDS)); d->albumPrivacyCombo->addItem(i18n("Everyone"), QVariant(Vkontakte::AlbumInfo::PRIVACY_PUBLIC)); privacyBoxLayout->addWidget(new QLabel(i18n("Album available to:")), 0, 0); privacyBoxLayout->addWidget(d->albumPrivacyCombo, 0, 1); d->commentsPrivacyCombo = new QComboBox(privacyBox); d->commentsPrivacyCombo->addItem(i18n("Only me"), QVariant(Vkontakte::AlbumInfo::PRIVACY_PRIVATE)); d->commentsPrivacyCombo->addItem(i18n("My friends"), QVariant(Vkontakte::AlbumInfo::PRIVACY_FRIENDS)); d->commentsPrivacyCombo->addItem(i18n("Friends of my friends"), QVariant(Vkontakte::AlbumInfo::PRIVACY_FRIENDS_OF_FRIENDS)); d->commentsPrivacyCombo->addItem(i18n("Everyone"), QVariant(Vkontakte::AlbumInfo::PRIVACY_PUBLIC)); privacyBoxLayout->addWidget(new QLabel(i18n("Comments available to:")), 1, 0); privacyBoxLayout->addWidget(d->commentsPrivacyCombo, 1, 1); privacyBox->setLayout(privacyBoxLayout); mainLayout->addWidget(albumBox); mainLayout->addWidget(privacyBox); mainLayout->addWidget(buttonBox); if (editing) { d->titleEdit->setText(d->album.title); d->summaryEdit->setText(d->album.description); d->albumPrivacyCombo->setCurrentIndex(d->albumPrivacyCombo->findData(d->album.privacy)); d->commentsPrivacyCombo->setCurrentIndex(d->commentsPrivacyCombo->findData(d->album.commentPrivacy)); } d->titleEdit->setFocus(); } void VKNewAlbumDlg::accept() { if (d->titleEdit->text().isEmpty()) { QMessageBox::critical(this, i18n("Error"), i18n("Title cannot be empty.")); return; } d->album.title = d->titleEdit->text(); d->album.description = d->summaryEdit->toPlainText(); if (d->albumPrivacyCombo->currentIndex() != -1) { d->album.privacy = d->albumPrivacyCombo->itemData(d->albumPrivacyCombo->currentIndex()).toInt(); } else { // for safety, see info about VK API bug below d->album.privacy = Vkontakte::AlbumInfo::PRIVACY_PRIVATE; } if (d->commentsPrivacyCombo->currentIndex() != -1) { d->album.commentPrivacy = d->commentsPrivacyCombo->itemData(d->commentsPrivacyCombo->currentIndex()).toInt(); } else { // VK API has a bug: if "comment_privacy" is not set, it will be set to PRIVACY_PUBLIC d->album.commentPrivacy = Vkontakte::AlbumInfo::PRIVACY_PRIVATE; } QDialog::accept(); } const VKNewAlbumDlg::AlbumProperties& VKNewAlbumDlg::album() const { return d->album; } } // namespace DigikamGenericVKontaktePlugin diff --git a/core/dplugins/generic/webservices/vkontakte/vkwindow.cpp b/core/dplugins/generic/webservices/vkontakte/vkwindow.cpp index d886094882..72abadaefa 100644 --- a/core/dplugins/generic/webservices/vkontakte/vkwindow.cpp +++ b/core/dplugins/generic/webservices/vkontakte/vkwindow.cpp @@ -1,445 +1,445 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2010-11-15 * Description : a tool to export images to VKontakte web service * * Copyright (C) 2011-2015 by Alexander Potashev * Copyright (C) 2005-2008 by Vardhman Jain * Copyright (C) 2008-2020 by Gilles Caulier * Copyright (C) 2009 by Luka Renko * Copyright (C) 2010 by Roman Tsisyk * * 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, 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. * * ============================================================ */ #include "vkwindow.h" // Qt includes #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // KDE includes #include #include #include // libkvkontakte includes -#include -#include +#include "vkontakte_uploadphotosjob.h" +#include "vkontakte_vkapi.h" // Local includes #include "digikam_version.h" #include "dprogresswdg.h" #include "vkalbumchooser.h" #include "vkauthwidget.h" namespace DigikamGenericVKontaktePlugin { class Q_DECL_HIDDEN VKWindow::Private { public: explicit Private() { import = false; mainWidget = nullptr; settingsBox = nullptr; headerLabel = nullptr; accountBox = nullptr; albumsBox = nullptr; imgList = nullptr; uploadWidget = nullptr; iface = nullptr; progressBar = nullptr; vkapi = nullptr; albumToSelect = -1; } bool import; /// User interface QWidget* mainWidget; QWidget* settingsBox; QLabel* headerLabel; /// accounts VKAuthWidget* accountBox; // album selection VKAlbumChooser* albumsBox; DItemsList* imgList; QWidget* uploadWidget; DInfoInterface* iface; DProgressWdg* progressBar; /// Pointers to running jobs QList jobs; Vkontakte::VkApi* vkapi; int albumToSelect; QString appId; }; VKWindow::VKWindow(DInfoInterface* const iface, QWidget* const parent, bool import) : WSToolDialog(parent, QLatin1String("VKontakte Dialog")), d(new Private) { d->iface = iface; d->vkapi = new Vkontakte::VkApi(this); // read settings from file readSettings(); connect(this, SIGNAL(finished(int)), this, SLOT(slotFinished())); d->import = import; d->mainWidget = new QWidget(this); QHBoxLayout* const mainLayout = new QHBoxLayout(d->mainWidget); d->imgList = new DItemsList(this); d->imgList->setObjectName(QLatin1String("WebService ImagesList")); d->imgList->setControlButtonsPlacement(DItemsList::ControlButtonsBelow); d->imgList->setAllowRAW(false); // TODO: implement conversion d->imgList->setIface(d->iface); d->imgList->loadImagesFromCurrentSelection(); d->imgList->listView()->setWhatsThis(i18n("This is the list of images " "to upload to your VKontakte album.")); d->settingsBox = new QWidget(this); QVBoxLayout* const settingsBoxLayout = new QVBoxLayout(d->settingsBox); d->headerLabel = new QLabel(d->settingsBox); d->headerLabel->setWhatsThis(i18n("This is a clickable link to open the " "VKontakte service in a web browser.")); d->headerLabel->setOpenExternalLinks(true); d->headerLabel->setFocusPolicy(Qt::NoFocus); // Account box initAccountBox(); // Album box d->albumsBox = new VKAlbumChooser(d->settingsBox, d->vkapi); d->albumsBox->selectAlbum(d->albumToSelect); // ------------------------------------------------------------------------ QGroupBox* const uploadBox = new QGroupBox(i18n("Destination"), d->settingsBox); uploadBox->setWhatsThis(i18n("This is the location where VKontakte images " "will be downloaded.")); QVBoxLayout* const uploadBoxLayout = new QVBoxLayout(uploadBox); d->uploadWidget = d->iface->uploadWidget(uploadBox); uploadBoxLayout->addWidget(d->uploadWidget); // ------------------------------------------------------------------------ #if 0 QGroupBox* const optionsBox = new QGroupBox(i18n("Options"), d->settingsBox); optionsBox->setWhatsThis(i18n("These are options that will be applied to images before upload.")); #endif // store state in rc file // d->checkKeepOriginal = new QCheckBox(i18n("Save in high resolution"), d->settingsBox); // QVBoxLayout* const optionsBoxLayout = new QVBoxLayout(optionsBox); // optionsBoxLayout->addWidget(d->checkKeepOriginal); d->progressBar = new DProgressWdg(d->settingsBox); d->progressBar->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed); d->progressBar->hide(); // Layouts settingsBoxLayout->addWidget(d->headerLabel); settingsBoxLayout->addWidget(d->accountBox); settingsBoxLayout->addWidget(d->albumsBox); settingsBoxLayout->addWidget(uploadBox); // settingsBoxLayout->addWidget(optionsBox); settingsBoxLayout->addSpacerItem(new QSpacerItem(0, 0, QSizePolicy::Minimum, QSizePolicy::Expanding)); settingsBoxLayout->addWidget(d->progressBar); mainLayout->addWidget(d->imgList); mainLayout->addWidget(d->settingsBox); setMainWidget(d->mainWidget); setModal(false); if (!d->import) { setWindowTitle(i18nc("@title:window", "Export to VKontakte Web Service")); startButton()->setText(i18n("Start Upload")); startButton()->setToolTip(i18n("Start upload to VKontakte service")); setMinimumSize(700, 520); uploadBox->hide(); } else { // TODO: import support d->imgList->hide(); // optionsBox->hide(); } // UI slots connect(startButton(), SIGNAL(clicked(bool)), this, SLOT(slotStartTransfer())); // for startReactivation() connect(d->vkapi, SIGNAL(authenticated()), this, SLOT(show())); // Dialog update slots connect(this, SIGNAL(signalUpdateBusyStatus(bool)), this, SLOT(slotUpdateBusyStatus(bool))); // TBD: busy status handling needs improvement connect(d->vkapi, SIGNAL(authenticated()), this, SLOT(slotUpdateBusyStatusReady())); slotUpdateBusyStatus(true); startReactivation(); } VKWindow::~VKWindow() { reset(); delete d; } //--------------------------------------------------------------------------- void VKWindow::initAccountBox() { d->accountBox = new VKAuthWidget(d->settingsBox, d->vkapi); connect(d->vkapi, SIGNAL(authenticated()), this, SLOT(slotAuthenticated())); connect(d->accountBox, SIGNAL(signalAuthCleared()), this, SLOT(slotAuthCleared())); connect(d->accountBox, SIGNAL(signalUpdateAuthInfo()), this, SLOT(slotUpdateHeaderLabel())); } void VKWindow::startReactivation() { d->imgList->loadImagesFromCurrentSelection(); reset(); // show() will be called after that d->accountBox->slotStartAuthentication(false); } void VKWindow::reset() { emit signalUpdateBusyStatus(false); } void VKWindow::slotUpdateBusyStatus(bool busy) { if (d->albumsBox) d->albumsBox->setEnabled(!busy && d->vkapi->isAuthenticated()); if (!busy) { setCursor(Qt::ArrowCursor); startButton()->setEnabled(d->vkapi->isAuthenticated()); setRejectButtonMode(QDialogButtonBox::Close); } else { setCursor(Qt::WaitCursor); startButton()->setEnabled(false); setRejectButtonMode(QDialogButtonBox::Cancel); } } void VKWindow::slotUpdateBusyStatusReady() { slotUpdateBusyStatus(false); } //--------------------------------------------------------------------------- void VKWindow::readSettings() { KSharedConfigPtr config = KSharedConfig::openConfig(); KConfigGroup grp = config->group("VKontakte Settings"); d->appId = grp.readEntry("VkAppId", "2446321"); d->albumToSelect = grp.readEntry("SelectedAlbumId", -1); d->vkapi->setAppId(d->appId); d->vkapi->setRequiredPermissions(Vkontakte::AppPermissions::Photos); d->vkapi->setInitialAccessToken(grp.readEntry("AccessToken", "")); } void VKWindow::writeSettings() { KSharedConfigPtr config = KSharedConfig::openConfig(); KConfigGroup grp = config->group("VKontakte Settings"); grp.writeEntry("VkAppId", d->appId); if (!d->vkapi->accessToken().isEmpty()) grp.writeEntry("AccessToken", d->vkapi->accessToken()); int aid = 0; if (!d->albumsBox->getCurrentAlbumId(aid)) { grp.deleteEntry("SelectedAlbumId"); } else { grp.writeEntry("SelectedAlbumId", aid); } } //--------------------------------------------------------------------------- void VKWindow::closeEvent(QCloseEvent* event) { if (!event) { return; } slotFinished(); event->accept(); } void VKWindow::slotFinished() { writeSettings(); reset(); } void VKWindow::slotAuthenticated() { if (d->albumsBox) d->albumsBox->setEnabled(true); } void VKWindow::slotAuthCleared() { if (d->albumsBox) { d->albumsBox->setEnabled(false); d->albumsBox->clearList(); } } void VKWindow::slotUpdateHeaderLabel() { d->headerLabel->setText(QString::fromLatin1("

" "%2

") .arg(d->accountBox->albumsURL()).arg(i18n("VKontakte"))); } //--------------------------------------------------------------------------- void VKWindow::handleVkError(KJob* kjob) { QMessageBox::critical(this, i18nc("@title:window", "Request to VKontakte failed"), kjob->errorText()); } //--------------------------------------------------------------------------- void VKWindow::slotStartTransfer() { int aid = 0; if (!d->albumsBox->getCurrentAlbumId(aid)) { // TODO: offer the user to create an album if there are no albums yet QMessageBox::information(this, QString(), i18n("Please select album first.")); return; } // TODO: import support if (!d->import) { emit signalUpdateBusyStatus(true); QStringList files; foreach (const QUrl& url, d->imgList->imageUrls(true)) files.append(url.toLocalFile()); Vkontakte::UploadPhotosJob* const job = new Vkontakte::UploadPhotosJob(d->vkapi->accessToken(), files, false /*d->checkKeepOriginal->isChecked()*/, aid); connect(job, SIGNAL(result(KJob*)), this, SLOT(slotPhotoUploadDone(KJob*))); connect(job, SIGNAL(progress(int)), d->progressBar, SLOT(setValue(int))); d->jobs.append(job); job->start(); } d->progressBar->show(); d->progressBar->progressScheduled(i18n("Vkontakte Export"), false, true); d->progressBar->progressThumbnailChanged( QIcon::fromTheme(QLatin1String("preferences-web-browser-shortcuts")).pixmap(22, 22)); } void VKWindow::slotPhotoUploadDone(KJob* kjob) { Vkontakte::UploadPhotosJob* const job = dynamic_cast(kjob); Q_ASSERT(job); d->jobs.removeAll(job); if (job == nullptr || job->error()) { handleVkError(job); } d->progressBar->hide(); d->progressBar->progressCompleted(); emit signalUpdateBusyStatus(false); } } // namespace DigikamGenericVKontaktePlugin diff --git a/core/libs/album/CMakeLists.txt b/core/libs/album/CMakeLists.txt index 9a77bc9f3f..7e6683b198 100644 --- a/core/libs/album/CMakeLists.txt +++ b/core/libs/album/CMakeLists.txt @@ -1,71 +1,71 @@ # # Copyright (c) 2010-2020 by Gilles Caulier, # Copyright (c) 2015 by Veaceslav Munteanu, # # Redistribution and use is allowed according to the terms of the BSD license. # For details see the accompanying COPYING-CMAKE-SCRIPTS file. APPLY_COMMON_POLICIES() set(libalbum_SRCS - engine/album.cpp - engine/albumhistory.cpp - engine/albummodificationhelper.cpp - engine/albumthumbnailloader.cpp - engine/albumwatch.cpp - engine/albumparser.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/engine/album.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/engine/albumhistory.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/engine/albummodificationhelper.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/engine/albumthumbnailloader.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/engine/albumwatch.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/engine/albumparser.cpp - widgets/albumpropsedit.cpp - widgets/albumselectors.cpp - widgets/albumselectcombobox.cpp - widgets/albumselectdialog.cpp - widgets/albumselectwidget.cpp - widgets/albumselecttabs.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/widgets/albumpropsedit.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/widgets/albumselectors.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/widgets/albumselectcombobox.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/widgets/albumselectdialog.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/widgets/albumselectwidget.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/widgets/albumselecttabs.cpp - treeview/abstractalbumtreeview.cpp - treeview/abstractcountingalbumtreeview.cpp - treeview/abstractcheckablealbumtreeview.cpp - treeview/albumtreeview.cpp - treeview/labelstreeview.cpp - treeview/albumlabelssearchhandler.cpp - treeview/tagtreeview.cpp - treeview/searchtreeview.cpp - treeview/datetreeview.cpp - treeview/albumselectiontreeview.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/treeview/abstractalbumtreeview.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/treeview/abstractcountingalbumtreeview.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/treeview/abstractcheckablealbumtreeview.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/treeview/albumtreeview.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/treeview/labelstreeview.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/treeview/albumlabelssearchhandler.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/treeview/tagtreeview.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/treeview/searchtreeview.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/treeview/datetreeview.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/treeview/albumselectiontreeview.cpp - manager/albummanager.cpp - manager/albummanager_p.cpp - manager/albummanager_album.cpp - manager/albummanager_palbum.cpp - manager/albummanager_talbum.cpp - manager/albummanager_dalbum.cpp - manager/albummanager_falbum.cpp - manager/albummanager_salbum.cpp - manager/albummanager_database.cpp - manager/albummanager_collection.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/manager/albummanager.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/manager/albummanager_p.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/manager/albummanager_album.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/manager/albummanager_palbum.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/manager/albummanager_talbum.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/manager/albummanager_dalbum.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/manager/albummanager_falbum.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/manager/albummanager_salbum.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/manager/albummanager_database.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/manager/albummanager_collection.cpp ) include_directories( $ $ $ $ $ $ $ $ ) if(ENABLE_DBUS) include_directories($) endif() # Used by digikamgui add_library(gui_digikamalbum_obj OBJECT ${libalbum_SRCS}) target_compile_definitions(gui_digikamalbum_obj PRIVATE digikamgui_EXPORTS ) diff --git a/core/libs/album/manager/albummanager.cpp b/core/libs/album/manager/albummanager.cpp index c05540e1cd..818660707e 100644 --- a/core/libs/album/manager/albummanager.cpp +++ b/core/libs/album/manager/albummanager.cpp @@ -1,299 +1,296 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2004-06-15 * Description : Albums manager interface. * * Copyright (C) 2004 by Renchi Raju * Copyright (C) 2006-2020 by Gilles Caulier * Copyright (C) 2006-2011 by Marcel Wiesweg * Copyright (C) 2015 by Mohamed_Anwer * * 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, 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. * * ============================================================ */ #include "albummanager_p.h" namespace Digikam { Q_GLOBAL_STATIC(AlbumManagerCreator, creator) // A friend-class shortcut to circumvent accessing this from within the destructor AlbumManager* AlbumManager::internalInstance = nullptr; AlbumManager* AlbumManager::instance() { return &creator->object; } // ----------------------------------------------------------------------------------- AlbumManager::AlbumManager() : d(new Private) { qRegisterMetaType>("QMap"); qRegisterMetaType>("QMap"); qRegisterMetaType >>("QMap >"); internalInstance = this; d->albumWatch = new AlbumWatch(this); // these operations are pretty fast, no need for long queuing d->scanPAlbumsTimer = new QTimer(this); d->scanPAlbumsTimer->setInterval(150); d->scanPAlbumsTimer->setSingleShot(true); connect(d->scanPAlbumsTimer, SIGNAL(timeout()), this, SLOT(scanPAlbums())); d->scanTAlbumsTimer = new QTimer(this); d->scanTAlbumsTimer->setInterval(150); d->scanTAlbumsTimer->setSingleShot(true); connect(d->scanTAlbumsTimer, SIGNAL(timeout()), this, SLOT(scanTAlbums())); d->scanSAlbumsTimer = new QTimer(this); d->scanSAlbumsTimer->setInterval(150); d->scanSAlbumsTimer->setSingleShot(true); connect(d->scanSAlbumsTimer, SIGNAL(timeout()), this, SLOT(scanSAlbums())); d->updatePAlbumsTimer = new QTimer(this); d->updatePAlbumsTimer->setInterval(150); d->updatePAlbumsTimer->setSingleShot(true); connect(d->updatePAlbumsTimer, SIGNAL(timeout()), this, SLOT(updateChangedPAlbums())); // this operation is much more expensive than the other scan methods d->scanDAlbumsTimer = new QTimer(this); d->scanDAlbumsTimer->setInterval(30 * 1000); d->scanDAlbumsTimer->setSingleShot(true); connect(d->scanDAlbumsTimer, SIGNAL(timeout()), this, SLOT(scanDAlbumsScheduled())); // moderately expensive d->albumItemCountTimer = new QTimer(this); d->albumItemCountTimer->setInterval(1000); d->albumItemCountTimer->setSingleShot(true); connect(d->albumItemCountTimer, SIGNAL(timeout()), this, SLOT(getAlbumItemsCount())); // more expensive d->tagItemCountTimer = new QTimer(this); d->tagItemCountTimer->setInterval(2500); d->tagItemCountTimer->setSingleShot(true); connect(d->tagItemCountTimer, SIGNAL(timeout()), this, SLOT(getTagItemsCount())); } AlbumManager::~AlbumManager() { delete d->rootPAlbum; delete d->rootTAlbum; delete d->rootDAlbum; delete d->rootSAlbum; internalInstance = nullptr; delete d; } void AlbumManager::cleanUp() { // This is what we prefer to do before Application destruction if (d->dateListJob) { d->dateListJob->cancel(); d->dateListJob = nullptr; } if (d->albumListJob) { d->albumListJob->cancel(); d->albumListJob = nullptr; } if (d->tagListJob) { d->tagListJob->cancel(); d->tagListJob = nullptr; } if (d->personListJob) { d->personListJob->cancel(); d->personListJob = nullptr; } } void AlbumManager::startScan() { if (!d->changed) { return; } d->changed = false; // create root albums d->rootPAlbum = new PAlbum(i18n("Albums")); insertPAlbum(d->rootPAlbum, nullptr); d->rootTAlbum = new TAlbum(i18n("Tags"), 0, true); insertTAlbum(d->rootTAlbum, nullptr); d->rootSAlbum = new SAlbum(i18n("Searches"), 0, true); emit signalAlbumAboutToBeAdded(d->rootSAlbum, nullptr, nullptr); d->allAlbumsIdHash[d->rootSAlbum->globalID()] = d->rootSAlbum; emit signalAlbumAdded(d->rootSAlbum); d->rootDAlbum = new DAlbum(QDate(), true); emit signalAlbumAboutToBeAdded(d->rootDAlbum, nullptr, nullptr); d->allAlbumsIdHash[d->rootDAlbum->globalID()] = d->rootDAlbum; emit signalAlbumAdded(d->rootDAlbum); // Create albums for album roots. Reuse logic implemented in the method foreach (const CollectionLocation& location, CollectionManager::instance()->allLocations()) { handleCollectionStatusChange(location, CollectionLocation::LocationNull); } // listen to location status changes connect(CollectionManager::instance(), SIGNAL(locationStatusChanged(CollectionLocation,int)), this, SLOT(slotCollectionLocationStatusChanged(CollectionLocation,int))); connect(CollectionManager::instance(), SIGNAL(locationPropertiesChanged(CollectionLocation)), this, SLOT(slotCollectionLocationPropertiesChanged(CollectionLocation))); // reload albums refresh(); // listen to album database changes connect(CoreDbAccess::databaseWatch(), SIGNAL(albumChange(AlbumChangeset)), this, SLOT(slotAlbumChange(AlbumChangeset))); connect(CoreDbAccess::databaseWatch(), SIGNAL(tagChange(TagChangeset)), this, SLOT(slotTagChange(TagChangeset))); connect(CoreDbAccess::databaseWatch(), SIGNAL(searchChange(SearchChangeset)), this, SLOT(slotSearchChange(SearchChangeset))); // listen to collection image changes connect(CoreDbAccess::databaseWatch(), SIGNAL(collectionImageChange(CollectionImageChangeset)), this, SLOT(slotCollectionImageChange(CollectionImageChangeset))); connect(CoreDbAccess::databaseWatch(), SIGNAL(imageTagChange(ImageTagChangeset)), this, SLOT(slotImageTagChange(ImageTagChangeset))); // listen to image attribute changes connect(ItemAttributesWatch::instance(), SIGNAL(signalImageDateChanged(qlonglong)), d->scanDAlbumsTimer, SLOT(start())); emit signalAllAlbumsLoaded(); } bool AlbumManager::isShowingOnlyAvailableAlbums() const { return d->showOnlyAvailableAlbums; } void AlbumManager::setShowOnlyAvailableAlbums(bool onlyAvailable) { if (d->showOnlyAvailableAlbums == onlyAvailable) { return; } d->showOnlyAvailableAlbums = onlyAvailable; emit signalShowOnlyAvailableAlbumsChanged(d->showOnlyAvailableAlbums); // We need to update the unavailable locations. // We assume the handleCollectionStatusChange does the right thing (even though old status == current status) foreach (const CollectionLocation& location, CollectionManager::instance()->allLocations()) { if (location.status() == CollectionLocation::LocationUnavailable) { handleCollectionStatusChange(location, CollectionLocation::LocationUnavailable); } } } void AlbumManager::refresh() { scanPAlbums(); scanTAlbums(); scanSAlbums(); scanDAlbums(); } void AlbumManager::prepareItemCounts() { // There is no way to find out if any data we had collected // previously is still valid - recompute scanDAlbums(); getAlbumItemsCount(); getTagItemsCount(); } void AlbumManager::slotImagesDeleted(const QList& imageIds) { qCDebug(DIGIKAM_GENERAL_LOG) << "Got image deletion notification from ItemViewUtilities for " << imageIds.size() << " images."; QSet sAlbumsToUpdate; QSet deletedImages = imageIds.toSet(); QList sAlbums = findSAlbumsBySearchType(DatabaseSearch::DuplicatesSearch); foreach (SAlbum* const sAlbum, sAlbums) { // Read the search query XML and save the image ids SearchXmlReader reader(sAlbum->query()); SearchXml::Element element; QSet images; while ((element = reader.readNext()) != SearchXml::End) { if ((element == SearchXml::Field) && (reader.fieldName().compare(QLatin1String("imageid")) == 0)) { images = reader.valueToLongLongList().toSet(); } } // If the deleted images are part of the SAlbum, // mark the album as ready for deletion and the images as ready for rescan. -#if QT_VERSION >= 0x050600 + if (images.intersects(deletedImages)) -#else - if (images.intersect(deletedImages).isEmpty()) -#endif { sAlbumsToUpdate.insert(sAlbum); } } if (!sAlbumsToUpdate.isEmpty()) { emit signalUpdateDuplicatesAlbums(sAlbumsToUpdate.values(), deletedImages.values()); } } } // namespace Digikam diff --git a/core/libs/database/CMakeLists.txt b/core/libs/database/CMakeLists.txt index 24d323e0c1..f7bccd18c7 100644 --- a/core/libs/database/CMakeLists.txt +++ b/core/libs/database/CMakeLists.txt @@ -1,238 +1,238 @@ # # Copyright (c) 2010-2020 by Gilles Caulier, # Copyright (c) 2015 by Veaceslav Munteanu, # Copyright (c) 2015 by Mohamed_Anwer, # Copyright (C) 2018 by Mario Frank, # # Redistribution and use is allowed according to the terms of the BSD license. # For details see the accompanying COPYING-CMAKE-SCRIPTS file. APPLY_COMMON_POLICIES() include_directories( $ $ $ $ $ $ $ $ $ $ ) if(ENABLE_DBUS) include_directories($) endif() # Boost uses operator names (and, not, ...) string(REPLACE "-fno-operator-names" "" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}") ### For digikamcore target - Symbols exported with DIGIKAM_EXPORT macro ########################## set(libdatabasecore_SRCS - server/databaseserverstarter.cpp - server/databaseservererror.cpp - server/databaseserver.cpp - - engine/dbengineconfigloader.cpp - engine/dbengineconfig.cpp - engine/dbengineactiontype.cpp - engine/dbengineerrorhandler.cpp - engine/dbengineguierrorhandler.cpp - engine/dbengineparameters.cpp - engine/dbenginebackend.cpp - engine/dbenginesqlquery.cpp - engine/dbengineaccess.cpp - - tags/tagregion.cpp - - thumbsdb/thumbsdb.cpp - thumbsdb/thumbsdbschemaupdater.cpp - thumbsdb/thumbsdbbackend.cpp - thumbsdb/thumbsdbaccess.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/server/databaseserverstarter.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/server/databaseservererror.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/server/databaseserver.cpp + + ${CMAKE_CURRENT_SOURCE_DIR}/engine/dbengineconfigloader.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/engine/dbengineconfig.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/engine/dbengineactiontype.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/engine/dbengineerrorhandler.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/engine/dbengineguierrorhandler.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/engine/dbengineparameters.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/engine/dbenginebackend.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/engine/dbenginesqlquery.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/engine/dbengineaccess.cpp + + ${CMAKE_CURRENT_SOURCE_DIR}/tags/tagregion.cpp + + ${CMAKE_CURRENT_SOURCE_DIR}/thumbsdb/thumbsdb.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/thumbsdb/thumbsdbschemaupdater.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/thumbsdb/thumbsdbbackend.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/thumbsdb/thumbsdbaccess.cpp ) # Used by digikamcore add_library(core_digikamdatabase_obj OBJECT ${libdatabasecore_SRCS}) target_compile_definitions(core_digikamdatabase_obj PRIVATE digikamcore_EXPORTS ) ### For digikamdatabase target - Symbols exported with DIGIKAM_DATABASE_EXPORT macro ############## set(libdatabase_SRCS - collection/collectionscanner.cpp - collection/collectionscanner_p.cpp - collection/collectionscanner_scan.cpp - collection/collectionscanner_utils.cpp - collection/collectionmanager.cpp - collection/collectionmanager_p.cpp - collection/collectionmanager_location.cpp - collection/collectionmanager_album.cpp - collection/collectionlocation.cpp - collection/collectionscannerhints.cpp - - coredb/coredb.cpp - coredb/coredbsearchxml.cpp - coredb/coredbchecker.cpp - coredb/coredbcopymanager.cpp - coredb/coredbconstants.cpp - coredb/coredbtransaction.cpp - coredb/coredbschemaupdater.cpp - coredb/coredbchangesets.cpp - coredb/coredbthumbinfoprovider.cpp - coredb/coredboperationgroup.cpp - coredb/coredbbackend.cpp - coredb/coredbwatch.cpp - coredb/coredburl.cpp - coredb/coredbaccess.cpp - coredb/coredbnamefilter.cpp - coredb/coredbdownloadhistory.cpp - - tags/tagproperties.cpp - tags/tagscache.cpp - tags/facetags.cpp - tags/facetagseditor.cpp - tags/facetagsiface.cpp - - dbjobs/dbjobsthread.cpp - dbjobs/dbjob.cpp - dbjobs/dbjobinfo.cpp - dbjobs/dbjobsmanager.cpp - dbjobs/duplicatesprogressobserver.cpp - - item/containers/iteminfo.cpp - item/containers/iteminfo_p.cpp - item/containers/iteminfo_containers.cpp - item/containers/iteminfo_geolocation.cpp - item/containers/iteminfo_groups.cpp - item/containers/iteminfo_history.cpp - item/containers/iteminfo_labels.cpp - item/containers/iteminfo_properties.cpp - item/containers/iteminfo_similarity.cpp - item/containers/iteminfo_tags.cpp - item/containers/iteminfo_thumbnail.cpp - item/containers/iteminfodata.cpp - item/containers/iteminfolist.cpp - item/containers/iteminfocache.cpp - item/containers/itemcomments.cpp - item/containers/itemcopyright.cpp - item/containers/itemposition.cpp - item/containers/itemtagpair.cpp - item/containers/itemextendedproperties.cpp - - item/lister/itemlister.cpp - item/lister/itemlister_palbum.cpp - item/lister/itemlister_talbum.cpp - item/lister/itemlister_salbum.cpp - item/lister/itemlister_falbum.cpp - item/lister/itemlisterrecord.cpp - item/lister/itemlisterreceiver.cpp - item/lister/itemattributeswatch.cpp - - item/query/itemquerybuilder.cpp - item/query/itemquerybuilder_p.cpp - item/query/itemqueryposthooks.cpp - item/query/fieldquerybuilder.cpp - - item/scanner/itemscanner.cpp - item/scanner/itemscanner_p.cpp - item/scanner/itemscanner_database.cpp - item/scanner/itemscanner_file.cpp - item/scanner/itemscanner_photo.cpp - item/scanner/itemscanner_video.cpp - item/scanner/itemscanner_history.cpp - item/scanner/itemscanner_baloo.cpp - - history/itemhistorygraph.cpp - history/itemhistorygraphmodel.cpp - - similaritydb/similaritydb.cpp - similaritydb/similaritydbschemaupdater.cpp - similaritydb/similaritydbbackend.cpp - similaritydb/similaritydbaccess.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/collection/collectionscanner.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/collection/collectionscanner_p.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/collection/collectionscanner_scan.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/collection/collectionscanner_utils.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/collection/collectionmanager.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/collection/collectionmanager_p.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/collection/collectionmanager_location.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/collection/collectionmanager_album.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/collection/collectionlocation.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/collection/collectionscannerhints.cpp + + ${CMAKE_CURRENT_SOURCE_DIR}/coredb/coredb.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/coredb/coredbsearchxml.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/coredb/coredbchecker.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/coredb/coredbcopymanager.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/coredb/coredbconstants.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/coredb/coredbtransaction.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/coredb/coredbschemaupdater.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/coredb/coredbchangesets.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/coredb/coredbthumbinfoprovider.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/coredb/coredboperationgroup.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/coredb/coredbbackend.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/coredb/coredbwatch.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/coredb/coredburl.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/coredb/coredbaccess.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/coredb/coredbnamefilter.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/coredb/coredbdownloadhistory.cpp + + ${CMAKE_CURRENT_SOURCE_DIR}/tags/tagproperties.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/tags/tagscache.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/tags/facetags.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/tags/facetagseditor.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/tags/facetagsiface.cpp + + ${CMAKE_CURRENT_SOURCE_DIR}/dbjobs/dbjobsthread.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/dbjobs/dbjob.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/dbjobs/dbjobinfo.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/dbjobs/dbjobsmanager.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/dbjobs/duplicatesprogressobserver.cpp + + ${CMAKE_CURRENT_SOURCE_DIR}/item/containers/iteminfo.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/item/containers/iteminfo_p.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/item/containers/iteminfo_containers.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/item/containers/iteminfo_geolocation.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/item/containers/iteminfo_groups.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/item/containers/iteminfo_history.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/item/containers/iteminfo_labels.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/item/containers/iteminfo_properties.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/item/containers/iteminfo_similarity.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/item/containers/iteminfo_tags.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/item/containers/iteminfo_thumbnail.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/item/containers/iteminfodata.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/item/containers/iteminfolist.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/item/containers/iteminfocache.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/item/containers/itemcomments.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/item/containers/itemcopyright.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/item/containers/itemposition.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/item/containers/itemtagpair.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/item/containers/itemextendedproperties.cpp + + ${CMAKE_CURRENT_SOURCE_DIR}/item/lister/itemlister.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/item/lister/itemlister_palbum.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/item/lister/itemlister_talbum.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/item/lister/itemlister_salbum.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/item/lister/itemlister_falbum.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/item/lister/itemlisterrecord.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/item/lister/itemlisterreceiver.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/item/lister/itemattributeswatch.cpp + + ${CMAKE_CURRENT_SOURCE_DIR}/item/query/itemquerybuilder.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/item/query/itemquerybuilder_p.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/item/query/itemqueryposthooks.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/item/query/fieldquerybuilder.cpp + + ${CMAKE_CURRENT_SOURCE_DIR}/item/scanner/itemscanner.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/item/scanner/itemscanner_p.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/item/scanner/itemscanner_database.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/item/scanner/itemscanner_file.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/item/scanner/itemscanner_photo.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/item/scanner/itemscanner_video.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/item/scanner/itemscanner_history.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/item/scanner/itemscanner_baloo.cpp + + ${CMAKE_CURRENT_SOURCE_DIR}/history/itemhistorygraph.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/history/itemhistorygraphmodel.cpp + + ${CMAKE_CURRENT_SOURCE_DIR}/similaritydb/similaritydb.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/similaritydb/similaritydbschemaupdater.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/similaritydb/similaritydbbackend.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/similaritydb/similaritydbaccess.cpp ) if(ENABLE_DBUS) set(libdatabase_SRCS ${libdatabase_SRCS} - coredb/coredbwatchadaptor.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/coredb/coredbwatchadaptor.cpp ) endif() if(Marble_FOUND) set(libdatabase_SRCS ${libdatabase_SRCS} - item/containers/itemgps.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/item/containers/itemgps.cpp ) endif() # enable exceptions -kde_source_files_enable_exceptions(history/itemhistorygraph.cpp - history/itemhistorygraphmodel.cpp +kde_source_files_enable_exceptions(${CMAKE_CURRENT_SOURCE_DIR}/history/itemhistorygraph.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/history/itemhistorygraphmodel.cpp ) set(libdatabasemodels_SRCS - models/itemfiltermodel.cpp - models/itemfiltermodel_p.cpp - models/itemfiltermodelthreads.cpp - models/itemfiltersettings.cpp - models/itemversionsmodel.cpp - models/itemthumbnailmodel.cpp - models/itemsortsettings.cpp - models/itemlistmodel.cpp - models/itemmodel.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/models/itemfiltermodel.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/models/itemfiltermodel_p.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/models/itemfiltermodelthreads.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/models/itemfiltersettings.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/models/itemversionsmodel.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/models/itemthumbnailmodel.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/models/itemsortsettings.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/models/itemlistmodel.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/models/itemmodel.cpp ) set(libhaar_SRCS - haar/haar.cpp - haar/haariface.cpp - haar/haariface_p.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/haar/haar.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/haar/haariface.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/haar/haariface_p.cpp ) # Used by digikamdatabase add_library(digikamdatabase_obj OBJECT ${libdatabase_SRCS} ${libdatabasemodels_SRCS} ${libhaar_SRCS} ) target_compile_definitions(digikamdatabase_obj PRIVATE digikamdatabase_EXPORTS ) ### For digikamgui target - Symbols exported with DIGIKAM_EXPORT macro ############################ set(libdatabasemain_SRCS - utils/widgets/dbstatdlg.cpp - utils/widgets/dbsettingswidget.cpp - utils/widgets/dbmigrationdlg.cpp - utils/widgets/mysqlinitbinary.cpp - utils/widgets/mysqlservbinary.cpp - utils/widgets/searchtextbardb.cpp - - utils/ifaces/dbinfoiface.cpp - utils/ifaces/syncjob.cpp - utils/ifaces/dio.cpp - utils/ifaces/diofinders.cpp - - utils/scan/scancontroller.cpp - utils/scan/scancontroller_p.cpp - utils/scan/scancontroller_start.cpp - utils/scan/scancontroller_progress.cpp - utils/scan/scancontroller_stop.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/utils/widgets/dbstatdlg.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/utils/widgets/dbsettingswidget.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/utils/widgets/dbmigrationdlg.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/utils/widgets/mysqlinitbinary.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/utils/widgets/mysqlservbinary.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/utils/widgets/searchtextbardb.cpp + + ${CMAKE_CURRENT_SOURCE_DIR}/utils/ifaces/dbinfoiface.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/utils/ifaces/syncjob.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/utils/ifaces/dio.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/utils/ifaces/diofinders.cpp + + ${CMAKE_CURRENT_SOURCE_DIR}/utils/scan/scancontroller.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/utils/scan/scancontroller_p.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/utils/scan/scancontroller_start.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/utils/scan/scancontroller_progress.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/utils/scan/scancontroller_stop.cpp ) # Used by digikamgui add_library(gui_digikamdatabasemain_obj OBJECT ${libdatabasemain_SRCS}) target_compile_definitions(gui_digikamdatabasemain_obj PRIVATE digikamgui_EXPORTS ) diff --git a/core/libs/database/collection/collectionscanner_scan.cpp b/core/libs/database/collection/collectionscanner_scan.cpp index c08dd01762..8aa25a7df5 100644 --- a/core/libs/database/collection/collectionscanner_scan.cpp +++ b/core/libs/database/collection/collectionscanner_scan.cpp @@ -1,1110 +1,1110 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2007-03-21 * Description : Collection scanning to database - scan operations. * * Copyright (C) 2005-2006 by Tom Albers * Copyright (C) 2007-2011 by Marcel Wiesweg * Copyright (C) 2009-2020 by Gilles Caulier * * 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, 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. * * ============================================================ */ #include "collectionscanner_p.h" namespace Digikam { void CollectionScanner::completeScan() { QElapsedTimer timer; timer.start(); emit startCompleteScan(); { // lock database CoreDbTransaction transaction; mainEntryPoint(true); d->resetRemovedItemsTime(); } // TODO: Implement a mechanism to watch for album root changes while we keep this list QList allLocations = CollectionManager::instance()->allAvailableLocations(); if (d->wantSignals && d->needTotalFiles) { // count for progress info int count = 0; foreach (const CollectionLocation& location, allLocations) { count += countItemsInFolder(location.albumRootPath()); } emit totalFilesToScan(count); } if (!d->checkObserver()) { emit cancelled(); return; } // if we have no hints to follow, clean up all stale albums if (!d->hints || !d->hints->hasAlbumHints()) { CoreDbAccess().db()->deleteStaleAlbums(); } scanForStaleAlbums(allLocations); if (!d->checkObserver()) { emit cancelled(); return; } if (d->wantSignals) { emit startScanningAlbumRoots(); } foreach (const CollectionLocation& location, allLocations) { scanAlbumRoot(location); } // do not continue to clean up without a complete scan! if (!d->checkObserver()) { emit cancelled(); return; } if (d->deferredFileScanning) { qCDebug(DIGIKAM_DATABASE_LOG) << "Complete scan (file scanning deferred) took:" << timer.elapsed() << "msecs."; emit finishedCompleteScan(); return; } CoreDbTransaction transaction; completeScanCleanupPart(); qCDebug(DIGIKAM_DATABASE_LOG) << "Complete scan took:" << timer.elapsed() << "msecs."; } void CollectionScanner::finishCompleteScan(const QStringList& albumPaths) { emit startCompleteScan(); { // lock database CoreDbTransaction transaction; mainEntryPoint(true); d->resetRemovedItemsTime(); } if (!d->checkObserver()) { emit cancelled(); return; } if (d->wantSignals) { emit startScanningAlbumRoots(); } // remove subalbums from list if parent album is already contained QStringList sortedPaths = albumPaths; std::sort(sortedPaths.begin(), sortedPaths.end()); QStringList::iterator it, it2; for (it = sortedPaths.begin() ; it != sortedPaths.end() ; ) { // remove all following entries as long as they have the same beginning (= are subalbums) for (it2 = it + 1 ; it2 != sortedPaths.end() && it2->startsWith(*it) ; ) { it2 = sortedPaths.erase(it2); } it = it2; } if (d->wantSignals && d->needTotalFiles) { // count for progress info int count = 0; foreach (const QString& path, sortedPaths) { count += countItemsInFolder(path); } emit totalFilesToScan(count); } foreach (const QString& path, sortedPaths) { CollectionLocation location = CollectionManager::instance()->locationForPath(path); QString album = CollectionManager::instance()->album(path); if (album == QLatin1String("/")) { scanAlbumRoot(location); } else { scanAlbum(location, album); } } // do not continue to clean up without a complete scan! if (!d->checkObserver()) { emit cancelled(); return; } CoreDbTransaction transaction; completeScanCleanupPart(); } void CollectionScanner::completeScanCleanupPart() { completeHistoryScanning(); updateRemovedItemsTime(); // Items may be set to status removed, without being definitely deleted. // This deletion shall be done after a certain time, as checked by checkedDeleteRemoved if (checkDeleteRemoved()) { // Mark items that are old enough and have the status trashed as obsolete // Only do this in a complete scan! CoreDbAccess access; QList trashedItems = access.db()->getImageIds(DatabaseItem::Status::Trashed); foreach (const qlonglong& item, trashedItems) { access.db()->setItemStatus(item, DatabaseItem::Status::Obsolete); } resetDeleteRemovedSettings(); } else { // increment the count of complete scans during which removed items were not deleted incrementDeleteRemovedCompleteScanCount(); } markDatabaseAsScanned(); emit finishedCompleteScan(); } void CollectionScanner::partialScan(const QString& filePath) { QString albumRoot = CollectionManager::instance()->albumRootPath(filePath); QString album = CollectionManager::instance()->album(filePath); partialScan(albumRoot, album); } void CollectionScanner::partialScan(const QString& albumRoot, const QString& album) { if (albumRoot.isNull() || album.isEmpty()) { // If you want to scan the album root, pass "/" qCWarning(DIGIKAM_DATABASE_LOG) << "partialScan(QString, QString) called with invalid values"; return; } /* if (CoreDbAccess().backend()->isInTransaction()) { // Install ScanController::instance()->suspendCollectionScan around your CoreDbTransaction qCDebug(DIGIKAM_DATABASE_LOG) << "Detected an active database transaction when starting a collection scan. " "Please report this error."; return; } */ mainEntryPoint(false); d->resetRemovedItemsTime(); CollectionLocation location = CollectionManager::instance()->locationForAlbumRootPath(albumRoot); if (location.isNull()) { qCWarning(DIGIKAM_DATABASE_LOG) << "Did not find a CollectionLocation for album root path " << albumRoot; return; } // if we have no hints to follow, clean up all stale albums // Hint: Rethink with next major db update if (!d->hints || !d->hints->hasAlbumHints()) { CoreDbAccess().db()->deleteStaleAlbums(); } // Usually, we can restrict stale album scanning to our own location. // But when there are album hints from a second location to this location, // also scan the second location QSet locationIdsToScan; locationIdsToScan << location.id(); if (d->hints) { QReadLocker locker(&d->hints->lock); QHash::const_iterator it; for (it = d->hints->albumHints.constBegin() ; it != d->hints->albumHints.constEnd() ; ++it) { if (it.key().albumRootId == location.id()) { locationIdsToScan << it.key().albumRootId; } } } scanForStaleAlbums(locationIdsToScan.values()); if (!d->checkObserver()) { emit cancelled(); return; } if (album == QLatin1String("/")) { scanAlbumRoot(location); } else { scanAlbum(location, album); } finishHistoryScanning(); if (!d->checkObserver()) { emit cancelled(); return; } updateRemovedItemsTime(); } qlonglong CollectionScanner::scanFile(const QString& filePath, FileScanMode mode) { QFileInfo info(filePath); QString dirPath = info.path(); // strip off filename QString albumRoot = CollectionManager::instance()->albumRootPath(dirPath); if (albumRoot.isNull()) { return -1; } QString album = CollectionManager::instance()->album(dirPath); return scanFile(albumRoot, album, info.fileName(), mode); } qlonglong CollectionScanner::scanFile(const QString& albumRoot, const QString& album, const QString& fileName, FileScanMode mode) { if (album.isEmpty() || fileName.isEmpty()) { qCWarning(DIGIKAM_DATABASE_LOG) << "scanFile(QString, QString, QString) called with empty album or empty filename"; return -1; } CollectionLocation location = CollectionManager::instance()->locationForAlbumRootPath(albumRoot); if (location.isNull()) { qCWarning(DIGIKAM_DATABASE_LOG) << "Did not find a CollectionLocation for album root path " << albumRoot; return -1; } QDir dir(location.albumRootPath() + album); QFileInfo fi(dir, fileName); if (!fi.exists()) { qCWarning(DIGIKAM_DATABASE_LOG) << "File given to scan does not exist" << albumRoot << album << fileName; return -1; } int albumId = checkAlbum(location, album); qlonglong imageId = CoreDbAccess().db()->getImageId(albumId, fileName); imageId = scanFile(fi, albumId, imageId, mode); return imageId; } void CollectionScanner::scanFile(const ItemInfo& info, FileScanMode mode) { if (info.isNull() || !info.isLocationAvailable()) { return; } QFileInfo fi(info.filePath()); scanFile(fi, info.albumId(), info.id(), mode); } qlonglong CollectionScanner::scanFile(const QFileInfo& fi, int albumId, qlonglong imageId, FileScanMode mode) { mainEntryPoint(false); if (imageId == -1) { switch (mode) { case NormalScan: case ModifiedScan: imageId = scanNewFile(fi, albumId); break; case Rescan: imageId = scanNewFileFullScan(fi, albumId); break; } } else { ItemScanInfo scanInfo = CoreDbAccess().db()->getItemScanInfo(imageId); switch (mode) { case NormalScan: scanFileNormal(fi, scanInfo); break; case ModifiedScan: scanModifiedFile(fi, scanInfo); break; case Rescan: rescanFile(fi, scanInfo); break; } } finishHistoryScanning(); return imageId; } void CollectionScanner::scanAlbumRoot(const CollectionLocation& location) { if (d->wantSignals) { emit startScanningAlbumRoot(location.albumRootPath()); } /* QDir dir(location.albumRootPath()); QStringList fileList(dir.entryList(QDir::Dirs | QDir::NoDotAndDotDot)); for (QStringList::iterator fileIt = fileList.begin(); fileIt != fileList.end(); ++fileIt) { scanAlbum(location, '/' + (*fileIt)); } */ // scan album that covers the root directory of this album root, // all contained albums, and their subalbums recursively. scanAlbum(location, QLatin1String("/")); if (d->wantSignals) { emit finishedScanningAlbumRoot(location.albumRootPath()); } } void CollectionScanner::scanForStaleAlbums(const QList& locations) { QList locationIdsToScan; foreach (const CollectionLocation& location, locations) { locationIdsToScan << location.id(); } scanForStaleAlbums(locationIdsToScan); } void CollectionScanner::scanForStaleAlbums(const QList& locationIdsToScan) { if (d->wantSignals) { emit startScanningForStaleAlbums(); } QList albumList = CoreDbAccess().db()->getAlbumShortInfos(); - QSet ignorePaths; QList toBeDeleted; QList::const_iterator it3; for (it3 = albumList.constBegin() ; it3 != albumList.constEnd() ; ++it3) { - if (!locationIdsToScan.contains((*it3).albumRootId)) + if (!locationIdsToScan.contains((*it3).albumRootId) || toBeDeleted.contains((*it3).id)) { continue; } CollectionLocation location = CollectionManager::instance()->locationForAlbumRootId((*it3).albumRootId); // Only handle albums on available locations if (location.isAvailable()) { QFileInfo fileInfo(location.albumRootPath() + (*it3).relativePath); bool dirExist = (fileInfo.exists() && fileInfo.isDir()); #ifdef Q_OS_WIN if (dirExist && !(*it3).relativePath.endsWith(QLatin1Char('/'))) { QDir dir(fileInfo.dir()); dirExist = dir.entryList(QDir::Dirs | QDir::NoDotAndDotDot) .contains(fileInfo.fileName()); } #endif // let digikam think that ignored directories got deleted // (if they already exist in the database, this will delete them) - if (!dirExist || - ignorePaths.contains(fileInfo.path()) || - d->ignoreDirectory.contains(fileInfo.fileName())) + if (!dirExist || d->ignoreDirectory.contains(fileInfo.fileName())) { - ignorePaths << fileInfo.filePath(); - toBeDeleted << (*it3).id; - d->scannedAlbums << (*it3).id; + // We have an ignored album, all sub-albums have to be ignored + + QList subAlbums = CoreDbAccess().db()->getAlbumAndSubalbumsForPath((*it3).albumRootId, + (*it3).relativePath); + toBeDeleted << subAlbums; + d->scannedAlbums << subAlbums; } } } // At this point, it is important to handle album renames. // We can still copy over album attributes later, but we cannot identify // the former album of removed images. // Just renaming the album is also much cheaper than rescanning all files. if (!toBeDeleted.isEmpty() && d->hints) { // shallow copy for reading without caring for locks QHash albumHints; { QReadLocker locker(&d->hints->lock); albumHints = d->hints->albumHints; } // go through all album copy/move hints QHash::const_iterator it; int toBeDeletedIndex; for (it = albumHints.constBegin() ; it != albumHints.constEnd() ; ++it) { // if the src entry of a hint is found in toBeDeleted, we have a move/rename, no copy. Handle these here. toBeDeletedIndex = toBeDeleted.indexOf(it.value().albumId); // We must double check that not, for some reason, the target album has already been scanned. QList::const_iterator it2; for (it2 = albumList.constBegin() ; it2 != albumList.constEnd() ; ++it2) { if ((it2->albumRootId == it.key().albumRootId) && (it2->relativePath == it.key().relativePath)) { toBeDeletedIndex = -1; break; } } if (toBeDeletedIndex != -1) { // check for existence of target CollectionLocation location = CollectionManager::instance()->locationForAlbumRootId(it.key().albumRootId); if (location.isAvailable()) { QFileInfo fileInfo(location.albumRootPath() + it.key().relativePath); bool dirExist = (fileInfo.exists() && fileInfo.isDir()); #ifdef Q_OS_WIN if (dirExist && !it.key().relativePath.endsWith(QLatin1Char('/'))) { QDir dir(fileInfo.dir()); dirExist = dir.entryList(QDir::Dirs | QDir::NoDotAndDotDot) .contains(fileInfo.fileName()); } #endif if (dirExist) { // Just set a new root/relativePath to the album. Further scanning will care for all cases or error. CoreDbAccess().db()->renameAlbum(it.value().albumId, it.key().albumRootId, it.key().relativePath); // No need any more to delete the album toBeDeleted.removeAt(toBeDeletedIndex); } } } } } safelyRemoveAlbums(toBeDeleted); if (d->wantSignals) { emit finishedScanningForStaleAlbums(); } } void CollectionScanner::scanAlbum(const CollectionLocation& location, const QString& album) { // + Adds album if it does not yet exist in the db. // + Recursively scans subalbums of album. // + Adds files if they do not yet exist in the db. // + Marks stale files as removed QDir dir(location.albumRootPath() + album); if (!dir.exists() || !dir.isReadable()) { qCWarning(DIGIKAM_DATABASE_LOG) << "Folder does not exist or is not readable: " << dir.path(); return; } if (d->wantSignals) { emit startScanningAlbum(location.albumRootPath(), album); } int albumID = checkAlbum(location, album); MetaEngineSettingsContainer settings = MetaEngineSettings::instance()->settings(); const QList& scanInfos = CoreDbAccess().db()->getItemScanInfos(albumID); // create a QHash filename -> index in list QHash fileNameIndexHash; QSet itemIdSet; for (int i = 0 ; i < scanInfos.size() ; ++i) { fileNameIndexHash[scanInfos.at(i).itemName] = i; itemIdSet << scanInfos.at(i).id; } const QStringList& list = dir.entryList(QDir::Dirs | QDir::Files | QDir::NoDotAndDotDot, QDir::Name | QDir::DirsLast); const QString xmpExt(QLatin1String(".xmp")); int counter = -1; foreach (const QString& entry, list) { if (!d->checkObserver()) { return; // return directly, do not go to cleanup code after loop! } ++counter; if (d->wantSignals && counter && (counter % 100 == 0)) { emit scannedFiles(counter); counter = 0; } QFileInfo info(dir, entry); if (info.isFile()) { // filter with name filter if (!d->nameFilters.contains(info.suffix().toLower())) { continue; } int index = fileNameIndexHash.value(info.fileName(), -1); if (index != -1) { // mark item as "seen" itemIdSet.remove(scanInfos.at(index).id); bool hasSidecar = (settings.useXMPSidecar4Reading && (list.contains(info.fileName() + xmpExt) || list.contains(info.completeBaseName() + xmpExt))); scanFileNormal(info, scanInfos.at(index), hasSidecar); } else if (info.completeSuffix().contains(QLatin1String("digikamtempfile."))) { // ignore temp files we created ourselves continue; } else { //qCDebug(DIGIKAM_DATABASE_LOG) << "Adding item " << info.fileName(); scanNewFile(info, albumID); // emit signals for scanned files with much higher granularity if (d->wantSignals && counter && (counter % 2 == 0)) { emit scannedFiles(counter); counter = 0; } } } else if (info.isDir()) { #ifdef Q_OS_WIN // Hide album that starts with a dot, as under Linux. if (info.fileName().startsWith(QLatin1Char('.'))) { continue; } #endif if (d->ignoreDirectory.contains(info.fileName())) { continue; } QString subAlbum = album; if (subAlbum != QLatin1String("/")) { subAlbum += QLatin1Char('/'); } scanAlbum(location, subAlbum + info.fileName()); } } if (d->wantSignals && counter) { emit scannedFiles(counter); } // Mark items in the db which we did not see on disk. if (!itemIdSet.isEmpty()) { QList ids = itemIdSet.values(); CoreDbOperationGroup group; CoreDbAccess().db()->removeItems(ids, QList() << albumID); itemsWereRemoved(ids); } // mark album as scanned d->scannedAlbums << albumID; if (d->wantSignals) { emit finishedScanningAlbum(location.albumRootPath(), album, list.count()); } } void CollectionScanner::scanFileNormal(const QFileInfo& fi, const ItemScanInfo& scanInfo, bool checkSidecar) { bool hasAnyHint = d->hints && d->hints->hasAnyNormalHint(scanInfo.id); // if the date is null, this signals a full rescan if (scanInfo.modificationDate.isNull() || (hasAnyHint && d->hints->hasRescanHint(scanInfo.id))) { if (hasAnyHint) { QWriteLocker locker(&d->hints->lock); d->hints->rescanItemHints.remove(scanInfo.id); } rescanFile(fi, scanInfo); return; } else if (hasAnyHint && d->hints->hasModificationHint(scanInfo.id)) { { QWriteLocker locker(&d->hints->lock); d->hints->modifiedItemHints.remove(scanInfo.id); } scanModifiedFile(fi, scanInfo); return; } else if (hasAnyHint) // metadata adjustment hints { if (d->hints->hasMetadataAboutToAdjustHint(scanInfo.id)) { // postpone scan return; } else // hasMetadataAdjustedHint { { QWriteLocker locker(&d->hints->lock); d->hints->metadataAdjustedHints.remove(scanInfo.id); } scanFileUpdateHashReuseThumbnail(fi, scanInfo, true); return; } } else if (d->updatingHashHint) { // if the file need not be scanned because of modification, update the hash if (s_modificationDateEquals(fi.lastModified(), scanInfo.modificationDate) && (fi.size() == scanInfo.fileSize)) { scanFileUpdateHashReuseThumbnail(fi, scanInfo, false); return; } } MetaEngineSettingsContainer settings = MetaEngineSettings::instance()->settings(); QDateTime modificationDate = fi.lastModified(); if (checkSidecar && settings.updateFileTimeStamp && settings.useXMPSidecar4Reading && DMetadata::hasSidecar(fi.filePath())) { QString filePath = DMetadata::sidecarPath(fi.filePath()); QDateTime sidecarDate = QFileInfo(filePath).lastModified(); if (sidecarDate > modificationDate) { modificationDate = sidecarDate; } } if (!s_modificationDateEquals(modificationDate, scanInfo.modificationDate) || (fi.size() != scanInfo.fileSize)) { if (settings.rescanImageIfModified) { rescanFile(fi, scanInfo); } else { scanModifiedFile(fi, scanInfo); } } } qlonglong CollectionScanner::scanNewFile(const QFileInfo& info, int albumId) { if (d->checkDeferred(info)) { return -1; } ItemScanner scanner(info); scanner.setCategory(category(info)); // Check copy/move hints for single items qlonglong srcId = 0; if (d->hints) { QReadLocker locker(&d->hints->lock); srcId = d->hints->itemHints.value(NewlyAppearedFile(albumId, info.fileName())); } if (srcId != 0) { scanner.copiedFrom(albumId, srcId); } else { // Check copy/move hints for whole albums int srcAlbum = d->establishedSourceAlbums.value(albumId); if (srcAlbum) { // if we have one source album, find out if there is a file with the same name srcId = CoreDbAccess().db()->getImageId(srcAlbum, info.fileName()); } if (srcId != 0) { scanner.copiedFrom(albumId, srcId); } else { // Establishing identity with the unique hsah scanner.newFile(albumId); } } d->finishScanner(scanner); return scanner.id(); } qlonglong CollectionScanner::scanNewFileFullScan(const QFileInfo& info, int albumId) { if (d->checkDeferred(info)) { return -1; } ItemScanner scanner(info); scanner.setCategory(category(info)); scanner.newFileFullScan(albumId); d->finishScanner(scanner); return scanner.id(); } void CollectionScanner::scanModifiedFile(const QFileInfo& info, const ItemScanInfo& scanInfo) { if (d->checkDeferred(info)) { return; } ItemScanner scanner(info, scanInfo); scanner.setCategory(category(info)); scanner.fileModified(); d->finishScanner(scanner); } void CollectionScanner::scanFileUpdateHashReuseThumbnail(const QFileInfo& info, const ItemScanInfo& scanInfo, bool fileWasEdited) { QString oldHash = scanInfo.uniqueHash; qlonglong oldSize = scanInfo.fileSize; // same code as scanModifiedFile ItemScanner scanner(info, scanInfo); scanner.setCategory(category(info)); scanner.fileModified(); QString newHash = scanner.itemScanInfo().uniqueHash; qlonglong newSize = scanner.itemScanInfo().fileSize; if (ThumbsDbAccess::isInitialized()) { if (fileWasEdited) { // The file was edited in such a way that we know that the pixel content did not change, so we can reuse the thumbnail. // We need to add a link to the thumbnail data with the new hash/file size _and_ adjust // the file modification date in the data table. ThumbsDbInfo thumbDbInfo = ThumbsDbAccess().db()->findByHash(oldHash, oldSize); if (thumbDbInfo.id != -1) { ThumbsDbAccess().db()->insertUniqueHash(newHash, newSize, thumbDbInfo.id); ThumbsDbAccess().db()->updateModificationDate(thumbDbInfo.id, scanner.itemScanInfo().modificationDate); // TODO: also update details thumbnails (by file path and URL scheme) } } else { ThumbsDbAccess().db()->replaceUniqueHash(oldHash, oldSize, newHash, newSize); } } d->finishScanner(scanner); } void CollectionScanner::rescanFile(const QFileInfo& info, const ItemScanInfo& scanInfo) { if (d->checkDeferred(info)) { return; } ItemScanner scanner(info, scanInfo); scanner.setCategory(category(info)); scanner.rescan(); d->finishScanner(scanner); } void CollectionScanner::completeHistoryScanning() { // scan tagged images int needResolvingTag = TagsCache::instance()->getOrCreateInternalTag(InternalTagName::needResolvingHistory()); int needTaggingTag = TagsCache::instance()->getOrCreateInternalTag(InternalTagName::needTaggingHistoryGraph()); QList ids = CoreDbAccess().db()->getItemIDsInTag(needResolvingTag); historyScanningStage2(ids); ids = CoreDbAccess().db()->getItemIDsInTag(needTaggingTag); qCDebug(DIGIKAM_DATABASE_LOG) << "items to tag" << ids; historyScanningStage3(ids); } void CollectionScanner::finishHistoryScanning() { // scan recorded ids QList ids; // stage 2 ids = d->needResolveHistorySet.values(); d->needResolveHistorySet.clear(); historyScanningStage2(ids); if (!d->checkObserver()) { return; } // stage 3 ids = d->needTaggingHistorySet.values(); d->needTaggingHistorySet.clear(); historyScanningStage3(ids); } void CollectionScanner::historyScanningStage2(const QList& ids) { foreach (const qlonglong& id, ids) { if (!d->checkObserver()) { return; } CoreDbOperationGroup group; if (d->recordHistoryIds) { QList needTaggingIds; ItemScanner::resolveImageHistory(id, &needTaggingIds); foreach (const qlonglong& needTag, needTaggingIds) { d->needTaggingHistorySet << needTag; } } else { ItemScanner::resolveImageHistory(id); } } } void CollectionScanner::historyScanningStage3(const QList& ids) { foreach (const qlonglong& id, ids) { if (!d->checkObserver()) { return; } CoreDbOperationGroup group; ItemScanner::tagItemHistoryGraph(id); } } bool CollectionScanner::databaseInitialScanDone() { CoreDbAccess access; return !access.db()->getSetting(QLatin1String("Scanned")).isEmpty(); } } // namespace Digikam diff --git a/core/libs/database/utils/ifaces/dbinfoiface.cpp b/core/libs/database/utils/ifaces/dbinfoiface.cpp index 08813469d5..15cb0d97ab 100644 --- a/core/libs/database/utils/ifaces/dbinfoiface.cpp +++ b/core/libs/database/utils/ifaces/dbinfoiface.cpp @@ -1,792 +1,792 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2017-05-06 * Description : interface to database information for shared tools. * * Copyright (C) 2017-2020 by Gilles Caulier * Copyright (C) 2019-2020 by Minh Nghia Duong * Copyright (C) 2017 by Mario Frank * * 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, 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. * * ============================================================ */ #include "dbinfoiface.h" // Qt includes #include #include // Local includes #include "albumselecttabs.h" #include "applicationsettings.h" #include "album.h" #include "albummodel.h" #include "albummanager.h" #include "albumfiltermodel.h" #include "albumselectwidget.h" #include "albumparser.h" #include "coredb.h" #include "collectionmanager.h" #include "coredbnamefilter.h" #include "collectionscanner.h" #include "digikamapp.h" #include "digikam_debug.h" #include "itemattributeswatch.h" #include "itemiconview.h" #include "itemcopyright.h" #include "iteminfo.h" #include "photoinfocontainer.h" #include "videoinfocontainer.h" #include "itemposition.h" #include "itemsortsettings.h" #include "coredbsearchxml.h" #include "itemlister.h" #include "itemlisterreceiver.h" #include "dio.h" #include "fileactionmngr.h" #include "tagsactionmngr.h" #ifdef HAVE_MARBLE # include "itemgps.h" #endif namespace Digikam { class Q_DECL_HIDDEN DBInfoIface::Private { public: explicit Private() : albumManager(AlbumManager::instance()), albumsChooser(nullptr), albumSelector(nullptr), operationType(ApplicationSettings::Unspecified), withGroupedIsSet(false), withGrouped(false) { } AlbumManager* albumManager; AlbumSelectTabs* albumsChooser; AlbumSelectWidget* albumSelector; QList itemUrls; ApplicationSettings::OperationType operationType; bool withGroupedIsSet; bool withGrouped; public: /** * get the images from the Physical album in database and return the items found. */ QList imagesFromPAlbum(PAlbum* const album) const { // get the images from the database and return the items found CoreDB::ItemSortOrder sortOrder = CoreDB::NoItemSorting; switch (ApplicationSettings::instance()->getImageSortOrder()) { default: case ItemSortSettings::SortByFileName: { sortOrder = CoreDB::ByItemName; break; } case ItemSortSettings::SortByFilePath: { sortOrder = CoreDB::ByItemPath; break; } case ItemSortSettings::SortByCreationDate: { sortOrder = CoreDB::ByItemDate; break; } case ItemSortSettings::SortByRating: { sortOrder = CoreDB::ByItemRating; break; } // NOTE: ByISize not supported } QStringList list = CoreDbAccess().db()->getItemURLsInAlbum(album->id(), sortOrder); QList urlList; CoreDbNameFilter nameFilter(ApplicationSettings::instance()->getAllFileFilter()); for (QStringList::const_iterator it = list.constBegin() ; it != list.constEnd() ; ++it) { if (nameFilter.matches(*it)) { urlList << QUrl::fromLocalFile(*it); } } return urlList; } /** * get the images from the Tags album in database and return the items found. */ QList imagesFromTAlbum(TAlbum* const album) const { QStringList list = CoreDbAccess().db()->getItemURLsInTag(album->id()); QList urlList; CoreDbNameFilter nameFilter(ApplicationSettings::instance()->getAllFileFilter()); for (QStringList::const_iterator it = list.constBegin() ; it != list.constEnd() ; ++it) { if (nameFilter.matches(*it)) { urlList << QUrl::fromLocalFile(*it); } } return urlList; } /** * get the images from the search album in database and return the items found. */ QList imagesFromSAlbum(SAlbum* const album) const { QList urlList; CoreDbNameFilter nameFilter(ApplicationSettings::instance()->getAllFileFilter()); if (album->isDuplicatesSearch()) { // duplicates search album -> get the id list from the query SearchXmlReader reader(album->query()); reader.readToFirstField(); // Get the defined image ids. QList list; list << reader.valueToIntList(); foreach (int imageId, list) { ItemInfo imageInfo(imageId); if (imageInfo.isVisible()) { // if the image is visible, i.e. existent and not deleted, // add the url if the name filter matches QUrl imageUrl = imageInfo.fileUrl(); /* qCDebug(DIGIKAM_GENERAL_LOG) << "Duplicates search Image url " << imageUrl.toString(); */ if (nameFilter.matches(imageUrl.toString())) { urlList << imageUrl; } } } } else { // If we do not have a duplicates search, we use the image lister to get the images. ItemLister lister; lister.setListOnlyAvailable(true); ItemListerValueListReceiver receiver; if (album->searchType() == DatabaseSearch::HaarSearch) { lister.listHaarSearch(&receiver, album->query()); } else { lister.listSearch(&receiver, album->query(), 0, -1); } if (!receiver.hasError) { // if there were no error, fetch and process the results. foreach (const ItemListerRecord &record, receiver.records) { ItemInfo imageInfo(record); QUrl imageUrl = imageInfo.fileUrl(); /* qCDebug(DIGIKAM_GENERAL_LOG) << "Fuzzy/other search Image url " << imageUrl.toString(); */ if (nameFilter.matches(imageUrl.toString())) { urlList << imageUrl; } } } } return urlList; } /** * Remove grouped images if user chose/chooses to. */ QList resolveGroupsFromAlbums(const QList& urlList) { QList lst = urlList; if (!(withGroupedIsSet && withGrouped)) { foreach (const QUrl& url, urlList) { ItemInfo info = ItemInfo::fromUrl(url); if (info.hasGroupedImages()) { if (!withGroupedIsSet) { withGroupedIsSet = true; withGrouped = ApplicationSettings::instance()->askGroupingOperateOnAll(operationType); if (withGrouped) { break; } } foreach (const ItemInfo& i, info.groupedImages()) { lst.removeOne(i.fileUrl()); } } } } return lst; } bool includeGroupedFromSelected() { if (withGroupedIsSet) { return withGrouped; } withGroupedIsSet = true; withGrouped = DigikamApp::instance()->view()->selectedNeedGroupResolving(operationType); return withGrouped; } }; DBInfoIface::DBInfoIface(QObject* const parent, const QList& lst, const ApplicationSettings::OperationType type) : DInfoInterface(parent), d(new Private) { setObjectName(QLatin1String("DBInfoIface")); d->itemUrls = lst; d->operationType = type; // forward signal to DPluginAction of Digikam connect(TagsActionMngr::defaultManager(), SIGNAL(signalShortcutPressed(QString,int)), this, SIGNAL(signalShortcutPressed(QString,int))); } DBInfoIface::~DBInfoIface() { delete d; } void DBInfoIface::slotDateTimeForUrl(const QUrl& url, const QDateTime& dt, bool updModDate) { ItemInfo info = ItemInfo::fromUrl(url); if (!info.isNull()) { info.setDateTime(dt); if (updModDate) { info.setModDateTime(dt); } } ItemAttributesWatch::instance()->fileMetadataChanged(url); } void DBInfoIface::slotMetadataChangedForUrl(const QUrl& url) { // Refresh Database with new metadata from file. CollectionScanner scanner; scanner.scanFile(url.toLocalFile(), CollectionScanner::Rescan); ItemAttributesWatch::instance()->fileMetadataChanged(url); } void DBInfoIface::parseAlbumItemsRecursive() { Album* const currAlbum = d->albumManager->currentAlbums().first(); if (currAlbum) { AlbumParser* const parser = new AlbumParser(currAlbum); connect(parser, SIGNAL(signalComplete(QList)), this, SIGNAL(signalAlbumItemsRecursiveCompleted(QList))); parser->run(); } } QList DBInfoIface::currentAlbumItems() const { if (!d->itemUrls.isEmpty()) { return d->itemUrls; } if (d->albumManager->currentAlbums().isEmpty()) { return QList(); } - Album* const currAlbum = d->albumManager->currentAlbums().first(); - QList imageList = d->resolveGroupsFromAlbums(albumItems(currAlbum)); + QList imageList = DigikamApp::instance()->view()->allUrls(d->includeGroupedFromSelected()); if (imageList.isEmpty()) { - imageList = DigikamApp::instance()->view()->allUrls(d->includeGroupedFromSelected()); + Album* const currAlbum = d->albumManager->currentAlbums().first(); + imageList = d->resolveGroupsFromAlbums(albumItems(currAlbum)); } return imageList; } QList DBInfoIface::currentSelectedItems() const { if (!d->itemUrls.isEmpty()) { return d->itemUrls; } return DigikamApp::instance()->view()->selectedUrls(d->includeGroupedFromSelected()); } QList DBInfoIface::allAlbumItems() const { QList imageList; const AlbumList palbumList = d->albumManager->allPAlbums(); for (AlbumList::ConstIterator it = palbumList.constBegin() ; it != palbumList.constEnd() ; ++it) { // don't add the root album if ((*it)->isRoot()) { continue; } PAlbum* const p = dynamic_cast(*it); if (p) { imageList.append(d->imagesFromPAlbum(p)); } } return imageList; } DBInfoIface::DInfoMap DBInfoIface::albumInfo(int gid) const { Album* const a = d->albumManager->findAlbum(gid); if (!a) { return DInfoMap(); } DInfoMap map; map.insert(QLatin1String("title"), a->title()); PAlbum* const p = dynamic_cast(a); if (p) { map.insert(QLatin1String("caption"), p->caption()); map.insert(QLatin1String("date"), p->date()); map.insert(QLatin1String("path"), p->folderPath()); } return map; } DBInfoIface::DInfoMap DBInfoIface::itemInfo(const QUrl& url) const { DInfoMap map; ItemInfo info = ItemInfo::fromUrl(url); if (!info.isNull()) { map.insert(QLatin1String("name"), info.name()); map.insert(QLatin1String("title"), info.title()); map.insert(QLatin1String("comment"), info.comment()); map.insert(QLatin1String("orientation"), info.orientation()); map.insert(QLatin1String("datetime"), info.dateTime()); map.insert(QLatin1String("rating"), info.rating()); map.insert(QLatin1String("colorlabel"), info.colorLabel()); map.insert(QLatin1String("picklabel"), info.pickLabel()); map.insert(QLatin1String("filesize"), info.fileSize()); map.insert(QLatin1String("dimensions"), info.dimensions()); // Get digiKam Tags Path list of picture from database. // Ex.: "City/Paris/Monuments/Notre Dame" QList tagIds = info.tagIds(); QStringList tagspath = AlbumManager::instance()->tagPaths(tagIds, false); map.insert(QLatin1String("tagspath"), tagspath); // Get digiKam Tags name (keywords) list of picture from database. // Ex.: "Notre Dame" QStringList tags = AlbumManager::instance()->tagNames(tagIds); map.insert(QLatin1String("keywords"), tags); // Get GPS location of picture from database. ItemPosition pos = info.imagePosition(); if (!pos.isEmpty()) { map.insert(QLatin1String("latitude"), pos.latitudeNumber()); map.insert(QLatin1String("longitude"), pos.longitudeNumber()); map.insert(QLatin1String("altitude"), pos.altitude()); } // Get Copyright information of picture from database. ItemCopyright rights = info.imageCopyright(); map.insert(QLatin1String("creators"), rights.creator()); map.insert(QLatin1String("credit"), rights.credit()); map.insert(QLatin1String("rights"), rights.rights()); map.insert(QLatin1String("source"), rights.source()); PhotoInfoContainer photoInfo = info.photoInfoContainer(); map.insert(QLatin1String("make"), photoInfo.make); map.insert(QLatin1String("model"), photoInfo.model); map.insert(QLatin1String("exposuretime"), photoInfo.exposureTime); map.insert(QLatin1String("sensitivity"), photoInfo.sensitivity); map.insert(QLatin1String("aperture"), photoInfo.aperture); map.insert(QLatin1String("focallength"), photoInfo.focalLength); map.insert(QLatin1String("focalLength35mm"), photoInfo.focalLength35mm); // TODO: add more video metadata as needed VideoInfoContainer videoInfo = info.videoInfoContainer(); map.insert(QLatin1String("videocodec"), videoInfo.videoCodec); qCDebug(DIGIKAM_GENERAL_LOG) << "Database Info populated for" << url; } else { qCDebug(DIGIKAM_GENERAL_LOG) << "Database Info is NULL for" << url; } return map; } void DBInfoIface::setItemInfo(const QUrl& url, const DInfoMap& map) const { ItemInfo info = ItemInfo::fromUrl(url); QStringList keys = map.keys(); if (map.contains(QLatin1String("orientation"))) { info.setOrientation(map[QLatin1String("orientation")].toInt()); keys.removeAll(QLatin1String("orientation")); } if (map.contains(QLatin1String("rating"))) { info.setRating(map[QLatin1String("rating")].toInt()); keys.removeAll(QLatin1String("rating")); } if (map.contains(QLatin1String("colorlabel"))) { info.setColorLabel(map[QLatin1String("colorlabel")].toInt()); keys.removeAll(QLatin1String("colorlabel")); } if (map.contains(QLatin1String("picklabel"))) { info.setPickLabel(map[QLatin1String("picklabel")].toInt()); keys.removeAll(QLatin1String("picklabel")); } // NOTE: For now tag doesn't really exist anywhere else apart from digikam therefore it's not really necessary to implement accessor method in InfoIface if (map.contains(QLatin1String("tag"))) { int tagID = map[QLatin1String("tag")].toInt(); if (!info.tagIds().contains(tagID)) { FileActionMngr::instance()->assignTag(info, tagID); } else { FileActionMngr::instance()->removeTag(info, tagID); } } if (!keys.isEmpty()) { qCWarning(DIGIKAM_GENERAL_LOG) << "Keys not yet supported in DMetaInfoIface::setItemInfo():" << keys; } } QList DBInfoIface::albumItems(Album* const album) const { if (!album) { return QList(); } QList imageList; switch (album->type()) { case Album::PHYSICAL: { PAlbum* const p = dynamic_cast(album); if (p) { imageList = d->imagesFromPAlbum(p); } break; } case Album::TAG: { TAlbum* const p = dynamic_cast(album); if (p) { imageList = d->imagesFromTAlbum(p); } break; } case Album::SEARCH: { SAlbum* const s = dynamic_cast(album); if (s) { imageList = d->imagesFromSAlbum(s); } break; } default: { qCWarning(DIGIKAM_GENERAL_LOG) << "Unknown album type"; break; } } return d->resolveGroupsFromAlbums(imageList); } QList DBInfoIface::albumItems(int id) const { return albumItems(d->albumManager->findAlbum(id)); } QList DBInfoIface::albumsItems(const DAlbumIDs& lst) const { QList imageList; foreach (int gid, lst) { imageList << albumItems(gid); } return d->resolveGroupsFromAlbums(imageList); } QWidget* DBInfoIface::albumChooser(QWidget* const parent) const { if (!d->albumsChooser) { d->albumsChooser = new AlbumSelectTabs(objectName(), parent); } connect(d->albumsChooser, SIGNAL(signalAlbumSelectionChanged()), this, SIGNAL(signalAlbumChooserSelectionChanged())); return d->albumsChooser; } DBInfoIface::DAlbumIDs DBInfoIface::albumChooserItems() const { if (!d->albumsChooser) { return DAlbumIDs(); } AlbumList lst = d->albumsChooser->selectedAlbums(); DAlbumIDs ids; foreach (Album* const a, lst) { if (a) { ids << a->globalID(); } } return ids; } bool DBInfoIface::supportAlbums() const { return true; } QWidget* DBInfoIface::uploadWidget(QWidget* const parent) const { if (!d->albumSelector) { d->albumSelector = new AlbumSelectWidget(parent); connect(d->albumSelector, SIGNAL(itemSelectionChanged()), this, SIGNAL(signalUploadUrlChanged())); } return d->albumSelector; } QUrl DBInfoIface::uploadUrl() const { QUrl url; if (d->albumSelector) { PAlbum* const p = d->albumSelector->currentAlbum(); if (p) { url = QUrl::fromLocalFile(p->folderPath()); } } return url; } QUrl DBInfoIface::defaultUploadUrl() const { QUrl place = QUrl::fromLocalFile(QDir::homePath()); QStringList pics = QStandardPaths::standardLocations(QStandardPaths::PicturesLocation); if (!pics.isEmpty()) { place = QUrl::fromLocalFile(pics.first()); } Album* const album = AlbumManager::instance()->currentAlbums().first(); if (album->type() == Album::PHYSICAL) { PAlbum* const p = dynamic_cast(album); if (p) { place = QUrl::fromLocalFile(p->folderPath()); } } else { QStringList cols = CollectionManager::instance()->allAvailableAlbumRootPaths(); if (!cols.isEmpty()) { place = QUrl::fromLocalFile(cols.first()); } } return place; } QAbstractItemModel* DBInfoIface::tagFilterModel() { TagModel* const tagModel = new TagModel(AbstractAlbumModel::IgnoreRootAlbum, this); TagPropertiesFilterModel* const filterModel = new TagPropertiesFilterModel(this); filterModel->setSourceAlbumModel(tagModel); filterModel->sort(0); return filterModel; } #ifdef HAVE_MARBLE QList DBInfoIface::currentGPSItems() const { QList items; foreach (const QUrl& url, currentSelectedItems()) { ItemInfo info = ItemInfo::fromUrl(url); items << new ItemGPS(info); } return items; } #endif QMap DBInfoIface::passShortcutActionsToWidget(QWidget* const wdg) const { TagsActionMngr::defaultManager()->registerActionsToWidget(wdg); QMap shortcutPrefixes; shortcutPrefixes.insert(QLatin1String("rating"), TagsActionMngr::defaultManager()->ratingShortcutPrefix()); shortcutPrefixes.insert(QLatin1String("tag"), TagsActionMngr::defaultManager()->tagShortcutPrefix()); shortcutPrefixes.insert(QLatin1String("picklabel"), TagsActionMngr::defaultManager()->pickShortcutPrefix()); shortcutPrefixes.insert(QLatin1String("colorlabel"), TagsActionMngr::defaultManager()->colorShortcutPrefix()); return shortcutPrefixes; } } // namespace Digikam diff --git a/core/libs/database/utils/ifaces/dio.cpp b/core/libs/database/utils/ifaces/dio.cpp index 63855785bb..0419403a3b 100644 --- a/core/libs/database/utils/ifaces/dio.cpp +++ b/core/libs/database/utils/ifaces/dio.cpp @@ -1,629 +1,642 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2005-05-17 * Description : low level files management interface. * * Copyright (C) 2005 by Renchi Raju * Copyright (C) 2012-2013 by Marcel Wiesweg * Copyright (C) 2015 by Mohamed_Anwer * Copyright (C) 2018 by Maik Qualmann * * 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, 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. * * ============================================================ */ #include "dio.h" // Qt includes #include // KDE includes #include // Local includes #include "digikam_debug.h" #include "iteminfo.h" #include "diofinders.h" #include "albummanager.h" #include "tagscache.h" #include "coredb.h" #include "coredbaccess.h" #include "album.h" #include "dmetadata.h" #include "metaenginesettings.h" #include "scancontroller.h" #include "thumbsdb.h" #include "thumbsdbaccess.h" #include "iojobsmanager.h" #include "collectionmanager.h" #include "dnotificationwrapper.h" #include "loadingcacheinterface.h" #include "progressmanager.h" #include "digikamapp.h" #include "iojobdata.h" namespace Digikam { class Q_DECL_HIDDEN DIOCreator { public: DIO object; }; Q_GLOBAL_STATIC(DIOCreator, creator) // ------------------------------------------------------------------------------------------------ DIO* DIO::instance() { return &creator->object; } DIO::DIO() { m_processingCount = 0; } DIO::~DIO() { } void DIO::cleanUp() { } bool DIO::itemsUnderProcessing() { return instance()->m_processingCount; } // Album -> Album ----------------------------------------------------- void DIO::copy(PAlbum* const src, PAlbum* const dest) { if (!src || !dest) { return; } instance()->processJob(new IOJobData(IOJobData::CopyAlbum, src, dest)); } void DIO::move(PAlbum* const src, PAlbum* const dest) { if (!src || !dest) { return; } #ifdef Q_OS_WIN AlbumManager::instance()->removeWatchedPAlbums(src); #endif instance()->processJob(new IOJobData(IOJobData::MoveAlbum, src, dest)); } // Images -> Album ---------------------------------------------------- void DIO::copy(const QList& infos, PAlbum* const dest) { if (!dest) { return; } instance()->processJob(new IOJobData(IOJobData::CopyImage, infos, dest)); } void DIO::move(const QList& infos, PAlbum* const dest) { if (!dest) { return; } instance()->processJob(new IOJobData(IOJobData::MoveImage, infos, dest)); } // External files -> album -------------------------------------------- void DIO::copy(const QUrl& src, PAlbum* const dest) { copy(QList() << src, dest); } void DIO::copy(const QList& srcList, PAlbum* const dest) { if (!dest) { return; } instance()->processJob(new IOJobData(IOJobData::CopyFiles, srcList, dest)); } void DIO::move(const QUrl& src, PAlbum* const dest) { move(QList() << src, dest); } void DIO::move(const QList& srcList, PAlbum* const dest) { if (!dest) { return; } instance()->processJob(new IOJobData(IOJobData::MoveFiles, srcList, dest)); } // Rename -------------------------------------------------------------- void DIO::rename(const QUrl& src, const QString& newName, bool overwrite) { if (src.isEmpty() || newName.isEmpty()) { return; } ItemInfo info = ItemInfo::fromUrl(src); instance()->processJob(new IOJobData(IOJobData::Rename, info, newName, overwrite)); } // Delete -------------------------------------------------------------- void DIO::del(const QList& infos, bool useTrash) { instance()->processJob(new IOJobData(useTrash ? IOJobData::Trash : IOJobData::Delete, infos)); } void DIO::del(const ItemInfo& info, bool useTrash) { del(QList() << info, useTrash); } void DIO::del(PAlbum* const album, bool useTrash) { if (!album) { return; } #ifdef Q_OS_WIN AlbumManager::instance()->removeWatchedPAlbums(album); #endif instance()->createJob(new IOJobData(useTrash ? IOJobData::Trash : IOJobData::Delete, album)); } // Restore Trash ------------------------------------------------------- void DIO::restoreTrash(const DTrashItemInfoList& infos) { instance()->createJob(new IOJobData(IOJobData::Restore, infos)); } // Empty Trash --------------------------------------------------------- void DIO::emptyTrash(const DTrashItemInfoList& infos) { instance()->createJob(new IOJobData(IOJobData::Empty, infos)); } // ------------------------------------------------------------------------------------------------ void DIO::processJob(IOJobData* const data) { const int operation = data->operation(); if (operation == IOJobData::CopyImage || operation == IOJobData::MoveImage) { // this is a fast db operation, do here GroupedImagesFinder finder(data->itemInfos()); data->setItemInfos(finder.infos); QStringList filenames; QList ids; foreach (const ItemInfo& info, data->itemInfos()) { filenames << info.name(); ids << info.id(); } ScanController::instance()->hintAtMoveOrCopyOfItems(ids, data->destAlbum(), filenames); } else if (operation == IOJobData::CopyAlbum || operation == IOJobData::MoveAlbum) { ScanController::instance()->hintAtMoveOrCopyOfAlbum(data->srcAlbum(), data->destAlbum()); createJob(data); return; } else if (operation == IOJobData::Delete || operation == IOJobData::Trash) { qCDebug(DIGIKAM_DATABASE_LOG) << "Number of files to be deleted:" << data->sourceUrls().count(); } SidecarFinder finder(data->sourceUrls()); data->setSourceUrls(finder.localFiles); if (operation == IOJobData::Rename) { if (!data->itemInfos().isEmpty()) { ItemInfo info = data->itemInfos().first(); PAlbum* const album = AlbumManager::instance()->findPAlbum(info.albumId()); if (album) { ScanController::instance()->hintAtMoveOrCopyOfItem(info.id(), album, data->destUrl().fileName()); } for (int i = 0 ; i < finder.localFiles.length() ; ++i) { - data->setDestUrl(finder.localFiles.at(i), - QUrl::fromLocalFile(data->destUrl().toLocalFile() + - finder.localFileSuffixes.at(i))); + if (finder.localFileModes.at(i)) + { + data->setDestUrl(finder.localFiles.at(i), + QUrl::fromLocalFile(data->destUrl().toLocalFile() + + finder.localFileSuffixes.at(i))); + } + else + { + QFileInfo info(data->destUrl().toLocalFile()); + + data->setDestUrl(finder.localFiles.at(i), + QUrl::fromLocalFile(info.path() + + QLatin1Char('/') + + info.completeBaseName() + + finder.localFileSuffixes.at(i))); + } } } } createJob(data); } void DIO::createJob(IOJobData* const data) { if (data->sourceUrls().isEmpty()) { delete data; return; } ProgressItem* item = nullptr; QString itemString = getItemString(data); if (!itemString.isEmpty()) { item = ProgressManager::instance()->createProgressItem(itemString, QString(), true, false); item->setTotalItems(data->sourceUrls().count()); data->setProgressId(item->id()); } IOJobsThread* const jobThread = IOJobsManager::instance()->startIOJobs(data); connect(jobThread, SIGNAL(signalOneProccessed(QUrl)), this, SLOT(slotOneProccessed(QUrl))); connect(jobThread, SIGNAL(finished()), this, SLOT(slotResult())); if (data->operation() == IOJobData::Rename) { connect(jobThread, SIGNAL(signalRenameFailed(QUrl)), this, SIGNAL(signalRenameFailed(QUrl))); connect(jobThread, SIGNAL(finished()), this, SIGNAL(signalRenameFinished())); } if (data->operation() == IOJobData::Empty || data->operation() == IOJobData::Restore) { connect(jobThread, SIGNAL(finished()), this, SIGNAL(signalTrashFinished())); } if (item) { connect(item, SIGNAL(progressItemCanceled(ProgressItem*)), jobThread, SLOT(slotCancel())); connect(item, SIGNAL(progressItemCanceled(ProgressItem*)), this, SLOT(slotCancel(ProgressItem*))); } ++m_processingCount; } void DIO::slotResult() { IOJobsThread* const jobThread = dynamic_cast(sender()); if (!jobThread || !jobThread->jobData()) { return; } IOJobData* const data = jobThread->jobData(); if (jobThread->hasErrors() && data->operation() != IOJobData::Rename) { // Pop-up a message about the error. QString errors = jobThread->errorsList().join(QLatin1Char('\n')); DNotificationWrapper(QString(), errors, DigikamApp::instance(), DigikamApp::instance()->windowTitle()); } if (m_processingCount) { --m_processingCount; } slotCancel(getProgressItem(data)); } void DIO::slotOneProccessed(const QUrl& url) { IOJobsThread* const jobThread = dynamic_cast(sender()); if (!jobThread || !jobThread->jobData()) { return; } IOJobData* const data = jobThread->jobData(); const int operation = data->operation(); if (operation == IOJobData::MoveImage) { ItemInfo info = data->findItemInfo(url); if (!info.isNull() && data->destAlbum()) { CoreDbAccess().db()->moveItem(info.albumId(), info.name(), data->destAlbum()->id(), info.name()); } } else if (operation == IOJobData::Delete) { // Mark the images as obsolete and remove them // from their album and from the grouped PAlbum* const album = data->srcAlbum(); if (album && album->fileUrl() == url) { // get all deleted albums CoreDbAccess access; QList albumsToDelete; QList imagesToRemove; addAlbumChildrenToList(albumsToDelete, album); foreach (int albumId, albumsToDelete) { imagesToRemove << access.db()->getItemIDsInAlbum(albumId); } foreach (const qlonglong& imageId, imagesToRemove) { access.db()->removeAllImageRelationsFrom(imageId, DatabaseRelation::Grouped); } access.db()->removeItemsPermanently(imagesToRemove, albumsToDelete); } else { ItemInfo info = data->findItemInfo(url); if (!info.isNull()) { int originalVersionTag = TagsCache::instance()->getOrCreateInternalTag(InternalTagName::originalVersion()); int needTaggingTag = TagsCache::instance()->getOrCreateInternalTag(InternalTagName::needTaggingHistoryGraph()); QList imageIds = CoreDbAccess().db()->getImagesRelatedFrom(info.id(), DatabaseRelation::DerivedFrom); CoreDbAccess access; foreach (const qlonglong& id, imageIds) { access.db()->removeItemTag(id, originalVersionTag); access.db()->addItemTag(id, needTaggingTag); } access.db()->removeAllImageRelationsFrom(info.id(), DatabaseRelation::Grouped); access.db()->removeItemsPermanently(QList() << info.id(), QList() << info.albumId()); } } } else if (operation == IOJobData::Trash) { ItemInfo info = data->findItemInfo(url); if (!info.isNull()) { int originalVersionTag = TagsCache::instance()->getOrCreateInternalTag(InternalTagName::originalVersion()); int needTaggingTag = TagsCache::instance()->getOrCreateInternalTag(InternalTagName::needTaggingHistoryGraph()); QList imageIds = CoreDbAccess().db()->getImagesRelatedFrom(info.id(), DatabaseRelation::DerivedFrom); CoreDbAccess access; foreach (const qlonglong& id, imageIds) { access.db()->removeItemTag(id, originalVersionTag); access.db()->addItemTag(id, needTaggingTag); } access.db()->removeItems(QList() << info.id(), QList() << info.albumId()); } } else if (operation == IOJobData::Rename) { ItemInfo info = data->findItemInfo(url); if (!info.isNull()) { QString oldPath = url.toLocalFile(); QString newName = data->destUrl(url).fileName(); QString newPath = data->destUrl(url).toLocalFile(); if (data->overwrite()) { ThumbsDbAccess().db()->removeByFilePath(newPath); LoadingCacheInterface::fileChanged(newPath, false); CoreDbAccess().db()->deleteItem(info.albumId(), newName); } ThumbsDbAccess().db()->renameByFilePath(oldPath, newPath); // Remove old thumbnails and images from the cache LoadingCacheInterface::fileChanged(oldPath, false); // Rename in ItemInfo and database info.setName(newName); } } // Scan folders for changes if (operation == IOJobData::Delete || operation == IOJobData::Trash || operation == IOJobData::MoveAlbum) { PAlbum* const album = data->srcAlbum(); QString scanPath; if (album) { PAlbum* const parent = dynamic_cast(album->parent()); if (parent) { scanPath = parent->fileUrl().toLocalFile(); } } if (scanPath.isEmpty()) { scanPath = url.adjusted(QUrl::RemoveFilename).toLocalFile(); } ScanController::instance()->scheduleCollectionScanRelaxed(scanPath); } if (operation == IOJobData::CopyImage || operation == IOJobData::CopyAlbum || operation == IOJobData::CopyFiles || operation == IOJobData::MoveImage || operation == IOJobData::MoveAlbum || operation == IOJobData::MoveFiles) { QString scanPath = data->destUrl().toLocalFile(); ScanController::instance()->scheduleCollectionScanRelaxed(scanPath); } if (operation == IOJobData::Restore) { QString scanPath = url.adjusted(QUrl::RemoveFilename).toLocalFile(); ScanController::instance()->scheduleCollectionScanRelaxed(scanPath); } ProgressItem* const item = getProgressItem(data); if (item) { item->advance(1); } } QString DIO::getItemString(IOJobData* const data) const { switch (data->operation()) { case IOJobData::CopyAlbum: return i18n("Copy Album"); case IOJobData::CopyImage: return i18n("Copy Images"); case IOJobData::CopyFiles: return i18n("Copy Files"); case IOJobData::MoveAlbum: return i18n("Move Album"); case IOJobData::MoveImage: return i18n("Move Images"); case IOJobData::MoveFiles: return i18n("Move Files"); case IOJobData::Delete: return i18n("Delete"); case IOJobData::Trash: return i18n("Trash"); case IOJobData::Restore: return i18n("Restore Trash"); case IOJobData::Empty: return i18n("Empty Trash"); default: break; } return QString(); } ProgressItem* DIO::getProgressItem(IOJobData* const data) const { QString itemId = data->getProgressId(); if (itemId.isEmpty()) { return nullptr; } return ProgressManager::instance()->findItembyId(itemId); } void DIO::slotCancel(ProgressItem* item) { if (item) { item->setComplete(); } } void DIO::addAlbumChildrenToList(QList& list, Album* const album) { // simple recursive helper function if (album) { if (!list.contains(album->id())) { list.append(album->id()); } AlbumIterator it(album); while (it.current()) { addAlbumChildrenToList(list, *it); ++it; } } } } // namespace Digikam diff --git a/core/libs/database/utils/ifaces/diofinders.cpp b/core/libs/database/utils/ifaces/diofinders.cpp index 980d55a15e..06b1d05aee 100644 --- a/core/libs/database/utils/ifaces/diofinders.cpp +++ b/core/libs/database/utils/ifaces/diofinders.cpp @@ -1,123 +1,154 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2005-05-17 * Description : low level files management interface - Finder classes * * Copyright (C) 2005 by Renchi Raju * Copyright (C) 2012-2013 by Marcel Wiesweg * Copyright (C) 2015 by Mohamed_Anwer * Copyright (C) 2018 by Maik Qualmann * * 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, 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. * * ============================================================ */ #include "diofinders.h" // Qt includes #include #include // Local includes #include "digikam_debug.h" #include "iteminfo.h" -#include "dmetadata.h" #include "metaenginesettings.h" namespace Digikam { SidecarFinder::SidecarFinder(const QList& files) { // First, the sidecar urls will be added so that they are first copied or renamed. + const QString xmp(QLatin1String(".xmp")); + foreach (const QUrl& url, files) { - QString path(url.toLocalFile()); + QFileInfo info(url.toLocalFile()); - if (!path.endsWith(QLatin1String(".xmp")) && DMetadata::hasSidecar(path)) + if (!info.filePath().endsWith(xmp)) { - localFiles << DMetadata::sidecarUrl(url); - localFileSuffixes << QLatin1String(".xmp"); - qCDebug(DIGIKAM_DATABASE_LOG) << "Detected a sidecar" << localFiles.last(); + QFileInfo extInfo(info.filePath() + xmp); + QFileInfo basInfo(info.path() + + QLatin1Char('/') + + info.completeBaseName() + xmp); + + if (extInfo.exists()) + { + localFiles << QUrl::fromLocalFile(extInfo.filePath()); + localFileModes << true; + localFileSuffixes << xmp; + qCDebug(DIGIKAM_DATABASE_LOG) << "Detected a sidecar" << localFiles.last(); + } + + if (basInfo.exists()) + { + localFiles << QUrl::fromLocalFile(basInfo.filePath()); + localFileModes << false; + localFileSuffixes << xmp; + qCDebug(DIGIKAM_DATABASE_LOG) << "Detected a sidecar" << localFiles.last(); + } } foreach (const QString& ext, MetaEngineSettings::instance()->settings().sidecarExtensions) { QString suffix(QLatin1Char('.') + ext); - QString sidecarPath(url.toLocalFile() + suffix); - QUrl sidecarUrl(QUrl::fromLocalFile(sidecarPath)); - if (QFileInfo::exists(sidecarPath) && !localFiles.contains(sidecarUrl)) + QFileInfo extInfo(info.filePath() + suffix); + QFileInfo basInfo(info.path() + + QLatin1Char('/') + + info.completeBaseName() + suffix); + + if (extInfo.exists() && !localFiles.contains(QUrl::fromLocalFile(extInfo.filePath()))) + { + localFiles << QUrl::fromLocalFile(extInfo.filePath()); + localFileModes << true; + localFileSuffixes << suffix; + qCDebug(DIGIKAM_DATABASE_LOG) << "Detected a sidecar" << localFiles.last(); + } + + if (basInfo.exists() && !localFiles.contains(QUrl::fromLocalFile(basInfo.filePath()))) { - localFiles << sidecarUrl; + localFiles << QUrl::fromLocalFile(basInfo.filePath()); + localFileModes << false; localFileSuffixes << suffix; qCDebug(DIGIKAM_DATABASE_LOG) << "Detected a sidecar" << localFiles.last(); } } } // Now the files, if the user has selected sidecars, these are ignored. foreach (const QUrl& url, files) { if (!localFiles.contains(url)) { localFiles << url; + localFileModes << true; localFileSuffixes << QString(); } } } // ------------------------------------------------------------------------------------------------ /** * TODO: Groups should not be resolved in dio, it should be handled in views. * This is already done for most things except for drag&drop, which is hard :) */ GroupedImagesFinder::GroupedImagesFinder(const QList& source) { QSet ids; foreach (const ItemInfo& info, source) { ids << info.id(); } infos.reserve(source.size()); foreach (const ItemInfo& info, source) { infos << info; if (info.hasGroupedImages()) { foreach (const ItemInfo& groupedImage, info.groupedImages()) { if (ids.contains(groupedImage.id())) { continue; } infos << groupedImage; ids << groupedImage.id(); } } } } } // namespace Digikam diff --git a/core/libs/database/utils/ifaces/diofinders.h b/core/libs/database/utils/ifaces/diofinders.h index e0b870e14f..c62c90278e 100644 --- a/core/libs/database/utils/ifaces/diofinders.h +++ b/core/libs/database/utils/ifaces/diofinders.h @@ -1,73 +1,74 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2005-05-17 * Description : low level files management interface - Finder classes. * * Copyright (C) 2005 by Renchi Raju * Copyright (C) 2012-2013 by Marcel Wiesweg * Copyright (C) 2018 by Maik Qualmann * * 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, 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. * * ============================================================ */ #ifndef DIGIKAM_DIO_FINDERS_H #define DIGIKAM_DIO_FINDERS_H // Qt includes #include #include #include // Local includes #include "digikam_export.h" namespace Digikam { class ItemInfo; class DIGIKAM_GUI_EXPORT SidecarFinder { public: explicit SidecarFinder(const QList& files); public: QList localFiles; + QList localFileModes; QList localFileSuffixes; }; // ----------------------------------------------------------------------------------------- class DIGIKAM_GUI_EXPORT GroupedImagesFinder { public: explicit GroupedImagesFinder(const QList& source); public: QList infos; }; } // namespace Digikam #endif // DIGIKAM_DIO_FINDERS_H diff --git a/core/libs/dialogs/CMakeLists.txt b/core/libs/dialogs/CMakeLists.txt index 894d0abb34..ea04137f1b 100644 --- a/core/libs/dialogs/CMakeLists.txt +++ b/core/libs/dialogs/CMakeLists.txt @@ -1,69 +1,69 @@ # # Copyright (c) 2010-2020 by Gilles Caulier, # Copyright (c) 2015 by Veaceslav Munteanu, # # Redistribution and use is allowed according to the terms of the BSD license. # For details see the accompanying COPYING-CMAKE-SCRIPTS file. APPLY_COMMON_POLICIES() if(LensFun_FOUND) include_directories(${LENSFUN_INCLUDE_DIRS}) endif() include_directories( $ $ $ $ $ $ $ ) if(ENABLE_QWEBENGINE) include_directories($) else() include_directories($) endif() set(libdialogs_SRCS - dprogressdlg.cpp - dbusydlg.cpp - filesaveoptionsdlg.cpp - iccprofileinfodlg.cpp - imagedialog.cpp - infodlg.cpp - libsinfodlg.cpp - rawcameradlg.cpp - dconfigdlg.cpp - dconfigdlgmngr.cpp - dconfigdlgmodels.cpp - dconfigdlgview.cpp - dconfigdlgview_p.cpp - dconfigdlgwidgets.cpp - dmessagebox.cpp - dsplashscreen.cpp - webbrowserdlg.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/dprogressdlg.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/dbusydlg.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/filesaveoptionsdlg.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/iccprofileinfodlg.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/imagedialog.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/infodlg.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/libsinfodlg.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/rawcameradlg.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/dconfigdlg.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/dconfigdlgmngr.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/dconfigdlgmodels.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/dconfigdlgview.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/dconfigdlgview_p.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/dconfigdlgwidgets.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/dmessagebox.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/dsplashscreen.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/webbrowserdlg.cpp ) # Used by digikamcore add_library(core_digikamdialogs_obj OBJECT ${libdialogs_SRCS}) target_compile_definitions(core_digikamdialogs_obj PRIVATE digikamcore_EXPORTS ) set(libdeletedialog_SRCS - deletedialog.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/deletedialog.cpp ) # Used by digikamgui add_library(gui_digikamdeletedialog_obj OBJECT ${libdeletedialog_SRCS}) target_compile_definitions(gui_digikamdeletedialog_obj PRIVATE digikamgui_EXPORTS ) diff --git a/core/libs/dialogs/dconfigdlgview_p.h b/core/libs/dialogs/dconfigdlgview_p.h index de92462a51..e5f2e8e07a 100644 --- a/core/libs/dialogs/dconfigdlgview_p.h +++ b/core/libs/dialogs/dconfigdlgview_p.h @@ -1,299 +1,299 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2009-11-03 * Description : A dialog base class which can handle multiple pages. * * Copyright (C) 2009-2020 by Gilles Caulier * Copyright (C) 2007 by Rafael Fernández López * Copyright (C) 2006 by Tobias Koenig * * 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, 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. * * ============================================================ */ #ifndef DIGIKAM_DCONFIG_DLG_VIEW_PRIVATE_H #define DIGIKAM_DCONFIG_DLG_VIEW_PRIVATE_H #include "dconfigdlgview.h" // Qt includes #include #include #include #include #include #include #include // Local includes #include "dconfigdlgwidgets.h" namespace Digikam { class Q_DECL_HIDDEN DConfigDlgStackedWidget : public QStackedWidget { public: explicit DConfigDlgStackedWidget(QWidget* const parent = nullptr) : QStackedWidget(parent) { } void setMinimumSize(const QSize& size) { mMinimumSize = size; } - QSize minimumSizeHint() const Q_DECL_OVERRIDE + QSize minimumSizeHint() const override { return mMinimumSize.expandedTo(QStackedWidget::minimumSizeHint()); } private: QSize mMinimumSize; }; // --------------------------- class Q_DECL_HIDDEN DConfigDlgViewPrivate { Q_DECLARE_PUBLIC(DConfigDlgView) protected: explicit DConfigDlgViewPrivate(DConfigDlgView* const); void updateTitleWidget(const QModelIndex& index); void updateSelection(); void cleanupPages(); QList collectPages(const QModelIndex& parent = QModelIndex()); DConfigDlgView::FaceType detectAutoFace() const; // private slots void _k_rebuildGui(); void _k_modelChanged(); void _k_dataChanged(const QModelIndex&, const QModelIndex&); void _k_pageSelected(const QItemSelection&, const QItemSelection&); protected: DConfigDlgView* q_ptr; // data QAbstractItemModel* model; DConfigDlgView::FaceType faceType; // gui QGridLayout* layout; DConfigDlgStackedWidget* stack; DConfigDlgTitle* titleWidget; QWidget* defaultWidget; QAbstractItemView* view; private: // cppcheck-suppress unusedPrivateFunction void init(); }; // -------------------------------------------------------------------------------------------- namespace DConfigDlgInternal { class DConfigDlgListViewDelegate; class DConfigDlgListViewProxy; class Q_DECL_HIDDEN DConfigDlgPlainView : public QAbstractItemView { public: explicit DConfigDlgPlainView(QWidget* const parent = nullptr); QModelIndex indexAt(const QPoint& point) const override; void scrollTo(const QModelIndex& index, ScrollHint hint = EnsureVisible) override; QRect visualRect(const QModelIndex& index) const override; protected: QModelIndex moveCursor(QAbstractItemView::CursorAction, Qt::KeyboardModifiers) override; int horizontalOffset() const override; int verticalOffset() const override; bool isIndexHidden(const QModelIndex&) const override; void setSelection(const QRect& , QFlags) override; QRegion visualRegionForSelection(const QItemSelection&) const override; }; // --------------------------- class Q_DECL_HIDDEN DConfigDlgListView : public QListView { Q_OBJECT public: explicit DConfigDlgListView(QWidget* const parent = nullptr); virtual ~DConfigDlgListView(); void setModel(QAbstractItemModel* model) override; private Q_SLOTS: void updateWidth(); }; // --------------------------- class Q_DECL_HIDDEN DConfigDlgTreeView : public QTreeView { Q_OBJECT public: explicit DConfigDlgTreeView(QWidget* const parent = nullptr); void setModel(QAbstractItemModel* model) override; private Q_SLOTS: void updateWidth(); private: void expandItems(const QModelIndex& index = QModelIndex()); }; // --------------------------- class Q_DECL_HIDDEN DConfigDlgTabbedView : public QAbstractItemView { Q_OBJECT public: explicit DConfigDlgTabbedView(QWidget* const parent = nullptr); virtual ~DConfigDlgTabbedView(); void setModel(QAbstractItemModel* model) override; QModelIndex indexAt(const QPoint& point) const override; void scrollTo(const QModelIndex& index, ScrollHint hint = EnsureVisible) override; QRect visualRect(const QModelIndex& index) const override; QSize minimumSizeHint() const override; protected: QModelIndex moveCursor(QAbstractItemView::CursorAction, Qt::KeyboardModifiers) override; int horizontalOffset() const override; int verticalOffset() const override; bool isIndexHidden(const QModelIndex&) const override; void setSelection(const QRect& , QFlags) override; QRegion visualRegionForSelection(const QItemSelection&) const override; private Q_SLOTS: void currentPageChanged(int); void layoutChanged(); void dataChanged(const QModelIndex&, const QModelIndex&, const QVector& roles) override; private: QTabWidget* mTabWidget; }; // --------------------------- class Q_DECL_HIDDEN DConfigDlgListViewDelegate : public QAbstractItemDelegate { Q_OBJECT public: explicit DConfigDlgListViewDelegate(QObject* const parent = nullptr); void paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const override; QSize sizeHint(const QStyleOptionViewItem& option, const QModelIndex& index) const override; private: void drawFocus(QPainter*, const QStyleOptionViewItem&, const QRect&) const; }; // --------------------------- /** * We need this proxy model to map the leaves of a tree-like model * to a one-level list model. */ class Q_DECL_HIDDEN DConfigDlgListViewProxy : public QAbstractProxyModel { Q_OBJECT public: explicit DConfigDlgListViewProxy(QObject* const parent = nullptr); virtual ~DConfigDlgListViewProxy(); int rowCount(const QModelIndex& parent = QModelIndex()) const override; int columnCount(const QModelIndex& parent = QModelIndex()) const override; QModelIndex index(int row, int column, const QModelIndex& parent = QModelIndex()) const override; QModelIndex parent(const QModelIndex&) const override; QVariant data(const QModelIndex& index, int role) const override; QModelIndex mapFromSource(const QModelIndex& index) const override; QModelIndex mapToSource(const QModelIndex& index) const override; public Q_SLOTS: void rebuildMap(); private: void addMapEntry(const QModelIndex&); private: QList mList; }; // --------------------------- class Q_DECL_HIDDEN SelectionModel : public QItemSelectionModel { Q_OBJECT public: SelectionModel(QAbstractItemModel* const model, QObject* const parent); public Q_SLOTS: void clear() override; void select(const QModelIndex& index, QItemSelectionModel::SelectionFlags command) override; void select(const QItemSelection& selection, QItemSelectionModel::SelectionFlags command) override; }; } // namespace DConfigDlgInternal } // namespace Digikam #endif // DIGIKAM_DCONFIG_DLG_VIEW_PRIVATE_H diff --git a/core/libs/dialogs/webbrowserdlg.cpp b/core/libs/dialogs/webbrowserdlg.cpp index e9dd588690..e96c64a97a 100644 --- a/core/libs/dialogs/webbrowserdlg.cpp +++ b/core/libs/dialogs/webbrowserdlg.cpp @@ -1,274 +1,272 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2017-06-21 * Description : a simple web browser dialog based on Qt WebEngine. * * Copyright (C) 2017-2020 by Gilles Caulier * * 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, 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. * * ============================================================ */ #include "webbrowserdlg.h" #include "digikam_config.h" // Qt includes #include #include #include #include #include #include #include #ifdef HAVE_QWEBENGINE # include # include # include # include #else # include #endif // KDE includes #include #include #include // Local includes #include "statusprogressbar.h" #include "searchtextbar.h" #include "dxmlguiwindow.h" #include "digikam_debug.h" namespace Digikam { class Q_DECL_HIDDEN WebBrowserDlg::Private { public: explicit Private() : browser(nullptr), toolbar(nullptr), progressbar(nullptr), searchbar(nullptr) { } public: QUrl home; #ifdef HAVE_QWEBENGINE QWebEngineView* browser; #else QWebView* browser; #endif QToolBar* toolbar; StatusProgressBar* progressbar; SearchTextBar* searchbar; }; WebBrowserDlg::WebBrowserDlg(const QUrl& url, QWidget* const parent, bool hideDeskBrowser) : QDialog(parent), d(new Private) { setModal(false); d->home = url; #ifdef HAVE_QWEBENGINE d->browser = new QWebEngineView(this); d->browser->page()->profile()->cookieStore()->deleteAllCookies(); #else d->browser = new QWebView(this); d->browser->settings()->setAttribute(QWebSettings::LocalStorageEnabled, true); d->browser->page()->networkAccessManager()->setCookieJar(new QNetworkCookieJar()); #endif // -------------------------- d->toolbar = new QToolBar(this); d->toolbar->setToolButtonStyle(Qt::ToolButtonTextBesideIcon); #ifdef HAVE_QWEBENGINE d->toolbar->addAction(d->browser->pageAction(QWebEnginePage::Back)); d->toolbar->addAction(d->browser->pageAction(QWebEnginePage::Forward)); d->toolbar->addAction(d->browser->pageAction(QWebEnginePage::Reload)); d->toolbar->addAction(d->browser->pageAction(QWebEnginePage::Stop)); #else d->toolbar->addAction(d->browser->pageAction(QWebPage::Back)); d->toolbar->addAction(d->browser->pageAction(QWebPage::Forward)); d->toolbar->addAction(d->browser->pageAction(QWebPage::Reload)); d->toolbar->addAction(d->browser->pageAction(QWebPage::Stop)); #endif QAction* const gohome = new QAction(QIcon::fromTheme(QLatin1String("go-home")), i18n("Home"), this); gohome->setToolTip(i18n("Go back to Home page")); d->toolbar->addAction(gohome); QAction* const deskweb = new QAction(QIcon::fromTheme(QLatin1String("internet-web-browser")), i18n("Desktop Browser"), this); deskweb->setToolTip(i18n("Open Home page with default desktop Web browser")); if (!hideDeskBrowser) { d->toolbar->addAction(deskweb); } // -------------------------- d->searchbar = new SearchTextBar(this, QLatin1String("WebBrowserDlgSearchBar")); d->searchbar->setHighlightOnResult(true); d->progressbar = new StatusProgressBar(this); d->progressbar->setProgressTotalSteps(100); d->progressbar->setAlignment(Qt::AlignLeft); d->progressbar->setNotify(false); // ---------------------- QGridLayout* const grid = new QGridLayout(this); grid->setSpacing(QApplication::style()->pixelMetric(QStyle::PM_DefaultLayoutSpacing)); grid->addWidget(d->toolbar, 0, 0, 1, 1); grid->addWidget(d->searchbar, 0, 2, 1, 1); grid->addWidget(d->browser, 1, 0, 1, 3); grid->addWidget(d->progressbar, 2, 0, 1, 3); grid->setColumnStretch(1, 10); grid->setRowStretch(1, 10); setLayout(grid); // ---------------------- /* -#if QT_VERSION >= 0x050700 connect(d->browser, SIGNAL(iconChanged(const QIcon&)), this, SLOT(slotIconChanged(const QIcon&))); -#endif */ connect(d->browser, SIGNAL(titleChanged(QString)), this, SLOT(slotTitleChanged(QString))); connect(d->browser, SIGNAL(urlChanged(QUrl)), this, SLOT(slotUrlChanged(QUrl))); connect(d->browser, SIGNAL(loadStarted()), this, SLOT(slotLoadingStarted())); connect(d->browser, SIGNAL(loadFinished(bool)), this, SLOT(slotLoadingFinished(bool))); connect(d->searchbar, SIGNAL(signalSearchTextSettings(SearchTextSettings)), this, SLOT(slotSearchTextChanged(SearchTextSettings))); connect(d->browser, SIGNAL(loadProgress(int)), d->progressbar, SLOT(setProgressValue(int))); connect(gohome, SIGNAL(triggered()), this, SLOT(slotGoHome())); connect(deskweb, SIGNAL(triggered()), this, SLOT(slotDesktopWebBrowser())); // ---------------------- KConfigGroup group = KSharedConfig::openConfig()->group("WebBrowserDlg"); winId(); windowHandle()->resize(800, 600); DXmlGuiWindow::restoreWindowSize(windowHandle(), group); resize(windowHandle()->size()); slotGoHome(); } WebBrowserDlg::~WebBrowserDlg() { delete d; } void WebBrowserDlg::closeEvent(QCloseEvent* e) { KConfigGroup group = KSharedConfig::openConfig()->group(QLatin1String("WebBrowserDlg")); DXmlGuiWindow::saveWindowSize(windowHandle(), group); emit closeView(false); e->accept(); } void WebBrowserDlg::slotUrlChanged(const QUrl& url) { d->progressbar->setText(url.toString()); emit urlChanged(url); } void WebBrowserDlg::slotTitleChanged(const QString& title) { setWindowTitle(title); } void WebBrowserDlg::slotIconChanged(const QIcon& icon) { setWindowIcon(icon); } void WebBrowserDlg::slotLoadingStarted() { d->progressbar->setProgressBarMode(StatusProgressBar::ProgressBarMode); } void WebBrowserDlg::slotLoadingFinished(bool b) { QString curUrl = d->browser->url().toString(); d->progressbar->setProgressBarMode(StatusProgressBar::TextMode, curUrl); if (!b) { d->progressbar->setText(i18n("Cannot load page %1", curUrl)); } } void WebBrowserDlg::slotSearchTextChanged(const SearchTextSettings& settings) { #ifdef HAVE_QWEBENGINE d->browser->findText(settings.text, (settings.caseSensitive == Qt::CaseSensitive) ? QWebEnginePage::FindCaseSensitively : QWebEnginePage::FindFlags(), [this](bool found) { d->searchbar->slotSearchResult(found); }); #else bool found = d->browser->findText( settings.text, (settings.caseSensitive == Qt::CaseInsensitive) ? QWebPage::FindCaseSensitively : QWebPage::FindFlags()); d->searchbar->slotSearchResult(found); #endif } void WebBrowserDlg::slotGoHome() { d->browser->setUrl(d->home); } void WebBrowserDlg::slotDesktopWebBrowser() { QDesktopServices::openUrl(d->home); } } // namespace Digikam diff --git a/core/libs/dimg/CMakeLists.txt b/core/libs/dimg/CMakeLists.txt index ee375c53b7..b24b3ead22 100644 --- a/core/libs/dimg/CMakeLists.txt +++ b/core/libs/dimg/CMakeLists.txt @@ -1,239 +1,258 @@ # # Copyright (c) 2010-2020 by Gilles Caulier, # Copyright (c) 2015 by Veaceslav Munteanu, # # Redistribution and use is allowed according to the terms of the BSD license. # For details see the accompanying COPYING-CMAKE-SCRIPTS file. APPLY_COMMON_POLICIES() kde_enable_exceptions() # Boost uses operator names (and, not, ...) string(REPLACE "-fno-operator-names" "" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}") set(libdimg_SRCS - dimg.cpp - dimg_bitsops.cpp - dimg_colors.cpp - dimg_copy.cpp - dimg_data.cpp - dimg_fileio.cpp - dimg_metadata.cpp - dimg_props.cpp - dimg_qimage.cpp - dimg_qpixmap.cpp - dimg_scale.cpp - dimg_transform.cpp - - color/dcolor.cpp - color/dcolorcomposer.cpp - - history/dimagehistory.cpp - history/filteraction.cpp - history/historyimageid.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/dimg.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/dimg_bitsops.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/dimg_colors.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/dimg_copy.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/dimg_data.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/dimg_fileio.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/dimg_metadata.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/dimg_props.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/dimg_qimage.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/dimg_qpixmap.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/dimg_scale.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/dimg_transform.cpp + + ${CMAKE_CURRENT_SOURCE_DIR}/color/dcolor.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/color/dcolorcomposer.cpp + + ${CMAKE_CURRENT_SOURCE_DIR}/history/dimagehistory.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/history/filteraction.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/history/historyimageid.cpp ) set(libdimgfilters_SRCS - filters/dimgbuiltinfilter.cpp - filters/dimgthreadedfilter.cpp - filters/dimgthreadedanalyser.cpp - filters/dimgfiltermanager.cpp - filters/dimgfiltergenerator.cpp - filters/dpixelsaliasfilter.cpp - filters/filteractionfilter.cpp - filters/randomnumbergenerator.cpp - filters/raw/rawprocessingfilter.cpp - filters/raw/drawdecoding.cpp - filters/decorate/borderfilter.cpp - filters/decorate/bordersettings.cpp - filters/decorate/texturefilter.cpp - filters/film/filmfilter.cpp - filters/fx/blurfilter.cpp - filters/fx/blurfxfilter.cpp - filters/fx/colorfxfilter.cpp - filters/fx/colorfxsettings.cpp - filters/fx/distortionfxfilter.cpp - filters/fx/charcoalfilter.cpp - filters/fx/embossfilter.cpp - filters/fx/filmgrainfilter.cpp - filters/fx/filmgrainsettings.cpp - filters/fx/invertfilter.cpp - filters/fx/oilpaintfilter.cpp - filters/fx/raindropfilter.cpp - filters/auto/autolevelsfilter.cpp - filters/auto/autoexpofilter.cpp - filters/auto/equalizefilter.cpp - filters/auto/stretchfilter.cpp - filters/auto/normalizefilter.cpp - filters/cb/cbfilter.cpp - filters/cb/cbsettings.cpp - filters/bcg/bcgfilter.cpp - filters/bcg/bcgsettings.cpp - filters/bw/bwsepiafilter.cpp - filters/bw/bwsepiasettings.cpp - filters/bw/tonalityfilter.cpp - filters/bw/infraredfilter.cpp - filters/bw/mixerfilter.cpp - filters/bw/mixersettings.cpp - filters/hsl/hslfilter.cpp - filters/hsl/hslsettings.cpp - filters/hsl/hspreviewwidget.cpp - filters/icc/iccmanager.cpp - filters/icc/iccprofile.cpp - filters/icc/iccprofilesettings.cpp - filters/icc/icctransform.cpp - filters/icc/icctransformfilter.cpp - filters/icc/iccsettingscontainer.cpp - filters/icc/iccsettings.cpp - filters/icc/digikam-lcms.cpp - filters/lc/localcontrastfilter.cpp - filters/lc/localcontrastsettings.cpp - filters/lc/localcontrastcontainer.cpp - filters/nr/nrfilter.cpp - filters/nr/nrestimate.cpp - filters/nr/nrsettings.cpp - filters/sharp/sharpenfilter.cpp - filters/sharp/unsharpmaskfilter.cpp - filters/sharp/sharpsettings.cpp - filters/levels/imagelevels.cpp - filters/levels/levelsfilter.cpp - filters/levels/imagehistogram.cpp - filters/levels/histogrambox.cpp - filters/levels/histogramwidget.cpp - filters/levels/histogrampainter.cpp - filters/curves/curvescontainer.cpp - filters/curves/imagecurves.cpp - filters/curves/curvesfilter.cpp - filters/curves/curvessettings.cpp - filters/curves/curveswidget.cpp - filters/curves/curvesbox.cpp - filters/wb/wbcontainer.cpp - filters/wb/wbfilter.cpp - filters/wb/wbsettings.cpp - filters/transform/freerotationfilter.cpp - filters/transform/freerotationsettings.cpp - filters/transform/shearfilter.cpp - filters/transform/autocrop.cpp - filters/greycstoration/greycstorationfilter.cpp - filters/greycstoration/greycstorationsettings.cpp - filters/lens/antivignettingfilter.cpp - filters/lens/antivignettingsettings.cpp - filters/lens/lensdistortionfilter.cpp - filters/lens/lensdistortionpixelaccess.cpp - filters/redeye/redeyecorrectionfilter.cpp - filters/redeye/redeyecorrectionsettings.cpp - filters/redeye/redeyecorrectioncontainer.cpp - filters/imgqsort/imagequalitycontainer.cpp - filters/imgqsort/imagequalitysettings.cpp - filters/imgqsort/imagequalityparser.cpp - filters/imgqsort/imagequalityparser_blur.cpp - filters/imgqsort/imagequalityparser_exposure.cpp - filters/imgqsort/imagequalityparser_noise.cpp - filters/imgqsort/imagequalityparser_compression.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/filters/dimgbuiltinfilter.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/filters/dimgthreadedfilter.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/filters/dimgthreadedanalyser.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/filters/dimgfiltermanager.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/filters/dimgfiltergenerator.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/filters/dpixelsaliasfilter.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/filters/filteractionfilter.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/filters/randomnumbergenerator.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/filters/raw/rawprocessingfilter.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/filters/raw/drawdecoding.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/filters/decorate/borderfilter.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/filters/decorate/bordersettings.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/filters/decorate/texturefilter.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/filters/film/filmfilter.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/filters/fx/blurfilter.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/filters/fx/blurfxfilter.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/filters/fx/colorfxfilter.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/filters/fx/colorfxsettings.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/filters/fx/distortionfxfilter.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/filters/fx/charcoalfilter.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/filters/fx/embossfilter.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/filters/fx/filmgrainfilter.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/filters/fx/filmgrainsettings.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/filters/fx/invertfilter.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/filters/fx/oilpaintfilter.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/filters/fx/raindropfilter.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/filters/auto/autolevelsfilter.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/filters/auto/autoexpofilter.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/filters/auto/equalizefilter.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/filters/auto/stretchfilter.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/filters/auto/normalizefilter.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/filters/cb/cbfilter.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/filters/cb/cbsettings.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/filters/bcg/bcgfilter.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/filters/bcg/bcgsettings.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/filters/bw/bwsepiafilter.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/filters/bw/bwsepiasettings.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/filters/bw/tonalityfilter.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/filters/bw/infraredfilter.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/filters/bw/mixerfilter.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/filters/bw/mixersettings.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/filters/hsl/hslfilter.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/filters/hsl/hslsettings.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/filters/hsl/hspreviewwidget.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/filters/icc/iccmanager.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/filters/icc/iccprofile.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/filters/icc/iccprofilesettings.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/filters/icc/icctransform.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/filters/icc/icctransformfilter.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/filters/icc/iccsettingscontainer.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/filters/icc/iccsettings.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/filters/icc/digikam-lcms.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/filters/lc/localcontrastfilter.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/filters/lc/localcontrastsettings.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/filters/lc/localcontrastcontainer.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/filters/nr/nrfilter.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/filters/nr/nrestimate.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/filters/nr/nrsettings.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/filters/sharp/sharpenfilter.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/filters/sharp/unsharpmaskfilter.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/filters/sharp/sharpsettings.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/filters/levels/imagelevels.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/filters/levels/levelsfilter.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/filters/levels/imagehistogram.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/filters/levels/histogrambox.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/filters/levels/histogramwidget.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/filters/levels/histogrampainter.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/filters/curves/curvescontainer.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/filters/curves/imagecurves.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/filters/curves/curvesfilter.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/filters/curves/curvessettings.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/filters/curves/curveswidget.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/filters/curves/curvesbox.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/filters/wb/wbcontainer.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/filters/wb/wbfilter.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/filters/wb/wbsettings.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/filters/transform/freerotationfilter.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/filters/transform/freerotationsettings.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/filters/transform/shearfilter.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/filters/transform/autocrop.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/filters/greycstoration/greycstorationfilter.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/filters/greycstoration/greycstorationsettings.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/filters/lens/antivignettingfilter.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/filters/lens/antivignettingsettings.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/filters/lens/lensdistortionfilter.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/filters/lens/lensdistortionpixelaccess.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/filters/redeye/redeyecorrectionfilter.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/filters/redeye/redeyecorrectionsettings.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/filters/redeye/redeyecorrectioncontainer.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/filters/imgqsort/imagequalitycontainer.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/filters/imgqsort/imagequalitysettings.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/filters/imgqsort/imagequalityparser.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/filters/imgqsort/imagequalityparser_blur.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/filters/imgqsort/imagequalityparser_exposure.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/filters/imgqsort/imagequalityparser_noise.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/filters/imgqsort/imagequalityparser_compression.cpp ) # ================================================================================================== # get the gcc version # CImg.h version 1.2.8 do not compile fine with gcc 4.3.x # See bug #163118: digikam-0.9.4_beta5 compilation hangs with gcc 4.3 # Using -fno-tree-pre is work around this problem. # TODO is this hack anymore required? if(CMAKE_COMPILER_IS_GNUCXX) + exec_program(${CMAKE_CXX_COMPILER} ARGS ${CMAKE_CXX_COMPILER_ARG1} -dumpversion OUTPUT_VARIABLE GCC_VERSION) if (${GCC_VERSION} VERSION_LESS "4.3.0") message(STATUS "Adjusting compilation flags for GCC version (${GCC_VERSION} )") add_definitions(-fno-tree-pre) endif() + endif() if(Lqr-1_FOUND) + set(libdimgfilters_SRCS ${libdimgfilters_SRCS} filters/transform/contentawarefilter.cpp ) + include_directories(${LQR-1_INCLUDE_DIRS}) + endif() if(LensFun_FOUND) + set(libdimgfilters_SRCS ${libdimgfilters_SRCS} - filters/lens/lensfunfilter.cpp - filters/lens/lensfuncameraselector.cpp - filters/lens/lensfuniface.cpp - filters/lens/lensfunsettings.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/filters/lens/lensfunfilter.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/filters/lens/lensfuncameraselector.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/filters/lens/lensfuniface.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/filters/lens/lensfunsettings.cpp ) + include_directories(${LENSFUN_INCLUDE_DIRS}) + endif() if(Eigen3_FOUND) + set(libdimgfilters_SRCS ${libdimgfilters_SRCS} - filters/sharp/refocusfilter.cpp - filters/sharp/refocusmatrix.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/filters/sharp/refocusfilter.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/filters/sharp/refocusmatrix.cpp ) + include_directories(${EIGEN3_INCLUDE_DIR}) + endif() set(libdimgloaders_SRCS - loaders/dimgloader.cpp - loaders/jpegsettings.cpp - loaders/pngsettings.cpp - loaders/tiffsettings.cpp - loaders/pgfsettings.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/loaders/dimgloader.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/loaders/jpegsettings.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/loaders/pngsettings.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/loaders/tiffsettings.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/loaders/pgfsettings.cpp ) # JPEG2000 support if(Jasper_FOUND) + set(libdimgloaders_SRCS ${libdimgloaders_SRCS} - loaders/jp2ksettings.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/loaders/jp2ksettings.cpp ) + endif() # HEIF support if(X265_FOUND) + set(libdimgloaders_SRCS ${libdimgloaders_SRCS} - loaders/heifsettings.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/loaders/heifsettings.cpp ) + endif() include_directories( $ $ $ $ $ $ ) if(X11_FOUND) + include_directories($) + endif() # Don't process automoc on LibCImg headers file(GLOB_RECURSE cimg_headers ${CMAKE_CURRENT_SOURCE_DIR}/filters/greycstoration/cimg/*.h) foreach(_file ${cimg_headers}) + set_property(SOURCE ${_file} PROPERTY SKIP_AUTOMOC ON) + endforeach() # Used by digikamcore add_library(core_dimg_obj OBJECT ${libdimgloaders_SRCS} ${libdimgfilters_SRCS} ${libdimg_SRCS} ) target_compile_definitions(core_dimg_obj PRIVATE digikamcore_EXPORTS ) diff --git a/core/libs/dimg/dimg_data.cpp b/core/libs/dimg/dimg_data.cpp index bee750349a..c59310deb0 100644 --- a/core/libs/dimg/dimg_data.cpp +++ b/core/libs/dimg/dimg_data.cpp @@ -1,209 +1,210 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2005-06-14 * Description : digiKam 8/16 bits image management API. * Data management. * * Copyright (C) 2005-2020 by Gilles Caulier * Copyright (C) 2006-2013 by Marcel Wiesweg * * 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, 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. * * ============================================================ */ #include "dimg_p.h" namespace Digikam { DImg& DImg::operator=(const DImg& image) { m_priv = image.m_priv; return *this; } bool DImg::operator==(const DImg& image) const { return (m_priv == image.m_priv); } void DImg::reset() { m_priv = new Private; } void DImg::detach() { // are we being shared? if (m_priv->ref == 1) { return; } QExplicitlySharedDataPointer old(m_priv.data()); m_priv = new Private; copyImageData(old); copyMetaData(old); if (old->data) { size_t size = allocateData(); memcpy(m_priv->data, old->data, size); } } void DImg::putImageData(uint width, uint height, bool sixteenBit, bool alpha, uchar* const data, bool copyData) { // set image data, metadata is untouched bool null = (width == 0) || (height == 0); // allocateData, or code below will set null to false setImageData(true, width, height, sixteenBit, alpha); // replace data delete [] m_priv->data; + m_priv->data = nullptr; if (null) { // image is null - no data - m_priv->data = nullptr; + return; } else if (copyData) { size_t size = allocateData(); - if (data) + if (m_priv->data && data) { memcpy(m_priv->data, data, size); } } else { if (data) { m_priv->data = data; m_priv->null = false; } else { allocateData(); } } } void DImg::putImageData(uchar* const data, bool copyData) { if (!data) { delete [] m_priv->data; m_priv->data = nullptr; m_priv->null = true; } else if (copyData) { memcpy(m_priv->data, data, numBytes()); } else { m_priv->data = data; } } void DImg::resetMetaData() { QMutexLocker lock(&m_priv->mutex); m_priv->attributes.clear(); m_priv->embeddedText.clear(); m_priv->metaData = MetaEngineData(); } uchar* DImg::stripImageData() { uchar* const data = m_priv->data; m_priv->data = nullptr; m_priv->null = true; return data; } void DImg::copyMetaData(const QExplicitlySharedDataPointer& src) { QMutexLocker lock(&m_priv->mutex); m_priv->metaData = src->metaData; m_priv->attributes = src->attributes; m_priv->embeddedText = src->embeddedText; m_priv->iccProfile = src->iccProfile; m_priv->imageHistory = src->imageHistory; // FIXME: what about sharing and deleting lanczos_func? } void DImg::copyImageData(const QExplicitlySharedDataPointer& src) { setImageData(src->null, src->width, src->height, src->sixteenBit, src->alpha); } size_t DImg::allocateData() const { quint64 size = (quint64)m_priv->width * - (quint64)m_priv->height * - (quint64)(m_priv->sixteenBit ? 8 : 4); + (quint64)m_priv->height * + (quint64)(m_priv->sixteenBit ? 8 : 4); - if (size > ((quint64)std::numeric_limits::max())) + if (size >= std::numeric_limits::max()) { m_priv->null = true; return 0; } m_priv->data = DImgLoader::new_failureTolerant(size); if (!m_priv->data) { m_priv->null = true; return 0; } m_priv->null = false; return size; } void DImg::setImageDimension(uint width, uint height) { m_priv->width = width; m_priv->height = height; } void DImg::setImageData(bool null, uint width, uint height, bool sixteenBit, bool alpha) { m_priv->null = null; m_priv->width = width; m_priv->height = height; m_priv->alpha = alpha; m_priv->sixteenBit = sixteenBit; } } // namespace Digikam diff --git a/core/libs/dimg/loaders/dimgloader.h b/core/libs/dimg/loaders/dimgloader.h index 2c213d8e48..04a764c1a5 100644 --- a/core/libs/dimg/loaders/dimgloader.h +++ b/core/libs/dimg/loaders/dimgloader.h @@ -1,207 +1,206 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2005-06-14 * Description : DImg image loader interface * * Copyright (C) 2005 by Renchi Raju * Copyright (C) 2005-2020 by Gilles Caulier * * 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, 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. * * ============================================================ */ #ifndef DIGIKAM_DIMG_LOADER_H #define DIGIKAM_DIMG_LOADER_H // C++ includes #include // Qt includes #include #include #include // Local includes #include "digikam_debug.h" #include "digikam_export.h" #include "dimg.h" namespace Digikam { class DImgLoaderObserver; class DMetadata; class DIGIKAM_EXPORT DImgLoader { public: /** * This is the list of loading modes usable by DImg image plugins */ enum LoadFlag { /// Load image information without image data LoadItemInfo = 1, ///< Image info as width and height LoadMetadata = 2, ///< Image metadata LoadICCData = 4, ///< Image color profile LoadImageData = 8, ///< Full image data LoadUniqueHash = 16, ///< Image unique hash LoadImageHistory = 32, ///< Image version history /// Special mode to load reduced image data LoadPreview = 64, ///< Load embedded preview image instead full size image /// Helper to load all information, metadata and full image. LoadAll = LoadItemInfo | LoadMetadata | LoadICCData | LoadImageData | LoadUniqueHash | LoadImageHistory }; Q_DECLARE_FLAGS(LoadFlags, LoadFlag) public: void setLoadFlags(LoadFlags flags); virtual ~DImgLoader(); virtual bool load(const QString& filePath, DImgLoaderObserver* const observer) = 0; virtual bool save(const QString& filePath, DImgLoaderObserver* const observer) = 0; virtual bool hasLoadedData() const; virtual bool hasAlpha() const = 0; virtual bool sixteenBit() const = 0; virtual bool isReadOnly() const = 0; static unsigned char* new_failureTolerant(size_t unsecureSize); static unsigned char* new_failureTolerant(quint64 w, quint64 h, uint typesPerPixel); static unsigned short* new_short_failureTolerant(size_t unsecureSize); static unsigned short* new_short_failureTolerant(quint64 w, quint64 h, uint typesPerPixel); /** * Value returned : -1 : unsupported platform * 0 : parse failure from supported platform * 1 : parse done with success from supported platform */ static qint64 checkAllocation(qint64 fullSize); template static Type* new_failureTolerant(size_t unsecureSize); template static Type* new_failureTolerant(uint w, uint h, uint typesPerPixel); protected: explicit DImgLoader(DImg* const image); unsigned char*& imageData(); unsigned int& imageWidth(); unsigned int& imageHeight(); bool imageHasAlpha() const; bool imageSixteenBit() const; quint64 imageNumBytes() const; int imageBitsDepth() const; int imageBytesDepth() const; void imageSetIccProfile(const IccProfile& profile); QVariant imageGetAttribute(const QString& key) const; void imageSetAttribute(const QString& key, const QVariant& value); QMap& imageEmbeddedText() const; QString imageGetEmbbededText(const QString& key) const; void imageSetEmbbededText(const QString& key, const QString& text); void loadingFailed(); bool checkExifWorkingColorSpace() const; void purgeExifWorkingColorSpace(); void storeColorProfileInMetadata(); virtual bool readMetadata(const QString& filePath); virtual bool saveMetadata(const QString& filePath); virtual int granularity(DImgLoaderObserver* const observer, int total, float progressSlice = 1.0); protected: DImg* m_image; LoadFlags m_loadFlags; private: DImgLoader(); }; // --------------------------------------------------------------------------------------------------- /** * Allows safe multiplication of requested pixel number and bytes per pixel, avoiding particularly * 32bit overflow and exceeding the size_t type */ template Q_INLINE_TEMPLATE Type* DImgLoader::new_failureTolerant(uint w, uint h, uint typesPerPixel) { quint64 requested = (quint64)w * (quint64)h * (quint64)typesPerPixel; - quint64 maximum = std::numeric_limits::max(); - if (requested > maximum) + if (requested >= std::numeric_limits::max()) { qCCritical(DIGIKAM_DIMG_LOG) << "Requested memory of" << requested * quint64(sizeof(Type)) << "is larger than size_t supported by platform."; return nullptr; } return new_failureTolerant(requested); } template Q_INLINE_TEMPLATE Type* DImgLoader::new_failureTolerant(size_t size) { qint64 res = checkAllocation(size); switch (res) { case 0: // parse failure from supported platform return nullptr; break; case -1: // unsupported platform // We will try to continue to allocate break; default: // parse done with success from supported platform break; } Type* const reserved = new (std::nothrow) Type[size]; if (!reserved) { qCCritical(DIGIKAM_DIMG_LOG) << "Failed to allocate chunk of memory of size" << size; } return reserved; } Q_DECLARE_OPERATORS_FOR_FLAGS(DImgLoader::LoadFlags) } // namespace Digikam #endif // DIGIKAM_DIMG_LOADER_H diff --git a/core/libs/dngwriter/CMakeLists.txt b/core/libs/dngwriter/CMakeLists.txt index c8686387e2..a57adaf596 100644 --- a/core/libs/dngwriter/CMakeLists.txt +++ b/core/libs/dngwriter/CMakeLists.txt @@ -1,204 +1,204 @@ # # Copyright (c) 2010-2020, Gilles Caulier, # # Redistribution and use is allowed according to the terms of the BSD license. # For details see the accompanying COPYING-CMAKE-SCRIPTS file. APPLY_COMMON_POLICIES() # DNG SDK and XMP SDK use C++ exceptions kde_enable_exceptions() include_directories( $ $ ${EXPAT_INCLUDE_DIR} - extra/md5 - extra/dng_sdk - extra/xmp_sdk/XMPCore - extra/xmp_sdk/common - extra/xmp_sdk/include - extra/xmp_sdk/include/client-glue + ${CMAKE_CURRENT_SOURCE_DIR}/extra/md5 + ${CMAKE_CURRENT_SOURCE_DIR}/extra/dng_sdk + ${CMAKE_CURRENT_SOURCE_DIR}/extra/xmp_sdk/XMPCore + ${CMAKE_CURRENT_SOURCE_DIR}/extra/xmp_sdk/common + ${CMAKE_CURRENT_SOURCE_DIR}/extra/xmp_sdk/include + ${CMAKE_CURRENT_SOURCE_DIR}/extra/xmp_sdk/include/client-glue ) #------------------------------------------------------------------------------------ set(libmd5_SRCS extra/md5/XMP_MD5.cpp) # Used by digikamcore add_library(core_libmd5_obj OBJECT ${libmd5_SRCS}) target_compile_definitions(core_libmd5_obj PRIVATE digikamcore_EXPORTS ) # Disable warnings: we will never touch this code. # Adjust flag for static lib and 64 bits computers using -fPIC for GCC compiler (bug: #269903) if(MSVC) target_compile_options(core_libmd5_obj PRIVATE /w) else() target_compile_options(core_libmd5_obj PRIVATE -w -fPIC) endif() #------------------------------------------------------------------------------------ set(libxmp_SRCS - extra/xmp_sdk/common/XML_Node.cpp - extra/xmp_sdk/common/UnicodeConversions.cpp - extra/xmp_sdk/XMPCore/XMPCore_Impl.cpp - extra/xmp_sdk/XMPCore/WXMPIterator.cpp - extra/xmp_sdk/XMPCore/WXMPMeta.cpp - extra/xmp_sdk/XMPCore/WXMPUtils.cpp - extra/xmp_sdk/XMPCore/XMPIterator.cpp - extra/xmp_sdk/XMPCore/XMPMeta-GetSet.cpp - extra/xmp_sdk/XMPCore/XMPMeta-Parse.cpp - extra/xmp_sdk/XMPCore/XMPMeta-Serialize.cpp - extra/xmp_sdk/XMPCore/XMPMeta.cpp - extra/xmp_sdk/XMPCore/XMPUtils-FileInfo.cpp - extra/xmp_sdk/XMPCore/XMPUtils.cpp - extra/xmp_sdk/XMPCore/ExpatAdapter.cpp - extra/xmp_sdk/XMPCore/ParseRDF.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/extra/xmp_sdk/common/XML_Node.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/extra/xmp_sdk/common/UnicodeConversions.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/extra/xmp_sdk/XMPCore/XMPCore_Impl.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/extra/xmp_sdk/XMPCore/WXMPIterator.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/extra/xmp_sdk/XMPCore/WXMPMeta.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/extra/xmp_sdk/XMPCore/WXMPUtils.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/extra/xmp_sdk/XMPCore/XMPIterator.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/extra/xmp_sdk/XMPCore/XMPMeta-GetSet.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/extra/xmp_sdk/XMPCore/XMPMeta-Parse.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/extra/xmp_sdk/XMPCore/XMPMeta-Serialize.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/extra/xmp_sdk/XMPCore/XMPMeta.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/extra/xmp_sdk/XMPCore/XMPUtils-FileInfo.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/extra/xmp_sdk/XMPCore/XMPUtils.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/extra/xmp_sdk/XMPCore/ExpatAdapter.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/extra/xmp_sdk/XMPCore/ParseRDF.cpp ) # Used by digikamcore add_library(core_libxmp_obj OBJECT ${libxmp_SRCS}) target_compile_definitions(core_libxmp_obj PRIVATE digikamcore_EXPORTS ) # Disable warnings: we will never touch this code. # Adjust flag for static lib and 64 bits computers using -fPIC for GCC compiler (bug: #269903) if(MSVC) target_compile_options(core_libxmp_obj PRIVATE /w) else() target_compile_options(core_libxmp_obj PRIVATE -w -fPIC) endif() #------------------------------------------------------------------------------------ set(libdng_SRCS - extra/dng_sdk/dng_1d_function.cpp - extra/dng_sdk/dng_date_time.cpp - extra/dng_sdk/dng_ifd.cpp - extra/dng_sdk/dng_memory.cpp - extra/dng_sdk/dng_point.cpp - extra/dng_sdk/dng_simple_image.cpp - extra/dng_sdk/dng_utils.cpp - extra/dng_sdk/dng_1d_table.cpp - extra/dng_sdk/dng_exceptions.cpp - extra/dng_sdk/dng_image.cpp - extra/dng_sdk/dng_memory_stream.cpp - extra/dng_sdk/dng_rational.cpp - extra/dng_sdk/dng_spline.cpp - extra/dng_sdk/dng_xmp.cpp - extra/dng_sdk/dng_abort_sniffer.cpp - extra/dng_sdk/dng_exif.cpp - extra/dng_sdk/dng_image_writer.cpp - extra/dng_sdk/dng_preview.cpp - extra/dng_sdk/dng_misc_opcodes.cpp - extra/dng_sdk/dng_mosaic_info.cpp - extra/dng_sdk/dng_read_image.cpp - extra/dng_sdk/dng_stream.cpp - extra/dng_sdk/dng_xmp_sdk.cpp - extra/dng_sdk/dng_area_task.cpp - extra/dng_sdk/dng_file_stream.cpp - extra/dng_sdk/dng_info.cpp - extra/dng_sdk/dng_mutex.cpp - extra/dng_sdk/dng_rect.cpp - extra/dng_sdk/dng_string.cpp - extra/dng_sdk/dng_xy_coord.cpp - extra/dng_sdk/dng_bottlenecks.cpp - extra/dng_sdk/dng_bad_pixels.cpp - extra/dng_sdk/dng_filter_task.cpp - extra/dng_sdk/dng_iptc.cpp - extra/dng_sdk/dng_negative.cpp - extra/dng_sdk/dng_reference.cpp - extra/dng_sdk/dng_string_list.cpp - extra/dng_sdk/dng_camera_profile.cpp - extra/dng_sdk/dng_fingerprint.cpp - extra/dng_sdk/dng_lens_correction.cpp - extra/dng_sdk/dng_linearization_info.cpp - extra/dng_sdk/dng_opcode_list.cpp - extra/dng_sdk/dng_opcodes.cpp - extra/dng_sdk/dng_orientation.cpp - extra/dng_sdk/dng_render.cpp - extra/dng_sdk/dng_tag_types.cpp - extra/dng_sdk/dng_color_space.cpp - extra/dng_sdk/dng_globals.cpp - extra/dng_sdk/dng_gain_map.cpp - extra/dng_sdk/dng_lossless_jpeg.cpp - extra/dng_sdk/dng_parse_utils.cpp - extra/dng_sdk/dng_resample.cpp - extra/dng_sdk/dng_temperature.cpp - extra/dng_sdk/dng_color_spec.cpp - extra/dng_sdk/dng_host.cpp - extra/dng_sdk/dng_matrix.cpp - extra/dng_sdk/dng_pixel_buffer.cpp - extra/dng_sdk/dng_shared.cpp - extra/dng_sdk/dng_tile_iterator.cpp - extra/dng_sdk/dng_tone_curve.cpp - extra/dng_sdk/dng_hue_sat_map.cpp - extra/dng_sdk/dng_pthread.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/extra/dng_sdk/dng_1d_function.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/extra/dng_sdk/dng_date_time.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/extra/dng_sdk/dng_ifd.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/extra/dng_sdk/dng_memory.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/extra/dng_sdk/dng_point.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/extra/dng_sdk/dng_simple_image.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/extra/dng_sdk/dng_utils.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/extra/dng_sdk/dng_1d_table.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/extra/dng_sdk/dng_exceptions.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/extra/dng_sdk/dng_image.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/extra/dng_sdk/dng_memory_stream.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/extra/dng_sdk/dng_rational.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/extra/dng_sdk/dng_spline.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/extra/dng_sdk/dng_xmp.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/extra/dng_sdk/dng_abort_sniffer.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/extra/dng_sdk/dng_exif.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/extra/dng_sdk/dng_image_writer.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/extra/dng_sdk/dng_preview.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/extra/dng_sdk/dng_misc_opcodes.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/extra/dng_sdk/dng_mosaic_info.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/extra/dng_sdk/dng_read_image.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/extra/dng_sdk/dng_stream.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/extra/dng_sdk/dng_xmp_sdk.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/extra/dng_sdk/dng_area_task.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/extra/dng_sdk/dng_file_stream.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/extra/dng_sdk/dng_info.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/extra/dng_sdk/dng_mutex.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/extra/dng_sdk/dng_rect.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/extra/dng_sdk/dng_string.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/extra/dng_sdk/dng_xy_coord.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/extra/dng_sdk/dng_bottlenecks.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/extra/dng_sdk/dng_bad_pixels.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/extra/dng_sdk/dng_filter_task.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/extra/dng_sdk/dng_iptc.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/extra/dng_sdk/dng_negative.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/extra/dng_sdk/dng_reference.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/extra/dng_sdk/dng_string_list.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/extra/dng_sdk/dng_camera_profile.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/extra/dng_sdk/dng_fingerprint.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/extra/dng_sdk/dng_lens_correction.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/extra/dng_sdk/dng_linearization_info.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/extra/dng_sdk/dng_opcode_list.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/extra/dng_sdk/dng_opcodes.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/extra/dng_sdk/dng_orientation.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/extra/dng_sdk/dng_render.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/extra/dng_sdk/dng_tag_types.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/extra/dng_sdk/dng_color_space.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/extra/dng_sdk/dng_globals.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/extra/dng_sdk/dng_gain_map.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/extra/dng_sdk/dng_lossless_jpeg.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/extra/dng_sdk/dng_parse_utils.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/extra/dng_sdk/dng_resample.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/extra/dng_sdk/dng_temperature.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/extra/dng_sdk/dng_color_spec.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/extra/dng_sdk/dng_host.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/extra/dng_sdk/dng_matrix.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/extra/dng_sdk/dng_pixel_buffer.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/extra/dng_sdk/dng_shared.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/extra/dng_sdk/dng_tile_iterator.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/extra/dng_sdk/dng_tone_curve.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/extra/dng_sdk/dng_hue_sat_map.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/extra/dng_sdk/dng_pthread.cpp ) # Don't process automoc on headers from extra subdir file(GLOB_RECURSE extra_headers ${CMAKE_CURRENT_SOURCE_DIR}/extra/*.h) foreach(_file ${extra_headers}) set_property(SOURCE ${_file} PROPERTY SKIP_AUTOMOC ON) endforeach() # Used by digikamcore add_library(core_libdng_obj OBJECT ${libdng_SRCS}) target_compile_definitions(core_libdng_obj PRIVATE digikamcore_EXPORTS ) # Disable warnings: we will never touch this code. # Adjust flag for static lib and 64 bits computers using -fPIC for GCC compiler (bug: #269903) if(MSVC) target_compile_options(core_libdng_obj PRIVATE /w) else() target_compile_options(core_libdng_obj PRIVATE -w -fPIC) endif() # For unit tests. add_library(libdng STATIC $ $ $ ) #------------------------------------------------------------------------------------ set(libdngwriter_SRCS - dngwriter.cpp - dngwriter_p.cpp - dngwriter_convert.cpp - dngwriterhost.cpp - dngsettings.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/dngwriter.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/dngwriter_p.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/dngwriter_convert.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/dngwriterhost.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/dngsettings.cpp ) # Used by digikamcore add_library(core_dngwriter_obj OBJECT ${libdngwriter_SRCS}) target_compile_definitions(core_dngwriter_obj PRIVATE digikamcore_EXPORTS ) diff --git a/core/libs/dplugins/CMakeLists.txt b/core/libs/dplugins/CMakeLists.txt index 015ec2a03f..f53d0c506b 100644 --- a/core/libs/dplugins/CMakeLists.txt +++ b/core/libs/dplugins/CMakeLists.txt @@ -1,102 +1,102 @@ # # Copyright (c) 2010-2020 by Gilles Caulier, # # Redistribution and use is allowed according to the terms of the BSD license. # For details see the accompanying COPYING-CMAKE-SCRIPTS file. APPLY_COMMON_POLICIES() include_directories($ $ $ $ $ $ $ $ $ ) if(ENABLE_QWEBENGINE) include_directories($) else() include_directories($) endif() if(KF5KIO_FOUND) include_directories($ $ ) endif() set(libdpluginsinterface_SRCS - core/dplugin.cpp - core/dplugingeneric.cpp - core/dplugineditor.cpp - core/dpluginrawimport.cpp - core/dplugindimg.cpp - core/dpluginauthor.cpp - core/dpluginaction.cpp - - setup/dpluginloader.cpp - setup/dpluginloader_p.cpp - setup/dpluginconfview.cpp - setup/dpluginconfviewgeneric.cpp - setup/dpluginconfvieweditor.cpp - setup/dpluginconfviewdimg.cpp - setup/dpluginsetup.cpp - setup/dpluginaboutdlg.cpp - - iface/dinfointerface.cpp - iface/dmetainfoiface.cpp - - widgets/dplugindialog.cpp - widgets/dwizardpage.cpp - widgets/dwizarddlg.cpp - widgets/dsavesettingswidget.cpp - widgets/dwizardpage.cpp - widgets/dpreviewmanager.cpp - widgets/dpreviewimage.cpp - widgets/ditemslist.cpp - - webservices/wscomboboxdelegate.cpp - webservices/wscomboboxintermediate.cpp - webservices/wstooldialog.cpp - webservices/wstoolutils.cpp - webservices/wssettingswidget.cpp - webservices/wsselectuserdlg.cpp - webservices/wslogindialog.cpp - webservices/wsnewalbumdialog.cpp - webservices/wssettings.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/core/dplugin.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/core/dplugingeneric.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/core/dplugineditor.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/core/dpluginrawimport.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/core/dplugindimg.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/core/dpluginauthor.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/core/dpluginaction.cpp + + ${CMAKE_CURRENT_SOURCE_DIR}/setup/dpluginloader.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/setup/dpluginloader_p.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/setup/dpluginconfview.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/setup/dpluginconfviewgeneric.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/setup/dpluginconfvieweditor.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/setup/dpluginconfviewdimg.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/setup/dpluginsetup.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/setup/dpluginaboutdlg.cpp + + ${CMAKE_CURRENT_SOURCE_DIR}/iface/dinfointerface.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/iface/dmetainfoiface.cpp + + ${CMAKE_CURRENT_SOURCE_DIR}/widgets/dplugindialog.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/widgets/dwizardpage.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/widgets/dwizarddlg.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/widgets/dsavesettingswidget.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/widgets/dwizardpage.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/widgets/dpreviewmanager.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/widgets/dpreviewimage.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/widgets/ditemslist.cpp + + ${CMAKE_CURRENT_SOURCE_DIR}/webservices/wscomboboxdelegate.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/webservices/wscomboboxintermediate.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/webservices/wstooldialog.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/webservices/wstoolutils.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/webservices/wssettingswidget.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/webservices/wsselectuserdlg.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/webservices/wslogindialog.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/webservices/wsnewalbumdialog.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/webservices/wssettings.cpp ) #if(ENABLE_QWEBENGINE) # set(libdpluginsinterface_SRCS # ${libdpluginsinterface_SRCS} -# webservices/webwidget_qwebengine.cpp +# ${CMAKE_CURRENT_SOURCE_DIR}/webservices/webwidget_qwebengine.cpp # ) #else() # set(libdpluginsinterface_SRCS # ${libdpluginsinterface_SRCS} -# webservices/webwidget.cpp +# ${CMAKE_CURRENT_SOURCE_DIR}/webservices/webwidget.cpp # ) #endif() # Used by digikamcore add_library(core_dpluginsinterface_obj OBJECT ${libdpluginsinterface_SRCS}) target_compile_definitions(core_dpluginsinterface_obj PRIVATE digikamcore_EXPORTS ) # Parse O2 library code and rules at end, and compilation rules remove important Qt definitions. if(WIN32) # NOTE: this O2 export symbols rule must be on top level. add_definitions(-DO2_DLL_EXPORT) endif() add_subdirectory(webservices) diff --git a/core/libs/dplugins/webservices/CMakeLists.txt b/core/libs/dplugins/webservices/CMakeLists.txt index 02e5780cdd..bc0c30e254 100644 --- a/core/libs/dplugins/webservices/CMakeLists.txt +++ b/core/libs/dplugins/webservices/CMakeLists.txt @@ -1,62 +1,64 @@ # # Copyright (c) 2010-2020 by Gilles Caulier, # # Redistribution and use is allowed according to the terms of the BSD license. # For details see the accompanying COPYING-CMAKE-SCRIPTS file. APPLY_COMMON_POLICIES() # OAuth2 library --------------------------------------------------- # The o2 library does not adhere to the flags we use remove_definitions( -DQT_NO_CAST_FROM_ASCII -DQT_NO_CAST_TO_ASCII -DQT_NO_URL_CAST_FROM_STRING -DQT_NO_CAST_FROM_BYTEARRAY) # Remove unwanted warnings from O2 library REMOVE_GCC_COMPILER_WARNINGS("-pedantic") DISABLE_CLANG_COMPILER_WARNINGS("4.99.99" "-Wno-extra-semi") # It also does not export symbols, so export all by default unset(CMAKE_CXX_VISIBILITY_PRESET) if(WIN32) add_definitions(-DO2_DLL_EXPORT) endif() set(libwso2_SRCS o2/src/o2.cpp o2/src/o2reply.cpp o2/src/o2replyserver.cpp o2/src/o2requestor.cpp o2/src/o2simplecrypt.cpp o2/src/o0settingsstore.cpp o2/src/o0baseauth.cpp o2/src/o0abstractstore.h o2/src/o0globals.h # Enable when needed o2/src/o1.cpp o2/src/o1requestor.cpp o2/src/o1timedreply.cpp o2/src/o1smugmug.cpp o2/src/o1twitter.h o2/src/oxtwitter.cpp #o2/src/o1dropbox.h #o2/src/o1flickr.h #o2/src/o2gft.cpp o2/src/o2facebook.cpp #o2/src/o2skydrive.cpp #o2/src/o2hubic.cpp #o2/src/o2uber.cpp + #o2/src/o2msgraph.cpp + #o2/src/o2vimeo.cpp ) # Used by digikamcore add_library(core_libwso2_obj OBJECT ${libwso2_SRCS}) target_compile_definitions(core_libwso2_obj PRIVATE digikamcore_EXPORTS ) diff --git a/core/libs/dplugins/webservices/o2/examples/CMakeLists.txt b/core/libs/dplugins/webservices/o2/examples/CMakeLists.txt index 8dcfda0684..8582ef88bb 100644 --- a/core/libs/dplugins/webservices/o2/examples/CMakeLists.txt +++ b/core/libs/dplugins/webservices/o2/examples/CMakeLists.txt @@ -1,13 +1,26 @@ cmake_minimum_required(VERSION 2.8.11) if(o2_WITH_FACEBOOK) add_subdirectory(facebookdemo) endif(o2_WITH_FACEBOOK) +if(o2_WITH_GOOGLE) + add_subdirectory(youtubedemo) +endif(o2_WITH_GOOGLE) + if(o2_WITH_TWITTER) add_subdirectory(twitterdemo) endif(o2_WITH_TWITTER) -if(o2_WITH_SPOTIFY) - add_subdirectory(spotifydemo) -endif(o2_WITH_SPOTIFY) +#if(o2_WITH_SPOTIFY) +# add_subdirectory(spotifydemo) +#endif(o2_WITH_SPOTIFY) + +if (o2_WITH_VIMEO) + add_subdirectory(vimeodemo) +endif(o2_WITH_VIMEO) + +if (o2_WITH_MSGRAPH) + add_subdirectory(msgraphdemo) + add_subdirectory(msgraphexternalinterceptordemo) +endif(o2_WITH_MSGRAPH) diff --git a/core/libs/dplugins/webservices/o2/examples/facebookdemo/CMakeLists.txt b/core/libs/dplugins/webservices/o2/examples/facebookdemo/CMakeLists.txt index afc3d27060..3326740123 100644 --- a/core/libs/dplugins/webservices/o2/examples/facebookdemo/CMakeLists.txt +++ b/core/libs/dplugins/webservices/o2/examples/facebookdemo/CMakeLists.txt @@ -1,43 +1,44 @@ cmake_minimum_required(VERSION 2.8.11) project( fbexample ) set(CMAKE_AUTOMOC ON) set(CMAKE_INCLUDE_CURRENT_DIR ON) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall") if(o2_WITH_QT5) # Qt5 packages find their own dependencies. find_package(Qt5Core REQUIRED) find_package(Qt5Widgets REQUIRED) find_package(Qt5Script REQUIRED) find_package(Qt5Network REQUIRED) else(o2_WITH_QT5) set(QT_USE_QTNETWORK true) set(QT_USE_QTSCRIPT true) find_package(Qt4 REQUIRED) endif(o2_WITH_QT5) if (NOT o2_WITH_QT5) include( ${QT_USE_FILE} ) endif(NOT o2_WITH_QT5) include_directories(${CMAKE_SOURCE_DIR} ${CMAKE_BINARY_DIR} "../../src" ) set(fb_SRCS main.cpp fbdemo.cpp + fbdemo.h ) if(NOT o2_WITH_QT5) add_definitions(${QT4_DEFINITIONS}) endif(NOT o2_WITH_QT5) add_executable( fbexample ${fb_SRCS} ) if(o2_WITH_QT5) qt5_use_modules( fbexample Core Widgets Network ) target_link_libraries( fbexample o2 ) else(o2_WITH_QT5) target_link_libraries( fbexample ${QT_LIBRARIES} o2 ) endif(o2_WITH_QT5) diff --git a/core/libs/dplugins/webservices/o2/examples/facebookdemo/CMakeLists.txt b/core/libs/dplugins/webservices/o2/examples/msgraphdemo/CMakeLists.txt similarity index 68% copy from core/libs/dplugins/webservices/o2/examples/facebookdemo/CMakeLists.txt copy to core/libs/dplugins/webservices/o2/examples/msgraphdemo/CMakeLists.txt index afc3d27060..449a1368dd 100644 --- a/core/libs/dplugins/webservices/o2/examples/facebookdemo/CMakeLists.txt +++ b/core/libs/dplugins/webservices/o2/examples/msgraphdemo/CMakeLists.txt @@ -1,43 +1,44 @@ cmake_minimum_required(VERSION 2.8.11) -project( fbexample ) +project( msgraphexample ) set(CMAKE_AUTOMOC ON) set(CMAKE_INCLUDE_CURRENT_DIR ON) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall") if(o2_WITH_QT5) # Qt5 packages find their own dependencies. find_package(Qt5Core REQUIRED) - find_package(Qt5Widgets REQUIRED) - find_package(Qt5Script REQUIRED) + find_package(Qt5Gui REQUIRED) find_package(Qt5Network REQUIRED) + find_package(Qt5Widgets REQUIRED) else(o2_WITH_QT5) set(QT_USE_QTNETWORK true) set(QT_USE_QTSCRIPT true) find_package(Qt4 REQUIRED) endif(o2_WITH_QT5) if (NOT o2_WITH_QT5) include( ${QT_USE_FILE} ) endif(NOT o2_WITH_QT5) include_directories(${CMAKE_SOURCE_DIR} ${CMAKE_BINARY_DIR} "../../src" ) -set(fb_SRCS +set(msgraph_SRCS main.cpp - fbdemo.cpp + msgraphdemo.cpp + msgraphdemo.h ) if(NOT o2_WITH_QT5) add_definitions(${QT4_DEFINITIONS}) endif(NOT o2_WITH_QT5) -add_executable( fbexample ${fb_SRCS} ) +add_executable( msgraphexample ${msgraph_SRCS} ) if(o2_WITH_QT5) - qt5_use_modules( fbexample Core Widgets Network ) - target_link_libraries( fbexample o2 ) + qt5_use_modules( msgraphexample Core Gui Network Widgets ) + target_link_libraries( msgraphexample o2 ) else(o2_WITH_QT5) - target_link_libraries( fbexample ${QT_LIBRARIES} o2 ) + target_link_libraries( msgraphexample ${QT_LIBRARIES} o2 ) endif(o2_WITH_QT5) diff --git a/core/libs/dplugins/webservices/o2/examples/msgraphdemo/main.cpp b/core/libs/dplugins/webservices/o2/examples/msgraphdemo/main.cpp new file mode 100644 index 0000000000..ec486b4867 --- /dev/null +++ b/core/libs/dplugins/webservices/o2/examples/msgraphdemo/main.cpp @@ -0,0 +1,60 @@ +#include +#include +#include +#include + +#include +#include + +#include "msgraphdemo.h" + +class Helper : public QObject { + Q_OBJECT + +public: + Helper() : QObject(), demo_(this) {} + +public slots: + void run() { + connect(&demo_, SIGNAL(linkingFailed()), this, SLOT(onLinkingFailed())); + connect(&demo_, SIGNAL(linkingSucceeded()), this, SLOT(onLinkingSucceeded())); + connect(&demo_, SIGNAL(userPrincipalNameReceived()), this, SLOT(onUserPrincipalNameReceived())); + connect(&demo_, SIGNAL(userPrincipalNameFailed()), this, SLOT(onUserPrincipalNameFailed())); + + demo_.doOAuth(O2::GrantFlowAuthorizationCode); + } + + void onLinkingFailed() { + qDebug() << "Linking failed!"; + qApp->exit(1); + } + + void onLinkingSucceeded() { + qDebug() << "Linking succeeded!"; + demo_.getUserPrincipalName(); + } + + void onUserPrincipalNameFailed() { + qDebug() << "Error getting userPrincipalName!"; + qApp->exit(1); + } + + void onUserPrincipalNameReceived() { + qDebug() << "UserPrincipalName received!"; + qApp->quit(); + } + +private: + MsgraphDemo demo_; +}; + +int main(int argc, char *argv[]) { + QApplication a(argc, argv); + QCoreApplication::setOrganizationName("O2"); + QCoreApplication::setApplicationName("Msgraph Example"); + Helper helper; + QTimer::singleShot(0, &helper, SLOT(run())); + return a.exec(); +} + +#include "main.moc" diff --git a/core/libs/dplugins/webservices/o2/examples/msgraphdemo/msgraphdemo.cpp b/core/libs/dplugins/webservices/o2/examples/msgraphdemo/msgraphdemo.cpp new file mode 100644 index 0000000000..907f39423e --- /dev/null +++ b/core/libs/dplugins/webservices/o2/examples/msgraphdemo/msgraphdemo.cpp @@ -0,0 +1,126 @@ +#include +#include +#include +#include +#include +#include +#include + +#include "msgraphdemo.h" +#include "o0globals.h" +#include "o0settingsstore.h" +#include "o2requestor.h" + +const char MSGRAPH_APP_ID[] = "YOUR_MSGRAPH_APP_ID"; +const char MSGRAPH_APP_SECRET[] = "YOUR_MSGRAPH_APP_SECRET"; +const char MSGRAPH_SCOPE[] = "User.Read"; +const char MSGRAPH_USER_INFO_URL[] = "https://graph.microsoft.com/v1.0/me"; + +const int localPort = 8888; + +#define QENUM_NAME(o,e,v) (o::staticMetaObject.enumerator(o::staticMetaObject.indexOfEnumerator(#e)).valueToKey((v))) +#define GRANTFLOW_STR(v) QString(QENUM_NAME(O2, GrantFlow, v)) + +MsgraphDemo::MsgraphDemo(QObject *parent) : + QObject(parent), requestId_(0) { + o2Msgraph_ = new O2Msgraph(this); + + o2Msgraph_->setClientId(MSGRAPH_APP_ID); + o2Msgraph_->setClientSecret(MSGRAPH_APP_SECRET); + o2Msgraph_->setLocalPort(localPort); + o2Msgraph_->setScope(MSGRAPH_SCOPE); + + // Create a store object for writing the received tokens + O0SettingsStore *store = new O0SettingsStore(O2_ENCRYPTION_KEY); + store->setGroupKey("msgraph"); + o2Msgraph_->setStore(store); + + connect(o2Msgraph_, SIGNAL(linkedChanged()), this, SLOT(onLinkedChanged())); + connect(o2Msgraph_, SIGNAL(linkingFailed()), this, SIGNAL(linkingFailed())); + connect(o2Msgraph_, SIGNAL(linkingSucceeded()), this, SLOT(onLinkingSucceeded())); + connect(o2Msgraph_, SIGNAL(openBrowser(QUrl)), this, SLOT(onOpenBrowser(QUrl))); + connect(o2Msgraph_, SIGNAL(closeBrowser()), this, SLOT(onCloseBrowser())); +} + +void MsgraphDemo::doOAuth(O2::GrantFlow grantFlowType) { + qDebug() << "Starting OAuth 2 with grant flow type" << GRANTFLOW_STR(grantFlowType) << "..."; + o2Msgraph_->setGrantFlow(grantFlowType); + o2Msgraph_->unlink(); + o2Msgraph_->link(); +} + +void MsgraphDemo::getUserPrincipalName() { + if (!o2Msgraph_->linked()) { + qWarning() << "ERROR: Application is not linked!"; + emit linkingFailed(); + return; + } + + QString userInfoURL = QString(MSGRAPH_USER_INFO_URL); + QNetworkRequest request = QNetworkRequest(QUrl(userInfoURL)); + QNetworkAccessManager *mgr = new QNetworkAccessManager(this); + O2Requestor *requestor = new O2Requestor(mgr, o2Msgraph_, this); + requestor->setAddAccessTokenInQuery(false); + requestor->setAccessTokenInAuthenticationHTTPHeaderFormat("Bearer %1"); + requestId_ = requestor->get(request); + connect(requestor, SIGNAL(finished(int, QNetworkReply::NetworkError, QByteArray)), + this, SLOT(onFinished(int, QNetworkReply::NetworkError, QByteArray)) + ); + qDebug() << "Getting user info... Please wait."; +} + +void MsgraphDemo::onOpenBrowser(const QUrl &url) { + QDesktopServices::openUrl(url); +} + +void MsgraphDemo::onCloseBrowser() { +} + +void MsgraphDemo::onLinkedChanged() { + qDebug() << "Link changed!"; +} + +void MsgraphDemo::onLinkingSucceeded() { + O2Msgraph *o2t = qobject_cast(sender()); + if (!o2t->linked()) { + return; + } + QVariantMap extraTokens = o2t->extraTokens(); + if (!extraTokens.isEmpty()) { + emit extraTokensReady(extraTokens); + qDebug() << "Extra tokens in response:"; + foreach (QString key, extraTokens.keys()) { + qDebug() << "\t" << key << ":" << (extraTokens.value(key).toString().left(3) + "..."); + } + } + emit linkingSucceeded(); +} + +void MsgraphDemo::onFinished(int requestId, QNetworkReply::NetworkError error, QByteArray replyData) { + if (requestId != requestId_) + return; + + if (error != QNetworkReply::NoError) { + qWarning() << "Reply error:" << error; + emit userPrincipalNameFailed(); + return; + } + + QString reply(replyData); + bool errorFound = reply.contains("error"); + if (errorFound) { + qDebug() << "Request failed"; + emit userPrincipalNameFailed(); + return; + } + + QRegExp userPrincipalNameRE("\"userPrincipalName\":\"([^\"]+)\""); + if (userPrincipalNameRE.indexIn(reply) == -1) { + qDebug() << "Can not parse reply:" << reply; + emit userPrincipalNameFailed(); + return; + } + + qInfo() << "userPrincipalName: " << userPrincipalNameRE.cap(1); + emit userPrincipalNameReceived(); +} diff --git a/core/libs/dplugins/webservices/o2/examples/msgraphdemo/msgraphdemo.h b/core/libs/dplugins/webservices/o2/examples/msgraphdemo/msgraphdemo.h new file mode 100644 index 0000000000..01d8512ce3 --- /dev/null +++ b/core/libs/dplugins/webservices/o2/examples/msgraphdemo/msgraphdemo.h @@ -0,0 +1,38 @@ +#ifndef MSGRAPH_H +#define MSGRAPH_H + +#include + +#include "o2msgraph.h" + +class MsgraphDemo : public QObject +{ + Q_OBJECT + +public: + explicit MsgraphDemo(QObject *parent = 0); + +signals: + void extraTokensReady(const QVariantMap &extraTokens); + void linkingFailed(); + void linkingSucceeded(); + void userPrincipalNameReceived(); + void userPrincipalNameFailed(); + +public slots: + void doOAuth(O2::GrantFlow grantFlowType); + void getUserPrincipalName(); + +private slots: + void onLinkedChanged(); + void onLinkingSucceeded(); + void onOpenBrowser(const QUrl &url); + void onCloseBrowser(); + void onFinished(int, QNetworkReply::NetworkError, QByteArray); + +private: + O2Msgraph *o2Msgraph_; + int requestId_; +}; + +#endif // MSGRAPHDEMO_H diff --git a/core/libs/dplugins/webservices/o2/examples/msgraphdemo/msgraphdemo.pro b/core/libs/dplugins/webservices/o2/examples/msgraphdemo/msgraphdemo.pro new file mode 100644 index 0000000000..32d7ff4746 --- /dev/null +++ b/core/libs/dplugins/webservices/o2/examples/msgraphdemo/msgraphdemo.pro @@ -0,0 +1,16 @@ +QT += core gui network + +greaterThan(QT_MAJOR_VERSION, 4) { + QT += widgets +} + +include(../../src/src.pri) + +TARGET = msgraphdemo +TEMPLATE = app + +SOURCES += main.cpp \ + msgraphdemo.cpp + +HEADERS += \ + msgraphdemo.h diff --git a/core/libs/dplugins/webservices/o2/examples/facebookdemo/CMakeLists.txt b/core/libs/dplugins/webservices/o2/examples/msgraphexternalinterceptordemo/CMakeLists.txt similarity index 61% copy from core/libs/dplugins/webservices/o2/examples/facebookdemo/CMakeLists.txt copy to core/libs/dplugins/webservices/o2/examples/msgraphexternalinterceptordemo/CMakeLists.txt index afc3d27060..b2944efb3b 100644 --- a/core/libs/dplugins/webservices/o2/examples/facebookdemo/CMakeLists.txt +++ b/core/libs/dplugins/webservices/o2/examples/msgraphexternalinterceptordemo/CMakeLists.txt @@ -1,43 +1,47 @@ cmake_minimum_required(VERSION 2.8.11) -project( fbexample ) +project( msgraphexternalinterceptorexample ) set(CMAKE_AUTOMOC ON) +set(CMAKE_AUTOUIC ON) set(CMAKE_INCLUDE_CURRENT_DIR ON) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall") if(o2_WITH_QT5) # Qt5 packages find their own dependencies. find_package(Qt5Core REQUIRED) find_package(Qt5Widgets REQUIRED) find_package(Qt5Script REQUIRED) find_package(Qt5Network REQUIRED) + find_package(Qt5WebEngine REQUIRED) else(o2_WITH_QT5) set(QT_USE_QTNETWORK true) set(QT_USE_QTSCRIPT true) find_package(Qt4 REQUIRED) endif(o2_WITH_QT5) if (NOT o2_WITH_QT5) include( ${QT_USE_FILE} ) endif(NOT o2_WITH_QT5) include_directories(${CMAKE_SOURCE_DIR} ${CMAKE_BINARY_DIR} "../../src" ) -set(fb_SRCS +set(msgraph_SRCS main.cpp - fbdemo.cpp + msgraphdemo.cpp + webwindow.cpp + webenginepage.cpp ) if(NOT o2_WITH_QT5) add_definitions(${QT4_DEFINITIONS}) endif(NOT o2_WITH_QT5) -add_executable( fbexample ${fb_SRCS} ) +add_executable( msgraphexternalinterceptorexample ${msgraph_SRCS} ) if(o2_WITH_QT5) - qt5_use_modules( fbexample Core Widgets Network ) - target_link_libraries( fbexample o2 ) + qt5_use_modules( msgraphexternalinterceptorexample Core Widgets Network WebEngineWidgets ) + target_link_libraries( msgraphexternalinterceptorexample o2 ) else(o2_WITH_QT5) - target_link_libraries( fbexample ${QT_LIBRARIES} o2 ) + target_link_libraries( msgraphexternalinterceptorexample ${QT_LIBRARIES} o2 ) endif(o2_WITH_QT5) diff --git a/core/libs/dplugins/webservices/o2/examples/msgraphexternalinterceptordemo/main.cpp b/core/libs/dplugins/webservices/o2/examples/msgraphexternalinterceptordemo/main.cpp new file mode 100644 index 0000000000..c2101ac0f4 --- /dev/null +++ b/core/libs/dplugins/webservices/o2/examples/msgraphexternalinterceptordemo/main.cpp @@ -0,0 +1,58 @@ +#include +#include +#include +#include + +#include "msgraphdemo.h" + +class Helper : public QObject { + Q_OBJECT + +public: + Helper() : QObject(), demo_(this) {} + +public slots: + void run() { + connect(&demo_, SIGNAL(linkingFailed()), this, SLOT(onLinkingFailed())); + connect(&demo_, SIGNAL(linkingSucceeded()), this, SLOT(onLinkingSucceeded())); + connect(&demo_, SIGNAL(userPrincipalNameReceived()), this, SLOT(onUserPrincipalNameReceived())); + connect(&demo_, SIGNAL(userPrincipalNameFailed()), this, SLOT(onUserPrincipalNameFailed())); + + // Start OAuth + demo_.doOAuth(O2::GrantFlowAuthorizationCode); + } + + void onLinkingFailed() { + qDebug() << "Linking failed!"; + qApp->exit(1); + } + + void onLinkingSucceeded() { + qDebug() << "Linking succeeded!"; + demo_.getUserPrincipalName(); + } + + void onUserPrincipalNameFailed() { + qDebug() << "Error getting userPrincipalName!"; + qApp->exit(1); + } + + void onUserPrincipalNameReceived() { + qDebug() << "UserPrincipalName received!"; + qApp->quit(); + } + +private: + MsgraphDemo demo_; +}; + +int main(int argc, char *argv[]) { + QApplication a(argc, argv); + QCoreApplication::setOrganizationName("O2"); + QCoreApplication::setApplicationName("Msgraph Example"); + Helper helper; + QTimer::singleShot(0, &helper, SLOT(run())); + return a.exec(); +} + +#include "main.moc" diff --git a/core/libs/dplugins/webservices/o2/examples/msgraphexternalinterceptordemo/msgraphdemo.cpp b/core/libs/dplugins/webservices/o2/examples/msgraphexternalinterceptordemo/msgraphdemo.cpp new file mode 100644 index 0000000000..04017f9b55 --- /dev/null +++ b/core/libs/dplugins/webservices/o2/examples/msgraphexternalinterceptordemo/msgraphdemo.cpp @@ -0,0 +1,160 @@ +#include +#include +#include +#include +#include +#include +#include + +#include "msgraphdemo.h" +#include "o0globals.h" +#include "o0settingsstore.h" +#include "o2requestor.h" + +const char MSGRAPH_APP_ID[] = "YOUR_MSGRAPH_APP_ID"; +const char MSGRAPH_REDIRECT_URI[] = "https://login.microsoftonline.com/common/oauth2/nativeclient"; +const char MSGRAPH_SCOPE[] = "User.Read"; +const char MSGRAPH_USER_INFO_URL[] = "https://graph.microsoft.com/v1.0/me"; + +#define QENUM_NAME(o,e,v) (o::staticMetaObject.enumerator(o::staticMetaObject.indexOfEnumerator(#e)).valueToKey((v))) +#define GRANTFLOW_STR(v) QString(QENUM_NAME(O2, GrantFlow, v)) + +MsgraphDemo::MsgraphDemo(QObject *parent) : + QObject(parent), authDialog(NULL), requestId_(0) { + o2Msgraph_ = new O2Msgraph(this); + + o2Msgraph_->setClientId(MSGRAPH_APP_ID); + o2Msgraph_->setUseExternalWebInterceptor(true); + o2Msgraph_->setLocalhostPolicy(MSGRAPH_REDIRECT_URI); + o2Msgraph_->setScope(MSGRAPH_SCOPE); + + + // Create a store object for writing the received tokens + O0SettingsStore *store = new O0SettingsStore(O2_ENCRYPTION_KEY); + store->setGroupKey("msgraph"); + o2Msgraph_->setStore(store); + + connect(o2Msgraph_, SIGNAL(linkedChanged()), this, SLOT(onLinkedChanged())); + connect(o2Msgraph_, SIGNAL(linkingFailed()), this, SIGNAL(linkingFailed())); + connect(o2Msgraph_, SIGNAL(linkingSucceeded()), this, SLOT(onLinkingSucceeded())); + connect(o2Msgraph_, SIGNAL(openBrowser(QUrl)), this, SLOT(onOpenBrowser(QUrl))); + connect(o2Msgraph_, SIGNAL(closeBrowser()), this, SLOT(onCloseBrowser())); +} + +void MsgraphDemo::doOAuth(O2::GrantFlow grantFlowType) { + qDebug() << "Starting OAuth 2 with grant flow type" << GRANTFLOW_STR(grantFlowType) << "..."; + o2Msgraph_->setGrantFlow(grantFlowType); + o2Msgraph_->unlink(); + o2Msgraph_->link(); +} + +void MsgraphDemo::getUserPrincipalName() { + if (!o2Msgraph_->linked()) { + qWarning() << "ERROR: Application is not linked!"; + emit linkingFailed(); + return; + } + + QString userInfoURL = QString(MSGRAPH_USER_INFO_URL); + QNetworkRequest request = QNetworkRequest(QUrl(userInfoURL)); + QNetworkAccessManager *mgr = new QNetworkAccessManager(this); + O2Requestor *requestor = new O2Requestor(mgr, o2Msgraph_, this); + requestor->setAddAccessTokenInQuery(false); + requestor->setAccessTokenInAuthenticationHTTPHeaderFormat("Bearer %1"); + requestId_ = requestor->get(request); + connect(requestor, SIGNAL(finished(int, QNetworkReply::NetworkError, QByteArray)), + this, SLOT(onFinished(int, QNetworkReply::NetworkError, QByteArray)) + ); + qDebug() << "Getting user info... Please wait."; +} + +void MsgraphDemo::onOpenBrowser(const QUrl &url) { + if (authDialog == Q_NULLPTR) { + authDialog = new WebWindow(QSize(650, 600), url, MSGRAPH_REDIRECT_URI, false); + QObject::connect(authDialog, SIGNAL(callbackCalled(const QString &)), this, SLOT(onAuthWindowCallbackCalled(const QString &))); + QObject::connect(authDialog, SIGNAL(windowClosed()), this, SLOT(onAuthWindowClosed())); + authDialog->exec(); + } +} + +void MsgraphDemo::onAuthWindowCallbackCalled(const QString &inURLString) +{ + if(o2Msgraph_ != NULL) + { + QUrl getTokenUrl(inURLString); + QUrlQuery query(getTokenUrl); + QList< QPair > tokens = query.queryItems(); + + QMultiMap queryParams; + QPair tokenPair; + foreach (tokenPair, tokens) { + // FIXME: We are decoding key and value again. This helps with Google OAuth, but is it mandated by the standard? + QString key = QUrl::fromPercentEncoding(QByteArray().append(tokenPair.first.trimmed().toLatin1())); + QString value = QUrl::fromPercentEncoding(QByteArray().append(tokenPair.second.trimmed().toLatin1())); + queryParams.insert(key, value); + } + + o2Msgraph_->onVerificationReceived(queryParams); + } +} + +void MsgraphDemo::onAuthWindowClosed() +{ + if (authDialog != Q_NULLPTR) + { + authDialog->close(); + delete authDialog; + } +} + +void MsgraphDemo::onCloseBrowser() { +} + +void MsgraphDemo::onLinkedChanged() { + qDebug() << "Link changed!"; +} + +void MsgraphDemo::onLinkingSucceeded() { + O2Msgraph *o2t = qobject_cast(sender()); + if (!o2t->linked()) { + return; + } + QVariantMap extraTokens = o2t->extraTokens(); + if (!extraTokens.isEmpty()) { + emit extraTokensReady(extraTokens); + qDebug() << "Extra tokens in response:"; + foreach (QString key, extraTokens.keys()) { + qDebug() << "\t" << key << ":" << (extraTokens.value(key).toString().left(3) + "..."); + } + } + emit linkingSucceeded(); +} + +void MsgraphDemo::onFinished(int requestId, QNetworkReply::NetworkError error, QByteArray replyData) { + if (requestId != requestId_) + return; + + if (error != QNetworkReply::NoError) { + qWarning() << "Reply error:" << error; + emit userPrincipalNameFailed(); + return; + } + + QString reply(replyData); + bool errorFound = reply.contains("error"); + if (errorFound) { + qDebug() << "Request failed"; + emit userPrincipalNameFailed(); + return; + } + + QRegExp userPrincipalNameRE("\"userPrincipalName\":\"([^\"]+)\""); + if (userPrincipalNameRE.indexIn(reply) == -1) { + qDebug() << "Can not parse reply:" << reply; + emit userPrincipalNameFailed(); + return; + } + + qInfo() << "userPrincipalName: " << userPrincipalNameRE.cap(1); + emit userPrincipalNameReceived(); +} diff --git a/core/libs/dplugins/webservices/o2/examples/msgraphexternalinterceptordemo/msgraphdemo.h b/core/libs/dplugins/webservices/o2/examples/msgraphexternalinterceptordemo/msgraphdemo.h new file mode 100644 index 0000000000..b9971a5868 --- /dev/null +++ b/core/libs/dplugins/webservices/o2/examples/msgraphexternalinterceptordemo/msgraphdemo.h @@ -0,0 +1,42 @@ +#ifndef MSGRAPHDEMO_H +#define MSGRAPHDEMO_H + +#include + +#include "o2msgraph.h" +#include "webwindow.h" + +class MsgraphDemo : public QObject +{ + Q_OBJECT + +public: + explicit MsgraphDemo(QObject *parent = 0); + +signals: + void extraTokensReady(const QVariantMap &extraTokens); + void linkingFailed(); + void linkingSucceeded(); + void userPrincipalNameReceived(); + void userPrincipalNameFailed(); + +public slots: + void doOAuth(O2::GrantFlow grantFlowType); + void getUserPrincipalName(); + +private slots: + void onLinkedChanged(); + void onLinkingSucceeded(); + void onOpenBrowser(const QUrl &url); + void onAuthWindowCallbackCalled(const QString &inURLString); + void onAuthWindowClosed(); + void onCloseBrowser(); + void onFinished(int, QNetworkReply::NetworkError, QByteArray); + +private: + O2Msgraph *o2Msgraph_; + WebWindow* authDialog; + int requestId_; +}; + +#endif // MSGRAPHDEMO_H diff --git a/core/libs/dplugins/webservices/o2/examples/msgraphexternalinterceptordemo/msgraphexternalinterceptordemo.pro b/core/libs/dplugins/webservices/o2/examples/msgraphexternalinterceptordemo/msgraphexternalinterceptordemo.pro new file mode 100644 index 0000000000..fabb47fe34 --- /dev/null +++ b/core/libs/dplugins/webservices/o2/examples/msgraphexternalinterceptordemo/msgraphexternalinterceptordemo.pro @@ -0,0 +1,23 @@ +QT += core gui webenginewidgets + +greaterThan(QT_MAJOR_VERSION, 4) { + QT += widgets +} + +DEFINES += O0_EXPORT= +include(../../src/src.pri) + +TARGET = msgraphdemo +TEMPLATE = app + +SOURCES += main.cpp \ + msgraphdemo.cpp \ + webenginepage.cpp \ + webwindow.cpp + +HEADERS += \ + msgraphdemo.h \ + webenginepage.h \ + webwindow.h + +FORMS += webwindow.ui diff --git a/core/libs/dplugins/webservices/o2/examples/msgraphexternalinterceptordemo/webenginepage.cpp b/core/libs/dplugins/webservices/o2/examples/msgraphexternalinterceptordemo/webenginepage.cpp new file mode 100644 index 0000000000..906e9e1bbe --- /dev/null +++ b/core/libs/dplugins/webservices/o2/examples/msgraphexternalinterceptordemo/webenginepage.cpp @@ -0,0 +1,93 @@ + +#include "webenginepage.h" +#include "webwindow.h" + +WebEnginePage::~WebEnginePage() +{ + for(WebWindow *createdWindow : mCreatedWindows) + { + createdWindow->close(); + createdWindow->deleteLater(); + } + + mCreatedWindows.clear(); +} + +bool WebEnginePage::acceptNavigationRequest(const QUrl &url, QWebEnginePage::NavigationType /*type*/, bool /*isMainFrame*/) +{ + QString urlString = url.toString(); + if(mRedirectURLString.length() > 0 && + (urlString.length() >= mRedirectURLString.length()) && + (urlString.left(mRedirectURLString.length()) == mRedirectURLString)) + { + emit callbackCatched(url.toString()); + return false; + } + + return true; +} + +QWebEnginePage *WebEnginePage::createWindow(WebWindowType type) +{ + switch(type) + { + case QWebEnginePage::WebBrowserTab: + case QWebEnginePage::WebBrowserBackgroundTab: + case QWebEnginePage::WebBrowserWindow: + case QWebEnginePage::WebDialog: + { + WebWindow *webViewDialog = new WebWindow(QSize(600, 500), QUrl(), mRedirectURLString, false); + QObject::connect(webViewDialog, SIGNAL(callbackCalled(const QString &)), this, SLOT(onAuthWindowCallbackCalled(const QString &))); + QObject::connect(webViewDialog, SIGNAL(windowClosed()), this, SLOT(onCreatedWindowClosed())); + QObject::connect(webViewDialog->GetWebEnginePage(), SIGNAL(windowCloseRequested()), this, SLOT(onWindowCloseRequested())); + mCreatedWindows.push_back(webViewDialog); + webViewDialog->open(); + return webViewDialog->GetWebEnginePage(); + } + } + + return NULL; +} + +void WebEnginePage::onAuthWindowCallbackCalled(const QString &inURLString) +{ + emit callbackCatched(inURLString); +} + +void WebEnginePage::onCreatedWindowClosed() +{ + WebWindow *createdWindow = qobject_cast(sender()); + if (createdWindow != Q_NULLPTR) + { + std::vector::iterator iterator = std::find(mCreatedWindows.begin(), mCreatedWindows.end(), createdWindow); + if(iterator != mCreatedWindows.end()) + { + mCreatedWindows.erase(iterator); + createdWindow->close(); + createdWindow->deleteLater(); + } + } +} + +void WebEnginePage::onWindowCloseRequested() +{ + QWebEnginePage *webEngine = qobject_cast(sender()); + if (webEngine != Q_NULLPTR) + { + for(WebWindow *createdWindow : mCreatedWindows) + { + if(createdWindow->GetWebEnginePage() == webEngine) + { + std::vector::iterator iterator = std::find(mCreatedWindows.begin(), mCreatedWindows.end(), createdWindow); + if(iterator != mCreatedWindows.end()) + { + mCreatedWindows.erase(iterator); + createdWindow->close(); + createdWindow->deleteLater(); + } + + break; + } + } + } +} diff --git a/core/libs/dplugins/webservices/o2/examples/msgraphexternalinterceptordemo/webenginepage.h b/core/libs/dplugins/webservices/o2/examples/msgraphexternalinterceptordemo/webenginepage.h new file mode 100644 index 0000000000..4b9ec42768 --- /dev/null +++ b/core/libs/dplugins/webservices/o2/examples/msgraphexternalinterceptordemo/webenginepage.h @@ -0,0 +1,35 @@ + +#pragma once + +#include + +class WebWindow; + +class WebEnginePage : public QWebEnginePage +{ + Q_OBJECT + +public: + + WebEnginePage(QWebEngineProfile *inProfile, const QString &inRedirectURLString) : QWebEnginePage(inProfile), mRedirectURLString(inRedirectURLString) { } + + virtual ~WebEnginePage(); + + bool acceptNavigationRequest(const QUrl & url, QWebEnginePage::NavigationType type, bool isMainFrame); + +protected: + QWebEnginePage *createWindow(WebWindowType type); + +signals: + void callbackCatched(const QString &inURLString); + +private slots: + void onCreatedWindowClosed(); + void onWindowCloseRequested(); + void onAuthWindowCallbackCalled(const QString &inURLString); + +private: + QString mRedirectURLString; + std::vector mCreatedWindows; +}; + diff --git a/core/libs/dplugins/webservices/o2/examples/msgraphexternalinterceptordemo/webwindow.cpp b/core/libs/dplugins/webservices/o2/examples/msgraphexternalinterceptordemo/webwindow.cpp new file mode 100644 index 0000000000..bc5f850a0b --- /dev/null +++ b/core/libs/dplugins/webservices/o2/examples/msgraphexternalinterceptordemo/webwindow.cpp @@ -0,0 +1,96 @@ +#include "webwindow.h" +#include "ui_webwindow.h" +#include "webenginepage.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +WebWindow::WebWindow(QSize inWindowSize, QUrl inLoginURL, QString inRedirectURLString, bool inAutoclose) : + QDialog(Q_NULLPTR), + ui(new Ui::WebWindow), + mAutoclose(inAutoclose) +{ + ui->setupUi(this); + setFocus(); + setFixedSize(inWindowSize); + + mWebView = new QWebEngineView(this); + mWebView->settings()->setAttribute(QWebEngineSettings::JavascriptCanOpenWindows, true); + + mWebEngineProfile = new QWebEngineProfile(); + + mWebEnginePage = new WebEnginePage(mWebEngineProfile, inRedirectURLString); + QObject::connect(mWebEnginePage, SIGNAL(callbackCatched(const QString &)), this, SLOT(onCallbackCatched(const QString &))); + + mWebView->setPage(mWebEnginePage); + + QRect rect_map = QRect(1, 1, width() - 2, height() - 2); + mWebView->setGeometry(rect_map); + mWebView->setObjectName("webView"); + + mWebView->page()->profile()->setPersistentCookiesPolicy(QWebEngineProfile::NoPersistentCookies); + if(!inLoginURL.isEmpty()) + { + mWebView->page()->profile()->cookieStore()->deleteAllCookies(); + } + + // Load the URL + mWebView->setUrl(inLoginURL); +} + +WebWindow::~WebWindow() +{ + mWebView->setPage(Q_NULLPTR); + delete mWebEnginePage; + + delete mWebEngineProfile; + + mWebView->close(); + delete mWebView; + + delete ui; +} + +void WebWindow::closeEvent(QCloseEvent *) +{ + emit (windowClosed()); +} + +void WebWindow::onCallbackCatched(const QString &inURLString) +{ + mCatchedOAuthURL = inURLString; + QTimer::singleShot(100, this, SLOT(onCallbackCatchedSafe())); +} + +void WebWindow::onCallbackCatchedSafe() +{ + if(mAutoclose) + { + QUrl getTokenUrl(mCatchedOAuthURL); + QUrlQuery query(getTokenUrl); + QList< QPair > tokens = query.queryItems(); + + QMultiMap queryParams; + QPair tokenPair; + foreach (tokenPair, tokens) { + QString key = QUrl::fromPercentEncoding(QByteArray().append(tokenPair.first.trimmed().toLatin1())); + QString value = QUrl::fromPercentEncoding(QByteArray().append(tokenPair.second.trimmed().toLatin1())); + queryParams.insert(key, value); + } + + resultStr = queryParams.value("code", ""); + close(); + } + else + { + emit callbackCalled(mCatchedOAuthURL); + } +} + + diff --git a/core/libs/dplugins/webservices/o2/examples/msgraphexternalinterceptordemo/webwindow.h b/core/libs/dplugins/webservices/o2/examples/msgraphexternalinterceptordemo/webwindow.h new file mode 100644 index 0000000000..f87a503e48 --- /dev/null +++ b/core/libs/dplugins/webservices/o2/examples/msgraphexternalinterceptordemo/webwindow.h @@ -0,0 +1,49 @@ + +#pragma once + +#include +#include + +namespace Ui { +class WebWindow; +} + +class WebWindow : public QDialog +{ + Q_OBJECT + +public: + explicit WebWindow(QSize inWindowSize, QUrl inLoginURL, QString inRedirectURLString, bool inAutoclose); + ~WebWindow(); + + QString resultStr; + + QWebEnginePage *GetWebEnginePage() { return mWebEnginePage; } + +protected: + void closeEvent(QCloseEvent *); + +signals: + void windowClosed(); + void callbackCalled(const QString &inURLString); + +private slots: + void onCallbackCatched(const QString &inURLString); + void onCallbackCatchedSafe(); + +private: + Ui::WebWindow *ui; + + // Profile to not store cookies and cache data to the disk + QWebEngineProfile *mWebEngineProfile; + + // Webengine page using the Off-the-record profile + QWebEnginePage *mWebEnginePage; + + // Webview + QWebEngineView* mWebView; + + QString mCatchedOAuthURL; + bool mAutoclose; +}; + diff --git a/core/libs/dplugins/webservices/o2/examples/msgraphexternalinterceptordemo/webwindow.ui b/core/libs/dplugins/webservices/o2/examples/msgraphexternalinterceptordemo/webwindow.ui new file mode 100644 index 0000000000..d874d6b5d9 --- /dev/null +++ b/core/libs/dplugins/webservices/o2/examples/msgraphexternalinterceptordemo/webwindow.ui @@ -0,0 +1,16 @@ + + + WebWindow + + + + 0 + 0 + 600 + 500 + + + + + + diff --git a/core/libs/dplugins/webservices/o2/examples/facebookdemo/CMakeLists.txt b/core/libs/dplugins/webservices/o2/examples/vimeodemo/CMakeLists.txt similarity index 70% copy from core/libs/dplugins/webservices/o2/examples/facebookdemo/CMakeLists.txt copy to core/libs/dplugins/webservices/o2/examples/vimeodemo/CMakeLists.txt index afc3d27060..e319fec17e 100644 --- a/core/libs/dplugins/webservices/o2/examples/facebookdemo/CMakeLists.txt +++ b/core/libs/dplugins/webservices/o2/examples/vimeodemo/CMakeLists.txt @@ -1,43 +1,44 @@ cmake_minimum_required(VERSION 2.8.11) -project( fbexample ) +project( vimeoexample ) set(CMAKE_AUTOMOC ON) set(CMAKE_INCLUDE_CURRENT_DIR ON) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall") if(o2_WITH_QT5) # Qt5 packages find their own dependencies. find_package(Qt5Core REQUIRED) - find_package(Qt5Widgets REQUIRED) - find_package(Qt5Script REQUIRED) + find_package(Qt5Gui REQUIRED) find_package(Qt5Network REQUIRED) + find_package(Qt5Widgets REQUIRED) else(o2_WITH_QT5) set(QT_USE_QTNETWORK true) set(QT_USE_QTSCRIPT true) find_package(Qt4 REQUIRED) endif(o2_WITH_QT5) if (NOT o2_WITH_QT5) include( ${QT_USE_FILE} ) endif(NOT o2_WITH_QT5) include_directories(${CMAKE_SOURCE_DIR} ${CMAKE_BINARY_DIR} "../../src" ) -set(fb_SRCS +set(vimeo_SRCS main.cpp - fbdemo.cpp + vimeodemo.cpp + vimeodemo.h ) if(NOT o2_WITH_QT5) add_definitions(${QT4_DEFINITIONS}) endif(NOT o2_WITH_QT5) -add_executable( fbexample ${fb_SRCS} ) +add_executable( vimeoexample ${vimeo_SRCS} ) if(o2_WITH_QT5) - qt5_use_modules( fbexample Core Widgets Network ) - target_link_libraries( fbexample o2 ) + qt5_use_modules( vimeoexample Core Gui Network Widgets ) + target_link_libraries( vimeoexample o2 ) else(o2_WITH_QT5) - target_link_libraries( fbexample ${QT_LIBRARIES} o2 ) + target_link_libraries( vimeoexample ${QT_LIBRARIES} o2 ) endif(o2_WITH_QT5) diff --git a/core/libs/dplugins/webservices/o2/examples/vimeodemo/main.cpp b/core/libs/dplugins/webservices/o2/examples/vimeodemo/main.cpp new file mode 100644 index 0000000000..9886b57f1d --- /dev/null +++ b/core/libs/dplugins/webservices/o2/examples/vimeodemo/main.cpp @@ -0,0 +1,64 @@ +#include +#include +#include +#include + +#include +#include + +#include "vimeodemo.h" + +const char OPT_OAUTH_CODE[] = "-o"; + +class Helper : public QObject { + Q_OBJECT + +public: + Helper() : QObject(), demo_(this), waitForMsg_(false), msg_(QString()) {} + +public slots: + void run() { + connect(&demo_, SIGNAL(linkingFailed()), this, SLOT(onLinkingFailed())); + connect(&demo_, SIGNAL(linkingSucceeded()), this, SLOT(onLinkingSucceeded())); + connect(&demo_, SIGNAL(userNameReceived()), this, SLOT(onUserNameReceived())); + connect(&demo_, SIGNAL(userNameFailed()), this, SLOT(onUserNameFailed())); + + demo_.doOAuth(O2::GrantFlowAuthorizationCode); + } + + void onLinkingFailed() { + qDebug() << "Linking failed!"; + qApp->exit(1); + } + + void onLinkingSucceeded() { + qDebug() << "Linking succeeded!"; + demo_.getUserName(); + } + + void onUserNameFailed() { + qDebug() << "Error getting user name!"; + qApp->exit(1); + } + + void onUserNameReceived() { + qDebug() << "User namereceived!"; + qApp->quit(); + } + +private: + VimeoDemo demo_; + bool waitForMsg_; + QString msg_; +}; + +int main(int argc, char *argv[]) { + QApplication a(argc, argv); + QCoreApplication::setOrganizationName("O2"); + QCoreApplication::setApplicationName("Vimeo Test"); + Helper helper; + QTimer::singleShot(0, &helper, SLOT(run())); + return a.exec(); +} + +#include "main.moc" diff --git a/core/libs/dplugins/webservices/o2/examples/vimeodemo/vimeodemo.cpp b/core/libs/dplugins/webservices/o2/examples/vimeodemo/vimeodemo.cpp new file mode 100644 index 0000000000..adfb373607 --- /dev/null +++ b/core/libs/dplugins/webservices/o2/examples/vimeodemo/vimeodemo.cpp @@ -0,0 +1,123 @@ +#include +#include +#include +#include +#include +#include +#include + +#include "vimeodemo.h" +#include "o0globals.h" +#include "o0settingsstore.h" +#include "o2requestor.h" + +const char VIMEO_APP_KEY[] = "YOUR_VIMEO_APP_KEY"; +const char VIMEO_APP_SECRET[] = "YOUT_VIMEO_APP_SECRET"; +const char VIMEO_SCOPE[] = "public"; +const char VIMEO_USER_INFO_URL[] = "https://api.vimeo.com/me"; + +const int localPort = 8888; + +#define QENUM_NAME(o,e,v) (o::staticMetaObject.enumerator(o::staticMetaObject.indexOfEnumerator(#e)).valueToKey((v))) +#define GRANTFLOW_STR(v) QString(QENUM_NAME(O2, GrantFlow, v)) + +VimeoDemo::VimeoDemo(QObject *parent) : + QObject(parent), requestId_(0) { + o2Vimeo_ = new O2Vimeo(this); + + o2Vimeo_->setClientId(VIMEO_APP_KEY); + o2Vimeo_->setClientSecret(VIMEO_APP_SECRET); + o2Vimeo_->setLocalPort(localPort); + o2Vimeo_->setScope(VIMEO_SCOPE); + + // Create a store object for writing the received tokens + O0SettingsStore *store = new O0SettingsStore(O2_ENCRYPTION_KEY); + store->setGroupKey("google"); + o2Vimeo_->setStore(store); + + connect(o2Vimeo_, SIGNAL(linkedChanged()), this, SLOT(onLinkedChanged())); + connect(o2Vimeo_, SIGNAL(linkingFailed()), this, SIGNAL(linkingFailed())); + connect(o2Vimeo_, SIGNAL(linkingSucceeded()), this, SLOT(onLinkingSucceeded())); + connect(o2Vimeo_, SIGNAL(openBrowser(QUrl)), this, SLOT(onOpenBrowser(QUrl))); + connect(o2Vimeo_, SIGNAL(closeBrowser()), this, SLOT(onCloseBrowser())); +} + +void VimeoDemo::doOAuth(O2::GrantFlow grantFlowType) { + qDebug() << "Starting OAuth 2 with grant flow type" << GRANTFLOW_STR(grantFlowType) << "..."; + o2Vimeo_->setGrantFlow(grantFlowType); + o2Vimeo_->unlink(); + o2Vimeo_->link(); +} + +void VimeoDemo::getUserName() { + if (!o2Vimeo_->linked()) { + qWarning() << "ERROR: Application is not linked!"; + emit linkingFailed(); + return; + } + + QString userInfoURL = QString(VIMEO_USER_INFO_URL); + QNetworkRequest request = QNetworkRequest(QUrl(userInfoURL)); + QNetworkAccessManager *mgr = new QNetworkAccessManager(this); + O2Requestor *requestor = new O2Requestor(mgr, o2Vimeo_, this); + requestId_ = requestor->get(request); + connect(requestor, SIGNAL(finished(int, QNetworkReply::NetworkError, QByteArray)), + this, SLOT(onFinished(int, QNetworkReply::NetworkError, QByteArray)) + ); + qDebug() << "Getting user channel info... Please wait."; +} + +void VimeoDemo::onOpenBrowser(const QUrl &url) { + QDesktopServices::openUrl(url); +} + +void VimeoDemo::onCloseBrowser() { +} + +void VimeoDemo::onLinkedChanged() { + qDebug() << "Link changed!"; +} + +void VimeoDemo::onLinkingSucceeded() { + if (!o2Vimeo_->linked()) { + return; + } + QVariantMap extraTokens = o2Vimeo_->extraTokens(); + if (!extraTokens.isEmpty()) { + emit extraTokensReady(extraTokens); + qDebug() << "Extra tokens in response:"; + foreach (QString key, extraTokens.keys()) { + qDebug() << "\t" << key << ":" << (extraTokens.value(key).toString().left(3) + "..."); + } + } + emit linkingSucceeded(); +} + +void VimeoDemo::onFinished(int requestId, QNetworkReply::NetworkError error, QByteArray replyData) { + if (requestId != requestId_) + return; + + if (error != QNetworkReply::NoError) { + qWarning() << "Reply error:" << error; + emit userNameFailed(); + return; + } + + QString reply(replyData); + bool errorFound = reply.contains("error"); + if (errorFound) { + qDebug() << "Request failed"; + emit userNameFailed(); + return; + } + + QRegExp nameRE("\"name\":\"([^\"]+)\""); + if (nameRE.indexIn(reply) == -1) { + qDebug() << "Can not parse reply:" << reply; + emit userNameFailed(); + return; + } + + qInfo() << "User name: " << nameRE.cap(1); + emit userNameReceived(); +} diff --git a/core/libs/dplugins/webservices/o2/examples/vimeodemo/vimeodemo.h b/core/libs/dplugins/webservices/o2/examples/vimeodemo/vimeodemo.h new file mode 100644 index 0000000000..8d53502230 --- /dev/null +++ b/core/libs/dplugins/webservices/o2/examples/vimeodemo/vimeodemo.h @@ -0,0 +1,38 @@ +#ifndef VMDEMO_H +#define VMDEMO_H + +#include + +#include "o2vimeo.h" + +class VimeoDemo : public QObject +{ + Q_OBJECT + +public: + explicit VimeoDemo(QObject *parent = 0); + +signals: + void extraTokensReady(const QVariantMap &extraTokens); + void linkingFailed(); + void linkingSucceeded(); + void userNameReceived(); + void userNameFailed(); + +public slots: + void doOAuth(O2::GrantFlow grantFlowType); + void getUserName(); + +private slots: + void onLinkedChanged(); + void onLinkingSucceeded(); + void onOpenBrowser(const QUrl &url); + void onCloseBrowser(); + void onFinished(int, QNetworkReply::NetworkError, QByteArray); + +private: + O2Vimeo *o2Vimeo_; + int requestId_; +}; + +#endif // VMDEMO_H diff --git a/core/libs/dplugins/webservices/o2/examples/vimeodemo/vimeodemo.pro b/core/libs/dplugins/webservices/o2/examples/vimeodemo/vimeodemo.pro new file mode 100644 index 0000000000..6f09825335 --- /dev/null +++ b/core/libs/dplugins/webservices/o2/examples/vimeodemo/vimeodemo.pro @@ -0,0 +1,16 @@ +QT += core gui network + +greaterThan(QT_MAJOR_VERSION, 4) { + QT += widgets +} + +include(../../src/src.pri) + +TARGET = vimeodemo +TEMPLATE = app + +SOURCES += main.cpp \ + vimeodemo.cpp + +HEADERS += \ + vimeodemo.h diff --git a/core/libs/dplugins/webservices/o2/examples/facebookdemo/CMakeLists.txt b/core/libs/dplugins/webservices/o2/examples/youtubedemo/CMakeLists.txt similarity index 71% copy from core/libs/dplugins/webservices/o2/examples/facebookdemo/CMakeLists.txt copy to core/libs/dplugins/webservices/o2/examples/youtubedemo/CMakeLists.txt index afc3d27060..974aa9cc37 100644 --- a/core/libs/dplugins/webservices/o2/examples/facebookdemo/CMakeLists.txt +++ b/core/libs/dplugins/webservices/o2/examples/youtubedemo/CMakeLists.txt @@ -1,43 +1,44 @@ cmake_minimum_required(VERSION 2.8.11) -project( fbexample ) +project( ytexample ) set(CMAKE_AUTOMOC ON) set(CMAKE_INCLUDE_CURRENT_DIR ON) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall") if(o2_WITH_QT5) # Qt5 packages find their own dependencies. find_package(Qt5Core REQUIRED) - find_package(Qt5Widgets REQUIRED) - find_package(Qt5Script REQUIRED) + find_package(Qt5Gui REQUIRED) find_package(Qt5Network REQUIRED) + find_package(Qt5Widgets REQUIRED) else(o2_WITH_QT5) set(QT_USE_QTNETWORK true) set(QT_USE_QTSCRIPT true) find_package(Qt4 REQUIRED) endif(o2_WITH_QT5) if (NOT o2_WITH_QT5) include( ${QT_USE_FILE} ) endif(NOT o2_WITH_QT5) include_directories(${CMAKE_SOURCE_DIR} ${CMAKE_BINARY_DIR} "../../src" ) -set(fb_SRCS +set(yt_SRCS main.cpp - fbdemo.cpp + ytdemo.cpp + ytdemo.h ) if(NOT o2_WITH_QT5) add_definitions(${QT4_DEFINITIONS}) endif(NOT o2_WITH_QT5) -add_executable( fbexample ${fb_SRCS} ) +add_executable( ytexample ${yt_SRCS} ) if(o2_WITH_QT5) - qt5_use_modules( fbexample Core Widgets Network ) - target_link_libraries( fbexample o2 ) + qt5_use_modules( ytexample Core Gui Network Widgets ) + target_link_libraries( ytexample o2 ) else(o2_WITH_QT5) - target_link_libraries( fbexample ${QT_LIBRARIES} o2 ) + target_link_libraries( ytexample ${QT_LIBRARIES} o2 ) endif(o2_WITH_QT5) diff --git a/core/libs/dplugins/webservices/o2/examples/youtubedemo/main.cpp b/core/libs/dplugins/webservices/o2/examples/youtubedemo/main.cpp new file mode 100644 index 0000000000..5c91f8e985 --- /dev/null +++ b/core/libs/dplugins/webservices/o2/examples/youtubedemo/main.cpp @@ -0,0 +1,61 @@ +#include +#include +#include +#include + +#include "ytdemo.h" + +const char OPT_OAUTH_CODE[] = "-o"; + +class Helper : public QObject { + Q_OBJECT + +public: + Helper() : QObject(), ytdemo_(this), waitForMsg_(false), msg_(QString()) {} + +public slots: + void run() { + connect(&ytdemo_, SIGNAL(linkingFailed()), this, SLOT(onLinkingFailed())); + connect(&ytdemo_, SIGNAL(linkingSucceeded()), this, SLOT(onLinkingSucceeded())); + connect(&ytdemo_, SIGNAL(channelInfoReceived()), this, SLOT(onChannelInfoReceived())); + connect(&ytdemo_, SIGNAL(channelInfoFailed()), this, SLOT(onChannelInfoFailed())); + + ytdemo_.doOAuth(O2::GrantFlowAuthorizationCode); + } + + void onLinkingFailed() { + qDebug() << "Linking failed!"; + qApp->exit(1); + } + + void onLinkingSucceeded() { + qDebug() << "Linking succeeded!"; + ytdemo_.getUserChannelInfo(); + } + + void onChannelInfoFailed() { + qDebug() << "Error getting channel info!"; + qApp->exit(1); + } + + void onChannelInfoReceived() { + qDebug() << "Channel info received!"; + qApp->quit(); + } + +private: + YTDemo ytdemo_; + bool waitForMsg_; + QString msg_; +}; + +int main(int argc, char *argv[]) { + QApplication a(argc, argv); + QCoreApplication::setOrganizationName("O2"); + QCoreApplication::setApplicationName("YouTube Test"); + Helper helper; + QTimer::singleShot(0, &helper, SLOT(run())); + return a.exec(); +} + +#include "main.moc" diff --git a/core/libs/dplugins/webservices/o2/examples/youtubedemo/youtubedemo.pro b/core/libs/dplugins/webservices/o2/examples/youtubedemo/youtubedemo.pro new file mode 100644 index 0000000000..ba67eedccb --- /dev/null +++ b/core/libs/dplugins/webservices/o2/examples/youtubedemo/youtubedemo.pro @@ -0,0 +1,16 @@ +QT += core gui network + +greaterThan(QT_MAJOR_VERSION, 4) { + QT += widgets +} + +include(../../src/src.pri) + +TARGET = youtubedemo +TEMPLATE = app + +SOURCES += main.cpp \ + ytdemo.cpp + +HEADERS += \ + ytdemo.h diff --git a/core/libs/dplugins/webservices/o2/examples/youtubedemo/ytdemo.cpp b/core/libs/dplugins/webservices/o2/examples/youtubedemo/ytdemo.cpp new file mode 100644 index 0000000000..4293e35304 --- /dev/null +++ b/core/libs/dplugins/webservices/o2/examples/youtubedemo/ytdemo.cpp @@ -0,0 +1,116 @@ +#include +#include +#include +#include +#include +#include + +#include "ytdemo.h" +#include "o0globals.h" +#include "o0settingsstore.h" +#include "o2requestor.h" + +const char GOOGLE_APP_KEY[] = "YOUR_YOUTUBE_APP_KEY"; +const char GOOGLE_APP_SECRET[] = "YOUR_YOUTUBE_APP_SECRET"; +const char YOUTUBE_SCOPE[] = "https://www.googleapis.com/auth/youtube.upload https://www.googleapis.com/auth/youtube"; +const char YOUTUBE_CHANNELS_LIST_URL[] = "https://www.googleapis.com/youtube/v3/channels?part=status&mine=true"; + +const int localPort = 8888; + +#define QENUM_NAME(o,e,v) (o::staticMetaObject.enumerator(o::staticMetaObject.indexOfEnumerator(#e)).valueToKey((v))) +#define GRANTFLOW_STR(v) QString(QENUM_NAME(O2, GrantFlow, v)) + +YTDemo::YTDemo(QObject *parent) : + QObject(parent), requestId_(0) { + o2Google_ = new O2Google(this); + + o2Google_->setClientId(GOOGLE_APP_KEY); + o2Google_->setClientSecret(GOOGLE_APP_SECRET); + o2Google_->setLocalPort(localPort); + o2Google_->setScope(YOUTUBE_SCOPE); + + // Create a store object for writing the received tokens + O0SettingsStore *store = new O0SettingsStore(O2_ENCRYPTION_KEY); + store->setGroupKey("google"); + o2Google_->setStore(store); + + connect(o2Google_, SIGNAL(linkedChanged()), this, SLOT(onLinkedChanged())); + connect(o2Google_, SIGNAL(linkingFailed()), this, SIGNAL(linkingFailed())); + connect(o2Google_, SIGNAL(linkingSucceeded()), this, SLOT(onLinkingSucceeded())); + connect(o2Google_, SIGNAL(openBrowser(QUrl)), this, SLOT(onOpenBrowser(QUrl))); + connect(o2Google_, SIGNAL(closeBrowser()), this, SLOT(onCloseBrowser())); +} + +void YTDemo::doOAuth(O2::GrantFlow grantFlowType) { + qDebug() << "Starting OAuth 2 with grant flow type" << GRANTFLOW_STR(grantFlowType) << "..."; + o2Google_->setGrantFlow(grantFlowType); + o2Google_->unlink(); + o2Google_->link(); +} + +void YTDemo::getUserChannelInfo() { + if (!o2Google_->linked()) { + qWarning() << "ERROR: Application is not linked!"; + emit linkingFailed(); + return; + } + + QString channelsListUrl = QString(YOUTUBE_CHANNELS_LIST_URL); + QNetworkRequest request = QNetworkRequest(QUrl(channelsListUrl)); + QNetworkAccessManager *mgr = new QNetworkAccessManager(this); + O2Requestor *requestor = new O2Requestor(mgr, o2Google_, this); + requestId_ = requestor->get(request); + connect(requestor, SIGNAL(finished(int, QNetworkReply::NetworkError, QByteArray)), + this, SLOT(onFinished(int, QNetworkReply::NetworkError, QByteArray)) + ); + qDebug() << "Getting user channel info... Please wait."; +} + +void YTDemo::onOpenBrowser(const QUrl &url) { + QDesktopServices::openUrl(url); +} + +void YTDemo::onCloseBrowser() { +} + +void YTDemo::onLinkedChanged() { + qDebug() << "Link changed!"; +} + +void YTDemo::onLinkingSucceeded() { + O2Google *o2t = qobject_cast(sender()); + if (!o2t->linked()) { + return; + } + QVariantMap extraTokens = o2t->extraTokens(); + if (!extraTokens.isEmpty()) { + emit extraTokensReady(extraTokens); + qDebug() << "Extra tokens in response:"; + foreach (QString key, extraTokens.keys()) { + qDebug() << "\t" << key << ":" << (extraTokens.value(key).toString().left(3) + "..."); + } + } + emit linkingSucceeded(); +} + +void YTDemo::onFinished(int requestId, QNetworkReply::NetworkError error, QByteArray replyData) { + if (requestId != requestId_) + return; + + if (error != QNetworkReply::NoError) { + qWarning() << "Reply error:" << error; + emit channelInfoFailed(); + return; + } + + QString reply(replyData); + bool errorFound = reply.contains("error"); + if (errorFound) { + qDebug() << "Request failed"; + emit channelInfoFailed(); + return; + } + + qInfo() << "Channel info: " << reply; + emit channelInfoReceived(); +} diff --git a/core/libs/dplugins/webservices/o2/examples/youtubedemo/ytdemo.h b/core/libs/dplugins/webservices/o2/examples/youtubedemo/ytdemo.h new file mode 100644 index 0000000000..6be20af505 --- /dev/null +++ b/core/libs/dplugins/webservices/o2/examples/youtubedemo/ytdemo.h @@ -0,0 +1,38 @@ +#ifndef YTDEMO_H +#define YTDEMO_H + +#include + +#include "o2google.h" + +class YTDemo : public QObject +{ + Q_OBJECT + +public: + explicit YTDemo(QObject *parent = 0); + +signals: + void extraTokensReady(const QVariantMap &extraTokens); + void linkingFailed(); + void linkingSucceeded(); + void channelInfoReceived(); + void channelInfoFailed(); + +public slots: + void doOAuth(O2::GrantFlow grantFlowType); + void getUserChannelInfo(); + +private slots: + void onLinkedChanged(); + void onLinkingSucceeded(); + void onOpenBrowser(const QUrl &url); + void onCloseBrowser(); + void onFinished(int, QNetworkReply::NetworkError, QByteArray); + +private: + O2Google *o2Google_; + int requestId_; +}; + +#endif // YTDEMO_H diff --git a/core/libs/dplugins/webservices/o2/src/CMakeLists.txt b/core/libs/dplugins/webservices/o2/src/CMakeLists.txt index cad487b8ef..2fc3a37eb3 100644 --- a/core/libs/dplugins/webservices/o2/src/CMakeLists.txt +++ b/core/libs/dplugins/webservices/o2/src/CMakeLists.txt @@ -1,240 +1,269 @@ cmake_minimum_required(VERSION 2.8.11) set(CMAKE_AUTOMOC ON) set(CMAKE_INCLUDE_CURRENT_DIR ON) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall") if(o2_WITH_QT5) find_package(Qt5 COMPONENTS Core Network REQUIRED) else(o2_WITH_QT5) set(QT_USE_QTNETWORK true) set(QT_USE_QTSCRIPT true) find_package(Qt4 REQUIRED) endif(o2_WITH_QT5) #find_package(QJson REQUIRED) if (NOT o2_WITH_QT5) include( ${QT_USE_FILE} ) endif(NOT o2_WITH_QT5) set( o2_SRCS o2.cpp o2reply.cpp o2replyserver.cpp o2requestor.cpp o2simplecrypt.cpp o0settingsstore.cpp o0baseauth.cpp ) set( o2_HDRS o2.h o2reply.h o2replyserver.h o2requestor.h o0abstractstore.h o0baseauth.h o0export.h o0globals.h o0requestparameter.h o0settingsstore.h o0simplecrypt.h ) if(o2_WITH_OAUTH1) set( o2_SRCS ${o2_SRCS} o1.cpp o1requestor.cpp o1timedreply.cpp ) set( o2_HDRS ${o2_HDRS} o1.h o1requestor.h o1timedreply.h o1freshbooks.h ) endif(o2_WITH_OAUTH1) if(o2_WITH_TWITTER) set( o2_SRCS ${o2_SRCS} oxtwitter.cpp ) set( o2_HDRS ${o2_HDRS} o1twitter.h oxtwitter.h ) endif(o2_WITH_TWITTER) if(o2_WITH_DROPBOX) set( o2_HDRS ${o2_HDRS} o1dropbox.h ) endif(o2_WITH_DROPBOX) if(o2_WITH_GOOGLE) set( o2_SRCS ${o2_SRCS} o2gft.cpp o2google.cpp ) set( o2_HDRS ${o2_HDRS} o2gft.h o2google.h ) endif(o2_WITH_GOOGLE) +if(o2_WITH_VIMEO) + set( o2_SRCS + ${o2_SRCS} + o2vimeo.cpp + ) + set( o2_HDRS + ${o2_HDRS} + o2vimeo.h + ) +endif(o2_WITH_VIMEO) + if(o2_WITH_FACEBOOK) set( o2_SRCS ${o2_SRCS} o2facebook.cpp ) set( o2_HDRS ${o2_HDRS} o2facebook.h ) endif(o2_WITH_FACEBOOK) if(o2_WITH_SKYDRIVE) set( o2_SRCS ${o2_SRCS} o2skydrive.cpp ) set( o2_HDRS ${o2_HDRS} o2skydrive.h ) endif(o2_WITH_SKYDRIVE) if(o2_WITH_FLICKR) set( o2_HDRS ${o2_HDRS} o1flickr.h ) endif(o2_WITH_FLICKR) if(o2_WITH_HUBIC) set( o2_SRCS ${o2_SRCS} o2hubic.cpp ) set( o2_HDRS ${o2_HDRS} o2hubic.h ) endif(o2_WITH_HUBIC) if(o2_WITH_SPOTIFY) set( o2_SRCS ${o2_SRCS} o2spotify.cpp ) set( o2_HDRS ${o2_HDRS} o2spotify.h ) endif(o2_WITH_SPOTIFY) if(o2_WITH_SURVEYMONKEY) set( o2_SRCS ${o2_SRCS} o2surveymonkey.cpp ) set( o2_HDRS ${o2_HDRS} o2surveymonkey.h ) endif(o2_WITH_SURVEYMONKEY) if(o2_WITH_SMUGMUG) set( o2_SRCS ${o2_SRCS} o1smugmug.cpp ) set( o2_HDRS ${o2_HDRS} o1smugmug.h ) endif(o2_WITH_SMUGMUG) +if(o2_WITH_MSGRAPH) + set( o2_SRCS + ${o2_SRCS} + o2msgraph.cpp + ) + set( o2_HDRS + ${o2_HDRS} + o2msgraph.h + ) +endif(o2_WITH_MSGRAPH) if(o2_WITH_KEYCHAIN) if (Qt5Core_DIR) find_package(Qt5Keychain REQUIRED) else() find_package(QtKeychain REQUIRED) endif() if(QTKEYCHAIN_FOUND OR QT5KEYCHAIN_FOUND) MESSAGE("Found QTKeychain") list(APPEND LINK_TARGETS ${QTKEYCHAIN_LIBRARY}) include_directories(${QTKEYCHAIN_INCLUDE_DIR}) set( o2_SRCS ${o2_SRCS} o0keychainstore.cpp ) set( o2_HDRS ${o2_HDRS} o0keychainstore.h ) else() MESSAGE("Qt5Keychain or QtKeychain is required") endif() endif(o2_WITH_KEYCHAIN) if(NOT o2_WITH_QT5) add_definitions(${QT4_DEFINITIONS}) endif(NOT o2_WITH_QT5) if(BUILD_SHARED_LIBS AND APPLE AND POLICY CMP0042) # in CMake >= 2.8.12 cmake_policy(SET CMP0042 OLD) set(CMAKE_MACOSX_RPATH OFF) # don't embed @rpath in install name endif(BUILD_SHARED_LIBS AND APPLE AND POLICY CMP0042) add_library( o2 ${o2_SRCS} ${o2_HDRS} ) +if(BUILD_SHARED_LIBS) + add_definitions( -DO2_SHARED_LIB ) +endif(BUILD_SHARED_LIBS) + if(o2_WITH_QT5) target_link_libraries( o2 Qt5::Core Qt5::Network ${LINK_TARGETS}) else(o2_WITH_QT5) target_link_libraries( o2 ${QT_LIBRARIES} ${LINK_TARGETS}) endif(o2_WITH_QT5) if(BUILD_SHARED_LIBS) if(APPLE) set_target_properties(o2 PROPERTIES INSTALL_NAME_DIR ${CMAKE_INSTALL_PREFIX}/lib ) endif(APPLE) set_target_properties(o2 PROPERTIES VERSION ${PROJECT_VERSION} SOVERSION ${API_VERSION} ) else(BUILD_SHARED_LIBS) # needed for statically linked o2 in shared libs on x86_64 set_target_properties(o2 PROPERTIES POSITION_INDEPENDENT_CODE TRUE ) endif(BUILD_SHARED_LIBS) install(TARGETS o2 RUNTIME DESTINATION bin LIBRARY DESTINATION lib${o2_LIB_SUFFIX} ARCHIVE DESTINATION lib${o2_LIB_SUFFIX} ) install(FILES ${o2_HDRS} DESTINATION ${CMAKE_INSTALL_PREFIX}/include/o2 ) message(STATUS "Writing pkg-config file...") configure_file(${CMAKE_SOURCE_DIR}/o2.pc.cmake ${CMAKE_BINARY_DIR}/o2.pc @ONLY) install(FILES ${CMAKE_BINARY_DIR}/o2.pc DESTINATION "${CMAKE_INSTALL_PREFIX}/lib${o2_LIB_SUFFIX}/pkgconfig/") + +configure_file(${CMAKE_SOURCE_DIR}/o2-config.h.cmake ${CMAKE_BINARY_DIR}/o2-config.h @ONLY) +install(FILES ${CMAKE_BINARY_DIR}/o2-config.h DESTINATION "${CMAKE_INSTALL_PREFIX}/include/o2") + diff --git a/core/libs/dplugins/webservices/o2/src/o0abstractstore.h b/core/libs/dplugins/webservices/o2/src/o0abstractstore.h index 28071180c6..d4b4309aff 100644 --- a/core/libs/dplugins/webservices/o2/src/o0abstractstore.h +++ b/core/libs/dplugins/webservices/o2/src/o0abstractstore.h @@ -1,24 +1,23 @@ #ifndef O0ABSTRACTSTORE_H #define O0ABSTRACTSTORE_H #include #include #include "o0export.h" /// Storage for strings. class O0_EXPORT O0AbstractStore: public QObject { - Q_OBJECT public: explicit O0AbstractStore(QObject *parent = 0): QObject(parent) { } /// Retrieve a string value by key. virtual QString value(const QString &key, const QString &defaultValue = QString()) = 0; /// Set a string value for a key. virtual void setValue(const QString &key, const QString &value) = 0; }; #endif // O0ABSTRACTSTORE_H diff --git a/core/libs/dplugins/webservices/o2/src/o0baseauth.cpp b/core/libs/dplugins/webservices/o2/src/o0baseauth.cpp index e7d385de43..054377159c 100644 --- a/core/libs/dplugins/webservices/o2/src/o0baseauth.cpp +++ b/core/libs/dplugins/webservices/o2/src/o0baseauth.cpp @@ -1,150 +1,160 @@ #include #include #include #include "o0baseauth.h" #include "o0globals.h" #include "o0settingsstore.h" #include "o2replyserver.h" static const quint16 DefaultLocalPort = 1965; O0BaseAuth::O0BaseAuth(QObject *parent, O0AbstractStore *store): QObject(parent), store_(0), useExternalWebInterceptor_(false), replyServer_(NULL) { localPort_ = DefaultLocalPort; setStore(store); } void O0BaseAuth::setStore(O0AbstractStore *store) { if (store_) { store_->deleteLater(); } if (store) { store_ = store; store_->setParent(this); } else { store_ = new O0SettingsStore(O2_ENCRYPTION_KEY, this); return; } } bool O0BaseAuth::linked() { QString key = QString(O2_KEY_LINKED).arg(clientId_); bool result = !store_->value(key).isEmpty(); qDebug() << "O0BaseAuth::linked:" << (result? "Yes": "No"); return result; } void O0BaseAuth::setLinked(bool v) { qDebug() << "O0BaseAuth::setLinked:" << (v? "true": "false"); bool oldValue = linked(); QString key = QString(O2_KEY_LINKED).arg(clientId_); store_->setValue(key, v? "1": ""); if (oldValue != v) { Q_EMIT linkedChanged(); } } QString O0BaseAuth::tokenSecret() { QString key = QString(O2_KEY_TOKEN_SECRET).arg(clientId_); return store_->value(key); } void O0BaseAuth::setTokenSecret(const QString &v) { QString key = QString(O2_KEY_TOKEN_SECRET).arg(clientId_); store_->setValue(key, v); Q_EMIT tokenSecretChanged(); } QString O0BaseAuth::token() { QString key = QString(O2_KEY_TOKEN).arg(clientId_); return store_->value(key); } void O0BaseAuth::setToken(const QString &v) { QString key = QString(O2_KEY_TOKEN).arg(clientId_); store_->setValue(key, v); Q_EMIT tokenChanged(); } QString O0BaseAuth::clientId() { return clientId_; } void O0BaseAuth::setClientId(const QString &value) { clientId_ = value; Q_EMIT clientIdChanged(); } QString O0BaseAuth::clientSecret() { return clientSecret_; } void O0BaseAuth::setClientSecret(const QString &value) { clientSecret_ = value; Q_EMIT clientSecretChanged(); } bool O0BaseAuth::useExternalWebInterceptor() { return useExternalWebInterceptor_; } void O0BaseAuth::setUseExternalWebInterceptor(bool useExternalWebInterceptor) { useExternalWebInterceptor_ = useExternalWebInterceptor; } QByteArray O0BaseAuth::replyContent() const { - if(replyServer_ != NULL) { - return replyServer_->replyContent(); - } - - return QByteArray(); + return replyContent_; } void O0BaseAuth::setReplyContent(const QByteArray &value) { - if(replyServer_ != NULL) { - return replyServer_->setReplyContent(value); + replyContent_ = value; + if (replyServer_) { + replyServer_->setReplyContent(replyContent_); } } int O0BaseAuth::localPort() { return localPort_; } void O0BaseAuth::setLocalPort(int value) { qDebug() << "O0BaseAuth::setLocalPort:" << value; localPort_ = value; Q_EMIT localPortChanged(); } QVariantMap O0BaseAuth::extraTokens() { QString key = QString(O2_KEY_EXTRA_TOKENS).arg(clientId_); QString value = store_->value(key); QByteArray bytes = QByteArray::fromBase64(value.toLatin1()); QDataStream stream(&bytes, QIODevice::ReadOnly); stream >> extraTokens_; return extraTokens_; } void O0BaseAuth::setExtraTokens(QVariantMap extraTokens) { extraTokens_ = extraTokens; QByteArray bytes; QDataStream stream(&bytes, QIODevice::WriteOnly); stream << extraTokens; QString key = QString(O2_KEY_EXTRA_TOKENS).arg(clientId_); store_->setValue(key, bytes.toBase64()); Q_EMIT extraTokensChanged(); } +void O0BaseAuth::setReplyServer(O2ReplyServer * server) +{ + delete replyServer_; + + replyServer_ = server; + replyServer_->setReplyContent(replyContent_); +} + +O2ReplyServer * O0BaseAuth::replyServer() const +{ + return replyServer_; +} + QByteArray O0BaseAuth::createQueryParameters(const QList ¶meters) { QByteArray ret; bool first = true; foreach (O0RequestParameter h, parameters) { if (first) { first = false; } else { ret.append("&"); } ret.append(QUrl::toPercentEncoding(h.name) + "=" + QUrl::toPercentEncoding(h.value)); } return ret; } diff --git a/core/libs/dplugins/webservices/o2/src/o0baseauth.h b/core/libs/dplugins/webservices/o2/src/o0baseauth.h index 0cf887f00a..356c393098 100644 --- a/core/libs/dplugins/webservices/o2/src/o0baseauth.h +++ b/core/libs/dplugins/webservices/o2/src/o0baseauth.h @@ -1,136 +1,144 @@ #ifndef O0BASEAUTH_H #define O0BASEAUTH_H #include #include #include #include #include #include #include "o0export.h" #include "o0abstractstore.h" #include "o0requestparameter.h" class O2ReplyServer; /// Base class of OAuth authenticators class O0_EXPORT O0BaseAuth : public QObject { Q_OBJECT public: explicit O0BaseAuth(QObject *parent = 0, O0AbstractStore *store = 0); public: /// Are we authenticated? Q_PROPERTY(bool linked READ linked WRITE setLinked NOTIFY linkedChanged) bool linked(); /// Authentication token. Q_PROPERTY(QString token READ token NOTIFY tokenChanged) QString token(); /// Authentication token secret. Q_PROPERTY(QString tokenSecret READ tokenSecret NOTIFY tokenSecretChanged) QString tokenSecret(); /// Provider-specific extra tokens, available after a successful authentication Q_PROPERTY(QVariantMap extraTokens READ extraTokens NOTIFY extraTokensChanged) QVariantMap extraTokens(); /// Client application ID. /// O1 instances with the same (client ID, client secret) share the same "linked", "token" and "tokenSecret" properties. Q_PROPERTY(QString clientId READ clientId WRITE setClientId NOTIFY clientIdChanged) QString clientId(); void setClientId(const QString &value); /// Client application secret. /// O1 instances with the same (client ID, client secret) share the same "linked", "token" and "tokenSecret" properties. Q_PROPERTY(QString clientSecret READ clientSecret WRITE setClientSecret NOTIFY clientSecretChanged) QString clientSecret(); void setClientSecret(const QString &value); /// Should we use a reply server (default) or an external web interceptor? Q_PROPERTY(bool useExternalWebInterceptor READ useExternalWebInterceptor WRITE setUseExternalWebInterceptor) bool useExternalWebInterceptor(); void setUseExternalWebInterceptor(bool inUseExternalWebInterceptor); /// Page content on local host after successful oauth. /// Provide it in case you do not want to close the browser, but display something Q_PROPERTY(QByteArray replyContent READ replyContent WRITE setReplyContent) QByteArray replyContent() const; void setReplyContent(const QByteArray &value); /// TCP port number to use in local redirections. /// The OAuth "redirect_uri" will be set to "http://localhost:/". /// If localPort is set to 0 (default), O2 will replace it with a free one. Q_PROPERTY(int localPort READ localPort WRITE setLocalPort NOTIFY localPortChanged) int localPort(); void setLocalPort(int value); /// Sets the storage object to use for storing the OAuth tokens on a peristent medium void setStore(O0AbstractStore *store); /// Construct query string from list of headers static QByteArray createQueryParameters(const QList ¶meters); public Q_SLOTS: /// Authenticate. Q_INVOKABLE virtual void link() = 0; /// De-authenticate. Q_INVOKABLE virtual void unlink() = 0; Q_SIGNALS: /// Emitted when client needs to open a web browser window, with the given URL. void openBrowser(const QUrl &url); /// Emitted when client can close the browser window. void closeBrowser(); /// Emitted when authentication/deauthentication succeeded. void linkingSucceeded(); /// Emitted when authentication/deauthentication failed. void linkingFailed(); // Property change signals void linkedChanged(); void clientIdChanged(); void clientSecretChanged(); void localPortChanged(); void tokenChanged(); void tokenSecretChanged(); void extraTokensChanged(); protected: /// Set authentication token. void setToken(const QString &v); /// Set authentication token secret. void setTokenSecret(const QString &v); /// Set the linked state void setLinked(bool v); /// Set extra tokens found in OAuth response void setExtraTokens(QVariantMap extraTokens); + /// Set local reply server + void setReplyServer(O2ReplyServer *server); + + O2ReplyServer * replyServer() const; + protected: QString clientId_; QString clientSecret_; QString redirectUri_; QString requestToken_; QString requestTokenSecret_; QUrl requestTokenUrl_; QUrl authorizeUrl_; QUrl accessTokenUrl_; quint16 localPort_; O0AbstractStore *store_; QVariantMap extraTokens_; bool useExternalWebInterceptor_; + QByteArray replyContent_; + +private: O2ReplyServer *replyServer_; }; #endif // O0BASEAUTH diff --git a/core/libs/dplugins/webservices/o2/src/o0globals.h b/core/libs/dplugins/webservices/o2/src/o0globals.h index f04a149c87..496b36dadb 100644 --- a/core/libs/dplugins/webservices/o2/src/o0globals.h +++ b/core/libs/dplugins/webservices/o2/src/o0globals.h @@ -1,62 +1,64 @@ #ifndef O0GLOBALS_H #define O0GLOBALS_H // Common constants const char O2_ENCRYPTION_KEY[] = "12345678"; const char O2_CALLBACK_URL[] = "http://127.0.0.1:%1/"; const char O2_MIME_TYPE_XFORM[] = "application/x-www-form-urlencoded"; const char O2_MIME_TYPE_JSON[] = "application/json"; // QSettings key names const char O2_KEY_TOKEN[] = "token.%1"; const char O2_KEY_TOKEN_SECRET[] = "tokensecret.%1"; const char O2_KEY_CODE[] = "code.%1"; const char O2_KEY_EXPIRES[] = "expires.%1"; const char O2_KEY_REFRESH_TOKEN[] = "refreshtoken.%1"; const char O2_KEY_LINKED[] = "linked.%1"; const char O2_KEY_EXTRA_TOKENS[] = "extratokens.%1"; // OAuth 1/1.1 Request Parameters const char O2_OAUTH_CALLBACK[] = "oauth_callback"; const char O2_OAUTH_CONSUMER_KEY[] = "oauth_consumer_key"; const char O2_OAUTH_NONCE[] = "oauth_nonce"; const char O2_OAUTH_SIGNATURE[] = "oauth_signature"; const char O2_OAUTH_SIGNATURE_METHOD[] = "oauth_signature_method"; const char O2_OAUTH_TIMESTAMP[] = "oauth_timestamp"; const char O2_OAUTH_VERSION[] = "oauth_version"; // OAuth 1/1.1 Response Parameters const char O2_OAUTH_TOKEN[] = "oauth_token"; const char O2_OAUTH_TOKEN_SECRET[] = "oauth_token_secret"; const char O2_OAUTH_CALLBACK_CONFIRMED[] = "oauth_callback_confirmed"; const char O2_OAUTH_VERFIER[] = "oauth_verifier"; // OAuth 2 Request Parameters const char O2_OAUTH2_RESPONSE_TYPE[] = "response_type"; const char O2_OAUTH2_CLIENT_ID[] = "client_id"; const char O2_OAUTH2_CLIENT_SECRET[] = "client_secret"; const char O2_OAUTH2_USERNAME[] = "username"; const char O2_OAUTH2_PASSWORD[] = "password"; const char O2_OAUTH2_REDIRECT_URI[] = "redirect_uri"; const char O2_OAUTH2_SCOPE[] = "scope"; const char O2_OAUTH2_GRANT_TYPE_CODE[] = "code"; const char O2_OAUTH2_GRANT_TYPE_TOKEN[] = "token"; const char O2_OAUTH2_GRANT_TYPE_PASSWORD[] = "password"; const char O2_OAUTH2_GRANT_TYPE[] = "grant_type"; const char O2_OAUTH2_API_KEY[] = "api_key"; +const char O2_OAUTH2_STATE[] = "state"; // OAuth 2 Response Parameters const char O2_OAUTH2_ACCESS_TOKEN[] = "access_token"; const char O2_OAUTH2_REFRESH_TOKEN[] = "refresh_token"; const char O2_OAUTH2_EXPIRES_IN[] = "expires_in"; // OAuth signature types const char O2_SIGNATURE_TYPE_HMAC_SHA1[] = "HMAC-SHA1"; const char O2_SIGNATURE_TYPE_PLAINTEXT[] = "PLAINTEXT"; // Parameter values const char O2_AUTHORIZATION_CODE[] = "authorization_code"; // Standard HTTP headers +const char O2_HTTP_HTTP_HEADER[] = "HTTP"; const char O2_HTTP_AUTHORIZATION_HEADER[] = "Authorization"; #endif // O0GLOBALS_H diff --git a/core/libs/dplugins/webservices/o2/src/o0simplecrypt.h b/core/libs/dplugins/webservices/o2/src/o0simplecrypt.h index 19b96fe19b..274e391185 100644 --- a/core/libs/dplugins/webservices/o2/src/o0simplecrypt.h +++ b/core/libs/dplugins/webservices/o2/src/o0simplecrypt.h @@ -1,227 +1,232 @@ /* Copyright (c) 2011, Andre Somers All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the Rathenau Instituut, Andre Somers nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL ANDRE SOMERS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef SIMPLECRYPT_H #define SIMPLECRYPT_H #include #include #include - +#if (QT_VERSION >= QT_VERSION_CHECK(5, 10, 0)) +#include +#endif #include "o0baseauth.h" /** @short Simple encryption and decryption of strings and byte arrays This class provides a simple implementation of encryption and decryption of strings and byte arrays. @warning The encryption provided by this class is NOT strong encryption. It may help to shield things from curious eyes, but it will NOT stand up to someone determined to break the encryption. Don't say you were not warned. The class uses a 64 bit key. Simply create an instance of the class, set the key, and use the encryptToString() method to calculate an encrypted version of the input string. To decrypt that string again, use an instance of SimpleCrypt initialized with the same key, and call the decryptToString() method with the encrypted string. If the key matches, the decrypted version of the string will be returned again. If you do not provide a key, or if something else is wrong, the encryption and decryption function will return an empty string or will return a string containing nonsense. lastError() will return a value indicating if the method was successful, and if not, why not. SimpleCrypt is prepared for the case that the encryption and decryption algorithm is changed in a later version, by prepending a version identifier to the cypertext. */ class O0_EXPORT O0SimpleCrypt { public: /** CompressionMode describes if compression will be applied to the data to be encrypted. */ enum CompressionMode { CompressionAuto, /*!< Only apply compression if that results in a shorter plaintext. */ CompressionAlways, /*!< Always apply compression. Note that for short inputs, a compression may result in longer data */ CompressionNever /*!< Never apply compression. */ }; /** IntegrityProtectionMode describes measures taken to make it possible to detect problems with the data or wrong decryption keys. Measures involve adding a checksum or a cryptograhpic hash to the data to be encrypted. This increases the length of the resulting cypertext, but makes it possible to check if the plaintext appears to be valid after decryption. */ enum IntegrityProtectionMode { ProtectionNone, /*!< The integrity of the encrypted data is not protected. It is not really possible to detect a wrong key, for instance. */ ProtectionChecksum,/*!< A simple checksum is used to verify that the data is in order. If not, an empty string is returned. */ ProtectionHash /*!< A cryptographic hash is used to verify the integrity of the data. This method produces a much stronger, but longer check */ }; /** Error describes the type of error that occurred. */ enum Error { ErrorNoError, /*!< No error occurred. */ ErrorNoKeySet, /*!< No key was set. You can not encrypt or decrypt without a valid key. */ ErrorUnknownVersion, /*!< The version of this data is unknown, or the data is otherwise not valid. */ ErrorIntegrityFailed /*!< The integrity check of the data failed. Perhaps the wrong key was used. */ }; /** Constructor. Constructs a SimpleCrypt instance without a valid key set on it. */ O0SimpleCrypt(); /** Constructor. Constructs a SimpleCrypt instance and initializes it with the given @arg key. */ explicit O0SimpleCrypt(quint64 key); /** (Re-) initializes the key with the given @arg key. */ void setKey(quint64 key); /** Returns true if SimpleCrypt has been initialized with a key. */ bool hasKey() const {return !m_keyParts.isEmpty();} /** Sets the compression mode to use when encrypting data. The default mode is Auto. Note that decryption is not influenced by this mode, as the decryption recognizes what mode was used when encrypting. */ void setCompressionMode(CompressionMode mode) {m_compressionMode = mode;} /** Returns the CompressionMode that is currently in use. */ CompressionMode compressionMode() const {return m_compressionMode;} /** Sets the integrity mode to use when encrypting data. The default mode is Checksum. Note that decryption is not influenced by this mode, as the decryption recognizes what mode was used when encrypting. */ void setIntegrityProtectionMode(IntegrityProtectionMode mode) {m_protectionMode = mode;} /** Returns the IntegrityProtectionMode that is currently in use. */ IntegrityProtectionMode integrityProtectionMode() const {return m_protectionMode;} /** Returns the last error that occurred. */ Error lastError() const {return m_lastError;} /** Encrypts the @arg plaintext string with the key the class was initialized with, and returns a cyphertext the result. The result is a base64 encoded version of the binary array that is the actual result of the string, so it can be stored easily in a text format. */ QString encryptToString(const QString& plaintext) ; /** Encrypts the @arg plaintext QByteArray with the key the class was initialized with, and returns a cyphertext the result. The result is a base64 encoded version of the binary array that is the actual result of the encryption, so it can be stored easily in a text format. */ QString encryptToString(QByteArray plaintext) ; /** Encrypts the @arg plaintext string with the key the class was initialized with, and returns a binary cyphertext in a QByteArray the result. This method returns a byte array, that is usable for storing a binary format. If you need a string you can store in a text file, use encryptToString() instead. */ QByteArray encryptToByteArray(const QString& plaintext) ; /** Encrypts the @arg plaintext QByteArray with the key the class was initialized with, and returns a binary cyphertext in a QByteArray the result. This method returns a byte array, that is usable for storing a binary format. If you need a string you can store in a text file, use encryptToString() instead. */ QByteArray encryptToByteArray(QByteArray plaintext) ; /** Decrypts a cyphertext string encrypted with this class with the set key back to the plain text version. If an error occurred, such as non-matching keys between encryption and decryption, an empty string or a string containing nonsense may be returned. */ QString decryptToString(const QString& cyphertext) ; /** Decrypts a cyphertext string encrypted with this class with the set key back to the plain text version. If an error occurred, such as non-matching keys between encryption and decryption, an empty string or a string containing nonsense may be returned. */ QByteArray decryptToByteArray(const QString& cyphertext) ; /** Decrypts a cyphertext binary encrypted with this class with the set key back to the plain text version. If an error occurred, such as non-matching keys between encryption and decryption, an empty string or a string containing nonsense may be returned. */ QString decryptToString(QByteArray cypher) ; /** Decrypts a cyphertext binary encrypted with this class with the set key back to the plain text version. If an error occurred, such as non-matching keys between encryption and decryption, an empty string or a string containing nonsense may be returned. */ QByteArray decryptToByteArray(QByteArray cypher) ; //enum to describe options that have been used for the encryption. Currently only one, but //that only leaves room for future extensions like adding a cryptographic hash... enum CryptoFlag{CryptoFlagNone = 0, CryptoFlagCompression = 0x01, CryptoFlagChecksum = 0x02, CryptoFlagHash = 0x04 }; Q_DECLARE_FLAGS(CryptoFlags, CryptoFlag) private: void splitKey(); quint64 m_key; QVector m_keyParts; CompressionMode m_compressionMode; IntegrityProtectionMode m_protectionMode; Error m_lastError; +#if (QT_VERSION >= QT_VERSION_CHECK(5, 10, 0)) + QRandomGenerator m_rand; +#endif }; Q_DECLARE_OPERATORS_FOR_FLAGS(O0SimpleCrypt::CryptoFlags) #endif // SimpleCrypt_H diff --git a/core/libs/dplugins/webservices/o2/src/o1.cpp b/core/libs/dplugins/webservices/o2/src/o1.cpp index 9c883a9baf..c7aa845dfa 100644 --- a/core/libs/dplugins/webservices/o2/src/o1.cpp +++ b/core/libs/dplugins/webservices/o2/src/o1.cpp @@ -1,421 +1,421 @@ #include #include #include #include #include #include #include #include #include #if QT_VERSION >= 0x050000 #include #endif #if QT_VERSION >= 0x050100 #include #endif #include "o1.h" #include "o2replyserver.h" #include "o0globals.h" #include "o0settingsstore.h" O1::O1(QObject *parent, QNetworkAccessManager *manager, O0AbstractStore *store): O0BaseAuth(parent, store) { setSignatureMethod(O2_SIGNATURE_TYPE_HMAC_SHA1); manager_ = manager ? manager : new QNetworkAccessManager(this); qRegisterMetaType("QNetworkReply::NetworkError"); setCallbackUrl(O2_CALLBACK_URL); } QByteArray O1::userAgent() const { return userAgent_; } void O1::setUserAgent(const QByteArray &v) { userAgent_ = v; } QUrl O1::requestTokenUrl() { return requestTokenUrl_; } void O1::setRequestTokenUrl(const QUrl &v) { requestTokenUrl_ = v; Q_EMIT requestTokenUrlChanged(); } QList O1::requestParameters() { return requestParameters_; } void O1::setRequestParameters(const QList &v) { requestParameters_ = v; } QString O1::callbackUrl() { return callbackUrl_; } void O1::setCallbackUrl(const QString &v) { callbackUrl_ = v; } QUrl O1::authorizeUrl() { return authorizeUrl_; } void O1::setAuthorizeUrl(const QUrl &value) { authorizeUrl_ = value; Q_EMIT authorizeUrlChanged(); } QUrl O1::accessTokenUrl() { return accessTokenUrl_; } void O1::setAccessTokenUrl(const QUrl &value) { accessTokenUrl_ = value; Q_EMIT accessTokenUrlChanged(); } QString O1::signatureMethod() { return signatureMethod_; } void O1::setSignatureMethod(const QString &value) { qDebug() << "O1::setSignatureMethod: " << value; signatureMethod_ = value; } void O1::unlink() { qDebug() << "O1::unlink"; setLinked(false); setToken(""); setTokenSecret(""); setExtraTokens(QVariantMap()); Q_EMIT linkingSucceeded(); } #if QT_VERSION < 0x050100 /// Calculate the HMAC variant of SHA1 hash. /// @author http://qt-project.org/wiki/HMAC-SHA1. /// @copyright Creative Commons Attribution-ShareAlike 2.5 Generic. static QByteArray hmacSha1(QByteArray key, QByteArray baseString) { int blockSize = 64; if (key.length() > blockSize) { key = QCryptographicHash::hash(key, QCryptographicHash::Sha1); } QByteArray innerPadding(blockSize, char(0x36)); QByteArray outerPadding(blockSize, char(0x5c)); for (int i = 0; i < key.length(); i++) { innerPadding[i] = innerPadding[i] ^ key.at(i); outerPadding[i] = outerPadding[i] ^ key.at(i); } QByteArray total = outerPadding; QByteArray part = innerPadding; part.append(baseString); total.append(QCryptographicHash::hash(part, QCryptographicHash::Sha1)); QByteArray hashed = QCryptographicHash::hash(total, QCryptographicHash::Sha1); return hashed.toBase64(); } #endif /// Get HTTP operation name. static QString getOperationName(QNetworkAccessManager::Operation op) { switch (op) { case QNetworkAccessManager::GetOperation: return "GET"; case QNetworkAccessManager::PostOperation: return "POST"; case QNetworkAccessManager::PutOperation: return "PUT"; case QNetworkAccessManager::DeleteOperation: return "DEL"; default: return ""; } } /// Build a concatenated/percent-encoded string from a list of headers. QByteArray O1::encodeHeaders(const QList &headers) { return QUrl::toPercentEncoding(createQueryParameters(headers)); } /// Build a base string for signing. QByteArray O1::getRequestBase(const QList &oauthParams, const QList &otherParams, const QUrl &url, QNetworkAccessManager::Operation op) { QByteArray base; // Initialize base string with the operation name (e.g. "GET") and the base URL base.append(getOperationName(op).toUtf8() + "&"); base.append(QUrl::toPercentEncoding(url.toString(QUrl::RemoveQuery)) + "&"); // Append a sorted+encoded list of all request parameters to the base string QList headers(oauthParams); headers.append(otherParams); std::sort(headers.begin(), headers.end()); base.append(encodeHeaders(headers)); return base; } QByteArray O1::sign(const QList &oauthParams, const QList &otherParams, const QUrl &url, QNetworkAccessManager::Operation op, const QString &consumerSecret, const QString &tokenSecret) { QByteArray baseString = getRequestBase(oauthParams, otherParams, url, op); QByteArray secret = QUrl::toPercentEncoding(consumerSecret) + "&" + QUrl::toPercentEncoding(tokenSecret); #if QT_VERSION >= 0x050100 return QMessageAuthenticationCode::hash(baseString, secret, QCryptographicHash::Sha1).toBase64(); #else return hmacSha1(secret, baseString); #endif } QByteArray O1::buildAuthorizationHeader(const QList &oauthParams) { bool first = true; QByteArray ret("OAuth "); QList headers(oauthParams); std::sort(headers.begin(), headers.end()); foreach (O0RequestParameter h, headers) { if (first) { first = false; } else { ret.append(","); } ret.append(h.name); ret.append("=\""); ret.append(QUrl::toPercentEncoding(h.value)); ret.append("\""); } return ret; } void O1::decorateRequest(QNetworkRequest &req, const QList &oauthParams) { req.setRawHeader(O2_HTTP_AUTHORIZATION_HEADER, buildAuthorizationHeader(oauthParams)); if (!userAgent_.isEmpty()) { #if QT_VERSION >= 0x050000 req.setHeader(QNetworkRequest::UserAgentHeader, userAgent_); #else req.setRawHeader("User-Agent", userAgent_); #endif } } QByteArray O1::generateSignature(const QList headers, const QNetworkRequest &req, const QList &signingParameters, QNetworkAccessManager::Operation operation) { QByteArray signature; if (signatureMethod() == O2_SIGNATURE_TYPE_HMAC_SHA1) { signature = sign(headers, signingParameters, req.url(), operation, clientSecret(), tokenSecret()); } else if (signatureMethod() == O2_SIGNATURE_TYPE_PLAINTEXT) { signature = clientSecret().toLatin1() + "&" + tokenSecret().toLatin1(); } return signature; } void O1::link() { qDebug() << "O1::link"; // Create the reply server if it doesn't exist // and we don't use an external web interceptor if(!useExternalWebInterceptor_) { - if(replyServer_ == NULL) { - replyServer_ = new O2ReplyServer(this); - connect(replyServer_, SIGNAL(verificationReceived(QMap)), this, SLOT(onVerificationReceived(QMap))); + if(replyServer() == NULL) { + O2ReplyServer * replyServer = new O2ReplyServer(this); + connect(replyServer, SIGNAL(verificationReceived(QMap)), this, SLOT(onVerificationReceived(QMap))); + setReplyServer(replyServer); } } if (linked()) { qDebug() << "O1::link: Linked already"; Q_EMIT linkingSucceeded(); return; } setLinked(false); setToken(""); setTokenSecret(""); setExtraTokens(QVariantMap()); - + if (!useExternalWebInterceptor_) { // Start reply server - if (!replyServer_->isListening()) - replyServer_->listen(QHostAddress::Any, localPort()); + if (!replyServer()->isListening()) + replyServer()->listen(QHostAddress::Any, localPort()); } - + // Get any query parameters for the request #if QT_VERSION >= 0x050000 QUrlQuery requestData; #else QUrl requestData = requestTokenUrl(); #endif O0RequestParameter param("", ""); foreach (param, requestParameters()) requestData.addQueryItem(QString(param.name), QUrl::toPercentEncoding(QString(param.value))); // Get the request url and add parameters #if QT_VERSION >= 0x050000 QUrl requestUrl = requestTokenUrl(); requestUrl.setQuery(requestData); // Create request QNetworkRequest request(requestUrl); #else // Create request QNetworkRequest request(requestData); #endif // Create initial token request QList headers; headers.append(O0RequestParameter(O2_OAUTH_CALLBACK, callbackUrl().arg(localPort()).toLatin1())); headers.append(O0RequestParameter(O2_OAUTH_CONSUMER_KEY, clientId().toLatin1())); headers.append(O0RequestParameter(O2_OAUTH_NONCE, nonce())); headers.append(O0RequestParameter(O2_OAUTH_TIMESTAMP, QString::number(QDateTime::currentDateTimeUtc().toTime_t()).toLatin1())); headers.append(O0RequestParameter(O2_OAUTH_VERSION, "1.0")); headers.append(O0RequestParameter(O2_OAUTH_SIGNATURE_METHOD, signatureMethod().toLatin1())); headers.append(O0RequestParameter(O2_OAUTH_SIGNATURE, generateSignature(headers, request, requestParameters(), QNetworkAccessManager::PostOperation))); // Clear request token requestToken_.clear(); requestTokenSecret_.clear(); // Post request decorateRequest(request, headers); request.setHeader(QNetworkRequest::ContentTypeHeader, O2_MIME_TYPE_XFORM); QNetworkReply *reply = manager_->post(request, QByteArray()); connect(reply, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(onTokenRequestError(QNetworkReply::NetworkError))); connect(reply, SIGNAL(finished()), this, SLOT(onTokenRequestFinished())); } void O1::onTokenRequestError(QNetworkReply::NetworkError error) { QNetworkReply *reply = qobject_cast(sender()); qWarning() << "O1::onTokenRequestError:" << (int)error << reply->errorString() << reply->readAll(); Q_EMIT linkingFailed(); } void O1::onTokenRequestFinished() { qDebug() << "O1::onTokenRequestFinished"; QNetworkReply *reply = qobject_cast(sender()); qDebug() << QString( "Request: %1" ).arg(reply->request().url().toString()); reply->deleteLater(); if (reply->error() != QNetworkReply::NoError) { qWarning() << "O1::onTokenRequestFinished: " << reply->errorString(); return; } // Get request token and secret QByteArray data = reply->readAll(); QMap response = parseResponse(data); requestToken_ = response.value(O2_OAUTH_TOKEN, ""); requestTokenSecret_ = response.value(O2_OAUTH_TOKEN_SECRET, ""); setToken(requestToken_); setTokenSecret(requestTokenSecret_); // Checking for "oauth_callback_confirmed" is present and set to true QString oAuthCbConfirmed = response.value(O2_OAUTH_CALLBACK_CONFIRMED, "false"); if (requestToken_.isEmpty() || requestTokenSecret_.isEmpty() || (oAuthCbConfirmed == "false")) { qWarning() << "O1::onTokenRequestFinished: No oauth_token, oauth_token_secret or oauth_callback_confirmed in response :" << data; Q_EMIT linkingFailed(); return; } // Continue authorization flow in the browser QUrl url(authorizeUrl()); #if QT_VERSION < 0x050000 url.addQueryItem(O2_OAUTH_TOKEN, requestToken_); url.addQueryItem(O2_OAUTH_CALLBACK, callbackUrl().arg(localPort()).toLatin1()); #else QUrlQuery query(url); query.addQueryItem(O2_OAUTH_TOKEN, requestToken_); query.addQueryItem(O2_OAUTH_CALLBACK, callbackUrl().arg(localPort()).toLatin1()); url.setQuery(query); #endif Q_EMIT openBrowser(url); } void O1::onVerificationReceived(QMap params) { qDebug() << "O1::onVerificationReceived"; Q_EMIT closeBrowser(); verifier_ = params.value(O2_OAUTH_VERFIER, ""); if (params.value(O2_OAUTH_TOKEN) == requestToken_) { // Exchange request token for access token exchangeToken(); } else { qWarning() << "O1::onVerificationReceived: oauth_token missing or doesn't match"; Q_EMIT linkingFailed(); } } void O1::exchangeToken() { qDebug() << "O1::exchangeToken"; // Create token exchange request QNetworkRequest request(accessTokenUrl()); QList oauthParams; oauthParams.append(O0RequestParameter(O2_OAUTH_CONSUMER_KEY, clientId().toLatin1())); oauthParams.append(O0RequestParameter(O2_OAUTH_VERSION, "1.0")); oauthParams.append(O0RequestParameter(O2_OAUTH_TIMESTAMP, QString::number(QDateTime::currentDateTimeUtc().toTime_t()).toLatin1())); oauthParams.append(O0RequestParameter(O2_OAUTH_NONCE, nonce())); oauthParams.append(O0RequestParameter(O2_OAUTH_TOKEN, requestToken_.toLatin1())); oauthParams.append(O0RequestParameter(O2_OAUTH_VERFIER, verifier_.toLatin1())); oauthParams.append(O0RequestParameter(O2_OAUTH_SIGNATURE_METHOD, signatureMethod().toLatin1())); oauthParams.append(O0RequestParameter(O2_OAUTH_SIGNATURE, generateSignature(oauthParams, request, QList(), QNetworkAccessManager::PostOperation))); // Post request decorateRequest(request, oauthParams); request.setHeader(QNetworkRequest::ContentTypeHeader, O2_MIME_TYPE_XFORM); QNetworkReply *reply = manager_->post(request, QByteArray()); connect(reply, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(onTokenExchangeError(QNetworkReply::NetworkError))); connect(reply, SIGNAL(finished()), this, SLOT(onTokenExchangeFinished())); } void O1::onTokenExchangeError(QNetworkReply::NetworkError error) { QNetworkReply *reply = qobject_cast(sender()); qWarning() << "O1::onTokenExchangeError:" << (int)error << reply->errorString() << reply->readAll(); Q_EMIT linkingFailed(); } void O1::onTokenExchangeFinished() { qDebug() << "O1::onTokenExchangeFinished"; QNetworkReply *reply = qobject_cast(sender()); reply->deleteLater(); if (reply->error() != QNetworkReply::NoError) { qWarning() << "O1::onTokenExchangeFinished: " << reply->errorString(); return; } // Get access token and secret QByteArray data = reply->readAll(); - qWarning() << "data: " << QString(data); QMap response = parseResponse(data); if (response.contains(O2_OAUTH_TOKEN) && response.contains(O2_OAUTH_TOKEN_SECRET)) { setToken(response.take(O2_OAUTH_TOKEN)); setTokenSecret(response.take(O2_OAUTH_TOKEN_SECRET)); // Set extra tokens if any if (!response.isEmpty()) { QVariantMap extraTokens; foreach (QString key, response.keys()) { extraTokens.insert(key, response.value(key)); } setExtraTokens(extraTokens); } setLinked(true); Q_EMIT linkingSucceeded(); } else { qWarning() << "O1::onTokenExchangeFinished: oauth_token or oauth_token_secret missing from response" << data; Q_EMIT linkingFailed(); } } QMap O1::parseResponse(const QByteArray &response) { QMap ret; foreach (QByteArray param, response.split('&')) { QList kv = param.split('='); if (kv.length() == 2) { ret.insert(QUrl::fromPercentEncoding(kv[0]), QUrl::fromPercentEncoding(kv[1])); } } return ret; } QByteArray O1::nonce() { static bool firstTime = true; if (firstTime) { firstTime = false; qsrand(QTime::currentTime().msec()); } QString u = QString::number(QDateTime::currentDateTimeUtc().toTime_t()); u.append(QString::number(qrand())); return u.toLatin1(); } diff --git a/core/libs/dplugins/webservices/o2/src/o1.h b/core/libs/dplugins/webservices/o2/src/o1.h index b54abd5a50..75302ad9f8 100644 --- a/core/libs/dplugins/webservices/o2/src/o1.h +++ b/core/libs/dplugins/webservices/o2/src/o1.h @@ -1,135 +1,135 @@ #ifndef O1_H #define O1_H #include #include #include #include "o0export.h" #include "o0baseauth.h" /// Simple OAuth 1.0 authenticator. class O0_EXPORT O1: public O0BaseAuth { Q_OBJECT public: /// HTTP User-Agent header /// Set user agent to a value unique for your application (https://tools.ietf.org/html/rfc7231#section-5.5.3) /// if you see the following error in the application log: /// O1::onTokenRequestError: 201 "Error transferring requestTokenUrl() - server replied: Forbidden" "Bad bot" Q_PROPERTY(QByteArray userAgent READ userAgent WRITE setUserAgent) QByteArray userAgent() const; void setUserAgent(const QByteArray &value); /// Signature method Q_PROPERTY(QString signatureMethod READ signatureMethod WRITE setSignatureMethod NOTIFY signatureMethodChanged) QString signatureMethod(); void setSignatureMethod(const QString &value); /// Token request URL. Q_PROPERTY(QUrl requestTokenUrl READ requestTokenUrl WRITE setRequestTokenUrl NOTIFY requestTokenUrlChanged) QUrl requestTokenUrl(); void setRequestTokenUrl(const QUrl &value); /// Parameters to pass with request URL. - Q_PROPERTY(QList requestParameters READ requestParameters WRITE setRequestParameters) + Q_PROPERTY(QList requestParameters READ requestParameters WRITE setRequestParameters); QList requestParameters(); void setRequestParameters(const QList &value); /// Callback URL. /// It should contain a `%1` place marker, to be replaced by `O0BaseAuth::localPort()`. /// Defaults to `O2_CALLBACK_URL`. Q_PROPERTY(QString callbackUrl READ callbackUrl WRITE setCallbackUrl) QString callbackUrl(); void setCallbackUrl(const QString &value); /// Authorization URL. Q_PROPERTY(QUrl authorizeUrl READ authorizeUrl WRITE setAuthorizeUrl NOTIFY authorizeUrlChanged) QUrl authorizeUrl(); void setAuthorizeUrl(const QUrl &value); /// Access token URL. Q_PROPERTY(QUrl accessTokenUrl READ accessTokenUrl WRITE setAccessTokenUrl NOTIFY accessTokenUrlChanged) QUrl accessTokenUrl(); void setAccessTokenUrl(const QUrl &value); /// Constructor. explicit O1(QObject *parent = 0, QNetworkAccessManager *manager = 0, O0AbstractStore *store = 0); /// Parse a URL-encoded response string. static QMap parseResponse(const QByteArray &response); /// Build the value of the "Authorization:" header. static QByteArray buildAuthorizationHeader(const QList &oauthParams); /// Add common configuration (headers) to @p req. void decorateRequest(QNetworkRequest &req, const QList &oauthParams); /// Create unique bytes to prevent replay attacks. static QByteArray nonce(); /// Generate signature string depending on signature method type QByteArray generateSignature(const QList headers, const QNetworkRequest &req, const QList &signingParameters, QNetworkAccessManager::Operation operation); /// Calculate the HMAC-SHA1 signature of a request. /// @param oauthParams OAuth parameters. /// @param otherParams Other parameters participating in signing. /// @param URL Request URL. May contain query parameters, but they will not be used for signing. /// @param op HTTP operation. /// @param consumerSecret Consumer (application) secret. /// @param tokenSecret Authorization token secret (empty if not yet available). /// @return Signature that can be used as the value of the "oauth_signature" parameter. static QByteArray sign(const QList &oauthParams, const QList &otherParams, const QUrl &url, QNetworkAccessManager::Operation op, const QString &consumerSecret, const QString &tokenSecret); /// Build a base string for signing. static QByteArray getRequestBase(const QList &oauthParams, const QList &otherParams, const QUrl &url, QNetworkAccessManager::Operation op); /// Build a concatenated/percent-encoded string from a list of headers. static QByteArray encodeHeaders(const QList &headers); public Q_SLOTS: /// Authenticate. Q_INVOKABLE virtual void link(); /// De-authenticate. Q_INVOKABLE virtual void unlink(); Q_SIGNALS: void requestTokenUrlChanged(); void authorizeUrlChanged(); void accessTokenUrlChanged(); void signatureMethodChanged(); public Q_SLOTS: /// Handle verification received from the reply server. virtual void onVerificationReceived(QMap params); protected Q_SLOTS: /// Handle token request error. virtual void onTokenRequestError(QNetworkReply::NetworkError error); /// Handle token request finished. virtual void onTokenRequestFinished(); /// Handle token exchange error. void onTokenExchangeError(QNetworkReply::NetworkError error); /// Handle token exchange finished. void onTokenExchangeFinished(); protected: /// Exchange temporary token to authentication token void exchangeToken(); QByteArray userAgent_; QUrl requestUrl_; QList requestParameters_; QString callbackUrl_; QUrl tokenUrl_; QUrl refreshTokenUrl_; QString verifier_; QString signatureMethod_; QNetworkAccessManager *manager_; }; #endif // O1_H diff --git a/core/libs/dplugins/webservices/o2/src/o2.cpp b/core/libs/dplugins/webservices/o2/src/o2.cpp index 6a50a31b15..7a3096a0ec 100644 --- a/core/libs/dplugins/webservices/o2/src/o2.cpp +++ b/core/libs/dplugins/webservices/o2/src/o2.cpp @@ -1,512 +1,523 @@ #include #include #include #include #include #include #include #include #include #include #include #include +#include #if QT_VERSION >= 0x050000 #include #include #include #else #include #include #endif #include "o2.h" #include "o2replyserver.h" #include "o0globals.h" #include "o0settingsstore.h" /// Parse JSON data into a QVariantMap static QVariantMap parseTokenResponse(const QByteArray &data) { #if QT_VERSION >= 0x050000 QJsonParseError err; QJsonDocument doc = QJsonDocument::fromJson(data, &err); if (err.error != QJsonParseError::NoError) { qWarning() << "parseTokenResponse: Failed to parse token response due to err:" << err.errorString(); return QVariantMap(); } if (!doc.isObject()) { qWarning() << "parseTokenResponse: Token response is not an object"; return QVariantMap(); } return doc.object().toVariantMap(); #else QScriptEngine engine; QScriptValue value = engine.evaluate("(" + QString(data) + ")"); QScriptValueIterator it(value); QVariantMap map; while (it.hasNext()) { it.next(); map.insert(it.name(), it.value().toVariant()); } return map; #endif } /// Add query parameters to a query static void addQueryParametersToUrl(QUrl &url, QList > parameters) { #if QT_VERSION < 0x050000 url.setQueryItems(parameters); #else QUrlQuery query(url); query.setQueryItems(parameters); url.setQuery(query); #endif } O2::O2(QObject *parent, QNetworkAccessManager *manager, O0AbstractStore *store): O0BaseAuth(parent, store) { manager_ = manager ? manager : new QNetworkAccessManager(this); grantFlow_ = GrantFlowAuthorizationCode; localhostPolicy_ = QString(O2_CALLBACK_URL); qRegisterMetaType("QNetworkReply::NetworkError"); } O2::GrantFlow O2::grantFlow() { return grantFlow_; } void O2::setGrantFlow(O2::GrantFlow value) { grantFlow_ = value; Q_EMIT grantFlowChanged(); } QString O2::username() { return username_; } void O2::setUsername(const QString &value) { username_ = value; Q_EMIT usernameChanged(); } QString O2::password() { return password_; } void O2::setPassword(const QString &value) { password_ = value; Q_EMIT passwordChanged(); } QString O2::scope() { return scope_; } void O2::setScope(const QString &value) { scope_ = value; Q_EMIT scopeChanged(); } QString O2::requestUrl() { return requestUrl_.toString(); } void O2::setRequestUrl(const QString &value) { requestUrl_ = value; Q_EMIT requestUrlChanged(); } QVariantMap O2::extraRequestParams() { return extraReqParams_; } void O2::setExtraRequestParams(const QVariantMap &value) { extraReqParams_ = value; Q_EMIT extraRequestParamsChanged(); } QString O2::tokenUrl() { return tokenUrl_.toString(); } void O2::setTokenUrl(const QString &value) { tokenUrl_= value; Q_EMIT tokenUrlChanged(); } QString O2::refreshTokenUrl() { return refreshTokenUrl_.toString(); } void O2::setRefreshTokenUrl(const QString &value) { refreshTokenUrl_ = value; Q_EMIT refreshTokenUrlChanged(); } void O2::link() { qDebug() << "O2::link"; // Create the reply server if it doesn't exist // and we don't use an external web interceptor if(!useExternalWebInterceptor_) { - if(replyServer_ == NULL) { - replyServer_ = new O2ReplyServer(this); - connect(replyServer_, SIGNAL(verificationReceived(QMap)), this, SLOT(onVerificationReceived(QMap))); - connect(replyServer_, SIGNAL(serverClosed(bool)), this, SLOT(serverHasClosed(bool))); + if(replyServer() == NULL) { + O2ReplyServer * replyServer = new O2ReplyServer(this); + connect(replyServer, SIGNAL(verificationReceived(QMap)), this, SLOT(onVerificationReceived(QMap))); + connect(replyServer, SIGNAL(serverClosed(bool)), this, SLOT(serverHasClosed(bool))); + setReplyServer(replyServer); } } if (linked()) { qDebug() << "O2::link: Linked already"; Q_EMIT linkingSucceeded(); return; } setLinked(false); setToken(""); setTokenSecret(""); setExtraTokens(QVariantMap()); setRefreshToken(QString()); setExpires(0); if (grantFlow_ == GrantFlowAuthorizationCode || grantFlow_ == GrantFlowImplicit) { + QString uniqueState = QUuid::createUuid().toString().remove(QRegExp("([^a-zA-Z0-9]|[-])")); if (useExternalWebInterceptor_) { // Save redirect URI, as we have to reuse it when requesting the access token redirectUri_ = localhostPolicy_.arg(localPort()); } else { // Start listening to authentication replies - if (!replyServer_->isListening()) { - if (replyServer_->listen(QHostAddress::Any, localPort_)) { + if (!replyServer()->isListening()) { + if (replyServer()->listen(QHostAddress::Any, localPort_)) { qDebug() << "O2::link: Reply server listening on port" << localPort(); } else { qWarning() << "O2::link: Reply server failed to start listening on port" << localPort(); Q_EMIT linkingFailed(); return; } } - + // Save redirect URI, as we have to reuse it when requesting the access token - redirectUri_ = localhostPolicy_.arg(replyServer_->serverPort()); + redirectUri_ = localhostPolicy_.arg(replyServer()->serverPort()); + replyServer()->setUniqueState(uniqueState); } - + // Assemble initial authentication URL QList > parameters; parameters.append(qMakePair(QString(O2_OAUTH2_RESPONSE_TYPE), (grantFlow_ == GrantFlowAuthorizationCode)? QString(O2_OAUTH2_GRANT_TYPE_CODE): QString(O2_OAUTH2_GRANT_TYPE_TOKEN))); parameters.append(qMakePair(QString(O2_OAUTH2_CLIENT_ID), clientId_)); if ( !redirectUri_.isEmpty() ) parameters.append(qMakePair(QString(O2_OAUTH2_REDIRECT_URI), redirectUri_)); if ( !scope_.isEmpty() ) parameters.append(qMakePair(QString(O2_OAUTH2_SCOPE), scope_.replace( " ", "+" ))); + parameters.append(qMakePair(QString(O2_OAUTH2_STATE), uniqueState)); if ( !apiKey_.isEmpty() ) parameters.append(qMakePair(QString(O2_OAUTH2_API_KEY), apiKey_)); foreach (QString key, extraRequestParams().keys()) { parameters.append(qMakePair(key, extraRequestParams().value(key).toString())); } // Show authentication URL with a web browser QUrl url(requestUrl_); addQueryParametersToUrl(url, parameters); qDebug() << "O2::link: Emit openBrowser" << url.toString(); Q_EMIT openBrowser(url); } else if (grantFlow_ == GrantFlowResourceOwnerPasswordCredentials) { QList parameters; parameters.append(O0RequestParameter(O2_OAUTH2_CLIENT_ID, clientId_.toUtf8())); if ( !clientSecret_.isEmpty() ) parameters.append(O0RequestParameter(O2_OAUTH2_CLIENT_SECRET, clientSecret_.toUtf8())); parameters.append(O0RequestParameter(O2_OAUTH2_USERNAME, username_.toUtf8())); parameters.append(O0RequestParameter(O2_OAUTH2_PASSWORD, password_.toUtf8())); parameters.append(O0RequestParameter(O2_OAUTH2_GRANT_TYPE, O2_OAUTH2_GRANT_TYPE_PASSWORD)); parameters.append(O0RequestParameter(O2_OAUTH2_SCOPE, scope_.toUtf8())); if ( !apiKey_.isEmpty() ) parameters.append(O0RequestParameter(O2_OAUTH2_API_KEY, apiKey_.toUtf8())); foreach (QString key, extraRequestParams().keys()) { parameters.append(O0RequestParameter(key.toUtf8(), extraRequestParams().value(key).toByteArray())); } QByteArray payload = O0BaseAuth::createQueryParameters(parameters); qDebug() << "O2::link: Sending token request for resource owner flow"; QUrl url(tokenUrl_); QNetworkRequest tokenRequest(url); tokenRequest.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded"); QNetworkReply *tokenReply = manager_->post(tokenRequest, payload); connect(tokenReply, SIGNAL(finished()), this, SLOT(onTokenReplyFinished()), Qt::QueuedConnection); connect(tokenReply, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(onTokenReplyError(QNetworkReply::NetworkError)), Qt::QueuedConnection); } } void O2::unlink() { qDebug() << "O2::unlink"; setLinked(false); setToken(QString()); setRefreshToken(QString()); setExpires(0); setExtraTokens(QVariantMap()); Q_EMIT linkingSucceeded(); } void O2::onVerificationReceived(const QMap response) { qDebug() << "O2::onVerificationReceived:" << response; qDebug() << "O2::onVerificationReceived: Emitting closeBrowser()"; Q_EMIT closeBrowser(); if (response.contains("error")) { qWarning() << "O2::onVerificationReceived: Verification failed:" << response; Q_EMIT linkingFailed(); return; } if (grantFlow_ == GrantFlowAuthorizationCode) { // Save access code setCode(response.value(QString(O2_OAUTH2_GRANT_TYPE_CODE))); // Exchange access code for access/refresh tokens QString query; if(!apiKey_.isEmpty()) query = QString("?" + QString(O2_OAUTH2_API_KEY) + "=" + apiKey_); QNetworkRequest tokenRequest(QUrl(tokenUrl_.toString() + query)); tokenRequest.setHeader(QNetworkRequest::ContentTypeHeader, O2_MIME_TYPE_XFORM); tokenRequest.setRawHeader("Accept", O2_MIME_TYPE_JSON); QMap parameters; parameters.insert(O2_OAUTH2_GRANT_TYPE_CODE, code()); parameters.insert(O2_OAUTH2_CLIENT_ID, clientId_); parameters.insert(O2_OAUTH2_CLIENT_SECRET, clientSecret_); parameters.insert(O2_OAUTH2_REDIRECT_URI, redirectUri_); parameters.insert(O2_OAUTH2_GRANT_TYPE, O2_AUTHORIZATION_CODE); QByteArray data = buildRequestBody(parameters); qDebug() << QString("O2::onVerificationReceived: Exchange access code data:\n%1").arg(QString(data)); QNetworkReply *tokenReply = manager_->post(tokenRequest, data); timedReplies_.add(tokenReply); connect(tokenReply, SIGNAL(finished()), this, SLOT(onTokenReplyFinished()), Qt::QueuedConnection); connect(tokenReply, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(onTokenReplyError(QNetworkReply::NetworkError)), Qt::QueuedConnection); } else if (grantFlow_ == GrantFlowImplicit) { // Check for mandatory tokens if (response.contains(O2_OAUTH2_ACCESS_TOKEN)) { qDebug() << "O2::onVerificationReceived: Access token returned for implicit flow"; setToken(response.value(O2_OAUTH2_ACCESS_TOKEN)); if (response.contains(O2_OAUTH2_EXPIRES_IN)) { bool ok = false; int expiresIn = response.value(O2_OAUTH2_EXPIRES_IN).toInt(&ok); if (ok) { qDebug() << "O2::onVerificationReceived: Token expires in" << expiresIn << "seconds"; setExpires((int)(QDateTime::currentMSecsSinceEpoch() / 1000 + expiresIn)); } } setLinked(true); Q_EMIT linkingSucceeded(); } else { qWarning() << "O2::onVerificationReceived: Access token missing from response for implicit flow"; Q_EMIT linkingFailed(); } } else { setToken(response.value(O2_OAUTH2_ACCESS_TOKEN)); setRefreshToken(response.value(O2_OAUTH2_REFRESH_TOKEN)); } } QString O2::code() { QString key = QString(O2_KEY_CODE).arg(clientId_); return store_->value(key); } void O2::setCode(const QString &c) { QString key = QString(O2_KEY_CODE).arg(clientId_); store_->setValue(key, c); } void O2::onTokenReplyFinished() { qDebug() << "O2::onTokenReplyFinished"; QNetworkReply *tokenReply = qobject_cast(sender()); if (!tokenReply) { qDebug() << "O2::onTokenReplyFinished: reply is null"; return; } if (tokenReply->error() == QNetworkReply::NoError) { QByteArray replyData = tokenReply->readAll(); // Dump replyData // SENSITIVE DATA in RelWithDebInfo or Debug builds //qDebug() << "O2::onTokenReplyFinished: replyData\n"; //qDebug() << QString( replyData ); QVariantMap tokens = parseTokenResponse(replyData); // Dump tokens qDebug() << "O2::onTokenReplyFinished: Tokens returned:\n"; foreach (QString key, tokens.keys()) { // SENSITIVE DATA in RelWithDebInfo or Debug builds, so it is truncated first qDebug() << key << ": "<< tokens.value( key ).toString().left( 3 ) << "..."; } // Check for mandatory tokens if (tokens.contains(O2_OAUTH2_ACCESS_TOKEN)) { qDebug() << "O2::onTokenReplyFinished: Access token returned"; setToken(tokens.take(O2_OAUTH2_ACCESS_TOKEN).toString()); bool ok = false; int expiresIn = tokens.take(O2_OAUTH2_EXPIRES_IN).toInt(&ok); if (ok) { qDebug() << "O2::onTokenReplyFinished: Token expires in" << expiresIn << "seconds"; setExpires((int)(QDateTime::currentMSecsSinceEpoch() / 1000 + expiresIn)); } setRefreshToken(tokens.take(O2_OAUTH2_REFRESH_TOKEN).toString()); setExtraTokens(tokens); timedReplies_.remove(tokenReply); setLinked(true); Q_EMIT linkingSucceeded(); } else { qWarning() << "O2::onTokenReplyFinished: Access token missing from response"; Q_EMIT linkingFailed(); } } tokenReply->deleteLater(); } void O2::onTokenReplyError(QNetworkReply::NetworkError error) { QNetworkReply *tokenReply = qobject_cast(sender()); - qWarning() << "O2::onTokenReplyError: " << error << ": " << tokenReply->errorString(); - qDebug() << "O2::onTokenReplyError: " << tokenReply->readAll(); + if (!tokenReply) + { + qDebug() << "O2::onTokenReplyError: reply is null"; + } else { + qWarning() << "O2::onTokenReplyError: " << error << ": " << tokenReply->errorString(); + qDebug() << "O2::onTokenReplyError: " << tokenReply->readAll(); + timedReplies_.remove(tokenReply); + } + setToken(QString()); setRefreshToken(QString()); - timedReplies_.remove(tokenReply); Q_EMIT linkingFailed(); } QByteArray O2::buildRequestBody(const QMap ¶meters) { QByteArray body; bool first = true; foreach (QString key, parameters.keys()) { if (first) { first = false; } else { body.append("&"); } QString value = parameters.value(key); body.append(QUrl::toPercentEncoding(key) + QString("=").toUtf8() + QUrl::toPercentEncoding(value)); } return body; } int O2::expires() { QString key = QString(O2_KEY_EXPIRES).arg(clientId_); return store_->value(key).toInt(); } void O2::setExpires(int v) { QString key = QString(O2_KEY_EXPIRES).arg(clientId_); store_->setValue(key, QString::number(v)); } QString O2::refreshToken() { QString key = QString(O2_KEY_REFRESH_TOKEN).arg(clientId_); return store_->value(key); } void O2::setRefreshToken(const QString &v) { qDebug() << "O2::setRefreshToken" << v.left(4) << "..."; QString key = QString(O2_KEY_REFRESH_TOKEN).arg(clientId_); store_->setValue(key, v); } void O2::refresh() { qDebug() << "O2::refresh: Token: ..." << refreshToken().right(7); if (refreshToken().isEmpty()) { qWarning() << "O2::refresh: No refresh token"; onRefreshError(QNetworkReply::AuthenticationRequiredError); return; } if (refreshTokenUrl_.isEmpty()) { qWarning() << "O2::refresh: Refresh token URL not set"; onRefreshError(QNetworkReply::AuthenticationRequiredError); return; } QNetworkRequest refreshRequest(refreshTokenUrl_); refreshRequest.setHeader(QNetworkRequest::ContentTypeHeader, O2_MIME_TYPE_XFORM); QMap parameters; parameters.insert(O2_OAUTH2_CLIENT_ID, clientId_); parameters.insert(O2_OAUTH2_CLIENT_SECRET, clientSecret_); parameters.insert(O2_OAUTH2_REFRESH_TOKEN, refreshToken()); parameters.insert(O2_OAUTH2_GRANT_TYPE, O2_OAUTH2_REFRESH_TOKEN); QByteArray data = buildRequestBody(parameters); QNetworkReply *refreshReply = manager_->post(refreshRequest, data); timedReplies_.add(refreshReply); connect(refreshReply, SIGNAL(finished()), this, SLOT(onRefreshFinished()), Qt::QueuedConnection); connect(refreshReply, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(onRefreshError(QNetworkReply::NetworkError)), Qt::QueuedConnection); } void O2::onRefreshFinished() { QNetworkReply *refreshReply = qobject_cast(sender()); - + if (refreshReply->error() == QNetworkReply::NoError) { QByteArray reply = refreshReply->readAll(); QVariantMap tokens = parseTokenResponse(reply); setToken(tokens.value(O2_OAUTH2_ACCESS_TOKEN).toString()); setExpires((int)(QDateTime::currentMSecsSinceEpoch() / 1000 + tokens.value(O2_OAUTH2_EXPIRES_IN).toInt())); QString refreshToken = tokens.value(O2_OAUTH2_REFRESH_TOKEN).toString(); if(!refreshToken.isEmpty()) { setRefreshToken(refreshToken); } else { qDebug() << "No new refresh token. Keep the old one."; } timedReplies_.remove(refreshReply); setLinked(true); Q_EMIT linkingSucceeded(); Q_EMIT refreshFinished(QNetworkReply::NoError); qDebug() << " New token expires in" << expires() << "seconds"; } else { qDebug() << "O2::onRefreshFinished: Error" << (int)refreshReply->error() << refreshReply->errorString(); } refreshReply->deleteLater(); } void O2::onRefreshError(QNetworkReply::NetworkError error) { QNetworkReply *refreshReply = qobject_cast(sender()); qWarning() << "O2::onRefreshError: " << error; unlink(); timedReplies_.remove(refreshReply); Q_EMIT refreshFinished(error); } void O2::serverHasClosed(bool paramsfound) { if ( !paramsfound ) { // server has probably timed out after receiving first response Q_EMIT linkingFailed(); } } QString O2::localhostPolicy() const { return localhostPolicy_; } void O2::setLocalhostPolicy(const QString &value) { localhostPolicy_ = value; } QString O2::apiKey() { return apiKey_; } void O2::setApiKey(const QString &value) { apiKey_ = value; } bool O2::ignoreSslErrors() { return timedReplies_.ignoreSslErrors(); } void O2::setIgnoreSslErrors(bool ignoreSslErrors) { timedReplies_.setIgnoreSslErrors(ignoreSslErrors); } diff --git a/core/libs/dplugins/webservices/o2/src/o2hubic.cpp b/core/libs/dplugins/webservices/o2/src/o2hubic.cpp index 5671cb9af1..e8213bf123 100644 --- a/core/libs/dplugins/webservices/o2/src/o2hubic.cpp +++ b/core/libs/dplugins/webservices/o2/src/o2hubic.cpp @@ -1,17 +1,16 @@ #include "o2hubic.h" -#include "o2globals.h" #include "o2replyserver.h" #include static const char *HubicScope = "usage.r,account.r,getAllLinks.r,credentials.r,activate.w,links.drw"; static const char *HubicEndpoint = "https://api.hubic.com/oauth/auth/"; static const char *HubicTokenUrl = "https://api.hubic.com/oauth/token/"; static const char *HubicRefreshUrl = "https://api.hubic.com/oauth/token/"; O2Hubic::O2Hubic(QObject *parent): O2(parent) { setRequestUrl(HubicEndpoint); setTokenUrl(HubicTokenUrl); setRefreshTokenUrl(HubicRefreshUrl); setScope(HubicScope); setLocalhostPolicy("http://localhost:%1/"); } diff --git a/core/libs/dplugins/webservices/o2/src/o2msgraph.cpp b/core/libs/dplugins/webservices/o2/src/o2msgraph.cpp new file mode 100644 index 0000000000..2274a75a9f --- /dev/null +++ b/core/libs/dplugins/webservices/o2/src/o2msgraph.cpp @@ -0,0 +1,10 @@ +#include "o2msgraph.h" + +static const char *MsgraphEndpoint = "https://login.microsoftonline.com/common/oauth2/v2.0/authorize"; +static const char *MsgraphTokenUrl = "https://login.microsoftonline.com/common/oauth2/v2.0/token"; + +O2Msgraph::O2Msgraph(QObject *parent): O2(parent) { + setRequestUrl(MsgraphEndpoint); + setTokenUrl(MsgraphTokenUrl); + setRefreshTokenUrl(MsgraphTokenUrl); +} diff --git a/core/libs/dplugins/webservices/o2/src/o2msgraph.h b/core/libs/dplugins/webservices/o2/src/o2msgraph.h new file mode 100644 index 0000000000..48db6c9f6b --- /dev/null +++ b/core/libs/dplugins/webservices/o2/src/o2msgraph.h @@ -0,0 +1,15 @@ +#ifndef O2MSGRAPH_H +#define O2MSGRAPH_H + +#include "o0export.h" +#include "o2.h" + +/// Microsoft Graph's dialect of OAuth 2.0 +class O0_EXPORT O2Msgraph: public O2 { + Q_OBJECT + +public: + explicit O2Msgraph(QObject *parent = 0); +}; + +#endif // O2MSGRAPH_H diff --git a/core/libs/dplugins/webservices/o2/src/o2replyserver.cpp b/core/libs/dplugins/webservices/o2/src/o2replyserver.cpp index fd268c5087..151378c6e0 100644 --- a/core/libs/dplugins/webservices/o2/src/o2replyserver.cpp +++ b/core/libs/dplugins/webservices/o2/src/o2replyserver.cpp @@ -1,168 +1,184 @@ #include #include #include #include #include #include #include #include #include #include #if QT_VERSION >= 0x050000 #include #endif +#include "o0globals.h" #include "o2replyserver.h" O2ReplyServer::O2ReplyServer(QObject *parent): QTcpServer(parent), timeout_(15), maxtries_(3), tries_(0) { qDebug() << "O2ReplyServer: Starting"; connect(this, SIGNAL(newConnection()), this, SLOT(onIncomingConnection())); replyContent_ = ""; } void O2ReplyServer::onIncomingConnection() { qDebug() << "O2ReplyServer::onIncomingConnection: Receiving..."; QTcpSocket *socket = nextPendingConnection(); connect(socket, SIGNAL(readyRead()), this, SLOT(onBytesReady()), Qt::UniqueConnection); connect(socket, SIGNAL(disconnected()), socket, SLOT(deleteLater())); // Wait for a bit *after* first response, then close server if no usable data has arrived // Helps with implicit flow, where a URL fragment may need processed by local user-agent and // sent as secondary query string callback, or additional requests make it through first, // like for favicons, etc., before such secondary callbacks are fired QTimer *timer = new QTimer(socket); timer->setObjectName("timeoutTimer"); connect(timer, SIGNAL(timeout()), this, SLOT(closeServer())); timer->setSingleShot(true); timer->setInterval(timeout() * 1000); connect(socket, SIGNAL(readyRead()), timer, SLOT(start())); } void O2ReplyServer::onBytesReady() { if (!isListening()) { // server has been closed, stop processing queued connections return; } qDebug() << "O2ReplyServer::onBytesReady: Processing request"; // NOTE: on first call, the timeout timer is started QTcpSocket *socket = qobject_cast(sender()); if (!socket) { qWarning() << "O2ReplyServer::onBytesReady: No socket available"; return; } QByteArray reply; reply.append("HTTP/1.0 200 OK \r\n"); reply.append("Content-Type: text/html; charset=\"utf-8\"\r\n"); reply.append(QString("Content-Length: %1\r\n\r\n").arg(replyContent_.size()).toLatin1()); reply.append(replyContent_); socket->write(reply); qDebug() << "O2ReplyServer::onBytesReady: Sent reply"; QByteArray data = socket->readAll(); QMap queryParams = parseQueryParams(&data); if (queryParams.isEmpty()) { if (tries_ < maxtries_ ) { qDebug() << "O2ReplyServer::onBytesReady: No query params found, waiting for more callbacks"; ++tries_; return; } else { tries_ = 0; qWarning() << "O2ReplyServer::onBytesReady: No query params found, maximum callbacks received"; closeServer(socket, false); return; } } + if (!uniqueState_.isEmpty() && !queryParams.contains(QString(O2_OAUTH2_STATE))) { + qDebug() << "O2ReplyServer::onBytesReady: Malicious or service request"; + closeServer(socket, true); + return; // Malicious or service (e.g. favicon.ico) request + } qDebug() << "O2ReplyServer::onBytesReady: Query params found, closing server"; closeServer(socket, true); Q_EMIT verificationReceived(queryParams); } QMap O2ReplyServer::parseQueryParams(QByteArray *data) { qDebug() << "O2ReplyServer::parseQueryParams"; //qDebug() << QString("O2ReplyServer::parseQueryParams data:\n%1").arg(QString(*data)); QString splitGetLine = QString(*data).split("\r\n").first(); splitGetLine.remove("GET "); splitGetLine.remove("HTTP/1.1"); splitGetLine.remove("\r\n"); splitGetLine.prepend("http://localhost"); QUrl getTokenUrl(splitGetLine); QList< QPair > tokens; #if QT_VERSION < 0x050000 tokens = getTokenUrl.queryItems(); #else QUrlQuery query(getTokenUrl); tokens = query.queryItems(); #endif - QMultiMap queryParams; + QMap queryParams; QPair tokenPair; foreach (tokenPair, tokens) { // FIXME: We are decoding key and value again. This helps with Google OAuth, but is it mandated by the standard? QString key = QUrl::fromPercentEncoding(QByteArray().append(tokenPair.first.trimmed().toLatin1())); QString value = QUrl::fromPercentEncoding(QByteArray().append(tokenPair.second.trimmed().toLatin1())); queryParams.insert(key, value); } return std::move(queryParams); } void O2ReplyServer::closeServer(QTcpSocket *socket, bool hasparameters) { if (!isListening()) { return; } qDebug() << "O2ReplyServer::closeServer: Initiating"; int port = serverPort(); if (!socket && sender()) { QTimer *timer = qobject_cast(sender()); if (timer) { qWarning() << "O2ReplyServer::closeServer: Closing due to timeout"; timer->stop(); socket = qobject_cast(timer->parent()); timer->deleteLater(); } } if (socket) { QTimer *timer = socket->findChild("timeoutTimer"); if (timer) { qDebug() << "O2ReplyServer::closeServer: Stopping socket's timeout timer"; timer->stop(); } socket->disconnectFromHost(); } close(); qDebug() << "O2ReplyServer::closeServer: Closed, no longer listening on port" << port; Q_EMIT serverClosed(hasparameters); } QByteArray O2ReplyServer::replyContent() { return replyContent_; } void O2ReplyServer::setReplyContent(const QByteArray &value) { replyContent_ = value; } int O2ReplyServer::timeout() { return timeout_; } void O2ReplyServer::setTimeout(int timeout) { timeout_ = timeout; } int O2ReplyServer::callbackTries() { return maxtries_; } void O2ReplyServer::setCallbackTries(int maxtries) { maxtries_ = maxtries; } + +QString O2ReplyServer::uniqueState() +{ + return uniqueState_; +} + +void O2ReplyServer::setUniqueState(const QString &state) +{ + uniqueState_ = state; +} diff --git a/core/libs/dplugins/webservices/o2/src/o2replyserver.h b/core/libs/dplugins/webservices/o2/src/o2replyserver.h index 53c8923797..666b3309d7 100644 --- a/core/libs/dplugins/webservices/o2/src/o2replyserver.h +++ b/core/libs/dplugins/webservices/o2/src/o2replyserver.h @@ -1,50 +1,54 @@ #ifndef O2REPLYSERVER_H #define O2REPLYSERVER_H #include #include #include #include #include "o0export.h" /// HTTP server to process authentication response. class O0_EXPORT O2ReplyServer: public QTcpServer { Q_OBJECT public: explicit O2ReplyServer(QObject *parent = 0); /// Page content on local host after successful oauth - in case you do not want to close the browser, but display something Q_PROPERTY(QByteArray replyContent READ replyContent WRITE setReplyContent) QByteArray replyContent(); void setReplyContent(const QByteArray &value); /// Seconds to keep listening *after* first response for a callback with token content Q_PROPERTY(int timeout READ timeout WRITE setTimeout) int timeout(); void setTimeout(int timeout); /// Maximum number of callback tries to accept, in case some don't have token content (favicons, etc.) Q_PROPERTY(int callbackTries READ callbackTries WRITE setCallbackTries) int callbackTries(); void setCallbackTries(int maxtries); + QString uniqueState(); + void setUniqueState(const QString &state); + Q_SIGNALS: void verificationReceived(QMap); void serverClosed(bool); // whether it has found parameters public Q_SLOTS: void onIncomingConnection(); void onBytesReady(); QMap parseQueryParams(QByteArray *data); void closeServer(QTcpSocket *socket = 0, bool hasparameters = false); protected: QByteArray replyContent_; int timeout_; int maxtries_; int tries_; + QString uniqueState_; }; #endif // O2REPLYSERVER_H diff --git a/core/libs/dplugins/webservices/o2/src/o2requestor.cpp b/core/libs/dplugins/webservices/o2/src/o2requestor.cpp index 14fc32b1d5..824802938c 100644 --- a/core/libs/dplugins/webservices/o2/src/o2requestor.cpp +++ b/core/libs/dplugins/webservices/o2/src/o2requestor.cpp @@ -1,209 +1,310 @@ +#include + #include #include +#include #if QT_VERSION >= 0x050000 #include #endif #include "o2requestor.h" #include "o2.h" #include "o0globals.h" -O2Requestor::O2Requestor(QNetworkAccessManager *manager, O2 *authenticator, QObject *parent): QObject(parent), reply_(NULL), status_(Idle) { +O2Requestor::O2Requestor(QNetworkAccessManager *manager, O2 *authenticator, QObject *parent): QObject(parent), reply_(NULL), status_(Idle), addAccessTokenInQuery_(true), rawData_(false) { manager_ = manager; authenticator_ = authenticator; if (authenticator) { timedReplies_.setIgnoreSslErrors(authenticator->ignoreSslErrors()); } qRegisterMetaType("QNetworkReply::NetworkError"); connect(authenticator, SIGNAL(refreshFinished(QNetworkReply::NetworkError)), this, SLOT(onRefreshFinished(QNetworkReply::NetworkError)), Qt::QueuedConnection); } O2Requestor::~O2Requestor() { } +void O2Requestor::setAddAccessTokenInQuery(bool value) { + addAccessTokenInQuery_ = value; +} + void O2Requestor::setAccessTokenInAuthenticationHTTPHeaderFormat(const QString &value) { accessTokenInAuthenticationHTTPHeaderFormat_ = value; } -int O2Requestor::get(const QNetworkRequest &req) { +int O2Requestor::get(const QNetworkRequest &req, int timeout/* = 60*1000*/) { if (-1 == setup(req, QNetworkAccessManager::GetOperation)) { return -1; } reply_ = manager_->get(request_); - timedReplies_.add(reply_); + timedReplies_.add(new O2Reply(reply_, timeout)); connect(reply_, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(onRequestError(QNetworkReply::NetworkError)), Qt::QueuedConnection); connect(reply_, SIGNAL(finished()), this, SLOT(onRequestFinished()), Qt::QueuedConnection); return id_; } -int O2Requestor::post(const QNetworkRequest &req, const QByteArray &data) { +int O2Requestor::post(const QNetworkRequest &req, const QByteArray &data, int timeout/* = 60*1000*/) { if (-1 == setup(req, QNetworkAccessManager::PostOperation)) { return -1; } + rawData_ = true; data_ = data; reply_ = manager_->post(request_, data_); - timedReplies_.add(reply_); + timedReplies_.add(new O2Reply(reply_, timeout)); connect(reply_, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(onRequestError(QNetworkReply::NetworkError)), Qt::QueuedConnection); connect(reply_, SIGNAL(finished()), this, SLOT(onRequestFinished()), Qt::QueuedConnection); connect(reply_, SIGNAL(uploadProgress(qint64,qint64)), this, SLOT(onUploadProgress(qint64,qint64))); return id_; } -int O2Requestor::put(const QNetworkRequest &req, const QByteArray &data) { +int O2Requestor::post(const QNetworkRequest & req, QHttpMultiPart* data, int timeout/* = 60*1000*/) +{ + if (-1 == setup(req, QNetworkAccessManager::PostOperation)) { + return -1; + } + rawData_ = false; + multipartData_ = data; + reply_ = manager_->post(request_, multipartData_); + multipartData_->setParent(reply_); + timedReplies_.add(new O2Reply(reply_, timeout)); + connect(reply_, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(onRequestError(QNetworkReply::NetworkError)), Qt::QueuedConnection); + connect(reply_, SIGNAL(finished()), this, SLOT(onRequestFinished()), Qt::QueuedConnection); + connect(reply_, SIGNAL(uploadProgress(qint64,qint64)), this, SLOT(onUploadProgress(qint64,qint64))); + return id_; +} + +int O2Requestor::put(const QNetworkRequest &req, const QByteArray &data, int timeout/* = 60*1000*/) { if (-1 == setup(req, QNetworkAccessManager::PutOperation)) { return -1; } + rawData_ = true; data_ = data; reply_ = manager_->put(request_, data_); - timedReplies_.add(reply_); + timedReplies_.add(new O2Reply(reply_, timeout)); connect(reply_, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(onRequestError(QNetworkReply::NetworkError)), Qt::QueuedConnection); connect(reply_, SIGNAL(finished()), this, SLOT(onRequestFinished()), Qt::QueuedConnection); connect(reply_, SIGNAL(uploadProgress(qint64,qint64)), this, SLOT(onUploadProgress(qint64,qint64))); return id_; } +int O2Requestor::put(const QNetworkRequest & req, QHttpMultiPart* data, int timeout/* = 60*1000*/) +{ + if (-1 == setup(req, QNetworkAccessManager::PutOperation)) { + return -1; + } + rawData_ = false; + multipartData_ = data; + reply_ = manager_->put(request_, multipartData_); + multipartData_->setParent(reply_); + timedReplies_.add(new O2Reply(reply_, timeout)); + connect(reply_, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(onRequestError(QNetworkReply::NetworkError)), Qt::QueuedConnection); + connect(reply_, SIGNAL(finished()), this, SLOT(onRequestFinished()), Qt::QueuedConnection); + connect(reply_, SIGNAL(uploadProgress(qint64,qint64)), this, SLOT(onUploadProgress(qint64,qint64))); + return id_; +} + +int O2Requestor::customRequest(const QNetworkRequest &req, const QByteArray &verb, const QByteArray &data, int timeout/* = 60*1000*/) +{ + Q_UNUSED(timeout); + if (-1 == setup(req, QNetworkAccessManager::CustomOperation, verb)) { + return -1; + } + data_ = data; + QBuffer * buffer = new QBuffer; + buffer->setData(data_); + reply_ = manager_->sendCustomRequest(request_, verb, buffer); + buffer->setParent(reply_); + timedReplies_.add(new O2Reply(reply_)); + connect(reply_, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(onRequestError(QNetworkReply::NetworkError)), Qt::QueuedConnection); + connect(reply_, SIGNAL(finished()), this, SLOT(onRequestFinished()), Qt::QueuedConnection); + connect(reply_, SIGNAL(uploadProgress(qint64,qint64)), this, SLOT(onUploadProgress(qint64,qint64))); + return id_; +} + +int O2Requestor::head(const QNetworkRequest &req, int timeout/* = 60*1000*/) +{ + if (-1 == setup(req, QNetworkAccessManager::HeadOperation)) { + return -1; + } + reply_ = manager_->head(request_); + timedReplies_.add(new O2Reply(reply_, timeout)); + connect(reply_, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(onRequestError(QNetworkReply::NetworkError)), Qt::QueuedConnection); + connect(reply_, SIGNAL(finished()), this, SLOT(onRequestFinished()), Qt::QueuedConnection); + return id_; +} + void O2Requestor::onRefreshFinished(QNetworkReply::NetworkError error) { if (status_ != Requesting) { qWarning() << "O2Requestor::onRefreshFinished: No pending request"; return; } if (QNetworkReply::NoError == error) { QTimer::singleShot(100, this, SLOT(retry())); } else { error_ = error; QTimer::singleShot(10, this, SLOT(finish())); } } void O2Requestor::onRequestFinished() { - QNetworkReply *senderReply = qobject_cast(sender()); - QNetworkReply::NetworkError error = senderReply->error(); if (status_ == Idle) { return; } - if (reply_ != senderReply) { + if (reply_ != qobject_cast(sender())) { return; } - if (error == QNetworkReply::NoError) { + if (reply_->error() == QNetworkReply::NoError) { QTimer::singleShot(10, this, SLOT(finish())); } } void O2Requestor::onRequestError(QNetworkReply::NetworkError error) { qWarning() << "O2Requestor::onRequestError: Error" << (int)error; if (status_ == Idle) { return; } if (reply_ != qobject_cast(sender())) { return; } int httpStatus = reply_->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); qWarning() << "O2Requestor::onRequestError: HTTP status" << httpStatus << reply_->attribute(QNetworkRequest::HttpReasonPhraseAttribute).toString(); if ((status_ == Requesting) && (httpStatus == 401)) { // Call O2::refresh. Note the O2 instance might live in a different thread if (QMetaObject::invokeMethod(authenticator_, "refresh")) { return; } qCritical() << "O2Requestor::onRequestError: Invoking remote refresh failed"; } error_ = error; QTimer::singleShot(10, this, SLOT(finish())); } void O2Requestor::onUploadProgress(qint64 uploaded, qint64 total) { if (status_ == Idle) { qWarning() << "O2Requestor::onUploadProgress: No pending request"; return; } if (reply_ != qobject_cast(sender())) { return; } + // Restart timeout because request in progress + O2Reply *o2Reply = timedReplies_.find(reply_); + if(o2Reply) + o2Reply->start(); Q_EMIT uploadProgress(id_, uploaded, total); } -int O2Requestor::setup(const QNetworkRequest &req, QNetworkAccessManager::Operation operation) { +int O2Requestor::setup(const QNetworkRequest &req, QNetworkAccessManager::Operation operation, const QByteArray &verb) { static int currentId; - QUrl url; if (status_ != Idle) { qWarning() << "O2Requestor::setup: Another request pending"; return -1; } request_ = req; operation_ = operation; id_ = currentId++; - url_ = url = req.url(); + url_ = req.url(); + + QUrl url = url_; + if (addAccessTokenInQuery_) { #if QT_VERSION < 0x050000 - url.addQueryItem(O2_OAUTH2_ACCESS_TOKEN, authenticator_->token()); + url.addQueryItem(O2_OAUTH2_ACCESS_TOKEN, authenticator_->token()); #else - QUrlQuery query(url); - query.addQueryItem(O2_OAUTH2_ACCESS_TOKEN, authenticator_->token()); - url.setQuery(query); + QUrlQuery query(url); + query.addQueryItem(O2_OAUTH2_ACCESS_TOKEN, authenticator_->token()); + url.setQuery(query); #endif + } + request_.setUrl(url); - + // If the service require the access token to be sent as a Authentication HTTP header, we add the access token. - if(!accessTokenInAuthenticationHTTPHeaderFormat_.isEmpty()) { + if (!accessTokenInAuthenticationHTTPHeaderFormat_.isEmpty()) { request_.setRawHeader(O2_HTTP_AUTHORIZATION_HEADER, accessTokenInAuthenticationHTTPHeaderFormat_.arg(authenticator_->token()).toLatin1()); } - + + if (!verb.isEmpty()) { + request_.setRawHeader(O2_HTTP_HTTP_HEADER, verb); + } + status_ = Requesting; error_ = QNetworkReply::NoError; return id_; } void O2Requestor::finish() { QByteArray data; if (status_ == Idle) { qWarning() << "O2Requestor::finish: No pending request"; return; } data = reply_->readAll(); status_ = Idle; timedReplies_.remove(reply_); reply_->disconnect(this); reply_->deleteLater(); + QList headers = reply_->rawHeaderPairs(); Q_EMIT finished(id_, error_, data); + Q_EMIT finished(id_, error_, data, headers); } void O2Requestor::retry() { if (status_ != Requesting) { qWarning() << "O2Requestor::retry: No pending request"; return; } timedReplies_.remove(reply_); reply_->disconnect(this); reply_->deleteLater(); QUrl url = url_; + if (addAccessTokenInQuery_) { #if QT_VERSION < 0x050000 - url.addQueryItem(O2_OAUTH2_ACCESS_TOKEN, authenticator_->token()); + url.addQueryItem(O2_OAUTH2_ACCESS_TOKEN, authenticator_->token()); #else - QUrlQuery query(url); - query.addQueryItem(O2_OAUTH2_ACCESS_TOKEN, authenticator_->token()); - url.setQuery(query); + QUrlQuery query(url); + query.addQueryItem(O2_OAUTH2_ACCESS_TOKEN, authenticator_->token()); + url.setQuery(query); #endif + } request_.setUrl(url); - + // If the service require the access token to be sent as a Authentication HTTP header, // we update the access token when retrying. if(!accessTokenInAuthenticationHTTPHeaderFormat_.isEmpty()) { request_.setRawHeader(O2_HTTP_AUTHORIZATION_HEADER, accessTokenInAuthenticationHTTPHeaderFormat_.arg(authenticator_->token()).toLatin1()); } - + status_ = ReRequesting; switch (operation_) { case QNetworkAccessManager::GetOperation: reply_ = manager_->get(request_); break; case QNetworkAccessManager::PostOperation: - reply_ = manager_->post(request_, data_); + reply_ = rawData_ ? manager_->post(request_, data_) : manager_->post(request_, multipartData_); + break; + case QNetworkAccessManager::CustomOperation: + { + QBuffer * buffer = new QBuffer; + buffer->setData(data_); + reply_ = manager_->sendCustomRequest(request_, request_.rawHeader(O2_HTTP_HTTP_HEADER), buffer); + buffer->setParent(reply_); + } + break; + case QNetworkAccessManager::PutOperation: + reply_ = rawData_ ? manager_->post(request_, data_) : manager_->put(request_, multipartData_); + break; + case QNetworkAccessManager::HeadOperation: + reply_ = manager_->head(request_); break; default: - reply_ = manager_->put(request_, data_); + assert(!"Unspecified operation for request"); + reply_ = manager_->get(request_); + break; } timedReplies_.add(reply_); connect(reply_, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(onRequestError(QNetworkReply::NetworkError)), Qt::QueuedConnection); connect(reply_, SIGNAL(finished()), this, SLOT(onRequestFinished()), Qt::QueuedConnection); connect(reply_, SIGNAL(uploadProgress(qint64,qint64)), this, SLOT(onUploadProgress(qint64,qint64))); } diff --git a/core/libs/dplugins/webservices/o2/src/o2requestor.h b/core/libs/dplugins/webservices/o2/src/o2requestor.h index 730f2f05d9..a157bbe9ad 100644 --- a/core/libs/dplugins/webservices/o2/src/o2requestor.h +++ b/core/libs/dplugins/webservices/o2/src/o2requestor.h @@ -1,90 +1,120 @@ #ifndef O2REQUESTOR_H #define O2REQUESTOR_H #include #include #include #include #include #include +#include #include "o0export.h" #include "o2reply.h" class O2; /// Makes authenticated requests. class O0_EXPORT O2Requestor: public QObject { Q_OBJECT public: explicit O2Requestor(QNetworkAccessManager *manager, O2 *authenticator, QObject *parent = 0); ~O2Requestor(); - + + + /// Some services require the access token to be sent as a Authentication HTTP header + /// and refuse requests with the access token in the query. + /// This function allows to use or ignore the access token in the query. + /// The default value of `true` means that the query will contain the access token. + /// By setting the value to false, the query will not contain the access token. + /// See: + /// https://tools.ietf.org/html/draft-ietf-oauth-v2-bearer-16#section-4.3 + /// https://tools.ietf.org/html/rfc6750#section-2.3 + + void setAddAccessTokenInQuery(bool value); + /// Some services require the access token to be sent as a Authentication HTTP header. /// This is the case for Twitch and Mixer. /// When the access token expires and is refreshed, O2Requestor::retry() needs to update the Authentication HTTP header. /// In order to do so, O2Requestor needs to know the format of the Authentication HTTP header. void setAccessTokenInAuthenticationHTTPHeaderFormat(const QString &value); public Q_SLOTS: /// Make a GET request. /// @return Request ID or -1 if there are too many requests in the queue. - int get(const QNetworkRequest &req); + int get(const QNetworkRequest &req, int timeout = 60*1000); /// Make a POST request. /// @return Request ID or -1 if there are too many requests in the queue. - int post(const QNetworkRequest &req, const QByteArray &data); + int post(const QNetworkRequest &req, const QByteArray &data, int timeout = 60*1000); + int post(const QNetworkRequest &req, QHttpMultiPart* data, int timeout = 60*1000); /// Make a PUT request. /// @return Request ID or -1 if there are too many requests in the queue. - int put(const QNetworkRequest &req, const QByteArray &data); + int put(const QNetworkRequest &req, const QByteArray &data, int timeout = 60*1000); + int put(const QNetworkRequest &req, QHttpMultiPart* data, int timeout = 60*1000); + + /// Make a HEAD request. + /// @return Request ID or -1 if there are too many requests in the queue. + int head(const QNetworkRequest &req, int timeout = 60*1000); + + /// Make a custom request. + /// @return Request ID or -1 if there are too many requests in the queue. + int customRequest(const QNetworkRequest &req, const QByteArray &verb, const QByteArray &data, int timeout = 60*1000); Q_SIGNALS: + /// Emitted when a request has been completed or failed. void finished(int id, QNetworkReply::NetworkError error, QByteArray data); + /// Emitted when a request has been completed or faled. Also reply headers will be provided. + void finished(int id, QNetworkReply::NetworkError error, QByteArray data, QList headers); + /// Emitted when an upload has progressed. void uploadProgress(int id, qint64 bytesSent, qint64 bytesTotal); protected Q_SLOTS: /// Handle refresh completion. void onRefreshFinished(QNetworkReply::NetworkError error); /// Handle request finished. void onRequestFinished(); /// Handle request error. void onRequestError(QNetworkReply::NetworkError error); /// Re-try request (after successful token refresh). void retry(); /// Finish the request, Q_EMIT finished() signal. void finish(); /// Handle upload progress. void onUploadProgress(qint64 uploaded, qint64 total); protected: - int setup(const QNetworkRequest &request, QNetworkAccessManager::Operation operation); + int setup(const QNetworkRequest &request, QNetworkAccessManager::Operation operation, const QByteArray &verb = QByteArray()); enum Status { Idle, Requesting, ReRequesting }; QNetworkAccessManager *manager_; O2 *authenticator_; QNetworkRequest request_; QByteArray data_; + QHttpMultiPart* multipartData_; QNetworkReply *reply_; Status status_; int id_; QNetworkAccessManager::Operation operation_; QUrl url_; O2ReplyList timedReplies_; QNetworkReply::NetworkError error_; + bool addAccessTokenInQuery_; QString accessTokenInAuthenticationHTTPHeaderFormat_; + bool rawData_; }; #endif // O2REQUESTOR_H diff --git a/core/libs/dplugins/webservices/o2/src/o2simplecrypt.cpp b/core/libs/dplugins/webservices/o2/src/o2simplecrypt.cpp index 210ae12b92..95824ec82f 100644 --- a/core/libs/dplugins/webservices/o2/src/o2simplecrypt.cpp +++ b/core/libs/dplugins/webservices/o2/src/o2simplecrypt.cpp @@ -1,254 +1,266 @@ /* Copyright (c) 2011, Andre Somers All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the Rathenau Instituut, Andre Somers nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL ANDRE SOMERS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "o0simplecrypt.h" #include #include #include #include #include #include O0SimpleCrypt::O0SimpleCrypt(): m_key(0), m_compressionMode(CompressionAuto), m_protectionMode(ProtectionChecksum), m_lastError(ErrorNoError) { +#if (QT_VERSION < QT_VERSION_CHECK(5, 10, 0)) qsrand(uint(QDateTime::currentMSecsSinceEpoch() & 0xFFFF)); +#else + m_rand.seed(uint(QDateTime::currentMSecsSinceEpoch() & 0xFFFF)); +#endif } O0SimpleCrypt::O0SimpleCrypt(quint64 key): m_key(key), m_compressionMode(CompressionAuto), m_protectionMode(ProtectionChecksum), m_lastError(ErrorNoError) { +#if (QT_VERSION < QT_VERSION_CHECK(5, 10, 0)) qsrand(uint(QDateTime::currentMSecsSinceEpoch() & 0xFFFF)); +#else + m_rand.seed(uint(QDateTime::currentMSecsSinceEpoch() & 0xFFFF)); +#endif splitKey(); } void O0SimpleCrypt::setKey(quint64 key) { m_key = key; splitKey(); } void O0SimpleCrypt::splitKey() { m_keyParts.clear(); m_keyParts.resize(8); for (int i=0;i<8;i++) { quint64 part = m_key; for (int j=i; j>0; j--) part = part >> 8; part = part & 0xff; m_keyParts[i] = static_cast(part); } } QByteArray O0SimpleCrypt::encryptToByteArray(const QString& plaintext) { QByteArray plaintextArray = plaintext.toUtf8(); return encryptToByteArray(plaintextArray); } QByteArray O0SimpleCrypt::encryptToByteArray(QByteArray plaintext) { if (m_keyParts.isEmpty()) { qWarning() << "No key set."; m_lastError = ErrorNoKeySet; return QByteArray(); } QByteArray ba = plaintext; CryptoFlags flags = CryptoFlagNone; if (m_compressionMode == CompressionAlways) { ba = qCompress(ba, 9); //maximum compression flags |= CryptoFlagCompression; } else if (m_compressionMode == CompressionAuto) { QByteArray compressed = qCompress(ba, 9); if (compressed.count() < ba.count()) { ba = compressed; flags |= CryptoFlagCompression; } } QByteArray integrityProtection; if (m_protectionMode == ProtectionChecksum) { flags |= CryptoFlagChecksum; QDataStream s(&integrityProtection, QIODevice::WriteOnly); s << qChecksum(ba.constData(), ba.length()); } else if (m_protectionMode == ProtectionHash) { flags |= CryptoFlagHash; QCryptographicHash hash(QCryptographicHash::Sha1); hash.addData(ba); integrityProtection += hash.result(); } //prepend a random char to the string +#if (QT_VERSION < QT_VERSION_CHECK(5, 10, 0)) char randomChar = char(qrand() & 0xFF); +#else + char randomChar = char(m_rand.generate() & 0xFF); +#endif ba = randomChar + integrityProtection + ba; int pos(0); char lastChar(0); int cnt = ba.count(); while (pos < cnt) { ba[pos] = ba.at(pos) ^ m_keyParts.at(pos % 8) ^ lastChar; lastChar = ba.at(pos); ++pos; } QByteArray resultArray; resultArray.append(char(0x03)); //version for future updates to algorithm resultArray.append(char(flags)); //encryption flags resultArray.append(ba); m_lastError = ErrorNoError; return resultArray; } QString O0SimpleCrypt::encryptToString(const QString& plaintext) { QByteArray plaintextArray = plaintext.toUtf8(); QByteArray cypher = encryptToByteArray(plaintextArray); QString cypherString = QString::fromLatin1(cypher.toBase64()); return cypherString; } QString O0SimpleCrypt::encryptToString(QByteArray plaintext) { QByteArray cypher = encryptToByteArray(plaintext); QString cypherString = QString::fromLatin1(cypher.toBase64()); return cypherString; } QString O0SimpleCrypt::decryptToString(const QString &cyphertext) { QByteArray cyphertextArray = QByteArray::fromBase64(cyphertext.toLatin1()); QByteArray plaintextArray = decryptToByteArray(cyphertextArray); QString plaintext = QString::fromUtf8(plaintextArray, plaintextArray.size()); return plaintext; } QString O0SimpleCrypt::decryptToString(QByteArray cypher) { QByteArray ba = decryptToByteArray(cypher); QString plaintext = QString::fromUtf8(ba, ba.size()); return plaintext; } QByteArray O0SimpleCrypt::decryptToByteArray(const QString& cyphertext) { QByteArray cyphertextArray = QByteArray::fromBase64(cyphertext.toLatin1()); QByteArray ba = decryptToByteArray(cyphertextArray); return ba; } QByteArray O0SimpleCrypt::decryptToByteArray(QByteArray cypher) { if (m_keyParts.isEmpty()) { qWarning() << "No key set."; m_lastError = ErrorNoKeySet; return QByteArray(); } if (!cypher.length()) { m_lastError = ErrorUnknownVersion; return QByteArray(); } QByteArray ba = cypher; char version = ba.at(0); if (version !=3) { //we only work with version 3 m_lastError = ErrorUnknownVersion; qWarning() << "Invalid version or not a cyphertext."; return QByteArray(); } CryptoFlags flags = CryptoFlags(ba.at(1)); ba = ba.mid(2); int pos(0); int cnt(ba.count()); char lastChar = 0; while (pos < cnt) { char currentChar = ba[pos]; ba[pos] = ba.at(pos) ^ lastChar ^ m_keyParts.at(pos % 8); lastChar = currentChar; ++pos; } ba = ba.mid(1); //chop off the random number at the start bool integrityOk(true); if (flags.testFlag(CryptoFlagChecksum)) { if (ba.length() < 2) { m_lastError = ErrorIntegrityFailed; return QByteArray(); } quint16 storedChecksum; { QDataStream s(&ba, QIODevice::ReadOnly); s >> storedChecksum; } ba = ba.mid(2); quint16 checksum = qChecksum(ba.constData(), ba.length()); integrityOk = (checksum == storedChecksum); } else if (flags.testFlag(CryptoFlagHash)) { if (ba.length() < 20) { m_lastError = ErrorIntegrityFailed; return QByteArray(); } QByteArray storedHash = ba.left(20); ba = ba.mid(20); QCryptographicHash hash(QCryptographicHash::Sha1); hash.addData(ba); integrityOk = (hash.result() == storedHash); } if (!integrityOk) { m_lastError = ErrorIntegrityFailed; return QByteArray(); } if (flags.testFlag(CryptoFlagCompression)) ba = qUncompress(ba); m_lastError = ErrorNoError; return ba; } diff --git a/core/libs/dplugins/webservices/o2/src/o2surveymonkey.cpp b/core/libs/dplugins/webservices/o2/src/o2surveymonkey.cpp index 8eb3613a0c..0f1b4a297d 100644 --- a/core/libs/dplugins/webservices/o2/src/o2surveymonkey.cpp +++ b/core/libs/dplugins/webservices/o2/src/o2surveymonkey.cpp @@ -1,21 +1,20 @@ #include #include #include #include #if QT_VERSION >= 0x050000 #include #endif #include "o2surveymonkey.h" -#include "o2globals.h" static const char *SMEndpoint = "https://api.surveymonkey.net/oauth/authorize"; static const char *SMTokenUrl = "https://api.surveymonkey.net/oauth/token"; static const quint16 SMLocalPort = 8000; O2SurveyMonkey::O2SurveyMonkey(QObject *parent): O2(parent) { setRequestUrl(SMEndpoint); setTokenUrl(SMTokenUrl); setLocalPort(SMLocalPort); setIgnoreSslErrors(true); //needed on Mac } diff --git a/core/libs/dplugins/webservices/o2/src/o2uber.cpp b/core/libs/dplugins/webservices/o2/src/o2uber.cpp index 9bb5d6e13d..632c84e1f2 100644 --- a/core/libs/dplugins/webservices/o2/src/o2uber.cpp +++ b/core/libs/dplugins/webservices/o2/src/o2uber.cpp @@ -1,98 +1,99 @@ #include #include #include #include #include #include #include #include #if QT_VERSION >= 0x050000 #include #endif #include "o2uber.h" #include "o0globals.h" static const char *UberEndpoint = "https://login.uber.com/oauth/v2/authorize"; static const char *UberTokenUrl = "https://login.uber.com/oauth/v2/token"; static const char *UberExpiresIn = "expires_in"; static const char *UberGrantType = "authorization_code"; O2Uber::O2Uber(QObject *parent): O2(parent) { setRequestUrl(UberEndpoint); setTokenUrl(UberTokenUrl); } void O2Uber::onVerificationReceived(const QMap response){ qDebug() << "O2Uber::onVerificationReceived: Emitting closeBrowser()"; Q_EMIT closeBrowser(); if (response.contains("error")) { qWarning() << "O2Uber::onVerificationReceived: Verification failed"; foreach (QString key, response.keys()) { qWarning() << "O2Uber::onVerificationReceived:" << key << response.value(key); } Q_EMIT linkingFailed(); return; } // Save access code setCode(response.value(O2_OAUTH2_GRANT_TYPE_CODE)); // Exchange access code for access/refresh tokens QUrl url(tokenUrl_); #if QT_VERSION < 0x050000 url.addQueryItem(O2_OAUTH2_CLIENT_ID, clientId_); url.addQueryItem(O2_OAUTH2_CLIENT_SECRET, clientSecret_); url.addQueryItem(O2_OAUTH2_GRANT_TYPE, UberGrantType); url.addQueryItem(O2_OAUTH2_REDIRECT_URI, redirectUri_); url.addQueryItem(O2_OAUTH2_GRANT_TYPE_CODE, code()); url.addQueryItem(O2_OAUTH2_SCOPE, scope_); #else QUrlQuery query(url); query.addQueryItem(O2_OAUTH2_CLIENT_ID, clientId_); query.addQueryItem(O2_OAUTH2_CLIENT_SECRET, clientSecret_); query.addQueryItem(O2_OAUTH2_GRANT_TYPE, UberGrantType); query.addQueryItem(O2_OAUTH2_REDIRECT_URI, redirectUri_); query.addQueryItem(O2_OAUTH2_GRANT_TYPE_CODE, code()); query.addQueryItem(O2_OAUTH2_SCOPE, scope_); url.setQuery(query); #endif QNetworkRequest tokenRequest(url); + tokenRequest.setHeader(QNetworkRequest::ContentTypeHeader, "application/json"); QNetworkReply *tokenReply = manager_->post(tokenRequest, QByteArray()); timedReplies_.add(tokenReply); connect(tokenReply, SIGNAL(finished()), this, SLOT(onTokenReplyFinished()), Qt::QueuedConnection); connect(tokenReply, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(onTokenReplyError(QNetworkReply::NetworkError)), Qt::QueuedConnection); } void O2Uber::onTokenReplyFinished(){ qDebug() << "O2Uber::onTokenReplyFinished"; QNetworkReply *tokenReply = qobject_cast(sender()); if (tokenReply->error() == QNetworkReply::NoError) { // Process reply QByteArray replyData = tokenReply->readAll(); QJsonDocument doc = QJsonDocument::fromJson(replyData); const QJsonObject rootObject = doc.object(); QVariantMap reply; for (const QString &key : rootObject.keys()) { reply.insert(key, rootObject[key].toVariant()); } // Interpret reply setToken(reply.value(O2_OAUTH2_ACCESS_TOKEN, QString()).toString()); setExpires(reply.value(UberExpiresIn).toInt()); setRefreshToken(reply.value(O2_OAUTH2_REFRESH_TOKEN, QString()).toString()); setExtraTokens(reply); timedReplies_.remove(tokenReply); setLinked(true); Q_EMIT linkingSucceeded(); } else { qWarning() << "O2Uber::onTokenReplyFinished:" << tokenReply->errorString(); } } diff --git a/core/libs/dplugins/webservices/o2/src/o2vimeo.cpp b/core/libs/dplugins/webservices/o2/src/o2vimeo.cpp new file mode 100644 index 0000000000..806b3f0d94 --- /dev/null +++ b/core/libs/dplugins/webservices/o2/src/o2vimeo.cpp @@ -0,0 +1,13 @@ +#include "o2vimeo.h" + +// Vimeo supported scopes: https://developer.vimeo.com/api/authentication#supported-scopes +static const char *VimeoScope = "public"; +static const char *VimeoEndpoint = "https://api.vimeo.com/oauth/authorize"; +static const char *VimeoTokenUrl = "https://api.vimeo.com/oauth/access_token"; + +O2Vimeo::O2Vimeo(QObject *parent): O2(parent) { + setRequestUrl(VimeoEndpoint); + setTokenUrl(VimeoTokenUrl); + setRefreshTokenUrl(VimeoTokenUrl); + setScope(VimeoScope); +} diff --git a/core/libs/dplugins/webservices/o2/src/o2vimeo.h b/core/libs/dplugins/webservices/o2/src/o2vimeo.h new file mode 100644 index 0000000000..f1acd164ff --- /dev/null +++ b/core/libs/dplugins/webservices/o2/src/o2vimeo.h @@ -0,0 +1,15 @@ +#ifndef O2VIMEO_H +#define O2VIMEO_H + +#include "o0export.h" +#include "o2.h" + +/// Vimeo dialect of OAuth 2.0 +class O0_EXPORT O2Vimeo : public O2 { + Q_OBJECT + +public: + explicit O2Vimeo(QObject *parent = 0); +}; + +#endif // O2VIMEO_H diff --git a/core/libs/dplugins/webservices/wstoolutils.cpp b/core/libs/dplugins/webservices/wstoolutils.cpp index 9efe900ccb..da77482869 100644 --- a/core/libs/dplugins/webservices/wstoolutils.cpp +++ b/core/libs/dplugins/webservices/wstoolutils.cpp @@ -1,91 +1,113 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2014-09-12 * Description : Web Service tool utils methods * * Copyright (C) 2014-2020 by Gilles Caulier * * 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, 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. * * ============================================================ */ #include "wstoolutils.h" // Qt includes #include #include #include #include #include +#if (QT_VERSION >= QT_VERSION_CHECK(5, 10, 0)) +# include +#endif + namespace Digikam { QDir WSToolUtils::makeTemporaryDir(const char* prefix) { QString subDir = QString::fromLatin1("digikam-%1-%2").arg(QString::fromUtf8(prefix)).arg(qApp->applicationPid()); QString path = QDir(QDir::tempPath()).filePath(subDir); if (!QDir().exists(path)) { QDir().mkpath(path); } return QDir(path); } // ------------------------------------------------------------------------------------ void WSToolUtils::removeTemporaryDir(const char* prefix) { QString subDir = QString::fromLatin1("digikam-%1-%2").arg(QString::fromUtf8(prefix)).arg(qApp->applicationPid()); QString path = QDir(QDir::tempPath()).filePath(subDir); if (QDir().exists(path)) { QDir(path).removeRecursively(); } } // ------------------------------------------------------------------------------------ QString WSToolUtils::randomString(const int& length) { const QString possibleCharacters(QLatin1String("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789")); QString randomString; + +#if (QT_VERSION < QT_VERSION_CHECK(5, 10, 0)) + qsrand((uint)QTime::currentTime().msec()); +#else + + QRandomGenerator* const generator = QRandomGenerator::global(); + +#endif + for (int i = 0 ; i < length ; ++i) { - int index = qrand() % possibleCharacters.length(); - QChar nextChar = possibleCharacters.at(index); +#if (QT_VERSION < QT_VERSION_CHECK(5, 10, 0)) + + const int index = qrand() % possibleCharacters.length(); + +#else + + const int index = generator->bounded(possibleCharacters.length()); + +#endif + + QChar nextChar = possibleCharacters.at(index); randomString.append(nextChar); } return randomString; } // ------------------------------------------------------------------------------------ QSettings* WSToolUtils::getOauthSettings(QObject* const parent) { QString dkoauth = QStandardPaths::writableLocation(QStandardPaths::ConfigLocation) + QLatin1String("/digikam_oauthrc"); return (new QSettings(dkoauth, QSettings::IniFormat, parent)); } } // namespace Digikam diff --git a/core/libs/dragdrop/CMakeLists.txt b/core/libs/dragdrop/CMakeLists.txt index 5985facad0..83f6fd221c 100644 --- a/core/libs/dragdrop/CMakeLists.txt +++ b/core/libs/dragdrop/CMakeLists.txt @@ -1,29 +1,29 @@ # # Copyright (c) 2010-2020 by Gilles Caulier, # # Redistribution and use is allowed according to the terms of the BSD license. # For details see the accompanying COPYING-CMAKE-SCRIPTS file. APPLY_COMMON_POLICIES() set(libdragdrop_SRCS - abstractitemdragdrophandler.cpp - dragdropimplementations.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/abstractitemdragdrophandler.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/dragdropimplementations.cpp ) include_directories( $ $ $ $ $ ) # Used by digikamcore add_library(core_dragdrop_obj OBJECT ${libdragdrop_SRCS}) target_compile_definitions(core_dragdrop_obj PRIVATE digikamcore_EXPORTS ) diff --git a/core/libs/dtrash/CMakeLists.txt b/core/libs/dtrash/CMakeLists.txt index 6c02a78c1d..18e95d6957 100644 --- a/core/libs/dtrash/CMakeLists.txt +++ b/core/libs/dtrash/CMakeLists.txt @@ -1,30 +1,30 @@ # # Copyright (c) 2015 by Mohamed_Anwer, # # Redistribution and use is allowed according to the terms of the BSD license. # For details see the accompanying COPYING-CMAKE-SCRIPTS file. APPLY_COMMON_POLICIES() set(libdtrash_SRCS - ${CMAKE_SOURCE_DIR}/core/libs/dtrash/dtrash.cpp - ${CMAKE_SOURCE_DIR}/core/libs/dtrash/dtrashiteminfo.cpp - ${CMAKE_SOURCE_DIR}/core/libs/dtrash/dtrashitemmodel.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/dtrash.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/dtrashiteminfo.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/dtrashitemmodel.cpp ) include_directories( $ $ $ $ $ ) # Used by digikamgui add_library(gui_dtrash_obj OBJECT ${libdtrash_SRCS}) target_compile_definitions(gui_dtrash_obj PRIVATE digikamgui_EXPORTS ) diff --git a/core/libs/facesengine/CMakeLists.txt b/core/libs/facesengine/CMakeLists.txt index 7c274cd63e..49f859b16a 100644 --- a/core/libs/facesengine/CMakeLists.txt +++ b/core/libs/facesengine/CMakeLists.txt @@ -1,193 +1,192 @@ # -# Copyright (c) 2010-2019, Gilles Caulier, +# Copyright (c) 2010-2020, Gilles Caulier, # # Redistribution and use is allowed according to the terms of the BSD license. # For details see the accompanying COPYING-CMAKE-SCRIPTS file. APPLY_COMMON_POLICIES() # Option to switch between DNN or HAAR face detection. # DNN is the default one, Haar is the legacy deprecated method. set(DNN_DETECTION TRUE) # Option to switch between DNN or LBPH face recognition. # DNN is the default one, LBPH is the legacy deprecated method. set(DNN_RECOGNITION TRUE) # --------------------------------------------------------------------------- kde_enable_exceptions() include_directories($ $ $ $ $ $ $ $ ) include_directories(${CMAKE_CURRENT_SOURCE_DIR}/.. ${CMAKE_CURRENT_SOURCE_DIR}/common ${CMAKE_CURRENT_SOURCE_DIR}/facedb ${CMAKE_CURRENT_SOURCE_DIR}/alignment/congealing ${CMAKE_CURRENT_SOURCE_DIR}/detection ${CMAKE_CURRENT_SOURCE_DIR}/preprocessing ${CMAKE_CURRENT_SOURCE_DIR}/preprocessing/shape-predictor ${CMAKE_CURRENT_SOURCE_DIR}/preprocessing/recognition # ${CMAKE_CURRENT_SOURCE_DIR}/alignment/flandmark # ${CMAKE_CURRENT_SOURCE_DIR}/preprocessing/tantriggs ) # --------------------------------------------------------------------------- -set(facesengine_LIB_SRCS common/identity.cpp - common/dataproviders.cpp - common/opencvmatdata.cpp +set(facesengine_LIB_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/common/identity.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/common/dataproviders.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/common/opencvmatdata.cpp - detection/facedetector.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/detection/facedetector.cpp - preprocessing/recognition/recognitionpreprocessor.cpp - preprocessing/recognition/openfacepreprocessor.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/preprocessing/recognition/recognitionpreprocessor.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/preprocessing/recognition/openfacepreprocessor.cpp - preprocessing/shape-predictor/matrixoperations.cpp - preprocessing/shape-predictor/pointtransformaffine.cpp - preprocessing/shape-predictor/qdatastreamoverloads.cpp - preprocessing/shape-predictor/fullobjectdetection.cpp - preprocessing/shape-predictor/shapepredictor.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/preprocessing/shape-predictor/matrixoperations.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/preprocessing/shape-predictor/pointtransformaffine.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/preprocessing/shape-predictor/qdatastreamoverloads.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/preprocessing/shape-predictor/fullobjectdetection.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/preprocessing/shape-predictor/shapepredictor.cpp - # alignment/flandmark/flandmarkaligner.cpp - # preprocessing/tantriggs/tantriggspreprocessor.cpp + # ${CMAKE_CURRENT_SOURCE_DIR}/alignment/flandmark/flandmarkaligner.cpp + # ${CMAKE_CURRENT_SOURCE_DIR}/preprocessing/tantriggs/tantriggspreprocessor.cpp ) if(DNN_DETECTION) add_definitions(-DUSE_DNN_DETECTION_BACKEND) include_directories(${CMAKE_CURRENT_SOURCE_DIR}/detection/opencv-dnn) set(facesengine_LIB_SRCS ${facesengine_LIB_SRCS} - detection/opencv-dnn/opencvdnnfacedetector.cpp - detection/opencv-dnn/dnnfacedetectorssd.cpp - detection/opencv-dnn/dnnfacedetectoryolo.cpp - detection/opencv-dnn/dnnfacedetectorbase.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/detection/opencv-dnn/opencvdnnfacedetector.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/detection/opencv-dnn/dnnfacedetectorssd.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/detection/opencv-dnn/dnnfacedetectoryolo.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/detection/opencv-dnn/dnnfacedetectorbase.cpp ) else() - include_directories(detection/opencv-face) + include_directories(${CMAKE_CURRENT_SOURCE_DIR}/detection/opencv-face) set(facesengine_LIB_SRCS ${facesengine_LIB_SRCS} - detection/opencv-face/opencvfacedetector.cpp - detection/opencv-face/opencvfacedetector_p.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/detection/opencv-face/opencvfacedetector.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/detection/opencv-face/opencvfacedetector_p.cpp ) endif() # Used by digikamcore add_library(core_digikamfacesengine_obj OBJECT ${facesengine_LIB_SRCS} ) target_compile_definitions(core_digikamfacesengine_obj PRIVATE digikamcore_EXPORTS ) # For unit-tests add_library(digikamfacesengine STATIC $) # --------------------------------------------------------------------------- -set(facesengine_database_LIB_SRCS recognition/recognitiondatabase.cpp - recognition/recognitiondatabase_p.cpp - recognition/recognitiondatabase_identity.cpp - recognition/recognitiondatabase_training.cpp - recognition/recognitiondatabase_backend.cpp - recognition/recognitiondatabase_recognize.cpp - recognition/recognitiontrainingprovider.cpp +set(facesengine_database_LIB_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/recognition/recognitiondatabase.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/recognition/recognitiondatabase_p.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/recognition/recognitiondatabase_identity.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/recognition/recognitiondatabase_training.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/recognition/recognitiondatabase_backend.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/recognition/recognitiondatabase_recognize.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/recognition/recognitiontrainingprovider.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/alignment/congealing/funnelreal.cpp - alignment/congealing/funnelreal.cpp - - facedb/facedbaccess.cpp - facedb/facedbbackend.cpp - facedb/facedboperationgroup.cpp - facedb/facedbschemaupdater.cpp - facedb/facedb.cpp - facedb/facedb_identity.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/facedb/facedbaccess.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/facedb/facedbbackend.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/facedb/facedboperationgroup.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/facedb/facedbschemaupdater.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/facedb/facedb.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/facedb/facedb_identity.cpp # Eigen Faces recognition module based on OpenCV (experimental and deprecated) -# recognition/opencv-eigenfaces/eigenfacemodel.cpp -# recognition/opencv-eigenfaces/opencveigenfacerecognizer.cpp -# recognition/opencv-eigenfaces/facerec_eigenborrowed.cpp -# facedb/facedb_eigen.cpp +# ${CMAKE_CURRENT_SOURCE_DIR}/recognition/opencv-eigenfaces/eigenfacemodel.cpp +# ${CMAKE_CURRENT_SOURCE_DIR}/recognition/opencv-eigenfaces/opencveigenfacerecognizer.cpp +# ${CMAKE_CURRENT_SOURCE_DIR}/recognition/opencv-eigenfaces/facerec_eigenborrowed.cpp +# ${CMAKE_CURRENT_SOURCE_DIR}/facedb/facedb_eigen.cpp # Fisher Faces recognition module based on OpenCV (experimental and deprecated) -# recognition/opencv-fisherfaces/fisherfacemodel.cpp -# recognition/opencv-fisherfaces/opencvfisherfacerecognizer.cpp -# recognition/opencv-fisherfaces/facerec_fisherborrowed.cpp -# facedb/facedb_fisher.cpp +# ${CMAKE_CURRENT_SOURCE_DIR}/recognition/opencv-fisherfaces/fisherfacemodel.cpp +# ${CMAKE_CURRENT_SOURCE_DIR}/recognition/opencv-fisherfaces/opencvfisherfacerecognizer.cpp +# ${CMAKE_CURRENT_SOURCE_DIR}/recognition/opencv-fisherfaces/facerec_fisherborrowed.cpp +# ${CMAKE_CURRENT_SOURCE_DIR}/facedb/facedb_fisher.cpp ) if(DNN_RECOGNITION) add_definitions(-DUSE_DNN_RECOGNITION_BACKEND) include_directories(${CMAKE_CURRENT_SOURCE_DIR}/recognition/opencv-dnn) set(facesengine_database_LIB_SRCS ${facesengine_database_LIB_SRCS} # Neural NetWork Faces recognition module based on OpenCV DNN - facedb/facedb_dnn.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/facedb/facedb_dnn.cpp - recognition/opencv-dnn/dnnfacemodel.cpp - recognition/opencv-dnn/opencvdnnfacerecognizer.cpp - recognition/opencv-dnn/facerec_dnnborrowed.cpp - recognition/opencv-dnn/dnnfaceextractor.cpp - recognition/opencv-dnn/dnndbscan.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/recognition/opencv-dnn/dnnfacemodel.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/recognition/opencv-dnn/opencvdnnfacerecognizer.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/recognition/opencv-dnn/facerec_dnnborrowed.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/recognition/opencv-dnn/dnnfaceextractor.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/recognition/opencv-dnn/dnndbscan.cpp ) else() include_directories(${CMAKE_CURRENT_SOURCE_DIR}/recognition/opencv-face ${CMAKE_CURRENT_SOURCE_DIR}/recognition/opencv-lbph ) set(facesengine_database_LIB_SRCS ${facesengine_database_LIB_SRCS} # Lpbh Faces recognition module based on OpenCV - recognition/opencv-face/opencv_face.cpp - facedb/facedb_lbph.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/recognition/opencv-face/opencv_face.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/facedb/facedb_lbph.cpp - recognition/opencv-lbph/lbphfacemodel.cpp - recognition/opencv-lbph/opencvlbphfacerecognizer.cpp - recognition/opencv-lbph/facerec_borrowed.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/recognition/opencv-lbph/lbphfacemodel.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/recognition/opencv-lbph/opencvlbphfacerecognizer.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/recognition/opencv-lbph/facerec_borrowed.cpp ) endif() # Used by digikamgui add_library(gui_digikamfacesenginedatabase_obj OBJECT ${facesengine_database_LIB_SRCS} ) target_compile_definitions(gui_digikamfacesenginedatabase_obj PRIVATE digikamgui_EXPORTS ) # For unit-tests add_library(digikamfacesenginedatabase STATIC $) diff --git a/core/libs/fileactionmanager/CMakeLists.txt b/core/libs/fileactionmanager/CMakeLists.txt index b19af463d1..da0faf0c90 100644 --- a/core/libs/fileactionmanager/CMakeLists.txt +++ b/core/libs/fileactionmanager/CMakeLists.txt @@ -1,43 +1,43 @@ # # Copyright (c) 2010-2020 by Gilles Caulier, # Copyright (c) 2015 by Veaceslav Munteanu, # # Redistribution and use is allowed according to the terms of the BSD license. # For details see the accompanying COPYING-CMAKE-SCRIPTS file. APPLY_COMMON_POLICIES() set(fileactionmanager_SRCS - metadatahub.cpp - metadatahubmngr.cpp - metadatastatusbar.cpp - fileactionprogress.cpp - fileactionmngr.cpp - fileactionmngr_p.cpp - iteminfotasksplitter.cpp - databaseworkeriface.cpp - fileworkeriface.cpp - fileactionimageinfolist.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/metadatahub.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/metadatahubmngr.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/metadatastatusbar.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/fileactionprogress.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/fileactionmngr.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/fileactionmngr_p.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/iteminfotasksplitter.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/databaseworkeriface.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/fileworkeriface.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/fileactionimageinfolist.cpp ) include_directories( $ $ $ $ $ $ ) if(ENABLE_DBUS) include_directories($) endif() # Used by digikamgui add_library(gui_fileactionmanager_obj OBJECT ${fileactionmanager_SRCS}) target_compile_definitions(gui_fileactionmanager_obj PRIVATE digikamgui_EXPORTS ) diff --git a/core/libs/fileactionmanager/databaseworkeriface.cpp b/core/libs/fileactionmanager/databaseworkeriface.cpp index c1269e8186..2b20218a31 100644 --- a/core/libs/fileactionmanager/databaseworkeriface.cpp +++ b/core/libs/fileactionmanager/databaseworkeriface.cpp @@ -1,383 +1,388 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2012-01-18 * Description : database worker interface * * Copyright (C) 2012 by Marcel Wiesweg * * 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, 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. * * ============================================================ */ #include "databaseworkeriface.h" // KDE includes #include // Local includes #include "digikam_debug.h" #include "collectionscanner.h" #include "coredboperationgroup.h" #include "iteminfotasksplitter.h" #include "fileactionmngr_p.h" #include "scancontroller.h" #include "disjointmetadata.h" namespace Digikam { void FileActionMngrDatabaseWorker::assignTags(FileActionItemInfoList infos, const QList& tagIDs) { changeTags(infos, tagIDs, true); } void FileActionMngrDatabaseWorker::removeTags(FileActionItemInfoList infos, const QList& tagIDs) { changeTags(infos, tagIDs, false); } void FileActionMngrDatabaseWorker::changeTags(FileActionItemInfoList infos, const QList& tagIDs, bool addOrRemove) { DisjointMetadata hub; QList forWriting; { CoreDbOperationGroup group; group.setMaximumTime(200); foreach (const ItemInfo& info, infos) { if (state() == WorkerObject::Deactivating) { break; } hub.load(info); for (QList::const_iterator tagIt = tagIDs.constBegin() ; tagIt != tagIDs.constEnd() ; ++tagIt) { if (addOrRemove) { hub.setTag(*tagIt, DisjointMetadata::MetadataAvailable); } else { hub.setTag(*tagIt, DisjointMetadata::MetadataInvalid); } } hub.write(info, DisjointMetadata::PartialWrite); if (hub.willWriteMetadata(DisjointMetadata::FullWriteIfChanged) && d->shallSendForWriting(info.id())) { forWriting << info; } infos.dbProcessedOne(); group.allowLift(); } } // send for writing file metadata if (!forWriting.isEmpty()) { FileActionItemInfoList forWritingTaskList = FileActionItemInfoList::continueTask(forWriting, infos.progress()); forWritingTaskList.schedulingForWrite(i18n("Writing metadata to files"), d->fileProgressCreator()); qCDebug(DIGIKAM_GENERAL_LOG) << "Scheduled to write"; for (ItemInfoTaskSplitter splitter(forWritingTaskList) ; splitter.hasNext() ; ) { emit writeMetadata(FileActionItemInfoList(splitter.next()), MetadataHub::WRITE_TAGS); } } infos.dbFinished(); } void FileActionMngrDatabaseWorker::assignPickLabel(FileActionItemInfoList infos, int pickId) { DisjointMetadata hub; QList forWriting; { CoreDbOperationGroup group; group.setMaximumTime(200); foreach (const ItemInfo& info, infos) { if (state() == WorkerObject::Deactivating) { break; } hub.load(info); hub.setPickLabel(pickId); hub.write(info, DisjointMetadata::PartialWrite); if (hub.willWriteMetadata(DisjointMetadata::FullWriteIfChanged) && d->shallSendForWriting(info.id())) { forWriting << info; } infos.dbProcessedOne(); group.allowLift(); } } // send for writing file metadata if (!forWriting.isEmpty()) { FileActionItemInfoList forWritingTaskList = FileActionItemInfoList::continueTask(forWriting, infos.progress()); forWritingTaskList.schedulingForWrite(i18n("Writing metadata to files"), d->fileProgressCreator()); for (ItemInfoTaskSplitter splitter(forWritingTaskList) ; splitter.hasNext() ; ) { emit writeMetadata(FileActionItemInfoList(splitter.next()), MetadataHub::WRITE_PICKLABEL); } } infos.dbFinished(); } void FileActionMngrDatabaseWorker::assignColorLabel(FileActionItemInfoList infos, int colorId) { DisjointMetadata hub; QList forWriting; { CoreDbOperationGroup group; group.setMaximumTime(200); foreach (const ItemInfo& info, infos) { if (state() == WorkerObject::Deactivating) { break; } hub.load(info); hub.setColorLabel(colorId); hub.write(info, DisjointMetadata::PartialWrite); if (hub.willWriteMetadata(DisjointMetadata::FullWriteIfChanged) && d->shallSendForWriting(info.id())) { forWriting << info; } infos.dbProcessedOne(); group.allowLift(); } } // send for writing file metadata if (!forWriting.isEmpty()) { FileActionItemInfoList forWritingTaskList = FileActionItemInfoList::continueTask(forWriting, infos.progress()); forWritingTaskList.schedulingForWrite(i18n("Writing metadata to files"), d->fileProgressCreator()); for (ItemInfoTaskSplitter splitter(forWritingTaskList) ; splitter.hasNext() ; ) { emit writeMetadata(FileActionItemInfoList(splitter.next()), MetadataHub::WRITE_COLORLABEL); } } infos.dbFinished(); } void FileActionMngrDatabaseWorker::assignRating(FileActionItemInfoList infos, int rating) { DisjointMetadata hub; QList forWriting; rating = qMin(RatingMax, qMax(RatingMin, rating)); { CoreDbOperationGroup group; group.setMaximumTime(200); foreach (const ItemInfo& info, infos) { if (state() == WorkerObject::Deactivating) { break; } hub.load(info); hub.setRating(rating); hub.write(info, DisjointMetadata::PartialWrite); if (hub.willWriteMetadata(DisjointMetadata::FullWriteIfChanged) && d->shallSendForWriting(info.id())) { forWriting << info; } infos.dbProcessedOne(); group.allowLift(); } } // send for writing file metadata if (!forWriting.isEmpty()) { FileActionItemInfoList forWritingTaskList = FileActionItemInfoList::continueTask(forWriting, infos.progress()); forWritingTaskList.schedulingForWrite(i18n("Writing metadata to files"), d->fileProgressCreator()); for (ItemInfoTaskSplitter splitter(forWritingTaskList) ; splitter.hasNext() ; ) { emit writeMetadata(FileActionItemInfoList(splitter.next()), MetadataHub::WRITE_RATING); } } infos.dbFinished(); } void FileActionMngrDatabaseWorker::editGroup(int groupAction, const ItemInfo& pick, FileActionItemInfoList infos) { { CoreDbOperationGroup group; group.setMaximumTime(200); foreach (const ItemInfo& constInfo, infos) { if (state() == WorkerObject::Deactivating) { break; } ItemInfo info(constInfo); switch (groupAction) { case AddToGroup: info.addToGroup(pick); break; case RemoveFromGroup: info.removeFromGroup(); break; case Ungroup: info.clearGroup(); break; } infos.dbProcessedOne(); group.allowLift(); } } infos.dbFinished(); } void FileActionMngrDatabaseWorker::setExifOrientation(FileActionItemInfoList infos, int orientation) { { CoreDbOperationGroup group; group.setMaximumTime(200); foreach (ItemInfo info, infos) { if (state() == WorkerObject::Deactivating) { break; } + MetadataHub hub; + hub.adjustFaceRectangles(info, false, + orientation, + info.orientation()); + info.setOrientation(orientation); } } infos.dbProcessed(infos.count()); infos.schedulingForWrite(infos.count(), i18n("Revising Exif Orientation tags"), d->fileProgressCreator()); for (ItemInfoTaskSplitter splitter(infos) ; splitter.hasNext() ; ) { emit writeOrientationToFiles(FileActionItemInfoList(splitter.next()), orientation); } infos.dbFinished(); } void FileActionMngrDatabaseWorker::applyMetadata(FileActionItemInfoList infos, DisjointMetadata *hub) { { CoreDbOperationGroup group; group.setMaximumTime(200); foreach (const ItemInfo& info, infos) { if (state() == WorkerObject::Deactivating) { break; } // apply to database hub->write(info); infos.dbProcessedOne(); group.allowLift(); } } if (hub->willWriteMetadata(DisjointMetadata::FullWriteIfChanged), Qt::DirectConnection) { int flags = hub->changedFlags(); // don't filter by shallSendForWriting here; we write from the hub, not from freshly loaded data infos.schedulingForWrite(infos.size(), i18n("Writing metadata to files"), d->fileProgressCreator()); for (ItemInfoTaskSplitter splitter(infos) ; splitter.hasNext() ; ) { emit writeMetadata(FileActionItemInfoList(splitter.next()), flags); } } delete hub; infos.dbFinished(); } void FileActionMngrDatabaseWorker::copyAttributes(FileActionItemInfoList infos, const QStringList& derivedPaths) { if (infos.size() == 1) { foreach (const QString& path, derivedPaths) { if (state() == WorkerObject::Deactivating) { break; } ItemInfo dest = ScanController::instance()->scannedInfo(path); CollectionScanner::copyFileProperties(infos.first(), dest); } infos.dbProcessedOne(); } infos.dbFinished(); } } // namespace Digikam diff --git a/core/libs/fileactionmanager/fileactionimageinfolist.cpp b/core/libs/fileactionmanager/fileactionimageinfolist.cpp index 964d0cf309..bda0041b30 100644 --- a/core/libs/fileactionmanager/fileactionimageinfolist.cpp +++ b/core/libs/fileactionmanager/fileactionimageinfolist.cpp @@ -1,150 +1,158 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2012-02-05 * Description : file action manager task list * * Copyright (C) 2012 by Marcel Wiesweg * * 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, 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. * * ============================================================ */ #include "fileactionimageinfolist.h" // Qt includes #include // Local includes #include "digikam_debug.h" #include "progressmanager.h" namespace Digikam { void TwoProgressItemsContainer::scheduleOnProgressItem(QAtomicPointer& ptr, int total, const QString& action, FileActionProgressItemCreator* const creator) { if (total <= 0) { return; } if (!ptr) { ProgressItem* const item = creator->createProgressItem(action); if (ptr.testAndSetOrdered(nullptr, item)) { creator->addProgressItem(item); } else { delete item; } } -#if QT_VERSION < QT_VERSION_CHECK(5, 14, 0) +#if (QT_VERSION < QT_VERSION_CHECK(5, 14, 0)) + ptr.load()->incTotalItems(total); + #else + ptr.loadRelaxed()->incTotalItems(total); + #endif } void TwoProgressItemsContainer::advance(QAtomicPointer& ptr, int n) { -#if QT_VERSION < QT_VERSION_CHECK(5, 14, 0) +#if (QT_VERSION < QT_VERSION_CHECK(5, 14, 0)) + if (ptr.load()->advance(n)) + #else + if (ptr.loadRelaxed()->advance(n)) + #endif { ProgressItem* const item = ptr; if (item && ptr.testAndSetOrdered(item, nullptr)) { item->setComplete(); } } } FileActionProgressItemContainer::FileActionProgressItemContainer() { } void FileActionProgressItemContainer::schedulingForDB(int numberOfInfos, const QString& action, FileActionProgressItemCreator* const creator) { scheduleOnProgressItem(firstItem, numberOfInfos, action, creator); } void FileActionProgressItemContainer::dbProcessed(int numberOfInfos) { advance(firstItem, numberOfInfos); } void FileActionProgressItemContainer::dbFinished() { /* checkFinish(firstItem); */ } void FileActionProgressItemContainer::schedulingForWrite(int numberOfInfos, const QString& action, FileActionProgressItemCreator* const creator) { scheduleOnProgressItem(secondItem, numberOfInfos, action, creator); connect(secondItem, SIGNAL(progressItemCompleted(ProgressItem*)), this, SIGNAL(signalWrittingDone())); } void FileActionProgressItemContainer::written(int numberOfInfos) { advance(secondItem, numberOfInfos); } void FileActionProgressItemContainer::finishedWriting() { /* checkFinish(secondItem); */ } FileActionItemInfoList FileActionItemInfoList::create(const QList& infos) { FileActionItemInfoList list; list = FileActionItemInfoList(infos); list.container = new FileActionProgressItemContainer; return list; } FileActionItemInfoList FileActionItemInfoList::continueTask(const QList& infos, FileActionProgressItemContainer* const container) { FileActionItemInfoList list; list = FileActionItemInfoList(infos); list.container = container; return list; } } // namespace Digikam diff --git a/core/libs/fileactionmanager/fileworkeriface.cpp b/core/libs/fileactionmanager/fileworkeriface.cpp index ceafce62a4..09ab9d9a6e 100644 --- a/core/libs/fileactionmanager/fileworkeriface.cpp +++ b/core/libs/fileactionmanager/fileworkeriface.cpp @@ -1,415 +1,331 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2012-01-18 * Description : database worker interface * * Copyright (C) 2012 by Marcel Wiesweg * * 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, 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. * * ============================================================ */ #include "fileworkeriface.h" // KDE includes #include // Local includes #include "digikam_debug.h" #include "digikam_globals.h" #include "fileactionmngr_p.h" #include "metaenginesettings.h" #include "itemattributeswatch.h" #include "iteminfotasksplitter.h" #include "collectionscanner.h" -#include "facetagseditor.h" #include "scancontroller.h" #include "jpegutils.h" #include "dimg.h" namespace Digikam { void FileActionMngrFileWorker::writeOrientationToFiles(FileActionItemInfoList infos, int orientation) { QStringList failedItems; foreach (const ItemInfo& info, infos) { if (state() == WorkerObject::Deactivating) { break; } QString path = info.filePath(); DMetadata metadata(path); DMetadata::ImageOrientation o = (DMetadata::ImageOrientation)orientation; metadata.setItemOrientation(o); if (!metadata.applyChanges()) { failedItems.append(info.name()); } else { emit imageDataChanged(path, true, true); QUrl url = QUrl::fromLocalFile(path); ItemAttributesWatch::instance()->fileMetadataChanged(url); } infos.writtenToOne(); } if (!failedItems.isEmpty()) { emit imageChangeFailed(i18n("Failed to revise Exif orientation these files:"), failedItems); } infos.finishedWriting(); } void FileActionMngrFileWorker::writeMetadataToFiles(FileActionItemInfoList infos) { d->startingToWrite(infos); ScanController::instance()->suspendCollectionScan(); foreach (const ItemInfo& info, infos) { MetadataHub hub; if (state() == WorkerObject::Deactivating) { break; } hub.load(info); QString path = info.filePath(); if (MetaEngineSettings::instance()->settings().useLazySync) { hub.write(path, MetadataHub::WRITE_ALL); } else { ScanController::FileMetadataWrite writeScope(info); writeScope.changed(hub.write(path, MetadataHub::WRITE_ALL)); } // hub emits fileMetadataChanged infos.writtenToOne(); } ScanController::instance()->resumeCollectionScan(); infos.finishedWriting(); } void FileActionMngrFileWorker::writeMetadata(FileActionItemInfoList infos, int flags) { d->startingToWrite(infos); ScanController::instance()->suspendCollectionScan(); foreach (const ItemInfo& info, infos) { MetadataHub hub; if (state() == WorkerObject::Deactivating) { break; } hub.load(info); // apply to file metadata if (MetaEngineSettings::instance()->settings().useLazySync) { hub.writeToMetadata(info, (MetadataHub::WriteComponents)flags); } else { ScanController::FileMetadataWrite writeScope(info); writeScope.changed(hub.writeToMetadata(info, (MetadataHub::WriteComponents)flags)); } // hub emits fileMetadataChanged infos.writtenToOne(); } ScanController::instance()->resumeCollectionScan(); infos.finishedWriting(); } void FileActionMngrFileWorker::transform(FileActionItemInfoList infos, int action) { d->startingToWrite(infos); QStringList failedItems; ScanController::instance()->suspendCollectionScan(); foreach (const ItemInfo& info, infos) { if (state() == WorkerObject::Deactivating) { break; } QString path = info.filePath(); QString format = info.format(); MetaEngine::ImageOrientation currentOrientation = (MetaEngine::ImageOrientation)info.orientation(); bool isRaw = info.format().startsWith(QLatin1String("RAW")); bool rotateAsJpeg = false; bool rotateLossy = false; MetaEngineSettingsContainer::RotationBehaviorFlags behavior; behavior = MetaEngineSettings::instance()->settings().rotationBehavior; bool rotateByMetadata = (behavior & MetaEngineSettingsContainer::RotateByMetadataFlag); // Check if rotation by content, as desired, is feasible // We'll later check again if it was successful if (behavior & MetaEngineSettingsContainer::RotatingPixels) { if (format == QLatin1String("JPG") && JPEGUtils::isJpegImage(path)) { rotateAsJpeg = true; } if (behavior & MetaEngineSettingsContainer::RotateByLossyRotation) { DImg::FORMAT frmt = DImg::fileFormat(path); switch (frmt) { case DImg::JPEG: case DImg::PNG: case DImg::TIFF: case DImg::JP2K: case DImg::PGF: case DImg::HEIF: rotateLossy = true; default: break; } } } MetaEngineRotation matrix; matrix *= currentOrientation; matrix *= (MetaEngineRotation::TransformationAction)action; MetaEngine::ImageOrientation finalOrientation = matrix.exifOrientation(); bool rotatedPixels = false; if (rotateAsJpeg) { JPEGUtils::JpegRotator rotator(path); rotator.setCurrentOrientation(currentOrientation); if (action == MetaEngineRotation::NoTransformation) { rotatedPixels = rotator.autoExifTransform(); } else { rotatedPixels = rotator.exifTransform((MetaEngineRotation::TransformationAction)action); } if (!rotatedPixels) { failedItems.append(info.name()); } } else if (rotateLossy) { // Non-JPEG image: DImg DImg image; if (!image.load(path)) { failedItems.append(info.name()); } else { if (action == MetaEngineRotation::NoTransformation) { image.rotateAndFlip(currentOrientation); } else { image.transform(action); } // TODO: Atomic operation!! // prepare metadata, including to reset Exif tag image.prepareMetadataToSave(path, image.format(), true); if (image.save(path, image.detectedFormat())) { rotatedPixels = true; } else { failedItems.append(info.name()); } } } - adjustFaceRectangles(info, rotatedPixels, - finalOrientation, - currentOrientation); + int newOrientation = finalOrientation; if (rotatedPixels) { - CollectionScanner scanner; - scanner.scanFile(info, CollectionScanner::NormalScan); - - // reset for DB. Metadata is already edited. - finalOrientation = MetaEngine::ORIENTATION_NORMAL; } + // DB rotation + + ItemInfo(info).setOrientation(finalOrientation); + + // Adjust Faces + + MetadataHub hub; + hub.adjustFaceRectangles(info, rotatedPixels, + newOrientation, + currentOrientation); + if (rotateByMetadata) { // Setting the rotation flag on Raws with embedded JPEG is a mess // Can apply to the RAW data, or to the embedded JPEG, or to both. if (!isRaw) { DMetadata metadata(path); metadata.setItemOrientation(finalOrientation); metadata.applyChanges(); } } + CollectionScanner scanner; + scanner.scanFile(info, CollectionScanner::NormalScan); + if (!failedItems.contains(info.name())) { emit imageDataChanged(path, true, true); ItemAttributesWatch::instance()->fileMetadataChanged(info.fileUrl()); } - // DB rotation - - ItemInfo(info).setOrientation(finalOrientation); - infos.writtenToOne(); } if (!failedItems.isEmpty()) { emit imageChangeFailed(i18n("Failed to transform these files:"), failedItems); } infos.finishedWriting(); ScanController::instance()->resumeCollectionScan(); } -void FileActionMngrFileWorker::adjustFaceRectangles(const ItemInfo& info, bool rotatedPixels, - int newOrientation, - int oldOrientation) -{ - /** - * Get all faces from database and rotate them - */ - QList facesList = FaceTagsEditor().databaseFaces(info.id()); - - if (facesList.isEmpty()) - { - return; - } - - QSize newSize = info.dimensions(); - QMultiMap adjustedFaces; - - foreach (const FaceTagsIface& dface, facesList) - { - QRect faceRect = dface.region().toRect(); - QString name = FaceTags::faceNameForTag(dface.tagId()); - - TagRegion::reverseToOrientation(faceRect, - oldOrientation, - info.dimensions()); - - newSize = TagRegion::adjustToOrientation(faceRect, - newOrientation, - info.dimensions()); - - if (dface.tagId() == FaceTags::unknownPersonTagId()) - { - name.clear(); - } - - adjustedFaces.insertMulti(name, faceRect); - } - - /** - * Delete all old faces and add rotated ones - */ - FaceTagsEditor().removeAllFaces(info.id()); - - QMultiMap::ConstIterator it = adjustedFaces.constBegin(); - - for ( ; it != adjustedFaces.constEnd() ; ++it) - { - TagRegion region(it.value()); - - if (it.key().isEmpty()) - { - int tagId = FaceTags::unknownPersonTagId(); - FaceTagsIface face(FaceTagsIface::UnknownName, info.id(), tagId, region); - - FaceTagsEditor().addManually(face); - } - else - { - int tagId = FaceTags::getOrCreateTagForPerson(it.key()); - - if (!tagId) - { - qCDebug(DIGIKAM_GENERAL_LOG) << "Failed to create a person tag for name" << it.key(); - } - - FaceTagsEditor().add(info.id(), tagId, region, false); - } - } - - if (!rotatedPixels) - { - newSize = info.dimensions(); - } - - /** - * Write medatada - */ - MetadataHub hub; - hub.load(info); - - // Adjusted newSize - - hub.loadFaceTags(info, newSize); - hub.write(info.filePath(), MetadataHub::WRITE_ALL, true); -} - } // namespace Digikam diff --git a/core/libs/fileactionmanager/fileworkeriface.h b/core/libs/fileactionmanager/fileworkeriface.h index 097e316748..a796276e65 100644 --- a/core/libs/fileactionmanager/fileworkeriface.h +++ b/core/libs/fileactionmanager/fileworkeriface.h @@ -1,85 +1,82 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2012-01-18 * Description : file worker interface * * Copyright (C) 2012 by Marcel Wiesweg * * 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, 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. * * ============================================================ */ #ifndef DIGIKAM_FILE_WORKER_IFACE_H #define DIGIKAM_FILE_WORKER_IFACE_H // Local includes #include "fileactionmngr.h" #include "fileactionimageinfolist.h" #include "iteminfo.h" #include "workerobject.h" namespace Digikam { class MetadataHub; class FileWorkerInterface : public WorkerObject { Q_OBJECT public Q_SLOTS: virtual void writeOrientationToFiles(FileActionItemInfoList, int) {}; virtual void writeMetadataToFiles(FileActionItemInfoList) {}; virtual void writeMetadata(FileActionItemInfoList, int) {}; virtual void transform(FileActionItemInfoList, int) {}; Q_SIGNALS: void imageDataChanged(const QString& path, bool removeThumbnails, bool notifyCache); void imageChangeFailed(const QString& message, const QStringList& fileNames); }; // --------------------------------------------------------------------------------------------- class FileActionMngrFileWorker : public FileWorkerInterface { public: explicit FileActionMngrFileWorker(FileActionMngr::Private* const d) : d(d) { } public: void writeOrientationToFiles(FileActionItemInfoList infos, int orientation) override; void writeMetadataToFiles(FileActionItemInfoList infos) override; void writeMetadata(FileActionItemInfoList infos, int flags) override; void transform(FileActionItemInfoList infos, int orientation) override; - void adjustFaceRectangles(const ItemInfo& info, bool rotatedPixels, - int newOrientation, - int oldOrientation); private: FileActionMngr::Private* const d; }; } // namespace Digikam #endif // DIGIKAM_FILE_WORKER_IFACE_H diff --git a/core/libs/fileactionmanager/metadatahub.cpp b/core/libs/fileactionmanager/metadatahub.cpp index a94df4ae02..3f44b1ec59 100644 --- a/core/libs/fileactionmanager/metadatahub.cpp +++ b/core/libs/fileactionmanager/metadatahub.cpp @@ -1,824 +1,909 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2007-01-05 * Description : Metadata handling * * Copyright (C) 2007-2012 by Marcel Wiesweg * Copyright (C) 2007-2020 by Gilles Caulier * Copyright (C) 2014-2015 by Veaceslav Munteanu * * 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, 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. * * ============================================================ */ #include "metadatahub.h" // Qt includes #include #include #include // Local includes #include "digikam_debug.h" #include "coredbaccess.h" #include "coredbwatch.h" #include "iteminfo.h" #include "itemcomments.h" #include "itemposition.h" #include "template.h" #include "templatemanager.h" #include "applicationsettings.h" #include "itemattributeswatch.h" #include "tagscache.h" #include "facetagseditor.h" #include "metadatahubmngr.h" #ifdef HAVE_KFILEMETADATA # include "baloowrap.h" #endif namespace Digikam { class Q_DECL_HIDDEN MetadataHub::Private { public: explicit Private() : pickLabel(-1), colorLabel(-1), rating(-1), count(0), dateTimeStatus(MetadataHub::MetadataInvalid), titlesStatus(MetadataHub::MetadataInvalid), commentsStatus(MetadataHub::MetadataInvalid), pickLabelStatus(MetadataHub::MetadataInvalid), colorLabelStatus(MetadataHub::MetadataInvalid), ratingStatus(MetadataHub::MetadataInvalid), templateStatus(MetadataHub::MetadataInvalid) { } public: int pickLabel; int colorLabel; int rating; int count; QDateTime dateTime; CaptionsMap titles; CaptionsMap comments; Template metadataTemplate; QMap tags; QStringList tagList; QMultiMap faceTagsList; ItemPosition itemPosition; MetadataHub::Status dateTimeStatus; MetadataHub::Status titlesStatus; MetadataHub::Status commentsStatus; MetadataHub::Status pickLabelStatus; MetadataHub::Status colorLabelStatus; MetadataHub::Status ratingStatus; MetadataHub::Status templateStatus; public: template void loadSingleValue(const T& data, T& storage, MetadataHub::Status& status); }; // ------------------------------------------------------------------------------------------ MetadataHub::MetadataHub() : d(new Private) { } MetadataHub::MetadataHub(const MetadataHub& other) : d(new Private(*other.d)) { } MetadataHub::~MetadataHub() { delete d; } MetadataHub& MetadataHub::operator=(const MetadataHub& other) { (*d) = (*other.d); return *this; } MetadataHub* MetadataHub::clone() const { return (new MetadataHub(*this)); } void MetadataHub::reset() { (*d) = Private(); } // -------------------------------------------------- void MetadataHub::load(const ItemInfo& info) { d->count++; //qCDebug(DIGIKAM_GENERAL_LOG) << "---------------------------------Load from ItemInfo ----------------"; CaptionsMap commentMap; CaptionsMap titleMap; { CoreDbAccess access; ItemComments comments = info.imageComments(access); commentMap = comments.toCaptionsMap(); titleMap = comments.toCaptionsMap(DatabaseComment::Title); } Template tref = info.metadataTemplate(); Template t = TemplateManager::defaultManager()->findByContents(tref); //qCDebug(DIGIKAM_GENERAL_LOG) << "Found Metadata Template: " << t.templateTitle(); load(info.dateTime(), titleMap, commentMap, info.colorLabel(), info.pickLabel(), info.rating(), t.isNull() ? tref : t); QList tagIds = info.tagIds(); loadTags(tagIds); loadFaceTags(info, info.dimensions()); d->itemPosition = info.imagePosition(); } // private common code to merge tags void MetadataHub::loadTags(const QList& loadedTags) { d->tags.clear(); foreach (int tagId, loadedTags) { if (TagsCache::instance()->isInternalTag(tagId)) { continue; } d->tags[tagId] = MetadataAvailable; } } // private common code to load dateTime, comment, color label, pick label, rating void MetadataHub::load(const QDateTime& dateTime, const CaptionsMap& titles, const CaptionsMap& comments, int colorLabel, int pickLabel, int rating, const Template& t) { if (dateTime.isValid()) { d->loadSingleValue(dateTime, d->dateTime, d->dateTimeStatus); } d->loadSingleValue(pickLabel, d->pickLabel, d->pickLabelStatus); d->loadSingleValue(colorLabel, d->colorLabel, d->colorLabelStatus); d->loadSingleValue(rating, d->rating, d->ratingStatus); d->loadSingleValue(titles, d->titles, d->titlesStatus); d->loadSingleValue(comments, d->comments, d->commentsStatus); d->loadSingleValue