diff --git a/NEWS b/NEWS index 5765e033cf..8c5f9de54d 100644 --- a/NEWS +++ b/NEWS @@ -1,681 +1,682 @@ digiKam 6.0.0 - Release date: 2019-02-10 ***************************************************************************************************** NEW FEATURES: ImageEditor : Add Web services Import and Export tools. Showfoto : Add Web services Import and Export tools. LightTable : Add Web services Import and Export tools. Database : Similarity database has been moved to a dedicated file to not bloat core database with computed finger-prints. This will speed-up query in core database in case of Similarity feature is used. Database : New video metadata parser based on ffmpeg to populate database. Search : Add video support to find files based on properties registered on database. General : Add new Exiv2 0.27 support. General : Add new Lensfun 0.4 support. General : Use DrMinGW has crash handler under Windows. General : Port all export/import tool to OAuth2 authentification. General : New tool to adjust quickly time and date information from items. Tool will be available in AlbumView, ImageEditor, LightTable, and Showfoto. General : New function to group images by timelapse / burst General : New tool to export to Pinterest social network. General : New tool to export to OneDrive webservice. General : New tool to export to Box webservice. General : New tool to export to Ipfs webservice. General : All bundles use last Lenfun 0.3.95 to process automatic lens correction. General : Add QWebEngine support. General : Fix all Krazy static analyzer reports. General : Update internal Libraw to last 0.19.2. New camera supported: * Apple: Phone 8, iPhone 8 plus, iPhone X * BlackMagic: URSA Mini 4k, URSA Mini 4.6k, URSA Mini Pro 4.6k * Canon: PowerShot A410, A540, D10, ELPH 130 IS, ELPH 160 IS, SD750, SX100 IS,SX130 IS, SX160 IS, SX510 HS, SX10 IS, IXUS 900Ti PowerShot G1 X Mark III, G9 X Mark II, EOS 6D Mark II, EOS 77D, EOS 200D, EOS 800D, EOS M6, EOS M100 * Casio: EX-ZR4100/5100 * DJI: Phantom4 Pro/Pro+, Zenmuse X5, Zenmuse X5R * FujiFilm: S6500fd, GFX 50S, X100f, X-A3, X-A5, X-A10, X-A20, X-E3, X-H1, X-T20 * Hasselblad: H6D-100c, A6D-100c * Huawei: P9 (EVA-L09/AL00), Honor6a, Honor9, Mate10 (BLA-L29) * Leica: CL, M10, TL2 * LG: V20 (F800K), VS995 * Nikon: D850, D5600, D7500, Coolpix B700 * Olympus: E-PL9, E-M10 Mark III, TG-5 * OnePlus: A3303, A5000 * Panasonic: DMC-FZ45, DMC-FZ72, DC-FZ80/82, DC-G9 (std. res mode only), DC-GF10/GF90, DC-GH5, DC-GX9, DC-GX800/850/GF9, DMC-LX1, DC-ZS70 (DC-TZ90/91/92, DC-T93), DC-TZ100/101/ZS100, DC-TZ200/ZS200 * PARROT: Bebop 2, Bebop Drone * Pentax: KP * PhaseOne: IQ3 100MP Trichromatic * Samsung: Galaxy Nexus, Galaxy S3, S6 (SM-G920F), S7, S7 Edge, S8 (SM-G950U) * Sony: A7R III, A9, DSC-RX0, DSC-RX10IV * Yi: M1 * YUNEEC: CGO3, CGO3P * Xiaoyi: YIAC3 (YI 4k) Collection : Add tool button to update an existing collection to the new drive or path. Tags : Add possibility to merge tags by drag & drop. IconView : Add capability to re-organize the contents manually. IconView : Add capability to separate the contents by month. HTML Gallery : New Vanilla theme to emulate export to HTML from Adobe LightRoom. HTML Gallery : New Blue Frame theme. HTML Gallery : New Kiosk-Mode theme. ***************************************************************************************************** BUGFIXES: 001 ==> 172650 - No export tools available. 002 ==> 149591 - The export menu is blank. 003 ==> 300424 - Export tools not detected. 004 ==> 327743 - MediaWiki export not displayed. 005 ==> 348146 - Export tools configuration module. 006 ==> 243275 - Crashing when calling configuration. 007 ==> 257134 - Crashes when entering its settings. 008 ==> 230666 - Crash during start. 009 ==> 306698 - Crashes after update to KDE. 010 ==> 207640 - Crashes immediately at startup, sometimes at closing. 011 ==> 245628 - Crash when enabling/disabling Facebook import/export. 012 ==> 097317 - sigsegv [New Thread 1100241184 (LWP 5665)]. 013 ==> 245776 - Crashes when selecting Settings. 014 ==> 245775 - Crashes even without export tools installed. 015 ==> 254283 - Crash as soon as i click settings. 016 ==> 255733 - crash when reopening configuration dialog. 017 ==> 262050 - Crash after new install and scan to mysql database. 018 ==> 263871 - Crashed after searching for duplicates. 019 ==> 268242 - Crashes after I clicked on 'Settings'. 020 ==> 276882 - Add export tool buttons to toolbar. 021 ==> 284801 - Export to picasaweb crashes. 022 ==> 277669 - Crash after files moved. 023 ==> 281250 - Crash after disabling Export tools. 024 ==> 202637 - Crash trying to start export tool. 025 ==> 306693 - after update to KDE host application crashes on startup. 026 ==> 282781 - Crashes when reopening configuration dialog after disabling export tools. 027 ==> 285499 - Crash when settings window opened. 028 ==> 297629 - Crashed when importing from Nikon P7000. 029 ==> 303338 - Crashes when clicking "send to" button. 030 ==> 307619 - Refuses to load Export tools. 031 ==> 311812 - Export tools not loading, SO version not defined. 032 ==> 313186 - Crashes on attempt to use the "Send to" menu. 033 ==> 313356 - Crashed when clicking the "send to" button. 034 ==> 313577 - Crashes when pressing the "send to" button. 035 ==> 313890 - Crash when clicking "Send to...". 036 ==> 315033 - Crashes on pressing Send To... button. 037 ==> 315914 - The facebook tool crashes everytime on initialization. 038 ==> 326556 - Export tools are not loaded when starting host application for second time. 039 ==> 095175 - crash on loading, signal 11 SIGSEGV. 040 ==> 175844 - Crashes at startup loading Export tools. 041 ==> 306511 - Crash during start. 042 ==> 234021 - Crash on loading. 043 ==> 219772 - Opening the application causes crash. 044 ==> 294173 - Crash after Image resize start. 045 ==> 306881 - Crashed when attempting to open Export tools. 046 ==> 306495 - Crash changing settings linux ubuntu lucid. 047 ==> 306497 - Crash after changing settings segmentation fault possible 2nd report? 048 ==> 185470 - "Import from facebook" is listed twice in import menu. 049 ==> 334045 - MediaWiki option not available in Export menu when plugin activated. 050 ==> 142112 - Can't save on my webspace with ShowFoto. 051 ==> 167417 - Showfoto cannot save files of CIFS mount. 052 ==> 125164 - Flickr export tool should respect host application selection. 053 ==> 238927 - Host application quits when uploading to Flickr. 054 ==> 326740 - Selection of tools is set to default after each update. 055 ==> 233063 - Add progress indicator when moving or copy files [patch]. 056 ==> 361829 - Rotated MP4 video with "Orientation" flag are not played back in the correct rotation angle. 057 ==> 329854 - digiKam doesn't rotate video thumbnail. 058 ==> 376661 - When importing ~200,000 video files Digikam crashes in about 2-5 seconds of starting. 059 ==> 377072 - Cannot read video metadata. 060 ==> 374453 - Extract right video date from MP4 files metadata. 061 ==> 377177 - Geolocation / GPS information doesn't appear in video metadata. 062 ==> 383588 - Imported video files have time shifted exactly 2 hours later. 063 ==> 380847 - MP4 video not importing with correct date. 064 ==> 373682 - geolocalisation filter does not take care of the videos geolocalisation tags. 065 ==> 340925 - digiKam crash when start it. 066 ==> 331506 - digiKam crashes on startup. 067 ==> 335816 - Crash when trying to add a big collection. 068 ==> 353295 - digiKam repeatedly crashes while importing pictures. 069 ==> 341433 - Crash when opening digiKam application. 070 ==> 375562 - digiKam crashes while scanning images into sqlite database. 071 ==> 334782 - Crash while doing nothing. 072 ==> 362672 - Crash on start of digiKam. 073 ==> 341023 - Crash after startup during check for updated images. 074 ==> 386891 - Crashed while adding pictures. 075 ==> 342666 - digiKam crashes during find new items. 076 ==> 341274 - digiKam crash on startup. 077 ==> 343708 - Crash when scanning album. 078 ==> 332721 - Crash when reading a certain MP4 video file. 079 ==> 343736 - Crashes when rebuilding thumbnails from database. 080 ==> 346356 - digiKam crashes when adding 90.000 pictures to library. 081 ==> 343714 - digiKam crash when scanning for new items. 082 ==> 341091 - digiKam crashes when updating the MySQL database of a a hudge photo collection. 083 ==> 340879 - digiKam crashes after getting unexpected but reasonable output from libexiv2. 084 ==> 342712 - Crash on collection scanning. 085 ==> 356704 - digiKam still crashes while scanning a new photo directory and subdirs. 086 ==> 339269 - Segfault when opening a folder that contains unknown file types (mov, avi, xcf). 087 ==> 364635 - digiKam crashes on startup. 088 ==> 357356 - digiKam crash on startup while scanning photos. 089 ==> 341554 - digiKam crashed by Data-Import from NFS. 090 ==> 345457 - digiKam crashes at "loading tools". 091 ==> 349635 - Crash of digiKam - moving album. 092 ==> 342604 - digiKam crash. 093 ==> 331450 - Signal 8 on album opening. 094 ==> 342030 - digiKam crashes when checking an AVI video file using exiv2. 095 ==> 352777 - Crash during scan. 096 ==> 352944 - digiKam crashes on start. 097 ==> 343643 - digiKam crashes while perform initial scanning of custom photo folder. 098 ==> 342000 - digiKam crash when opening folder with Videos (Album or SD Card import). 099 ==> 353447 - digiKam crashes when scanning files. 100 ==> 346807 - Crashes on startup. 101 ==> 364639 - digiKam crashed while opening database. 102 ==> 341504 - Crash while using application. 103 ==> 367691 - Searching for pictures crashes at 30% every time. 104 ==> 334604 - Crash after changing disk partions. 105 ==> 351689 - Crash on opening digiKam. 106 ==> 149267 - digiKam crashes after finding avi and so on. 107 ==> 170387 - Add movies management. 108 ==> 369629 - digiKam does not use GPS data from video files. 109 ==> 367880 - Nexus 5X videos show up upside-down in digiKam. 110 ==> 330116 - digiKam does not take care about GPS info stored in MP4 video files. 111 ==> 339150 - digikam crashes when trying to display video file. 112 ==> 344406 - Crash at start. 113 ==> 339909 - digiKam Segmentation Fault on open. 114 ==> 343231 - Crash at scanning for new fotos. 115 ==> 340373 - Crash on scanning video directory. 116 ==> 134679 - Video and audio files are not imported. 117 ==> 375357 - No video preview. 118 ==> 261773 - Batch renaming does not complete when MP4 video file is processed. 119 ==> 185915 - Album View: "Created" time of video set to "00:00". 120 ==> 303218 - digiKam import crashes when you select video files. 121 ==> 374241 - Bad video date rename. 122 ==> 375646 - Be able to scan only photo, not video and audio. 123 ==> 262499 - Cannot rename .AVI files. 124 ==> 199261 - Import avi movies from sdcard wrong date and no thumbnail. 125 ==> 181521 - NEF's in descending order, AVI in ascending order in import from SD-card. 126 ==> 392019 - Two persons can point to the same face tag in pictures. 127 ==> 392013 - Metadata explorer does not show XMP face rectangles. 128 ==> 389508 - Dates Side Menu Is Not Updated Automatically After Exif Date Change [patch]. 129 ==> 331864 - Merge Tags with same name when moving to same hierarchy level. 130 ==> 347302 - Reassign name to face. 131 ==> 391747 - BQM Tool "Remove Metadata" doesn't remove all metadata from image. 132 ==> 285683 - Already imported pictures not recognized after daylight savings time. 133 ==> 392309 - Icons are pixelated when my display scale factor is 1.2 134 ==> 392405 - Function 'getImageIdsFromArea' argument order different. 135 ==> 386224 - Metadata is not updated when moving tags. 136 ==> 370245 - Be able to rename tags which have been setted in pictures. 137 ==> 374470 - Deleted tags are not removed from file metadata. 138 ==> 374516 - Persons metadata are not updated after a tag removed. 139 ==> 392436 - Count Files in directory. 140 ==> 363859 - digiKam core port from QWebKit to QWebEngine [patch]. 141 ==> 392427 - Cannot add collection on network drive. 142 ==> 392022 - Position of a face tag appears on top of bottom of the list, instead of being sorted alphabetically. 143 ==> 372763 - Rename does not give options on Conflict. 144 ==> 391533 - Feature request: add "NOT" tag matching condition in "Filters" panel. 145 ==> 381222 - digiKam crash on fuzzy search. 146 ==> 386275 - Crash caused by QtAV. 147 ==> 372342 - Face tag area is very short [patch]. 148 ==> 391348 - People Side Menu Shows Only Faces Not People Tagged Images. 149 ==> 385630 - Views Requiring Maps Takes ~30s to Launch. 150 ==> 192908 - Allow to split icon-view in order to show multiple albums at the same time. 151 ==> 339088 - GIT master: crash when clicking through images in preview, with face recognition running in background. 152 ==> 341605 - Crash if I attempt to use left-sidebar tags tab. 153 ==> 227266 - Handle Video Date from metadata. 154 ==> 227259 - Needs to Edit Video Date. 155 ==> 373284 - digiKam crashed with SIGSEGV in QSortFilterProxyModel::parent(). 156 ==> 384807 - digikam 5.7.0 AppImage bundle : provide a more recent ffmpeg version for video support. 157 ==> 391835 - Deleted pictures still appear in group. 158 ==> 387483 - Elegant theme: Selected frame colors swapped [patch]. 159 ==> 375424 - Thumbnails are not being removed from AlbumsView after moving the images to Trash. 160 ==> 368796 - Problem with Exif-tags: ImageDescription and UserComment. 161 ==> 392417 - AppImage (5.9.0-01-x86-64) does not support "--install" cli parameter. 162 ==> 392922 - digikam-6.0.0 fail to start. 163 ==> 391399 - Not possible to add location bookmarks in Digikam >5.6.0. 164 ==> 380876 - Tags in Digikam DB maintained after being removed from file and file re-scanned. 165 ==> 392017 - Merging, renaming and removing face tags. 166 ==> 352711 - Externally removed tags are not removed from digiKam. 167 ==> 393108 - Tags not always visible when selecting multiple pictures in a group. 168 ==> 392656 - Selecting a root album for face scan doesn't include subfolders, but rather scans an unexpected album set. 169 ==> 329438 - Rename function with Date & Time does not work with NTFS. 170 ==> 376473 - Can"t set empty IPTC country code when using metadata templates. 171 ==> 380289 - Cannot write to Albums residing on NFS. 172 ==> 384465 - With Compact Flash Card Created date in thumbnails is wrong. 173 ==> 381958 - Cannot add additional collection. 174 ==> 383747 - "Rotate only by setting a flag" Changes Image Instead. 175 ==> 387977 - No icon only view of "Extras sidebar": sidebar taking up a lot of space. 176 ==> 277502 - All versions of version set always displayed in Album view [patch]. 177 ==> 393283 - Caption not updating Exif.Image.ImageDescription field. 178 ==> 391060 - Crashes on undo of very large tif. 179 ==> 366305 - Add a message at startup about the lack of temporary space to perform Undo operations. 180 ==> 366391 - Rotating an image seems to forget to reset the orientation flag. 181 ==> 393654 - Not able to select gpx file. 182 ==> 367596 - Sub-folder count images but don't show them (unsupported JPEG file?). 183 ==> 379922 - Digikam won't remove tags set by Windows Explorer. 184 ==> 379081 - GPS data are in file but geolocation indicator is not shown and map view empty. 185 ==> 354819 - Specific pictures not showing up in digikam. 186 ==> 393855 - MySQL/MariaDB upgrade fails. 187 ==> 384603 - Camera Creation Date not set from EXIF data. 188 ==> 386959 - Properties view: wrong creation date [patch]. 189 ==> 393970 - No mts video thumbnails. 190 ==> 393728 - Reread metadata from Video uses sidecar only. 191 ==> 393925 - UpdateSchemaFromV7ToV9 fails due to duplicate key in album_2. 192 ==> 393773 - showfoto crashes when geotagging. 193 ==> 388199 - No context menu to copy coordinates from map. 194 ==> 393399 - Windows defender freaks out in windows 10 and Edge. 195 ==> 392134 - SIGSEGV While Scanning Faces [patch]. 196 ==> 394168 - OSM Search Yields No Results. 197 ==> 377719 - Cannot rename file with overwrite [patch]. 198 ==> 388002 - remove kio related legacy [patch] 199 ==> 394242 - Import settings unneccesarily asks to overwrite image database, and crashes when I decline. 200 ==> 394278 - A slideshow theme for kiosk mode. 201 ==> 340389 - digiKam crashes while editing pictures for color balancing on OSX [patch]. 202 ==> 394413 - Unify group handling [patch]. 203 ==> 394573 - Revers geodata from open street map does not work. 204 ==> 394590 - Feature request: being able to filter on all metadatas fields. 205 ==> 394671 - Distortion on Panasonic DMC-LX15. 206 ==> 393205 - Advanced rename very slow. 207 ==> 382474 - Thumbnail regeneration. 208 ==> 394865 - digikam suspicious crash on exit. 209 ==> 390541 - Tooltip background cannot be changed. 210 ==> 391521 - "Tool-tip" box difficult to read due to default color scheme. 211 ==> 377849 - Albums disappear when the network is interrupted. 212 ==> 394988 - PgDown and PgUp hardcoded in preview mode. 213 ==> 366312 - Efficient photo tagging workflow got lost in transition from 4.x to 5. 214 ==> 395093 - Being able to export a list of paths from a selection of thumbnails. 215 ==> 395144 - When zooming in preview, face tags show on wrong places. 216 ==> 275671 - Scan single image for faces. 217 ==> 395199 - Uploading large video files to flickr fails. 218 ==> 348274 - "Change Account" immediately opens web page in browser, before I click Continue 219 ==> 263347 - Print wizard ignores selected paper size, reverts to A4. 220 ==> 395579 - Only one tag being exported to flickr. 221 ==> 395790 - Rename with nested tags breaks due to | bar character. 222 ==> 395875 - ImageEditor window is blank when opened a second time. 223 ==> 385822 - [Suggested feature] Re-use thumbnails from the database for the items in Trash. 224 ==> 382174 - Not creating thumbnails and not editing (F4) files created for Samsung panorama jpgs [patch]. 225 ==> 376124 - Some Photos are not previewed. 226 ==> 386188 - Preview Does Not Display Some JPGs Editor Crashes Program. 227 ==> 394906 - Toggling "use file metadata" for input will make Digikam forget all imported photos. 228 ==> 388391 - Windows x64 installer crashes at 60%. 229 ==> 377433 - Crash on opening settings. 230 ==> 390286 - Please update version of lensfun. 231 ==> 386649 - Crash on opening RAW file from OnePlus One. 232 ==> 172836 - No menu item for lensfun. 233 ==> 319462 - Crash while applying lensfun distortion correction a second time. 234 ==> 380844 - Demosaicing choosing VCD & AHD then update. 235 ==> 380843 - Demosaicing. 236 ==> 301219 - digiKam crash. 237 ==> 216013 - try to open extras/ batch raw converter was followed by a crash. 238 ==> 102045 - RAW conversion in digikam fails to convert or generate preview. 239 ==> 137281 - Cannot convert nef into raw. 240 ==> 230763 - Exif ISO data missing when converting Olympus ORF. 241 ==> 137278 - 16-bit/channel workspace when working with RAW conversions. 242 ==> 133004 - Canon CRW portrait preview upside-down. 243 ==> 221345 - Do not process search immediately when enter text to query. 244 ==> 396234 - Add a feature which allow to switch latitude and longitude. 245 ==> 396283 - Missing images in album preview. 246 ==> 396352 - Some iptc tags are not displayed in the metadata part. 247 ==> 396434 - Uncontrolled log messages oversize log file up to run out of disk space. 248 ==> 396482 - Empty folder selector window after select and click on "Import selected elements". 249 ==> 396170 - Gallery creation error. 250 ==> 140374 - HTML Gallery export fails to parse xml from non-UTF8 metadata in jpeg. 251 ==> 396712 - "Save Search" does not work. 252 ==> 396944 - Integrity constraint violation on Albums.icons when migrating from sqlite to mysql. 253 ==> 396952 - Cut and Paste Into Caption Has Incorrect Font Initially. 254 ==> 091562 - Change order of pictures in the virtual album manually. 255 ==> 098340 - Re-sort images in albums 256 ==> 177355 - Feature request: export slideshow in lighttable. 257 ==> 158520 - Wishlist sort thumbnails manually and rename images. 258 ==> 191000 - Wish: Visual tool to change the sort order of photos. 259 ==> 216802 - digiKam ability to sort album with "picture is before picture". 260 ==> 230136 - It lacks the ability to sort photos manually. 261 ==> 236249 - Wish: custom sort in album view. 262 ==> 323559 - Wish: Albums like Playlists in Amarok. 263 ==> 337002 - Manual image sort. 264 ==> 397167 - Some tags are not displayed on the preview. 265 ==> 397207 - Too many open files, when reverse geocoding many images. 266 ==> 397278 - DNG conversion not possible. 267 ==> 397311 - DIN A relation in "Aspect Ratio Crop". 268 ==> 222716 - digiKam does not start. Showing: Reading database. 269 ==> 276633 - Crash when using map view inside of digiKam. 270 ==> 224706 - digiKam for KDE on Windows "Failed to rename Album" (KDirWatch Relevant). 271 ==> 383016 - digiKam with incomplete German translation. 272 ==> 244982 - digiKam crashes at initial configuration. 273 ==> 366453 - digiKam crash while using mouse over title bar. 274 ==> 278490 - Incorrect tab layout with slider/spinbox/reset button. 275 ==> 324642 - digiKam crash when starting a bug report while offline. 276 ==> 238392 - digiKam Camera interface : rename dialog interprets alt-s as plain s when button greyed out. 277 ==> 397177 - image editor does not export exif and IPTC data when saving to PNG. 278 ==> 341276 - Picture bigger than 5MB are not previewed. 279 ==> 388908 - Error while scanning with HP F300 series. 280 ==> 381723 - digiKam crashed when I tried to close the window opened by clicking import button. 281 ==> 345288 - Crash from digiKam when try customizing a shortcut to a Tag. 282 ==> 381193 - digiKam crashes after uploading photos from smartphone via USB. 283 ==> 275670 - Face preview image incorrect on 'rotated' images. 284 ==> 395199 - Uploading large video files to flickr fails [patch]. 285 ==> 240144 - digiKam : When I press the left button of the mouse on a photo, a menu appears, but no items are seen. 286 ==> 240229 - White contextual menu problem. 287 ==> 309508 - Export to Flickr does not send the geodata. 288 ==> 383987 - Flickr tool no longer authenticates to Flickr. 289 ==> 338333 - Account problem. 290 ==> 397001 - The "maximise" button is missing in decoration of the geolocation editor window [patch]. 291 ==> 397406 - Digikam shouldn't use parentheses when renaming files. 292 ==> 397411 - Not being able to move files in a removable media collection which is not mounted. 293 ==> 227566 - digiKam does not allow smb:// shared folder as picture collection. 294 ==> 395201 - SSL broken in 5.9.0 AppImage bundle. 295 ==> 385363 - digiKam crashes when authorizing access to Google photo because of SSL error in AppImage. 296 ==> 348277 - Please focus auth code input field in the GDrive authentication dialog. 297 ==> 383174 - Google photo, export stops after a random quantity of photos. 298 ==> 387422 - Flickr export authorization does not work. 299 ==> 397126 - Fail to replace a photo in google photo. 300 ==> 387201 - kioslave needed by flickr export but not included in appimage. 301 ==> 389785 - 'Open in Filemanager' dolphin not working. 302 ==> 396619 - All Exports fail - cannot find ioslave. 303 ==> 254512 - Crash when initializing the export to Flickr.com. 304 ==> 237818 - Crash when uploading multiple images to Flickr. 305 ==> 286754 - Crash when when entering collection name in flickr uploader. 306 ==> 337980 - Flickr Export tool can no longer obtain a new token. 307 ==> 151018 - Requires new token with every invokation. 308 ==> 132922 - Export to flickr doesn't work. 309 ==> 149864 - Dialog box pops up saying 'Error Occured: Missing signature. We can not proceed further". 310 ==> 196179 - Flickr export does not work. 311 ==> 391734 - Can't export to google photos: SSL Handshake failed. 312 ==> 386402 - Not all page sizes for selected printer can be selected. 313 ==> 395557 - Printformat not selectable. 314 ==> 340644 - Allow to use standard photo paper sizes. 315 ==> 330906 - Crashes when exporting to HTML. 316 ==> 309316 - Html gallery export crash and other bugs. 317 ==> 314248 - digiKam crashing when exporting pictures to html. 318 ==> 225725 - Crash using HTML export. 319 ==> 396092 - Don't init search till user press a buttom specifically for this task. 320 ==> 397376 - Search in Geolocation doesn't work for Streets / location, only for City. 321 ==> 393259 - Configuration conflicts. 322 ==> 391329 - Two actions that wants to use same shortcut (Ctrl+Shift+,). 323 ==> 391655 - Kubuntu 18.04 beta, Digikam 5.6.0 Ambiguous Shortcuts - Zoom to 100% and Configure Digikam share the same shortcut (Ctrl+Shift+,). 324 ==> 393031 - Two actions for digikam[helpfully identified as a bug]. 325 ==> 395518 - When manually typing face name, sort the filtered tags by recent usage. 326 ==> 304202 - No option to re-read metadata from XMP sidecar file. 327 ==> 289445 - Allow to create Contact Sheet from an album. 328 ==> 164750 - Picture alignment and background picture. 329 ==> 338180 - Add "Toggle auto-refresh" option in search view. 330 ==> 359235 - digiKam doesn't response after start when StatusBar=Disabled in digikamrc. 331 ==> 090550 - Linking error with jpeg. 332 ==> 393974 - Appimage bundle does not automatically recognize Gphoto2 devices connected. 333 ==> 141288 - Error KIoexec usb camera. 334 ==> 397554 - digiKam crashes while running BatchQueueManager. 335 ==> 379261 - Crash on attempting camera import. 336 ==> 379335 - digiKam crashes when deleting images in the download window [patch]. 337 ==> 394291 - Cannot upgrade mysql db from v7 to v9. 338 ==> 389468 - Immediate Crash On Metadata Sync. 339 ==> 379807 - ShowFoto crashes when clicking Color Effects from menu. 340 ==> 257301 - showfoto reaches an assert when saving file with "~/" in the path. 341 ==> 183629 - Digikam/Showfoto Dark Theme: The selected photo filename is nearly invisible. 342 ==> 249379 - All versions of Digikam/Showfoto/KDE download is missing a file. 343 ==> 237286 - digiKam crash after to scan collection (upgrate from 9.10 to 10.04). 344 ==> 254878 - digiKam error segmentation fault when open it. 345 ==> 255759 - digikam crash when open it kunbuntu lucid. 346 ==> 249009 - digiKam crashes at initialisation. 347 ==> 236647 - After upgrading to Ubuntu 10.04 digiKam crashes everytime it is launched. 348 ==> 235905 - digiKam crash after upgrade. 349 ==> 185265 - digiKam constantly using some CPU in a poll loop due a timeout 350 ==> 250364 - digiKam crashes at start-up in xubuntu 10.04. 351 ==> 253205 - digiKam crashes while loading on Ubunu 10.04. 352 ==> 249033 - After upgrade to 10.04 digiKam crashes after starting always. 353 ==> 300713 - Crash while editing tag in digiKam. 354 ==> 184443 - digiKam save as "JPEG" appends a ".JPEG" file extension. Bad! 355 ==> 189084 - Crash while editing toolbars (here from digiKam camera GUI). 356 ==> 205275 - After changing symbols in bar on preview digikam closes. 357 ==> 214418 - Crash while importing into digiKam. 358 ==> 279909 - digiKam crashed when I tried to import photos from my iPhone 4. 359 ==> 287616 - digiKam crash when selecting folder to store photos. 360 ==> 327714 - digiKam crashes when starting up [Qt Bmp Image IO]. 361 ==> 396892 - digiKam font sizes cannot be changed. Mostly too small. 362 ==> 371726 - Dates view empty using MYSQL due to SQL query error (fix supplied). 363 ==> 382217 - Use normalized connects [patch]. 364 ==> 113692 - digiKam thumbnail hangs on .mov movie. 365 ==> 185638 - digiKam crash on startup. 366 ==> 350404 - digiKam crashes at startup. 367 ==> 301583 - digiKam crash on preview avi. 368 ==> 236960 - No video in digiKam embedded viewer no way to change codec? 369 ==> 227113 - digiKam crashes when selecting a movie. 370 ==> 274333 - digiKam crashed upon startup. 371 ==> 247019 - digiKam crashes in Album view with Canon IXUS 90 video clip. 372 ==> 238525 - digiKam crashes on browsing in album view. 373 ==> 317437 - digiKam crashes on startup. 374 ==> 237183 - digiKam crashes on playing Quicktime movies. 375 ==> 256644 - digiKam crashes after adding photos to light table. 376 ==> 245033 - digiKam falls on video files. 377 ==> 252411 - digiKam crashes when .AVI videoclip is launched. 378 ==> 247399 - digiKam crashes when viewing videos. 379 ==> 261706 - digiKam crashed after downloading pictures. 380 ==> 172170 - digiKam embedded video player - video not synchronized with sound. 381 ==> 293173 - digiKam does not play videos. 382 ==> 182401 - Wrong color palette on playback videos within digiKam. 383 ==> 219419 - digiKam crashes after splash screen with PTP. 384 ==> 397565 - Unable to launch in OS X after install. 385 ==> 337978 - digiKam crashes on startup - always. 386 ==> 215673 - digiKam movie no preview. 387 ==> 204481 - digikam: Wizard not translated in French. 388 ==> 365694 - digiKam 5.0.0 : every menus are in French, excepted the menu bar. 389 ==> 394434 - Installation problem mysql internal on mac os high sierra [patch]. 390 ==> 397696 - [Website] On Fedora, use dnf instead of yum. 391 ==> 392970 - Website: Mention KDE Familly. 392 ==> 394694 - Showfoto crashes on change picture size. 393 ==> 326006 - digiKam Raw engine generates pink & black stripe on the right. 394 ==> 379984 - ASSERT failure in Q_GLOBAL_STATIC: "The global static was used after being destroyed". 395 ==> 373572 - File synchronization. 396 ==> 296768 - When creating thumbnails, digiKam uses 80% CPU - quadcore 3800, 9GB RAM. 397 ==> 397777 - Need to default to native filesystem browser on first run to see external drives. 398 ==> 279818 - digiKam crashed when updating fingerprints. 399 ==> 275931 - digiKam crashed when trying to stop a batch queue. 400 ==> 272144 - Alphabetically sorting of the tools in BQM. 401 ==> 271531 - Cannot edit canon crw-pics. 402 ==> 397739 - Menu icons are not scaled correctly on external/secondary screen (2560x1440) while using MacBookPro. 403 ==> 380841 - Crash while correcting color manually. 404 ==> 378176 - Using tool causes program to crash. 405 ==> 395842 - digiKam crashes adjusting color balance on a RAW file. 406 ==> 388608 - Faces engine hangs and crashes. 407 ==> 397855 - Not compatible with Windows 10. 408 ==> 391039 - digiKam stalled after trying to make a panorama. 409 ==> 184318 - Starting digiKam it crashed and caused the signal 11 (SIGSEGV). 410 ==> 222740 - Starting digiKam cause sound system to report pulseaudio is removed. 411 ==> 378697 - Add image carousel to front page screenshots. 412 ==> 180375 - Web page: Wrong link to Alpenglow Webpage. 413 ==> 331141 - Broken link to database schema in web site digikam.org pointing to project.kde.org. 414 ==> 397862 - Reordering geolocation bookmarks will remove them from the list. 415 ==> 223348 - digiKam crash during album view browsing AVIs with PgUp/PgDown. 416 ==> 156146 - Reading data base, digiKam don't start. 417 ==> 118090 - digiKam crashed while attempting to save scanned (tiff) image. 418 ==> 397828 - Operation cancelled when exporting to flickr. 419 ==> 397924 - Merge tag names of people. 420 ==> 397425 - Bug when trying find duplicates. 421 ==> 397496 - Wish: Read facetags from jpg files edited in Mylio. 422 ==> 397727 - Don't show "Mod."" label when no modified timestamp exists on an image. 423 ==> 397893 - MacOS : digiKam do not link with libksane 18.08.0. 424 ==> 397990 - Carousel images too large. 425 ==> 389273 - Copy/move selected items to anothor Album doesn't work correctly [patch]. 426 ==> 261471 - Crashes when trying to delete photos or albums. 427 ==> 342108 - Crashes when trying to import images 428 ==> 341268 - digiKam freeze each time I select a photograph in a collection. 429 ==> 388551 - Error message at startup showing that Marble plugins are not loading but they do. 430 ==> 342110 - Bug reporting feature gives error wont report bug. 431 ==> 387047 - Easily accessible signatures. 432 ==> 286286 - digiKam crashes if I request fullscreen mode. 433 ==> 292522 - digiKam crashes on startup. 434 ==> 294791 - digiKam and Showfoto crashes while switching to fullscreen mode. 435 ==> 347589 - Menu icons size is not unified - text overlapping. 436 ==> 382053 - digiKam preferences when using mysql host port resets to default (3306) when accessing preferences. 437 ==> 388227 - Image version always shown. 438 ==> 386561 - Cannot import images from mac /Volumes/. 439 ==> 396491 - Versions are not grouped despite the correct setting. 440 ==> 389505 - Database port reset to 3306 when oppening digiKam setup (working with maiaDB10). 441 ==> 266165 - Advanced slideshow crash. 442 ==> 387372 - With Digikam Version 5.7 is no preview possible. 443 ==> 286341 - Crash at startup with a "Bus Error". 444 ==> 329050 - Application crashes while starts. 445 ==> 396374 - editing a picture (F4) presents an empty screen. 446 ==> 336874 - After exiting full screen, Menu Bar disappear and crash. 447 ==> 237494 - B&W JPEG displayed incorrectly. 448 ==> 265749 - Blank context menus. 449 ==> 274733 - Delete file permanentry works wrong. 450 ==> 383711 - All Albums Disappear. 451 ==> 378929 - DNG conversion fails in Windows 10 for Olympus ORF files. 452 ==> 170458 - Object position on pictures. 453 ==> 184638 - Generate XMP for raw files. 454 ==> 200380 - Usability of editing exif comments. 455 ==> 195090 - Thumbnails bar should have no empty space. 456 ==> 196730 - Add Color Space converter to batch queue tool. 457 ==> 207921 - Using image editing tools in fullscreen modes leaves right sidebar open. 458 ==> 211066 - TIFFs written by digiKam not readable with GIMP. 459 ==> 224603 - One-touch download when connecting media. 460 ==> 282021 - Impossible to start... 461 ==> 202430 - Crash after startup on Mac OS X. 462 ==> 139153 - Can't compile from source. 463 ==> 380971 - Albums disappear when the network is interrupted. (5.6.0). 464 ==> 342673 - Rebuild fingerprints crashes with large number (>110000) of images. 465 ==> 261134 - Drag a folder into album view - deletes source folder. 466 ==> 365809 - Menu bar disappears after having used full screen mode. 467 ==> 383924 - Tags syncing and reading from file not working. 468 ==> 395788 - If tags have a in them rename effectively deletes the file. 469 ==> 388596 - Main area remains empty with Qt 5.9.3. 470 ==> 387552 - digiKam suddenly is unable to read or display any images. 471 ==> 397972 - Unable to navigate in DK during flickr export. 472 ==> 390228 - No clear rule on where digikam wants to import. 473 ==> 187274 - The dialogue to export to an HTML gallery has an album selected but the Next button is greyed out. 474 ==> 129762 - Flickr export tool should export whole host application albums. 475 ==> 130966 - Exporting to html gallery doesn't include subalbums. 476 ==> 097396 - Export to HTML stops/ slow. 477 ==> 128035 - HTML export adds extra .jpg extention to all image filenames. 478 ==> 129870 - Album sent to HTMLGallery tool doesn't respect the view order. 479 ==> 111462 - Exporting multiple "tag" albums in HTML produces invalid hyperlinks on main webpage. 480 ==> 147770 - Use a hierarchical treeview in HTMLExport (instead of a listview). 481 ==> 259748 - Xorg crashes when launching the "Export to flickr" tool. 482 ==> 398206 - Strongly underexposed areas of the jpeg file are white rather than pure black. 483 ==> 127498 - Reduce image size delete exif data. 484 ==> 219009 - Crashed when selecting photos for upload. 485 ==> 234395 - Unable to get list of albums. 486 ==> 241587 - Flickr export reencode jpeg before sending them. 487 ==> 278890 - Showfoto crashes with qt 4.8. 488 ==> 355744 - Segfault on SmugMug upload. 489 ==> 358704 - Request authorization does not occur and upload becomes impossible. 490 ==> 175233 - Showfoto crashes on windows. 491 ==> 259623 - Application: showFoto (showfoto), signal: Segmentation fault (opening jpg). 492 ==> 194832 - Showfoto open and crash after one second. 493 ==> 390287 - digiKam crashes when I do batch renames. 494 ==> 390290 - Trying to use "red eyes" reduction function make showfoto crash. 495 ==> 388542 - DNG crash when i open a .nef files. 496 ==> 390580 - First crashes when saving tiff file, then crashes when starting. 497 ==> 391721 - No photography is displayed in the main view. 498 ==> 392813 - Showfoto crashed when clicked on close button in title bar. 499 ==> 393069 - I do not see images in the file browser, only in calendar. 500 ==> 392189 - Database upgrade fails. 501 ==> 386653 - Message error on face detectior. 502 ==> 398287 - Print creator causes digiKam crash when orientation is changed. 503 ==> 397305 - Failed to add tag to database. 504 ==> 398462 - IPTC from Ligthroom. 505 ==> 397340 - Update image file timestamps if metadata is written to sidecar file [patch]. 506 ==> 398331 - Xmp sidecar files are not reloaded on change. 507 ==> 380341 - Modified sidecar not rescanned. 508 ==> 339342 - SCAN : Search for new items does not recognize modified tags in sidecar files. 509 ==> 387351 - Meta data for RAW files checkbox disabled. 510 ==> 398676 - Time Adjust in Batch Queue Manager jumps from hour to second. 511 ==> 338533 - Sort by date should use picture created date not modified date. 512 ==> 398675 - Image Rotation Inflates File Size. 513 ==> 309520 - GROUP : add 'group by month' into 'View.. | group by...' menu. 514 ==> 398714 - MicrosoftPhoto:LastKeywordXMP does not show up in digkam tags list. 515 ==> 388386 - Time adjustment tool is misleading and potentially leads to data loss. 516 ==> 366777 - No preview for Time Adjust tool. 517 ==> 306092 - Creation date as file name - doesn't work in BQM. 518 ==> 089993 - Allow setting time to specific date/time. 519 ==> 099894 - TimeAdjust only modifies file modification date, not EXIF. 520 ==> 119232 - Mass changing date for jpegs without EXIF. 521 ==> 119634 - Batch operation to change the date to several images at the same time. 522 ==> 134306 - Image mass tagging feature. 523 ==> 211845 - Missing progress bar while changing images dates. 524 ==> 249320 - Unable to change time file. 525 ==> 282559 - Graphical interface for changing dates and time. 526 ==> 354668 - Crashes when adjusting time and date on two or more photos at a time. 527 ==> 360770 - Crash when adjusting time on many pictures. 528 ==> 362104 - digiKam crashes when changing date and time of more than 25 selected pictures 529 ==> 366450 - Batch time adjust is ok, but time not adjusted in digikam thumbnail. 530 ==> 378006 - Preview of the time adjust result. 531 ==> 398624 - Cannot edit Date in XMP video metadata. 532 ==> 398810 - "Dates" panel is not refreshed after a photo with a new year is added. 533 ==> 398986 - Deleting a tag while using it as filter removes all images from the current view. 534 ==> 370093 - Opening a picture (double-click) in Showfoto (default viewer) does not show the other pictures in the folder (similar to Bug 221245). 535 ==> 398479 - digiKam crashing at startup Thread 2 Crashed:: Digikam::ScanController. 536 ==> 397808 - digiKam Mac Package crashes after resume when screen configuration changes. 537 ==> 399071 - "Adjust Time Date" tool shows all times as "00:00:00". 538 ==> 399134 - Progress bar or status when importing from camera/card reader. 539 ==> 399153 - Selecting a picture with a tag and another without that tag, the checkbox is black instead of gray. 540 ==> 384759 - Can't adjust timestamp of RAW files. 541 ==> 399221 - Cannot scroll in videos by clicking on the progress bar (only by moving the progress slider). 542 ==> 399315 - Horizontal scroll bar aligned to the right by default. 543 ==> 397962 - digiKam 6.0 does not start after install. 544 ==> 399336 - Google maps – "For development purposes only" printed on the map. 545 ==> 399338 - Count of items in Tree Views does not match total number of items. 546 ==> 399292 - Video file datetime not shown correctly after editing. 547 ==> 370553 - Adjust date - not possible to update "digiKam timestamp". 548 ==> 329091 - MySQL : needs to set "max-allowed-packet" server settings from 4.096 to 16.777.216 to prevent Maintenance tool crash. 549 ==> 399406 - digiKam Crash on advance rename. 550 ==> 397386 - Add a separate checkbox to "Show Rating". 551 ==> 335870 - Nexus 4 in PTP mode gets error: The specified camera ("/org/kde/solid/udev/...") is not supported. 552 ==> 399917 - Fails to build with QT5.7.1 553 ==> 399762 - digiKam Geolocation Correlator ignores time offset. 554 ==> 400148 - Files exported in google photos does not have their filename as name, but their date. 555 ==> 373678 - Videos exported in google photos are not available (still processing state). 556 ==> 400139 - Titles are not exported anymore on google photos. 557 ==> 400216 - Components Information window: no standardized uppercase/lowercase. 558 ==> 400217 - Incorrect Video date/time information: shifted by 2 hours. 559 ==> 396961 - Empty space on interface on thumbnail view. 560 ==> 400436 - Crash when saving jpeg files with metadata. 561 ==> 400492 - Rotation does not work. 562 ==> 246727 - Add undo functionality to move to trash action. 563 ==> 400637 - IPFS icons have non-square size, causing them to get blurred. 564 ==> 400434 - Trash content disappears after selecting 'Map' or 'Table' view. 565 ==> 394660 - Crashes when refreshing. 566 ==> 400712 - File modification timestamp is updated when images are moved to collection on removable media. 567 ==> 394214 - Import .mp4 files does not preserve file mtime as date stamp. 568 ==> 400762 - Trash - "Delete All Permanently" button not visible. 569 ==> 400792 - Failure to export to JPEG 2000. 570 ==> 400777 - Group images by timelapse / burst [patch]. 571 ==> 400902 - NAS Mysql cannot connect with instructions given by Dmitry Popov. 572 ==> 400918 - Tags are erroneously copied to untagged images in a selection when new tag added. 573 ==> 375197 - It is possible to rotate video thumbnails (but the video isn't rotated). 574 ==> 334678 - Thumbnails fail to generate for certain AVI files - infinite loop. 575 ==> 400917 - Rotation of picture in Preview mode: thumbnail not rotated. 576 ==> 400960 - Do not reset horizontal scroll bar slider position when selecting album. 577 ==> 400766 - Export to Onedrive: not possible to export into subfolder. 578 ==> 398207 - Box.com video upload fails despite successful authentication. 579 ==> 401160 - Build against lensfun-devel. 580 ==> 401173 - Wish for file saving: set changed datetime to exif datetime. 581 ==> 401423 - Video sidecar files nor read or written. 582 ==> 401438 - Calendar plugin prints improper month names (Czech localization). 583 ==> 401515 - Export to OneDrive: created subfolders not correctly managed. 584 ==> 401516 - Export to (Onedrive, Dropbox ...): list of subfolders not correctly displayed (problems if several subfolders have the same name). 585 ==> 401645 - digiKam crashes on possibly damaged video-file. 586 ==> 401676 - BQM resize tool : allow to resize by percentage (%). 587 ==> 401709 - Double clicking a name in a face-frame closes image preview. 588 ==> 401811 - DB migration (SQLite -> MySQL) fails if images are in trash. 589 ==> 401767 - digiKam unable to find existing path. 590 ==> 215486 - Collection not found in location on disk with UUID (LVM volume). 591 ==> 401834 - The "Scanning Faces" dialog box is too large and can't be resized in french language. 592 ==> 401664 - Trash shows list view only no thumbnails. 593 ==> 402029 - Find New Items process takes significantly longer to finish on 6.0.0. 594 ==> 402286 - Metadata settings don't display the combo value options. 595 ==> 402288 - The default Xmp.lr.hierarchicalSubject setting does not read darktable tags. 596 ==> 402300 - Template string in Help > About. 597 ==> 402283 - Duplicate entries drive to unpredictable performance. 598 ==> 402380 - Database schema upgrade to V10 is incomplete. 599 ==> 402379 - Moving location of RAW file causes loss of metadata. 600 ==> 366211 - When changing database, cannot open thumbnails into edit window. 601 ==> 402496 - digiKam crashed after a while during face recognition. 602 ==> 402726 - Piwigo export is missing. 603 ==> 401301 - digiKam Close crash. 604 ==> 401837 - Button Caption transcription error in french. 605 ==> 398880 - digiKam stays as a background process after closing. 606 ==> 398129 - ExpoBlend tool filename dialog shows previous file's name. 607 ==> 402556 - digikam-data conflict with dropbox installation. 608 ==> 402733 - Launch failure on MacOS with Digkam 6 beta3. 609 ==> 388705 - Unexpected crash on adding new items to the collection from an external application - 5.7.0 suse packages. 610 ==> 402800 - Main album view does not update when moving images between albums. -611 ==> +611 ==> 402811 - Drag-n-drop disabled in Geolocation editor. +612 ==> diff --git a/core/utilities/geolocation/editor/dialog/geolocationedit.cpp b/core/utilities/geolocation/editor/dialog/geolocationedit.cpp index cd93796499..ef705bc784 100644 --- a/core/utilities/geolocation/editor/dialog/geolocationedit.cpp +++ b/core/utilities/geolocation/editor/dialog/geolocationedit.cpp @@ -1,1099 +1,1099 @@ /* ============================================================ * * This file is a part of digiKam project * http://www.digikam.org * * Date : 2006-05-16 * Description : A tool to edit geolocation * * Copyright (C) 2006-2019 by Gilles Caulier * Copyright (C) 2010-2014 by Michael G. Hansen * Copyright (C) 2010 by Gabriel Voicu * Copyright (C) 2014 by Justus Schwartz * * 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 "geolocationedit.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 #include #include #include #include // KDE includes #include #include // Local includes #include "dlayoutbox.h" #include "digikam_config.h" #include "mapwidget.h" #include "itemmarkertiler.h" #include "trackmanager.h" #include "gpscommon.h" #include "gpsitemmodel.h" #include "mapdragdrophandler.h" #include "gpsitemlist.h" #include "gpsitemlistdragdrophandler.h" #include "gpsitemlistcontextmenu.h" #include "gpscorrelatorwidget.h" #include "digikam_debug.h" #include "dmessagebox.h" #include "gpsundocommand.h" #include "rgwidget.h" #include "kmlwidget.h" #include "statusprogressbar.h" #include "searchwidget.h" #include "backend-rg.h" #include "gpsitemdetails.h" #include "gpsgeoifacemodelhelper.h" #include "dxmlguiwindow.h" #include "gpsbookmarkowner.h" #include "gpsbookmarkmodelhelper.h" #include "dinfointerface.h" #ifdef GPSSYNC_MODELTEST # include #endif namespace Digikam { struct SaveChangedImagesHelper { public: explicit SaveChangedImagesHelper(GPSItemModel* const model) : imageModel(model) { } QPair operator()(const QPersistentModelIndex& itemIndex) { GPSItemContainer* const item = imageModel->itemFromIndex(itemIndex); if (!item) return QPair(QUrl(), QString()); return QPair(item->url(), item->saveChanges()); } public: typedef QPair result_type; GPSItemModel* const imageModel; }; // --------------------------------------------------------------------------------- struct LoadFileMetadataHelper { public: explicit LoadFileMetadataHelper(GPSItemModel* const model) : imageModel(model) { } QPair operator()(const QPersistentModelIndex& itemIndex) { GPSItemContainer* const item = imageModel->itemFromIndex(itemIndex); if (!item) return QPair(QUrl(), QString()); item->loadImageData(); return QPair(item->url(), QString()); } public: typedef QPair result_type; GPSItemModel* const imageModel; }; // --------------------------------------------------------------------------------- class Q_DECL_HIDDEN GeolocationEdit::Private { public: explicit Private() { imageModel = 0; selectionModel = 0; uiEnabled = true; listViewContextMenu = 0; trackManager = 0; fileIOFutureWatcher = 0; fileIOCountDone = 0; fileIOCountTotal = 0; fileIOCloseAfterSaving = false; buttonBox = 0; VSplitter = 0; HSplitter = 0; treeView = 0; stackedWidget = 0; tabBar = 0; splitterSize = 0; undoStack = 0; undoView = 0; progressBar = 0; progressCancelButton = 0; progressCancelObject = 0; detailsWidget = 0; correlatorWidget = 0; rgWidget = 0; searchWidget = 0; kmlWidget = 0; mapSplitter = 0; mapWidget = 0; mapWidget2 = 0; mapDragDropHandler = 0; mapModelHelper = 0; geoifaceMarkerModel = 0; sortActionOldestFirst = 0; sortActionYoungestFirst = 0; sortMenu = 0; mapLayout = MapLayoutOne; cbMapLayout = 0; bookmarkOwner = 0; actionBookmarkVisibility = 0; iface = 0; } // General things GPSItemModel* imageModel; QItemSelectionModel* selectionModel; bool uiEnabled; GPSItemListContextMenu* listViewContextMenu; TrackManager* trackManager; // Loading and saving QFuture > fileIOFuture; QFutureWatcher >* fileIOFutureWatcher; int fileIOCountDone; int fileIOCountTotal; bool fileIOCloseAfterSaving; // UI QDialogButtonBox* buttonBox; QSplitter* VSplitter; QSplitter* HSplitter; GPSItemList* treeView; QStackedWidget* stackedWidget; QTabBar* tabBar; int splitterSize; QUndoStack* undoStack; QUndoView* undoView; // UI: progress StatusProgressBar* progressBar; QPushButton* progressCancelButton; QObject* progressCancelObject; QString progressCancelSlot; // UI: tab widgets GPSItemDetails* detailsWidget; GPSCorrelatorWidget* correlatorWidget; RGWidget* rgWidget; SearchWidget* searchWidget; KmlWidget* kmlWidget; // map: UI MapLayout mapLayout; QSplitter* mapSplitter; MapWidget* mapWidget; MapWidget* mapWidget2; // map: helpers MapDragDropHandler* mapDragDropHandler; GPSGeoIfaceModelHelper* mapModelHelper; ItemMarkerTiler* geoifaceMarkerModel; // map: actions QAction* sortActionOldestFirst; QAction* sortActionYoungestFirst; QMenu* sortMenu; QComboBox* cbMapLayout; GPSBookmarkOwner* bookmarkOwner; QAction* actionBookmarkVisibility; DInfoInterface* iface; }; GeolocationEdit::GeolocationEdit(QAbstractItemModel* const externTagModel, DInfoInterface* const iface, QWidget* const parent) : QDialog(parent), d(new Private) { setWindowFlags((windowFlags() & ~Qt::Dialog) | Qt::Window | Qt::WindowCloseButtonHint | Qt::WindowMinMaxButtonsHint); setAttribute(Qt::WA_DeleteOnClose, true); setWindowTitle(i18n("Geolocation Editor")); setMinimumSize(300, 400); setModal(true); d->iface = iface; d->imageModel = new GPSItemModel(this); d->selectionModel = new QItemSelectionModel(d->imageModel); d->trackManager = new TrackManager(this); #ifdef GPSSYNC_MODELTEST new ModelTest(d->imageModel, this); #endif d->bookmarkOwner = new GPSBookmarkOwner(d->imageModel, this); d->undoStack = new QUndoStack(this); d->stackedWidget = new QStackedWidget(); d->searchWidget = new SearchWidget(d->bookmarkOwner, d->imageModel, d->selectionModel, d->stackedWidget); GPSItemContainer::setHeaderData(d->imageModel); d->mapModelHelper = new GPSGeoIfaceModelHelper(d->imageModel, d->selectionModel, this); d->mapModelHelper->addUngroupedModelHelper(d->bookmarkOwner->bookmarkModelHelper()); d->mapModelHelper->addUngroupedModelHelper(d->searchWidget->getModelHelper()); d->mapDragDropHandler = new MapDragDropHandler(d->imageModel, d->mapModelHelper); d->geoifaceMarkerModel = new ItemMarkerTiler(d->mapModelHelper, this); d->actionBookmarkVisibility = new QAction(this); d->actionBookmarkVisibility->setIcon(QIcon::fromTheme(QLatin1String("bookmark-new"))); d->actionBookmarkVisibility->setToolTip(i18n("Display bookmarked positions on the map.")); d->actionBookmarkVisibility->setCheckable(true); connect(d->actionBookmarkVisibility, SIGNAL(changed()), this, SLOT(slotBookmarkVisibilityToggled())); QVBoxLayout* const mainLayout = new QVBoxLayout(this); setLayout(mainLayout); DHBox* const hboxMain = new DHBox(this); mainLayout->addWidget(hboxMain, 10); d->HSplitter = new QSplitter(Qt::Horizontal, hboxMain); d->HSplitter->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); // ------------------------------------------------------------------------------------------------ DHBox* const hbox = new DHBox(this); QLabel* const labelMapLayout = new QLabel(i18n("Layout:"), hbox); d->cbMapLayout = new QComboBox(hbox); d->cbMapLayout->addItem(i18n("One map"), QVariant::fromValue(MapLayoutOne)); d->cbMapLayout->addItem(i18n("Two maps - horizontal"), QVariant::fromValue(MapLayoutHorizontal)); d->cbMapLayout->addItem(i18n("Two maps - vertical"), QVariant::fromValue(MapLayoutVertical)); labelMapLayout->setBuddy(d->cbMapLayout); d->progressBar = new StatusProgressBar(hbox); d->progressBar->setVisible(false); d->progressBar->setProgressBarMode(StatusProgressBar::ProgressBarMode); d->progressBar->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum); d->progressCancelButton = new QPushButton(hbox); d->progressCancelButton->setVisible(false); d->progressCancelButton->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); d->progressCancelButton->setIcon(QIcon::fromTheme(QLatin1String("dialog-cancel"))); connect(d->progressCancelButton, SIGNAL(clicked()), this, SLOT(slotProgressCancelButtonClicked())); d->buttonBox = new QDialogButtonBox(QDialogButtonBox::Apply | QDialogButtonBox::Close, hbox); connect(d->buttonBox->button(QDialogButtonBox::Apply), &QPushButton::clicked, this, &GeolocationEdit::slotApplyClicked); connect(d->buttonBox->button(QDialogButtonBox::Close), &QPushButton::clicked, this, &GeolocationEdit::close); mainLayout->addWidget(hbox, 0); // ------------------------------------------------------------------------------------------------ d->VSplitter = new QSplitter(Qt::Vertical, d->HSplitter); d->HSplitter->addWidget(d->VSplitter); d->HSplitter->setStretchFactor(0, 10); d->sortMenu = new QMenu(this); d->sortMenu->setTitle(i18n("Sorting")); QActionGroup* const sortOrderExclusive = new QActionGroup(d->sortMenu); sortOrderExclusive->setExclusive(true); connect(sortOrderExclusive, SIGNAL(triggered(QAction*)), this, SLOT(slotSortOptionTriggered(QAction*))); d->sortActionOldestFirst = new QAction(i18n("Show oldest first"), sortOrderExclusive); d->sortActionOldestFirst->setCheckable(true); d->sortMenu->addAction(d->sortActionOldestFirst); d->sortActionYoungestFirst = new QAction(i18n("Show youngest first"), sortOrderExclusive); d->sortMenu->addAction(d->sortActionYoungestFirst); d->sortActionYoungestFirst->setCheckable(true); QWidget* mapVBox = 0; d->mapWidget = makeMapWidget(&mapVBox); d->searchWidget->setPrimaryMapWidget(d->mapWidget); d->mapSplitter = new QSplitter(this); d->mapSplitter->addWidget(mapVBox); d->VSplitter->addWidget(d->mapSplitter); d->treeView = new GPSItemList(this); d->treeView->setModelAndSelectionModel(d->imageModel, d->selectionModel); d->treeView->setDragDropHandler(new GPSItemListDragDropHandler(this)); d->treeView->setDragEnabled(true); // TODO: save and restore the state of the header // TODO: add a context menu to the header to select which columns should be visible // TODO: add sorting by column d->treeView->setSelectionMode(QAbstractItemView::ExtendedSelection); d->treeView->setSortingEnabled(true); d->VSplitter->addWidget(d->treeView); d->listViewContextMenu = new GPSItemListContextMenu(d->treeView, d->bookmarkOwner); d->HSplitter->addWidget(d->stackedWidget); d->HSplitter->setCollapsible(1, true); d->splitterSize = 0; DVBox* const vboxTabBar = new DVBox(hboxMain); vboxTabBar->layout()->setContentsMargins(QMargins()); vboxTabBar->layout()->setSpacing(0); d->tabBar = new QTabBar(vboxTabBar); d->tabBar->setShape(QTabBar::RoundedEast); dynamic_cast(vboxTabBar->layout())->addStretch(200); d->tabBar->addTab(i18n("Details")); d->tabBar->addTab(i18n("GPS Correlator")); d->tabBar->addTab(i18n("Undo/Redo")); d->tabBar->addTab(i18n("Reverse Geocoding")); d->tabBar->addTab(i18n("Search")); d->tabBar->addTab(i18n("KML Export")); d->tabBar->installEventFilter(this); d->detailsWidget = new GPSItemDetails(d->stackedWidget, d->imageModel); d->stackedWidget->addWidget(d->detailsWidget); d->correlatorWidget = new GPSCorrelatorWidget(d->stackedWidget, d->imageModel, d->trackManager); d->stackedWidget->addWidget(d->correlatorWidget); d->undoView = new QUndoView(d->undoStack, d->stackedWidget); d->stackedWidget->addWidget(d->undoView); d->rgWidget = new RGWidget(d->imageModel, d->selectionModel, externTagModel, d->stackedWidget); d->stackedWidget->addWidget(d->rgWidget); d->stackedWidget->addWidget(d->searchWidget); d->kmlWidget = new KmlWidget(this, d->imageModel, d->iface); d->stackedWidget->addWidget(d->kmlWidget); // --------------------------------------------------------------- connect(d->treeView, SIGNAL(signalImageActivated(QModelIndex)), this, SLOT(slotImageActivated(QModelIndex))); connect(d->correlatorWidget, SIGNAL(signalSetUIEnabled(bool)), this, SLOT(slotSetUIEnabled(bool))); connect(d->correlatorWidget, SIGNAL(signalSetUIEnabled(bool,QObject*const,QString)), this, SLOT(slotSetUIEnabled(bool,QObject*const,QString))); connect(d->correlatorWidget, SIGNAL(signalProgressSetup(int,QString)), this, SLOT(slotProgressSetup(int,QString))); connect(d->correlatorWidget, SIGNAL(signalProgressChanged(int)), this, SLOT(slotProgressChanged(int))); connect(d->correlatorWidget, SIGNAL(signalUndoCommand(GPSUndoCommand*)), this, SLOT(slotGPSUndoCommand(GPSUndoCommand*))); connect(d->mapModelHelper, SIGNAL(signalUndoCommand(GPSUndoCommand*)), this, SLOT(slotGPSUndoCommand(GPSUndoCommand*))); connect(d->rgWidget, SIGNAL(signalSetUIEnabled(bool)), this, SLOT(slotSetUIEnabled(bool))); connect(d->rgWidget, SIGNAL(signalSetUIEnabled(bool,QObject*const,QString)), this, SLOT(slotSetUIEnabled(bool,QObject*const,QString))); connect(d->rgWidget, SIGNAL(signalProgressSetup(int,QString)), this, SLOT(slotProgressSetup(int,QString))); connect(d->rgWidget, SIGNAL(signalProgressChanged(int)), this, SLOT(slotProgressChanged(int))); connect(d->rgWidget, SIGNAL(signalUndoCommand(GPSUndoCommand*)), this, SLOT(slotGPSUndoCommand(GPSUndoCommand*))); connect(d->searchWidget, SIGNAL(signalUndoCommand(GPSUndoCommand*)), this, SLOT(slotGPSUndoCommand(GPSUndoCommand*))); connect(d->listViewContextMenu, SIGNAL(signalSetUIEnabled(bool)), this, SLOT(slotSetUIEnabled(bool))); connect(d->listViewContextMenu, SIGNAL(signalSetUIEnabled(bool,QObject*const,QString)), this, SLOT(slotSetUIEnabled(bool,QObject*const,QString))); connect(d->listViewContextMenu, SIGNAL(signalProgressSetup(int,QString)), this, SLOT(slotProgressSetup(int,QString))); connect(d->listViewContextMenu, SIGNAL(signalProgressChanged(int)), this, SLOT(slotProgressChanged(int))); connect(d->listViewContextMenu, SIGNAL(signalUndoCommand(GPSUndoCommand*)), this, SLOT(slotGPSUndoCommand(GPSUndoCommand*))); connect(d->tabBar, SIGNAL(currentChanged(int)), this, SLOT(slotCurrentTabChanged(int))); connect(d->bookmarkOwner->bookmarkModelHelper(), SIGNAL(signalUndoCommand(GPSUndoCommand*)), this, SLOT(slotGPSUndoCommand(GPSUndoCommand*))); connect(d->detailsWidget, SIGNAL(signalUndoCommand(GPSUndoCommand*)), this, SLOT(slotGPSUndoCommand(GPSUndoCommand*))); connect(d->cbMapLayout, SIGNAL(activated(int)), this, SLOT(slotLayoutChanged(int))); readSettings(); d->mapWidget->setActive(true); // Workaround for a drag & drop problem of images to the map for Qt>=5.9.2 - show(); + //show(); } GeolocationEdit::~GeolocationEdit() { delete d; } bool GeolocationEdit::eventFilter(QObject* const o, QEvent* const e) { if ( ( o == d->tabBar ) && ( e->type() == QEvent::MouseButtonPress ) ) { QMouseEvent const* m = static_cast(e); QPoint p (m->x(), m->y()); const int var = d->tabBar->tabAt(p); if (var < 0) { return false; } QList sizes = d->HSplitter->sizes(); if (d->splitterSize == 0) { if (sizes.at(1) == 0) { sizes[1] = d->stackedWidget->widget(var)->minimumSizeHint().width(); } else if (d->tabBar->currentIndex() == var) { d->splitterSize = sizes.at(1); sizes[1] = 0; } } else { sizes[1] = d->splitterSize; d->splitterSize = 0; } d->tabBar->setCurrentIndex(var); d->stackedWidget->setCurrentIndex(var); d->HSplitter->setSizes(sizes); d->detailsWidget->slotSetActive( (d->stackedWidget->currentWidget()==d->detailsWidget) && (d->splitterSize==0) ); return true; } return QWidget::eventFilter(o, e); } void GeolocationEdit::slotCurrentTabChanged(int index) { d->tabBar->setCurrentIndex(index); d->stackedWidget->setCurrentIndex(index); d->detailsWidget->slotSetActive(d->stackedWidget->currentWidget()==d->detailsWidget); } void GeolocationEdit::setCurrentTab(int index) { d->tabBar->setCurrentIndex(index); d->stackedWidget->setCurrentIndex(index); QList sizes = d->HSplitter->sizes(); if (d->splitterSize >= 0) { sizes[1] = d->splitterSize; d->splitterSize = 0; } d->HSplitter->setSizes(sizes); d->detailsWidget->slotSetActive((d->stackedWidget->currentWidget() == d->detailsWidget) && (d->splitterSize == 0)); } void GeolocationEdit::setImages(const QList& images) { QList items; foreach(const QUrl& u, images) { items << new GPSItemContainer(u); } setItems(items); } void GeolocationEdit::setItems(const QList& items) { foreach(GPSItemContainer* const newItem, items) { newItem->loadImageData(); d->imageModel->addItem(newItem); } QList imagesToLoad; for (int i = 0 ; i < d->imageModel->rowCount() ; ++i) { imagesToLoad << d->imageModel->index(i, 0); } slotSetUIEnabled(false); slotProgressSetup(imagesToLoad.count(), i18n("Loading metadata -")); // initiate the saving d->fileIOCountDone = 0; d->fileIOCountTotal = imagesToLoad.count(); d->fileIOFutureWatcher = new QFutureWatcher >(this); connect(d->fileIOFutureWatcher, SIGNAL(resultsReadyAt(int,int)), this, SLOT(slotFileMetadataLoaded(int,int))); d->fileIOFuture = QtConcurrent::mapped(imagesToLoad, LoadFileMetadataHelper(d->imageModel)); d->fileIOFutureWatcher->setFuture(d->fileIOFuture); } void GeolocationEdit::slotFileMetadataLoaded(int beginIndex, int endIndex) { qCDebug(DIGIKAM_GENERAL_LOG) << beginIndex << endIndex; d->fileIOCountDone += (endIndex-beginIndex); slotProgressChanged(d->fileIOCountDone); if (d->fileIOCountDone == d->fileIOCountTotal) { slotSetUIEnabled(true); } } void GeolocationEdit::readSettings() { KSharedConfig::Ptr config = KSharedConfig::openConfig(); KConfigGroup group = config->group("Geolocation Edit Settings"); // -------------------------- // TODO: sanely determine a default backend const KConfigGroup groupMapWidget = KConfigGroup(&group, "Map Widget"); d->mapWidget->readSettingsFromGroup(&groupMapWidget); const KConfigGroup groupCorrelatorWidget = KConfigGroup(&group, "Correlator Widget"); d->correlatorWidget->readSettingsFromGroup(&groupCorrelatorWidget); const KConfigGroup groupTreeView = KConfigGroup(&group, "Tree View"); d->treeView->readSettingsFromGroup(&groupTreeView); const KConfigGroup groupSearchWidget = KConfigGroup(&group, "Search Widget"); d->searchWidget->readSettingsFromGroup(&groupSearchWidget); const KConfigGroup groupRGWidget = KConfigGroup(&group, "Reverse Geocoding Widget"); d->rgWidget->readSettingsFromGroup(&groupRGWidget); const KConfigGroup groupDialog = KConfigGroup(&group, "Dialog"); winId(); windowHandle()->resize(800, 600); DXmlGuiWindow::restoreWindowSize(windowHandle(), groupDialog); resize(windowHandle()->size()); // -------------------------- setCurrentTab(group.readEntry("Current Tab", 0)); const bool showOldestFirst = group.readEntry("Show oldest images first", false); if (showOldestFirst) { d->sortActionOldestFirst->setChecked(true); d->mapWidget->setSortKey(1); } else { d->sortActionYoungestFirst->setChecked(true); d->mapWidget->setSortKey(0); } d->actionBookmarkVisibility->setChecked(group.readEntry("Bookmarks visible", false)); slotBookmarkVisibilityToggled(); if (group.hasKey("SplitterState V1")) { const QByteArray splitterState = QByteArray::fromBase64(group.readEntry("SplitterState V1", QByteArray())); if (!splitterState.isEmpty()) { d->VSplitter->restoreState(splitterState); } } if (group.hasKey("SplitterState H1")) { const QByteArray splitterState = QByteArray::fromBase64(group.readEntry("SplitterState H1", QByteArray())); if (!splitterState.isEmpty()) { d->HSplitter->restoreState(splitterState); } } d->splitterSize = group.readEntry("Splitter H1 CollapsedSize", 0); // ---------------------------------- d->mapLayout = MapLayout(group.readEntry("Map Layout", QVariant::fromValue(int(MapLayoutOne))).value()); d->cbMapLayout->setCurrentIndex(d->mapLayout); adjustMapLayout(false); if (d->mapWidget2) { const KConfigGroup groupMapWidget = KConfigGroup(&group, "Map Widget 2"); d->mapWidget2->readSettingsFromGroup(&groupMapWidget); d->mapWidget2->setActive(true); } } void GeolocationEdit::saveSettings() { KSharedConfig::Ptr config = KSharedConfig::openConfig(); KConfigGroup group = config->group("Geolocation Edit Settings"); // -------------------------- KConfigGroup groupMapWidget = KConfigGroup(&group, "Map Widget"); d->mapWidget->saveSettingsToGroup(&groupMapWidget); if (d->mapWidget2) { KConfigGroup groupMapWidget = KConfigGroup(&group, "Map Widget 2"); d->mapWidget2->saveSettingsToGroup(&groupMapWidget); } KConfigGroup groupCorrelatorWidget = KConfigGroup(&group, "Correlator Widget"); d->correlatorWidget->saveSettingsToGroup(&groupCorrelatorWidget); KConfigGroup groupTreeView = KConfigGroup(&group, "Tree View"); d->treeView->saveSettingsToGroup(&groupTreeView); KConfigGroup groupSearchWidget = KConfigGroup(&group, "Search Widget"); d->searchWidget->saveSettingsToGroup(&groupSearchWidget); KConfigGroup groupRGWidget = KConfigGroup(&group, "Reverse Geocoding Widget"); d->rgWidget->saveSettingsToGroup(&groupRGWidget); KConfigGroup groupDialog = KConfigGroup(&group, "Dialog"); DXmlGuiWindow::saveWindowSize(windowHandle(), groupDialog); // -------------------------- group.writeEntry("Current Tab", d->tabBar->currentIndex()); group.writeEntry("Show oldest images first", d->sortActionOldestFirst->isChecked()); group.writeEntry("SplitterState V1", d->VSplitter->saveState().toBase64()); group.writeEntry("SplitterState H1", d->HSplitter->saveState().toBase64()); group.writeEntry("Splitter H1 CollapsedSize", d->splitterSize); group.writeEntry("Map Layout", QVariant::fromValue(int(d->mapLayout))); group.writeEntry("Bookmarks visible", d->actionBookmarkVisibility->isChecked()); config->sync(); } void GeolocationEdit::closeEvent(QCloseEvent *e) { if (!e) return; // is the UI locked? if (!d->uiEnabled) { // please wait until we are done ... return; } // are there any modified images? int dirtyImagesCount = 0; for (int i = 0; i < d->imageModel->rowCount(); ++i) { const QModelIndex itemIndex = d->imageModel->index(i, 0); GPSItemContainer* const item = d->imageModel->itemFromIndex(itemIndex); if (item->isDirty() || item->isTagListDirty()) { dirtyImagesCount++; } } if (dirtyImagesCount > 0) { const QString message = i18np( "You have 1 modified image.", "You have %1 modified images.", dirtyImagesCount ); const int chosenAction = DMessageBox::showYesNo(QMessageBox::Warning, this, i18n("Unsaved changes"), i18n("%1 Would you like to save the changes you made to them?", message) ); if (chosenAction == QMessageBox::No) { saveSettings(); e->accept(); return; } if (chosenAction == QMessageBox::Yes) { // the user wants to save his changes. // this will initiate the saving process and then close the dialog. saveChanges(true); } // do not close the dialog for now e->ignore(); return; } saveSettings(); e->accept(); } void GeolocationEdit::slotImageActivated(const QModelIndex& index) { d->detailsWidget->slotSetCurrentImage(index); if (!index.isValid()) return; GPSItemContainer* const item = d->imageModel->itemFromIndex(index); if (!item) return; const GeoCoordinates imageCoordinates = item->coordinates(); if (imageCoordinates.hasCoordinates()) { d->mapWidget->setCenter(imageCoordinates); } } void GeolocationEdit::slotSetUIEnabled(const bool enabledState, QObject* const cancelObject, const QString& cancelSlot) { if (enabledState) { // hide the progress bar d->progressBar->setVisible(false); d->progressCancelButton->setVisible(false); d->progressBar->setProgressValue(d->progressBar->progressTotalSteps()); } // TODO: disable the worldmapwidget and the images list (at least disable editing operations) d->progressCancelObject = cancelObject; d->progressCancelSlot = cancelSlot; d->uiEnabled = enabledState; d->buttonBox->setEnabled(enabledState); d->correlatorWidget->setUIEnabledExternal(enabledState); d->detailsWidget->setUIEnabledExternal(enabledState); d->rgWidget->setUIEnabled(enabledState); d->treeView->setEditEnabled(enabledState); d->listViewContextMenu->setEnabled(enabledState); d->mapWidget->setAllowModifications(enabledState); } void GeolocationEdit::slotSetUIEnabled(const bool enabledState) { slotSetUIEnabled(enabledState, 0, QString()); } void GeolocationEdit::saveChanges(const bool closeAfterwards) { // TODO: actually save the changes // are there any modified images? QList dirtyImages; for (int i = 0 ; i < d->imageModel->rowCount() ; ++i) { const QModelIndex itemIndex = d->imageModel->index(i, 0); GPSItemContainer* const item = d->imageModel->itemFromIndex(itemIndex); if (item->isDirty() || item->isTagListDirty()) { dirtyImages << itemIndex; } } if (dirtyImages.isEmpty()) { if (closeAfterwards) { close(); } return; } // TODO: disable the UI and provide progress and cancel information slotSetUIEnabled(false); slotProgressSetup(dirtyImages.count(), i18n("Saving changes -")); // initiate the saving d->fileIOCountDone = 0; d->fileIOCountTotal = dirtyImages.count(); d->fileIOCloseAfterSaving = closeAfterwards; d->fileIOFutureWatcher = new QFutureWatcher >(this); connect(d->fileIOFutureWatcher, SIGNAL(resultsReadyAt(int,int)), this, SLOT(slotFileChangesSaved(int,int))); d->fileIOFuture = QtConcurrent::mapped(dirtyImages, SaveChangedImagesHelper(d->imageModel)); d->fileIOFutureWatcher->setFuture(d->fileIOFuture); } void GeolocationEdit::slotFileChangesSaved(int beginIndex, int endIndex) { qCDebug(DIGIKAM_GENERAL_LOG) << beginIndex << endIndex; d->fileIOCountDone += (endIndex-beginIndex); slotProgressChanged(d->fileIOCountDone); if (d->fileIOCountDone == d->fileIOCountTotal) { slotSetUIEnabled(true); // any errors? QList > errorList; for (int i = 0 ; i < d->fileIOFuture.resultCount() ; ++i) { if (!d->fileIOFuture.resultAt(i).second.isEmpty()) errorList << d->fileIOFuture.resultAt(i); } if (!errorList.isEmpty()) { QStringList errorStrings; for (int i = 0 ; i < errorList.count() ; ++i) { // TODO: how to do kurl->qstring? errorStrings << QString::fromLatin1("%1: %2") .arg(errorList.at(i).first.toLocalFile()) .arg(errorList.at(i).second); } DMessageBox::showInformationList(QMessageBox::Critical, this, i18n("Error"), i18n("Failed to save some information:"), errorStrings); } // done saving files if (d->fileIOCloseAfterSaving) { close(); } } } void GeolocationEdit::slotApplyClicked() { // save the changes, but do not close afterwards saveChanges(false); } void GeolocationEdit::slotProgressChanged(const int currentProgress) { d->progressBar->setProgressValue(currentProgress); } void GeolocationEdit::slotProgressSetup(const int maxProgress, const QString& progressText) { d->progressBar->setProgressText(progressText); d->progressBar->setProgressTotalSteps(maxProgress); d->progressBar->setProgressValue(0); d->progressBar->setNotify(true); d->progressBar->setNotificationTitle(i18n("Edit Geolocation"), QIcon::fromTheme(QLatin1String("globe"))); d->progressBar->setVisible(true); d->progressCancelButton->setVisible(d->progressCancelObject != 0); } void GeolocationEdit::slotGPSUndoCommand(GPSUndoCommand* undoCommand) { d->undoStack->push(undoCommand); } void GeolocationEdit::slotSortOptionTriggered(QAction* /*sortAction*/) { int newSortKey = 0; if (d->sortActionOldestFirst->isChecked()) { newSortKey |= 1; } d->mapWidget->setSortKey(newSortKey); } void GeolocationEdit::slotProgressCancelButtonClicked() { if (d->progressCancelObject) { QTimer::singleShot(0, d->progressCancelObject, d->progressCancelSlot.toUtf8().constData()); d->progressBar->setProgressValue(d->progressBar->progressTotalSteps()); } } void GeolocationEdit::slotBookmarkVisibilityToggled() { d->bookmarkOwner->bookmarkModelHelper()->setVisible(d->actionBookmarkVisibility->isChecked()); } void GeolocationEdit::slotLayoutChanged(int lay) { d->mapLayout = (MapLayout)lay; adjustMapLayout(true); } MapWidget* GeolocationEdit::makeMapWidget(QWidget** const pvbox) { QWidget* const dummyWidget = new QWidget(this); QVBoxLayout* const vbox = new QVBoxLayout(dummyWidget); MapWidget* const mapWidget = new MapWidget(dummyWidget); mapWidget->setAvailableMouseModes(MouseModePan|MouseModeZoomIntoGroup|MouseModeSelectThumbnail); mapWidget->setVisibleMouseModes(MouseModePan|MouseModeZoomIntoGroup|MouseModeSelectThumbnail); mapWidget->setMouseMode(MouseModeSelectThumbnail); mapWidget->setGroupedModel(d->geoifaceMarkerModel); mapWidget->setDragDropHandler(d->mapDragDropHandler); mapWidget->addUngroupedModel(d->bookmarkOwner->bookmarkModelHelper()); mapWidget->addUngroupedModel(d->searchWidget->getModelHelper()); mapWidget->setTrackManager(d->trackManager); mapWidget->setSortOptionsMenu(d->sortMenu); vbox->addWidget(mapWidget); vbox->addWidget(mapWidget->getControlWidget()); QToolButton* const bookmarkVisibilityButton = new QToolButton(mapWidget); bookmarkVisibilityButton->setDefaultAction(d->actionBookmarkVisibility); mapWidget->addWidgetToControlWidget(bookmarkVisibilityButton); *pvbox = dummyWidget; return mapWidget; } void GeolocationEdit::adjustMapLayout(const bool syncSettings) { if (d->mapLayout == MapLayoutOne) { if (d->mapSplitter->count()>1) { delete d->mapSplitter->widget(1); d->mapWidget2 = 0; } } else { if (d->mapSplitter->count() == 1) { QWidget* mapHolder = 0; d->mapWidget2 = makeMapWidget(&mapHolder); d->mapSplitter->addWidget(mapHolder); if (syncSettings) { KSharedConfig::Ptr config = KSharedConfig::openConfig(); KConfigGroup group = config->group("Geolocation Edit Settings"); const KConfigGroup groupMapWidget = KConfigGroup(&group, "Map Widget"); d->mapWidget2->readSettingsFromGroup(&groupMapWidget); d->mapWidget2->setActive(true); } } if (d->mapLayout == MapLayoutHorizontal) { d->mapSplitter->setOrientation(Qt::Horizontal); } else { d->mapSplitter->setOrientation(Qt::Vertical); } } } } // namespace Digikam diff --git a/core/utilities/geolocation/geoiface/widgets/mapwidget.cpp b/core/utilities/geolocation/geoiface/widgets/mapwidget.cpp index 30d0f9f528..d5aa4a0429 100644 --- a/core/utilities/geolocation/geoiface/widgets/mapwidget.cpp +++ b/core/utilities/geolocation/geoiface/widgets/mapwidget.cpp @@ -1,2335 +1,2334 @@ /* ============================================================ * * This file is a part of digiKam project * http://www.digikam.org * * Date : 2009-12-01 * Description : world map widget library * * Copyright (C) 2010-2019 by Gilles Caulier * Copyright (C) 2009-2011 by Michael G. Hansen * Copyright (C) 2014 by Justus Schwartz * * 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 "mapwidget.h" // C++ includes #include // Qt includes #include #include #include #include #include #include #include #include #include #include #include #include // KDE includes #include #include #include // Marbel includes #include #include #include // local includes #include "geoifacecommon.h" #include "geodragdrophandler.h" #include "geomodelhelper.h" #include "trackmanager.h" #include "placeholderwidget.h" #include "tilegrouper.h" #include "digikam_debug.h" #include "abstractmarkertiler.h" #include "backendgooglemaps.h" #include "backendmarble.h" namespace Digikam { /** * @class MapWidget * @brief The central map view class of geolocation interface * * The MapWidget class is the central widget of geolocation interface. It provides a widget which can display maps using * either the Marble or Google Maps backend. Using a model, items can be displayed on the map. For * models containing only a small number of items, the items can be shown directly, but for models with * a larger number of items, the items can also be grouped. Currently, any number of ungrouped models * can be shown, but only one grouped model. Item selection models can also be used along with the models, * to interact with the selection states of the items on the map. In order to use a model with geolocation interface, however, * a model helper has to be implemented, which extracts data from the model that is not provided by the Qt part * of a model's API. * * Now, a brief introduction on how to get geolocation interface working is provided: * @li First, an instance of @c MapWidget has to be created. * @li Next, @c GeoModelHelper has to be subclassed and at least the pure virtual functions have to be implemented. * @li To show the model's data ungrouped, the model helper has to be added to @c MapWidget instance using addUngroupedModel. * @li To show the model's data grouped, an instance of @c AbstractMarkerTiler has to be created and the model helper has to be * set to it using setMarkerGeoModelHelper. The @c AbstractMarkerTiler has then to be given to MapWidget using setGroupedModel. If * the items to be displayed do not reside in a model, a subclass of @c AbstractMarkerTiler can be created which returns * just the number of items in a particular area, and picks representative items for thumbnails. * @li To handle dropping of items from the host applications UI onto the map, @c DragDropHandler has to be subclassed * as well and added to the model using setDragDropHandler. * @li Finally, setActive() has to be called to tell the widget that it should start displaying things. */ class Q_DECL_HIDDEN MapWidget::Private { public: explicit Private() : loadedBackends(), currentBackend(0), currentBackendName(), stackedLayout(0), cacheCenterCoordinate(52.0,6.0), cacheZoom(QLatin1String("marble:900")), configurationMenu(0), actionGroupBackendSelection(0), actionZoomIn(0), actionZoomOut(0), actionShowThumbnails(0), mouseModesHolder(0), controlWidget(0), actionPreviewSingleItems(0), actionPreviewGroupedItems(0), actionShowNumbersOnItems(0), lazyReclusteringRequested(false), dragDropHandler(0), sortMenu(0), actionIncreaseThumbnailSize(0), actionDecreaseThumbnailSize(0), hBoxForAdditionalControlWidgetItems(0), mouseModeActionGroup(0), actionRemoveCurrentRegionSelection(0), actionSetRegionSelectionMode(0), actionSetPanMode(0), actionSetZoomIntoGroupMode(0), actionSetRegionSelectionFromIconMode(0), actionSetFilterMode(0), actionRemoveFilter(0), actionSetSelectThumbnailMode(0), setPanModeButton(0), setSelectionModeButton(0), removeCurrentSelectionButton(0), setZoomModeButton(0), setRegionSelectionFromIconModeButton(0), setFilterModeButton(0), removeFilterModeButton(0), setSelectThumbnailMode(0), thumbnailTimer(0), thumbnailTimerCount(0), thumbnailsHaveBeenLoaded(false), availableExtraActions(0), visibleExtraActions(0), actionStickyMode(0), buttonStickyMode(0), placeholderWidget(0) { } QList loadedBackends; MapBackend* currentBackend; QString currentBackendName; QStackedLayout* stackedLayout; // these values are cached in case the backend is not ready: GeoCoordinates cacheCenterCoordinate; QString cacheZoom; // actions for controlling the widget QMenu* configurationMenu; QActionGroup* actionGroupBackendSelection; QAction* actionZoomIn; QAction* actionZoomOut; QAction* actionShowThumbnails; QWidget* mouseModesHolder; QPointer controlWidget; QAction* actionPreviewSingleItems; QAction* actionPreviewGroupedItems; QAction* actionShowNumbersOnItems; bool lazyReclusteringRequested; GeoDragDropHandler* dragDropHandler; QMenu* sortMenu; QAction* actionIncreaseThumbnailSize; QAction* actionDecreaseThumbnailSize; QWidget* hBoxForAdditionalControlWidgetItems; QActionGroup* mouseModeActionGroup; QAction* actionRemoveCurrentRegionSelection; QAction* actionSetRegionSelectionMode; QAction* actionSetPanMode; QAction* actionSetZoomIntoGroupMode; QAction* actionSetRegionSelectionFromIconMode; QAction* actionSetFilterMode; QAction* actionRemoveFilter; QAction* actionSetSelectThumbnailMode; QToolButton* setPanModeButton; QToolButton* setSelectionModeButton; QToolButton* removeCurrentSelectionButton; QToolButton* setZoomModeButton; QToolButton* setRegionSelectionFromIconModeButton; QToolButton* setFilterModeButton; QToolButton* removeFilterModeButton; QToolButton* setSelectThumbnailMode; QTimer* thumbnailTimer; int thumbnailTimerCount; bool thumbnailsHaveBeenLoaded; GeoExtraActions availableExtraActions; GeoExtraActions visibleExtraActions; QAction* actionStickyMode; QToolButton* buttonStickyMode; // to be sorted later PlaceholderWidget* placeholderWidget; }; MapWidget::MapWidget(QWidget* const parent) : QWidget(parent), s(new GeoIfaceSharedData), d(new Private) { createActions(); s->worldMapWidget = this; s->tileGrouper = new TileGrouper(s, this); d->stackedLayout = new QStackedLayout(this); setLayout(d->stackedLayout); d->placeholderWidget = new PlaceholderWidget(); d->stackedLayout->addWidget(d->placeholderWidget); d->loadedBackends.append(new BackendGoogleMaps(s, this)); d->loadedBackends.append(new BackendMarble(s, this)); // d->loadedBackends.append(new BackendOSM(s, this)); createActionsForBackendSelection(); setAcceptDrops(true); } void MapWidget::createActions() { d->actionZoomIn = new QAction(this); d->actionZoomIn->setIcon(QIcon::fromTheme( QLatin1String("zoom-in") )); d->actionZoomIn->setToolTip(i18n("Zoom in")); connect(d->actionZoomIn, &QAction::triggered, this, &MapWidget::slotZoomIn); d->actionZoomOut = new QAction(this); d->actionZoomOut->setIcon(QIcon::fromTheme( QLatin1String("zoom-out") )); d->actionZoomOut->setToolTip(i18n("Zoom out")); connect(d->actionZoomOut, &QAction::triggered, this, &MapWidget::slotZoomOut); d->actionShowThumbnails = new QAction(this); d->actionShowThumbnails->setToolTip(i18n("Switch between markers and thumbnails.")); d->actionShowThumbnails->setCheckable(true); d->actionShowThumbnails->setChecked(true); connect(d->actionShowThumbnails, &QAction::triggered, this, &MapWidget::slotShowThumbnailsChanged); // create backend selection entries: d->actionGroupBackendSelection = new QActionGroup(this); d->actionGroupBackendSelection->setExclusive(true); connect(d->actionGroupBackendSelection, &QActionGroup::triggered, this, &MapWidget::slotChangeBackend); createActionsForBackendSelection(); d->configurationMenu = new QMenu(this); d->actionPreviewSingleItems = new QAction(i18n("Preview single items"), this); d->actionPreviewSingleItems->setCheckable(true); d->actionPreviewSingleItems->setChecked(true); d->actionPreviewGroupedItems = new QAction(i18n("Preview grouped items"), this); d->actionPreviewGroupedItems->setCheckable(true); d->actionPreviewGroupedItems->setChecked(true); d->actionShowNumbersOnItems = new QAction(i18n("Show numbers"), this); d->actionShowNumbersOnItems->setCheckable(true); d->actionShowNumbersOnItems->setChecked(true); d->actionIncreaseThumbnailSize = new QAction(i18n("T+"), this); d->actionIncreaseThumbnailSize->setToolTip(i18n("Increase the thumbnail size on the map")); d->actionDecreaseThumbnailSize = new QAction(i18n("T-"), this); d->actionDecreaseThumbnailSize->setToolTip(i18n("Decrease the thumbnail size on the map")); d->actionRemoveCurrentRegionSelection = new QAction(this); //d->actionRemoveCurrentRegionSelection->setEnabled(false); d->actionRemoveCurrentRegionSelection->setIcon(QIcon::fromTheme( QLatin1String("edit-clear") )); d->actionRemoveCurrentRegionSelection->setToolTip(i18n("Remove the current region selection")); d->mouseModeActionGroup = new QActionGroup(this); d->mouseModeActionGroup->setExclusive(true); d->actionSetRegionSelectionMode = new QAction(d->mouseModeActionGroup); d->actionSetRegionSelectionMode->setCheckable(true); d->actionSetRegionSelectionMode->setIcon(QIcon::fromTheme( QLatin1String("select-rectangular") )); d->actionSetRegionSelectionMode->setToolTip(i18n("Select images by drawing a rectangle")); d->actionSetRegionSelectionMode->setData(QVariant::fromValue(MouseModeRegionSelection)); d->actionSetPanMode = new QAction(d->mouseModeActionGroup); d->actionSetPanMode->setCheckable(true); d->actionSetPanMode->setToolTip(i18n("Pan mode")); d->actionSetPanMode->setIcon(QIcon::fromTheme( QLatin1String("transform-move") )); d->actionSetPanMode->setChecked(true); d->actionSetPanMode->setData(QVariant::fromValue(MouseModePan)); d->actionSetZoomIntoGroupMode = new QAction(d->mouseModeActionGroup); d->actionSetZoomIntoGroupMode->setCheckable(true); d->actionSetZoomIntoGroupMode->setToolTip(i18n("Zoom into a group")); d->actionSetZoomIntoGroupMode->setIcon(QIcon::fromTheme( QLatin1String("zoom-fit-best") )); d->actionSetZoomIntoGroupMode->setData(QVariant::fromValue(MouseModeZoomIntoGroup)); d->actionSetRegionSelectionFromIconMode = new QAction(d->mouseModeActionGroup); d->actionSetRegionSelectionFromIconMode->setCheckable(true); d->actionSetRegionSelectionFromIconMode->setToolTip(i18n("Create a region selection from a thumbnail")); d->actionSetRegionSelectionFromIconMode->setIcon(QIcon::fromTheme( QLatin1String("edit-node") )); d->actionSetRegionSelectionFromIconMode->setData(QVariant::fromValue(MouseModeRegionSelectionFromIcon)); d->actionSetFilterMode = new QAction(d->mouseModeActionGroup); d->actionSetFilterMode->setCheckable(true); d->actionSetFilterMode->setToolTip(i18n("Filter images")); d->actionSetFilterMode->setIcon(QIcon::fromTheme( QLatin1String("view-filter") )); d->actionSetFilterMode->setData(QVariant::fromValue(MouseModeFilter)); d->actionRemoveFilter = new QAction(this); d->actionRemoveFilter->setToolTip(i18n("Remove the current filter")); d->actionRemoveFilter->setIcon(QIcon::fromTheme( QLatin1String("window-close") )); d->actionSetSelectThumbnailMode = new QAction(d->mouseModeActionGroup); d->actionSetSelectThumbnailMode->setCheckable(true); d->actionSetSelectThumbnailMode->setToolTip(i18n("Select images")); d->actionSetSelectThumbnailMode->setIcon(QIcon::fromTheme( QLatin1String("edit-select") )); d->actionSetSelectThumbnailMode->setData(QVariant::fromValue(MouseModeSelectThumbnail)); d->actionStickyMode = new QAction(this); d->actionStickyMode->setCheckable(true); d->actionStickyMode->setToolTip(i18n("Lock the map position")); connect(d->actionStickyMode, &QAction::triggered, this, &MapWidget::slotStickyModeChanged); connect(d->actionIncreaseThumbnailSize, &QAction::triggered, this, &MapWidget::slotIncreaseThumbnailSize); connect(d->actionDecreaseThumbnailSize, &QAction::triggered, this, &MapWidget::slotDecreaseThumbnailSize); connect(d->actionPreviewSingleItems, &QAction::changed, this, &MapWidget::slotItemDisplaySettingsChanged); connect(d->actionPreviewGroupedItems, &QAction::changed, this, &MapWidget::slotItemDisplaySettingsChanged); connect(d->actionShowNumbersOnItems, &QAction::changed, this, &MapWidget::slotItemDisplaySettingsChanged); connect(d->mouseModeActionGroup, &QActionGroup::triggered, this, &MapWidget::slotMouseModeChanged); connect(d->actionRemoveFilter, &QAction::triggered, this, &MapWidget::signalRemoveCurrentFilter); connect(d->actionRemoveCurrentRegionSelection, &QAction::triggered, this, &MapWidget::slotRemoveCurrentRegionSelection); } void MapWidget::createActionsForBackendSelection() { // delete the existing actions: qDeleteAll(d->actionGroupBackendSelection->actions()); // create actions for all backends: for (int i = 0 ; i < d->loadedBackends.size() ; ++i) { const QString backendName = d->loadedBackends.at(i)->backendName(); QAction* const backendAction = new QAction(d->actionGroupBackendSelection); backendAction->setData(backendName); backendAction->setText(d->loadedBackends.at(i)->backendHumanName()); backendAction->setCheckable(true); } } MapWidget::~MapWidget() { // release all widgets: for (int i = 0 ; i < d->stackedLayout->count() ; ++i) { d->stackedLayout->removeWidget(d->stackedLayout->widget(i)); } qDeleteAll(d->loadedBackends); delete d; /// @todo delete s, but make sure it is not accessed by any other objects any more! } QStringList MapWidget::availableBackends() const { QStringList result; MapBackend* backend = 0; foreach(backend, d->loadedBackends) { result.append(backend->backendName()); } return result; } bool MapWidget::setBackend(const QString& backendName) { if (backendName == d->currentBackendName) { return true; } saveBackendToCache(); // switch to the placeholder widget: setShowPlaceholderWidget(true); removeMapWidgetFromFrame(); // disconnect signals from old backend: if (d->currentBackend) { d->currentBackend->setActive(false); disconnect(d->currentBackend, SIGNAL(signalBackendReadyChanged(QString)), this, SLOT(slotBackendReadyChanged(QString))); disconnect(d->currentBackend, SIGNAL(signalZoomChanged(QString)), this, SLOT(slotBackendZoomChanged(QString))); disconnect(d->currentBackend, SIGNAL(signalClustersMoved(QIntList,QPair)), this, SLOT(slotClustersMoved(QIntList,QPair))); disconnect(d->currentBackend, SIGNAL(signalClustersClicked(QIntList)), this, SLOT(slotClustersClicked(QIntList))); disconnect(this, SIGNAL(signalUngroupedModelChanged(int)), d->currentBackend, SLOT(slotUngroupedModelChanged(int))); if (s->markerModel) { disconnect(s->markerModel, SIGNAL(signalThumbnailAvailableForIndex(QVariant,QPixmap)), d->currentBackend, SLOT(slotThumbnailAvailableForIndex(QVariant,QPixmap))); } disconnect(d->currentBackend, SIGNAL(signalSelectionHasBeenMade(Digikam::GeoCoordinates::Pair)), this, SLOT(slotNewSelectionFromMap(Digikam::GeoCoordinates::Pair))); } MapBackend* backend = 0; foreach(backend, d->loadedBackends) { if (backend->backendName() == backendName) { qCDebug(DIGIKAM_GEOIFACE_LOG) << QString::fromLatin1("setting backend %1").arg(backendName); d->currentBackend = backend; d->currentBackendName = backendName; connect(d->currentBackend, &MapBackend::signalBackendReadyChanged, this, &MapWidget::slotBackendReadyChanged); connect(d->currentBackend, &MapBackend::signalZoomChanged, this, &MapWidget::slotBackendZoomChanged); connect(d->currentBackend, &MapBackend::signalClustersMoved, this, &MapWidget::slotClustersMoved); connect(d->currentBackend, &MapBackend::signalClustersClicked, this, &MapWidget::slotClustersClicked); /** * @todo This connection is queued because otherwise QAbstractItemModel::itemSelected does not * reflect the true state. Maybe monitor another signal instead? */ connect(this, SIGNAL(signalUngroupedModelChanged(int)), d->currentBackend, SLOT(slotUngroupedModelChanged(int)), Qt::QueuedConnection); if (s->markerModel) { connect(s->markerModel, SIGNAL(signalThumbnailAvailableForIndex(QVariant,QPixmap)), d->currentBackend, SLOT(slotThumbnailAvailableForIndex(QVariant,QPixmap))); } connect(d->currentBackend, &MapBackend::signalSelectionHasBeenMade, this, &MapWidget::slotNewSelectionFromMap); if (s->activeState) { setMapWidgetInFrame(d->currentBackend->mapWidget()); // call this slot manually in case the backend was ready right away: if (d->currentBackend->isReady()) { slotBackendReadyChanged(d->currentBackendName); } else { rebuildConfigurationMenu(); } } d->currentBackend->setActive(s->activeState); return true; } } return false; } void MapWidget::applyCacheToBackend() { if ((!currentBackendReady()) || (!s->activeState)) { return; } /// @todo Only do this if the zoom was changed! qCDebug(DIGIKAM_GEOIFACE_LOG) << d->cacheZoom; setZoom(d->cacheZoom); setCenter(d->cacheCenterCoordinate); d->currentBackend->mouseModeChanged(); d->currentBackend->regionSelectionChanged(); } void MapWidget::saveBackendToCache() { if (!currentBackendReady()) { return; } d->cacheCenterCoordinate = getCenter(); d->cacheZoom = getZoom(); } GeoCoordinates MapWidget::getCenter() const { if (!currentBackendReady()) { return d->cacheCenterCoordinate; } return d->currentBackend->getCenter(); } void MapWidget::setCenter(const GeoCoordinates& coordinate) { d->cacheCenterCoordinate = coordinate; if (!currentBackendReady()) { return; } d->currentBackend->setCenter(coordinate); } void MapWidget::slotBackendReadyChanged(const QString& backendName) { qCDebug(DIGIKAM_GEOIFACE_LOG) << QString::fromLatin1("backend %1 is ready!").arg(backendName); if (backendName != d->currentBackendName) { return; } if (!currentBackendReady()) { return; } applyCacheToBackend(); setShowPlaceholderWidget(false); if (!d->thumbnailsHaveBeenLoaded) { d->thumbnailTimer = new QTimer(this); d->thumbnailTimerCount = 0; connect(d->thumbnailTimer, &QTimer::timeout, this, &MapWidget::stopThumbnailTimer); d->thumbnailTimer->start(2000); } updateMarkers(); markClustersAsDirty(); rebuildConfigurationMenu(); } void MapWidget::stopThumbnailTimer() { d->currentBackend->updateMarkers(); d->thumbnailTimerCount++; if (d->thumbnailTimerCount == 10) { d->thumbnailTimer->stop(); d->thumbnailsHaveBeenLoaded = true; } } void MapWidget::saveSettingsToGroup(KConfigGroup* const group) { GEOIFACE_ASSERT(group != 0); if (!group) return; if (!d->currentBackendName.isEmpty()) { group->writeEntry("Backend", d->currentBackendName); } group->writeEntry("Center", getCenter().geoUrl()); group->writeEntry("Zoom", getZoom()); group->writeEntry("Preview Single Items", s->previewSingleItems); group->writeEntry("Preview Grouped Items", s->previewGroupedItems); group->writeEntry("Show numbers on items", s->showNumbersOnItems); group->writeEntry("Thumbnail Size", s->thumbnailSize); group->writeEntry("Thumbnail Grouping Radius", s->thumbnailGroupingRadius); group->writeEntry("Marker Grouping Radius", s->markerGroupingRadius); group->writeEntry("Show Thumbnails", s->showThumbnails); group->writeEntry("Mouse Mode", int(s->currentMouseMode)); if (d->visibleExtraActions.testFlag(ExtraActionSticky)) { group->writeEntry("Sticky Mode State", d->actionStickyMode->isChecked()); } for (int i = 0 ; i < d->loadedBackends.size() ; ++i) { d->loadedBackends.at(i)->saveSettingsToGroup(group); } } void MapWidget::readSettingsFromGroup(const KConfigGroup* const group) { GEOIFACE_ASSERT(group != 0); if (!group) { return; } setBackend(group->readEntry("Backend", "marble")); // Options concerning the display of markers d->actionPreviewSingleItems->setChecked(group->readEntry("Preview Single Items", true)); d->actionPreviewGroupedItems->setChecked(group->readEntry("Preview Grouped Items", true)); d->actionShowNumbersOnItems->setChecked(group->readEntry("Show numbers on items", true)); setThumnailSize(group->readEntry("Thumbnail Size", 2*GeoIfaceMinThumbnailSize)); setThumbnailGroupingRadius(group->readEntry("Thumbnail Grouping Radius", 2*GeoIfaceMinThumbnailGroupingRadius)); setMarkerGroupingRadius(group->readEntry("Edit Grouping Radius", GeoIfaceMinMarkerGroupingRadius)); s->showThumbnails = group->readEntry("Show Thumbnails", s->showThumbnails); d->actionShowThumbnails->setChecked(s->showThumbnails); d->actionStickyMode->setChecked(group->readEntry("Sticky Mode State", d->actionStickyMode->isChecked())); // let the backends load their settings for (int i = 0 ; i < d->loadedBackends.size() ; ++i) { d->loadedBackends.at(i)->readSettingsFromGroup(group); } // current map state const GeoCoordinates centerDefault = GeoCoordinates(52.0, 6.0); const QString centerGeoUrl = group->readEntry("Center", centerDefault.geoUrl()); bool centerGeoUrlValid = false; const GeoCoordinates centerCoordinate = GeoCoordinates::fromGeoUrl(centerGeoUrl, ¢erGeoUrlValid); d->cacheCenterCoordinate = centerGeoUrlValid ? centerCoordinate : centerDefault; d->cacheZoom = group->readEntry("Zoom", d->cacheZoom); s->currentMouseMode = GeoMouseModes(group->readEntry("Mouse Mode", int(s->currentMouseMode))); // propagate the loaded values to the map, if appropriate applyCacheToBackend(); slotUpdateActionsEnabled(); } void MapWidget::rebuildConfigurationMenu() { d->configurationMenu->clear(); const QList backendSelectionActions = d->actionGroupBackendSelection->actions(); for (int i = 0 ; i < backendSelectionActions.count() ; ++i) { QAction* const backendAction = backendSelectionActions.at(i); if (backendAction->data().toString()==d->currentBackendName) { backendAction->setChecked(true); } d->configurationMenu->addAction(backendAction); } if (currentBackendReady()) { d->currentBackend->addActionsToConfigurationMenu(d->configurationMenu); } if (s->showThumbnails) { d->configurationMenu->addSeparator(); if (d->sortMenu) { d->configurationMenu->addMenu(d->sortMenu); } d->configurationMenu->addAction(d->actionPreviewSingleItems); d->configurationMenu->addAction(d->actionPreviewGroupedItems); d->configurationMenu->addAction(d->actionShowNumbersOnItems); } slotUpdateActionsEnabled(); } QAction* MapWidget::getControlAction(const QString& actionName) { if (actionName == QLatin1String("zoomin")) { return d->actionZoomIn; } else if (actionName == QLatin1String("zoomout")) { return d->actionZoomOut; } else if (actionName == QLatin1String("mousemode-regionselectionmode")) { return d->actionSetRegionSelectionMode; } else if (actionName == QLatin1String("mousemode-removecurrentregionselection")) { return d->actionRemoveCurrentRegionSelection; } else if (actionName == QLatin1String("mousemode-regionselectionfromiconmode")) { return d->actionSetRegionSelectionFromIconMode; } else if (actionName == QLatin1String("mousemode-removefilter")) { return d->actionRemoveFilter; } return 0; } /** * @brief Returns the control widget. */ QWidget* MapWidget::getControlWidget() { if (!d->controlWidget) { d->controlWidget = new QWidget(this); QHBoxLayout* const controlWidgetHBoxLayout = new QHBoxLayout(d->controlWidget); controlWidgetHBoxLayout->setContentsMargins(QMargins()); QToolButton* const configurationButton = new QToolButton(d->controlWidget); controlWidgetHBoxLayout->addWidget(configurationButton); configurationButton->setToolTip(i18n("Map settings")); configurationButton->setIcon(QIcon::fromTheme( QLatin1String("globe") )); configurationButton->setMenu(d->configurationMenu); configurationButton->setPopupMode(QToolButton::InstantPopup); QToolButton* const zoomInButton = new QToolButton(d->controlWidget); controlWidgetHBoxLayout->addWidget(zoomInButton); zoomInButton->setDefaultAction(d->actionZoomIn); QToolButton* const zoomOutButton = new QToolButton(d->controlWidget); controlWidgetHBoxLayout->addWidget(zoomOutButton); zoomOutButton->setDefaultAction(d->actionZoomOut); QToolButton* const showThumbnailsButton = new QToolButton(d->controlWidget); controlWidgetHBoxLayout->addWidget(showThumbnailsButton); showThumbnailsButton->setDefaultAction(d->actionShowThumbnails); QFrame* const vline1 = new QFrame(d->controlWidget); vline1->setLineWidth(1); vline1->setMidLineWidth(0); vline1->setFrameShape(QFrame::VLine); vline1->setFrameShadow(QFrame::Sunken); vline1->setMinimumSize(2, 0); vline1->updateGeometry(); controlWidgetHBoxLayout->addWidget(vline1); QToolButton* const increaseThumbnailSizeButton = new QToolButton(d->controlWidget); controlWidgetHBoxLayout->addWidget(increaseThumbnailSizeButton); increaseThumbnailSizeButton->setDefaultAction(d->actionIncreaseThumbnailSize); QToolButton* const decreaseThumbnailSizeButton = new QToolButton(d->controlWidget); controlWidgetHBoxLayout->addWidget(decreaseThumbnailSizeButton); decreaseThumbnailSizeButton->setDefaultAction(d->actionDecreaseThumbnailSize); /* --- --- --- */ d->mouseModesHolder = new QWidget(d->controlWidget); QHBoxLayout* const mouseModesHolderHBoxLayout = new QHBoxLayout(d->mouseModesHolder); mouseModesHolderHBoxLayout->setContentsMargins(QMargins()); controlWidgetHBoxLayout->addWidget(d->mouseModesHolder); QFrame* const vline2 = new QFrame(d->mouseModesHolder); vline2->setLineWidth(1); vline2->setMidLineWidth(0); vline2->setFrameShape(QFrame::VLine); vline2->setFrameShadow(QFrame::Sunken); vline2->setMinimumSize(2, 0); vline2->updateGeometry(); mouseModesHolderHBoxLayout->addWidget(vline2); d->setPanModeButton = new QToolButton(d->mouseModesHolder); mouseModesHolderHBoxLayout->addWidget(d->setPanModeButton); d->setPanModeButton->setDefaultAction(d->actionSetPanMode); d->setSelectionModeButton = new QToolButton(d->mouseModesHolder); mouseModesHolderHBoxLayout->addWidget(d->setSelectionModeButton); d->setSelectionModeButton->setDefaultAction(d->actionSetRegionSelectionMode); d->setRegionSelectionFromIconModeButton = new QToolButton(d->mouseModesHolder); mouseModesHolderHBoxLayout->addWidget(d->setRegionSelectionFromIconModeButton); d->setRegionSelectionFromIconModeButton->setDefaultAction(d->actionSetRegionSelectionFromIconMode); d->removeCurrentSelectionButton = new QToolButton(d->mouseModesHolder); mouseModesHolderHBoxLayout->addWidget(d->removeCurrentSelectionButton); d->removeCurrentSelectionButton->setDefaultAction(d->actionRemoveCurrentRegionSelection); d->setZoomModeButton = new QToolButton(d->mouseModesHolder); mouseModesHolderHBoxLayout->addWidget(d->setZoomModeButton); d->setZoomModeButton->setDefaultAction(d->actionSetZoomIntoGroupMode); d->setFilterModeButton = new QToolButton(d->mouseModesHolder); mouseModesHolderHBoxLayout->addWidget(d->setFilterModeButton); d->setFilterModeButton->setDefaultAction(d->actionSetFilterMode); d->removeFilterModeButton = new QToolButton(d->mouseModesHolder); mouseModesHolderHBoxLayout->addWidget(d->removeFilterModeButton); d->removeFilterModeButton->setDefaultAction(d->actionRemoveFilter); d->setSelectThumbnailMode = new QToolButton(d->mouseModesHolder); mouseModesHolderHBoxLayout->addWidget(d->setSelectThumbnailMode); d->setSelectThumbnailMode->setDefaultAction(d->actionSetSelectThumbnailMode); d->buttonStickyMode = new QToolButton(d->controlWidget); controlWidgetHBoxLayout->addWidget(d->buttonStickyMode); d->buttonStickyMode->setDefaultAction(d->actionStickyMode); d->hBoxForAdditionalControlWidgetItems = new QWidget(d->controlWidget); QHBoxLayout *hBoxForAdditionalControlWidgetItemsHBoxLayout = new QHBoxLayout(d->hBoxForAdditionalControlWidgetItems); hBoxForAdditionalControlWidgetItemsHBoxLayout->setContentsMargins(QMargins()); controlWidgetHBoxLayout->addWidget(d->hBoxForAdditionalControlWidgetItems); setVisibleMouseModes(s->visibleMouseModes); setVisibleExtraActions(d->visibleExtraActions); // add stretch after the controls: QHBoxLayout* const hBoxLayout = reinterpret_cast(d->controlWidget->layout()); if (hBoxLayout) { hBoxLayout->addStretch(); } } // make sure the menu exists, even if no backend has been set: rebuildConfigurationMenu(); return d->controlWidget; } void MapWidget::slotZoomIn() { if (!currentBackendReady()) return; d->currentBackend->zoomIn(); } void MapWidget::slotZoomOut() { if (!currentBackendReady()) return; d->currentBackend->zoomOut(); } void MapWidget::slotUpdateActionsEnabled() { if (!s->activeState) { // this widget is not active, no need to update the action availability return; } d->actionDecreaseThumbnailSize->setEnabled((s->showThumbnails)&&(s->thumbnailSize>GeoIfaceMinThumbnailSize)); /// @todo Define an upper limit for the thumbnail size! d->actionIncreaseThumbnailSize->setEnabled(s->showThumbnails); d->actionSetRegionSelectionMode->setEnabled(s->availableMouseModes.testFlag(MouseModeRegionSelection)); d->actionSetPanMode->setEnabled(s->availableMouseModes.testFlag(MouseModePan)); d->actionSetZoomIntoGroupMode->setEnabled(s->availableMouseModes.testFlag(MouseModeZoomIntoGroup)); d->actionSetRegionSelectionFromIconMode->setEnabled(s->availableMouseModes.testFlag(MouseModeRegionSelectionFromIcon)); d->actionSetFilterMode->setEnabled(s->availableMouseModes.testFlag(MouseModeFilter)); d->actionSetSelectThumbnailMode->setEnabled(s->availableMouseModes.testFlag(MouseModeSelectThumbnail)); // the 'Remove X' actions are only available if the corresponding X is actually there: bool clearRegionSelectionAvailable = s->availableMouseModes.testFlag(MouseModeRegionSelection); if (clearRegionSelectionAvailable && s->markerModel) { clearRegionSelectionAvailable = s->markerModel->getGlobalGroupState() & RegionSelectedMask; } d->actionRemoveCurrentRegionSelection->setEnabled(clearRegionSelectionAvailable); bool clearFilterAvailable = s->availableMouseModes.testFlag(MouseModeRegionSelectionFromIcon); if (clearFilterAvailable && s->markerModel) { clearFilterAvailable = s->markerModel->getGlobalGroupState() & FilteredPositiveMask; } d->actionRemoveFilter->setEnabled(clearFilterAvailable); d->actionStickyMode->setEnabled(d->availableExtraActions.testFlag(ExtraActionSticky)); /// @todo Only set the icons if they have to be changed! d->actionStickyMode->setIcon(QIcon::fromTheme(QLatin1String(d->actionStickyMode->isChecked() ? "document-encrypted" : "document-decrypt"))); d->actionShowThumbnails->setIcon(d->actionShowThumbnails->isChecked() ? QIcon::fromTheme(QLatin1String("folder-pictures")) : GeoIfaceGlobalObject::instance()->getMarkerPixmap(QLatin1String("marker-icon-16x16"))); // make sure the action for the current mouse mode is checked const QList mouseModeActions = d->mouseModeActionGroup->actions(); foreach(QAction* const action, mouseModeActions) { if (action->data().value() == s->currentMouseMode) { action->setChecked(true); break; } } } void MapWidget::slotChangeBackend(QAction* action) { GEOIFACE_ASSERT(action != 0); if (!action) return; const QString newBackendName = action->data().toString(); setBackend(newBackendName); } void MapWidget::updateMarkers() { if (!currentBackendReady()) { return; } // tell the backend to update the markers d->currentBackend->updateMarkers(); } void MapWidget::updateClusters() { /// @todo Find a better way to tell the TileGrouper about the backend s->tileGrouper->setCurrentBackend(d->currentBackend); s->tileGrouper->updateClusters(); } void MapWidget::slotClustersNeedUpdating() { if (currentBackendReady()) { d->currentBackend->slotClustersNeedUpdating(); } } /** * @brief Return color and style information for rendering the cluster * @param clusterIndex Index of the cluster * @param fillColor Color used to fill the circle * @param strokeColor Color used for the stroke around the circle * @param strokeStyle Style used to draw the stroke around the circle * @param labelText Text for the label * @param labelColor Color for the label text * @param overrideSelection Get the colors for a different selection state * @param overrideCount Get the colors for a different amount of markers */ void MapWidget::getColorInfos(const int clusterIndex, QColor* fillColor, QColor* strokeColor, Qt::PenStyle* strokeStyle, QString* labelText, QColor* labelColor, const GeoGroupState* const overrideSelection, const int* const overrideCount) const { /// @todo Call the new getColorInfos function! const GeoIfaceCluster& cluster = s->clusterList.at(clusterIndex); /// @todo Check that this number is already valid! const int nMarkers = overrideCount ? *overrideCount : cluster.markerCount; getColorInfos(overrideSelection ? *overrideSelection : cluster.groupState, nMarkers, fillColor, strokeColor, strokeStyle, labelText, labelColor); } void MapWidget::getColorInfos(const GeoGroupState groupState, const int nMarkers, QColor* fillColor, QColor* strokeColor, Qt::PenStyle* strokeStyle, QString* labelText, QColor* labelColor) const { if (nMarkers < 1000) { *labelText = QString::number(nMarkers); } else if ((nMarkers >= 1000) && (nMarkers <= 1950)) { *labelText = QString::fromLatin1("%L1k").arg(qreal(nMarkers)/1000.0, 0, 'f', 1); } else if ((nMarkers >= 1951) && (nMarkers < 19500)) { *labelText = QString::fromLatin1("%L1k").arg(qreal(nMarkers)/1000.0, 0, 'f', 0); } else { // convert to "1E5" notation for numbers >=20k: qreal exponent = floor(log((qreal)nMarkers)/log((qreal)10)); qreal nMarkersFirstDigit = round(qreal(nMarkers)/pow(10,exponent)); if (nMarkersFirstDigit >= 10) { nMarkersFirstDigit=round(nMarkersFirstDigit/10.0); exponent++; } *labelText = QString::fromLatin1("%1E%2").arg(int(nMarkersFirstDigit)).arg(int(exponent)); } *labelColor = QColor(Qt::black); *strokeStyle = Qt::NoPen; /// @todo On my system, digikam uses QColor(67, 172, 232) as the selection color. Or should we just use blue? switch (groupState & SelectedMask) { case SelectedNone: *strokeStyle = Qt::SolidLine; *strokeColor = QColor(Qt::black); break; case SelectedSome: *strokeStyle = Qt::DotLine; *strokeColor = QColor(Qt::blue);//67, 172, 232); break; case SelectedAll: *strokeStyle = Qt::SolidLine; *strokeColor = QColor(Qt::blue);//67, 172, 232); break; } /// @todo These are the fill colors for the circles, for cases in which only some or all of the images are positively filtered. Filtering is implemented in GeoIface, but the code here has not been adapted yet. QColor fillAll, fillSome, fillNone; if (nMarkers >= 100) { fillAll = QColor(255, 0, 0); fillSome = QColor(255, 188, 125); fillNone = QColor(255, 185, 185); } else if (nMarkers >= 50) { fillAll = QColor(255, 127, 0); fillSome = QColor(255, 190, 125); fillNone = QColor(255, 220, 185); } else if (nMarkers >= 10) { fillAll = QColor(255, 255, 0); fillSome = QColor(255, 255, 105); fillNone = QColor(255, 255, 185); } else if (nMarkers >= 2) { fillAll = QColor(0, 255, 0); fillSome = QColor(125, 255, 125); fillNone = QColor(185, 255, 255); } else { fillAll = QColor(0, 255, 255); fillSome = QColor(125, 255, 255); fillNone = QColor(185, 255, 255); } *fillColor = fillAll; // switch (groupState) // { // case PartialAll: // *fillColor = fillAll; // break; // case PartialSome: // *fillColor = fillSome; // break; // case PartialNone: // if (haveAnySolo) // { // *fillColor = fillNone; // } // else // { // *fillColor = fillAll; // } // break; // } } QString MapWidget::convertZoomToBackendZoom(const QString& someZoom, const QString& targetBackend) const { const QStringList zoomParts = someZoom.split(QLatin1Char( ':' )); GEOIFACE_ASSERT(zoomParts.count() == 2); const QString sourceBackend = zoomParts.first(); if (sourceBackend == targetBackend) { return someZoom; } const int sourceZoom = zoomParts.last().toInt(); int targetZoom = -1; // all of these values were found experimentally! if (targetBackend == QLatin1String("marble" )) { if (sourceZoom == 0) { targetZoom = 900; } else if (sourceZoom == 1) { targetZoom = 970; } else if (sourceZoom == 2) { targetZoom = 1108; } else if (sourceZoom == 3) { targetZoom = 1250; } else if (sourceZoom == 4) { targetZoom = 1384; } else if (sourceZoom == 5) { targetZoom = 1520; } else if (sourceZoom == 6) { targetZoom = 1665; } else if (sourceZoom == 7) { targetZoom = 1800; } else if (sourceZoom == 8) { targetZoom = 1940; } else if (sourceZoom == 9) { targetZoom = 2070; } else if (sourceZoom ==10) { targetZoom = 2220; } else if (sourceZoom ==11) { targetZoom = 2357; } else if (sourceZoom ==12) { targetZoom = 2510; } else if (sourceZoom ==13) { targetZoom = 2635; } else if (sourceZoom ==14) { targetZoom = 2775; } else if (sourceZoom ==15) { targetZoom = 2900; } else if (sourceZoom ==16) { targetZoom = 3051; } else if (sourceZoom ==17) { targetZoom = 3180; } else if (sourceZoom ==18) { targetZoom = 3295; } else if (sourceZoom ==19) { targetZoom = 3450; } else { targetZoom = 3500; } /// @todo Find values for level 20 and up } if (targetBackend == QLatin1String("googlemaps" )) { if (sourceZoom <= 900) { targetZoom = 0; } else if (sourceZoom <= 970) { targetZoom = 1; } else if (sourceZoom <=1108) { targetZoom = 2; } else if (sourceZoom <=1250) { targetZoom = 3; } else if (sourceZoom <=1384) { targetZoom = 4; } else if (sourceZoom <=1520) { targetZoom = 5; } else if (sourceZoom <=1665) { targetZoom = 6; } else if (sourceZoom <=1800) { targetZoom = 7; } else if (sourceZoom <=1940) { targetZoom = 8; } else if (sourceZoom <=2070) { targetZoom = 9; } else if (sourceZoom <=2220) { targetZoom = 10; } else if (sourceZoom <=2357) { targetZoom = 11; } else if (sourceZoom <=2510) { targetZoom = 12; } else if (sourceZoom <=2635) { targetZoom = 13; } else if (sourceZoom <=2775) { targetZoom = 14; } else if (sourceZoom <=2900) { targetZoom = 15; } else if (sourceZoom <=3051) { targetZoom = 16; } else if (sourceZoom <=3180) { targetZoom = 17; } else if (sourceZoom <=3295) { targetZoom = 18; } else if (sourceZoom <=3450) { targetZoom = 19; } else { targetZoom = 20; } /// @todo Find values for level 20 and up } GEOIFACE_ASSERT(targetZoom >= 0); return QString::fromLatin1("%1:%2").arg(targetBackend).arg(targetZoom); } void MapWidget::slotBackendZoomChanged(const QString& newZoom) { d->cacheZoom = newZoom; } void MapWidget::setZoom(const QString& newZoom) { d->cacheZoom = newZoom; if (currentBackendReady()) { d->currentBackend->setZoom(d->cacheZoom); } } QString MapWidget::getZoom() { if (currentBackendReady()) { d->cacheZoom = d->currentBackend->getZoom(); } return d->cacheZoom; } GeoCoordinates::Pair MapWidget::getRegionSelection() { return s->selectionRectangle; } void MapWidget::slotClustersMoved(const QIntList& clusterIndices, const QPair& snapTarget) { qCDebug(DIGIKAM_GEOIFACE_LOG) << clusterIndices; /// @todo We actually expect only one clusterindex int clusterIndex = clusterIndices.first(); GeoCoordinates targetCoordinates = s->clusterList.at(clusterIndex).coordinates; TileIndex::List movedTileIndices; if (s->clusterList.at(clusterIndex).groupState == SelectedNone) { // a not-selected marker was moved. update all of its items: const GeoIfaceCluster& cluster = s->clusterList.at(clusterIndex); for (int i = 0 ; i < cluster.tileIndicesList.count() ; ++i) { const TileIndex tileIndex = cluster.tileIndicesList.at(i); movedTileIndices << tileIndex; } } else { // selected items were moved. The model helper should know which tiles are selected, // therefore we give him an empty list } s->markerModel->onIndicesMoved(movedTileIndices, targetCoordinates, snapTarget.second); /** * @todo Clusters are marked as dirty by slotClustersNeedUpdating * which is called while we update the model */ } void MapWidget::addUngroupedModel(GeoModelHelper* const modelHelper) { s->ungroupedModels << modelHelper; /// @todo monitor all model signals! connect(modelHelper->model(), SIGNAL(dataChanged(QModelIndex,QModelIndex)), this, SLOT(slotUngroupedModelChanged())); connect(modelHelper->model(), SIGNAL(rowsInserted(QModelIndex,int,int)), this, SLOT(slotUngroupedModelChanged())); connect(modelHelper->model(), SIGNAL(modelReset()), this, SLOT(slotUngroupedModelChanged())); connect(modelHelper, SIGNAL(signalVisibilityChanged()), this, SLOT(slotUngroupedModelChanged())); if (modelHelper->selectionModel()) { connect(modelHelper->selectionModel(), SIGNAL(currentChanged(QModelIndex,QModelIndex)), this, SLOT(slotUngroupedModelChanged())); } emit(signalUngroupedModelChanged(s->ungroupedModels.count() - 1)); } void MapWidget::removeUngroupedModel(GeoModelHelper* const modelHelper) { if (!modelHelper) return; const int modelIndex = s->ungroupedModels.indexOf(modelHelper); if (modelIndex < 0) return; /// @todo monitor all model signals! disconnect(modelHelper->model(), SIGNAL(dataChanged(QModelIndex,QModelIndex)), this, SLOT(slotUngroupedModelChanged())); disconnect(modelHelper->model(), SIGNAL(rowsInserted(QModelIndex,int,int)), this, SLOT(slotUngroupedModelChanged())); disconnect(modelHelper->model(), SIGNAL(modelReset()), this, SLOT(slotUngroupedModelChanged())); disconnect(modelHelper, SIGNAL(signalVisibilityChanged()), this, SLOT(slotUngroupedModelChanged())); if (modelHelper->selectionModel()) { disconnect(modelHelper->selectionModel(), SIGNAL(currentChanged(QModelIndex,QModelIndex)), this, SLOT(slotUngroupedModelChanged())); } s->ungroupedModels.removeAt(modelIndex); // the indices changed, therefore send out notifications // sending out a signal with i=s->ungroupedModel.count() // will cause the backends to see that the last model is missing for (int i = modelIndex ; i <= s->ungroupedModels.count() ; ++i) { emit(signalUngroupedModelChanged(i)); } } void MapWidget::setGroupedModel(AbstractMarkerTiler* const markerModel) { s->markerModel = markerModel; if (s->markerModel) { s->markerModel->setActive(s->activeState); /// @todo this needs some buffering for the google maps backend connect(s->markerModel, SIGNAL(signalTilesOrSelectionChanged()), this, SLOT(slotRequestLazyReclustering())); if (d->currentBackend) { connect(s->markerModel, SIGNAL(signalThumbnailAvailableForIndex(QVariant,QPixmap)), d->currentBackend, SLOT(slotThumbnailAvailableForIndex(QVariant,QPixmap))); } } slotRequestLazyReclustering(); } void MapWidget::setShowThumbnails(const bool state) { s->showThumbnails = state; rebuildConfigurationMenu(); slotUpdateActionsEnabled(); slotRequestLazyReclustering(); } void MapWidget::slotShowThumbnailsChanged() { setShowThumbnails(d->actionShowThumbnails->isChecked()); } /** * @brief Request reclustering, repeated calls should generate only one actual update of the clusters */ void MapWidget::slotRequestLazyReclustering() { if (d->lazyReclusteringRequested) return; s->tileGrouper->setClustersDirty(); if (s->activeState) { d->lazyReclusteringRequested = true; QTimer::singleShot(0, this, SLOT(slotLazyReclusteringRequestCallBack())); } } /** * @brief Helper function to buffer reclustering */ void MapWidget::slotLazyReclusteringRequestCallBack() { if (!d->lazyReclusteringRequested) return; d->lazyReclusteringRequested = false; slotClustersNeedUpdating(); } /** * @todo Clicking on several clusters at once is not actually possible */ void MapWidget::slotClustersClicked(const QIntList& clusterIndices) { qCDebug(DIGIKAM_GEOIFACE_LOG)<currentMouseMode == MouseModeZoomIntoGroup) || (s->currentMouseMode == MouseModeRegionSelectionFromIcon) ) { int maxTileLevel = 0; Marble::GeoDataLineString tileString; for (int i = 0 ; i < clusterIndices.count() ; ++i) { const int clusterIndex = clusterIndices.at(i); const GeoIfaceCluster currentCluster = s->clusterList.at(clusterIndex); for (int j = 0 ; j < currentCluster.tileIndicesList.count() ; ++j) { const TileIndex& currentTileIndex = currentCluster.tileIndicesList.at(j); for (int corner = 1 ; corner <= 4 ; ++corner) { GeoCoordinates currentTileCoordinate; if (corner == 1) currentTileCoordinate = currentTileIndex.toCoordinates(TileIndex::CornerNW); else if (corner == 2) currentTileCoordinate = currentTileIndex.toCoordinates(TileIndex::CornerSW); else if (corner == 3) currentTileCoordinate = currentTileIndex.toCoordinates(TileIndex::CornerNE); else if (corner == 4) currentTileCoordinate = currentTileIndex.toCoordinates(TileIndex::CornerSE); const Marble::GeoDataCoordinates tileCoordinate(currentTileCoordinate.lon(), currentTileCoordinate.lat(), 0, Marble::GeoDataCoordinates::Degree); if (maxTileLevel < currentTileIndex.level()) { maxTileLevel = currentTileIndex.level(); } tileString.append(tileCoordinate); } } } Marble::GeoDataLatLonBox latLonBox = Marble::GeoDataLatLonBox::fromLineString(tileString); /// @todo Review this section /* if (maxTileLevel != 0) { //increase the selection boundaries with 0.1 degrees because some thumbnails aren't caught by selection latLonBox.setWest((latLonBox.west(Marble::GeoDataCoordinates::Degree)-(0.1/maxTileLevel)), Marble::GeoDataCoordinates::Degree); latLonBox.setNorth((latLonBox.north(Marble::GeoDataCoordinates::Degree)+(0.1/maxTileLevel)), Marble::GeoDataCoordinates::Degree); latLonBox.setEast((latLonBox.east(Marble::GeoDataCoordinates::Degree)+(0.1/maxTileLevel)), Marble::GeoDataCoordinates::Degree); latLonBox.setSouth((latLonBox.south(Marble::GeoDataCoordinates::Degree)-(0.1/maxTileLevel)), Marble::GeoDataCoordinates::Degree); } else { */ latLonBox.setWest((latLonBox.west(Marble::GeoDataCoordinates::Degree)-0.0001), Marble::GeoDataCoordinates::Degree); latLonBox.setNorth((latLonBox.north(Marble::GeoDataCoordinates::Degree)+0.0001), Marble::GeoDataCoordinates::Degree); latLonBox.setEast((latLonBox.east(Marble::GeoDataCoordinates::Degree)+0.0001), Marble::GeoDataCoordinates::Degree); latLonBox.setSouth((latLonBox.south(Marble::GeoDataCoordinates::Degree)-0.0001), Marble::GeoDataCoordinates::Degree); // } if (s->currentMouseMode == MouseModeZoomIntoGroup) { /// @todo Very small latLonBoxes can crash Marble d->currentBackend->centerOn(latLonBox); } else { const GeoCoordinates::Pair newSelection( GeoCoordinates(latLonBox.north(Marble::GeoDataCoordinates::Degree), latLonBox.west(Marble::GeoDataCoordinates::Degree)), GeoCoordinates(latLonBox.south(Marble::GeoDataCoordinates::Degree), latLonBox.east(Marble::GeoDataCoordinates::Degree)) ); s->selectionRectangle = newSelection; d->currentBackend->regionSelectionChanged(); emit(signalRegionSelectionChanged()); } } else if ((s->currentMouseMode == MouseModeFilter && s->selectionRectangle.first.hasCoordinates()) || (s->currentMouseMode == MouseModeSelectThumbnail) ) { // update the selection and filtering state of the clusters for (int i = 0 ; i < clusterIndices.count() ; ++i) { const int clusterIndex = clusterIndices.at(i); const GeoIfaceCluster currentCluster = s->clusterList.at(clusterIndex); const TileIndex::List tileIndices = currentCluster.tileIndicesList; /// @todo Isn't this cached in the cluster? const QVariant representativeIndex = getClusterRepresentativeMarker(clusterIndex, s->sortKey); AbstractMarkerTiler::ClickInfo clickInfo; clickInfo.tileIndicesList = tileIndices; clickInfo.representativeIndex = representativeIndex; clickInfo.groupSelectionState = currentCluster.groupState; clickInfo.currentMouseMode = s->currentMouseMode; s->markerModel->onIndicesClicked(clickInfo); } } } void MapWidget::dragEnterEvent(QDragEnterEvent* event) { /// @todo ignore drops if no marker tiler or model can accept them if (!d->dragDropHandler) { event->ignore(); return; } if (d->dragDropHandler->accepts(event) == Qt::IgnoreAction) { event->ignore(); return; } /// @todo need data about the dragged object: #markers, selected, icon, ... event->accept(); - -// if (!dragData->haveDragPixmap) -// d->currentBackend->updateDragDropMarker(event->pos(), dragData); } void MapWidget::dragMoveEvent(QDragMoveEvent* event) { - Q_UNUSED(event); + if (!d->dragDropHandler) + { + event->ignore(); + return; + } - /// @todo update the position of the drag marker if it is to be shown -// if (!dragData->haveDragPixmap) -// d->currentBackend->updateDragDropMarkerPosition(event->pos()); + event->accept(); } void MapWidget::dropEvent(QDropEvent* event) { // remove the drag marker: // d->currentBackend->updateDragDropMarker(QPoint(), 0); if (!d->dragDropHandler) { event->ignore(); return; } GeoCoordinates dropCoordinates; if (!d->currentBackend->geoCoordinates(event->pos(), &dropCoordinates)) return; // the drag and drop handler handled the drop if it returned true here if (d->dragDropHandler->dropEvent(event, dropCoordinates)) { event->acceptProposedAction(); } } void MapWidget::dragLeaveEvent(QDragLeaveEvent* event) { Q_UNUSED(event); // remove the marker: // d->currentBackend->updateDragDropMarker(QPoint(), 0); } void MapWidget::markClustersAsDirty() { s->tileGrouper->setClustersDirty(); } void MapWidget::setDragDropHandler(GeoDragDropHandler* const dragDropHandler) { d->dragDropHandler = dragDropHandler; } QVariant MapWidget::getClusterRepresentativeMarker(const int clusterIndex, const int sortKey) { if (!s->markerModel) return QVariant(); const GeoIfaceCluster cluster = s->clusterList.at(clusterIndex); QMap::const_iterator it = cluster.representativeMarkers.find(sortKey); if (it != cluster.representativeMarkers.end()) return *it; QList repIndices; for (int i = 0 ; i < cluster.tileIndicesList.count() ; ++i) { repIndices << s->markerModel->getTileRepresentativeMarker(cluster.tileIndicesList.at(i), sortKey); } const QVariant clusterRepresentative = s->markerModel->bestRepresentativeIndexFromList(repIndices, sortKey); s->clusterList[clusterIndex].representativeMarkers[sortKey] = clusterRepresentative; return clusterRepresentative; } void MapWidget::slotItemDisplaySettingsChanged() { s->previewSingleItems = d->actionPreviewSingleItems->isChecked(); s->previewGroupedItems = d->actionPreviewGroupedItems->isChecked(); s->showNumbersOnItems = d->actionShowNumbersOnItems->isChecked(); /// @todo Update action availability? /// @todo We just need to update the display, no need to recluster? slotRequestLazyReclustering(); } void MapWidget::setSortOptionsMenu(QMenu* const sortMenu) { d->sortMenu = sortMenu; rebuildConfigurationMenu(); } void MapWidget::setSortKey(const int sortKey) { s->sortKey = sortKey; // this is probably faster than writing a function that changes all the clusters icons... /// @todo We just need to update the display, no need to recluster? slotRequestLazyReclustering(); } QPixmap MapWidget::getDecoratedPixmapForCluster(const int clusterId, const GeoGroupState* const selectedStateOverride, const int* const countOverride, QPoint* const centerPoint) { GeoIfaceCluster& cluster = s->clusterList[clusterId]; int markerCount = cluster.markerCount; GeoGroupState groupState = cluster.groupState; if (selectedStateOverride) { groupState = *selectedStateOverride; markerCount = *countOverride; } const GeoGroupState selectedState = groupState & SelectedMask; // first determine all the color and style values QColor fillColor; QColor strokeColor; Qt::PenStyle strokeStyle; QColor labelColor; QString labelText; getColorInfos(clusterId, &fillColor, &strokeColor, &strokeStyle, &labelText, &labelColor, &selectedState, &markerCount); // determine whether we should use a pixmap or a placeholder if (!s->showThumbnails) { /// @todo Handle positive filtering and region selection! QString pixmapName = fillColor.name().mid(1); if (selectedState == SelectedAll) { pixmapName += QLatin1String("-selected"); } if (selectedState == SelectedSome) { pixmapName += QLatin1String("-someselected"); } const QPixmap& markerPixmap = GeoIfaceGlobalObject::instance()->getMarkerPixmap(pixmapName); // update the display information stored in the cluster: cluster.pixmapType = GeoIfaceCluster::PixmapMarker; cluster.pixmapOffset = QPoint(markerPixmap.width()/2, markerPixmap.height()-1); cluster.pixmapSize = markerPixmap.size(); if (centerPoint) { *centerPoint = cluster.pixmapOffset; } return markerPixmap; } /// @todo This check is strange, there can be no clusters without a markerModel? bool displayThumbnail = (s->markerModel != 0); if (displayThumbnail) { if (markerCount==1) { displayThumbnail = s->previewSingleItems; } else { displayThumbnail = s->previewGroupedItems; } } if (displayThumbnail) { const QVariant representativeMarker = getClusterRepresentativeMarker(clusterId, s->sortKey); const int undecoratedThumbnailSize = getUndecoratedThumbnailSize(); QPixmap clusterPixmap = s->markerModel->pixmapFromRepresentativeIndex(representativeMarker, QSize(undecoratedThumbnailSize, undecoratedThumbnailSize)); if (!clusterPixmap.isNull()) { QPixmap resultPixmap(clusterPixmap.size() + QSize(2,2)); // we may draw with partially transparent pixmaps later, so make sure we have a defined // background color resultPixmap.fill(QColor::fromRgb(0xff, 0xff, 0xff)); QPainter painter(&resultPixmap); // painter.setRenderHint(QPainter::Antialiasing); const int borderWidth = (groupState&SelectedSome) ? 2 : 1; QPen borderPen; borderPen.setWidth(borderWidth); borderPen.setJoinStyle(Qt::MiterJoin); GeoGroupState globalState = s->markerModel->getGlobalGroupState(); /// @todo What about partially in the region or positively filtered? const bool clusterIsNotInRegionSelection = (globalState & RegionSelectedMask) && ((groupState & RegionSelectedMask) == RegionSelectedNone); const bool clusterIsNotPositivelyFiltered = (globalState & FilteredPositiveMask) && ((groupState & FilteredPositiveMask) == FilteredPositiveNone); const bool shouldGrayOut = clusterIsNotInRegionSelection || clusterIsNotPositivelyFiltered; const bool shouldCrossOut = clusterIsNotInRegionSelection; if (shouldGrayOut) { /// @todo Cache the alphaPixmap! QPixmap alphaPixmap(clusterPixmap.size()); alphaPixmap.fill(QColor::fromRgb(0x80, 0x80, 0x80)); /* NOTE : old Qt4 code ported to Qt5 due to deprecated QPixmap::setAlphaChannel() clusterPixmap.setAlphaChannel(alphaPixmap); */ QPainter p(&clusterPixmap); p.setOpacity(0.2); p.drawPixmap(0, 0, alphaPixmap); p.end(); } painter.drawPixmap(QPoint(1,1), clusterPixmap); if (shouldGrayOut || shouldCrossOut) { // draw a red cross above the pixmap QPen crossPen(Qt::red); if (!shouldCrossOut) { /// @todo Maybe we should also do a cross for not positively filtered images? crossPen.setColor(Qt::blue); } crossPen.setWidth(2); painter.setPen(crossPen); const int width = resultPixmap.size().width(); const int height = resultPixmap.size().height(); painter.drawLine(0, 0, width-1, height-1); painter.drawLine(width-1, 0, 0, height-1); } if (strokeStyle != Qt::SolidLine) { // paint a white border around the image borderPen.setColor(Qt::white); painter.setPen(borderPen); painter.drawRect(borderWidth-1, borderWidth-1, resultPixmap.size().width()-borderWidth, resultPixmap.size().height()-borderWidth); } // now draw the selection border borderPen.setColor(strokeColor); borderPen.setStyle(strokeStyle); painter.setPen(borderPen); painter.drawRect(borderWidth-1, borderWidth-1, resultPixmap.size().width()-borderWidth, resultPixmap.size().height()-borderWidth); if (s->showNumbersOnItems) { QPen labelPen(labelColor); // note: the pen has to be set, otherwise the bounding rect is 0 x 0!!! painter.setPen(labelPen); const QRect textRect(0, 0, resultPixmap.width(), resultPixmap.height()); QRect textBoundingRect = painter.boundingRect(textRect, Qt::AlignHCenter | Qt::AlignVCenter, labelText); textBoundingRect.adjust(-1, -1, 1, 1); // fill the bounding rect: painter.setPen(Qt::NoPen); painter.setBrush(QColor::fromRgb(0xff, 0xff, 0xff, 0x80)); painter.drawRect(textBoundingRect); // draw the text: painter.setPen(labelPen); painter.setBrush(Qt::NoBrush); painter.drawText(textRect, Qt::AlignHCenter|Qt::AlignVCenter, labelText); } // update the display information stored in the cluster: cluster.pixmapType = GeoIfaceCluster::PixmapImage; cluster.pixmapOffset = QPoint(resultPixmap.width()/2, resultPixmap.height()/2); cluster.pixmapSize = resultPixmap.size(); if (centerPoint) { *centerPoint = cluster.pixmapOffset; } return resultPixmap; } } // we do not have a thumbnail, draw the circle instead: const int circleRadius = s->thumbnailSize/2; QPen circlePen; circlePen.setColor(strokeColor); circlePen.setStyle(strokeStyle); circlePen.setWidth(2); QBrush circleBrush(fillColor); QPen labelPen; labelPen.setColor(labelColor); const QRect circleRect(0, 0, 2*circleRadius, 2*circleRadius); const int pixmapDiameter = 2*(circleRadius+1); QPixmap circlePixmap(pixmapDiameter, pixmapDiameter); /// @todo cache this somehow circlePixmap.fill(QColor(0, 0, 0, 0)); QPainter circlePainter(&circlePixmap); circlePainter.setPen(circlePen); circlePainter.setBrush(circleBrush); circlePainter.drawEllipse(circleRect); circlePainter.setPen(labelPen); circlePainter.setBrush(Qt::NoBrush); circlePainter.drawText(circleRect, Qt::AlignHCenter|Qt::AlignVCenter, labelText); // update the display information stored in the cluster: cluster.pixmapType = GeoIfaceCluster::PixmapCircle; cluster.pixmapOffset = QPoint(circlePixmap.width()/2, circlePixmap.height()/2); cluster.pixmapSize = circlePixmap.size(); if (centerPoint) { *centerPoint = QPoint(circlePixmap.width()/2, circlePixmap.height()/2); } return circlePixmap; } void MapWidget::setThumnailSize(const int newThumbnailSize) { s->thumbnailSize = qMax(GeoIfaceMinThumbnailSize, newThumbnailSize); // make sure the grouping radius is larger than the thumbnail size if (2*s->thumbnailGroupingRadius < newThumbnailSize) { /// @todo more straightforward way for this? s->thumbnailGroupingRadius = newThumbnailSize/2 + newThumbnailSize%2; } if (s->showThumbnails) { slotRequestLazyReclustering(); } slotUpdateActionsEnabled(); } void MapWidget::setThumbnailGroupingRadius(const int newGroupingRadius) { s->thumbnailGroupingRadius = qMax(GeoIfaceMinThumbnailGroupingRadius, newGroupingRadius); // make sure the thumbnails are smaller than the grouping radius if (2*s->thumbnailGroupingRadius < s->thumbnailSize) { s->thumbnailSize = 2*newGroupingRadius; } if (s->showThumbnails) { slotRequestLazyReclustering(); } slotUpdateActionsEnabled(); } void MapWidget::setMarkerGroupingRadius(const int newGroupingRadius) { s->markerGroupingRadius = qMax(GeoIfaceMinMarkerGroupingRadius, newGroupingRadius); if (!s->showThumbnails) { slotRequestLazyReclustering(); } slotUpdateActionsEnabled(); } void MapWidget::slotDecreaseThumbnailSize() { if (!s->showThumbnails) return; if (s->thumbnailSize>GeoIfaceMinThumbnailSize) { const int newThumbnailSize = qMax(GeoIfaceMinThumbnailSize, s->thumbnailSize-5); // make sure the grouping radius is also decreased // this will automatically decrease the thumbnail size as well setThumbnailGroupingRadius(newThumbnailSize/2); } } void MapWidget::slotIncreaseThumbnailSize() { if (!s->showThumbnails) return; setThumnailSize(s->thumbnailSize+5); } int MapWidget::getThumbnailSize() const { return s->thumbnailSize; } int MapWidget::getUndecoratedThumbnailSize() const { return s->thumbnailSize-2; } void MapWidget::setRegionSelection(const GeoCoordinates::Pair& region) { s->selectionRectangle = region; d->currentBackend->regionSelectionChanged(); slotUpdateActionsEnabled(); } void MapWidget::clearRegionSelection() { s->selectionRectangle.first.clear(); d->currentBackend->regionSelectionChanged(); slotUpdateActionsEnabled(); } void MapWidget::slotNewSelectionFromMap(const Digikam::GeoCoordinates::Pair& sel) { /// @todo Should the backend update s on its own? s->selectionRectangle = sel; slotUpdateActionsEnabled(); emit(signalRegionSelectionChanged()); } void MapWidget::slotRemoveCurrentRegionSelection() { clearRegionSelection(); d->currentBackend->regionSelectionChanged(); slotUpdateActionsEnabled(); emit(signalRegionSelectionChanged()); } void MapWidget::slotUngroupedModelChanged() { // determine the index under which we handle this model QObject* const senderObject = sender(); QAbstractItemModel* const senderModel = qobject_cast(senderObject); if (senderModel) { for (int i = 0 ; i < s->ungroupedModels.count() ; ++i) { if (s->ungroupedModels.at(i)->model() == senderModel) { emit(signalUngroupedModelChanged(i)); break; } } return; } GeoModelHelper* const senderHelper = qobject_cast(senderObject); if (senderHelper) { for (int i = 0 ; i < s->ungroupedModels.count() ; ++i) { if (s->ungroupedModels.at(i) == senderHelper) { emit(signalUngroupedModelChanged(i)); break; } } } QItemSelectionModel* const senderSelectionModel = qobject_cast(senderObject); if (senderSelectionModel) { for (int i = 0 ; i < s->ungroupedModels.count() ; ++i) { if (s->ungroupedModels.at(i)->selectionModel()==senderSelectionModel) { emit(signalUngroupedModelChanged(i)); break; } } return; } } void MapWidget::addWidgetToControlWidget(QWidget* const newWidget) { // make sure the control widget exists if (!d->controlWidget) getControlWidget(); QHBoxLayout* const hBoxLayout = reinterpret_cast(d->hBoxForAdditionalControlWidgetItems->layout()); if (hBoxLayout) { hBoxLayout->addWidget(newWidget); } } // Static methods --------------------------------------------------------- QString MapWidget::MarbleWidgetVersion() { return QString(Marble::MARBLE_VERSION_STRING).section(QLatin1Char(' '), 0, 0); } void MapWidget::setActive(const bool state) { const bool oldState = s->activeState; s->activeState = state; if (d->currentBackend) { d->currentBackend->setActive(state); } if (s->markerModel) { s->markerModel->setActive(state); } if (state) { // do we have a map widget shown? if ( (d->stackedLayout->count() == 1) && d->currentBackend ) { setMapWidgetInFrame(d->currentBackend->mapWidget()); // call this slot manually in case the backend was ready right away: if (d->currentBackend->isReady()) { slotBackendReadyChanged(d->currentBackendName); } else { rebuildConfigurationMenu(); } } } if (state && !oldState && s->tileGrouper->getClustersDirty()) { slotRequestLazyReclustering(); } } bool MapWidget::getActiveState() { return s->activeState; } void MapWidget::setVisibleMouseModes(const GeoMouseModes mouseModes) { s->visibleMouseModes = mouseModes; if (d->mouseModesHolder) { d->mouseModesHolder->setVisible(s->visibleMouseModes); d->setSelectionModeButton->setVisible(s->visibleMouseModes.testFlag(MouseModeRegionSelection)); d->removeCurrentSelectionButton->setVisible(s->visibleMouseModes.testFlag(MouseModeRegionSelection)); d->setPanModeButton->setVisible(s->visibleMouseModes.testFlag(MouseModePan)); d->setZoomModeButton->setVisible(s->visibleMouseModes.testFlag(MouseModeZoomIntoGroup)); d->setRegionSelectionFromIconModeButton->setVisible(s->visibleMouseModes.testFlag(MouseModeRegionSelectionFromIcon)); d->setFilterModeButton->setVisible(s->visibleMouseModes.testFlag(MouseModeFilter)); d->removeFilterModeButton->setVisible(s->visibleMouseModes.testFlag(MouseModeFilter)); d->setSelectThumbnailMode->setVisible(s->visibleMouseModes.testFlag(MouseModeSelectThumbnail)); } } void MapWidget::setAvailableMouseModes(const GeoMouseModes mouseModes) { s->availableMouseModes = mouseModes; } bool MapWidget::getStickyModeState() const { return d->actionStickyMode->isChecked(); } void MapWidget::setStickyModeState(const bool state) { d->actionStickyMode->setChecked(state); slotUpdateActionsEnabled(); } void MapWidget::setVisibleExtraActions(const GeoExtraActions actions) { d->visibleExtraActions = actions; if (d->buttonStickyMode) { d->buttonStickyMode->setVisible(actions.testFlag(ExtraActionSticky)); } slotUpdateActionsEnabled(); } void MapWidget::setEnabledExtraActions(const GeoExtraActions actions) { d->availableExtraActions = actions; slotUpdateActionsEnabled(); } void MapWidget::slotStickyModeChanged() { slotUpdateActionsEnabled(); emit(signalStickyModeChanged()); } void MapWidget::setAllowModifications(const bool state) { s->modificationsAllowed = state; slotUpdateActionsEnabled(); slotRequestLazyReclustering(); } /** * @brief Adjusts the visible map area such that all grouped markers are visible. * * Note that a call to this function currently has no effect if the widget has been * set inactive via setActive() or the backend is not yet ready. * * @param useSaneZoomLevel Stop zooming at a sane level, if markers are too close together. */ void MapWidget::adjustBoundariesToGroupedMarkers(const bool useSaneZoomLevel) { if ( (!s->activeState) || (!s->markerModel) || (!currentBackendReady()) ) { return; } Marble::GeoDataLineString tileString; /// @todo not sure that this is the best way to find the bounding box of all items for (AbstractMarkerTiler::NonEmptyIterator tileIterator(s->markerModel, TileIndex::MaxLevel); !tileIterator.atEnd(); tileIterator.nextIndex()) { const TileIndex tileIndex = tileIterator.currentIndex(); for (int corner = 1 ; corner <= 4 ; ++corner) { const GeoCoordinates currentTileCoordinate = tileIndex.toCoordinates(TileIndex::CornerPosition(corner)); const Marble::GeoDataCoordinates tileCoordinate(currentTileCoordinate.lon(), currentTileCoordinate.lat(), 0, Marble::GeoDataCoordinates::Degree); tileString.append(tileCoordinate); } } const Marble::GeoDataLatLonBox latLonBox = Marble::GeoDataLatLonBox::fromLineString(tileString); /// @todo use a sane zoom level d->currentBackend->centerOn(latLonBox, useSaneZoomLevel); } void MapWidget::refreshMap() { slotRequestLazyReclustering(); } void MapWidget::setShowPlaceholderWidget(const bool state) { if (state) { d->stackedLayout->setCurrentIndex(0); } else { if (d->stackedLayout->count() > 1) { d->stackedLayout->setCurrentIndex(1); } } } /** * @brief Set @p widgetForFrame as the widget in the frame, but does not show it. */ void MapWidget::setMapWidgetInFrame(QWidget* const widgetForFrame) { if (d->stackedLayout->count() > 1) { // widget 0 is the status widget, widget 1 is the map widget if (d->stackedLayout->widget(1) == widgetForFrame) { return; } // there is some other widget at the target position. // remove it and add our widget instead d->stackedLayout->removeWidget(d->stackedLayout->widget(1)); } d->stackedLayout->addWidget(widgetForFrame); } void MapWidget::removeMapWidgetFromFrame() { if (d->stackedLayout->count() > 1) { d->stackedLayout->removeWidget(d->stackedLayout->widget(1)); } d->stackedLayout->setCurrentIndex(0); } void MapWidget::slotMouseModeChanged(QAction* triggeredAction) { // determine the new mouse mode: const QVariant triggeredActionData = triggeredAction->data(); const GeoMouseModes newMouseMode = triggeredActionData.value(); if (newMouseMode == s->currentMouseMode) { return; } // store the new mouse mode: s->currentMouseMode = newMouseMode; if (d->currentBackend) { d->currentBackend->mouseModeChanged(); } emit(signalMouseModeChanged(s->currentMouseMode)); /// @todo Update action availability? } bool MapWidget::currentBackendReady() const { if (!d->currentBackend) { return false; } return d->currentBackend->isReady(); } void MapWidget::setMouseMode(const GeoMouseModes mouseMode) { s->currentMouseMode = mouseMode; if (currentBackendReady()) { d->currentBackend->mouseModeChanged(); } slotUpdateActionsEnabled(); } void MapWidget::setTrackManager(TrackManager* const trackManager) { s->trackManager = trackManager; // Some backends track the track manager activity even when not active // therefore they have to be notified. foreach(MapBackend* const backend, d->loadedBackends) { backend->slotTrackManagerChanged(); } } } // namespace Digikam