diff --git a/NEWS b/NEWS
index 0b45fdab8e..ec3bdaafb6 100644
--- a/NEWS
+++ b/NEWS
@@ -1,553 +1,554 @@
digiKam 7.0.0-beta2 - Release date: 2020-01-26
*****************************************************************************************************
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 simlified and embeded into left side-bar tab.
SlideShow : Add new shuffle mode.
HTMLGallery : Add new theme "Html5Responsive".
General : Update internal libraw engine to last 201910 snapshot aka next 0.20.0 release (https://www.libraw.org/news/libraw-snapshot-201910)
*****************************************************************************************************
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 ==>
+538 ==> 416120 - Error transfering.
+539 ==>
diff --git a/core/data/geolocation/geoiface/backend-googlemaps.html b/core/data/geolocation/geoiface/backend-googlemaps.html
index 0fa1878ea9..1563b9d5b5 100644
--- a/core/data/geolocation/geoiface/backend-googlemaps.html
+++ b/core/data/geolocation/geoiface/backend-googlemaps.html
@@ -1,9 +1,9 @@
-
+
diff --git a/core/dplugins/generic/webservices/facebook/fbtalker.cpp b/core/dplugins/generic/webservices/facebook/fbtalker.cpp
index 855aa15b9c..22fb4f2586 100644
--- a/core/dplugins/generic/webservices/facebook/fbtalker.cpp
+++ b/core/dplugins/generic/webservices/facebook/fbtalker.cpp
@@ -1,860 +1,832 @@
/* ============================================================
*
* This file is a part of digiKam project
* https://www.digikam.org
*
* Date : 2008-12-26
* Description : a tool to export items to Facebook web service
*
* Copyright (C) 2008-2010 by Luka Renko
* Copyright (c) 2011 by Dirk Tilger
* Copyright (C) 2008-2020 by Gilles Caulier
* Copyright (C) 2018 by Thanh Trung Dinh
*
* 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 "fbtalker.h"
// Qt includes
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
-#include
#include
+// KDE includes
+
+#include
+
// Local includes
#include "digikam_debug.h"
-#include "digikam_config.h"
#include "digikam_version.h"
#include "fbmpform.h"
#include "wstoolutils.h"
-
-#ifdef HAVE_QWEBENGINE
-# include "webwidget_qwebengine.h"
-#else
-# include "webwidget.h"
-#endif
+#include "webbrowserdlg.h"
using namespace Digikam;
namespace DigikamGenericFaceBookPlugin
{
bool operator< (const FbUser& first, const FbUser& second)
{
return first.name < second.name;
}
bool operator< (const FbAlbum& first, const FbAlbum& second)
{
return first.title < second.title;
}
// -----------------------------------------------------------------------------
class Q_DECL_HIDDEN FbTalker::Private
{
public:
enum State
{
FB_GETLOGGEDINUSER = 0,
FB_LOGOUTUSER,
FB_LISTALBUMS,
FB_CREATEALBUM,
FB_ADDPHOTO
};
public:
explicit Private()
- {
- apiURL = QLatin1String("https://graph.facebook.com/%1/%2");
- authUrl = QLatin1String("https://www.facebook.com/dialog/oauth");
- tokenUrl = QLatin1String("https://graph.facebook.com/oauth/access_token");
- redirectUrl = QLatin1String("https://www.facebook.com/connect/login_success.html");
- scope = QLatin1String("user_photos,publish_pages,manage_pages"); //publish_to_groups,user_friends not necessary?
- apikey = QLatin1String("400589753481372");
- clientSecret = QLatin1String("5b0b5cd096e110cd4f4c72f517e2c544");
-
- serviceName = QLatin1String("Facebook");
- serviceTime = QLatin1String("token_time");
- serviceKey = QLatin1String("access_token");
-
- dialog = nullptr;
- parent = nullptr;
- settings = nullptr;
- netMngr = nullptr;
- reply = nullptr;
- view = nullptr;
- state = FB_GETLOGGEDINUSER;
+ : dialog(nullptr),
+ parent(nullptr),
+ settings(nullptr),
+ netMngr(nullptr),
+ reply(nullptr),
+ browser(nullptr),
+ state(FB_GETLOGGEDINUSER)
+ {
+ apiURL = QLatin1String("https://graph.facebook.com/%1/%2");
+ authUrl = QLatin1String("https://www.facebook.com/dialog/oauth");
+ tokenUrl = QLatin1String("https://graph.facebook.com/oauth/access_token");
+ redirectUrl = QLatin1String("https://www.facebook.com/connect/login_success.html");
+ scope = QLatin1String("user_photos,publish_pages,manage_pages"); //publish_to_groups,user_friends not necessary?
+ apikey = QLatin1String("400589753481372");
+ clientSecret = QLatin1String("5b0b5cd096e110cd4f4c72f517e2c544");
+
+ serviceName = QLatin1String("Facebook");
+ serviceTime = QLatin1String("token_time");
+ serviceKey = QLatin1String("access_token");
}
QString apiURL;
QString authUrl;
QString tokenUrl;
QString redirectUrl;
QString scope;
QString apikey;
QString clientSecret;
QString accessToken;
QString serviceName;
QString serviceTime;
QString serviceKey;
QDateTime expiryTime;
QDialog* dialog;
QWidget* parent;
QSettings* settings;
QNetworkAccessManager* netMngr;
QNetworkReply* reply;
- WebWidget* view;
+ WebBrowserDlg* browser;
State state;
FbUser user;
};
// -----------------------------------------------------------------------------
FbTalker::FbTalker(QWidget* const parent)
: d(new Private())
{
d->parent = parent;
d->netMngr = new QNetworkAccessManager(this);
-
d->settings = WSToolUtils::getOauthSettings(this);
connect(this, SIGNAL(linkingSucceeded()),
this, SLOT(slotLinkingSucceeded()));
connect(d->netMngr, SIGNAL(finished(QNetworkReply*)),
this, SLOT(slotFinished(QNetworkReply*)));
}
FbTalker::~FbTalker()
{
if (d->reply)
{
d->reply->abort();
}
- clearCookies();
-
delete d;
}
// ----------------------------------------------------------------------------------------------
void FbTalker::link()
{
emit signalBusy(true);
emit signalLoginProgress(1, 3);
QUrl url(d->authUrl);
QUrlQuery query(url);
query.addQueryItem(QLatin1String("client_id"), d->apikey);
query.addQueryItem(QLatin1String("response_type"), QLatin1String("token"));
query.addQueryItem(QLatin1String("redirect_uri"), d->redirectUrl);
query.addQueryItem(QLatin1String("scope"), d->scope);
url.setQuery(query);
- if (!d->view)
- {
- d->view = new WebWidget(d->parent);
- d->view->setWindowFlags(Qt::Dialog);
- d->view->resize(800, 600);
+ delete d->browser;
+ d->browser = new WebBrowserDlg(url, d->parent, true);
+ d->browser->setModal(true);
- connect(d->view, SIGNAL(urlChanged(QUrl)),
- this, SLOT(slotCatchUrl(QUrl)));
+ connect(d->browser, SIGNAL(urlChanged(QUrl)),
+ this, SLOT(slotCatchUrl(QUrl)));
- connect(d->view, SIGNAL(closeView(bool)),
- this, SIGNAL(signalBusy(bool)));
- }
+ connect(d->browser, SIGNAL(closeView(bool)),
+ this, SIGNAL(signalBusy(bool)));
- d->view->load(url);
- d->view->show();
+ d->browser->show();
}
void FbTalker::unlink()
{
d->accessToken = QString();
d->user = FbUser();
d->settings->beginGroup(d->serviceName);
d->settings->remove(QString());
d->settings->endGroup();
- clearCookies();
-
emit linkingSucceeded();
}
void FbTalker::cancel()
{
if (d->reply)
{
d->reply->abort();
d->reply = nullptr;
}
emit signalBusy(false);
}
void FbTalker::slotLinkingSucceeded()
{
if (d->accessToken.isEmpty())
{
qCDebug(DIGIKAM_WEBSERVICES_LOG) << "UNLINK to Facebook";
emit signalBusy(false);
return;
}
qCDebug(DIGIKAM_WEBSERVICES_LOG) << "LINK to Facebook";
- if (d->view)
+ if (d->browser)
{
- d->view->close();
+ d->browser->close();
}
getLoggedInUser();
}
void FbTalker::slotCatchUrl(const QUrl& url)
{
qCDebug(DIGIKAM_WEBSERVICES_LOG) << "Received URL from webview:" << url;
QString str = url.toString();
QUrlQuery query(str.section(QLatin1Char('#'), -1, -1));
if (query.hasQueryItem(QLatin1String("access_token")))
{
d->accessToken = query.queryItemValue(QLatin1String("access_token"));
int seconds = query.queryItemValue(QLatin1String("expires_in")).toInt();
d->expiryTime = QDateTime::currentDateTime().addSecs(seconds);
writeSettings();
qDebug(DIGIKAM_WEBSERVICES_LOG) << "Access token received";
emit linkingSucceeded();
}
else
{
qCDebug(DIGIKAM_WEBSERVICES_LOG) << "No access token in URL";
emit signalBusy(false);
}
}
FbUser FbTalker::getUser() const
{
return d->user;
}
bool FbTalker::linked()
{
return (!d->accessToken.isEmpty());
}
-void FbTalker::clearCookies()
-{
-#ifdef HAVE_QWEBENGINE
- if (d->view)
- {
- d->view->page()->profile()->cookieStore()->deleteAllCookies();
- }
-#else
- if (d->view)
- {
- d->view->page()->networkAccessManager()->setCookieJar(new QNetworkCookieJar());
- }
-#endif
-}
-
void FbTalker::getLoggedInUser()
{
qCDebug(DIGIKAM_WEBSERVICES_LOG) << "getLoggedInUser called";
if (d->reply)
{
d->reply->abort();
d->reply = nullptr;
}
emit signalBusy(true);
emit signalLoginProgress(2, 3);
QUrl url(d->apiURL.arg(QLatin1String("me")).arg(QString()));
QUrlQuery q;
q.addQueryItem(QLatin1String("access_token"), d->accessToken);
url.setQuery(q);
QNetworkRequest netRequest(url);
netRequest.setHeader(QNetworkRequest::ContentTypeHeader,
QLatin1String("application/x-www-form-urlencoded"));
d->reply = d->netMngr->get(netRequest);
d->state = Private::FB_GETLOGGEDINUSER;
}
// ----------------------------------------------------------------------------------------------
void FbTalker::logout()
{
qCDebug(DIGIKAM_WEBSERVICES_LOG) << "logout called";
if (d->reply)
{
d->reply->abort();
d->reply = nullptr;
}
emit signalBusy(true);
QUrl url(QLatin1String("https://www.facebook.com/logout.php"));
QUrlQuery q;
q.addQueryItem(QLatin1String("next"), d->redirectUrl);
q.addQueryItem(QLatin1String("access_token"), d->accessToken);
url.setQuery(q);
QNetworkRequest netRequest(url);
netRequest.setHeader(QNetworkRequest::ContentTypeHeader,
QLatin1String("application/x-www-form-urlencoded"));
d->reply = d->netMngr->get(netRequest);
d->state = Private::FB_LOGOUTUSER;
}
//----------------------------------------------------------------------------------------------------
void FbTalker::listAlbums(long long userID)
{
qCDebug(DIGIKAM_WEBSERVICES_LOG) << "Requesting albums for user" << userID;
if (d->reply)
{
d->reply->abort();
d->reply = nullptr;
}
emit signalBusy(true);
emit signalLoginProgress(1, 3);
QUrl url;
/*
* If userID is specified, load albums of that user,
* else load albums of current user
*/
if (!userID)
{
url = QUrl(d->apiURL.arg(d->user.id)
.arg(QLatin1String("albums")));
}
else
{
url = QUrl(d->apiURL.arg(userID)
.arg(QLatin1String("albums")));
}
QUrlQuery q;
q.addQueryItem(QLatin1String("fields"),
QLatin1String("id,name,description,privacy,link,location"));
q.addQueryItem(QLatin1String("access_token"), d->accessToken);
url.setQuery(q);
QNetworkRequest netRequest(url);
netRequest.setHeader(QNetworkRequest::ContentTypeHeader,
QLatin1String("application/x-www-form-urlencoded"));
d->reply = d->netMngr->get(netRequest);
d->state = Private::FB_LISTALBUMS;
}
void FbTalker::createAlbum(const FbAlbum& album)
{
if (d->reply)
{
d->reply->abort();
d->reply = nullptr;
}
emit signalBusy(true);
QUrlQuery params;
params.addQueryItem(QLatin1String("access_token"), d->accessToken);
params.addQueryItem(QLatin1String("name"), album.title);
if (!album.location.isEmpty())
params.addQueryItem(QLatin1String("location"), album.location);
/*
* description is deprecated and now a param of message
*/
if (!album.description.isEmpty())
params.addQueryItem(QLatin1String("message"), album.description);
// TODO (Dirk): Wasn't that a requested feature in Bugzilla?
switch (album.privacy)
{
case FB_ME:
params.addQueryItem(QLatin1String("privacy"), QLatin1String("{'value':'SELF'}"));
break;
case FB_FRIENDS:
params.addQueryItem(QLatin1String("privacy"), QLatin1String("{'value':'ALL_FRIENDS'}"));
break;
case FB_FRIENDS_OF_FRIENDS:
params.addQueryItem(QLatin1String("privacy"), QLatin1String("{'value':'FRIENDS_OF_FRIENDS'}"));
break;
case FB_EVERYONE:
params.addQueryItem(QLatin1String("privacy"), QLatin1String("{'value':'EVERYONE'}"));
break;
case FB_CUSTOM:
//TODO
params.addQueryItem(QLatin1String("privacy"), QLatin1String("{'value':'CUSTOM'}"));
break;
}
QUrl url(QUrl(d->apiURL.arg(d->user.id)
.arg(QLatin1String("albums"))));
url.setQuery(params);
QNetworkRequest netRequest(url);
netRequest.setHeader(QNetworkRequest::ContentTypeHeader,
QLatin1String("application/x-www-form-urlencoded"));
qCDebug(DIGIKAM_WEBSERVICES_LOG) << "url to create new album" << netRequest.url() << params.query();
d->reply = d->netMngr->post(netRequest, params.query().toUtf8());
d->state = Private::FB_CREATEALBUM;
}
void FbTalker::addPhoto(const QString& imgPath, const QString& albumID, const QString& caption)
{
qCDebug(DIGIKAM_WEBSERVICES_LOG) << "Adding photo" << imgPath << "to album with id"
<< albumID << "using caption '" << caption << "'";
if (d->reply)
{
d->reply->abort();
d->reply = nullptr;
}
emit signalBusy(true);
QMap args;
args[QLatin1String("access_token")] = d->accessToken;
if (!caption.isEmpty())
args[QLatin1String("message")] = caption;
FbMPForm form;
for (QMap::const_iterator it = args.constBegin() ;
it != args.constEnd() ; ++it)
{
form.addPair(it.key(), it.value());
}
qCDebug(DIGIKAM_WEBSERVICES_LOG) << "FORM:" << endl << form.formData();
if (!form.addFile(QUrl::fromLocalFile(imgPath).fileName(), imgPath))
{
emit signalAddPhotoDone(666, i18n("Cannot open file"));
emit signalBusy(false);
return;
}
form.finish();
QVariant arg_1;
if (albumID.isEmpty())
{
arg_1 = d->user.id;
}
else
{
arg_1 = albumID;
}
QNetworkRequest netRequest(QUrl(d->apiURL.arg(arg_1.toString()).arg(QLatin1String("photos"))));
netRequest.setHeader(QNetworkRequest::ContentTypeHeader, form.contentType());
d->reply = d->netMngr->post(netRequest, form.formData());
d->state = Private::FB_ADDPHOTO;
}
//----------------------------------------------------------------------------------------------------
QString FbTalker::errorToText(int errCode, const QString &errMsg)
{
QString transError;
qCDebug(DIGIKAM_WEBSERVICES_LOG) << "errorToText:" << errCode << ":" << errMsg;
switch (errCode)
{
case 0:
transError = QLatin1String("");
break;
case 2:
transError = i18n("The service is not available at this time.");
break;
case 4:
transError = i18n("The application has reached the maximum number of requests allowed.");
break;
case 102:
transError = i18n("Invalid session key or session expired. Try to log in again.");
break;
case 120:
transError = i18n("Invalid album ID.");
break;
case 321:
transError = i18n("Album is full.");
break;
case 324:
transError = i18n("Missing or invalid file.");
break;
case 325:
transError = i18n("Too many unapproved photos pending.");
break;
default:
transError = errMsg;
break;
}
return transError;
}
void FbTalker::slotFinished(QNetworkReply* reply)
{
if (reply != d->reply)
{
return;
}
d->reply = nullptr;
if (reply->error() != QNetworkReply::NoError)
{
if (d->state == Private::FB_ADDPHOTO)
{
emit signalBusy(false);
emit signalAddPhotoDone(reply->error(), reply->errorString());
}
else
{
emit signalBusy(false);
QMessageBox::critical(QApplication::activeWindow(),
i18n("Error"), reply->errorString());
}
reply->deleteLater();
return;
}
QByteArray buffer = reply->readAll();
switch(d->state)
{
case (Private::FB_GETLOGGEDINUSER):
parseResponseGetLoggedInUser(buffer);
break;
case (Private::FB_LOGOUTUSER):
parseResponseLogoutUser();
break;
case (Private::FB_LISTALBUMS):
parseResponseListAlbums(buffer);
break;
case (Private::FB_CREATEALBUM):
parseResponseCreateAlbum(buffer);
break;
case (Private::FB_ADDPHOTO):
parseResponseAddPhoto(buffer);
break;
}
reply->deleteLater();
}
int FbTalker::parseErrorResponse(const QDomElement& e, QString& errMsg)
{
int errCode = -1;
for (QDomNode node = e.firstChild() ;
!node.isNull() ; node = node.nextSibling())
{
if (!node.isElement())
continue;
if (node.nodeName() == QLatin1String("error_code"))
{
errCode = node.toElement().text().toInt();
qCDebug(DIGIKAM_WEBSERVICES_LOG) << "Error Code:" << errCode;
}
else if (node.nodeName() == QLatin1String("error_msg"))
{
errMsg = node.toElement().text();
qCDebug(DIGIKAM_WEBSERVICES_LOG) << "Error Text:" << errMsg;
}
}
return errCode;
}
void FbTalker::parseResponseGetLoggedInUser(const QByteArray& data)
{
QString errMsg;
QJsonParseError err;
QJsonDocument doc = QJsonDocument::fromJson(data, &err);
qCDebug(DIGIKAM_WEBSERVICES_LOG) << "Logged in data" << doc;
if (err.error != QJsonParseError::NoError)
{
emit signalBusy(false);
return;
}
QJsonObject jsonObject = doc.object();
d->user.id = jsonObject[QLatin1String("id")].toString();
if (!(QString::compare(jsonObject[QLatin1String("id")].toString(), QLatin1String(""), Qt::CaseInsensitive) == 0))
{
qCDebug(DIGIKAM_WEBSERVICES_LOG) << "ID found in response of GetLoggedInUser";
}
d->user.name = jsonObject[QLatin1String("name")].toString();
d->user.profileURL = jsonObject[QLatin1String("link")].toString();
emit signalLoginDone(0, QString());
}
void FbTalker::parseResponseAddPhoto(const QByteArray& data)
{
qCDebug(DIGIKAM_WEBSERVICES_LOG) <<"Parse Add Photo data is" << data;
int errCode = -1;
QString errMsg;
QJsonParseError err;
QJsonDocument doc = QJsonDocument::fromJson(data, &err);
if (err.error != QJsonParseError::NoError)
{
emit signalBusy(false);
return;
}
QJsonObject jsonObject = doc.object();
if (jsonObject.contains(QLatin1String("id")))
{
qCDebug(DIGIKAM_WEBSERVICES_LOG) << "Id of photo exported is"
<< jsonObject[QLatin1String("id")].toString();
errCode = 0;
}
if (jsonObject.contains(QLatin1String("error")))
{
QJsonObject obj = jsonObject[QLatin1String("error")].toObject();
errCode = obj[QLatin1String("code")].toInt();
errMsg = obj[QLatin1String("message")].toString();
}
qCDebug(DIGIKAM_WEBSERVICES_LOG) << "add photo:" << doc;
emit signalBusy(false);
emit signalAddPhotoDone(errCode, errorToText(errCode, errMsg));
}
void FbTalker::parseResponseCreateAlbum(const QByteArray& data)
{
qCDebug(DIGIKAM_WEBSERVICES_LOG) <<"Parse Create album data is" << data;
int errCode = -1;
QString errMsg;
QString newAlbumID;
QJsonParseError err;
QJsonDocument doc = QJsonDocument::fromJson(data, &err);
if (err.error != QJsonParseError::NoError)
{
emit signalBusy(false);
return;
}
QJsonObject jsonObject = doc.object();
if (jsonObject.contains(QLatin1String("id")))
{
newAlbumID = jsonObject[QLatin1String("id")].toString();
qCDebug(DIGIKAM_WEBSERVICES_LOG) << "Id of album created is" << newAlbumID;
errCode = 0;
}
if (jsonObject.contains(QLatin1String("error")))
{
QJsonObject obj = jsonObject[QLatin1String("error")].toObject();
errCode = obj[QLatin1String("code")].toInt();
errMsg = obj[QLatin1String("message")].toString();
}
qCDebug(DIGIKAM_WEBSERVICES_LOG) << "error create photo:" << doc;
emit signalBusy(false);
emit signalCreateAlbumDone(errCode, errorToText(errCode, errMsg), newAlbumID);
}
void FbTalker::parseResponseListAlbums(const QByteArray& data)
{
int errCode = -1;
QString errMsg;
QJsonParseError err;
QList albumsList;
QJsonDocument doc = QJsonDocument::fromJson(data, &err);
if (err.error != QJsonParseError::NoError)
{
emit signalBusy(false);
return;
}
QJsonObject jsonObject = doc.object();
if (jsonObject.contains(QLatin1String("data")))
{
QJsonArray jsonArray = jsonObject[QLatin1String("data")].toArray();
foreach (const QJsonValue& value, jsonArray)
{
QJsonObject obj = value.toObject();
FbAlbum album;
album.id = obj[QLatin1String("id")].toString();
album.title = obj[QLatin1String("name")].toString();
album.location = obj[QLatin1String("location")].toString();
album.url = obj[QLatin1String("link")].toString();
album.description = obj[QLatin1String("description")].toString();
album.uploadable = obj[QLatin1String("can_upload")].toBool();
qCDebug(DIGIKAM_WEBSERVICES_LOG) << "can_upload " << album.uploadable;
if (QString::compare(obj[QLatin1String("privacy")].toString(),
QLatin1String("ALL_FRIENDS"), Qt::CaseInsensitive) == 0)
{
album.privacy = FB_FRIENDS;
}
else if (QString::compare(obj[QLatin1String("privacy")].toString(),
QLatin1String("FRIENDS_OF_FRIENDS"), Qt::CaseInsensitive) == 0)
{
album.privacy = FB_FRIENDS;
}
else if (QString::compare(obj[QLatin1String("privacy")].toString(),
QLatin1String("EVERYONE"), Qt::CaseInsensitive) == 0)
{
album.privacy = FB_EVERYONE;
}
else if (QString::compare(obj[QLatin1String("privacy")].toString(),
QLatin1String("CUSTOM"), Qt::CaseInsensitive) == 0)
{
album.privacy = FB_CUSTOM;
}
else if (QString::compare(obj[QLatin1String("privacy")].toString(),
QLatin1String("SELF"), Qt::CaseInsensitive) == 0)
{
album.privacy = FB_ME;
}
albumsList.append(album);
}
errCode = 0;
}
if (jsonObject.contains(QLatin1String("error")))
{
QJsonObject obj = jsonObject[QLatin1String("error")].toObject();
errCode = obj[QLatin1String("code")].toInt();
errMsg = obj[QLatin1String("message")].toString();
}
std::sort(albumsList.begin(), albumsList.end());
emit signalBusy(false);
emit signalListAlbumsDone(errCode, errorToText(errCode, errMsg), albumsList);
}
void FbTalker::writeSettings()
{
d->settings->beginGroup(d->serviceName);
d->settings->setValue(d->serviceTime, d->expiryTime);
d->settings->setValue(d->serviceKey, d->accessToken);
d->settings->endGroup();
}
void FbTalker::readSettings()
{
d->settings->beginGroup(d->serviceName);
d->expiryTime = d->settings->value(d->serviceTime).toDateTime();
d->accessToken = d->settings->value(d->serviceKey).toString();
d->settings->endGroup();
if (d->accessToken.isEmpty())
{
qCDebug(DIGIKAM_WEBSERVICES_LOG) << "Access token is empty";
emit signalLoginDone(-1, QString());
}
else if (QDateTime::currentDateTime() > d->expiryTime)
{
qCDebug(DIGIKAM_WEBSERVICES_LOG) << "Access token has expired";
d->accessToken = QString();
emit signalLoginDone(-1, QString());
}
else
{
qCDebug(DIGIKAM_WEBSERVICES_LOG) << "Already Linked";
emit linkingSucceeded();
}
}
void FbTalker::parseResponseLogoutUser()
{
unlink();
emit signalLoginDone(-1, QString());
}
} // namespace DigikamGenericFaceBookPlugin
diff --git a/core/dplugins/generic/webservices/facebook/fbtalker.h b/core/dplugins/generic/webservices/facebook/fbtalker.h
index 09100326ed..6ce32ae545 100644
--- a/core/dplugins/generic/webservices/facebook/fbtalker.h
+++ b/core/dplugins/generic/webservices/facebook/fbtalker.h
@@ -1,112 +1,111 @@
/* ============================================================
*
* This file is a part of digiKam project
* https://www.digikam.org
*
* Date : 2008-12-26
* Description : a tool to export items to Facebook web service
*
* Copyright (C) 2008-2009 by Luka Renko
* Copyright (C) 2008-2020 by Gilles Caulier
* Copyright (C) 2018 by Thanh Trung Dinh
*
* 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_FB_TALKER_H
#define DIGIKAM_FB_TALKER_H
// Qt includes
#include
#include
#include
#include
#include
#include
#include
// Local includes
#include "fbitem.h"
class QDomElement;
namespace DigikamGenericFaceBookPlugin
{
class FbTalker : public QObject
{
Q_OBJECT
public:
explicit FbTalker(QWidget* const parent);
~FbTalker();
void link();
void unlink();
bool linked();
void cancel();
FbUser getUser() const;
void logout();
void listAlbums(long long userID = 0);
void createAlbum(const FbAlbum& album);
void addPhoto(const QString& imgPath, const QString& albumID,
const QString& caption);
void readSettings();
void writeSettings();
Q_SIGNALS:
void signalBusy(bool val);
void signalListAlbumsDone(int errCode, const QString& errMsg, const QList & albumsList);
void signalCreateAlbumDone(int errCode, const QString& errMsg, const QString& newAlbumId);
void signalAddPhotoDone(int errCode, const QString& errMsg);
void signalLoginProgress(int step, int maxStep = 0, const QString& label = QString());
void signalLoginDone(int errCode, const QString& errMsg);
void linkingSucceeded();
private:
void getLoggedInUser();
- void clearCookies();
QString errorToText(int errCode, const QString& errMsg);
int parseErrorResponse(const QDomElement& e, QString& errMsg);
void parseResponseGetLoggedInUser(const QByteArray& data);
void parseResponseAddPhoto(const QByteArray& data);
void parseResponseCreateAlbum(const QByteArray& data);
void parseResponseListAlbums(const QByteArray& data);
void parseResponseLogoutUser();
private Q_SLOTS:
void slotLinkingSucceeded();
void slotCatchUrl(const QUrl& url);
void slotFinished(QNetworkReply* reply);
private:
class Private;
Private* const d;
};
} // namespace DigikamGenericFaceBookPlugin
#endif // DIGIKAM_FB_TALKER_H
diff --git a/core/dplugins/generic/webservices/flickr/flickrtalker.cpp b/core/dplugins/generic/webservices/flickr/flickrtalker.cpp
index 61379d9e9f..0bb2d65c82 100644
--- a/core/dplugins/generic/webservices/flickr/flickrtalker.cpp
+++ b/core/dplugins/generic/webservices/flickr/flickrtalker.cpp
@@ -1,1221 +1,1209 @@
/* ============================================================
*
* This file is a part of digiKam project
* https://www.digikam.org
*
* Date : 2005-07-07
* Description : a tool to export images to Flickr web service
*
* Copyright (C) 2005-2009 by Vardhman Jain
* Copyright (C) 2009-2020 by Gilles Caulier
* Copyright (C) 2017-2019 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 "flickrtalker.h"
// Qt includes
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
-#include
// KDE includes
#include
// Local includes
#include "dmetadata.h"
#include "wstoolutils.h"
#include "flickrmpform.h"
#include "flickrwindow.h"
+#include "webbrowserdlg.h"
#include "digikam_debug.h"
#include "digikam_config.h"
#include "digikam_version.h"
#include "previewloadthread.h"
-#ifdef HAVE_QWEBENGINE
-# include "webwidget_qwebengine.h"
-#else
-# include "webwidget.h"
-#endif
-
// OAuth2 library includes
#if defined(Q_CC_CLANG)
# pragma clang diagnostic push
# pragma clang diagnostic ignored "-Wextra-semi"
#endif
#include "o1.h"
#include "o0globals.h"
#include "o1requestor.h"
#include "o0settingsstore.h"
#if defined(Q_CC_CLANG)
# pragma clang diagnostic pop
#endif
namespace DigikamGenericFlickrPlugin
{
class Q_DECL_HIDDEN FlickrTalker::Private
{
public:
explicit Private()
+ : parent(nullptr),
+ netMngr(nullptr),
+ reply(nullptr),
+ settings(nullptr),
+ state(FE_LOGOUT),
+ iface(nullptr),
+ o1(nullptr),
+ store(nullptr),
+ requestor(nullptr),
+ browser(nullptr)
{
- parent = nullptr;
- netMngr = nullptr;
- reply = nullptr;
- settings = nullptr;
- state = FE_LOGOUT;
- iface = nullptr;
- o1 = nullptr;
- store = nullptr;
- requestor = nullptr;
- view = nullptr;
+ apiUrl = QLatin1String("https://www.flickr.com/services/rest/");
+ authUrl = QLatin1String("https://www.flickr.com/services/oauth/authorize?perms=write");
+ tokenUrl = QLatin1String("https://www.flickr.com/services/oauth/request_token");
+ accessUrl = QLatin1String("https://www.flickr.com/services/oauth/access_token");
+ uploadUrl = QLatin1String("https://up.flickr.com/services/upload/");
+ callbackUrl = QLatin1String("https://www.flickr.com");
+
+ apikey = QLatin1String("74f882bf4dabe22baaaace1f6d33c66b");
+ secret = QLatin1String("537d58e3ead2d6d5");
}
QWidget* parent;
QString serviceName;
QString apiUrl;
QString authUrl;
QString tokenUrl;
QString accessUrl;
QString uploadUrl;
QString callbackUrl;
QString apikey;
QString secret;
QString maxSize;
QString username;
QString userId;
QString lastTmpFile;
QNetworkAccessManager* netMngr;
QNetworkReply* reply;
QSettings* settings;
State state;
DInfoInterface* iface;
O1* o1;
O0SettingsStore* store;
O1Requestor* requestor;
- WebWidget* view;
+ WebBrowserDlg* browser;
};
FlickrTalker::FlickrTalker(QWidget* const parent,
const QString& serviceName,
DInfoInterface* const iface)
: d(new Private)
{
d->parent = parent;
d->serviceName = serviceName;
d->iface = iface;
m_photoSetsList = nullptr;
m_authProgressDlg = nullptr;
- d->apiUrl = QLatin1String("https://www.flickr.com/services/rest/");
- d->authUrl = QLatin1String("https://www.flickr.com/services/oauth/authorize?perms=write");
- d->tokenUrl = QLatin1String("https://www.flickr.com/services/oauth/request_token");
- d->accessUrl = QLatin1String("https://www.flickr.com/services/oauth/access_token");
- d->uploadUrl = QLatin1String("https://up.flickr.com/services/upload/");
- d->callbackUrl = QLatin1String("https://www.flickr.com");
-
- d->apikey = QLatin1String("74f882bf4dabe22baaaace1f6d33c66b");
- d->secret = QLatin1String("537d58e3ead2d6d5");
-
- d->netMngr = new QNetworkAccessManager(this);
+ d->netMngr = new QNetworkAccessManager(this);
connect(d->netMngr, SIGNAL(finished(QNetworkReply*)),
this, SLOT(slotFinished(QNetworkReply*)));
/* Initialize selected photo set as empty. */
m_selectedPhotoSet = FPhotoSet();
/* Initialize photo sets list. */
m_photoSetsList = new QLinkedList();
d->o1 = new O1(this);
d->o1->setLocalPort(80);
d->o1->setClientId(d->apikey);
d->o1->setClientSecret(d->secret);
d->o1->setCallbackUrl(d->callbackUrl);
d->o1->setAuthorizeUrl(QUrl(d->authUrl));
d->o1->setAccessTokenUrl(QUrl(d->accessUrl));
d->o1->setRequestTokenUrl(QUrl(d->tokenUrl));
d->o1->setUseExternalWebInterceptor(true);
d->settings = WSToolUtils::getOauthSettings(this);
d->store = new O0SettingsStore(d->settings, QLatin1String(O2_ENCRYPTION_KEY), this);
d->store->setGroupKey(d->serviceName);
d->o1->setStore(d->store);
connect(d->o1, SIGNAL(linkingFailed()),
this, SLOT(slotLinkingFailed()));
connect(d->o1, SIGNAL(linkingSucceeded()),
this, SLOT(slotLinkingSucceeded()));
connect(d->o1, SIGNAL(openBrowser(QUrl)),
this, SLOT(slotOpenBrowser(QUrl)));
d->requestor = new O1Requestor(d->netMngr, d->o1, this);
}
FlickrTalker::~FlickrTalker()
{
if (d->reply)
{
d->reply->abort();
}
WSToolUtils::removeTemporaryDir(d->serviceName.toLatin1().constData());
delete m_photoSetsList;
delete d;
}
void FlickrTalker::link(const QString& userName)
{
emit signalBusy(true);
if (userName.isEmpty())
{
d->store->setGroupKey(d->serviceName);
}
else
{
d->store->setGroupKey(d->serviceName + userName);
}
d->o1->link();
}
void FlickrTalker::unLink()
{
d->o1->unlink();
}
void FlickrTalker::removeUserName(const QString& userName)
{
if (userName.startsWith(d->serviceName))
{
d->settings->beginGroup(userName);
d->settings->remove(QString());
d->settings->endGroup();
}
}
void FlickrTalker::slotCatchUrl(const QUrl& url)
{
qCDebug(DIGIKAM_WEBSERVICES_LOG) << "Received URL from webview:" << url;
QString str = url.toString();
QUrlQuery query(str.section(QLatin1Char('?'), -1, -1));
if (query.hasQueryItem(QLatin1String("oauth_token")))
{
QMultiMap queryParams;
queryParams.insert(QLatin1String("oauth_token"),
query.queryItemValue(QLatin1String("oauth_token")));
queryParams.insert(QLatin1String("oauth_verifier"),
query.queryItemValue(QLatin1String("oauth_verifier")));
d->o1->onVerificationReceived(queryParams);
}
}
void FlickrTalker::slotLinkingFailed()
{
qCDebug(DIGIKAM_WEBSERVICES_LOG) << "LINK to Flickr fail";
d->username = QString();
emit signalBusy(false);
}
void FlickrTalker::slotLinkingSucceeded()
{
if (!d->o1->linked())
{
qCDebug(DIGIKAM_WEBSERVICES_LOG) << "UNLINK to Flickr ok";
d->username = QString();
return;
}
- if (d->view)
+ if (d->browser)
{
- d->view->close();
+ d->browser->close();
}
qCDebug(DIGIKAM_WEBSERVICES_LOG) << "LINK to Flickr ok";
d->username = d->o1->extraTokens()[QLatin1String("username")].toString();
d->userId = d->o1->extraTokens()[QLatin1String("user_nsid")].toString();
if (d->store->groupKey() == d->serviceName)
{
d->settings->beginGroup(d->serviceName);
QStringList keys = d->settings->allKeys();
d->settings->endGroup();
foreach (const QString& key, keys)
{
d->settings->beginGroup(d->serviceName);
QVariant value = d->settings->value(key);
d->settings->endGroup();
d->settings->beginGroup(d->serviceName + d->username);
d->settings->setValue(key, value);
d->settings->endGroup();
}
d->store->setGroupKey(d->serviceName + d->username);
removeUserName(d->serviceName);
}
emit signalLinkingSucceeded();
}
void FlickrTalker::slotOpenBrowser(const QUrl& url)
{
qCDebug(DIGIKAM_WEBSERVICES_LOG) << "Open Browser... (" << url << ")";
- delete d->view;
- d->view = new WebWidget(d->parent);
- d->view->resize(800, 600);
+ delete d->browser;
+ d->browser = new WebBrowserDlg(url, d->parent, true);
+ d->browser->setModal(true);
- connect(d->view, SIGNAL(urlChanged(QUrl)),
+ connect(d->browser, SIGNAL(urlChanged(QUrl)),
this, SLOT(slotCatchUrl(QUrl)));
-#ifdef HAVE_QWEBENGINE
- d->view->page()->profile()->cookieStore()->deleteAllCookies();
-#else
- d->view->page()->networkAccessManager()->setCookieJar(new QNetworkCookieJar());
-#endif
+ connect(d->browser, SIGNAL(closeView(bool)),
+ this, SIGNAL(signalBusy(bool)));
- d->view->setWindowFlags(Qt::Dialog);
- d->view->load(url);
- d->view->show();
+ d->browser->show();
}
QString FlickrTalker::getMaxAllowedFileSize()
{
return d->maxSize;
}
void FlickrTalker::maxAllowedFileSize()
{
if (d->reply)
{
d->reply->abort();
d->reply = nullptr;
}
if (!d->o1->linked())
return;
QUrl url(d->apiUrl);
QNetworkRequest netRequest(url);
QList reqParams = QList();
netRequest.setHeader(QNetworkRequest::ContentTypeHeader, QLatin1String(O2_MIME_TYPE_XFORM));
reqParams << O0RequestParameter("method", "flickr.people.getLimits");
QByteArray postData = O1::createQueryParameters(reqParams);
d->reply = d->requestor->post(netRequest, reqParams, postData);
d->state = FE_GETMAXSIZE;
m_authProgressDlg->setLabelText(i18n("Getting the maximum allowed file size."));
m_authProgressDlg->setMaximum(4);
m_authProgressDlg->setValue(1);
emit signalBusy(true);
}
void FlickrTalker::listPhotoSets()
{
if (d->reply)
{
d->reply->abort();
d->reply = nullptr;
}
if (!d->o1->linked())
return;
qCDebug(DIGIKAM_WEBSERVICES_LOG) << "List photoset invoked";
QUrl url(d->apiUrl);
QNetworkRequest netRequest(url);
QList reqParams = QList();
netRequest.setHeader(QNetworkRequest::ContentTypeHeader, QLatin1String(O2_MIME_TYPE_XFORM));
reqParams << O0RequestParameter("method", "flickr.photosets.getList");
QByteArray postData = O1::createQueryParameters(reqParams);
d->reply = d->requestor->post(netRequest, reqParams, postData);
d->state = FE_LISTPHOTOSETS;
emit signalBusy(true);
}
void FlickrTalker::getPhotoProperty(const QString& method, const QStringList& argList)
{
if (d->reply)
{
d->reply->abort();
d->reply = nullptr;
}
if (!d->o1->linked())
return;
QUrl url(d->apiUrl);
QNetworkRequest netRequest(url);
QList reqParams = QList();
netRequest.setHeader(QNetworkRequest::ContentTypeHeader, QLatin1String(O2_MIME_TYPE_XFORM));
reqParams << O0RequestParameter("method", method.toLatin1());
for (QStringList::const_iterator it = argList.constBegin(); it != argList.constEnd(); ++it)
{
QStringList str = (*it).split(QLatin1Char('='), QString::SkipEmptyParts);
reqParams << O0RequestParameter(str[0].toLatin1(), str[1].toLatin1());
}
QByteArray postData = O1::createQueryParameters(reqParams);
d->reply = d->requestor->post(netRequest, reqParams, postData);
d->state = FE_GETPHOTOPROPERTY;
emit signalBusy(true);
}
void FlickrTalker::listPhotos(const QString& /*albumName*/)
{
// TODO
}
void FlickrTalker::createPhotoSet(const QString& /*albumName*/, const QString& albumTitle,
const QString& albumDescription, const QString& primaryPhotoId)
{
if (d->reply)
{
d->reply->abort();
d->reply = nullptr;
}
if (!d->o1->linked())
return;
qCDebug(DIGIKAM_WEBSERVICES_LOG) << "Create photoset invoked";
QUrl url(d->apiUrl);
QNetworkRequest netRequest(url);
QList reqParams = QList();
netRequest.setHeader(QNetworkRequest::ContentTypeHeader, QLatin1String(O2_MIME_TYPE_XFORM));
reqParams << O0RequestParameter("method", "flickr.photosets.create");
reqParams << O0RequestParameter("title", albumTitle.toLatin1());
reqParams << O0RequestParameter("description", albumDescription.toLatin1());
reqParams << O0RequestParameter("primary_photo_id", primaryPhotoId.toLatin1());
QByteArray postData = O1::createQueryParameters(reqParams);
d->reply = d->requestor->post(netRequest, reqParams, postData);
d->state = FE_CREATEPHOTOSET;
emit signalBusy(true);
}
void FlickrTalker::addPhotoToPhotoSet(const QString& photoId,
const QString& photoSetId)
{
if (d->reply)
{
d->reply->abort();
d->reply = nullptr;
}
if (!d->o1->linked())
return;
qCDebug(DIGIKAM_WEBSERVICES_LOG) << "AddPhotoToPhotoSet invoked";
/* If the photoset id starts with the special string "UNDEFINED_", it means
* it doesn't exist yet on Flickr and needs to be created. Note that it's
* not necessary to subsequently add the photo to the photo set, as this
* is done in the set creation call to Flickr. */
if (photoSetId.startsWith(QLatin1String("UNDEFINED_")))
{
createPhotoSet(QLatin1String(""), m_selectedPhotoSet.title, m_selectedPhotoSet.description, photoId);
}
else
{
QUrl url(d->apiUrl);
QNetworkRequest netRequest(url);
QList reqParams = QList();
netRequest.setHeader(QNetworkRequest::ContentTypeHeader, QLatin1String(O2_MIME_TYPE_XFORM));
reqParams << O0RequestParameter("method", "flickr.photosets.addPhoto");
reqParams << O0RequestParameter("photoset_id", photoSetId.toLatin1());
reqParams << O0RequestParameter("photo_id", photoId.toLatin1());
QByteArray postData = O1::createQueryParameters(reqParams);
d->reply = d->requestor->post(netRequest, reqParams, postData);
d->state = FE_ADDPHOTOTOPHOTOSET;
emit signalBusy(true);
}
}
bool FlickrTalker::addPhoto(const QString& photoPath, const FPhotoInfo& info,
bool original, bool rescale, int maxDim, int imageQuality)
{
if (d->reply)
{
d->reply->abort();
d->reply = nullptr;
}
if (!d->o1->linked())
return false;
emit signalBusy(true);
QUrl url(d->uploadUrl);
QNetworkRequest netRequest(url);
QList reqParams = QList();
QString path = photoPath;
FlickrMPForm form;
QString ispublic = (info.is_public == 1) ? QLatin1String("1") : QLatin1String("0");
form.addPair(QLatin1String("is_public"), ispublic, QLatin1String("text/plain"));
reqParams << O0RequestParameter("is_public", ispublic.toLatin1());
QString isfamily = (info.is_family == 1) ? QLatin1String("1") : QLatin1String("0");
form.addPair(QLatin1String("is_family"), isfamily, QLatin1String("text/plain"));
reqParams << O0RequestParameter("is_family", isfamily.toLatin1());
QString isfriend = (info.is_friend == 1) ? QLatin1String("1") : QLatin1String("0");
form.addPair(QLatin1String("is_friend"), isfriend, QLatin1String("text/plain"));
reqParams << O0RequestParameter("is_friend", isfriend.toLatin1());
QString safetyLevel = QString::number(static_cast(info.safety_level));
form.addPair(QLatin1String("safety_level"), safetyLevel, QLatin1String("text/plain"));
reqParams << O0RequestParameter("safety_level", safetyLevel.toLatin1());
QString contentType = QString::number(static_cast(info.content_type));
form.addPair(QLatin1String("content_type"), contentType, QLatin1String("text/plain"));
reqParams << O0RequestParameter("content_type", contentType.toLatin1());
QString tags = QLatin1Char('"') + info.tags.join(QLatin1String("\" \"")) + QLatin1Char('"');
if (tags.length() > 0)
{
form.addPair(QLatin1String("tags"), tags, QLatin1String("text/plain"));
reqParams << O0RequestParameter("tags", tags.toUtf8());
}
if (!info.title.isEmpty())
{
form.addPair(QLatin1String("title"), info.title, QLatin1String("text/plain"));
reqParams << O0RequestParameter("title", info.title.toUtf8());
}
if (!info.description.isEmpty())
{
form.addPair(QLatin1String("description"), info.description, QLatin1String("text/plain"));
reqParams << O0RequestParameter("description", info.description.toUtf8());
}
if (!original)
{
QImage image = PreviewLoadThread::loadHighQualitySynchronously(photoPath).copyQImage();
if (image.isNull())
{
image.load(photoPath);
}
if (!image.isNull())
{
if (!d->lastTmpFile.isEmpty())
{
QFile::remove(d->lastTmpFile);
}
path = WSToolUtils::makeTemporaryDir(d->serviceName.toLatin1().constData()).filePath(QFileInfo(photoPath)
.baseName().trimmed() + QLatin1String(".jpg"));
if (rescale && (image.width() > maxDim || image.height() > maxDim))
{
image = image.scaled(maxDim, maxDim, Qt::KeepAspectRatio, Qt::SmoothTransformation);
}
image.save(path, "JPEG", imageQuality);
d->lastTmpFile = path;
// Restore all metadata.
DMetadata meta;
if (meta.load(photoPath))
{
meta.setItemDimensions(image.size());
meta.setItemOrientation(MetaEngine::ORIENTATION_NORMAL);
// NOTE: see bug #153207: Flickr use IPTC keywords to create Tags in web interface
// As IPTC do not support UTF-8, we need to remove it.
// This function call remove all Application2 Tags.
meta.removeIptcTags(QStringList() << QLatin1String("Application2"));
// NOTE: see bug # 384260: Flickr use Xmp.dc.subject to create Tags
// in web interface, we need to remove it.
// This function call remove all Dublin Core Tags.
meta.removeXmpTags(QStringList() << QLatin1String("dc"));
meta.setMetadataWritingMode((int)DMetadata::WRITE_TO_FILE_ONLY);
meta.save(path, true);
}
else
{
qCWarning(DIGIKAM_WEBSERVICES_LOG) << "Flickr::Image do not have metadata";
}
qCDebug(DIGIKAM_WEBSERVICES_LOG) << "Resizing and saving to temp file: " << path;
}
}
QFileInfo tempFileInfo(path);
qCDebug(DIGIKAM_WEBSERVICES_LOG) << "QUrl path is " << QUrl::fromLocalFile(path) << "Image size (in bytes) is "<< tempFileInfo.size();
if (tempFileInfo.size() > (getMaxAllowedFileSize().toLongLong()))
{
emit signalAddPhotoFailed(i18n("File Size exceeds maximum allowed file size."));
emit signalBusy(false);
return false;
}
if (!form.addFile(QLatin1String("photo"), path))
{
emit signalBusy(false);
return false;
}
form.finish();
netRequest.setHeader(QNetworkRequest::ContentTypeHeader, form.contentType());
d->reply = d->requestor->post(netRequest, reqParams, form.formData());
d->state = FE_ADDPHOTO;
return true;
}
void FlickrTalker::setGeoLocation(const QString& photoId, const QString& lat, const QString& lon)
{
if (d->reply)
{
d->reply->abort();
d->reply = nullptr;
}
if (!d->o1->linked())
return;
QUrl url(d->apiUrl);
QNetworkRequest netRequest(url);
netRequest.setHeader(QNetworkRequest::ContentTypeHeader, QLatin1String(O2_MIME_TYPE_XFORM));
QList reqParams = QList();
reqParams << O0RequestParameter("method", "flickr.photos.geo.setLocation");
reqParams << O0RequestParameter("photo_id", photoId.toLatin1());
reqParams << O0RequestParameter("lat", lat.toLatin1());
reqParams << O0RequestParameter("lon", lon.toLatin1());
QByteArray postData = O1::createQueryParameters(reqParams);
d->reply = d->requestor->post(netRequest, reqParams, postData);
d->state = FE_SETGEO;
emit signalBusy(true);
}
QString FlickrTalker::getUserName() const
{
return d->username;
}
QString FlickrTalker::getUserId() const
{
return d->userId;
}
void FlickrTalker::cancel()
{
if (d->reply)
{
d->reply->abort();
d->reply = nullptr;
}
if (m_authProgressDlg && !m_authProgressDlg->isHidden())
{
m_authProgressDlg->hide();
}
}
void FlickrTalker::slotError(const QString& error)
{
QString transError;
int errorNo = error.toInt();
switch (errorNo)
{
case 2:
transError = i18n("No photo specified");
break;
case 3:
transError = i18n("General upload failure");
break;
case 4:
transError = i18n("Filesize was zero");
break;
case 5:
transError = i18n("Filetype was not recognized");
break;
case 6:
transError = i18n("User exceeded upload limit");
break;
case 96:
transError = i18n("Invalid signature");
break;
case 97:
transError = i18n("Missing signature");
break;
case 98:
transError = i18n("Login Failed / Invalid auth token");
break;
case 100:
transError = i18n("Invalid API Key");
break;
case 105:
transError = i18n("Service currently unavailable");
break;
case 108:
transError = i18n("Invalid Frob");
break;
case 111:
transError = i18n("Format \"xxx\" not found");
break;
case 112:
transError = i18n("Method \"xxx\" not found");
break;
case 114:
transError = i18n("Invalid SOAP envelope");
break;
case 115:
transError = i18n("Invalid XML-RPC Method Call");
break;
case 116:
transError = i18n("The POST method is now required for all setters");
break;
default:
transError = i18n("Unknown error");
break;
};
QMessageBox::critical(QApplication::activeWindow(),
i18n("Error"),
i18n("Error Occurred: %1\nCannot proceed any further.", transError));
}
void FlickrTalker::slotFinished(QNetworkReply* reply)
{
emit signalBusy(false);
if (reply != d->reply)
{
return;
}
d->reply = nullptr;
if (reply->error() != QNetworkReply::NoError)
{
if (d->state == FE_ADDPHOTO)
{
emit signalAddPhotoFailed(reply->errorString());
}
else
{
QMessageBox::critical(QApplication::activeWindow(),
i18n("Error"), reply->errorString());
}
reply->deleteLater();
return;
}
QByteArray buffer = reply->readAll();
switch (d->state)
{
case (FE_LOGIN):
//parseResponseLogin(buffer);
break;
case (FE_LISTPHOTOSETS):
parseResponseListPhotoSets(buffer);
break;
case (FE_LISTPHOTOS):
parseResponseListPhotos(buffer);
break;
case (FE_GETPHOTOPROPERTY):
parseResponsePhotoProperty(buffer);
break;
case (FE_ADDPHOTO):
parseResponseAddPhoto(buffer);
break;
case (FE_ADDPHOTOTOPHOTOSET):
parseResponseAddPhotoToPhotoSet(buffer);
break;
case (FE_CREATEPHOTOSET):
parseResponseCreatePhotoSet(buffer);
break;
case (FE_GETMAXSIZE):
parseResponseMaxSize(buffer);
break;
case (FE_SETGEO):
parseResponseSetGeoLocation(buffer);
break;
default: // FR_LOGOUT
break;
}
reply->deleteLater();
}
void FlickrTalker::parseResponseMaxSize(const QByteArray& data)
{
QString errorString;
QDomDocument doc(QLatin1String("mydocument"));
if (!doc.setContent(data))
{
return;
}
QDomElement docElem = doc.documentElement();
QDomNode node = docElem.firstChild();
QDomElement e;
while (!node.isNull())
{
if (node.isElement() && node.nodeName() == QLatin1String("person"))
{
e = node.toElement();
QDomNode details = e.firstChild();
while (!details.isNull())
{
if (details.isElement())
{
e = details.toElement();
if (details.nodeName() == QLatin1String("videos"))
{
QDomAttr a = e.attributeNode(QLatin1String("maxupload"));
d->maxSize = a.value();
qCDebug(DIGIKAM_WEBSERVICES_LOG) << "Max upload size is"<maxSize;
}
}
details = details.nextSibling();
}
}
if (node.isElement() && node.nodeName() == QLatin1String("err"))
{
qCDebug(DIGIKAM_WEBSERVICES_LOG) << "Checking Error in response";
errorString = node.toElement().attribute(QLatin1String("code"));
qCDebug(DIGIKAM_WEBSERVICES_LOG) << "Error code=" << errorString;
qCDebug(DIGIKAM_WEBSERVICES_LOG) << "Msg=" << node.toElement().attribute(QLatin1String("msg"));
}
node = node.nextSibling();
}
m_authProgressDlg->hide();
}
void FlickrTalker::parseResponseCreatePhotoSet(const QByteArray& data)
{
qCDebug(DIGIKAM_WEBSERVICES_LOG) << "Parse response create photoset received " << data;
//bool success = false;
QDomDocument doc(QLatin1String("getListPhotoSets"));
if (!doc.setContent(data))
{
return;
}
QDomElement docElem = doc.documentElement();
QDomNode node = docElem.firstChild();
QDomElement e;
while (!node.isNull())
{
if (node.isElement() && node.nodeName() == QLatin1String("photoset"))
{
// Parse the id from the response.
QString new_id = node.toElement().attribute(QLatin1String("id"));
// Set the new id in the photo sets list.
QLinkedList::iterator it = m_photoSetsList->begin();
while (it != m_photoSetsList->end())
{
if (it->id == m_selectedPhotoSet.id)
{
it->id = new_id;
break;
}
++it;
}
// Set the new id in the selected photo set.
m_selectedPhotoSet.id = new_id;
qCDebug(DIGIKAM_WEBSERVICES_LOG) << "PhotoSet created successfully with id" << new_id;
emit signalAddPhotoSetSucceeded();
}
if (node.isElement() && node.nodeName() == QLatin1String("err"))
{
qCDebug(DIGIKAM_WEBSERVICES_LOG) << "Checking Error in response";
QString code = node.toElement().attribute(QLatin1String("code"));
qCDebug(DIGIKAM_WEBSERVICES_LOG) << "Error code=" << code;
QString msg = node.toElement().attribute(QLatin1String("msg"));
qCDebug(DIGIKAM_WEBSERVICES_LOG) << "Msg=" << msg;
QMessageBox::critical(QApplication::activeWindow(), i18n("Error"), i18n("PhotoSet creation failed: ") + msg);
}
node = node.nextSibling();
}
}
void FlickrTalker::parseResponseListPhotoSets(const QByteArray& data)
{
qCDebug(DIGIKAM_WEBSERVICES_LOG) << "parseResponseListPhotosets" << data;
bool success = false;
QDomDocument doc(QLatin1String("getListPhotoSets"));
if (!doc.setContent(data))
{
return;
}
QDomElement docElem = doc.documentElement();
QDomNode node = docElem.firstChild();
QDomElement e;
QString photoSet_id, photoSet_title, photoSet_description;
m_photoSetsList->clear();
while (!node.isNull())
{
if (node.isElement() && node.nodeName() == QLatin1String("photosets"))
{
e = node.toElement();
QDomNode details = e.firstChild();
FPhotoSet fps;
QDomNode detailsNode = details;
while (!detailsNode.isNull())
{
if (detailsNode.isElement())
{
e = detailsNode.toElement();
if (detailsNode.nodeName() == QLatin1String("photoset"))
{
qCDebug(DIGIKAM_WEBSERVICES_LOG) << "id=" << e.attribute(QLatin1String("id"));
photoSet_id = e.attribute(QLatin1String("id")); // this is what is obtained from data.
fps.id = photoSet_id;
QDomNode photoSetDetails = detailsNode.firstChild();
QDomElement e_detail;
while (!photoSetDetails.isNull())
{
e_detail = photoSetDetails.toElement();
if (photoSetDetails.nodeName() == QLatin1String("title"))
{
qCDebug(DIGIKAM_WEBSERVICES_LOG) << "Title=" << e_detail.text();
photoSet_title = e_detail.text();
fps.title = photoSet_title;
}
else if (photoSetDetails.nodeName() == QLatin1String("description"))
{
qCDebug(DIGIKAM_WEBSERVICES_LOG) << "Description =" << e_detail.text();
photoSet_description = e_detail.text();
fps.description = photoSet_description;
}
photoSetDetails = photoSetDetails.nextSibling();
}
m_photoSetsList->append(fps);
}
}
detailsNode = detailsNode.nextSibling();
}
details = details.nextSibling();
success = true;
}
if (node.isElement() && node.nodeName() == QLatin1String("err"))
{
qCDebug(DIGIKAM_WEBSERVICES_LOG) << "Checking Error in response";
QString code = node.toElement().attribute(QLatin1String("code"));
qCDebug(DIGIKAM_WEBSERVICES_LOG) << "Error code=" << code;
qCDebug(DIGIKAM_WEBSERVICES_LOG) << "Msg=" << node.toElement().attribute(QLatin1String("msg"));
emit signalError(code);
}
node = node.nextSibling();
}
qCDebug(DIGIKAM_WEBSERVICES_LOG) << "GetPhotoList finished";
if (!success)
{
emit signalListPhotoSetsFailed(i18n("Failed to fetch list of photo sets."));
}
else
{
emit signalListPhotoSetsSucceeded();
maxAllowedFileSize();
}
}
void FlickrTalker::parseResponseListPhotos(const QByteArray& data)
{
QDomDocument doc(QLatin1String("getPhotosList"));
if (!doc.setContent(data))
{
return;
}
QDomElement docElem = doc.documentElement();
QDomNode node = docElem.firstChild();
//QDomElement e;
//TODO
}
void FlickrTalker::parseResponseCreateAlbum(const QByteArray& data)
{
QDomDocument doc(QLatin1String("getCreateAlbum"));
if (!doc.setContent(data))
{
return;
}
QDomElement docElem = doc.documentElement();
QDomNode node = docElem.firstChild();
//TODO
}
void FlickrTalker::parseResponseAddPhoto(const QByteArray& data)
{
bool success = false;
QString line;
QDomDocument doc(QLatin1String("AddPhoto Response"));
if (!doc.setContent(data))
{
return;
}
QDomElement docElem = doc.documentElement();
QDomNode node = docElem.firstChild();
QDomElement e;
QString photoId;
while (!node.isNull())
{
if (node.isElement() && node.nodeName() == QLatin1String("photoid"))
{
e = node.toElement(); // try to convert the node to an element.
QDomNode details = e.firstChild();
photoId = e.text();
qCDebug(DIGIKAM_WEBSERVICES_LOG) << "Photoid= " << photoId;
success = true;
}
if (node.isElement() && node.nodeName() == QLatin1String("err"))
{
qCDebug(DIGIKAM_WEBSERVICES_LOG) << "Checking Error in response";
QString code = node.toElement().attribute(QLatin1String("code"));
qCDebug(DIGIKAM_WEBSERVICES_LOG) << "Error code=" << code;
qCDebug(DIGIKAM_WEBSERVICES_LOG) << "Msg=" << node.toElement().attribute(QLatin1String("msg"));
emit signalError(code);
}
node = node.nextSibling();
}
if (!success)
{
emit signalAddPhotoFailed(i18n("Failed to upload photo"));
}
else
{
QString photoSetId = m_selectedPhotoSet.id;
if (photoSetId == QLatin1String("-1"))
{
qCDebug(DIGIKAM_WEBSERVICES_LOG) << "PhotoSet Id not set, not adding the photo to any photoset";
emit signalAddPhotoSucceeded(photoId);
}
else
{
addPhotoToPhotoSet(photoId, photoSetId);
}
}
}
void FlickrTalker::parseResponsePhotoProperty(const QByteArray& data)
{
bool success = false;
QString line;
QDomDocument doc(QLatin1String("Photos Properties"));
if (!doc.setContent(data))
{
return;
}
QDomElement docElem = doc.documentElement();
QDomNode node = docElem.firstChild();
QDomElement e;
while (!node.isNull())
{
if (node.isElement() && node.nodeName() == QLatin1String("photoid"))
{
e = node.toElement(); // try to convert the node to an element.
QDomNode details = e.firstChild();
success = true;
qCDebug(DIGIKAM_WEBSERVICES_LOG) << "Photoid=" << e.text();
}
if (node.isElement() && node.nodeName() == QLatin1String("err"))
{
qCDebug(DIGIKAM_WEBSERVICES_LOG) << "Checking Error in response";
QString code = node.toElement().attribute(QLatin1String("code"));
qCDebug(DIGIKAM_WEBSERVICES_LOG) << "Error code=" << code;
qCDebug(DIGIKAM_WEBSERVICES_LOG) << "Msg=" << node.toElement().attribute(QLatin1String("msg"));
emit signalError(code);
}
node = node.nextSibling();
}
qCDebug(DIGIKAM_WEBSERVICES_LOG) << "GetToken finished";
if (!success)
{
emit signalAddPhotoFailed(i18n("Failed to query photo information"));
}
else
{
emit signalAddPhotoSucceeded(QLatin1String(""));
}
}
void FlickrTalker::parseResponseAddPhotoToPhotoSet(const QByteArray& data)
{
qCDebug(DIGIKAM_WEBSERVICES_LOG) << "parseResponseListPhotosets" << data;
emit signalAddPhotoSucceeded(QLatin1String(""));
}
void FlickrTalker::parseResponseSetGeoLocation(const QByteArray& data)
{
qCDebug(DIGIKAM_WEBSERVICES_LOG) << "parseResponseSetGeoLocation" << data;
emit signalAddPhotoSucceeded(QLatin1String(""));
}
} // namespace DigikamGenericFlickrPlugin
diff --git a/core/dplugins/generic/webservices/google/gswindow.cpp b/core/dplugins/generic/webservices/google/gswindow.cpp
index 800c29ec2c..9717b7cdfb 100644
--- a/core/dplugins/generic/webservices/google/gswindow.cpp
+++ b/core/dplugins/generic/webservices/google/gswindow.cpp
@@ -1,1273 +1,1269 @@
/* ============================================================
*
* This file is a part of digiKam project
* https://www.digikam.org
*
* Date : 2013-11-18
* Description : a tool to export items to Google web services
*
* Copyright (C) 2013 by Pankaj Kumar
* Copyright (C) 2015 by Shourya Singh Gupta
* Copyright (C) 2013-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.
*
* ============================================================ */
#include "gswindow.h"
// Qt includes
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
// KDE includes
#include
#include
#include
// Local includes
#include "wstoolutils.h"
#include "ditemslist.h"
#include "digikam_version.h"
#include "dprogresswdg.h"
#include "gdtalker.h"
#include "gsitem.h"
#include "gsnewalbumdlg.h"
#include "gswidget.h"
#include "gptalker.h"
#include "gsreplacedlg.h"
#include "digikam_debug.h"
#include "dfileoperations.h"
namespace DigikamGenericGoogleServicesPlugin
{
class Q_DECL_HIDDEN GSWindow::Private
{
public:
explicit Private()
{
widget = nullptr;
albumDlg = nullptr;
gphotoAlbumDlg = nullptr;
talker = nullptr;
gphotoTalker = nullptr;
iface = nullptr;
imagesCount = 0;
imagesTotal = 0;
renamingOpt = 0;
service = GoogleService::GPhotoImport;
}
unsigned int imagesCount;
unsigned int imagesTotal;
int renamingOpt;
QString serviceName;
QString toolName;
GoogleService service;
QString tmp;
GSWidget* widget;
GSNewAlbumDlg* albumDlg;
GSNewAlbumDlg* gphotoAlbumDlg;
GDTalker* talker;
GPTalker* gphotoTalker;
QString currentAlbumId;
QList< QPair > transferQueue;
QList< QPair > uploadQueue;
DInfoInterface* iface;
DMetadata meta;
};
GSWindow::GSWindow(DInfoInterface* const iface,
QWidget* const /*parent*/,
const QString& serviceName)
: WSToolDialog(nullptr, QString::fromLatin1("%1Export Dialog").arg(serviceName)),
d(new Private)
{
d->iface = iface;
d->serviceName = serviceName;
if (QString::compare(d->serviceName, QLatin1String("googledriveexport"),
Qt::CaseInsensitive) == 0)
{
d->service = GoogleService::GDrive;
d->toolName = QLatin1String("Google Drive");
}
else if (QString::compare(d->serviceName, QLatin1String("googlephotoexport"),
Qt::CaseInsensitive) == 0)
{
d->service = GoogleService::GPhotoExport;
d->toolName = QLatin1String("Google Photos");
}
else
{
d->service = GoogleService::GPhotoImport;
d->toolName = QLatin1String("Google Photos");
}
d->tmp = WSToolUtils::makeTemporaryDir("google").absolutePath() + QLatin1Char('/');;
d->widget = new GSWidget(this, d->iface, d->service, d->toolName);
setMainWidget(d->widget);
setModal(false);
switch (d->service)
{
case GoogleService::GDrive:
setWindowTitle(i18n("Export to Google Drive"));
startButton()->setText(i18n("Start Upload"));
startButton()->setToolTip(i18n("Start upload to Google Drive"));
- d->widget->setMinimumSize(700,500);
+ d->widget->setMinimumSize(700, 500);
d->albumDlg = new GSNewAlbumDlg(this, d->serviceName, d->toolName);
d->talker = new GDTalker(this);
connect(d->talker,SIGNAL(signalBusy(bool)),
this,SLOT(slotBusy(bool)));
connect(d->talker,SIGNAL(signalAccessTokenObtained()),
this,SLOT(slotAccessTokenObtained()));
connect(d->talker, SIGNAL(signalAuthenticationRefused()),
this,SLOT(slotAuthenticationRefused()));
connect(d->talker,SIGNAL(signalSetUserName(QString)),
this,SLOT(slotSetUserName(QString)));
connect(d->talker,SIGNAL(signalListAlbumsDone(int,QString,QList)),
this,SLOT(slotListAlbumsDone(int,QString,QList)));
connect(d->talker,SIGNAL(signalCreateFolderDone(int,QString)),
this,SLOT(slotCreateFolderDone(int,QString)));
connect(d->talker,SIGNAL(signalAddPhotoDone(int,QString)),
this,SLOT(slotAddPhotoDone(int,QString)));
connect(d->talker, SIGNAL(signalUploadPhotoDone(int,QString,QStringList)),
this, SLOT(slotUploadPhotoDone(int,QString,QStringList)));
readSettings();
buttonStateChange(false);
d->talker->doOAuth();
break;
case GoogleService::GPhotoImport:
case GoogleService::GPhotoExport:
if (d->service == GoogleService::GPhotoExport)
{
setWindowTitle(i18n("Export to Google Photos Service"));
startButton()->setText(i18n("Start Upload"));
startButton()->setToolTip(i18n("Start upload to Google Photos Service"));
d->widget->setMinimumSize(700, 500);
}
else
{
setWindowTitle(i18n("Import from Google Photos Service"));
startButton()->setText(i18n("Start Download"));
startButton()->setToolTip(i18n("Start download from Google Photos service"));
d->widget->setMinimumSize(300, 400);
}
d->gphotoAlbumDlg = new GSNewAlbumDlg(this, d->serviceName, d->toolName);
d->gphotoTalker = new GPTalker(this);
connect(d->gphotoTalker, SIGNAL(signalBusy(bool)),
this, SLOT(slotBusy(bool)));
connect(d->gphotoTalker,SIGNAL(signalSetUserName(QString)),
this,SLOT(slotSetUserName(QString)));
connect(d->gphotoTalker, SIGNAL(signalAccessTokenObtained()),
this, SLOT(slotAccessTokenObtained()));
connect(d->gphotoTalker, SIGNAL(signalAuthenticationRefused()),
this,SLOT(slotAuthenticationRefused()));
connect(d->gphotoTalker, SIGNAL(signalListAlbumsDone(int,QString,QList)),
this, SLOT(slotListAlbumsDone(int,QString,QList)));
connect(d->gphotoTalker, SIGNAL(signalCreateAlbumDone(int,QString,QString)),
this, SLOT(slotCreateFolderDone(int,QString,QString)));
connect(d->gphotoTalker, SIGNAL(signalAddPhotoDone(int,QString)),
this, SLOT(slotAddPhotoDone(int,QString)));
connect(d->gphotoTalker, SIGNAL(signalUploadPhotoDone(int,QString,QStringList)),
this, SLOT(slotUploadPhotoDone(int,QString,QStringList)));
connect(d->gphotoTalker, SIGNAL(signalGetPhotoDone(int,QString,QByteArray,QString)),
this, SLOT(slotGetPhotoDone(int,QString,QByteArray,QString)));
readSettings();
buttonStateChange(false);
d->gphotoTalker->doOAuth();
break;
}
connect(d->widget->imagesList(), SIGNAL(signalImageListChanged()),
this, SLOT(slotImageListChanged()));
connect(d->widget->getChangeUserBtn(), SIGNAL(clicked()),
this, SLOT(slotUserChangeRequest()));
connect(d->widget->getNewAlbmBtn(), SIGNAL(clicked()),
this,SLOT(slotNewAlbumRequest()));
connect(d->widget->getReloadBtn(), SIGNAL(clicked()),
this, SLOT(slotReloadAlbumsRequest()));
connect(startButton(), SIGNAL(clicked()),
this, SLOT(slotStartTransfer()));
connect(this, SIGNAL(finished(int)),
this, SLOT(slotFinished()));
}
GSWindow::~GSWindow()
{
d->transferQueue.clear();
delete d->gphotoTalker;
delete d->talker;
delete d;
}
void GSWindow::reactivate()
{
d->widget->imagesList()->loadImagesFromCurrentSelection();
d->widget->progressBar()->hide();
show();
}
void GSWindow::readSettings()
{
KConfig config;
KConfigGroup grp;
switch (d->service)
{
case GoogleService::GDrive:
grp = config.group("Google Drive Settings");
break;
default:
grp = config.group("Google Photo Settings");
break;
}
d->currentAlbumId = grp.readEntry("Current Album", QString());
if (grp.readEntry("Resize", false))
{
d->widget->getResizeCheckBox()->setChecked(true);
d->widget->getDimensionSpB()->setEnabled(true);
}
else
{
d->widget->getResizeCheckBox()->setChecked(false);
d->widget->getDimensionSpB()->setEnabled(false);
}
d->widget->getOriginalCheckBox()->setChecked(grp.readEntry("Upload Original", false));
d->widget->getPhotoIdCheckBox()->setChecked(grp.readEntry("Write PhotoID", true));
d->widget->getDimensionSpB()->setValue(grp.readEntry("Maximum Width", 1600));
d->widget->getImgQualitySpB()->setValue(grp.readEntry("Image Quality", 90));
if (d->service == GoogleService::GPhotoExport && d->widget->m_tagsBGrp)
{
d->widget->m_tagsBGrp->button(grp.readEntry("Tag Paths", 0))->setChecked(true);
}
KConfigGroup dialogGroup = config.group(QString::fromLatin1("%1Export Dialog").arg(d->serviceName));
winId();
KWindowConfig::restoreWindowSize(windowHandle(), dialogGroup);
resize(windowHandle()->size());
}
void GSWindow::writeSettings()
{
KConfig config;
KConfigGroup grp;
switch (d->service)
{
case GoogleService::GDrive:
grp = config.group("Google Drive Settings");
break;
default:
grp = config.group("Google Photo Settings");
break;
}
grp.writeEntry("Current Album", d->currentAlbumId);
grp.writeEntry("Resize", d->widget->getResizeCheckBox()->isChecked());
grp.writeEntry("Upload Original", d->widget->getOriginalCheckBox()->isChecked());
grp.writeEntry("Write PhotoID", d->widget->getPhotoIdCheckBox()->isChecked());
grp.writeEntry("Maximum Width", d->widget->getDimensionSpB()->value());
grp.writeEntry("Image Quality", d->widget->getImgQualitySpB()->value());
if (d->service == GoogleService::GPhotoExport && d->widget->m_tagsBGrp)
{
grp.writeEntry("Tag Paths", d->widget->m_tagsBGrp->checkedId());
}
KConfigGroup dialogGroup = config.group(QString::fromLatin1("%1Export Dialog").arg(d->serviceName));
KWindowConfig::saveWindowSize(windowHandle(), dialogGroup);
config.sync();
}
void GSWindow::slotSetUserName(const QString& msg)
{
d->widget->updateLabels(msg);
}
void GSWindow::slotListPhotosDoneForDownload(int errCode,
const QString& errMsg,
const QList & photosList)
{
disconnect(d->gphotoTalker, SIGNAL(signalListPhotosDone(int,QString,QList)),
this, SLOT(slotListPhotosDoneForDownload(int,QString,QList)));
if (errCode == 0)
{
QMessageBox::critical(this, i18nc("@title:window", "Error"),
i18n("Google Photos call failed: %1\n", errMsg));
return;
}
typedef QPair Pair;
d->transferQueue.clear();
QList::const_iterator itPWP;
for (itPWP = photosList.begin() ; itPWP != photosList.end() ; ++itPWP)
{
d->transferQueue.append(Pair((*itPWP).originalURL, (*itPWP)));
}
if (d->transferQueue.isEmpty())
return;
d->currentAlbumId = d->widget->getAlbumsCoB()->itemData(d->widget->getAlbumsCoB()->currentIndex()).toString();
d->imagesTotal = d->transferQueue.count();
d->imagesCount = 0;
d->widget->progressBar()->setFormat(i18n("%v / %m"));
d->widget->progressBar()->show();
d->renamingOpt = 0;
// start download with first photo in queue
downloadNextPhoto();
}
void GSWindow::slotListAlbumsDone(int code, const QString& errMsg, const QList & list)
{
switch (d->service)
{
case GoogleService::GDrive:
if (code == 0)
{
QMessageBox::critical(this, i18nc("@title:window", "Error"),
i18n("Google Drive call failed: %1\n", errMsg));
return;
}
d->widget->getAlbumsCoB()->clear();
for (int i = 0 ; i < list.size() ; ++i)
{
d->widget->getAlbumsCoB()->addItem(QIcon::fromTheme(QLatin1String("system-users")),
list.value(i).title, list.value(i).id);
if (d->currentAlbumId == list.value(i).id)
{
d->widget->getAlbumsCoB()->setCurrentIndex(i);
}
}
buttonStateChange(true);
d->talker->getUserName();
break;
default:
if (code == 0)
{
QMessageBox::critical(this, i18nc("@title:window", "Error"),
i18n("Google Photos call failed: %1\n", errMsg));
return;
}
d->widget->getAlbumsCoB()->clear();
for (int i = 0 ; i < list.size() ; ++i)
{
if (d->service == GoogleService::GPhotoImport && i == 0)
{
// remove album
continue;
}
QString albumIcon;
if (list.at(i).isWriteable)
{
albumIcon = QLatin1String("folder");
}
else
{
albumIcon = QLatin1String("folder-locked");
}
d->widget->getAlbumsCoB()->addItem(QIcon::fromTheme(albumIcon), list.at(i).title, list.at(i).id);
if (d->currentAlbumId == list.at(i).id)
d->widget->getAlbumsCoB()->setCurrentIndex(i);
buttonStateChange(true);
}
break;
}
}
void GSWindow::slotBusy(bool val)
{
- if (val)
- {
- setCursor(Qt::WaitCursor);
- d->widget->getChangeUserBtn()->setEnabled(false);
- buttonStateChange(false);
- }
- else
- {
- setCursor(Qt::ArrowCursor);
- d->widget->getChangeUserBtn()->setEnabled(true);
- buttonStateChange(true);
- }
+ setCursor(val ? Qt::WaitCursor
+ : Qt::ArrowCursor);
+
+ d->widget->imagesList()->enableControlButtons(!val);
+ d->widget->imagesList()->enableDragAndDrop(!val);
+ d->widget->getChangeUserBtn()->setEnabled(!val);
+ d->widget->getOptionsBox()->setEnabled(!val);
+ buttonStateChange(!val);
}
void GSWindow::slotStartTransfer()
{
d->widget->imagesList()->clearProcessedStatus();
switch (d->service)
{
case GoogleService::GDrive:
case GoogleService::GPhotoExport:
if (d->widget->imagesList()->imageUrls().isEmpty())
{
QMessageBox::critical(this, i18nc("@title:window", "Error"),
i18n("No image selected. Please select which images should be uploaded."));
return;
}
break;
case GoogleService::GPhotoImport:
break;
}
switch (d->service)
{
case GoogleService::GDrive:
if (!(d->talker->authenticated()))
{
QPointer warn = new QMessageBox(QMessageBox::Warning,
i18n("Warning"),
i18n("Authentication failed. Click \"Continue\" to authenticate."),
QMessageBox::Yes | QMessageBox::No);
(warn->button(QMessageBox::Yes))->setText(i18n("Continue"));
(warn->button(QMessageBox::No))->setText(i18n("Cancel"));
if (warn->exec() == QMessageBox::Yes)
{
d->talker->doOAuth();
delete warn;
return;
}
else
{
delete warn;
return;
}
}
break;
default:
if (!(d->gphotoTalker->authenticated()))
{
QPointer warn = new QMessageBox(QMessageBox::Warning,
i18n("Warning"),
i18n("Authentication failed. Click \"Continue\" to authenticate."),
QMessageBox::Yes | QMessageBox::No);
(warn->button(QMessageBox::Yes))->setText(i18n("Continue"));
(warn->button(QMessageBox::No))->setText(i18n("Cancel"));
if (warn->exec() == QMessageBox::Yes)
{
d->gphotoTalker->doOAuth();
delete warn;
return;
}
else
{
delete warn;
return;
}
}
if (d->service == GoogleService::GPhotoImport)
{
qCDebug(DIGIKAM_WEBSERVICES_LOG) << "Google Photo Transfer invoked";
// list photos of the album, then start download
connect(d->gphotoTalker, SIGNAL(signalListPhotosDone(int,QString,QList)),
this, SLOT(slotListPhotosDoneForDownload(int,QString,QList)));
d->gphotoTalker->listPhotos(
d->widget->getAlbumsCoB()->itemData(d->widget->getAlbumsCoB()->currentIndex()).toString(),
d->widget->getDimensionCoB()->itemData(d->widget->getDimensionCoB()->currentIndex()).toString());
return;
}
}
typedef QPair Pair;
for (int i = 0 ; i < (d->widget->imagesList()->imageUrls().size()) ; ++i)
{
DItemInfo info(d->iface->itemInfo(d->widget->imagesList()->imageUrls().value(i)));
GSPhoto temp;
qCDebug(DIGIKAM_WEBSERVICES_LOG) << "in start transfer info" <service)
{
case GoogleService::GDrive:
temp.title = info.title();
temp.description = info.comment().section(QLatin1String("\n"), 0, 0);
break;
default:
temp.title = info.name();
// Google Photo doesn't support image titles. Include it in descriptions if needed.
QStringList descriptions = QStringList() << info.title() << info.comment();
descriptions.removeAll(QLatin1String(""));
temp.description = descriptions.join(QLatin1String("\n\n"));
temp.description.replace(QLatin1Char('"'), QLatin1String("\\\""));
break;
}
temp.gpsLat.setNum(info.latitude());
temp.gpsLon.setNum(info.longitude());
temp.tags = info.tagsPath();
d->transferQueue.append(Pair(d->widget->imagesList()->imageUrls().value(i), temp));
}
d->currentAlbumId = d->widget->getAlbumsCoB()->itemData(d->widget->getAlbumsCoB()->currentIndex()).toString();
d->imagesTotal = d->transferQueue.count();
d->imagesCount = 0;
d->widget->progressBar()->setFormat(i18n("%v / %m"));
d->widget->progressBar()->setMaximum(d->imagesTotal);
d->widget->progressBar()->setValue(0);
d->widget->progressBar()->show();
switch (d->service)
{
case GoogleService::GDrive:
d->widget->progressBar()->progressScheduled(i18n("Google Drive export"), true, true);
d->widget->progressBar()->progressThumbnailChanged(
QIcon::fromTheme(QLatin1String("dk-googledrive")).pixmap(22, 22));
break;
default:
d->widget->progressBar()->progressScheduled(i18n("Google Photo export"), true, true);
d->widget->progressBar()->progressThumbnailChanged(
QIcon::fromTheme((QLatin1String("dk-googlephoto"))).pixmap(22, 22));
break;
}
uploadNextPhoto();
}
void GSWindow::uploadNextPhoto()
{
qCDebug(DIGIKAM_WEBSERVICES_LOG) << "in upload nextphoto" << d->transferQueue.count();
if (d->transferQueue.isEmpty())
{
//d->widget->progressBar()->hide();
d->widget->progressBar()->progressCompleted();
/**
* Now all raw photos have been added,
* for GPhoto: prepare to upload on user account
* for GDrive: get listPhotoId to write metadata and finish upload
*/
if (d->service == GoogleService::GPhotoExport)
{
emit d->gphotoTalker->signalReadyToUpload();
}
else
{
emit d->talker->signalReadyToUpload();
}
return;
}
typedef QPair Pair;
Pair pathComments = d->transferQueue.first();
GSPhoto info = pathComments.second;
bool res = true;
d->widget->imagesList()->processing(pathComments.first);
switch (d->service)
{
case GoogleService::GDrive:
{
res = d->talker->addPhoto(pathComments.first.toLocalFile(),
info,
d->currentAlbumId,
d->widget->getOriginalCheckBox()->isChecked(),
d->widget->getResizeCheckBox()->isChecked(),
d->widget->getDimensionSpB()->value(),
d->widget->getImgQualitySpB()->value());
break;
}
case GoogleService::GPhotoExport:
{
bool bCancel = false;
bool bAdd = true;
if (!info.id.isEmpty() && !info.editUrl.isEmpty())
{
switch (d->renamingOpt)
{
case PWR_ADD_ALL:
bAdd = true;
break;
case PWR_REPLACE_ALL:
bAdd = false;
break;
default:
{
QPointer dlg = new ReplaceDialog(this, QLatin1String(""),
d->iface, pathComments.first,
info.thumbURL);
dlg->exec();
switch (dlg->getResult())
{
case PWR_ADD_ALL:
d->renamingOpt = PWR_ADD_ALL;
break;
case PWR_ADD:
bAdd = true;
break;
case PWR_REPLACE_ALL:
d->renamingOpt = PWR_REPLACE_ALL;
break;
case PWR_REPLACE:
bAdd = false;
break;
case PWR_CANCEL:
default:
bCancel = true;
break;
}
delete dlg;
break;
}
}
}
// adjust tags according to radio button clicked
if (d->widget->m_tagsBGrp)
{
switch (d->widget->m_tagsBGrp->checkedId())
{
case GPTagLeaf:
{
QStringList newTags;
QStringList::const_iterator itT;
for (itT = info.tags.constBegin() ; itT != info.tags.constEnd() ; ++itT)
{
QString strTmp = *itT;
int idx = strTmp.lastIndexOf(QLatin1Char('/'));
if (idx > 0)
{
strTmp.remove(0, idx + 1);
}
newTags.append(strTmp);
}
info.tags = newTags;
break;
}
case GPTagSplit:
{
QSet newTagsSet;
QStringList::const_iterator itT;
for (itT = info.tags.constBegin() ; itT != info.tags.constEnd() ; ++itT)
{
QStringList strListTmp = itT->split(QLatin1Char('/'));
QStringList::const_iterator itT2;
for (itT2 = strListTmp.constBegin() ; itT2 != strListTmp.constEnd() ; ++itT2)
{
if (!newTagsSet.contains(*itT2))
{
newTagsSet.insert(*itT2);
}
}
}
info.tags.clear();
QSet::const_iterator itT3;
for (itT3 = newTagsSet.begin() ; itT3 != newTagsSet.end() ; ++itT3)
{
info.tags.append(*itT3);
}
break;
}
case GPTagCombined:
default:
break;
}
}
if (bCancel)
{
slotTransferCancel();
res = true;
}
else
{
if (bAdd)
{
res = d->gphotoTalker->addPhoto(pathComments.first.toLocalFile(),
info,
d->currentAlbumId,
d->widget->getOriginalCheckBox()->isChecked(),
d->widget->getResizeCheckBox()->isChecked(),
d->widget->getDimensionSpB()->value(),
d->widget->getImgQualitySpB()->value());
}
else
{
res = d->gphotoTalker->updatePhoto(pathComments.first.toLocalFile(),
info,
d->widget->getResizeCheckBox()->isChecked(),
d->widget->getDimensionSpB()->value(),
d->widget->getImgQualitySpB()->value());
}
}
break;
}
case GoogleService::GPhotoImport:
break;
}
if (!res)
{
slotAddPhotoDone(0, QLatin1String(""));
return;
}
}
void GSWindow::downloadNextPhoto()
{
if (d->transferQueue.isEmpty())
{
d->widget->progressBar()->hide();
d->widget->progressBar()->progressCompleted();
return;
}
d->widget->progressBar()->setMaximum(d->imagesTotal);
d->widget->progressBar()->setValue(d->imagesCount);
QString imgPath = d->transferQueue.first().first.url();
d->gphotoTalker->getPhoto(imgPath);
}
void GSWindow::slotGetPhotoDone(int errCode, const QString& errMsg,
const QByteArray& photoData, const QString& fileName)
{
if (d->transferQueue.isEmpty())
{
return;
}
GSPhoto item = d->transferQueue.first().second;
/**
* (Trung)
* Google Photo API now does not support title for image,
* so we use the file name from the header. As fallback
* the creation time for image name instead.
*/
QString itemName(item.title);
QString suffix(item.mimeType.section(QLatin1Char('/'), -1));
if (itemName.isEmpty() && !fileName.isEmpty())
{
QFileInfo info(fileName);
itemName = info.completeBaseName();
suffix = info.suffix();
}
if (itemName.isEmpty())
{
itemName = QString::fromLatin1("image-%1").arg(item.creationTime);
// Replace colon for Windows file systems
itemName.replace(QLatin1Char(':'), QLatin1Char('-'));
}
QUrl tmpUrl = QUrl::fromLocalFile(QString(d->tmp + itemName +
QLatin1Char('.') +
suffix));
if (errCode == 1)
{
QString errText;
QFile imgFile(tmpUrl.toLocalFile());
if (!imgFile.open(QIODevice::WriteOnly))
{
errText = imgFile.errorString();
qCDebug(DIGIKAM_WEBSERVICES_LOG) << "error write";
}
else if (imgFile.write(photoData) != photoData.size())
{
errText = imgFile.errorString();
}
else
{
imgFile.close();
}
if (errText.isEmpty())
{
if (d->meta.load(tmpUrl.toLocalFile()))
{
if (d->meta.supportXmp() && d->meta.canWriteXmp(tmpUrl.toLocalFile()))
{
d->meta.setXmpTagString("Xmp.digiKam.picasawebGPhotoId", item.id);
d->meta.setXmpKeywords(item.tags);
}
if (!item.gpsLat.isEmpty() && !item.gpsLon.isEmpty())
{
d->meta.setGPSInfo(0.0, item.gpsLat.toDouble(), item.gpsLon.toDouble());
}
d->meta.setMetadataWritingMode((int)DMetadata::WRITE_TO_FILE_ONLY);
d->meta.save(tmpUrl.toLocalFile());
}
d->transferQueue.removeFirst();
d->imagesCount++;
}
else
{
QPointer warn = new QMessageBox(QMessageBox::Warning,
i18n("Warning"),
i18n("Failed to save photo: %1\n"
"Do you want to continue?", errText),
QMessageBox::Yes | QMessageBox::No);
(warn->button(QMessageBox::Yes))->setText(i18n("Continue"));
(warn->button(QMessageBox::No))->setText(i18n("Cancel"));
if (warn->exec() != QMessageBox::Yes)
{
slotTransferCancel();
delete warn;
return;
}
delete warn;
}
}
else
{
QPointer warn = new QMessageBox(QMessageBox::Warning,
i18n("Warning"),
i18n("Failed to download photo: %1\n"
"Do you want to continue?", errMsg),
QMessageBox::Yes | QMessageBox::No);
(warn->button(QMessageBox::Yes))->setText(i18n("Continue"));
(warn->button(QMessageBox::No))->setText(i18n("Cancel"));
if (warn->exec() != QMessageBox::Yes)
{
slotTransferCancel();
delete warn;
return;
}
delete warn;
}
QUrl newUrl = QUrl::fromLocalFile(QString::fromLatin1("%1/%2").arg(d->widget->getDestinationPath())
.arg(tmpUrl.fileName()));
newUrl = DFileOperations::getUniqueFileUrl(newUrl);
qCDebug(DIGIKAM_WEBSERVICES_LOG) << "location" << newUrl;
if (!QFile::rename(tmpUrl.toLocalFile(), newUrl.toLocalFile()))
{
QMessageBox::critical(this, i18nc("@title:window", "Error"),
i18n("Failed to save image to %1",
newUrl.toLocalFile()));
}
emit updateHostApp(newUrl);
downloadNextPhoto();
}
void GSWindow::slotAddPhotoDone(int err, const QString& msg)
{
if (d->transferQueue.isEmpty())
{
return;
}
if (err == 0)
{
d->widget->imagesList()->processed(d->transferQueue.first().first,false);
QPointer warn = new QMessageBox(QMessageBox::Warning,
i18n("Warning"),
i18n("Failed to upload photo to %1.\n%2\n"
"Do you want to continue?",
d->toolName, msg),
QMessageBox::Yes | QMessageBox::No);
(warn->button(QMessageBox::Yes))->setText(i18n("Continue"));
(warn->button(QMessageBox::No))->setText(i18n("Cancel"));
if (warn->exec() != QMessageBox::Yes)
{
d->transferQueue.clear();
d->widget->progressBar()->hide();
}
else
{
d->transferQueue.removeFirst();
d->imagesTotal--;
d->widget->progressBar()->setMaximum(d->imagesTotal);
d->widget->progressBar()->setValue(d->imagesCount);
uploadNextPhoto();
}
delete warn;
}
else
{
/**
* (Trung) Take first item out of transferQueue and append to uploadQueue,
* in order to use it again to write id in slotUploadPhotoDone
*/
QPair item = d->transferQueue.first();
d->uploadQueue.append(item);
// Remove photo uploaded from the transfer queue
d->transferQueue.removeFirst();
d->imagesCount++;
qCDebug(DIGIKAM_WEBSERVICES_LOG) << "In slotAddPhotoSucceeded" << d->imagesCount;
d->widget->progressBar()->setMaximum(d->imagesTotal);
d->widget->progressBar()->setValue(d->imagesCount);
uploadNextPhoto();
}
}
void GSWindow::slotUploadPhotoDone(int err, const QString& msg, const QStringList& listPhotoId)
{
if (err == 0)
{
QPointer warn = new QMessageBox(QMessageBox::Warning,
i18n("Warning"),
i18n("Failed to finish uploading photo to %1.\n%2\n"
"No image uploaded to your account.",
d->toolName, msg),
QMessageBox::Yes);
(warn->button(QMessageBox::Yes))->setText(i18n("OK"));
d->uploadQueue.clear();
d->widget->progressBar()->hide();
delete warn;
}
else
{
foreach (const QString& photoId, listPhotoId)
{
// Remove image from upload list and from UI
QPair item = d->uploadQueue.takeFirst();
d->widget->imagesList()->removeItemByUrl(item.first);
QUrl fileUrl = item.first;
qCDebug(DIGIKAM_WEBSERVICES_LOG) << "photoID:" << photoId;
if (d->widget->getPhotoIdCheckBox()->isChecked() &&
d->meta.supportXmp() &&
d->meta.canWriteXmp(fileUrl.toLocalFile()) &&
d->meta.load(fileUrl.toLocalFile()) &&
!photoId.isEmpty())
{
d->meta.setXmpTagString("Xmp.digiKam.picasawebGPhotoId", photoId);
d->meta.save(fileUrl.toLocalFile());
}
}
if (!d->widget->imagesList()->imageUrls().isEmpty())
{
qCDebug(DIGIKAM_WEBSERVICES_LOG) << "continue to upload";
emit d->gphotoTalker->signalReadyToUpload();
}
}
}
void GSWindow::slotImageListChanged()
{
startButton()->setEnabled(!(d->widget->imagesList()->imageUrls().isEmpty()));
}
void GSWindow::slotNewAlbumRequest()
{
switch (d->service)
{
case GoogleService::GDrive:
if (d->albumDlg->exec() == QDialog::Accepted)
{
GSFolder newFolder;
d->albumDlg->getAlbumProperties(newFolder);
d->currentAlbumId = d->widget->getAlbumsCoB()->itemData(d->widget->getAlbumsCoB()->currentIndex()).toString();
d->talker->createFolder(newFolder.title, d->currentAlbumId);
}
break;
default:
if (d->gphotoAlbumDlg->exec() == QDialog::Accepted)
{
GSFolder newFolder;
d->gphotoAlbumDlg->getAlbumProperties(newFolder);
d->gphotoTalker->createAlbum(newFolder);
}
break;
}
}
void GSWindow::slotReloadAlbumsRequest()
{
switch (d->service)
{
case GoogleService::GDrive:
d->talker->listFolders();
break;
case GoogleService::GPhotoImport:
case GoogleService::GPhotoExport:
d->gphotoTalker->listAlbums();
break;
}
}
void GSWindow::slotAccessTokenObtained()
{
switch (d->service)
{
case GoogleService::GDrive:
d->talker->listFolders();
break;
case GoogleService::GPhotoImport:
case GoogleService::GPhotoExport:
d->gphotoTalker->getLoggedInUser();
break;
}
}
void GSWindow::slotAuthenticationRefused()
{
// QMessageBox::critical(this, i18nc("@title:window", "Error"),
// i18n("An authentication error occurred: account failed to link"));
// Clear list albums
d->widget->getAlbumsCoB()->clear();
// Clear user name
d->widget->updateLabels(QString());
return;
}
void GSWindow::slotCreateFolderDone(int code, const QString& msg, const QString& albumId)
{
switch (d->service)
{
case GoogleService::GDrive:
if (code == 0)
QMessageBox::critical(this, i18nc("@title:window", "Error"),
i18n("Google Drive call failed:\n%1", msg));
else
{
d->currentAlbumId = albumId;
d->talker->listFolders();
}
break;
case GoogleService::GPhotoImport:
case GoogleService::GPhotoExport:
if (code == 0)
QMessageBox::critical(this, i18nc("@title:window", "Error"),
i18n("Google Photos call failed:\n%1", msg));
else
{
d->currentAlbumId = albumId;
d->gphotoTalker->listAlbums();
}
break;
}
}
void GSWindow::slotTransferCancel()
{
d->transferQueue.clear();
d->widget->progressBar()->hide();
switch (d->service)
{
case GoogleService::GDrive:
d->talker->cancel();
break;
case GoogleService::GPhotoImport:
case GoogleService::GPhotoExport:
d->gphotoTalker->cancel();
break;
}
}
void GSWindow::slotUserChangeRequest()
{
QPointer warn = new QMessageBox(QMessageBox::Warning,
i18n("Warning"),
i18n("You will be logged out of your account, "
"click \"Continue\" to authenticate for another account"),
QMessageBox::Yes | QMessageBox::No);
(warn->button(QMessageBox::Yes))->setText(i18n("Continue"));
(warn->button(QMessageBox::No))->setText(i18n("Cancel"));
if (warn->exec() == QMessageBox::Yes)
{
/**
* We do not force user to logout from their account
* We simply unlink user account and direct use to login page to login new account
* (In the future, we may not unlink() user, but let them change account and
* choose which one they want to use)
* After unlink(), waiting actively until O2 completely unlink() account, before doOAuth() again
*/
switch (d->service)
{
case GoogleService::GDrive:
d->talker->unlink();
while(d->talker->authenticated());
d->talker->doOAuth();
break;
case GoogleService::GPhotoImport:
case GoogleService::GPhotoExport:
d->gphotoTalker->unlink();
while(d->gphotoTalker->authenticated());
d->gphotoTalker->doOAuth();
break;
}
}
delete warn;
}
void GSWindow::buttonStateChange(bool state)
{
d->widget->getNewAlbmBtn()->setEnabled(state);
d->widget->getReloadBtn()->setEnabled(state);
startButton()->setEnabled(state);
}
void GSWindow::slotFinished()
{
writeSettings();
d->transferQueue.clear();
d->widget->imagesList()->listView()->clear();
}
void GSWindow::closeEvent(QCloseEvent* e)
{
if (!e)
{
return;
}
slotFinished();
e->accept();
}
} // namespace DigikamGenericGoogleServicesPlugin
diff --git a/core/dplugins/generic/webservices/onedrive/odtalker.cpp b/core/dplugins/generic/webservices/onedrive/odtalker.cpp
index dfc0a49583..cf2864690c 100644
--- a/core/dplugins/generic/webservices/onedrive/odtalker.cpp
+++ b/core/dplugins/generic/webservices/onedrive/odtalker.cpp
@@ -1,600 +1,587 @@
/* ============================================================
*
* This file is a part of digiKam project
* https://www.digikam.org
*
* Date : 2018-05-20
* Description : a tool to export images to Onedrive web service
*
* Copyright (C) 2018 by Tarek Talaat
*
* 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 "odtalker.h"
// Qt includes
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
-#include
#include
// KDE includes
#include
#include
// Local includes
-#include "digikam_config.h"
#include "digikam_debug.h"
#include "digikam_version.h"
#include "wstoolutils.h"
#include "odwindow.h"
#include "oditem.h"
#include "odmpform.h"
+#include "webbrowserdlg.h"
#include "previewloadthread.h"
-#ifdef HAVE_QWEBENGINE
-# include "webwidget_qwebengine.h"
-#else
-# include "webwidget.h"
-#endif
-
namespace DigikamGenericOneDrivePlugin
{
class Q_DECL_HIDDEN ODTalker::Private
{
public:
enum State
{
OD_USERNAME = 0,
OD_LISTFOLDERS,
OD_CREATEFOLDER,
OD_ADDPHOTO
};
public:
explicit Private()
+ : state(OD_USERNAME),
+ parent(nullptr),
+ netMngr(nullptr),
+ reply(nullptr),
+ settings(nullptr),
+ browser(nullptr)
{
clientId = QLatin1String("4c20a541-2ca8-4b98-8847-a375e4d33f34");
clientSecret = QLatin1String("wtdcaXADCZ0|tcDA7633|@*");
- scope = QLatin1String("Files.ReadWrite User.Read");
authUrl = QLatin1String("https://login.live.com/oauth20_authorize.srf");
tokenUrl = QLatin1String("https://login.live.com/oauth20_token.srf");
+ scope = QLatin1String("Files.ReadWrite User.Read");
redirectUrl = QLatin1String("https://login.live.com/oauth20_desktop.srf");
-
- serviceName = QLatin1String("Onedrive");
- serviceTime = QLatin1String("token_time");
- serviceKey = QLatin1String("access_token");
-
- state = OD_USERNAME;
-
- parent = nullptr;
- netMngr = nullptr;
- reply = nullptr;
- settings = nullptr;
- view = nullptr;
+ serviceName = QLatin1String("Onedrive");
+ serviceTime = QLatin1String("token_time");
+ serviceKey = QLatin1String("access_token");
}
public:
QString clientId;
QString clientSecret;
QString authUrl;
QString tokenUrl;
QString scope;
QString redirectUrl;
QString accessToken;
QString serviceName;
QString serviceTime;
QString serviceKey;
QDateTime expiryTime;
State state;
QWidget* parent;
QNetworkAccessManager* netMngr;
QNetworkReply* reply;
QSettings* settings;
- WebWidget* view;
+ WebBrowserDlg* browser;
QList > folderList;
QList nextFolder;
};
ODTalker::ODTalker(QWidget* const parent)
: d(new Private)
{
d->parent = parent;
d->netMngr = new QNetworkAccessManager(this);
- d->view = new WebWidget(d->parent);
- d->view->resize(800, 600);
-
d->settings = WSToolUtils::getOauthSettings(this);
connect(this, SIGNAL(oneDriveLinkingFailed()),
this, SLOT(slotLinkingFailed()));
connect(this, SIGNAL(oneDriveLinkingSucceeded()),
this, SLOT(slotLinkingSucceeded()));
connect(d->netMngr, SIGNAL(finished(QNetworkReply*)),
this, SLOT(slotFinished(QNetworkReply*)));
-
- connect(d->view, SIGNAL(urlChanged(QUrl)),
- this, SLOT(slotCatchUrl(QUrl)));
-
- connect(d->view, SIGNAL(closeView(bool)),
- this, SIGNAL(signalBusy(bool)));
}
ODTalker::~ODTalker()
{
if (d->reply)
{
d->reply->abort();
}
WSToolUtils::removeTemporaryDir("onedrive");
delete d;
}
void ODTalker::link()
{
emit signalBusy(true);
QUrl url(d->authUrl);
QUrlQuery query(url);
query.addQueryItem(QLatin1String("client_id"), d->clientId);
query.addQueryItem(QLatin1String("scope"), d->scope);
query.addQueryItem(QLatin1String("redirect_uri"), d->redirectUrl);
query.addQueryItem(QLatin1String("response_type"), QLatin1String("token"));
url.setQuery(query);
- d->view->setWindowFlags(Qt::Dialog);
- d->view->load(url);
- d->view->show();
+ delete d->browser;
+ d->browser = new WebBrowserDlg(url, d->parent, true);
+ d->browser->setModal(true);
+
+ connect(d->browser, SIGNAL(urlChanged(QUrl)),
+ this, SLOT(slotCatchUrl(QUrl)));
+
+ connect(d->browser, SIGNAL(closeView(bool)),
+ this, SIGNAL(signalBusy(bool)));
+
+ d->browser->show();
}
void ODTalker::unLink()
{
d->accessToken = QString();
d->settings->beginGroup(d->serviceName);
d->settings->remove(QString());
d->settings->endGroup();
-#ifdef HAVE_QWEBENGINE
- d->view->page()->profile()->cookieStore()->deleteAllCookies();
-#else
- d->view->page()->networkAccessManager()->setCookieJar(new QNetworkCookieJar());
-#endif
-
emit oneDriveLinkingSucceeded();
}
void ODTalker::slotCatchUrl(const QUrl& url)
{
qCDebug(DIGIKAM_WEBSERVICES_LOG) << "Received URL from webview:" << url;
QString str = url.toString();
QUrlQuery query(str.section(QLatin1Char('#'), -1, -1));
if (query.hasQueryItem(QLatin1String("access_token")))
{
d->accessToken = query.queryItemValue(QLatin1String("access_token"));
int seconds = query.queryItemValue(QLatin1String("expires_in")).toInt();
d->expiryTime = QDateTime::currentDateTime().addSecs(seconds);
writeSettings();
qDebug(DIGIKAM_WEBSERVICES_LOG) << "Access token received";
emit oneDriveLinkingSucceeded();
}
else
{
emit oneDriveLinkingFailed();
}
}
void ODTalker::slotLinkingFailed()
{
qCDebug(DIGIKAM_WEBSERVICES_LOG) << "LINK to Onedrive fail";
emit signalBusy(false);
}
void ODTalker::slotLinkingSucceeded()
{
if (d->accessToken.isEmpty())
{
qCDebug(DIGIKAM_WEBSERVICES_LOG) << "UNLINK to Onedrive";
emit signalBusy(false);
return;
}
qCDebug(DIGIKAM_WEBSERVICES_LOG) << "LINK to Onedrive";
- d->view->close();
+ if (d->browser)
+ {
+ d->browser->close();
+ }
+
emit signalLinkingSucceeded();
}
bool ODTalker::authenticated()
{
return (!d->accessToken.isEmpty());
}
void ODTalker::cancel()
{
if (d->reply)
{
d->reply->abort();
d->reply = nullptr;
}
emit signalBusy(false);
}
void ODTalker::createFolder(QString& path)
{
//path also has name of new folder so send path parameter accordingly
QString name = QUrl(path).fileName();
QString folderPath = QUrl(path).adjusted(QUrl::RemoveFilename |
QUrl::StripTrailingSlash).path();
QUrl url;
if (folderPath == QLatin1String("/"))
{
url = QUrl(QLatin1String("https://graph.microsoft.com/v1.0/me/drive/root/children"));
}
else
{
url = QUrl(QString::fromUtf8("https://graph.microsoft.com/v1.0/me/drive/root:/%1:/children").arg(folderPath));
}
QNetworkRequest netRequest(url);
netRequest.setHeader(QNetworkRequest::ContentTypeHeader, QLatin1String("application/json"));
netRequest.setRawHeader("Authorization", QString::fromLatin1("bearer %1").arg(d->accessToken).toUtf8());
QByteArray postData = QString::fromUtf8("{\"name\": \"%1\",\"folder\": {}}").arg(name).toUtf8();
d->reply = d->netMngr->post(netRequest, postData);
d->state = Private::OD_CREATEFOLDER;
emit signalBusy(true);
}
void ODTalker::getUserName()
{
QUrl url(QLatin1String("https://graph.microsoft.com/v1.0/me"));
QNetworkRequest netRequest(url);
netRequest.setRawHeader("Authorization", QString::fromLatin1("bearer %1").arg(d->accessToken).toUtf8());
netRequest.setHeader(QNetworkRequest::ContentTypeHeader, QLatin1String("application/json"));
d->reply = d->netMngr->get(netRequest);
d->state = Private::OD_USERNAME;
emit signalBusy(true);
}
/** Get list of folders by parsing json sent by onedrive
*/
void ODTalker::listFolders(const QString& folder)
{
QString nextFolder;
if (folder.isEmpty())
{
d->folderList.clear();
d->nextFolder.clear();
}
else
{
nextFolder = QLatin1Char(':') + folder + QLatin1Char(':');
}
QUrl url(QString::fromLatin1("https://graph.microsoft.com/v1.0/me/drive/root%1/"
"children?select=name,folder,path,parentReference").arg(nextFolder));
QNetworkRequest netRequest(url);
netRequest.setRawHeader("Authorization", QString::fromLatin1("bearer %1").arg(d->accessToken).toUtf8());
netRequest.setHeader(QNetworkRequest::ContentTypeHeader, QLatin1String("application/json"));
d->reply = d->netMngr->get(netRequest);
d->state = Private::OD_LISTFOLDERS;
emit signalBusy(true);
}
bool ODTalker::addPhoto(const QString& imgPath, const QString& uploadFolder, bool rescale, int maxDim, int imageQuality)
{
if (d->reply)
{
d->reply->abort();
d->reply = nullptr;
}
emit signalBusy(true);
ODMPForm form;
QString path = imgPath;
QMimeDatabase mimeDB;
if (mimeDB.mimeTypeForFile(imgPath).name().startsWith(QLatin1String("image/")))
{
QImage image = PreviewLoadThread::loadHighQualitySynchronously(imgPath).copyQImage();
if (image.isNull())
{
emit signalBusy(false);
return false;
}
path = WSToolUtils::makeTemporaryDir("onedrive").filePath(QFileInfo(imgPath)
.baseName().trimmed() + QLatin1String(".jpg"));
if (rescale && (image.width() > maxDim || image.height() > maxDim))
{
image = image.scaled(maxDim, maxDim, Qt::KeepAspectRatio, Qt::SmoothTransformation);
}
image.save(path, "JPEG", imageQuality);
DMetadata meta;
if (meta.load(imgPath))
{
meta.setItemDimensions(image.size());
meta.setItemOrientation(DMetadata::ORIENTATION_NORMAL);
meta.setMetadataWritingMode((int)DMetadata::WRITE_TO_FILE_ONLY);
meta.save(path, true);
}
}
if (!form.addFile(path))
{
emit signalBusy(false);
return false;
}
QString uploadPath = uploadFolder + QUrl(imgPath).fileName();
QUrl url(QString::fromLatin1("https://graph.microsoft.com/v1.0/me/drive/root:/%1:/content").arg(uploadPath));
QNetworkRequest netRequest(url);
netRequest.setHeader(QNetworkRequest::ContentTypeHeader, QLatin1String("application/octet-stream"));
netRequest.setRawHeader("Authorization", QString::fromLatin1("bearer {%1}").arg(d->accessToken).toUtf8());
d->reply = d->netMngr->put(netRequest, form.formData());
d->state = Private::OD_ADDPHOTO;
return true;
}
void ODTalker::slotFinished(QNetworkReply* reply)
{
if (reply != d->reply)
{
return;
}
d->reply = nullptr;
if (reply->error() != QNetworkReply::NoError)
{
if (d->state != Private::OD_CREATEFOLDER)
{
emit signalBusy(false);
QMessageBox::critical(QApplication::activeWindow(),
i18n("Error"), reply->errorString());
reply->deleteLater();
return;
}
}
QByteArray buffer = reply->readAll();
switch (d->state)
{
case Private::OD_LISTFOLDERS:
qCDebug(DIGIKAM_WEBSERVICES_LOG) << "In OD_LISTFOLDERS";
parseResponseListFolders(buffer);
break;
case Private::OD_CREATEFOLDER:
qCDebug(DIGIKAM_WEBSERVICES_LOG) << "In OD_CREATEFOLDER";
parseResponseCreateFolder(buffer);
break;
case Private::OD_ADDPHOTO:
qCDebug(DIGIKAM_WEBSERVICES_LOG) << "In OD_ADDPHOTO";
parseResponseAddPhoto(buffer);
break;
case Private::OD_USERNAME:
qCDebug(DIGIKAM_WEBSERVICES_LOG) << "In OD_USERNAME";
parseResponseUserName(buffer);
break;
default:
break;
}
reply->deleteLater();
}
void ODTalker::parseResponseAddPhoto(const QByteArray& data)
{
QJsonDocument doc = QJsonDocument::fromJson(data);
QJsonObject jsonObject = doc.object();
bool success = jsonObject.contains(QLatin1String("size"));
emit signalBusy(false);
if (!success)
{
emit signalAddPhotoFailed(i18n("Failed to upload photo"));
}
else
{
emit signalAddPhotoSucceeded();
}
}
void ODTalker::parseResponseUserName(const QByteArray& data)
{
QJsonDocument doc = QJsonDocument::fromJson(data);
QString name = doc.object()[QLatin1String("displayName")].toString();
emit signalBusy(false);
emit signalSetUserName(name);
}
void ODTalker::parseResponseListFolders(const QByteArray& data)
{
QJsonParseError err;
QJsonDocument doc = QJsonDocument::fromJson(data, &err);
if (err.error != QJsonParseError::NoError)
{
emit signalBusy(false);
emit signalListAlbumsFailed(i18n("Failed to list folders"));
return;
}
QJsonObject jsonObject = doc.object();
//qCDebug(DIGIKAM_WEBSERVICES_LOG) << "Json: " << doc;
QJsonArray jsonArray = jsonObject[QLatin1String("value")].toArray();
if (d->folderList.isEmpty())
{
d->folderList.append(qMakePair(QLatin1String(""), QLatin1String("root")));
}
foreach (const QJsonValue& value, jsonArray)
{
QString path;
QString listName;
QString folderPath;
QString folderName;
QJsonObject folder;
QJsonObject parent;
QJsonObject obj = value.toObject();
folder = obj[QLatin1String("folder")].toObject();
parent = obj[QLatin1String("parentReference")].toObject();
if (!folder.isEmpty())
{
folderPath = parent[QLatin1String("path")].toString();
folderName = obj[QLatin1String("name")].toString();
path = folderPath.section(QLatin1String("root:"), -1, -1) +
QLatin1Char('/') + folderName;
path = QUrl(path).toString(QUrl::FullyDecoded);
listName = path.section(QLatin1Char('/'), 1);
d->folderList.append(qMakePair(path, listName));
if (folder[QLatin1String("childCount")].toInt() > 0)
{
d->nextFolder << path;
}
}
}
if (!d->nextFolder.isEmpty())
{
listFolders(d->nextFolder.takeLast());
}
else
{
std::sort(d->folderList.begin(), d->folderList.end());
emit signalBusy(false);
emit signalListAlbumsDone(d->folderList);
}
}
void ODTalker::parseResponseCreateFolder(const QByteArray& data)
{
QJsonDocument doc = QJsonDocument::fromJson(data);
QJsonObject jsonObject = doc.object();
bool fail = jsonObject.contains(QLatin1String("error"));
emit signalBusy(false);
if (fail)
{
QJsonParseError err;
QJsonDocument doc = QJsonDocument::fromJson(data, &err);
emit signalCreateFolderFailed(jsonObject[QLatin1String("error_summary")].toString());
}
else
{
emit signalCreateFolderSucceeded();
}
}
void ODTalker::writeSettings()
{
d->settings->beginGroup(d->serviceName);
d->settings->setValue(d->serviceTime, d->expiryTime);
d->settings->setValue(d->serviceKey, d->accessToken);
d->settings->endGroup();
}
void ODTalker::readSettings()
{
d->settings->beginGroup(d->serviceName);
d->expiryTime = d->settings->value(d->serviceTime).toDateTime();
d->accessToken = d->settings->value(d->serviceKey).toString();
d->settings->endGroup();
if (d->accessToken.isEmpty())
{
qCDebug(DIGIKAM_WEBSERVICES_LOG) << "Linking...";
link();
}
else if (QDateTime::currentDateTime() > d->expiryTime)
{
qCDebug(DIGIKAM_WEBSERVICES_LOG) << "Access token has expired";
d->accessToken = QString();
link();
}
else
{
qCDebug(DIGIKAM_WEBSERVICES_LOG) << "Already Linked";
emit oneDriveLinkingSucceeded();
}
}
} // namespace DigikamGenericOneDrivePlugin
diff --git a/core/dplugins/generic/webservices/pinterest/ptalker.cpp b/core/dplugins/generic/webservices/pinterest/ptalker.cpp
index dcfc3add40..7fffb0b377 100644
--- a/core/dplugins/generic/webservices/pinterest/ptalker.cpp
+++ b/core/dplugins/generic/webservices/pinterest/ptalker.cpp
@@ -1,645 +1,623 @@
/* ============================================================
*
* This file is a part of digiKam project
* https://www.digikam.org
*
* Date : 2018-05-20
* Description : a tool to export images to Pinterest web service
*
* Copyright (C) 2018 by Tarek Talaat
*
* 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 "ptalker.h"
// Qt includes
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
-#include
#include
// KDE includes
#include
#include
// Local includes
-#include "digikam_config.h"
#include "digikam_debug.h"
#include "digikam_version.h"
#include "wstoolutils.h"
#include "pwindow.h"
#include "pitem.h"
+#include "webbrowserdlg.h"
#include "previewloadthread.h"
-#ifdef HAVE_QWEBENGINE
-# include "webwidget_qwebengine.h"
-#else
-# include "webwidget.h"
-#endif
-
namespace DigikamGenericPinterestPlugin
{
class Q_DECL_HIDDEN PTalker::Private
{
public:
enum State
{
P_USERNAME = 0,
P_LISTBOARDS,
P_CREATEBOARD,
P_ADDPIN,
P_ACCESSTOKEN
};
public:
explicit Private()
+ : parent(nullptr),
+ netMngr(nullptr),
+ reply(nullptr),
+ settings(nullptr),
+ state(P_USERNAME),
+ browser(nullptr)
{
clientId = QLatin1String("4983380570301022071");
clientSecret = QLatin1String("2a698db679125930d922a2dfb897e16b668a67c6f614593636e83fc3d8d9b47d");
authUrl = QLatin1String("https://api.pinterest.com/oauth/");
tokenUrl = QLatin1String("https://api.pinterest.com/v1/oauth/token");
redirectUrl = QLatin1String("https://login.live.com/oauth20_desktop.srf");
-
scope = QLatin1String("read_public,write_public");
-
serviceName = QLatin1String("Pinterest");
serviceKey = QLatin1String("access_token");
-
- state = P_USERNAME;
-
- parent = nullptr;
- netMngr = nullptr;
- reply = nullptr;
- settings = nullptr;
- view = nullptr;
}
public:
QString clientId;
QString clientSecret;
QString authUrl;
QString tokenUrl;
QString redirectUrl;
QString accessToken;
QString scope;
QString userName;
QString serviceName;
QString serviceKey;
QWidget* parent;
QNetworkAccessManager* netMngr;
QNetworkReply* reply;
QSettings* settings;
State state;
DMetadata meta;
QMap urlParametersMap;
- WebWidget* view;
+ WebBrowserDlg* browser;
};
PTalker::PTalker(QWidget* const parent)
: d(new Private)
{
d->parent = parent;
d->netMngr = new QNetworkAccessManager(this);
- d->view = new WebWidget(d->parent);
- d->view->resize(800, 600);
-
d->settings = WSToolUtils::getOauthSettings(this);
-#ifndef HAVE_QWEBENGINE
- d->view->settings()->setAttribute(QWebSettings::LocalStorageEnabled, true);
-#endif
-
connect(d->netMngr, SIGNAL(finished(QNetworkReply*)),
this, SLOT(slotFinished(QNetworkReply*)));
connect(this, SIGNAL(pinterestLinkingFailed()),
this, SLOT(slotLinkingFailed()));
connect(this, SIGNAL(pinterestLinkingSucceeded()),
this, SLOT(slotLinkingSucceeded()));
-
- connect(d->view, SIGNAL(urlChanged(QUrl)),
- this, SLOT(slotCatchUrl(QUrl)));
-
- connect(d->view, SIGNAL(closeView(bool)),
- this, SIGNAL(signalBusy(bool)));
}
PTalker::~PTalker()
{
if (d->reply)
{
d->reply->abort();
}
WSToolUtils::removeTemporaryDir("pinterest");
delete d;
}
void PTalker::link()
{
emit signalBusy(true);
QUrl url(d->authUrl);
QUrlQuery query(url);
query.addQueryItem(QLatin1String("client_id"), d->clientId);
query.addQueryItem(QLatin1String("scope"), d->scope);
query.addQueryItem(QLatin1String("redirect_uri"), d->redirectUrl);
query.addQueryItem(QLatin1String("response_type"), QLatin1String("code"));
url.setQuery(query);
- d->view->setWindowFlags(Qt::Dialog);
- d->view->load(url);
- d->view->show();
+ delete d->browser;
+ d->browser = new WebBrowserDlg(url, d->parent, true);
+ d->browser->setModal(true);
+
+ connect(d->browser, SIGNAL(urlChanged(QUrl)),
+ this, SLOT(slotCatchUrl(QUrl)));
+
+ connect(d->browser, SIGNAL(closeView(bool)),
+ this, SIGNAL(signalBusy(bool)));
+
+ d->browser->show();
}
void PTalker::unLink()
{
d->accessToken = QString();
d->settings->beginGroup(d->serviceName);
d->settings->remove(QString());
d->settings->endGroup();
-#ifdef HAVE_QWEBENGINE
- d->view->page()->profile()->cookieStore()->deleteAllCookies();
-#else
- d->view->page()->networkAccessManager()->setCookieJar(new QNetworkCookieJar());
-#endif
-
emit pinterestLinkingSucceeded();
}
void PTalker::slotCatchUrl(const QUrl& url)
{
d->urlParametersMap = ParseUrlParameters(url.toString());
QString code = d->urlParametersMap.value(QLatin1String("code"));
qCDebug(DIGIKAM_WEBSERVICES_LOG) << "Received URL from webview in link function: " << url ;
if (!code.isEmpty())
{
qCDebug(DIGIKAM_WEBSERVICES_LOG) << "CODE Received";
- d->view->close();
+ d->browser->close();
getToken(code);
emit signalBusy(false);
}
}
void PTalker::getToken(const QString& code)
{
qCDebug(DIGIKAM_WEBSERVICES_LOG) << "Code: " << code;
QUrl url(d->tokenUrl);
QUrlQuery query(url);
query.addQueryItem(QLatin1String("grant_type"), QLatin1String("authorization_code"));
query.addQueryItem(QLatin1String("client_id"), d->clientId);
query.addQueryItem(QLatin1String("client_secret"), d->clientSecret);
query.addQueryItem(QLatin1String("code"), code);
url.setQuery(query);
qCDebug(DIGIKAM_WEBSERVICES_LOG) << "Token Request URL: " << url.toString();
QNetworkRequest netRequest(url);
netRequest.setHeader(QNetworkRequest::ContentTypeHeader, QLatin1String("application/x-www-form-urlencoded"));
netRequest.setRawHeader("Accept", "application/json");
d->reply = d->netMngr->post(netRequest, QByteArray());
d->state = Private::P_ACCESSTOKEN;
}
QMap PTalker::ParseUrlParameters(const QString& url)
{
QMap urlParameters;
if (url.indexOf(QLatin1Char('?')) == -1)
{
return urlParameters;
}
QString tmp = url.right(url.length()-url.indexOf(QLatin1Char('?')) - 1);
QStringList paramlist = tmp.split(QLatin1Char('&'));
for (int i = 0 ; i < paramlist.count() ; ++i)
{
QStringList paramarg = paramlist.at(i).split(QLatin1Char('='));
if (paramarg.count() == 2)
{
urlParameters.insert(paramarg.at(0), paramarg.at(1));
}
}
return urlParameters;
}
void PTalker::slotLinkingFailed()
{
qCDebug(DIGIKAM_WEBSERVICES_LOG) << "LINK to Pinterest fail";
emit signalBusy(false);
}
void PTalker::slotLinkingSucceeded()
{
if (d->accessToken.isEmpty())
{
qCDebug(DIGIKAM_WEBSERVICES_LOG) << "UNLINK to Pinterest ok";
emit signalBusy(false);
return;
}
qCDebug(DIGIKAM_WEBSERVICES_LOG) << "LINK to Pinterest ok";
writeSettings();
emit signalLinkingSucceeded();
}
bool PTalker::authenticated()
{
return (!d->accessToken.isEmpty());
}
void PTalker::cancel()
{
if (d->reply)
{
d->reply->abort();
d->reply = nullptr;
}
emit signalBusy(false);
}
void PTalker::createBoard(QString& boardName)
{
QUrl url(QLatin1String("https://api.pinterest.com/v1/boards/"));
QNetworkRequest netRequest(url);
netRequest.setHeader(QNetworkRequest::ContentTypeHeader, QLatin1String("application/json"));
netRequest.setRawHeader("Authorization", QString::fromLatin1("Bearer %1").arg(d->accessToken).toUtf8());
QByteArray postData = QString::fromUtf8("{\"name\": \"%1\"}").arg(boardName).toUtf8();
/*
qCDebug(DIGIKAM_WEBSERVICES_LOG) << "createBoard:" << postData;
*/
d->reply = d->netMngr->post(netRequest, postData);
d->state = Private::P_CREATEBOARD;
emit signalBusy(true);
}
void PTalker::getUserName()
{
QUrl url(QLatin1String("https://api.pinterest.com/v1/me/?fields=username"));
QNetworkRequest netRequest(url);
netRequest.setRawHeader("Authorization", QString::fromLatin1("Bearer %1").arg(d->accessToken).toUtf8());
d->reply = d->netMngr->get(netRequest);
d->state = Private::P_USERNAME;
emit signalBusy(true);
}
/**
* Get list of boards by parsing json sent by pinterest
*/
void PTalker::listBoards(const QString& /*path*/)
{
QUrl url(QLatin1String("https://api.pinterest.com/v1/me/boards/"));;
QNetworkRequest netRequest(url);
netRequest.setRawHeader("Authorization", QString::fromLatin1("Bearer %1").arg(d->accessToken).toUtf8());
//netRequest.setHeader(QNetworkRequest::ContentTypeHeader, QLatin1String("application/json"));
d->reply = d->netMngr->get(netRequest);
d->state = Private::P_LISTBOARDS;
emit signalBusy(true);
}
bool PTalker::addPin(const QString& imgPath,
const QString& uploadBoard,
bool rescale,
int maxDim,
int imageQuality)
{
if (d->reply)
{
d->reply->abort();
d->reply = nullptr;
}
emit signalBusy(true);
QImage image = PreviewLoadThread::loadHighQualitySynchronously(imgPath).copyQImage();
if (image.isNull())
{
emit signalBusy(false);
return false;
}
QString path = WSToolUtils::makeTemporaryDir("pinterest").filePath(QFileInfo(imgPath)
.baseName().trimmed() + QLatin1String(".jpg"));
if (rescale && (image.width() > maxDim || image.height() > maxDim))
{
image = image.scaled(maxDim, maxDim, Qt::KeepAspectRatio, Qt::SmoothTransformation);
}
image.save(path, "JPEG", imageQuality);
if (d->meta.load(imgPath))
{
d->meta.setItemDimensions(image.size());
d->meta.setItemOrientation(DMetadata::ORIENTATION_NORMAL);
d->meta.setMetadataWritingMode((int)DMetadata::WRITE_TO_FILE_ONLY);
d->meta.save(path, true);
}
QString boardParam = d->userName + QLatin1Char('/') + uploadBoard;
QUrl url(QString::fromLatin1("https://api.pinterest.com/v1/pins/?access_token=%1").arg(d->accessToken));
QHttpMultiPart* const multiPart = new QHttpMultiPart(QHttpMultiPart::FormDataType);
// Board Section
QHttpPart board;
QString boardHeader = QLatin1String("form-data; name=\"board\"") ;
board.setHeader(QNetworkRequest::ContentDispositionHeader, boardHeader);
QByteArray postData = boardParam.toUtf8();
board.setBody(postData);
multiPart->append(board);
// Note section
QHttpPart note;
QString noteHeader = QLatin1String("form-data; name=\"note\"") ;
note.setHeader(QNetworkRequest::ContentDispositionHeader, noteHeader);
postData = QByteArray();
note.setBody(postData);
multiPart->append(note);
// image section
QFile* const file = new QFile(imgPath);
if (!file)
{
return false;
}
if (!file->open(QIODevice::ReadOnly))
{
return false;
}
QHttpPart imagePart;
QString imagePartHeader = QLatin1String("form-data; name=\"image\"; filename=\"") +
QFileInfo(imgPath).fileName() + QLatin1Char('"');
imagePart.setHeader(QNetworkRequest::ContentDispositionHeader, imagePartHeader);
imagePart.setHeader(QNetworkRequest::ContentTypeHeader, QLatin1String("image/jpeg"));
imagePart.setBodyDevice(file);
multiPart->append(imagePart);
QString content = QLatin1String("multipart/form-data;boundary=") + QString::fromUtf8(multiPart->boundary());
QNetworkRequest netRequest(url);
netRequest.setHeader(QNetworkRequest::ContentTypeHeader, content);
d->reply = d->netMngr->post(netRequest, multiPart);
// delete the multiPart and file with the reply
multiPart->setParent(d->reply);
d->state = Private::P_ADDPIN;
return true;
}
void PTalker::slotFinished(QNetworkReply* reply)
{
if (reply != d->reply)
{
return;
}
d->reply = nullptr;
if (reply->error() != QNetworkReply::NoError)
{
if (d->state != Private::P_CREATEBOARD)
{
emit signalBusy(false);
QMessageBox::critical(QApplication::activeWindow(),
i18n("Error"), reply->errorString());
/*
qCDebug(DIGIKAM_WEBSERVICES_LOG) << "Error content: " << QString(reply->readAll());
*/
reply->deleteLater();
return;
}
}
QByteArray buffer = reply->readAll();
/*
qCDebug(DIGIKAM_WEBSERVICES_LOG) << "BUFFER" << QString(buffer);
*/
switch (d->state)
{
case Private::P_LISTBOARDS:
qCDebug(DIGIKAM_WEBSERVICES_LOG) << "In P_LISTBOARDS";
parseResponseListBoards(buffer);
break;
case Private::P_CREATEBOARD:
qCDebug(DIGIKAM_WEBSERVICES_LOG) << "In P_CREATEBOARD";
parseResponseCreateBoard(buffer);
break;
case Private::P_ADDPIN:
qCDebug(DIGIKAM_WEBSERVICES_LOG) << "In P_ADDPIN";
parseResponseAddPin(buffer);
break;
case Private::P_USERNAME:
qCDebug(DIGIKAM_WEBSERVICES_LOG) << "In P_USERNAME";
parseResponseUserName(buffer);
break;
case Private::P_ACCESSTOKEN:
qCDebug(DIGIKAM_WEBSERVICES_LOG) << "In P_ACCESSTOKEN";
parseResponseAccessToken(buffer);
break;
default:
break;
}
reply->deleteLater();
}
void PTalker::parseResponseAccessToken(const QByteArray& data)
{
QJsonDocument doc = QJsonDocument::fromJson(data);
QJsonObject jsonObject = doc.object();
d->accessToken = jsonObject[QLatin1String("access_token")].toString();
if (!d->accessToken.isEmpty())
{
qDebug(DIGIKAM_WEBSERVICES_LOG) << "Access token Received: " << d->accessToken;
emit pinterestLinkingSucceeded();
}
else
{
emit pinterestLinkingFailed();
}
emit signalBusy(false);
}
void PTalker::parseResponseAddPin(const QByteArray& data)
{
QJsonDocument doc = QJsonDocument::fromJson(data);
QJsonObject jsonObject = doc.object()[QLatin1String("data")].toObject();
bool success = jsonObject.contains(QLatin1String("id"));
emit signalBusy(false);
if (!success)
{
emit signalAddPinFailed(i18n("Failed to upload Pin"));
}
else
{
emit signalAddPinSucceeded();
}
}
void PTalker::parseResponseUserName(const QByteArray& data)
{
QJsonDocument doc = QJsonDocument::fromJson(data);
QJsonObject jsonObject = doc.object()[QLatin1String("data")].toObject();
d->userName = jsonObject[QLatin1String("username")].toString();
emit signalBusy(false);
emit signalSetUserName(d->userName);
}
void PTalker::parseResponseListBoards(const QByteArray& data)
{
QJsonParseError err;
QJsonDocument doc = QJsonDocument::fromJson(data, &err);
if (err.error != QJsonParseError::NoError)
{
emit signalBusy(false);
emit signalListBoardsFailed(i18n("Failed to list boards"));
return;
}
QJsonObject jsonObject = doc.object();
/*
qCDebug(DIGIKAM_WEBSERVICES_LOG) << "Json Listing Boards : " << doc;
*/
QJsonArray jsonArray = jsonObject[QLatin1String("data")].toArray();
QList > list;
QString boardID;
QString boardName;
foreach (const QJsonValue& value, jsonArray)
{
QString boardID;
QString boardName;
QJsonObject obj = value.toObject();
boardID = obj[QLatin1String("id")].toString();
boardName = obj[QLatin1String("name")].toString();
list.append(qMakePair(boardID, boardName));
}
emit signalBusy(false);
emit signalListBoardsDone(list);
}
void PTalker::parseResponseCreateBoard(const QByteArray& data)
{
QJsonDocument doc = QJsonDocument::fromJson(data);
QJsonObject jsonObject = doc.object();
bool fail = jsonObject.contains(QLatin1String("error"));
emit signalBusy(false);
if (fail)
{
QJsonParseError err;
QJsonDocument doc = QJsonDocument::fromJson(data, &err);
emit signalCreateBoardFailed(jsonObject[QLatin1String("error_summary")].toString());
}
else
{
emit signalCreateBoardSucceeded();
}
}
void PTalker::writeSettings()
{
d->settings->beginGroup(d->serviceName);
d->settings->setValue(d->serviceKey, d->accessToken);
d->settings->endGroup();
}
void PTalker::readSettings()
{
d->settings->beginGroup(d->serviceName);
d->accessToken = d->settings->value(d->serviceKey).toString();
d->settings->endGroup();
if (d->accessToken.isEmpty())
{
qCDebug(DIGIKAM_WEBSERVICES_LOG) << "Linking...";
link();
}
else
{
qCDebug(DIGIKAM_WEBSERVICES_LOG) << "Already Linked";
emit pinterestLinkingSucceeded();
}
}
} // namespace DigikamGenericPinterestPlugin
diff --git a/core/libs/dialogs/webbrowserdlg.cpp b/core/libs/dialogs/webbrowserdlg.cpp
index 19ce380277..4d9eaa22b7 100644
--- a/core/libs/dialogs/webbrowserdlg.cpp
+++ b/core/libs/dialogs/webbrowserdlg.cpp
@@ -1,262 +1,274 @@
/* ============================================================
*
* 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
+#include
#ifdef HAVE_QWEBENGINE
# include
# include
+# include
+# include
#else
# include
#endif
// KDE includes
#include
-#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)
+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"));
- d->toolbar->addAction(deskweb);
+
+ 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/dialogs/webbrowserdlg.h b/core/libs/dialogs/webbrowserdlg.h
index 896f29f9aa..499dd0ec39 100644
--- a/core/libs/dialogs/webbrowserdlg.h
+++ b/core/libs/dialogs/webbrowserdlg.h
@@ -1,76 +1,83 @@
/* ============================================================
*
* 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.
*
* ============================================================ */
#ifndef DIGIKAM_WEB_BROWSER_DLG_H
#define DIGIKAM_WEB_BROWSER_DLG_H
// Qt include
#include
#include
#include
#include
#include
// Local includes
#include "digikam_export.h"
namespace Digikam
{
class SearchTextSettings;
class DIGIKAM_EXPORT WebBrowserDlg : public QDialog
{
Q_OBJECT
public:
- explicit WebBrowserDlg(const QUrl& url, QWidget* const parent = nullptr);
+ explicit WebBrowserDlg(const QUrl& url,
+ QWidget* const parent,
+ bool hideDeskBrowser = false);
~WebBrowserDlg();
+Q_SIGNALS:
+
+ void urlChanged(const QUrl& url);
+ void closeView(bool val);
+
private Q_SLOTS:
void slotUrlChanged(const QUrl&);
void slotLoadingStarted();
void slotLoadingFinished(bool);
void slotIconChanged(const QIcon&);
void slotTitleChanged(const QString&);
void slotSearchTextChanged(const SearchTextSettings&);
void slotGoHome();
void slotDesktopWebBrowser();
protected:
void closeEvent(QCloseEvent*) override;
private:
class Private;
Private* const d;
};
} // namespace Digikam
#endif // DIGIKAM_WEB_BROWSER_DLG_H
diff --git a/core/libs/dimg/CMakeLists.txt b/core/libs/dimg/CMakeLists.txt
index 51d6ccacf5..c7d0b6732a 100644
--- a/core/libs/dimg/CMakeLists.txt
+++ b/core/libs/dimg/CMakeLists.txt
@@ -1,222 +1,221 @@
#
# 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.
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
- drawdecoding.cpp
- dcolor.cpp
- dcolorcomposer.cpp
+ color/dcolor.cpp
+ color/dcolorcomposer.cpp
history/dimagehistory.cpp
history/filteraction.cpp
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/rawprocessingfilter.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
)
# ==================================================================================================
# 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
)
include_directories(${LENSFUN_INCLUDE_DIRS})
endif()
if(Eigen3_FOUND)
set(libdimgfilters_SRCS
${libdimgfilters_SRCS}
filters/sharp/refocusfilter.cpp
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
)
# JPEG2000 support
if(Jasper_FOUND)
set(libdimgloaders_SRCS
${libdimgloaders_SRCS}
loaders/jp2ksettings.cpp
)
endif()
# HEIF support
if(X265_FOUND)
set(libdimgloaders_SRCS
${libdimgloaders_SRCS}
loaders/heifsettings.cpp
)
endif()
include_directories(
$
$
$
$
$
$
)
if(X11_FOUND)
include_directories($)
endif()
add_library(dimg_src OBJECT
${libdimgloaders_SRCS}
${libdimgfilters_SRCS}
${libdimg_SRCS}
- ${libdlib_SRCS}
)
diff --git a/core/libs/dimg/dcolor.cpp b/core/libs/dimg/color/dcolor.cpp
similarity index 100%
rename from core/libs/dimg/dcolor.cpp
rename to core/libs/dimg/color/dcolor.cpp
diff --git a/core/libs/dimg/dcolor.h b/core/libs/dimg/color/dcolor.h
similarity index 100%
rename from core/libs/dimg/dcolor.h
rename to core/libs/dimg/color/dcolor.h
diff --git a/core/libs/dimg/dcolorblend.h b/core/libs/dimg/color/dcolorblend.h
similarity index 100%
rename from core/libs/dimg/dcolorblend.h
rename to core/libs/dimg/color/dcolorblend.h
diff --git a/core/libs/dimg/dcolorcomposer.cpp b/core/libs/dimg/color/dcolorcomposer.cpp
similarity index 100%
rename from core/libs/dimg/dcolorcomposer.cpp
rename to core/libs/dimg/color/dcolorcomposer.cpp
diff --git a/core/libs/dimg/dcolorcomposer.h b/core/libs/dimg/color/dcolorcomposer.h
similarity index 100%
rename from core/libs/dimg/dcolorcomposer.h
rename to core/libs/dimg/color/dcolorcomposer.h
diff --git a/core/libs/dimg/dcolorpixelaccess.h b/core/libs/dimg/color/dcolorpixelaccess.h
similarity index 100%
rename from core/libs/dimg/dcolorpixelaccess.h
rename to core/libs/dimg/color/dcolorpixelaccess.h
diff --git a/core/libs/dimg/filters/README b/core/libs/dimg/filters/README
index e2ce0598b6..53ddf41a52 100644
--- a/core/libs/dimg/filters/README
+++ b/core/libs/dimg/filters/README
@@ -1,20 +1,21 @@
auto : Auto Color Correction.
bw : Black and White.
bcg : Brighness / Contrast / Gamma.
cb : Color Balance Correction.
curves : Histogram Curves Correction.
decorate : Decorative Effects.
fx : Special Effects.
greystoration : CImg library interface for Greystoration filter.
hsl : Hue / Saturation / Lightness.
icc : Color Management.
imgqsort : Image Quality Sorter.
levels : Histogram Levels Correction.
lens : Misc lens correction filters.
lut3d : 3D LUT color filters.
lc : Local Contrast (LDR Tonemapping).
nr : Wavelets Noise Reduction.
+raw : RAW image processing
sharp : Sharp / Unsharp Mask / Refocus
transform : Change Image Geometry.
wb : White Balance.
redeye : Red-Eye Detection and Correction.
diff --git a/core/libs/dimg/filters/auto/autoexpofilter.h b/core/libs/dimg/filters/auto/autoexpofilter.h
index 957dae3717..329713de78 100644
--- a/core/libs/dimg/filters/auto/autoexpofilter.h
+++ b/core/libs/dimg/filters/auto/autoexpofilter.h
@@ -1,85 +1,85 @@
/* ============================================================
*
* This file is a part of digiKam project
* https://www.digikam.org
*
* Date : 2010-15-02
* Description : auto exposure image filter.
*
* Copyright (C) 2010-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_AUTO_EXPO_FILTER_H
#define DIGIKAM_AUTO_EXPO_FILTER_H
// Local includes
#include "digikam_export.h"
#include "wbfilter.h"
#include "digikam_globals.h"
#include "filteraction.h"
namespace Digikam
{
class DImg;
class DIGIKAM_EXPORT AutoExpoFilter : public WBFilter
{
public:
explicit AutoExpoFilter(QObject* const parent = nullptr);
AutoExpoFilter(DImg* const orgImage, const DImg* const refImage, QObject* const parent=nullptr);
virtual ~AutoExpoFilter();
static QString FilterIdentifier()
{
return QLatin1String("digikam:AutoExpoFilter");
}
static QList SupportedVersions()
{
return QList() << 1;
}
static int CurrentVersion()
{
return 1;
}
static QString DisplayableName();
- virtual QString filterIdentifier() const override
+ virtual QString filterIdentifier() const override
{
return FilterIdentifier();
}
- virtual FilterAction filterAction() override;
+ virtual FilterAction filterAction() override;
- void readParameters(const FilterAction& action) override;
+ void readParameters(const FilterAction& action) override;
private:
- void filterImage() override;
+ void filterImage() override;
private:
DImg m_refImage;
};
} // namespace Digikam
#endif // DIGIKAM_AUTO_EXPO_FILTER_H
diff --git a/core/libs/dimg/filters/auto/autolevelsfilter.cpp b/core/libs/dimg/filters/auto/autolevelsfilter.cpp
index bc70e8cdf8..c6ef03acd5 100644
--- a/core/libs/dimg/filters/auto/autolevelsfilter.cpp
+++ b/core/libs/dimg/filters/auto/autolevelsfilter.cpp
@@ -1,189 +1,195 @@
/* ============================================================
*
* This file is a part of digiKam project
* https://www.digikam.org
*
* Date : 2005-24-01
* Description : auto levels image filter.
*
* 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.
*
* ============================================================ */
#include "autolevelsfilter.h"
// C++ includes
#include
#include
// KDE includes
#include
// Local includes
#include "dimg.h"
#include "digikam_debug.h"
#include "imagehistogram.h"
#include "imagelevels.h"
namespace Digikam
{
AutoLevelsFilter::AutoLevelsFilter(QObject* const parent)
: DImgThreadedFilter(parent)
{
initFilter();
}
AutoLevelsFilter::AutoLevelsFilter(DImg* const orgImage, const DImg* const refImage, QObject* const parent)
: DImgThreadedFilter(orgImage, parent, QLatin1String("AutoLevelsFilter")),
m_refImage(*refImage)
{
initFilter();
}
AutoLevelsFilter::~AutoLevelsFilter()
{
cancelFilter();
}
QString AutoLevelsFilter::DisplayableName()
{
return QString::fromUtf8(I18N_NOOP("Auto Levels"));
}
void AutoLevelsFilter::filterImage()
{
if (m_refImage.isNull())
{
m_refImage = m_orgImage;
}
autoLevelsCorrectionImage();
m_destImage = m_orgImage;
}
/**
* Performs histogram auto correction of levels.
* This method maximizes the tonal range in the Red,
* Green, and Blue channels. It search the image shadow and highlight
* limit values and adjust the Red, Green, and Blue channels
* to a full histogram range.
*/
void AutoLevelsFilter::autoLevelsCorrectionImage()
{
if (m_orgImage.sixteenBit() != m_refImage.sixteenBit())
{
qCDebug(DIGIKAM_DIMG_LOG) << "Ref. image and Org. has different bits depth";
return;
}
uchar* data = m_orgImage.bits();
int w = m_orgImage.width();
int h = m_orgImage.height();
bool sixteenBit = m_orgImage.sixteenBit();
QScopedArrayPointer desData;
QScopedPointer histogram;
QScopedPointer levels;
postProgress(10);
int sizeSixteenBit = w * h * 8;
int sizeEightBit = w * h * 4;
// Create the new empty destination image data space.
+
if (runningFlag())
{
if (sixteenBit)
{
desData.reset(new uchar[sizeSixteenBit]);
}
else
{
desData.reset(new uchar[sizeEightBit]);
}
postProgress(20);
}
// Create an histogram of the reference image.
+
if (runningFlag())
{
histogram.reset(new ImageHistogram(m_refImage));
histogram->calculate();
postProgress(30);
}
// Create an empty instance of levels to use.
+
if (runningFlag())
{
levels.reset(new ImageLevels(sixteenBit));
postProgress(40);
}
// Initialize an auto levels correction of the histogram.
+
if (runningFlag())
{
levels->levelsAuto(histogram.data());
postProgress(50);
}
// Calculate the LUT to apply on the image.
+
if (runningFlag())
{
levels->levelsLutSetup(AlphaChannel);
postProgress(60);
}
// Apply the lut to the image.
+
if (runningFlag())
{
levels->levelsLutProcess(data, desData.data(), w, h);
postProgress(70);
}
if (runningFlag())
{
if (sixteenBit)
{
memcpy(data, desData.data(), sizeSixteenBit);
}
else
{
memcpy(data, desData.data(), sizeEightBit);
}
postProgress(80);
}
if (runningFlag())
{
postProgress(90);
}
}
FilterAction AutoLevelsFilter::filterAction()
{
return DefaultFilterAction();
}
void AutoLevelsFilter::readParameters(const FilterAction& /*action*/)
{
return;
}
} // namespace Digikam
diff --git a/core/libs/dimg/filters/auto/autolevelsfilter.h b/core/libs/dimg/filters/auto/autolevelsfilter.h
index cad175ba17..65d38b5028 100644
--- a/core/libs/dimg/filters/auto/autolevelsfilter.h
+++ b/core/libs/dimg/filters/auto/autolevelsfilter.h
@@ -1,84 +1,85 @@
/* ============================================================
*
* This file is a part of digiKam project
* https://www.digikam.org
*
* Date : 2005-24-01
* Description : auto levels image filter.
*
* 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_AUTO_LEVELS_FILTER_H
#define DIGIKAM_AUTO_LEVELS_FILTER_H
// Local includes
#include "digikam_export.h"
#include "dimgthreadedfilter.h"
#include "digikam_globals.h"
namespace Digikam
{
class DImg;
class DIGIKAM_EXPORT AutoLevelsFilter : public DImgThreadedFilter
{
public:
+
explicit AutoLevelsFilter(QObject* const parent = nullptr);
AutoLevelsFilter(DImg* const orgImage, const DImg* const refImage, QObject* const parent=nullptr);
virtual ~AutoLevelsFilter();
static QString FilterIdentifier()
{
return QLatin1String("digikam:AutoLevelsFilter");
}
static QList SupportedVersions()
{
return QList() << 1;
}
static int CurrentVersion()
{
return 1;
}
static QString DisplayableName();
- virtual QString filterIdentifier() const override
+ virtual QString filterIdentifier() const override
{
return FilterIdentifier();
}
- virtual FilterAction filterAction() override;
+ virtual FilterAction filterAction() override;
- void readParameters(const FilterAction& action) override;
+ void readParameters(const FilterAction& action) override;
private:
- void filterImage() override;
+ void filterImage() override;
void autoLevelsCorrectionImage();
private:
DImg m_refImage;
};
} // namespace Digikam
#endif // DIGIKAM_AUTO_LEVELS_FILTER_H
diff --git a/core/libs/dimg/filters/auto/equalizefilter.cpp b/core/libs/dimg/filters/auto/equalizefilter.cpp
index 38036f0493..ac30a712b6 100644
--- a/core/libs/dimg/filters/auto/equalizefilter.cpp
+++ b/core/libs/dimg/filters/auto/equalizefilter.cpp
@@ -1,262 +1,266 @@
/* ============================================================
*
* This file is a part of digiKam project
* https://www.digikam.org
*
* Date : 2005-24-01
* Description : equalize image filter.
*
* 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.
*
* ============================================================ */
#include "equalizefilter.h"
// C++ includes
#include
#include
// KDE includes
#include
// Local includes
#include "dimg.h"
#include "digikam_debug.h"
#include "imagehistogram.h"
namespace Digikam
{
EqualizeFilter::EqualizeFilter(QObject* const parent)
: DImgThreadedFilter(parent)
{
initFilter();
}
EqualizeFilter::EqualizeFilter(DImg* const orgImage, const DImg* const refImage, QObject* const parent)
: DImgThreadedFilter(orgImage, parent, QLatin1String("EqualizeFilter")),
m_refImage(*refImage)
{
initFilter();
}
EqualizeFilter::~EqualizeFilter()
{
}
QString EqualizeFilter::DisplayableName()
{
return QString::fromUtf8(I18N_NOOP("Auto Equalize"));
}
void EqualizeFilter::filterImage()
{
if (m_refImage.isNull())
{
m_refImage = m_orgImage;
}
equalizeImage();
m_destImage = m_orgImage;
}
/**
* Performs an histogram equalization of the image.
* this method adjusts the brightness of colors across the
* active image so that the histogram for the value channel
* is as nearly as possible flat, that is, so that each possible
* brightness value appears at about the same number of pixels
* as each other value. Sometimes Equalize works wonderfully at
* enhancing the contrasts in an image. Other times it gives
* garbage. It is a very powerful operation, which can either work
* miracles on an image or destroy it.
*/
void EqualizeFilter::equalizeImage()
{
if (m_orgImage.sixteenBit() != m_refImage.sixteenBit())
{
qCDebug(DIGIKAM_DIMG_LOG) << "Ref. image and Org. has different bits depth";
return;
}
struct double_packet high, low, intensity;
int i;
int progress;
// Create an histogram of the reference image.
+
QScopedPointer histogram(new ImageHistogram(m_refImage));
histogram->calculate();
// Memory allocation.
+
QScopedArrayPointer map(new double_packet[histogram->getHistogramSegments()]);
QScopedArrayPointer equalize_map(new int_packet[histogram->getHistogramSegments()]);
if (map.isNull() || equalize_map.isNull())
{
qCWarning(DIGIKAM_DIMG_LOG) << ("Unable to allocate memory!");
return;
}
// Integrate the histogram to get the equalization map.
for (i = 0 ; runningFlag() && (i < histogram->getHistogramSegments()) ; ++i)
{
intensity.red += histogram->getValue(RedChannel, i);
intensity.green += histogram->getValue(GreenChannel, i);
intensity.blue += histogram->getValue(BlueChannel, i);
intensity.alpha += histogram->getValue(AlphaChannel, i);
map[i] = intensity;
}
// Stretch the histogram.
low = map[0];
high = map[histogram->getHistogramSegments() - 1];
// TODO magic number 256
+
for (i = 0 ; runningFlag() && (i < histogram->getHistogramSegments()) ; ++i)
{
if (high.red != low.red)
equalize_map[i].red = (uint)(((256 * histogram->getHistogramSegments() - 1) *
(map[i].red - low.red)) / (high.red - low.red));
if (high.green != low.green)
equalize_map[i].green = (uint)(((256 * histogram->getHistogramSegments() - 1) *
(map[i].green - low.green)) / (high.green - low.green));
if (high.blue != low.blue)
equalize_map[i].blue = (uint)(((256 * histogram->getHistogramSegments() - 1) *
(map[i].blue - low.blue)) / (high.blue - low.blue));
if (high.alpha != low.alpha)
equalize_map[i].alpha = (uint)(((256 * histogram->getHistogramSegments() - 1) *
(map[i].alpha - low.alpha)) / (high.alpha - low.alpha));
}
uchar* data = m_orgImage.bits();
int w = m_orgImage.width();
int h = m_orgImage.height();
bool sixteenBit = m_orgImage.sixteenBit();
int size = w * h;
// Apply results to image.
// TODO magic number 257
+
if (!sixteenBit) // 8 bits image.
{
uchar red, green, blue, alpha;
uchar* ptr = data;
for (i = 0 ; runningFlag() && (i < size) ; ++i)
{
blue = ptr[0];
green = ptr[1];
red = ptr[2];
alpha = ptr[3];
if (low.red != high.red)
{
red = (equalize_map[red].red) / 257;
}
if (low.green != high.green)
{
green = (equalize_map[green].green) / 257;
}
if (low.blue != high.blue)
{
blue = (equalize_map[blue].blue) / 257;
}
if (low.alpha != high.alpha)
{
alpha = (equalize_map[alpha].alpha) / 257;
}
ptr[0] = blue;
ptr[1] = green;
ptr[2] = red;
ptr[3] = alpha;
ptr += 4;
progress = (int)(((double)i * 100.0) / size);
- if (progress % 5 == 0)
+ if ((progress % 5) == 0)
{
postProgress(progress);
}
}
}
else // 16 bits image.
{
unsigned short red, green, blue, alpha;
unsigned short* ptr = reinterpret_cast(data);
for (i = 0 ; runningFlag() && (i < size) ; ++i)
{
blue = ptr[0];
green = ptr[1];
red = ptr[2];
alpha = ptr[3];
if (low.red != high.red)
{
red = (equalize_map[red].red) / 257;
}
if (low.green != high.green)
{
green = (equalize_map[green].green) / 257;
}
if (low.blue != high.blue)
{
blue = (equalize_map[blue].blue) / 257;
}
if (low.alpha != high.alpha)
{
alpha = (equalize_map[alpha].alpha) / 257;
}
ptr[0] = blue;
ptr[1] = green;
ptr[2] = red;
ptr[3] = alpha;
ptr += 4;
progress = (int)(((double)i * 100.0) / size);
- if (progress % 5 == 0)
+ if ((progress % 5) == 0)
{
postProgress(progress);
}
}
}
}
FilterAction EqualizeFilter::filterAction()
{
return DefaultFilterAction();
}
void EqualizeFilter::readParameters(const FilterAction& /*action*/)
{
return;
}
} // namespace Digikam
diff --git a/core/libs/dimg/filters/auto/equalizefilter.h b/core/libs/dimg/filters/auto/equalizefilter.h
index 416b289b51..53efafeca5 100644
--- a/core/libs/dimg/filters/auto/equalizefilter.h
+++ b/core/libs/dimg/filters/auto/equalizefilter.h
@@ -1,117 +1,117 @@
/* ============================================================
*
* This file is a part of digiKam project
* https://www.digikam.org
*
* Date : 2005-24-01
* Description : equalize image filter.
*
* 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_EQUALIZE_FILTER_H
#define DIGIKAM_EQUALIZE_FILTER_H
// Local includes
#include "digikam_export.h"
#include "dimgthreadedfilter.h"
#include "digikam_globals.h"
namespace Digikam
{
class DImg;
class DIGIKAM_EXPORT EqualizeFilter : public DImgThreadedFilter
{
public:
explicit EqualizeFilter(QObject* const parent = nullptr);
EqualizeFilter(DImg* const orgImage, const DImg* const refImage, QObject* const parent=nullptr);
virtual ~EqualizeFilter();
static QString FilterIdentifier()
{
return QLatin1String("digikam:EqualizeFilter");
}
static QList SupportedVersions()
{
return QList() << 1;
}
static int CurrentVersion()
{
return 1;
}
static QString DisplayableName();
- virtual QString filterIdentifier() const override
+ virtual QString filterIdentifier() const override
{
return FilterIdentifier();
}
- virtual FilterAction filterAction() override;
+ virtual FilterAction filterAction() override;
- void readParameters(const FilterAction& action) override;
+ void readParameters(const FilterAction& action) override;
private:
- void filterImage() override;
+ void filterImage() override;
void equalizeImage();
private:
struct double_packet
{
double_packet()
: red(0.0),
green(0.0),
blue(0.0),
alpha(0.0)
{
}
double red;
double green;
double blue;
double alpha;
};
struct int_packet
{
int_packet()
: red(0),
green(0),
blue(0),
alpha(0)
{
}
unsigned int red;
unsigned int green;
unsigned int blue;
unsigned int alpha;
};
DImg m_refImage;
};
} // namespace Digikam
#endif // DIGIKAM_EQUALIZE_FILTER_H
diff --git a/core/libs/dimg/filters/auto/normalizefilter.cpp b/core/libs/dimg/filters/auto/normalizefilter.cpp
index b3067d05c7..d5f718d323 100644
--- a/core/libs/dimg/filters/auto/normalizefilter.cpp
+++ b/core/libs/dimg/filters/auto/normalizefilter.cpp
@@ -1,286 +1,287 @@
/* ============================================================
*
* This file is a part of digiKam project
* https://www.digikam.org
*
* Date : 2005-24-01
* Description : normalize image filter.
*
* 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.
*
* ============================================================ */
#include "normalizefilter.h"
// C++ includes
#include
#include
// KDE includes
#include
// Local includes
#include "dimg.h"
#include "digikam_debug.h"
namespace Digikam
{
NormalizeFilter::NormalizeFilter(QObject* const parent)
: DImgThreadedFilter(parent)
{
initFilter();
}
NormalizeFilter::NormalizeFilter(DImg* const orgImage, const DImg* const refImage, QObject* const parent)
: DImgThreadedFilter(orgImage, parent, QLatin1String("NormalizeFilter")),
m_refImage(*refImage)
{
initFilter();
}
NormalizeFilter::~NormalizeFilter()
{
cancelFilter();
}
QString NormalizeFilter::DisplayableName()
{
return QString::fromUtf8(I18N_NOOP("Auto Normalize"));
}
void NormalizeFilter::filterImage()
{
if (m_refImage.isNull())
{
m_refImage = m_orgImage;
}
normalizeImage();
m_destImage = m_orgImage;
}
/**
* This method scales brightness values across the active
* image so that the darkest point becomes black, and the
* brightest point becomes as bright as possible without
* altering its hue. This is often a magic fix for
* images that are dim or washed out.
*/
void NormalizeFilter::normalizeImage()
{
if (m_orgImage.sixteenBit() != m_refImage.sixteenBit())
{
qCDebug(DIGIKAM_DIMG_LOG) << "Ref. image and Org. has different bits depth";
return;
}
bool sixteenBit = m_orgImage.sixteenBit();
int segments = sixteenBit ? NUM_SEGMENTS_16BIT : NUM_SEGMENTS_8BIT;
// Memory allocation.
NormalizeParam param;
param.lut = new unsigned short[segments];
// Find min. and max. values.
param.min = segments - 1;
param.max = 0;
uint refSize = m_refImage.width() * m_refImage.height();
if (!sixteenBit) // 8 bits image.
{
uchar red, green, blue;
uchar* ptr = m_refImage.bits();
for (uint i = 0 ; runningFlag() && (i < refSize) ; ++i)
{
blue = ptr[0];
green = ptr[1];
red = ptr[2];
if (red < param.min)
{
param.min = red;
}
if (red > param.max)
{
param.max = red;
}
if (green < param.min)
{
param.min = green;
}
if (green > param.max)
{
param.max = green;
}
if (blue < param.min)
{
param.min = blue;
}
if (blue > param.max)
{
param.max = blue;
}
ptr += 4;
}
}
else // 16 bits image.
{
unsigned short red, green, blue;
unsigned short* ptr = reinterpret_cast(m_refImage.bits());
for (uint i = 0 ; runningFlag() && (i < refSize) ; ++i)
{
blue = ptr[0];
green = ptr[1];
red = ptr[2];
if (red < param.min)
{
param.min = red;
}
if (red > param.max)
{
param.max = red;
}
if (green < param.min)
{
param.min = green;
}
if (green > param.max)
{
param.max = green;
}
if (blue < param.min)
{
param.min = blue;
}
if (blue > param.max)
{
param.max = blue;
}
ptr += 4;
}
}
// Calculate LUT.
if (runningFlag())
{
unsigned short range = (unsigned short)(param.max - param.min);
if (range != 0)
{
for (int x = (int)param.min ; x <= (int)param.max ; ++x)
{
param.lut[x] = (unsigned short)((segments - 1) * (x - param.min) / range);
}
}
else
{
param.lut[(int)param.min] = (unsigned short)param.min;
}
}
uchar* data = m_orgImage.bits();
int w = m_orgImage.width();
int h = m_orgImage.height();
uint size = w * h;
// Apply LUT to image.
+
int progress = 0;
if (!sixteenBit) // 8 bits image.
{
uchar red, green, blue;
uchar* ptr = data;
for (uint i = 0 ; runningFlag() && (i < size) ; ++i)
{
blue = ptr[0];
green = ptr[1];
red = ptr[2];
ptr[0] = param.lut[blue];
ptr[1] = param.lut[green];
ptr[2] = param.lut[red];
- ptr += 4;
+ ptr += 4;
progress = (int)(((double)i * 100.0) / size);
- if (progress % 5 == 0)
+ if ((progress % 5) == 0)
{
postProgress(progress);
}
}
}
else // 16 bits image.
{
unsigned short red, green, blue;
unsigned short* ptr = reinterpret_cast(data);
for (uint i = 0 ; runningFlag() && (i < size) ; ++i)
{
blue = ptr[0];
green = ptr[1];
red = ptr[2];
ptr[0] = param.lut[blue];
ptr[1] = param.lut[green];
ptr[2] = param.lut[red];
- ptr += 4;
+ ptr += 4;
progress = (int)(((double)i * 100.0) / size);
- if (progress % 5 == 0)
+ if ((progress % 5) == 0)
{
postProgress(progress);
}
}
}
delete [] param.lut;
}
FilterAction NormalizeFilter::filterAction()
{
return DefaultFilterAction();
}
void NormalizeFilter::readParameters(const FilterAction& /*action*/)
{
return;
}
} // namespace Digikam
diff --git a/core/libs/dimg/filters/auto/normalizefilter.h b/core/libs/dimg/filters/auto/normalizefilter.h
index d5ef494bc9..2cc22a756b 100644
--- a/core/libs/dimg/filters/auto/normalizefilter.h
+++ b/core/libs/dimg/filters/auto/normalizefilter.h
@@ -1,92 +1,92 @@
/* ============================================================
*
* This file is a part of digiKam project
* https://www.digikam.org
*
* Date : 2005-24-01
* Description : normalize image filter.
*
* 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_NORMALIZE_FILTER_H
#define DIGIKAM_NORMALIZE_FILTER_H
// Local includes
#include "digikam_export.h"
#include "dimgthreadedfilter.h"
#include "digikam_globals.h"
namespace Digikam
{
class DImg;
class DIGIKAM_EXPORT NormalizeFilter : public DImgThreadedFilter
{
public:
explicit NormalizeFilter(QObject* const parent = nullptr);
NormalizeFilter(DImg* const orgImage, const DImg* const refImage, QObject* const parent=nullptr);
virtual ~NormalizeFilter();
static QString FilterIdentifier()
{
return QLatin1String("digikam:NormalizeFilter");
}
static QList SupportedVersions()
{
return QList() << 1;
}
static int CurrentVersion()
{
return 1;
}
static QString DisplayableName();
- virtual QString filterIdentifier() const override
+ virtual QString filterIdentifier() const override
{
return FilterIdentifier();
}
- virtual FilterAction filterAction() override;
+ virtual FilterAction filterAction() override;
- void readParameters(const FilterAction& action) override;
+ void readParameters(const FilterAction& action) override;
private:
- void filterImage() override;
+ void filterImage() override;
void normalizeImage();
private:
struct NormalizeParam
{
unsigned short* lut;
double min;
double max;
};
DImg m_refImage;
};
} // namespace Digikam
#endif // DIGIKAM_NORMALIZE_FILTER_H
diff --git a/core/libs/dimg/filters/auto/stretchfilter.cpp b/core/libs/dimg/filters/auto/stretchfilter.cpp
index 4e19e265e4..de5a8ae15a 100644
--- a/core/libs/dimg/filters/auto/stretchfilter.cpp
+++ b/core/libs/dimg/filters/auto/stretchfilter.cpp
@@ -1,461 +1,464 @@
/* ============================================================
*
* This file is a part of digiKam project
* https://www.digikam.org
*
* Date : 2005-24-01
* Description : stretch contrast image filter.
*
* 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.
*
* ============================================================ */
#include "stretchfilter.h"
// C++ includes
#include
#include
// KDE includes
#include
// Local includes
#include "dimg.h"
#include "digikam_debug.h"
#include "imagehistogram.h"
namespace Digikam
{
StretchFilter::StretchFilter(QObject* const parent)
: DImgThreadedFilter(parent)
{
initFilter();
}
StretchFilter::StretchFilter(DImg* const orgImage, const DImg* const refImage, QObject* const parent)
: DImgThreadedFilter(orgImage, parent, QLatin1String("StretchFilter")),
m_refImage(*refImage)
{
initFilter();
}
StretchFilter::~StretchFilter()
{
cancelFilter();
}
QString StretchFilter::DisplayableName()
{
return QString::fromUtf8(I18N_NOOP("Stretch Contrast"));
}
void StretchFilter::filterImage()
{
if (m_refImage.isNull())
{
m_refImage = m_orgImage;
}
stretchContrastImage();
m_destImage = m_orgImage;
}
/**
* Performs histogram normalization of the image. The algorithm normalizes
* the pixel values from an image for to span the full range
- * of color values. This is a contrast enhancement technique.
+ * of color values. This is a contrast enhancement technique.
*/
void StretchFilter::stretchContrastImage()
{
if (m_orgImage.sixteenBit() != m_refImage.sixteenBit())
{
qCDebug(DIGIKAM_DIMG_LOG) << "Ref. image and Org. has different bits depth";
return;
}
struct double_packet high, low, intensity;
long long number_pixels;
long i;
int progress;
unsigned long threshold_intensity;
// Create an histogram of the reference image.
+
QScopedPointer histogram(new ImageHistogram(m_refImage));
if (histogram.isNull())
{
qCWarning(DIGIKAM_DIMG_LOG) << ("Unable to allocate memory!");
return;
}
histogram->calculate();
// Memory allocation.
+
QScopedArrayPointer normalize_map(new int_packet[histogram->getHistogramSegments()]);
if (normalize_map.isNull())
{
qCWarning(DIGIKAM_DIMG_LOG) << ("Unable to allocate memory!");
return;
}
// Find the histogram boundaries by locating the 0.1 percent levels.
number_pixels = (long long)m_refImage.width() * (long long)m_refImage.height();
threshold_intensity = number_pixels / 1000;
// Red.
intensity = double_packet();
for (high.red = histogram->getMaxSegmentIndex() ; high.red != 0 ; --high.red)
{
intensity.red += histogram->getValue(RedChannel, (int)high.red);
if (intensity.red > threshold_intensity)
{
break;
}
}
if (low.red == high.red)
{
threshold_intensity = 0;
intensity = double_packet();
for (low.red = 0 ; low.red < histogram->getMaxSegmentIndex() ; ++low.red)
{
intensity.red += histogram->getValue(RedChannel, (int)low.red);
if (intensity.red > threshold_intensity)
{
break;
}
}
intensity = double_packet();
for (high.red = histogram->getMaxSegmentIndex() ; high.red != 0 ; --high.red)
{
intensity.red += histogram->getValue(RedChannel, (int)high.red);
if (intensity.red > threshold_intensity)
{
break;
}
}
}
// Green.
intensity = double_packet();
for (high.green = histogram->getMaxSegmentIndex() ; high.green != 0 ; --high.green)
{
intensity.green += histogram->getValue(GreenChannel, (int)high.green);
if (intensity.green > threshold_intensity)
{
break;
}
}
if (low.green == high.green)
{
threshold_intensity = 0;
intensity = double_packet();
for (low.green = 0 ; low.green < histogram->getMaxSegmentIndex() ; ++low.green)
{
intensity.green += histogram->getValue(GreenChannel, (int)low.green);
if (intensity.green > threshold_intensity)
{
break;
}
}
intensity = double_packet();
for (high.green = histogram->getMaxSegmentIndex() ; high.green != 0 ; --high.green)
{
intensity.green += histogram->getValue(GreenChannel, (int)high.green);
if (intensity.green > threshold_intensity)
{
break;
}
}
}
// Blue.
intensity = double_packet();
for (high.blue = histogram->getMaxSegmentIndex() ; high.blue != 0 ; --high.blue)
{
intensity.blue += histogram->getValue(BlueChannel, (int)high.blue);
if (intensity.blue > threshold_intensity)
{
break;
}
}
if (low.blue == high.blue)
{
threshold_intensity = 0;
intensity = double_packet();
for (low.blue = 0 ; low.blue < histogram->getMaxSegmentIndex() ; ++low.blue)
{
intensity.blue += histogram->getValue(BlueChannel, (int)low.blue);
if (intensity.blue > threshold_intensity)
{
break;
}
}
intensity = double_packet();
for (high.blue = histogram->getMaxSegmentIndex() ; high.blue != 0 ; --high.blue)
{
intensity.blue += histogram->getValue(BlueChannel, (int)high.blue);
if (intensity.blue > threshold_intensity)
{
break;
}
}
}
// Alpha.
intensity = double_packet();
for (high.alpha = histogram->getMaxSegmentIndex() ; high.alpha != 0 ; --high.alpha)
{
intensity.alpha += histogram->getValue(AlphaChannel, (int)high.alpha);
if (intensity.alpha > threshold_intensity)
{
break;
}
}
if (low.alpha == high.alpha)
{
threshold_intensity = 0;
intensity = double_packet();
for (low.alpha = 0 ; low.alpha < histogram->getMaxSegmentIndex() ; ++low.alpha)
{
intensity.alpha += histogram->getValue(AlphaChannel, (int)low.alpha);
if (intensity.alpha > threshold_intensity)
{
break;
}
}
intensity = double_packet();
for (high.alpha = histogram->getMaxSegmentIndex() ; high.alpha != 0 ; --high.alpha)
{
intensity.alpha += histogram->getValue(AlphaChannel, (int)high.alpha);
if (intensity.alpha > threshold_intensity)
{
break;
}
}
}
// Stretch the histogram to create the normalized image mapping.
-
// TODO magic number 256
+
for (i = 0 ; runningFlag() && (i <= (long)histogram->getMaxSegmentIndex()) ; ++i)
{
- if (i < (long) low.red)
+ if (i < (long) low.red)
{
normalize_map[i].red = 0;
}
else if (i > (long) high.red)
{
normalize_map[i].red = (256 * histogram->getHistogramSegments() - 1);
}
else if (low.red != high.red)
{
normalize_map[i].red = (int)(((256 * histogram->getHistogramSegments() - 1) * (i - low.red)) / (high.red - low.red));
}
- if (i < (long) low.green)
+ if (i < (long) low.green)
{
normalize_map[i].green = 0;
}
else if (i > (long) high.green)
{
normalize_map[i].green = (256 * histogram->getHistogramSegments() - 1);
}
else if (low.green != high.green)
{
normalize_map[i].green = (int)(((256 * histogram->getHistogramSegments() - 1) * (i - low.green)) / (high.green - low.green));
}
- if (i < (long) low.blue)
+ if (i < (long) low.blue)
{
normalize_map[i].blue = 0;
}
else if (i > (long) high.blue)
{
normalize_map[i].blue = (256 * histogram->getHistogramSegments() - 1);
}
else if (low.blue != high.blue)
{
normalize_map[i].blue = (int)(((256 * histogram->getHistogramSegments() - 1) * (i - low.blue)) / (high.blue - low.blue));
}
- if (i < (long) low.alpha)
+ if (i < (long) low.alpha)
{
normalize_map[i].alpha = 0;
}
else if (i > (long) high.alpha)
{
normalize_map[i].alpha = (256 * histogram->getHistogramSegments() - 1);
}
else if (low.alpha != high.alpha)
{
normalize_map[i].alpha = (int)(((256 * histogram->getHistogramSegments() - 1) * (i - low.alpha)) / (high.alpha - low.alpha));
}
}
// Apply result to image.
uchar* data = m_orgImage.bits();
int w = m_orgImage.width();
int h = m_orgImage.height();
bool sixteenBit = m_orgImage.sixteenBit();
int size = w * h;
// TODO magic number 257
+
if (!sixteenBit) // 8 bits image.
{
uchar red, green, blue, alpha;
uchar* ptr = data;
for (i = 0 ; runningFlag() && (i < size) ; ++i)
{
blue = ptr[0];
green = ptr[1];
red = ptr[2];
alpha = ptr[3];
if (low.red != high.red)
{
red = (normalize_map[red].red) / 257;
}
if (low.green != high.green)
{
green = (normalize_map[green].green) / 257;
}
if (low.blue != high.blue)
{
blue = (normalize_map[blue].blue) / 257;
}
if (low.alpha != high.alpha)
{
alpha = (normalize_map[alpha].alpha) / 257;
}
ptr[0] = blue;
ptr[1] = green;
ptr[2] = red;
ptr[3] = alpha;
- ptr += 4;
+ ptr += 4;
progress = (int)(((double)i * 100.0) / size);
- if (progress % 5 == 0)
+ if ((progress % 5) == 0)
{
postProgress(progress);
}
}
}
else // 16 bits image.
{
unsigned short red, green, blue, alpha;
unsigned short* ptr = reinterpret_cast(data);
for (i = 0 ; runningFlag() && (i < size) ; ++i)
{
blue = ptr[0];
green = ptr[1];
red = ptr[2];
alpha = ptr[3];
if (low.red != high.red)
{
red = (normalize_map[red].red) / 257;
}
if (low.green != high.green)
{
green = (normalize_map[green].green) / 257;
}
if (low.blue != high.blue)
{
blue = (normalize_map[blue].blue) / 257;
}
if (low.alpha != high.alpha)
{
alpha = (normalize_map[alpha].alpha) / 257;
}
ptr[0] = blue;
ptr[1] = green;
ptr[2] = red;
ptr[3] = alpha;
- ptr += 4;
+ ptr += 4;
progress = (int)(((double)i * 100.0) / size);
- if (progress % 5 == 0)
+ if ((progress % 5) == 0)
{
postProgress(progress);
}
}
}
}
FilterAction StretchFilter::filterAction()
{
return DefaultFilterAction();
}
void StretchFilter::readParameters(const FilterAction& /*action*/)
{
return;
}
} // namespace Digikam
diff --git a/core/libs/dimg/filters/auto/stretchfilter.h b/core/libs/dimg/filters/auto/stretchfilter.h
index 4f0a7b9614..2a1f599cab 100644
--- a/core/libs/dimg/filters/auto/stretchfilter.h
+++ b/core/libs/dimg/filters/auto/stretchfilter.h
@@ -1,117 +1,117 @@
/* ============================================================
*
* This file is a part of digiKam project
* https://www.digikam.org
*
* Date : 2005-24-01
* Description : stretch contrast image filter.
*
* 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_STRETCH_FILTER_H
#define DIGIKAM_STRETCH_FILTER_H
// Local includes
#include "digikam_export.h"
#include "dimgthreadedfilter.h"
#include "digikam_globals.h"
namespace Digikam
{
class DImg;
class DIGIKAM_EXPORT StretchFilter : public DImgThreadedFilter
{
public:
explicit StretchFilter(QObject* const parent = nullptr);
StretchFilter(DImg* const orgImage, const DImg* const refImage, QObject* const parent=nullptr);
virtual ~StretchFilter();
static QString FilterIdentifier()
{
return QLatin1String("digikam:StretchFilter");
}
static QList