diff --git a/NEWS b/NEWS index 49ff9589a3..cbc9aebfff 100644 --- a/NEWS +++ b/NEWS @@ -1,655 +1,656 @@ digiKam 6.0.0-beta3 - Release date: 2018-11-25 ***************************************************************************************************** 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.0. 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) 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 ==> +587 ==> 401709 - Double clicking a name in a face-frame closes image preview. +588 ==> diff --git a/core/app/items/views/digikamitemview.cpp b/core/app/items/views/digikamitemview.cpp index f447b5d0bb..f8ffbabeb8 100644 --- a/core/app/items/views/digikamitemview.cpp +++ b/core/app/items/views/digikamitemview.cpp @@ -1,600 +1,608 @@ /* ============================================================ * * This file is a part of digiKam project * http://www.digikam.org * * Date : 2009-04-24 * Description : Qt model-view for items * * Copyright (C) 2009-2011 by Marcel Wiesweg * Copyright (C) 2009-2018 by Gilles Caulier * Copyright (C) 2011 by Andi Clemens * Copyright (C) 2013 by Michael G. Hansen * Copyright (C) 2014 by Mohamed_Anwer * Copyright (C) 2017 by Simon Frei * * 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 "digikamitemview.h" #include "digikamitemview_p.h" // Qt includes #include #include #include #include #include #include // Local includes #include "digikam_debug.h" #include "albummanager.h" #include "coredb.h" #include "coredboperationgroup.h" #include "advancedrenamedialog.h" #include "advancedrenameprocessdialog.h" #include "applicationsettings.h" #include "assignnameoverlay.h" #include "contextmenuhelper.h" #include "coredbaccess.h" #include "ddragobjects.h" #include "digikamapp.h" #include "digikamitemdelegate.h" #include "itemfacedelegate.h" #include "dio.h" #include "groupindicatoroverlay.h" #include "itemalbumfiltermodel.h" #include "itemalbummodel.h" #include "itemdragdrop.h" #include "itemratingoverlay.h" #include "itemfullscreenoverlay.h" #include "itemcoordinatesoverlay.h" #include "tagslineeditoverlay.h" #include "itemviewutilities.h" #include "imagewindow.h" #include "fileactionmngr.h" #include "fileactionprogress.h" #include "thumbnailloadthread.h" #include "tagregion.h" #include "addtagslineedit.h" #include "facerejectionoverlay.h" #include "facetagsiface.h" namespace Digikam { DigikamItemView::DigikamItemView(QWidget* const parent) : ItemCategorizedView(parent), d(new Private(this)) { installDefaultModels(); d->editPipeline.plugDatabaseEditor(); d->editPipeline.plugTrainer(); d->editPipeline.construct(); connect(&d->editPipeline, SIGNAL(scheduled()), this, SLOT(slotInitProgressIndicator())); d->normalDelegate = new DigikamItemDelegate(this); d->faceDelegate = new ItemFaceDelegate(this); setItemDelegate(d->normalDelegate); setSpacing(10); ApplicationSettings* const settings = ApplicationSettings::instance(); imageFilterModel()->setCategorizationMode(ItemSortSettings::CategoryByAlbum); imageAlbumModel()->setThumbnailLoadThread(ThumbnailLoadThread::defaultIconViewThread()); setThumbnailSize(ThumbnailSize(settings->getDefaultIconSize())); imageAlbumModel()->setPreloadThumbnails(true); imageModel()->setDragDropHandler(new ItemDragDropHandler(imageModel())); setDragEnabled(true); setAcceptDrops(true); setDropIndicatorShown(false); setToolTipEnabled(settings->showToolTipsIsValid()); imageFilterModel()->setStringTypeNatural(settings->isStringTypeNatural()); imageFilterModel()->setSortRole((ItemSortSettings::SortRole)settings->getImageSortOrder()); imageFilterModel()->setSortOrder((ItemSortSettings::SortOrder)settings->getImageSorting()); imageFilterModel()->setCategorizationMode((ItemSortSettings::CategorizationMode)settings->getImageSeparationMode()); imageFilterModel()->setCategorizationSortOrder((ItemSortSettings::SortOrder) settings->getImageSeparationSortOrder()); // selection overlay addSelectionOverlay(d->normalDelegate); addSelectionOverlay(d->faceDelegate); // rotation overlays d->rotateLeftOverlay = ItemRotateOverlay::left(this); d->rotateRightOverlay = ItemRotateOverlay::right(this); d->fullscreenOverlay = ItemFullScreenOverlay::instance(this); d->updateOverlays(); // rating overlay ItemRatingOverlay* const ratingOverlay = new ItemRatingOverlay(this); addOverlay(ratingOverlay); // face overlays // NOTE: order to plug this overlay is important, else rejection cant be suitable (see bug #324759). addAssignNameOverlay(d->faceDelegate); addRejectionOverlay(d->faceDelegate); GroupIndicatorOverlay* const groupOverlay = new GroupIndicatorOverlay(this); addOverlay(groupOverlay); addOverlay(new ItemCoordinatesOverlay(this)); connect(ratingOverlay, SIGNAL(ratingEdited(QList,int)), this, SLOT(assignRating(QList,int))); connect(groupOverlay, SIGNAL(toggleGroupOpen(QModelIndex)), this, SLOT(groupIndicatorClicked(QModelIndex))); connect(groupOverlay, SIGNAL(showButtonContextMenu(QModelIndex,QContextMenuEvent*)), this, SLOT(showGroupContextMenu(QModelIndex,QContextMenuEvent*))); d->utilities = new ItemViewUtilities(this); connect(imageModel()->dragDropHandler(), SIGNAL(assignTags(QList,QList)), FileActionMngr::instance(), SLOT(assignTags(QList,QList))); connect(imageModel()->dragDropHandler(), SIGNAL(addToGroup(ItemInfo,QList)), FileActionMngr::instance(), SLOT(addToGroup(ItemInfo,QList))); connect(imageModel()->dragDropHandler(), SIGNAL(dragDropSort(ItemInfo,QList)), this, SLOT(dragDropSort(ItemInfo,QList))); connect(d->utilities, SIGNAL(editorCurrentUrlChanged(QUrl)), this, SLOT(setCurrentUrlWhenAvailable(QUrl))); connect(settings, SIGNAL(setupChanged()), this, SLOT(slotSetupChanged())); slotSetupChanged(); } DigikamItemView::~DigikamItemView() { delete d; } ItemViewUtilities* DigikamItemView::utilities() const { return d->utilities; } void DigikamItemView::setThumbnailSize(const ThumbnailSize& size) { imageThumbnailModel()->setPreloadThumbnailSize(size); ItemCategorizedView::setThumbnailSize(size); } ItemInfoList DigikamItemView::allItemInfos(bool grouping) const { if (grouping) { return resolveGrouping(ItemCategorizedView::allItemInfos()); } return ItemCategorizedView::allItemInfos(); } ItemInfoList DigikamItemView::selectedItemInfos(bool grouping) const { if (grouping) { return resolveGrouping(ItemCategorizedView::selectedItemInfos()); } return ItemCategorizedView::selectedItemInfos(); } ItemInfoList DigikamItemView::selectedItemInfosCurrentFirst(bool grouping) const { if (grouping) { return resolveGrouping(ItemCategorizedView::selectedItemInfosCurrentFirst()); } return ItemCategorizedView::selectedItemInfosCurrentFirst(); } void DigikamItemView::dragDropSort(const ItemInfo& pick, const QList& infos) { if (pick.isNull() || infos.isEmpty()) { return; } ItemInfoList infoList = allItemInfos(false); qlonglong counter = pick.manualOrder(); bool order = (ApplicationSettings::instance()-> getImageSorting() == Qt::AscendingOrder); bool found = false; QApplication::setOverrideCursor(Qt::WaitCursor); CoreDbOperationGroup group; group.setMaximumTime(200); foreach (ItemInfo info, infoList) { if (!found && info.id() == pick.id()) { foreach (ItemInfo dropInfo, infos) { dropInfo.setManualOrder(counter); counter += (order ? 1 : -1); } info.setManualOrder(counter); found = true; } else if (found && !infos.contains(info)) { if (( order && info.manualOrder() > counter) || (!order && info.manualOrder() < counter)) { break; } counter += (order ? 100 : -100); info.setManualOrder(counter); } group.allowLift(); } QApplication::restoreOverrideCursor(); imageFilterModel()->invalidate(); } bool DigikamItemView::allNeedGroupResolving(const ApplicationSettings::OperationType type) const { return needGroupResolving(type, allItemInfos()); } bool DigikamItemView::selectedNeedGroupResolving(const ApplicationSettings::OperationType type) const { return needGroupResolving(type, selectedItemInfos()); } int DigikamItemView::fitToWidthIcons() { return delegate()->calculatethumbSizeToFit(viewport()->size().width()); } void DigikamItemView::slotSetupChanged() { imageFilterModel()->setStringTypeNatural(ApplicationSettings::instance()->isStringTypeNatural()); setToolTipEnabled(ApplicationSettings::instance()->showToolTipsIsValid()); setFont(ApplicationSettings::instance()->getIconViewFont()); d->updateOverlays(); ItemCategorizedView::slotSetupChanged(); } bool DigikamItemView::hasHiddenGroupedImages(const ItemInfo& info) const { return info.hasGroupedImages() && !imageFilterModel()->isGroupOpen(info.id()); } ItemInfoList DigikamItemView::imageInfos(const QList& indexes, ApplicationSettings::OperationType type) const { ItemInfoList infos = ItemCategorizedView::imageInfos(indexes); if (needGroupResolving(type, infos)) { return resolveGrouping(infos); } return infos; } void DigikamItemView::setFaceMode(bool on) { d->faceMode = on; if (on) { // See ItemLister, which creates a search the implements listing tag in the ioslave imageAlbumModel()->setSpecialTagListing(QLatin1String("faces")); setItemDelegate(d->faceDelegate); // grouping is not very much compatible with faces imageFilterModel()->setAllGroupsOpen(true); } else { imageAlbumModel()->setSpecialTagListing(QString()); setItemDelegate(d->normalDelegate); imageFilterModel()->setAllGroupsOpen(false); } } void DigikamItemView::addRejectionOverlay(ItemDelegate* delegate) { FaceRejectionOverlay* const rejectionOverlay = new FaceRejectionOverlay(this); connect(rejectionOverlay, SIGNAL(rejectFaces(QList)), this, SLOT(removeFaces(QList))); addOverlay(rejectionOverlay, delegate); } /* void DigikamItemView::addTagEditOverlay(ItemDelegate* delegate) { TagsLineEditOverlay* tagOverlay = new TagsLineEditOverlay(this); connect(tagOverlay, SIGNAL(tagEdited(QModelIndex,QString)), this, SLOT(assignTag(QModelIndex,QString))); addOverlay(tagOverlay, delegate); } */ void DigikamItemView::addAssignNameOverlay(ItemDelegate* delegate) { AssignNameOverlay* const nameOverlay = new AssignNameOverlay(this); addOverlay(nameOverlay, delegate); connect(nameOverlay, SIGNAL(confirmFaces(QList,int)), this, SLOT(confirmFaces(QList,int))); connect(nameOverlay, SIGNAL(removeFaces(QList)), this, SLOT(removeFaces(QList))); } void DigikamItemView::confirmFaces(const QList& indexes, int tagId) { QList infos; QList faces; QList sourceIndexes; // fast-remove in the "unknown person" view bool needFastRemove = false; if (imageAlbumModel()->currentAlbums().size() == 1) { needFastRemove = d->faceMode && (tagId != imageAlbumModel()->currentAlbums().first()->id()); } foreach (const QModelIndex& index, indexes) { infos << ItemModel::retrieveItemInfo(index); faces << d->faceDelegate->face(index); if (needFastRemove) { sourceIndexes << imageSortFilterModel()->mapToSourceItemModel(index); } } imageAlbumModel()->removeIndexes(sourceIndexes); for (int i = 0 ; i < infos.size() ; ++i) { d->editPipeline.confirm(infos[i], faces[i], tagId); } } void DigikamItemView::removeFaces(const QList& indexes) { QList infos; QList faces; QList sourceIndexes; foreach (const QModelIndex& index, indexes) { infos << ItemModel::retrieveItemInfo(index); faces << d->faceDelegate->face(index); sourceIndexes << imageSortFilterModel()->mapToSourceItemModel(index); } imageAlbumModel()->removeIndexes(sourceIndexes); for (int i = 0 ; i < infos.size() ; ++i) { d->editPipeline.remove(infos[i], faces[i]); } } -void DigikamItemView::activated(const ItemInfo& info, Qt::KeyboardModifiers modifiers) +void DigikamItemView::activated(const ItemInfo& info, QMouseEvent* const event) { if (info.isNull()) { return; } - if (modifiers != Qt::MetaModifier) + if (event) { - if (ApplicationSettings::instance()->getItemLeftClickAction() == ApplicationSettings::ShowPreview) + const bool acceptedClick = ((!qApp->style()->styleHint(QStyle::SH_ItemView_ActivateItemOnSingleClick) && + event->type() == QEvent::MouseButtonDblClick) || + ( qApp->style()->styleHint(QStyle::SH_ItemView_ActivateItemOnSingleClick) && + event->type() == QEvent::MouseButtonRelease)); + + if (event->modifiers() != Qt::MetaModifier) { - emit previewRequested(info); + if (ApplicationSettings::instance()->getItemLeftClickAction() == ApplicationSettings::ShowPreview) + { + emit previewRequested(info); + } + else if (acceptedClick) + { + openFile(info); + } } - else + else if (acceptedClick) { - openFile(info); + d->utilities->openInfosWithDefaultApplication(QList() << info); } } - else - { - d->utilities->openInfosWithDefaultApplication(QList() << info); - } } void DigikamItemView::showContextMenuOnInfo(QContextMenuEvent* event, const ItemInfo& info) { emit signalShowContextMenuOnInfo(event, info, QList(), imageFilterModel()); } void DigikamItemView::showGroupContextMenu(const QModelIndex& index, QContextMenuEvent* event) { Q_UNUSED(index); emit signalShowGroupContextMenu(event, selectedItemInfosCurrentFirst(), imageFilterModel()); } void DigikamItemView::showContextMenu(QContextMenuEvent* event) { emit signalShowContextMenu(event); } void DigikamItemView::openFile(const ItemInfo& info) { d->utilities->openInfos(info, allItemInfos(), currentAlbum()); } void DigikamItemView::deleteSelected(const ItemViewUtilities::DeleteMode deleteMode) { ItemInfoList imageInfoList = selectedItemInfos(true); if (d->utilities->deleteImages(imageInfoList, deleteMode)) { awayFromSelection(); } } void DigikamItemView::deleteSelectedDirectly(const ItemViewUtilities::DeleteMode deleteMode) { ItemInfoList imageInfoList = selectedItemInfos(true); d->utilities->deleteImagesDirectly(imageInfoList, deleteMode); awayFromSelection(); } void DigikamItemView::assignRating(const QList& indexes, int rating) { ItemInfoList infos = imageInfos(indexes, ApplicationSettings::Metadata); FileActionMngr::instance()->assignRating(infos, rating); } void DigikamItemView::groupIndicatorClicked(const QModelIndex& index) { ItemInfo info = imageFilterModel()->imageInfo(index); if (info.isNull()) { return; } setCurrentIndex(index); imageFilterModel()->toggleGroupOpen(info.id()); imageAlbumModel()->ensureHasGroupedImages(info); } void DigikamItemView::rename() { ItemInfoList infos = selectedItemInfos(); if (needGroupResolving(ApplicationSettings::Rename, infos)) { infos = resolveGrouping(infos); } QList urls = infos.toImageUrlList(); bool loop = false; NewNamesList newNamesList; do { qCDebug(DIGIKAM_GENERAL_LOG) << "Selected URLs to rename: " << urls; QPointer dlg = new AdvancedRenameDialog(this); dlg->slotAddImages(urls); if (dlg->exec() != QDialog::Accepted) { delete dlg; break; } if (!loop) { QUrl nextUrl = nextInOrder(infos.last(), 1).fileUrl(); setCurrentUrl(nextUrl); loop = true; } newNamesList = dlg->newNames(); delete dlg; setFocus(); qApp->processEvents(); if (!newNamesList.isEmpty()) { QPointer dlg = new AdvancedRenameProcessDialog(newNamesList, this); dlg->exec(); imageFilterModel()->invalidate(); urls = dlg->failedUrls(); delete dlg; } } while (!urls.isEmpty() && !newNamesList.isEmpty()); } void DigikamItemView::slotRotateLeft(const QList& indexes) { ItemInfoList infos = imageInfos(indexes, ApplicationSettings::Metadata); FileActionMngr::instance()->transform(infos, MetaEngineRotation::Rotate270); } void DigikamItemView::slotRotateRight(const QList& indexes) { ItemInfoList infos = imageInfos(indexes, ApplicationSettings::Metadata); FileActionMngr::instance()->transform(infos, MetaEngineRotation::Rotate90); } void DigikamItemView::slotFullscreen(const QList& indexes) { QList infos = imageInfos(indexes, ApplicationSettings::Slideshow); if (infos.isEmpty()) { return; } // Just fullscreen the first. const ItemInfo& info = infos.at(0); emit fullscreenRequested(info); } void DigikamItemView::slotInitProgressIndicator() { if (!ProgressManager::instance()->findItembyId(QLatin1String("FaceActionProgress"))) { FileActionProgress* const item = new FileActionProgress(QLatin1String("FaceActionProgress")); connect(&d->editPipeline, SIGNAL(started(QString)), item, SLOT(slotProgressStatus(QString))); connect(&d->editPipeline, SIGNAL(progressValueChanged(float)), item, SLOT(slotProgressValue(float))); connect(&d->editPipeline, SIGNAL(finished()), item, SLOT(slotCompleted())); } } } // namespace Digikam diff --git a/core/app/items/views/digikamitemview.h b/core/app/items/views/digikamitemview.h index 3339dec309..7c30e00b58 100644 --- a/core/app/items/views/digikamitemview.h +++ b/core/app/items/views/digikamitemview.h @@ -1,129 +1,129 @@ /* ============================================================ * * This file is a part of digiKam project * http://www.digikam.org * * Date : 2009-04-24 * Description : Qt model-view for items * * Copyright (C) 2009-2011 by Marcel Wiesweg * Copyright (C) 2009-2018 by Gilles Caulier * * This program is free software; you can redistribute it * and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation; * either version 2, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * ============================================================ */ #ifndef DIGIKAM_DIGIKAMITEM_VIEW_H #define DIGIKAM_DIGIKAMITEM_VIEW_H // Local includes #include "applicationsettings.h" #include "itemcategorizedview.h" #include "itemviewutilities.h" #include "groupingviewimplementation.h" namespace Digikam { class ItemViewUtilities; class ItemInfoList; class DigikamItemView : public ItemCategorizedView, public GroupingViewImplementation { Q_OBJECT public: explicit DigikamItemView(QWidget* const parent = 0); ~DigikamItemView(); ItemViewUtilities* utilities() const; int fitToWidthIcons(); virtual void setThumbnailSize(const ThumbnailSize& size); ItemInfoList allItemInfos(bool grouping = false) const; ItemInfoList selectedItemInfos(bool grouping = false) const; ItemInfoList selectedItemInfosCurrentFirst(bool grouping = false) const; bool allNeedGroupResolving(const ApplicationSettings::OperationType type) const; bool selectedNeedGroupResolving(const ApplicationSettings::OperationType type) const; public Q_SLOTS: void openFile(const ItemInfo& info); void deleteSelected(const ItemViewUtilities::DeleteMode deleteMode = ItemViewUtilities::DeleteUseTrash); void deleteSelectedDirectly(const ItemViewUtilities::DeleteMode deleteMode = ItemViewUtilities::DeleteUseTrash); void rename(); void assignRating(const QList& index, int rating); void setFaceMode(bool on); void confirmFaces(const QList& indexes, int tagId); void removeFaces(const QList& indexes); void dragDropSort(const ItemInfo& pick, const QList& infos); Q_SIGNALS: void previewRequested(const ItemInfo& info); void fullscreenRequested(const ItemInfo& info); void signalShowContextMenu(QContextMenuEvent* event, const QList& actions = QList()); void signalShowContextMenuOnInfo(QContextMenuEvent* event, const ItemInfo& info, const QList& actions, ItemFilterModel* filterModel); void signalShowGroupContextMenu(QContextMenuEvent* event, const QList& selectedInfos, ItemFilterModel* filterModel); protected Q_SLOTS: void groupIndicatorClicked(const QModelIndex& index); void showGroupContextMenu(const QModelIndex& index, QContextMenuEvent* event); protected: void addRejectionOverlay(ItemDelegate* delegate = 0); void addAssignNameOverlay(ItemDelegate* delegate = 0); - virtual void activated(const ItemInfo& info, Qt::KeyboardModifiers modifiers); + virtual void activated(const ItemInfo& info, QMouseEvent* const event); virtual void showContextMenuOnInfo(QContextMenuEvent* event, const ItemInfo& info); virtual void showContextMenu(QContextMenuEvent* event); virtual void slotSetupChanged(); virtual bool hasHiddenGroupedImages(const ItemInfo& info) const; ItemInfoList imageInfos(const QList& indexes, ApplicationSettings::OperationType type) const; private Q_SLOTS: void slotRotateLeft(const QList&); void slotRotateRight(const QList&); void slotFullscreen(const QList&); void slotInitProgressIndicator(); private: class Private; Private* const d; }; } // namespace Digikam #endif // DIGIKAM_DIGIKAMITEM_VIEW_H diff --git a/core/app/items/views/itemcategorizedview.cpp b/core/app/items/views/itemcategorizedview.cpp index 5d01021354..c14e77a9c2 100644 --- a/core/app/items/views/itemcategorizedview.cpp +++ b/core/app/items/views/itemcategorizedview.cpp @@ -1,740 +1,740 @@ /* ============================================================ * * This file is a part of digiKam project * http://www.digikam.org * * Date : 2009-04-22 * Description : Qt model-view for items * * Copyright (C) 2009-2012 by Marcel Wiesweg * Copyright (C) 2017 by Simon Frei * * 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 "itemcategorizedview.h" // Qt includes #include #include // Local includes #include "digikam_debug.h" #include "album.h" #include "albummanager.h" #include "coredbfields.h" #include "iccsettings.h" #include "itemalbummodel.h" #include "itemalbumfiltermodel.h" #include "itemcategorydrawer.h" #include "itemdelegate.h" #include "itemdelegateoverlay.h" #include "itemthumbnailmodel.h" #include "itemselectionoverlay.h" #include "itemviewtooltip.h" #include "loadingcacheinterface.h" #include "thumbnailloadthread.h" #include "tooltipfiller.h" #include "itemfacedelegate.h" namespace Digikam { class Q_DECL_HIDDEN ItemCategorizedViewToolTip : public ItemViewToolTip { public: explicit ItemCategorizedViewToolTip(ItemCategorizedView* const view) : ItemViewToolTip(view) { } ItemCategorizedView* view() const { return static_cast(ItemViewToolTip::view()); } protected: virtual QString tipContents() { ItemInfo info = ItemModel::retrieveItemInfo(currentIndex()); return ToolTipFiller::imageInfoTipContents(info); } }; // ------------------------------------------------------------------------------- class Q_DECL_HIDDEN ItemCategorizedView::Private { public: explicit Private() : model(0), filterModel(0), delegate(0), showToolTip(false), scrollToItemId(0), delayedEnterTimer(0), currentMouseEvent(0) { } ItemModel* model; ImageSortFilterModel* filterModel; ItemDelegate* delegate; bool showToolTip; qlonglong scrollToItemId; QUrl unknownCurrentUrl; QTimer* delayedEnterTimer; QMouseEvent* currentMouseEvent; }; // ------------------------------------------------------------------------------- ItemCategorizedView::ItemCategorizedView(QWidget* const parent) : ItemViewCategorized(parent), d(new Private) { setToolTip(new ItemCategorizedViewToolTip(this)); LoadingCacheInterface::connectToSignalFileChanged(this, SLOT(slotFileChanged(QString))); connect(IccSettings::instance(), SIGNAL(settingsChanged(ICCSettingsContainer,ICCSettingsContainer)), this, SLOT(slotIccSettingsChanged(ICCSettingsContainer,ICCSettingsContainer))); d->delayedEnterTimer = new QTimer(this); d->delayedEnterTimer->setInterval(10); d->delayedEnterTimer->setSingleShot(true); connect(d->delayedEnterTimer, SIGNAL(timeout()), this, SLOT(slotDelayedEnter())); } ItemCategorizedView::~ItemCategorizedView() { d->delegate->removeAllOverlays(); delete d; } void ItemCategorizedView::installDefaultModels() { ItemAlbumModel* model = new ItemAlbumModel(this); ItemAlbumFilterModel* filterModel = new ItemAlbumFilterModel(this); filterModel->setSourceItemModel(model); filterModel->setSortRole(ItemSortSettings::SortByFileName); filterModel->setCategorizationMode(ItemSortSettings::CategoryByAlbum); filterModel->sort(0); // an initial sorting is necessary // set flags that we want to get dataChanged() signals for model->setWatchFlags(filterModel->suggestedWatchFlags()); setModels(model, filterModel); } void ItemCategorizedView::setModels(ItemModel* model, ImageSortFilterModel* filterModel) { if (d->delegate) { d->delegate->setAllOverlaysActive(false); } if (d->filterModel) { disconnect(d->filterModel, SIGNAL(layoutAboutToBeChanged()), this, SLOT(layoutAboutToBeChanged())); disconnect(d->filterModel, SIGNAL(layoutChanged()), this, SLOT(layoutWasChanged())); } if (d->model) { disconnect(d->model, SIGNAL(imageInfosAdded(QList)), this, SLOT(slotItemInfosAdded())); } d->model = model; d->filterModel = filterModel; setModel(d->filterModel); connect(d->filterModel, SIGNAL(layoutAboutToBeChanged()), this, SLOT(layoutAboutToBeChanged())); connect(d->filterModel, SIGNAL(layoutChanged()), this, SLOT(layoutWasChanged()), Qt::QueuedConnection); connect(d->model, SIGNAL(imageInfosAdded(QList)), this, SLOT(slotItemInfosAdded())); emit modelChanged(); if (d->delegate) { d->delegate->setAllOverlaysActive(true); } } ItemModel* ItemCategorizedView::imageModel() const { return d->model; } ImageSortFilterModel* ItemCategorizedView::imageSortFilterModel() const { return d->filterModel; } ItemFilterModel* ItemCategorizedView::imageFilterModel() const { return d->filterModel->imageFilterModel(); } ItemThumbnailModel* ItemCategorizedView::imageThumbnailModel() const { return qobject_cast(d->model); } ItemAlbumModel* ItemCategorizedView::imageAlbumModel() const { return qobject_cast(d->model); } ItemAlbumFilterModel* ItemCategorizedView::imageAlbumFilterModel() const { return qobject_cast(d->filterModel->imageFilterModel()); } QSortFilterProxyModel* ItemCategorizedView::filterModel() const { return d->filterModel; } ItemDelegate* ItemCategorizedView::delegate() const { return d->delegate; } void ItemCategorizedView::setItemDelegate(ItemDelegate* delegate) { ThumbnailSize oldSize = thumbnailSize(); ItemDelegate* oldDelegate = d->delegate; if (oldDelegate) { hideIndexNotification(); d->delegate->setAllOverlaysActive(false); d->delegate->setViewOnAllOverlays(0); // Note: Be precise, no wildcard disconnect! disconnect(d->delegate, SIGNAL(requestNotification(QModelIndex,QString)), this, SLOT(showIndexNotification(QModelIndex,QString))); disconnect(d->delegate, SIGNAL(hideNotification()), this, SLOT(hideIndexNotification())); } d->delegate = delegate; d->delegate->setThumbnailSize(oldSize); if (oldDelegate) { d->delegate->setSpacing(oldDelegate->spacing()); } ItemViewCategorized::setItemDelegate(d->delegate); setCategoryDrawer(d->delegate->categoryDrawer()); updateDelegateSizes(); d->delegate->setViewOnAllOverlays(this); d->delegate->setAllOverlaysActive(true); connect(d->delegate, SIGNAL(requestNotification(QModelIndex,QString)), this, SLOT(showIndexNotification(QModelIndex,QString))); connect(d->delegate, SIGNAL(hideNotification()), this, SLOT(hideIndexNotification())); } Album* ItemCategorizedView::currentAlbum() const { ItemAlbumModel* albumModel = imageAlbumModel(); /** TODO: Change to QList return type **/ if (albumModel && !(albumModel->currentAlbums().isEmpty())) { return albumModel->currentAlbums().first(); } return 0; } ItemInfo ItemCategorizedView::currentInfo() const { return imageInfo(currentIndex()); } QUrl ItemCategorizedView::currentUrl() const { return currentInfo().fileUrl(); } ItemInfo ItemCategorizedView::imageInfo(const QModelIndex& index) const { return d->filterModel->imageInfo(index); } ItemInfoList ItemCategorizedView::imageInfos(const QList& indexes) const { return ItemInfoList(d->filterModel->imageInfos(indexes)); } ItemInfoList ItemCategorizedView::allItemInfos() const { return ItemInfoList(d->filterModel->imageInfosSorted()); } QList ItemCategorizedView::allUrls() const { return allItemInfos().toImageUrlList(); } ItemInfoList ItemCategorizedView::selectedItemInfos() const { return imageInfos(selectedIndexes()); } ItemInfoList ItemCategorizedView::selectedItemInfosCurrentFirst() const { QModelIndexList indexes = selectedIndexes(); const QModelIndex current = currentIndex(); if (!indexes.isEmpty()) { if (indexes.first() != current) { if (indexes.removeOne(current)) { indexes.prepend(current); } } } return imageInfos(indexes); } void ItemCategorizedView::toIndex(const QUrl& url) { ItemViewCategorized::toIndex(d->filterModel->indexForPath(url.toLocalFile())); } QModelIndex ItemCategorizedView::indexForInfo(const ItemInfo& info) const { return d->filterModel->indexForItemInfo(info); } ItemInfo ItemCategorizedView::nextInOrder(const ItemInfo& startingPoint, int nth) { QModelIndex index = d->filterModel->indexForItemInfo(startingPoint); if (!index.isValid()) { return ItemInfo(); } return imageInfo(d->filterModel->index(index.row() + nth, 0, QModelIndex())); } QModelIndex ItemCategorizedView::nextIndexHint(const QModelIndex& anchor, const QItemSelectionRange& removed) const { QModelIndex hint = ItemViewCategorized::nextIndexHint(anchor, removed); ItemInfo info = imageInfo(anchor); //qCDebug(DIGIKAM_GENERAL_LOG) << "Having initial hint" << hint << "for" << anchor << d->model->numberOfIndexesForItemInfo(info); // Fixes a special case of multiple (face) entries for the same image. // If one is removed, any entry of the same image shall be preferred. if (d->model->numberOfIndexesForItemInfo(info) > 1) { // The hint is for a different info, but we may have a hint for the same info if (info != imageInfo(hint)) { int minDiff = d->filterModel->rowCount(); QList indexesForItemInfo = d->filterModel->mapListFromSource(d->model->indexesForItemInfo(info)); foreach(const QModelIndex& index, indexesForItemInfo) { if (index == anchor || !index.isValid() || removed.contains(index)) { continue; } int distance = qAbs(index.row() - anchor.row()); if (distance < minDiff) { minDiff = distance; hint = index; //qCDebug(DIGIKAM_GENERAL_LOG) << "Chose index" << hint << "at distance" << minDiff << "to" << anchor; } } } } return hint; } void ItemCategorizedView::openAlbum(const QList& albums) { ItemAlbumModel* const albumModel = imageAlbumModel(); if (albumModel) { albumModel->openAlbum(albums); } } ThumbnailSize ItemCategorizedView::thumbnailSize() const { /* ItemThumbnailModel* const thumbModel = imageThumbnailModel(); if (thumbModel) return thumbModel->thumbnailSize(); */ if (d->delegate) { return d->delegate->thumbnailSize(); } return ThumbnailSize(); } void ItemCategorizedView::setThumbnailSize(int size) { setThumbnailSize(ThumbnailSize(size)); } void ItemCategorizedView::setThumbnailSize(const ThumbnailSize& s) { // we abuse this pair of method calls to restore scroll position layoutAboutToBeChanged(); d->delegate->setThumbnailSize(s); layoutWasChanged(); } void ItemCategorizedView::setCurrentWhenAvailable(qlonglong imageId) { d->scrollToItemId = imageId; } void ItemCategorizedView::setCurrentUrlWhenAvailable(const QUrl& url) { if (url.isEmpty()) { clearSelection(); setCurrentIndex(QModelIndex()); return; } QString path = url.toLocalFile(); QModelIndex index = d->filterModel->indexForPath(path); if (!index.isValid()) { d->unknownCurrentUrl = url; return; } clearSelection(); setCurrentIndex(index); d->unknownCurrentUrl.clear(); } void ItemCategorizedView::setCurrentUrl(const QUrl& url) { if (url.isEmpty()) { clearSelection(); setCurrentIndex(QModelIndex()); return; } QString path = url.toLocalFile(); QModelIndex index = d->filterModel->indexForPath(path); if (!index.isValid()) { return; } clearSelection(); setCurrentIndex(index); } void ItemCategorizedView::setCurrentInfo(const ItemInfo& info) { QModelIndex index = d->filterModel->indexForItemInfo(info); clearSelection(); setCurrentIndex(index); } void ItemCategorizedView::setSelectedUrls(const QList& urlList) { QItemSelection mySelection; for (QList::const_iterator it = urlList.constBegin(); it!=urlList.constEnd(); ++it) { const QString path = it->toLocalFile(); const QModelIndex index = d->filterModel->indexForPath(path); if (!index.isValid()) { qCWarning(DIGIKAM_GENERAL_LOG) << "no QModelIndex found for" << *it; } else { // TODO: is there a better way? mySelection.select(index, index); } } clearSelection(); selectionModel()->select(mySelection, QItemSelectionModel::Select); } void ItemCategorizedView::setSelectedItemInfos(const QList& infos) { QItemSelection mySelection; foreach(const ItemInfo& info, infos) { QModelIndex index = d->filterModel->indexForItemInfo(info); mySelection.select(index, index); } selectionModel()->select(mySelection, QItemSelectionModel::ClearAndSelect); } void ItemCategorizedView::hintAt(const ItemInfo& info) { if (info.isNull()) { return; } QModelIndex index = d->filterModel->indexForItemInfo(info); if (!index.isValid()) { return; } selectionModel()->setCurrentIndex(index, QItemSelectionModel::NoUpdate); scrollTo(index); } void ItemCategorizedView::addOverlay(ItemDelegateOverlay* overlay, ItemDelegate* delegate) { if (!delegate) { delegate = d->delegate; } delegate->installOverlay(overlay); if (delegate == d->delegate) { overlay->setView(this); overlay->setActive(true); } } void ItemCategorizedView::removeOverlay(ItemDelegateOverlay* overlay) { ItemDelegate* delegate = dynamic_cast(overlay->delegate()); if (delegate) { delegate->removeOverlay(overlay); } overlay->setView(0); } void ItemCategorizedView::updateGeometries() { ItemViewCategorized::updateGeometries(); d->delayedEnterTimer->start(); } void ItemCategorizedView::slotDelayedEnter() { // re-emit entered() for index under mouse (after layout). QModelIndex mouseIndex = indexAt(mapFromGlobal(QCursor::pos())); if (mouseIndex.isValid()) { emit DCategorizedView::entered(mouseIndex); } } void ItemCategorizedView::addSelectionOverlay(ItemDelegate* delegate) { addOverlay(new ItemSelectionOverlay(this), delegate); } void ItemCategorizedView::scrollToStoredItem() { if (d->scrollToItemId) { if (d->model->hasImage(d->scrollToItemId)) { QModelIndex index = d->filterModel->indexForImageId(d->scrollToItemId); setCurrentIndex(index); scrollToRelaxed(index, QAbstractItemView::PositionAtCenter); d->scrollToItemId = 0; } } } void ItemCategorizedView::slotItemInfosAdded() { if (d->scrollToItemId) { scrollToStoredItem(); } else if (!d->unknownCurrentUrl.isEmpty()) { QTimer::singleShot(100, this, SLOT(slotCurrentUrlTimer())); } } void ItemCategorizedView::slotCurrentUrlTimer() { setCurrentUrl(d->unknownCurrentUrl); d->unknownCurrentUrl.clear(); } void ItemCategorizedView::slotFileChanged(const QString& filePath) { QModelIndex index = d->filterModel->indexForPath(filePath); if (index.isValid()) { update(index); } } -void ItemCategorizedView::indexActivated(const QModelIndex& index, Qt::KeyboardModifiers modifiers) +void ItemCategorizedView::indexActivated(const QModelIndex& index, QMouseEvent* const event) { ItemInfo info = imageInfo(index); if (!info.isNull()) { - activated(info, modifiers); + activated(info, event); emit imageActivated(info); } } void ItemCategorizedView::currentChanged(const QModelIndex& index, const QModelIndex& previous) { ItemViewCategorized::currentChanged(index, previous); emit currentChanged(imageInfo(index)); } void ItemCategorizedView::selectionChanged(const QItemSelection& selectedItems, const QItemSelection& deselectedItems) { ItemViewCategorized::selectionChanged(selectedItems, deselectedItems); if (!selectedItems.isEmpty()) { emit selected(imageInfos(selectedItems.indexes())); } if (!deselectedItems.isEmpty()) { emit deselected(imageInfos(deselectedItems.indexes())); } } Album* ItemCategorizedView::albumAt(const QPoint& pos) const { if (imageFilterModel()->imageSortSettings().categorizationMode == ItemSortSettings::CategoryByAlbum) { QModelIndex categoryIndex = indexForCategoryAt(pos); if (categoryIndex.isValid()) { int albumId = categoryIndex.data(ItemFilterModel::CategoryAlbumIdRole).toInt(); return AlbumManager::instance()->findPAlbum(albumId); } } return currentAlbum(); } -void ItemCategorizedView::activated(const ItemInfo&, Qt::KeyboardModifiers) +void ItemCategorizedView::activated(const ItemInfo&, QMouseEvent*) { // implemented in subclass } void ItemCategorizedView::showContextMenuOnIndex(QContextMenuEvent* event, const QModelIndex& index) { showContextMenuOnInfo(event, imageInfo(index)); } void ItemCategorizedView::showContextMenuOnInfo(QContextMenuEvent*, const ItemInfo&) { // implemented in subclass } void ItemCategorizedView::paintEvent(QPaintEvent* e) { // We want the thumbnails to be loaded in order. ItemThumbnailModel* const thumbModel = imageThumbnailModel(); if (thumbModel) { QModelIndexList indexesToThumbnail = imageFilterModel()->mapListToSource(categorizedIndexesIn(viewport()->rect())); d->delegate->prepareThumbnails(thumbModel, indexesToThumbnail); } ItemViewCategorized::paintEvent(e); } QItemSelectionModel* ItemCategorizedView::getSelectionModel() const { return selectionModel(); } AbstractItemDragDropHandler* ItemCategorizedView::dragDropHandler() const { return d->model->dragDropHandler(); } void ItemCategorizedView::slotIccSettingsChanged(const ICCSettingsContainer&, const ICCSettingsContainer&) { viewport()->update(); } } // namespace Digikam diff --git a/core/app/items/views/itemcategorizedview.h b/core/app/items/views/itemcategorizedview.h index ae9926f914..5fccbd0168 100644 --- a/core/app/items/views/itemcategorizedview.h +++ b/core/app/items/views/itemcategorizedview.h @@ -1,228 +1,228 @@ /* ============================================================ * * This file is a part of digiKam project * http://www.digikam.org * * Date : 2009-04-22 * Description : Qt model-view for items * * Copyright (C) 2009-2012 by Marcel Wiesweg * Copyright (C) 2017 by Simon Frei * * This program is free software; you can redistribute it * and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation; * either version 2, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * ============================================================ */ #ifndef DIGIKAM_ITEM_CATEGORIZED_VIEW_H #define DIGIKAM_ITEM_CATEGORIZED_VIEW_H // Local includes #include "iteminfo.h" #include "itemviewcategorized.h" #include "thumbnailsize.h" #include "iccsettingscontainer.h" namespace Digikam { class Album; class ItemAlbumModel; class ItemAlbumFilterModel; class ItemModel; class ItemFilterModel; class ImageSortFilterModel; class ItemDelegate; class ItemDelegateOverlay; class ItemThumbnailModel; class ItemCategorizedView : public ItemViewCategorized { Q_OBJECT public: explicit ItemCategorizedView(QWidget* const parent = 0); ~ItemCategorizedView(); void setModels(ItemModel* model, ImageSortFilterModel* filterModel); ItemModel* imageModel() const; ImageSortFilterModel* imageSortFilterModel() const; QItemSelectionModel* getSelectionModel() const; /// Returns any ItemFilterMode in chain. May not be sourceModel() ItemFilterModel* imageFilterModel() const; /// Returns 0 if the ItemModel is not an ItemThumbnailModel ItemThumbnailModel* imageThumbnailModel() const; /// Returns 0 if the ItemModel is not an ItemAlbumModel ItemAlbumModel* imageAlbumModel() const; ItemAlbumFilterModel* imageAlbumFilterModel() const; ItemDelegate* delegate() const; Album* currentAlbum() const; ItemInfo currentInfo() const; QUrl currentUrl() const; ItemInfoList allItemInfos() const; QList allUrls() const; ItemInfoList selectedItemInfos() const; ItemInfoList selectedItemInfosCurrentFirst() const; /** Selects the index as current and scrolls to it. */ void toIndex(const QUrl& url); /** Returns the n-th info after the given one. * Specifically, return the previous info for nth = -1 * and the next info for n = 1. * Returns a null info if either startingPoint or the nth info are * not contained in the model. */ ItemInfo nextInOrder(const ItemInfo& startingPoint, int nth); ItemInfo previousInfo(const ItemInfo& info) { return nextInOrder(info, -1); } ItemInfo nextInfo(const ItemInfo& info) { return nextInOrder(info, 1); } QModelIndex indexForInfo(const ItemInfo& info) const; ThumbnailSize thumbnailSize() const; virtual void setThumbnailSize(const ThumbnailSize& size); /** If the model is categorized by an album, returns the album of the category * that contains the position. * If this is not applicable, return the current album. May return 0. */ Album* albumAt(const QPoint& pos) const; /// Add and remove an overlay. It will as well be removed automatically when destroyed. /// Unless you pass a different delegate, the current delegate will be used. void addOverlay(ItemDelegateOverlay* overlay, ItemDelegate* delegate = 0); void removeOverlay(ItemDelegateOverlay* overlay); void addSelectionOverlay(ItemDelegate* delegate = 0); public Q_SLOTS: void openAlbum(const QList& album); void setThumbnailSize(int size); /** Scroll the view to the given item when it becomes available. */ void setCurrentWhenAvailable(qlonglong imageId); /** Set as current item when it becomes available, the item identified by its file url. */ void setCurrentUrlWhenAvailable(const QUrl& url); /** Set as current item the item identified by its file url. */ void setCurrentUrl(const QUrl& url); /** Set as current item the item identified by the imageinfo. */ void setCurrentInfo(const ItemInfo& info); /** Set selected items identified by their file urls. */ void setSelectedUrls(const QList& urlList); /** Set selected items. */ void setSelectedItemInfos(const QList& infos); /** Does something to gain attention for info, but not changing current selection. */ void hintAt(const ItemInfo& info); Q_SIGNALS: void currentChanged(const ItemInfo& info); /// Emitted when new items are selected. The parameter includes only the newly selected infos, /// there may be other already selected infos. void selected(const QList& newSelectedInfos); /// Emitted when items are deselected. There may be other selected infos left. /// This signal is not emitted when the model is reset; then only selectionCleared is emitted. void deselected(const QList& nowDeselectedInfos); /// Emitted when the given image is activated. Info is never null. void imageActivated(const ItemInfo& info); /// Emitted when a new model is set void modelChanged(); protected Q_SLOTS: void slotItemInfosAdded(); void slotCurrentUrlTimer(); protected: /// install default ItemAlbumModel and filter model, ready for use void installDefaultModels(); // reimplemented from parent class QSortFilterProxyModel* filterModel() const; AbstractItemDragDropHandler* dragDropHandler() const; QModelIndex nextIndexHint(const QModelIndex& indexToAnchor, const QItemSelectionRange& removed) const; void setItemDelegate(ItemDelegate* delegate); - void indexActivated(const QModelIndex& index, Qt::KeyboardModifiers modifiers); + void indexActivated(const QModelIndex& index, QMouseEvent* const event); void currentChanged(const QModelIndex& index, const QModelIndex& previous); void paintEvent(QPaintEvent* e); void selectionChanged(const QItemSelection&, const QItemSelection&); void updateGeometries(); /// Reimplement these in a subclass - virtual void activated(const ItemInfo& info, Qt::KeyboardModifiers modifiers); + virtual void activated(const ItemInfo& info, QMouseEvent* const event); virtual void showContextMenuOnInfo(QContextMenuEvent* event, const ItemInfo& info); virtual void showContextMenuOnIndex(QContextMenuEvent* event, const QModelIndex& index); ItemInfo imageInfo(const QModelIndex& index) const; ItemInfoList imageInfos(const QList& indexes) const; private Q_SLOTS: void slotIccSettingsChanged(const ICCSettingsContainer&, const ICCSettingsContainer&); void slotFileChanged(const QString& filePath); void slotDelayedEnter(); private: void scrollToStoredItem(); private: class Private; Private* const d; }; } // namespace Digikam #endif // DIGIKAM_ITEM_CATEGORIZED_VIEW_H diff --git a/core/libs/widgets/graphicsview/graphicsdimgview.cpp b/core/libs/widgets/graphicsview/graphicsdimgview.cpp index 9d319cf695..5150e6f82d 100644 --- a/core/libs/widgets/graphicsview/graphicsdimgview.cpp +++ b/core/libs/widgets/graphicsview/graphicsdimgview.cpp @@ -1,520 +1,512 @@ /* ============================================================ * * This file is a part of digiKam project * http://www.digikam.org * * Date : 2010-04-30 * Description : Graphics View for DImg preview * * Copyright (C) 2010-2012 by Marcel Wiesweg * Copyright (C) 2011-2018 by Gilles Caulier * * This program is free software; you can redistribute it * and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation; * either version 2, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * ============================================================ */ #include "graphicsdimgview.h" // Qt includes #include #include #include #include #include // Local includes #include "digikam_debug.h" #include "dimgpreviewitem.h" #include "imagezoomsettings.h" #include "paniconwidget.h" #include "previewlayout.h" #include "dimgchilditem.h" namespace Digikam { class Q_DECL_HIDDEN GraphicsDImgView::Private { public: explicit Private() { scene = 0; item = 0; layout = 0; cornerButton = 0; panIconPopup = 0; movingInProgress = false; showText = true; } QGraphicsScene* scene; GraphicsDImgItem* item; SinglePhotoPreviewLayout* layout; QToolButton* cornerButton; PanIconFrame* panIconPopup; QPoint mousePressPos; QPoint panningScrollPos; bool movingInProgress; bool showText; }; GraphicsDImgView::GraphicsDImgView(QWidget* const parent) : QGraphicsView(parent), d(new Private) { d->scene = new QGraphicsScene(this); d->scene->setItemIndexMethod(QGraphicsScene::NoIndex); setScene(d->scene); d->layout = new SinglePhotoPreviewLayout(this); d->layout->setGraphicsView(this); setViewportUpdateMode(QGraphicsView::MinimalViewportUpdate); horizontalScrollBar()->setSingleStep(1); horizontalScrollBar()->setPageStep(1); verticalScrollBar()->setSingleStep(1); verticalScrollBar()->setPageStep(1); connect(horizontalScrollBar(), SIGNAL(valueChanged(int)), this, SLOT(slotContentsMoved())); connect(verticalScrollBar(), SIGNAL(valueChanged(int)), this, SLOT(slotContentsMoved())); } GraphicsDImgView::~GraphicsDImgView() { delete d; } void GraphicsDImgView::setItem(GraphicsDImgItem* const item) { d->item = item; d->scene->addItem(d->item); d->layout->addItem(d->item); } GraphicsDImgItem* GraphicsDImgView::item() const { return d->item; } DImgPreviewItem* GraphicsDImgView::previewItem() const { return dynamic_cast(item()); } SinglePhotoPreviewLayout* GraphicsDImgView::layout() const { return d->layout; } void GraphicsDImgView::installPanIcon() { d->cornerButton = PanIconWidget::button(); setCornerWidget(d->cornerButton); connect(d->cornerButton, SIGNAL(pressed()), this, SLOT(slotCornerButtonPressed())); } void GraphicsDImgView::drawForeground(QPainter* p, const QRectF& rect) { QGraphicsView::drawForeground(p, rect); if (!d->movingInProgress) { QString text = d->item->userLoadingHint(); if (text.isNull() || !d->showText) { return; } QRect viewportRect = viewport()->rect(); QRect fontRect = p->fontMetrics().boundingRect(viewportRect, 0, text); QPoint drawingPoint(viewportRect.topRight().x() - fontRect.width() - 10, viewportRect.topRight().y() + 5); QPointF sceneDrawingPoint = mapToScene(drawingPoint); QRectF sceneDrawingRect(sceneDrawingPoint, fontRect.size()); if (!rect.intersects(sceneDrawingRect)) { return; } drawText(p, sceneDrawingRect, text); } } void GraphicsDImgView::drawText(QPainter* p, const QRectF& rect, const QString& text) { p->save(); p->setRenderHint(QPainter::Antialiasing, true); p->setBackgroundMode(Qt::TransparentMode); // increase width by 5 and height by 2 QRectF textRect = rect.adjusted(0, 0, 5, 2); // Draw background p->setPen(Qt::black); QColor semiTransBg = palette().color(QPalette::Window); semiTransBg.setAlpha(190); p->setBrush(semiTransBg); //p->translate(0.5, 0.5); p->drawRoundRect(textRect, 10.0, 10.0); // Draw shadow and text p->setPen(palette().color(QPalette::Window).dark(115)); p->drawText(textRect.translated(3, 1), text); p->setPen(palette().color(QPalette::WindowText)); p->drawText(textRect.translated(2, 0), text); p->restore(); } void GraphicsDImgView::mouseDoubleClickEvent(QMouseEvent* e) { QGraphicsView::mouseDoubleClickEvent(e); -/* + if (!acceptsMouseClick(e)) { return; } -*/ + if (e->button() == Qt::LeftButton) { emit leftButtonDoubleClicked(); - - if (!qApp->style()->styleHint(QStyle::SH_ItemView_ActivateItemOnSingleClick)) - { - emit activated(); - } } } void GraphicsDImgView::mousePressEvent(QMouseEvent* e) { QGraphicsView::mousePressEvent(e); d->mousePressPos = QPoint(); d->movingInProgress = false; if (!acceptsMouseClick(e)) { return; } if (e->button() == Qt::LeftButton) { emit leftButtonClicked(); } if (e->button() == Qt::LeftButton || e->button() == Qt::MidButton) { d->mousePressPos = e->pos(); - if (!qApp->style()->styleHint(QStyle::SH_ItemView_ActivateItemOnSingleClick) || e->button() == Qt::MidButton) + if (e->button() == Qt::MidButton) { startPanning(e->pos()); } return; } if (e->button() == Qt::RightButton) { emit rightButtonClicked(); } } void GraphicsDImgView::mouseMoveEvent(QMouseEvent* e) { QGraphicsView::mouseMoveEvent(e); if ((e->buttons() & Qt::LeftButton || e->buttons() & Qt::MidButton) && !d->mousePressPos.isNull()) { if (!d->movingInProgress && e->buttons() & Qt::LeftButton) { if ((d->mousePressPos - e->pos()).manhattanLength() > QApplication::startDragDistance()) { startPanning(d->mousePressPos); } } if (d->movingInProgress) { continuePanning(e->pos()); } } } void GraphicsDImgView::mouseReleaseEvent(QMouseEvent* e) { QGraphicsView::mouseReleaseEvent(e); // Do not call acceptsMouseClick() here, only on press. Seems that release event are accepted per default. if ((e->button() == Qt::LeftButton || e->button() == Qt::MidButton) && !d->mousePressPos.isNull()) { if (!d->movingInProgress && e->button() == Qt::LeftButton) { - if (qApp->style()->styleHint(QStyle::SH_ItemView_ActivateItemOnSingleClick)) - { - emit activated(); - } + emit activated(); } else { finishPanning(); } } d->movingInProgress = false; d->mousePressPos = QPoint(); } bool GraphicsDImgView::acceptsMouseClick(QMouseEvent* e) { // the basic condition is that now item ate the event if (e->isAccepted()) { return false; } return true; } void GraphicsDImgView::resizeEvent(QResizeEvent* e) { QGraphicsView::resizeEvent(e); d->layout->updateZoomAndSize(); emit resized(); emit viewportRectChanged(mapToScene(viewport()->rect()).boundingRect()); } void GraphicsDImgView::scrollContentsBy(int dx, int dy) { QGraphicsView::scrollContentsBy(dx, dy); emit viewportRectChanged(mapToScene(viewport()->rect()).boundingRect()); } void GraphicsDImgView::startPanning(const QPoint& pos) { if (horizontalScrollBar()->maximum() || verticalScrollBar()->maximum()) { d->movingInProgress = true; d->mousePressPos = pos; d->panningScrollPos = QPoint(horizontalScrollBar()->value(), verticalScrollBar()->value()); viewport()->setCursor(Qt::SizeAllCursor); } } void GraphicsDImgView::continuePanning(const QPoint& pos) { QPoint delta = pos - d->mousePressPos; horizontalScrollBar()->setValue(d->panningScrollPos.x() + (isRightToLeft() ? delta.x() : -delta.x())); verticalScrollBar()->setValue(d->panningScrollPos.y() - delta.y()); emit contentsMoved(false); viewport()->update(); } void GraphicsDImgView::finishPanning() { emit contentsMoved(true); viewport()->unsetCursor(); } void GraphicsDImgView::scrollPointOnPoint(const QPointF& scenePos, const QPoint& viewportPos) { // This is inspired from QGraphicsView's centerOn() QPointF viewPoint = matrix().map(scenePos); if (horizontalScrollBar()->maximum()) { if (isRightToLeft()) { qint64 horizontal = 0; horizontal += horizontalScrollBar()->minimum(); horizontal += horizontalScrollBar()->maximum(); horizontal -= int(viewPoint.x() - viewportPos.x()); horizontalScrollBar()->setValue(horizontal); } else { horizontalScrollBar()->setValue(int(viewPoint.x() - viewportPos.x())); } } if (verticalScrollBar()->maximum()) { verticalScrollBar()->setValue(int(viewPoint.y() - viewportPos.y())); } viewport()->update(); } void GraphicsDImgView::wheelEvent(QWheelEvent* e) { if (e->modifiers() & Qt::ShiftModifier) { e->accept(); if (e->delta() < 0) { emit toNextImage(); } else if (e->delta() > 0) { emit toPreviousImage(); } return; } else if (e->modifiers() & Qt::ControlModifier) { // When zooming with the mouse-wheel, the image center is kept fixed. if (e->delta() < 0) { d->layout->decreaseZoom(e->pos()); } else if (e->delta() > 0) { d->layout->increaseZoom(e->pos()); } return; } QGraphicsView::wheelEvent(e); } void GraphicsDImgView::slotCornerButtonPressed() { if (d->panIconPopup) { d->panIconPopup->hide(); d->panIconPopup->deleteLater(); d->panIconPopup = 0; } d->panIconPopup = new PanIconFrame(this); PanIconWidget* const pan = new PanIconWidget(d->panIconPopup); //connect(pan, SIGNAL(signalSelectionTakeFocus()), // this, SIGNAL(signalContentTakeFocus())); connect(pan, SIGNAL(signalSelectionMoved(QRect,bool)), this, SLOT(slotPanIconSelectionMoved(QRect,bool))); connect(pan, SIGNAL(signalHidden()), this, SLOT(slotPanIconHidden())); pan->setImage(180, 120, item()->image()); QRectF sceneRect(mapToScene(viewport()->rect().topLeft()), mapToScene(viewport()->rect().bottomRight())); pan->setRegionSelection(item()->zoomSettings()->sourceRect(sceneRect).toRect()); pan->setMouseFocus(); d->panIconPopup->setMainWidget(pan); //slotContentTakeFocus(); QPoint g = mapToGlobal(viewport()->pos()); g.setX(g.x()+ viewport()->size().width()); g.setY(g.y()+ viewport()->size().height()); d->panIconPopup->popup(QPoint(g.x() - d->panIconPopup->width(), g.y() - d->panIconPopup->height())); pan->setCursorToLocalRegionSelectionCenter(); } void GraphicsDImgView::slotPanIconHidden() { d->cornerButton->blockSignals(true); d->cornerButton->animateClick(); d->cornerButton->blockSignals(false); } void GraphicsDImgView::slotPanIconSelectionMoved(const QRect& imageRect, bool b) { QRectF zoomRect = item()->zoomSettings()->mapImageToZoom(imageRect); qCDebug(DIGIKAM_WIDGETS_LOG) << imageRect << zoomRect; centerOn(item()->mapToScene(zoomRect.center())); if (b) { d->panIconPopup->hide(); d->panIconPopup->deleteLater(); d->panIconPopup = 0; slotPanIconHidden(); //slotContentLeaveFocus(); } } void GraphicsDImgView::slotContentsMoved() { emit contentsMoving(horizontalScrollBar()->value(), verticalScrollBar()->value()); } int GraphicsDImgView::contentsX() const { return horizontalScrollBar()->value(); } int GraphicsDImgView::contentsY() const { return verticalScrollBar()->value(); } void GraphicsDImgView::setContentsPos(int x, int y) { horizontalScrollBar()->setValue(x); verticalScrollBar()->setValue(y); } void GraphicsDImgView::setShowText(bool val) { d->showText = val; } QRect GraphicsDImgView::visibleArea() const { return (mapToScene(viewport()->geometry()).boundingRect().toRect()); } void GraphicsDImgView::fitToWindow() { layout()->fitToWindow(); update(); } void GraphicsDImgView::toggleFullScreen(bool set) { if (set) { d->scene->setBackgroundBrush(Qt::black); setFrameShape(QFrame::NoFrame); } else { d->scene->setBackgroundBrush(Qt::NoBrush); setFrameShape(QFrame::StyledPanel); } } } // namespace Digikam diff --git a/core/libs/widgets/graphicsview/graphicsdimgview.h b/core/libs/widgets/graphicsview/graphicsdimgview.h index 860ad57679..a344cbee6b 100644 --- a/core/libs/widgets/graphicsview/graphicsdimgview.h +++ b/core/libs/widgets/graphicsview/graphicsdimgview.h @@ -1,139 +1,138 @@ /* ============================================================ * * This file is a part of digiKam project * http://www.digikam.org * * Date : 2010-04-30 * Description : Graphics View for DImg preview * * Copyright (C) 2010-2012 by Marcel Wiesweg * Copyright (C) 2011-2018 by Gilles Caulier * * This program is free software; you can redistribute it * and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation; * either version 2, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * ============================================================ */ #ifndef DIGIKAM_GRAPHICS_DIMG_VIEW_H #define DIGIKAM_GRAPHICS_DIMG_VIEW_H // Qt includes #include // Local includes #include "digikam_export.h" namespace Digikam { class GraphicsDImgItem; class DImgPreviewItem; class SinglePhotoPreviewLayout; class DIGIKAM_EXPORT GraphicsDImgView : public QGraphicsView { Q_OBJECT public: explicit GraphicsDImgView(QWidget* const parent = 0); virtual ~GraphicsDImgView(); /** Store internal instance of item as GraphicsDImgItem. You can store DImgPreviewItem object also by this method. * Use item() or previewItem() to get right version. * Note: if you store a GraphicsDImgItem object, previewItem() will return 0. */ void setItem(GraphicsDImgItem* const item); /** Return the instance of item set by setItem(). */ GraphicsDImgItem* item() const; /** Return a cast of item instance of item set by setItem() as DImgPreviewItem * Note: if you store a GraphicsDImgItem object using setItem(), this method will return 0. */ DImgPreviewItem* previewItem() const; SinglePhotoPreviewLayout* layout() const; /** Scrolls the view such that scenePos (in scene coordinates * is displayed on the viewport at viewportPos (in viewport coordinates). * E.g., calling scrollPointOnPoint(scenePos, viewport()->rect().center()) is * equivalent to calling centerOn(scenePos). */ void scrollPointOnPoint(const QPointF& scenePos, const QPoint& viewportPos); // Change from protected to public to be used by ImageRegionWidget and ImageRegionItem void drawText(QPainter* p, const QRectF& rect, const QString& text); int contentsX() const; int contentsY() const; QRect visibleArea() const; void setContentsPos(int x, int y); void fitToWindow(); void toggleFullScreen(bool set); Q_SIGNALS: void contentsMoving(int, int); void rightButtonClicked(); void leftButtonClicked(); void leftButtonDoubleClicked(); void activated(); void toNextImage(); void toPreviousImage(); void contentsMoved(bool panningFinished); void resized(); - //void contentTakeFocus(); void viewportRectChanged(const QRectF& viewportRect); protected: void drawForeground(QPainter* painter, const QRectF& rect); void installPanIcon(); void mouseDoubleClickEvent(QMouseEvent*); void mousePressEvent(QMouseEvent*); void mouseMoveEvent(QMouseEvent*); void mouseReleaseEvent(QMouseEvent*); void wheelEvent(QWheelEvent*); void resizeEvent(QResizeEvent*); void startPanning(const QPoint& pos); void continuePanning(const QPoint& pos); void finishPanning(); void setShowText(bool value); virtual bool acceptsMouseClick(QMouseEvent* e); virtual void scrollContentsBy(int dx, int dy); protected Q_SLOTS: void slotContentsMoved(); void slotCornerButtonPressed(); void slotPanIconHidden(); virtual void slotPanIconSelectionMoved(const QRect&, bool); private: class Private; Private* const d; }; } // namespace Digikam #endif // DIGIKAM_GRAPHICS_DIMG_VIEW_H diff --git a/core/libs/widgets/itemview/itemviewcategorized.cpp b/core/libs/widgets/itemview/itemviewcategorized.cpp index b9d05c4240..b56bbbcd68 100644 --- a/core/libs/widgets/itemview/itemviewcategorized.cpp +++ b/core/libs/widgets/itemview/itemviewcategorized.cpp @@ -1,1062 +1,1050 @@ /* ============================================================ * * This file is a part of digiKam project * http://www.digikam.org * * Date : 2010-01-16 * Description : Qt item view for images * * Copyright (C) 2009-2012 by Marcel Wiesweg * Copyright (C) 2011-2018 by Gilles Caulier * * This program is free software; you can redistribute it * and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation; * either version 2, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * ============================================================ */ #include "itemviewcategorized.h" // Qt includes #include #include #include #include #include #include // Local includes #include "digikam_debug.h" #include "thememanager.h" #include "ditemdelegate.h" #include "abstractitemdragdrophandler.h" #include "itemviewtooltip.h" namespace Digikam { class Q_DECL_HIDDEN ItemViewCategorized::Private { public: explicit Private(ItemViewCategorized* const q) : delegate(0), toolTip(0), notificationToolTip(0), showToolTip(false), usePointingHand(true), scrollStepFactor(10), currentMouseEvent(0), ensureOneSelectedItem(false), ensureInitialSelectedItem(false), scrollCurrentToCenter(false), mouseButtonPressed(Qt::NoButton), hintAtSelectionRow(-1), q(q) { } QModelIndex scrollPositionHint() const; public: DItemDelegate* delegate; ItemViewToolTip* toolTip; ItemViewToolTip* notificationToolTip; bool showToolTip; bool usePointingHand; int scrollStepFactor; QMouseEvent* currentMouseEvent; bool ensureOneSelectedItem; bool ensureInitialSelectedItem; bool scrollCurrentToCenter; Qt::MouseButton mouseButtonPressed; QPersistentModelIndex hintAtSelectionIndex; int hintAtSelectionRow; QPersistentModelIndex hintAtScrollPosition; ItemViewCategorized* const q; }; QModelIndex ItemViewCategorized::Private::scrollPositionHint() const { if (q->verticalScrollBar()->value() == q->verticalScrollBar()->minimum()) { return QModelIndex(); } QModelIndex hint = q->currentIndex(); // If the user scrolled, do not take current item, but first visible if (!hint.isValid() || !q->viewport()->rect().intersects(q->visualRect(hint))) { QList visibleIndexes = q->categorizedIndexesIn(q->viewport()->rect()); if (!visibleIndexes.isEmpty()) { hint = visibleIndexes.first(); } } return hint; } // ------------------------------------------------------------------------------- ItemViewCategorized::ItemViewCategorized(QWidget* const parent) : DCategorizedView(parent), d(new Private(this)) { setViewMode(QListView::IconMode); setLayoutDirection(Qt::LeftToRight); setFlow(QListView::LeftToRight); setResizeMode(QListView::Adjust); setMovement(QListView::Static); setWrapping(true); // important optimization for layouting setUniformItemSizes(true); // disable "feature" from DCategorizedView setDrawDraggedItems(false); setSelectionMode(QAbstractItemView::ExtendedSelection); setDragEnabled(true); setEditTriggers(QAbstractItemView::NoEditTriggers); viewport()->setAcceptDrops(true); setMouseTracking(true); - connect(this, SIGNAL(activated(QModelIndex)), - this, SLOT(slotActivated(QModelIndex))); - connect(this, SIGNAL(clicked(QModelIndex)), this, SLOT(slotClicked(QModelIndex))); connect(this, SIGNAL(entered(QModelIndex)), this, SLOT(slotEntered(QModelIndex))); + connect(this, SIGNAL(clicked(QModelIndex)), + this, SLOT(slotActivated(QModelIndex))); + + connect(this, SIGNAL(doubleClicked(QModelIndex)), + this, SLOT(slotActivated(QModelIndex))); + connect(ThemeManager::instance(), SIGNAL(signalThemeChanged()), this, SLOT(slotThemeChanged())); } ItemViewCategorized::~ItemViewCategorized() { delete d; } void ItemViewCategorized::setToolTip(ItemViewToolTip* tip) { d->toolTip = tip; } void ItemViewCategorized::setItemDelegate(DItemDelegate* delegate) { if (d->delegate == delegate) { return; } if (d->delegate) { disconnect(d->delegate, SIGNAL(gridSizeChanged(QSize)), this, SLOT(slotGridSizeChanged(QSize))); } d->delegate = delegate; DCategorizedView::setItemDelegate(d->delegate); connect(d->delegate, SIGNAL(gridSizeChanged(QSize)), this, SLOT(slotGridSizeChanged(QSize))); } void ItemViewCategorized::setSpacing(int spacing) { d->delegate->setSpacing(spacing); } void ItemViewCategorized::setUsePointingHandCursor(bool useCursor) { d->usePointingHand = useCursor; } void ItemViewCategorized::setScrollStepGranularity(int factor) { d->scrollStepFactor = qMax(1, factor); } DItemDelegate* ItemViewCategorized::delegate() const { return d->delegate; } int ItemViewCategorized::numberOfSelectedIndexes() const { return selectedIndexes().size(); } void ItemViewCategorized::toFirstIndex() { QModelIndex index = moveCursor(MoveHome, Qt::NoModifier); clearSelection(); setCurrentIndex(index); scrollToTop(); } void ItemViewCategorized::toLastIndex() { QModelIndex index = moveCursor(MoveEnd, Qt::NoModifier); clearSelection(); setCurrentIndex(index); scrollToBottom(); } void ItemViewCategorized::toNextIndex() { toIndex(moveCursor(MoveNext, Qt::NoModifier)); } void ItemViewCategorized::toPreviousIndex() { toIndex(moveCursor(MovePrevious, Qt::NoModifier)); } void ItemViewCategorized::toIndex(const QModelIndex& index) { if (!index.isValid()) { return; } clearSelection(); setCurrentIndex(index); scrollTo(index); } void ItemViewCategorized::awayFromSelection() { QItemSelection selection = selectionModel()->selection(); if (selection.isEmpty()) { return; } const QModelIndex first = model()->index(0, 0); const QModelIndex last = model()->index(model()->rowCount() - 1, 0); if (selection.contains(first) && selection.contains(last)) { QItemSelection remaining(first, last); remaining.merge(selection, QItemSelectionModel::Toggle); QList indexes = remaining.indexes(); if (indexes.isEmpty()) { clearSelection(); setCurrentIndex(QModelIndex()); } else { toIndex(remaining.indexes().first()); } } else if (selection.contains(last)) { setCurrentIndex(selection.indexes().first()); toPreviousIndex(); } else { setCurrentIndex(selection.indexes().last()); toNextIndex(); } } void ItemViewCategorized::scrollToRelaxed(const QModelIndex& index, QAbstractItemView::ScrollHint hint) { if (viewport()->rect().intersects(visualRect(index))) { return; } scrollTo(index, hint); } void ItemViewCategorized::invertSelection() { const QModelIndex topLeft = model()->index(0, 0); const QModelIndex bottomRight = model()->index(model()->rowCount() - 1, 0); const QItemSelection selection(topLeft, bottomRight); selectionModel()->select(selection, QItemSelectionModel::Toggle); } void ItemViewCategorized::setSelectedIndexes(const QList& indexes) { if (selectedIndexes() == indexes) { return; } QItemSelection mySelection; foreach (const QModelIndex& index, indexes) { mySelection.select(index, index); } selectionModel()->select(mySelection, QItemSelectionModel::ClearAndSelect); } void ItemViewCategorized::setToolTipEnabled(bool enable) { d->showToolTip = enable; } bool ItemViewCategorized::isToolTipEnabled() const { return d->showToolTip; } void ItemViewCategorized::slotThemeChanged() { viewport()->update(); } void ItemViewCategorized::slotSetupChanged() { viewport()->update(); } void ItemViewCategorized::slotGridSizeChanged(const QSize& gridSize) { setGridSize(gridSize); if (!gridSize.isNull()) { horizontalScrollBar()->setSingleStep(gridSize.width() / d->scrollStepFactor); verticalScrollBar()->setSingleStep(gridSize.height() / d->scrollStepFactor); } } void ItemViewCategorized::updateDelegateSizes() { QStyleOptionViewItem option = viewOptions(); /* int frameAroundContents = 0; if (style()->styleHint(QStyle::SH_ScrollView_FrameOnlyAroundContents)) { frameAroundContents = style()->pixelMetric(QStyle::PM_DefaultFrameWidth) * 2; } const int contentWidth = viewport()->width() - 1 - frameAroundContents - style()->pixelMetric(QStyle::PM_ScrollBarExtent, 0, verticalScrollBar()); const int contentHeight = viewport()->height() - 1 - frameAroundContents - style()->pixelMetric(QStyle::PM_ScrollBarExtent, 0, horizontalScrollBar()); option.rect = QRect(0, 0, contentWidth, contentHeight); */ option.rect = QRect(QPoint(0, 0), viewport()->size()); d->delegate->setDefaultViewOptions(option); } void ItemViewCategorized::slotActivated(const QModelIndex& index) { - Qt::KeyboardModifiers modifiers = Qt::NoModifier; - if (d->currentMouseEvent) { // Ignore activation if Ctrl or Shift is pressed (for selection) - modifiers = d->currentMouseEvent->modifiers(); - const bool shiftKeyPressed = modifiers & Qt::ShiftModifier; - const bool controlKeyPressed = modifiers & Qt::ControlModifier; - - if (shiftKeyPressed || controlKeyPressed) - { - return; - } - - const bool rightClick = d->currentMouseEvent->button() & Qt::RightButton; + const Qt::KeyboardModifiers modifiers = d->currentMouseEvent->modifiers(); + const Qt::MouseButton button = d->currentMouseEvent->button(); + const bool shiftKeyPressed = modifiers & Qt::ShiftModifier; + const bool controlKeyPressed = modifiers & Qt::ControlModifier; + const bool rightButtonPressed = button & Qt::RightButton; - if (rightClick) + if (shiftKeyPressed || controlKeyPressed || rightButtonPressed) { return; } // if the activation is caused by mouse click (not keyboard) // we need to check the hot area if (d->currentMouseEvent->isAccepted() && !d->delegate->acceptsActivation(d->currentMouseEvent->pos(), visualRect(index), index)) { return; } } - d->currentMouseEvent = 0; - indexActivated(index, modifiers); + indexActivated(index, d->currentMouseEvent); } void ItemViewCategorized::slotClicked(const QModelIndex& index) { if (d->currentMouseEvent) { emit clicked(d->currentMouseEvent, index); } } void ItemViewCategorized::slotEntered(const QModelIndex& index) { if (d->currentMouseEvent) { emit entered(d->currentMouseEvent, index); } } void ItemViewCategorized::reset() { DCategorizedView::reset(); // FIXME : Emitting this causes a crash importstackedview, because the model is not yet set. // atm there's a check against null models though. emit selectionChanged(); emit selectionCleared(); d->ensureInitialSelectedItem = true; d->hintAtScrollPosition = QModelIndex(); d->hintAtSelectionIndex = QModelIndex(); d->hintAtSelectionRow = -1; verticalScrollBar()->setValue(verticalScrollBar()->minimum()); horizontalScrollBar()->setValue(horizontalScrollBar()->minimum()); } void ItemViewCategorized::selectionChanged(const QItemSelection& selectedItems, const QItemSelection& deselectedItems) { DCategorizedView::selectionChanged(selectedItems, deselectedItems); emit selectionChanged(); if (!selectionModel()->hasSelection()) { emit selectionCleared(); } userInteraction(); } void ItemViewCategorized::rowsInserted(const QModelIndex& parent, int start, int end) { DCategorizedView::rowsInserted(parent, start, end); if (start == 0) { ensureSelectionAfterChanges(); } } void ItemViewCategorized::rowsRemoved(const QModelIndex& parent, int start, int end) { DCategorizedView::rowsRemoved(parent, start, end); if (d->scrollCurrentToCenter) { scrollTo(currentIndex(), QAbstractItemView::PositionAtCenter); } } void ItemViewCategorized::rowsAboutToBeRemoved(const QModelIndex& parent, int start, int end) { DCategorizedView::rowsAboutToBeRemoved(parent, start, end); // Ensure one selected item int totalToRemove = end - start + 1; bool remainingRows = model()->rowCount(parent) > totalToRemove; if (!remainingRows) { return; } QItemSelection removed(model()->index(start, 0), model()->index(end, 0)); if (selectionModel()->hasSelection()) { // find out which selected indexes are left after rows are removed QItemSelection selected = selectionModel()->selection(); QModelIndex current = currentIndex(); QModelIndex indexToAnchor; if (selected.contains(current)) { indexToAnchor = current; } else if (!selected.isEmpty()) { indexToAnchor = selected.first().topLeft(); } selected.merge(removed, QItemSelectionModel::Deselect); if (selected.isEmpty()) { QModelIndex newCurrent = nextIndexHint(indexToAnchor, removed.first() /*a range*/); setCurrentIndex(newCurrent); } } QModelIndex hint = d->scrollPositionHint(); if (removed.contains(hint)) { d->hintAtScrollPosition = nextIndexHint(hint, removed.first() /*a range*/); } } void ItemViewCategorized::layoutAboutToBeChanged() { if (selectionModel()) { d->ensureOneSelectedItem = selectionModel()->hasSelection(); } else { qCWarning(DIGIKAM_GENERAL_LOG) << "Called without selection model, check whether the models are ok.."; } QModelIndex current = currentIndex(); // store some hints so that if all selected items were removed do not need to default to 0,0. if (d->ensureOneSelectedItem) { QItemSelection currentSelection = selectionModel()->selection(); QModelIndex indexToAnchor; if (currentSelection.contains(current)) { indexToAnchor = current; } else if (!currentSelection.isEmpty()) { indexToAnchor = currentSelection.first().topLeft(); } if (indexToAnchor.isValid()) { d->hintAtSelectionRow = indexToAnchor.row(); d->hintAtSelectionIndex = nextIndexHint(indexToAnchor, QItemSelectionRange(indexToAnchor)); } } // some precautions to keep current scroll position d->hintAtScrollPosition = d->scrollPositionHint(); } QModelIndex ItemViewCategorized::nextIndexHint(const QModelIndex& indexToAnchor, const QItemSelectionRange& removed) const { Q_UNUSED(indexToAnchor); if (removed.bottomRight().row() == model()->rowCount() - 1) { if (removed.topLeft().row() == 0) { return QModelIndex(); } return model()->index(removed.topLeft().row() - 1, 0); // last remaining, no next one left } else { return model()->index(removed.bottomRight().row() + 1, 0); // next remaining } } void ItemViewCategorized::layoutWasChanged() { // connected queued to layoutChanged() ensureSelectionAfterChanges(); if (d->hintAtScrollPosition.isValid()) { scrollToRelaxed(d->hintAtScrollPosition); d->hintAtScrollPosition = QModelIndex(); } else { scrollToRelaxed(currentIndex()); } } void ItemViewCategorized::userInteraction() { // as soon as the user did anything affecting selection, we don't interfere anymore d->ensureInitialSelectedItem = false; d->hintAtSelectionIndex = QModelIndex(); } void ItemViewCategorized::ensureSelectionAfterChanges() { if (d->ensureInitialSelectedItem && model()->rowCount()) { // Ensure the item (0,0) is selected, if the model was reset previously // and the user did not change the selection since reset. // Caveat: Item at (0,0) may have changed. bool hadInitial = d->ensureInitialSelectedItem; d->ensureInitialSelectedItem = false; d->ensureOneSelectedItem = false; QModelIndex index = model()->index(0,0); if (index.isValid()) { selectionModel()->select(index, QItemSelectionModel::SelectCurrent | QItemSelectionModel::Clear); setCurrentIndex(index); // we want ensureInitial set to false if and only if the selection // is done from any other place than the previous line (i.e., by user action) // Effect: we select whatever is the current index(0,0) if (hadInitial) { d->ensureInitialSelectedItem = true; } } } else if (d->ensureOneSelectedItem) { // ensure we have a selection if there was one before d->ensureOneSelectedItem = false; if (model()->rowCount() && selectionModel()->selection().isEmpty()) { QModelIndex index; if (d->hintAtSelectionIndex.isValid()) { index = d->hintAtSelectionIndex; } else if (d->hintAtSelectionRow != -1) { index = model()->index(qMin(model()->rowCount(), d->hintAtSelectionRow), 0); } else { index = currentIndex(); } if (!index.isValid()) { index = model()->index(0, 0); } d->hintAtSelectionRow = -1; d->hintAtSelectionIndex = QModelIndex(); if (index.isValid()) { setCurrentIndex(index); selectionModel()->select(index, QItemSelectionModel::SelectCurrent); } } } } QModelIndex ItemViewCategorized::indexForCategoryAt(const QPoint& pos) const { return categoryAt(pos); } QModelIndex ItemViewCategorized::moveCursor(CursorAction cursorAction, Qt::KeyboardModifiers modifiers) { QModelIndex current = currentIndex(); if (!current.isValid()) { return DCategorizedView::moveCursor(cursorAction, modifiers); } // We want a simple wrapping navigation. // Default behavior we do not want: right/left does never change row; Next/Previous is equivalent to Down/Up switch (cursorAction) { case MoveNext: case MoveRight: { QModelIndex next = model()->index(current.row() + 1, 0); if (next.isValid()) { return next; } else { return current; } break; } case MovePrevious: case MoveLeft: { QModelIndex previous = model()->index(current.row() - 1, 0); if (previous.isValid()) { return previous; } else { return current; } break; } default: break; } return DCategorizedView::moveCursor(cursorAction, modifiers); } void ItemViewCategorized::showContextMenuOnIndex(QContextMenuEvent*, const QModelIndex&) { // implemented in subclass } void ItemViewCategorized::showContextMenu(QContextMenuEvent*) { // implemented in subclass } -void ItemViewCategorized::indexActivated(const QModelIndex&, Qt::KeyboardModifiers) +void ItemViewCategorized::indexActivated(const QModelIndex&, QMouseEvent*) { } bool ItemViewCategorized::showToolTip(const QModelIndex& index, QStyleOptionViewItem& option, QHelpEvent* he) { QRect innerRect; QPoint pos; if (he) { pos = he->pos(); } else { pos = option.rect.center(); } if (d->delegate->acceptsToolTip(he->pos(), option.rect, index, &innerRect)) { if (!innerRect.isNull()) { option.rect = innerRect; } d->toolTip->show(option, index); return true; } return false; } void ItemViewCategorized::contextMenuEvent(QContextMenuEvent* event) { userInteraction(); QModelIndex index = indexAt(event->pos()); if (index.isValid()) { showContextMenuOnIndex(event, index); } else { showContextMenu(event); } } void ItemViewCategorized::leaveEvent(QEvent*) { hideIndexNotification(); if (d->mouseButtonPressed != Qt::RightButton) { d->mouseButtonPressed = Qt::NoButton; } } void ItemViewCategorized::mousePressEvent(QMouseEvent* event) { userInteraction(); + // store event for entered(), clicked(), activated() signal handlers + d->currentMouseEvent = event; + const QModelIndex index = indexAt(event->pos()); // Clear selection on click on empty area. Standard behavior, but not done by QAbstractItemView for some reason. Qt::KeyboardModifiers modifiers = event->modifiers(); const Qt::MouseButton button = event->button(); const bool rightButtonPressed = button & Qt::RightButton; const bool shiftKeyPressed = modifiers & Qt::ShiftModifier; const bool controlKeyPressed = modifiers & Qt::ControlModifier; d->mouseButtonPressed = button; if (!index.isValid() && !rightButtonPressed && !shiftKeyPressed && !controlKeyPressed) { clearSelection(); } - // store event for entered(), clicked(), activated() signal handlers - if (!rightButtonPressed) - { - d->currentMouseEvent = event; - } - else - { - d->currentMouseEvent = 0; - } - DCategorizedView::mousePressEvent(event); if (!index.isValid()) { emit viewportClicked(event); } } void ItemViewCategorized::mouseReleaseEvent(QMouseEvent* event) { userInteraction(); if (d->scrollCurrentToCenter) { scrollTo(currentIndex(), QAbstractItemView::PositionAtCenter); } DCategorizedView::mouseReleaseEvent(event); } void ItemViewCategorized::mouseMoveEvent(QMouseEvent* event) { QModelIndex index = indexAt(event->pos()); QRect indexVisualRect; if (index.isValid()) { indexVisualRect = visualRect(index); if (d->usePointingHand && d->delegate->acceptsActivation(event->pos(), indexVisualRect, index)) { setCursor(Qt::PointingHandCursor); } else { unsetCursor(); } } else { unsetCursor(); } if (d->notificationToolTip && d->notificationToolTip->isVisible()) { if (!d->notificationToolTip->rect().adjusted(-50, -50, 50, 50).contains(event->pos())) { hideIndexNotification(); } } DCategorizedView::mouseMoveEvent(event); d->delegate->mouseMoved(event, indexVisualRect, index); } void ItemViewCategorized::wheelEvent(QWheelEvent* event) { // DCategorizedView updates the single step at some occasions in a private methody horizontalScrollBar()->setSingleStep(d->delegate->gridSize().height() / d->scrollStepFactor); verticalScrollBar()->setSingleStep(d->delegate->gridSize().width() / d->scrollStepFactor); if (event->modifiers() & Qt::ControlModifier) { const int delta = event->delta(); if (delta > 0) { emit zoomInStep(); } else if (delta < 0) { emit zoomOutStep(); } event->accept(); return; } if (verticalScrollBarPolicy() == Qt::ScrollBarAlwaysOff && event->orientation() == Qt::Vertical) { QWheelEvent n(event->pos(), event->globalPos(), event->delta(), event->buttons(), event->modifiers(), Qt::Horizontal); QApplication::sendEvent(horizontalScrollBar(), &n); event->setAccepted(n.isAccepted()); } else { DCategorizedView::wheelEvent(event); } } void ItemViewCategorized::keyPressEvent(QKeyEvent* event) { userInteraction(); if (event == QKeySequence::Copy) { copy(); event->accept(); return; } else if (event == QKeySequence::Paste) { paste(); event->accept(); return; } /* // from dolphincontroller.cpp const QItemSelectionModel* selModel = m_itemView->selectionModel(); const QModelIndex currentIndex = selModel->currentIndex(); const bool trigger = currentIndex.isValid() && ((event->key() == Qt::Key_Return) || (event->key() == Qt::Key_Enter)) && (selModel->selectedIndexes().count() > 0); if (trigger) { const QModelIndexList indexList = selModel->selectedIndexes(); foreach (const QModelIndex& index, indexList) { emit itemTriggered(itemForIndex(index)); } } */ DCategorizedView::keyPressEvent(event); emit keyPressed(event); } void ItemViewCategorized::resizeEvent(QResizeEvent* e) { QModelIndex oldPosition = d->scrollPositionHint(); DCategorizedView::resizeEvent(e); updateDelegateSizes(); scrollToRelaxed(oldPosition, QAbstractItemView::PositionAtTop); } bool ItemViewCategorized::viewportEvent(QEvent* event) { switch (event->type()) { case QEvent::FontChange: { updateDelegateSizes(); break; } case QEvent::ToolTip: { if (!d->showToolTip) { return true; } QHelpEvent* he = static_cast(event); const QModelIndex index = indexAt(he->pos()); if (!index.isValid()) { break; } QStyleOptionViewItem option = viewOptions(); option.rect = visualRect(index); option.state |= (index == currentIndex() ? QStyle::State_HasFocus : QStyle::State_None); showToolTip(index, option, he); return true; } default: break; } return DCategorizedView::viewportEvent(event); } void ItemViewCategorized::showIndexNotification(const QModelIndex& index, const QString& message) { hideIndexNotification(); if (!index.isValid()) { return; } if (!d->notificationToolTip) { d->notificationToolTip = new ItemViewToolTip(this); } d->notificationToolTip->setTipContents(message); QStyleOptionViewItem option = viewOptions(); option.rect = visualRect(index); option.state |= (index == currentIndex() ? QStyle::State_HasFocus : QStyle::State_None); d->notificationToolTip->show(option, index); } void ItemViewCategorized::hideIndexNotification() { if (d->notificationToolTip) { d->notificationToolTip->hide(); } } /** * cut(), copy(), paste(), dragEnterEvent(), dragMoveEvent(), dropEvent(), startDrag() * are implemented by DragDropViewImplementation */ QModelIndex ItemViewCategorized::mapIndexForDragDrop(const QModelIndex& index) const { return filterModel()->mapToSource(index); } QPixmap ItemViewCategorized::pixmapForDrag(const QList& indexes) const { QStyleOptionViewItem option = viewOptions(); option.rect = viewport()->rect(); return d->delegate->pixmapForDrag(option, indexes); } void ItemViewCategorized::setScrollCurrentToCenter(bool enabled) { d->scrollCurrentToCenter = enabled; } void ItemViewCategorized::scrollTo(const QModelIndex& index, ScrollHint hint) { if (d->scrollCurrentToCenter && d->mouseButtonPressed == Qt::NoButton) { hint = QAbstractItemView::PositionAtCenter; } d->mouseButtonPressed = Qt::NoButton; DCategorizedView::scrollTo(index, hint); } } // namespace Digikam diff --git a/core/libs/widgets/itemview/itemviewcategorized.h b/core/libs/widgets/itemview/itemviewcategorized.h index 04d8781fe6..dd5c872771 100644 --- a/core/libs/widgets/itemview/itemviewcategorized.h +++ b/core/libs/widgets/itemview/itemviewcategorized.h @@ -1,204 +1,204 @@ /* ============================================================ * * This file is a part of digiKam project * http://www.digikam.org * * Date : 2010-01-16 * Description : Qt item view for images * * Copyright (C) 2009-2012 by Marcel Wiesweg * Copyright (C) 2011-2018 by Gilles Caulier * * This program is free software; you can redistribute it * and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation; * either version 2, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * ============================================================ */ #ifndef DIGIKAM_ITEM_VIEW_CATEGORIZED_H #define DIGIKAM_ITEM_VIEW_CATEGORIZED_H // Local includes #include "digikam_export.h" #include "dcategorizedview.h" #include "dragdropimplementations.h" class QSortFilterProxyModel; namespace Digikam { class DItemDelegate; class ItemViewToolTip; class DIGIKAM_EXPORT ItemViewCategorized : public DCategorizedView, public DragDropViewImplementation { Q_OBJECT public: explicit ItemViewCategorized(QWidget* const parent = 0); ~ItemViewCategorized(); DItemDelegate* delegate() const; int numberOfSelectedIndexes() const; /** Selects the index as current and scrolls to it */ void toFirstIndex(); void toLastIndex(); void toNextIndex(); void toPreviousIndex(); void toIndex(const QModelIndex& index); void awayFromSelection(); /** Scroll automatically the current index to center of the view. */ void setScrollCurrentToCenter(bool enabled); /** Like scrollTo, but only scrolls if the index is not visible, regardless of hint. */ void scrollToRelaxed(const QModelIndex& index, ScrollHint hint = EnsureVisible); void invertSelection(); void setSelectedIndexes(const QList& indexes); void setToolTipEnabled(bool enabled); bool isToolTipEnabled() const; /** Sets the spacing. Does not use setSpacing()/spacing() from QListView */ void setSpacing(int spacing); /** Set if the PointingHand Cursor should be shown over the activation area */ void setUsePointingHandCursor(bool useCursor); /** Determine a step size for scrolling: The larger this number, * the smaller and more precise is the scrolling. Default is 10. */ void setScrollStepGranularity(int factor); virtual QSortFilterProxyModel* filterModel() const = 0; virtual void scrollTo(const QModelIndex& index, ScrollHint hint = EnsureVisible); public Q_SLOTS: void showIndexNotification(const QModelIndex& index, const QString& message); void hideIndexNotification(); virtual void cut() { DragDropViewImplementation::cut(); } virtual void copy() { DragDropViewImplementation::copy(); } virtual void paste() { DragDropViewImplementation::paste(); } Q_SIGNALS: /// Emitted when any selection change occurs. Any of the signals below will be emitted before. void selectionChanged(); /// Emitted when the selection is completely cleared. void selectionCleared(); void zoomOutStep(); void zoomInStep(); /** For overlays: Like the respective parent class signals, but with additional info. * Do not change the mouse events. */ void clicked(const QMouseEvent* e, const QModelIndex& index); void entered(const QMouseEvent* e, const QModelIndex& index); /// While clicked() is emitted with a valid index, this corresponds to clicking on empty space void viewportClicked(const QMouseEvent* e); /** Remember you may want to check if the event is accepted or ignored. * This signal is emitted after being handled by this widget. * You can accept it if ignored. */ void keyPressed(QKeyEvent* e); protected Q_SLOTS: void slotActivated(const QModelIndex& index); void slotClicked(const QModelIndex& index); void slotEntered(const QModelIndex& index); void layoutAboutToBeChanged(); void layoutWasChanged(); virtual void slotThemeChanged(); virtual void slotSetupChanged(); protected: void encodeIsCutSelection(QMimeData* mime, bool isCutSelection); bool decodeIsCutSelection(const QMimeData* mimeData); void setToolTip(ItemViewToolTip* tip); void setItemDelegate(DItemDelegate* delegate); void updateDelegateSizes(); void userInteraction(); /** Returns an index that is representative for the category at position pos */ QModelIndex indexForCategoryAt(const QPoint& pos) const; // reimplemented from parent class void contextMenuEvent(QContextMenuEvent* event); void keyPressEvent(QKeyEvent* event); void leaveEvent(QEvent* event); void mouseMoveEvent(QMouseEvent* event); void mousePressEvent(QMouseEvent* event); void mouseReleaseEvent(QMouseEvent* event); void resizeEvent(QResizeEvent* e); void reset(); void rowsAboutToBeRemoved(const QModelIndex& parent, int start, int end); void rowsInserted(const QModelIndex& parent, int start, int end); void rowsRemoved(const QModelIndex& parent, int start, int end); void selectionChanged(const QItemSelection&, const QItemSelection&); bool viewportEvent(QEvent* event); void wheelEvent(QWheelEvent* event); QModelIndex moveCursor(CursorAction cursorAction, Qt::KeyboardModifiers modifiers); /// Reimplement these in a subclass virtual void showContextMenuOnIndex(QContextMenuEvent* event, const QModelIndex& index); virtual void showContextMenu(QContextMenuEvent* event); - virtual void indexActivated(const QModelIndex& index, Qt::KeyboardModifiers modifiers); + virtual void indexActivated(const QModelIndex& index, QMouseEvent* const event); /** Provides default behavior, can reimplement in a subclass. * Returns true if a tooltip was shown. * The help event is optional. */ virtual bool showToolTip(const QModelIndex& index, QStyleOptionViewItem& option, QHelpEvent* e = 0); DECLARE_VIEW_DRAG_DROP_METHODS(DCategorizedView) /// Note: pure virtual dragDropHandler() still open from DragDropViewImplementation virtual QModelIndex mapIndexForDragDrop(const QModelIndex& index) const; virtual QPixmap pixmapForDrag(const QList& indexes) const; /** * Assuming the given indexes would be removed (hypothetically!), * return the index to be selected instead, starting from anchor. * The default implementation returns the next remaining sibling. */ virtual QModelIndex nextIndexHint(const QModelIndex& indexToAnchor, const QItemSelectionRange& removed) const; private Q_SLOTS: void slotGridSizeChanged(const QSize&); private: void ensureSelectionAfterChanges(); private: class Private; Private* const d; }; } // namespace Digikam #endif // DIGIKAM_ITEM_VIEW_CATEGORIZED_H diff --git a/core/showfoto/thumbbar/showfotocategorizedview.cpp b/core/showfoto/thumbbar/showfotocategorizedview.cpp index b63dcefb74..05c4bcebfc 100644 --- a/core/showfoto/thumbbar/showfotocategorizedview.cpp +++ b/core/showfoto/thumbbar/showfotocategorizedview.cpp @@ -1,580 +1,580 @@ /* ============================================================ * * This file is a part of digiKam project * http://www.digikam.org * * Date : 2012-07-13 * Description : Qt categorized item view for showfoto items * * Copyright (C) 2013 by Mohamed_Anwer * * This program is free software; you can redistribute it * and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation; * either version 2, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * ============================================================ */ #include "showfotocategorizedview.h" // Qt includes #include // Local include #include "digikam_debug.h" #include "loadingcacheinterface.h" #include "itemviewtooltip.h" #include "showfotodelegate.h" #include "showfototooltipfiller.h" using namespace Digikam; namespace ShowFoto { class Q_DECL_HIDDEN ShowfotoItemViewToolTip : public ItemViewToolTip { public: explicit ShowfotoItemViewToolTip(ShowfotoCategorizedView* const view) : ItemViewToolTip(view) { } ShowfotoCategorizedView* view() const { return static_cast(ItemViewToolTip::view()); } protected: virtual QString tipContents() { ShowfotoItemInfo info = ShowfotoItemModel::retrieveShowfotoItemInfo(currentIndex()); return ShowfotoToolTipFiller::ShowfotoItemInfoTipContents(info); } }; class Q_DECL_HIDDEN ShowfotoCategorizedView::Private { public: explicit Private() : model(0), filterModel(0), delegate(0), showToolTip(false), scrollToItemId(0), delayedEnterTimer(0), currentMouseEvent(0) { } ShowfotoItemModel* model; ShowfotoSortFilterModel* filterModel; ShowfotoDelegate* delegate; bool showToolTip; qlonglong scrollToItemId; QTimer* delayedEnterTimer; QMouseEvent* currentMouseEvent; }; ShowfotoCategorizedView::ShowfotoCategorizedView(QWidget* const parent) : ItemViewCategorized(parent), d(new Private) { setToolTip(new ShowfotoItemViewToolTip(this)); LoadingCacheInterface::connectToSignalFileChanged(this, SLOT(slotFileChanged(QString))); d->delayedEnterTimer = new QTimer(this); d->delayedEnterTimer->setInterval(10); d->delayedEnterTimer->setSingleShot(false); connect(d->delayedEnterTimer, &QTimer::timeout, this, &ShowfotoCategorizedView::slotDelayedEnter); } ShowfotoCategorizedView::~ShowfotoCategorizedView() { d->delegate->removeAllOverlays(); delete d; } void ShowfotoCategorizedView::setModels(ShowfotoItemModel* model, ShowfotoSortFilterModel* filterModel) { if (d->delegate) { d->delegate->setAllOverlaysActive(false); } if (d->filterModel) { disconnect(d->filterModel, SIGNAL(layoutAboutToBeChanged()), this, SLOT(layoutAboutToBeChanged())); disconnect(d->filterModel, SIGNAL(layoutChanged()), this, SLOT(layoutWasChanged())); } d->model = model; d->filterModel = filterModel; setModel(d->filterModel); connect(d->filterModel, &ShowfotoSortFilterModel::layoutAboutToBeChanged, this, &ShowfotoCategorizedView::layoutAboutToBeChanged); connect(d->filterModel, &ShowfotoSortFilterModel::layoutChanged, this, &ShowfotoCategorizedView::layoutWasChanged, Qt::QueuedConnection); emit modelChanged(); if (d->delegate) { d->delegate->setAllOverlaysActive(true); } } ShowfotoItemModel* ShowfotoCategorizedView::showfotoItemModel() const { return d->model; } ShowfotoSortFilterModel* ShowfotoCategorizedView::showfotoSortFilterModel() const { return d->filterModel; } ShowfotoFilterModel* ShowfotoCategorizedView::showfotoFilterModel() const { return d->filterModel->showfotoFilterModel(); } ShowfotoThumbnailModel* ShowfotoCategorizedView::showfotoThumbnailModel() const { return qobject_cast(d->model); } QSortFilterProxyModel* ShowfotoCategorizedView::filterModel() const { return d->filterModel; } ShowfotoDelegate* ShowfotoCategorizedView::delegate() const { return d->delegate; } void ShowfotoCategorizedView::setItemDelegate(ShowfotoDelegate* delegate) { ThumbnailSize oldSize = thumbnailSize(); ShowfotoDelegate* const oldDelegate = d->delegate; if (oldDelegate) { hideIndexNotification(); d->delegate->setAllOverlaysActive(false); d->delegate->setViewOnAllOverlays(0); // Note: Be precise, no wildcard disconnect! disconnect(d->delegate, SIGNAL(requestNotification(QModelIndex,QString)), this, SLOT(showIndexNotification(QModelIndex,QString))); disconnect(d->delegate, SIGNAL(hideNotification()), this, SLOT(hideIndexNotification())); } d->delegate = delegate; delegate->setThumbnailSize(oldSize); if (oldDelegate) { d->delegate->setSpacing(oldDelegate->spacing()); } ItemViewCategorized::setItemDelegate(d->delegate); updateDelegateSizes(); d->delegate->setViewOnAllOverlays(this); d->delegate->setAllOverlaysActive(true); connect(d->delegate, &ShowfotoDelegate::requestNotification, this, &ShowfotoCategorizedView::showIndexNotification); connect(d->delegate, &ShowfotoDelegate::hideNotification, this, &ShowfotoCategorizedView::hideIndexNotification); } ShowfotoItemInfo ShowfotoCategorizedView::currentInfo() const { return d->filterModel->showfotoItemInfo(currentIndex()); } QUrl ShowfotoCategorizedView::currentUrl() const { return currentInfo().url; } QList ShowfotoCategorizedView::selectedShowfotoItemInfos() const { return d->filterModel->showfotoItemInfos(selectedIndexes()); } QList ShowfotoCategorizedView::selectedShowfotoItemInfosCurrentFirst() const { QList indexes = selectedIndexes(); QModelIndex current = currentIndex(); QList infos; foreach (const QModelIndex& index, indexes) { ShowfotoItemInfo info = d->filterModel->showfotoItemInfo(index); if (index == current) { infos.prepend(info); } else { infos.append(info); } } return infos; } QList ShowfotoCategorizedView::showfotoItemInfos() const { return d->filterModel->showfotoItemInfosSorted(); } QList ShowfotoCategorizedView::urls() const { QList infos = showfotoItemInfos(); QList urls; foreach (const ShowfotoItemInfo& info, infos) { urls << info.url; } return urls; } QList ShowfotoCategorizedView::selectedUrls() const { QList infos = selectedShowfotoItemInfos(); QList urls; foreach (const ShowfotoItemInfo& info, infos) { urls << info.url; } return urls; } void ShowfotoCategorizedView::toIndex(const QUrl& url) { ItemViewCategorized::toIndex(d->filterModel->indexForUrl(url)); } ShowfotoItemInfo ShowfotoCategorizedView::nextInOrder(const ShowfotoItemInfo& startingPoint, int nth) { QModelIndex index = d->filterModel->indexForShowfotoItemInfo(startingPoint); if (!index.isValid()) { return ShowfotoItemInfo(); } return d->filterModel->showfotoItemInfo(d->filterModel->index(index.row() + nth, 0, QModelIndex())); } QModelIndex ShowfotoCategorizedView::nextIndexHint(const QModelIndex& anchor, const QItemSelectionRange& removed) const { QModelIndex hint = ItemViewCategorized::nextIndexHint(anchor, removed); ShowfotoItemInfo info = d->filterModel->showfotoItemInfo(anchor); //qCDebug(DIGIKAM_SHOWFOTO_LOG) << "Having initial hint" << hint << "for" << anchor << d->model->numberOfIndexesForShowfotoItemInfo(info); // Fixes a special case of multiple (face) entries for the same image. // If one is removed, any entry of the same image shall be preferred. if (d->model->indexesForShowfotoItemInfo(info).size() > 1) { // The hint is for a different info, but we may have a hint for the same info if (info != d->filterModel->showfotoItemInfo(hint)) { int minDiff = d->filterModel->rowCount(); QList indexesForShowfotoItemInfo = d->filterModel->mapListFromSource(d->model->indexesForShowfotoItemInfo(info)); foreach (const QModelIndex& index, indexesForShowfotoItemInfo) { if (index == anchor || !index.isValid() || removed.contains(index)) { continue; } int distance = qAbs(index.row() - anchor.row()); if (distance < minDiff) { minDiff = distance; hint = index; //qCDebug(DIGIKAM_SHOWFOTO_LOG) << "Chose index" << hint << "at distance" << minDiff << "to" << anchor; } } } } return hint; } ThumbnailSize ShowfotoCategorizedView::thumbnailSize() const { /* ShowfotoThumbnailModel *thumbModel = ShowfotoThumbnailModel(); if (thumbModel) return thumbModel->thumbnailSize(); */ if (d->delegate) { return d->delegate->thumbnailSize(); } return ThumbnailSize(); } void ShowfotoCategorizedView::setThumbnailSize(int size) { setThumbnailSize(ThumbnailSize(size)); } void ShowfotoCategorizedView::setThumbnailSize(const ThumbnailSize& s) { // we abuse this pair of method calls to restore scroll position layoutAboutToBeChanged(); d->delegate->setThumbnailSize(s); layoutWasChanged(); } void ShowfotoCategorizedView::setCurrentWhenAvailable(qlonglong showfotoItemId) { d->scrollToItemId = showfotoItemId; } void ShowfotoCategorizedView::setCurrentUrl(const QUrl& url) { if (url.isEmpty()) { clearSelection(); setCurrentIndex(QModelIndex()); return; } QModelIndex index = d->filterModel->indexForUrl(url); if (!index.isValid()) { return; } clearSelection(); setCurrentIndex(index); } void ShowfotoCategorizedView::setCurrentInfo(const ShowfotoItemInfo& info) { QModelIndex index = d->filterModel->indexForShowfotoItemInfo(info); clearSelection(); setCurrentIndex(index); } void ShowfotoCategorizedView::setSelectedUrls(const QList& urlList) { QItemSelection mySelection; for (QList::const_iterator it = urlList.constBegin() ; it!=urlList.constEnd() ; ++it) { const QModelIndex index = d->filterModel->indexForUrl(*it); if (!index.isValid()) { qCWarning(DIGIKAM_GENERAL_LOG) << "no QModelIndex found for" << *it; } else { // TODO: is there a better way? mySelection.select(index, index); } } clearSelection(); selectionModel()->select(mySelection, QItemSelectionModel::Select); } void ShowfotoCategorizedView::setSelectedShowfotoItemInfos(const QList& infos) { QItemSelection mySelection; foreach (const ShowfotoItemInfo& info, infos) { QModelIndex index = d->filterModel->indexForShowfotoItemInfo(info); mySelection.select(index, index); } selectionModel()->select(mySelection, QItemSelectionModel::ClearAndSelect); } void ShowfotoCategorizedView::hintAt(const ShowfotoItemInfo& info) { if (info.isNull()) { return; } QModelIndex index = d->filterModel->indexForShowfotoItemInfo(info); if (!index.isValid()) { return; } selectionModel()->setCurrentIndex(index, QItemSelectionModel::NoUpdate); scrollTo(index); } void ShowfotoCategorizedView::addOverlay(ItemDelegateOverlay* overlay, ShowfotoDelegate* delegate) { if (!delegate) { delegate = d->delegate; } delegate->installOverlay(overlay); if (delegate == d->delegate) { overlay->setView(this); overlay->setActive(true); } } void ShowfotoCategorizedView::removeOverlay(ItemDelegateOverlay* overlay) { ShowfotoDelegate* const delegate = dynamic_cast(overlay->delegate()); if (delegate) { delegate->removeOverlay(overlay); } overlay->setView(0); } void ShowfotoCategorizedView::updateGeometries() { ItemViewCategorized::updateGeometries(); d->delayedEnterTimer->start(); } void ShowfotoCategorizedView::slotDelayedEnter() { // re-emit entered() for index under mouse (after layout). QModelIndex mouseIndex = indexAt(mapFromGlobal(QCursor::pos())); if (mouseIndex.isValid()) { emit DCategorizedView::entered(mouseIndex); } } void ShowfotoCategorizedView::slotFileChanged(const QString& filePath) { QModelIndex index = d->filterModel->indexForUrl(QUrl::fromLocalFile(filePath)); if (index.isValid()) { update(index); } } -void ShowfotoCategorizedView::indexActivated(const QModelIndex& index, Qt::KeyboardModifiers modifiers) +void ShowfotoCategorizedView::indexActivated(const QModelIndex& index, QMouseEvent* const event) { ShowfotoItemInfo info = d->filterModel->showfotoItemInfo(index); if (!info.isNull()) { - activated(info, modifiers); + activated(info, event); emit showfotoItemInfoActivated(info); } } void ShowfotoCategorizedView::currentChanged(const QModelIndex& index, const QModelIndex& previous) { ItemViewCategorized::currentChanged(index, previous); emit currentChanged(d->filterModel->showfotoItemInfo(index)); } void ShowfotoCategorizedView::selectionChanged(const QItemSelection& selectedItems, const QItemSelection& deselectedItems) { ItemViewCategorized::selectionChanged(selectedItems, deselectedItems); if (!selectedItems.isEmpty()) { emit selected(d->filterModel->showfotoItemInfos(selectedItems.indexes())); } if (!deselectedItems.isEmpty()) { emit deselected(d->filterModel->showfotoItemInfos(deselectedItems.indexes())); } } -void ShowfotoCategorizedView::activated(const ShowfotoItemInfo&, Qt::KeyboardModifiers) +void ShowfotoCategorizedView::activated(const ShowfotoItemInfo&, QMouseEvent*) { // implemented in subclass } void ShowfotoCategorizedView::showContextMenuOnIndex(QContextMenuEvent* event, const QModelIndex& index) { ShowfotoItemInfo info = d->filterModel->showfotoItemInfo(index); showContextMenuOnInfo(event, info); } void ShowfotoCategorizedView::showContextMenuOnInfo(QContextMenuEvent*, const ShowfotoItemInfo&) { // implemented in subclass } void ShowfotoCategorizedView::paintEvent(QPaintEvent* e) { ItemViewCategorized::paintEvent(e); } QItemSelectionModel* ShowfotoCategorizedView::getSelectionModel() const { return selectionModel(); } AbstractItemDragDropHandler* ShowfotoCategorizedView::dragDropHandler() const { return d->model->dragDropHandler(); } } // namespace Showfoto diff --git a/core/showfoto/thumbbar/showfotocategorizedview.h b/core/showfoto/thumbbar/showfotocategorizedview.h index 9a93f822a8..887440914b 100644 --- a/core/showfoto/thumbbar/showfotocategorizedview.h +++ b/core/showfoto/thumbbar/showfotocategorizedview.h @@ -1,193 +1,193 @@ /* ============================================================ * * This file is a part of digiKam project * http://www.digikam.org * * Date : 2012-07-13 * Description : Qt categorized item view for showfoto items * * Copyright (C) 2013 by Mohamed_Anwer * * This program is free software; you can redistribute it * and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation; * either version 2, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * ============================================================ */ #ifndef SHOW_FOTO_CATEGORIZED_VIEW_H #define SHOW_FOTO_CATEGORIZED_VIEW_H // Local includes #include "itemviewcategorized.h" #include "showfotoimagemodel.h" #include "showfotofiltermodel.h" #include "showfotothumbnailmodel.h" #include "showfotoiteminfo.h" #include "itemdelegateoverlay.h" namespace ShowFoto { class ShowfotoDelegate; class ShowfotoCategorizedView : public ItemViewCategorized { public: Q_OBJECT public: explicit ShowfotoCategorizedView(QWidget* const parent = 0); ~ShowfotoCategorizedView(); void setModels(ShowfotoItemModel* model, ShowfotoSortFilterModel* filterModel); ShowfotoItemModel* showfotoItemModel() const; ShowfotoSortFilterModel* showfotoSortFilterModel() const; QItemSelectionModel* getSelectionModel() const; /// Returns any ShowfotoFilterModel in chain. May not be sourceModel() ShowfotoFilterModel* showfotoFilterModel() const; /// Returns 0 if the ShowfotoItemModel is not an ShowfotoThumbnailModel ShowfotoThumbnailModel* showfotoThumbnailModel() const; ShowfotoDelegate* delegate() const; ShowfotoItemInfo currentInfo() const; QUrl currentUrl() const; QList selectedShowfotoItemInfos() const; QList selectedShowfotoItemInfosCurrentFirst() const; QList selectedUrls() const; QList showfotoItemInfos() const; QList urls() const; /** Selects the index as current and scrolls to it */ void toIndex(const QUrl& url); /** Returns the n-th info after the given one. * Specifically, return the previous info for nth = -1 * and the next info for n = 1. * Returns a null info if either startingPoint or the nth info are * not contained in the model */ ShowfotoItemInfo nextInOrder(const ShowfotoItemInfo& startingPoint, int nth); ShowfotoItemInfo previousInfo(const ShowfotoItemInfo& info) { return nextInOrder(info, -1); } ShowfotoItemInfo nextInfo(const ShowfotoItemInfo& info) { return nextInOrder(info, 1); } /// Add and remove an overlay. It will as well be removed automatically when destroyed. /// Unless you pass a different delegate, the current delegate will be used. void addOverlay(ItemDelegateOverlay* overlay, ShowfotoDelegate* delegate = 0); void removeOverlay(ItemDelegateOverlay* overlay); //TODO: Implement This // void addSelectionOverlay(ShowfotoDelegate* delegate = 0); ThumbnailSize thumbnailSize() const; virtual void setThumbnailSize(const ThumbnailSize& size); public Q_SLOTS: void setThumbnailSize(int size); /** Scroll the view to the given item when it becomes available */ void setCurrentWhenAvailable(qlonglong ShowfotoItemId); /** Set as current item the item identified by its file url */ void setCurrentUrl(const QUrl& url); /** Set as current item the item identified by the ShowfotoItemInfo */ void setCurrentInfo(const ShowfotoItemInfo& info); /** Set selected items identified by their file urls */ void setSelectedUrls(const QList& urlList); /** Set selected items */ void setSelectedShowfotoItemInfos(const QList& infos); /** Does something to gain attention for info, but not changing current selection */ void hintAt(const ShowfotoItemInfo& info); Q_SIGNALS: void currentChanged(const ShowfotoItemInfo& info); /// Emitted when new items are selected. The parameter includes only the newly selected infos, /// there may be other already selected infos. void selected(const QList& newSelectedInfos); /// Emitted when items are deselected. There may be other selected infos left. /// This signal is not emitted when the model is reset; then only selectionCleared is emitted. void deselected(const QList& nowDeselectedInfos); /// Emitted when the given ShowfotoItemInfo is activated. Info is never null. void showfotoItemInfoActivated(const ShowfotoItemInfo& info); /// Emitted when a new model is set void modelChanged(); protected: // reimplemented from parent class QSortFilterProxyModel* filterModel() const; AbstractItemDragDropHandler* dragDropHandler() const; QModelIndex nextIndexHint(const QModelIndex& indexToAnchor, const QItemSelectionRange& removed) const; void setItemDelegate(ShowfotoDelegate* delegate); - void indexActivated(const QModelIndex& index, Qt::KeyboardModifiers modifiers); + void indexActivated(const QModelIndex& index, QMouseEvent* const event); void currentChanged(const QModelIndex& index, const QModelIndex& previous); void paintEvent(QPaintEvent* e); void selectionChanged(const QItemSelection&, const QItemSelection&); void updateGeometries(); /// Reimplement these in a subclass - virtual void activated(const ShowfotoItemInfo& info, Qt::KeyboardModifiers modifiers); + virtual void activated(const ShowfotoItemInfo& info, QMouseEvent* const event); virtual void showContextMenuOnInfo(QContextMenuEvent* event, const ShowfotoItemInfo& info); virtual void showContextMenuOnIndex(QContextMenuEvent* event, const QModelIndex& index); private Q_SLOTS: void slotFileChanged(const QString& filePath); void slotDelayedEnter(); private: void scrollToStoredItem(); private: class Private; Private* const d; }; } // namespace Showfoto #endif // SHOW_FOTO_CATEGORIZED_VIEW_H diff --git a/core/utilities/import/views/importcategorizedview.cpp b/core/utilities/import/views/importcategorizedview.cpp index b9592bb7c6..49ff0c2607 100644 --- a/core/utilities/import/views/importcategorizedview.cpp +++ b/core/utilities/import/views/importcategorizedview.cpp @@ -1,631 +1,631 @@ /* ============================================================ * * This file is a part of digiKam project * http://www.digikam.org * * Date : 2012-07-13 * Description : Qt categorized item view for camera items * * Copyright (C) 2012 by Islam Wazery * * 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 "importcategorizedview.h" // Qt includes #include // Local includes #include "digikam_debug.h" #include "camitemsortsettings.h" #include "iccsettings.h" #include "itemselectionoverlay.h" #include "importdelegate.h" #include "importtooltipfiller.h" #include "importsettings.h" #include "itemviewtooltip.h" #include "loadingcacheinterface.h" #include "thumbnailloadthread.h" namespace Digikam { class Q_DECL_HIDDEN ImportItemViewToolTip : public ItemViewToolTip { public: explicit ImportItemViewToolTip(ImportCategorizedView* const view) : ItemViewToolTip(view) { } ImportCategorizedView* view() const { return static_cast(ItemViewToolTip::view()); } protected: virtual QString tipContents() { CamItemInfo info = ImportItemModel::retrieveCamItemInfo(currentIndex()); return ImportToolTipFiller::CamItemInfoTipContents(info); } }; class Q_DECL_HIDDEN ImportCategorizedView::Private { public: explicit Private() : model(0), filterModel(0), delegate(0), showToolTip(false), scrollToItemId(0), delayedEnterTimer(0), currentMouseEvent(0) { } ImportItemModel* model; ImportSortFilterModel* filterModel; ImportDelegate* delegate; bool showToolTip; qlonglong scrollToItemId; QTimer* delayedEnterTimer; QMouseEvent* currentMouseEvent; }; ImportCategorizedView::ImportCategorizedView(QWidget* const parent) : ItemViewCategorized(parent), d(new Private) { setToolTip(new ImportItemViewToolTip(this)); LoadingCacheInterface::connectToSignalFileChanged(this, SLOT(slotFileChanged(QString))); d->delayedEnterTimer = new QTimer(this); d->delayedEnterTimer->setInterval(10); d->delayedEnterTimer->setSingleShot(true); connect(d->delayedEnterTimer, SIGNAL(timeout()), this, SLOT(slotDelayedEnter())); connect(IccSettings::instance(), SIGNAL(settingsChanged(ICCSettingsContainer,ICCSettingsContainer)), this, SLOT(slotIccSettingsChanged(ICCSettingsContainer,ICCSettingsContainer))); } ImportCategorizedView::~ImportCategorizedView() { d->delegate->removeAllOverlays(); delete d; } void ImportCategorizedView::setModels(ImportItemModel* model, ImportSortFilterModel* filterModel) { if (d->delegate) { d->delegate->setAllOverlaysActive(false); } if (d->filterModel) { disconnect(d->filterModel, SIGNAL(layoutAboutToBeChanged()), this, SLOT(layoutAboutToBeChanged())); disconnect(d->filterModel, SIGNAL(layoutChanged()), this, SLOT(layoutWasChanged())); } if (d->model) { disconnect(d->model, SIGNAL(itemInfosAdded(QList)), this, SLOT(slotCamItemInfosAdded())); } d->model = model; d->filterModel = filterModel; setModel(d->filterModel); connect(d->filterModel, SIGNAL(layoutAboutToBeChanged()), this, SLOT(layoutAboutToBeChanged())); connect(d->filterModel, SIGNAL(layoutChanged()), this, SLOT(layoutWasChanged()), Qt::QueuedConnection); connect(d->model, SIGNAL(itemInfosAdded(QList)), this, SLOT(slotCamItemInfosAdded())); emit modelChanged(); if (d->delegate) { d->delegate->setAllOverlaysActive(true); } } ImportItemModel* ImportCategorizedView::importItemModel() const { return d->model; } ImportSortFilterModel* ImportCategorizedView::importSortFilterModel() const { return d->filterModel; } ImportFilterModel* ImportCategorizedView::importFilterModel() const { return d->filterModel->importFilterModel(); } ImportThumbnailModel* ImportCategorizedView::importThumbnailModel() const { return qobject_cast(d->model); } QSortFilterProxyModel* ImportCategorizedView::filterModel() const { return d->filterModel; } ImportDelegate* ImportCategorizedView::delegate() const { return d->delegate; } void ImportCategorizedView::setItemDelegate(ImportDelegate* delegate) { ThumbnailSize oldSize = thumbnailSize(); ImportDelegate* oldDelegate = d->delegate; if (oldDelegate) { hideIndexNotification(); d->delegate->setAllOverlaysActive(false); d->delegate->setViewOnAllOverlays(0); // Note: Be precise, no wildcard disconnect! disconnect(d->delegate, SIGNAL(requestNotification(QModelIndex,QString)), this, SLOT(showIndexNotification(QModelIndex,QString))); disconnect(d->delegate, SIGNAL(hideNotification()), this, SLOT(hideIndexNotification())); } d->delegate = delegate; delegate->setThumbnailSize(oldSize); if (oldDelegate) { d->delegate->setSpacing(oldDelegate->spacing()); } ItemViewCategorized::setItemDelegate(d->delegate); setCategoryDrawer(d->delegate->categoryDrawer()); updateDelegateSizes(); d->delegate->setViewOnAllOverlays(this); d->delegate->setAllOverlaysActive(true); connect(d->delegate, SIGNAL(requestNotification(QModelIndex,QString)), this, SLOT(showIndexNotification(QModelIndex,QString))); connect(d->delegate, SIGNAL(hideNotification()), this, SLOT(hideIndexNotification())); } CamItemInfo ImportCategorizedView::currentInfo() const { return d->filterModel->camItemInfo(currentIndex()); } QUrl ImportCategorizedView::currentUrl() const { return currentInfo().url(); } QList ImportCategorizedView::selectedCamItemInfos() const { return d->filterModel->camItemInfos(selectedIndexes()); } QList ImportCategorizedView::selectedCamItemInfosCurrentFirst() const { QList indexes = selectedIndexes(); QModelIndex current = currentIndex(); QList infos; foreach(const QModelIndex& index, indexes) { CamItemInfo info = d->filterModel->camItemInfo(index); if (index == current) { infos.prepend(info); } else { infos.append(info); } } return infos; } QList ImportCategorizedView::camItemInfos() const { return d->filterModel->camItemInfosSorted(); } QList ImportCategorizedView::urls() const { QList infos = camItemInfos(); QList urls; foreach(const CamItemInfo& info, infos) { urls << info.url(); } return urls; } QList ImportCategorizedView::selectedUrls() const { QList infos = selectedCamItemInfos(); QList urls; foreach(const CamItemInfo& info, infos) { urls << info.url(); } return urls; } void ImportCategorizedView::toIndex(const QUrl& url) { ItemViewCategorized::toIndex(d->filterModel->indexForPath(url.toLocalFile())); } CamItemInfo ImportCategorizedView::nextInOrder(const CamItemInfo& startingPoint, int nth) { QModelIndex index = d->filterModel->indexForCamItemInfo(startingPoint); if (!index.isValid()) { return CamItemInfo(); } return d->filterModel->camItemInfo(d->filterModel->index(index.row() + nth, 0, QModelIndex())); } QModelIndex ImportCategorizedView::nextIndexHint(const QModelIndex& anchor, const QItemSelectionRange& removed) const { QModelIndex hint = ItemViewCategorized::nextIndexHint(anchor, removed); CamItemInfo info = d->filterModel->camItemInfo(anchor); //qCDebug(DIGIKAM_IMPORTUI_LOG) << "Having initial hint" << hint << "for" << anchor << d->model->numberOfIndexesForCamItemInfo(info); // Fixes a special case of multiple (face) entries for the same image. // If one is removed, any entry of the same image shall be preferred. if (d->model->numberOfIndexesForCamItemInfo(info) > 1) { // The hint is for a different info, but we may have a hint for the same info if (info != d->filterModel->camItemInfo(hint)) { int minDiff = d->filterModel->rowCount(); QList indexesForCamItemInfo = d->filterModel->mapListFromSource(d->model->indexesForCamItemInfo(info)); foreach(const QModelIndex& index, indexesForCamItemInfo) { if (index == anchor || !index.isValid() || removed.contains(index)) { continue; } int distance = qAbs(index.row() - anchor.row()); if (distance < minDiff) { minDiff = distance; hint = index; //qCDebug(DIGIKAM_IMPORTUI_LOG) << "Chose index" << hint << "at distance" << minDiff << "to" << anchor; } } } } return hint; } ThumbnailSize ImportCategorizedView::thumbnailSize() const { /* ImportThumbnailModel *thumbModel = importThumbnailModel(); if (thumbModel) return thumbModel->thumbnailSize(); */ if (d->delegate) { return d->delegate->thumbnailSize(); } return ThumbnailSize(); } void ImportCategorizedView::setThumbnailSize(int size) { setThumbnailSize(ThumbnailSize(size)); } void ImportCategorizedView::setThumbnailSize(const ThumbnailSize& s) { // we abuse this pair of method calls to restore scroll position // TODO check if needed layoutAboutToBeChanged(); d->delegate->setThumbnailSize(s); layoutWasChanged(); } void ImportCategorizedView::setCurrentWhenAvailable(qlonglong camItemId) { d->scrollToItemId = camItemId; } void ImportCategorizedView::setCurrentUrl(const QUrl& url) { if (url.isEmpty()) { clearSelection(); setCurrentIndex(QModelIndex()); return; } QString path = url.toLocalFile(); QModelIndex index = d->filterModel->indexForPath(path); if (!index.isValid()) { return; } clearSelection(); setCurrentIndex(index); } void ImportCategorizedView::setCurrentInfo(const CamItemInfo& info) { QModelIndex index = d->filterModel->indexForCamItemInfo(info); clearSelection(); setCurrentIndex(index); } void ImportCategorizedView::setSelectedUrls(const QList& urlList) { QItemSelection mySelection; for (QList::const_iterator it = urlList.constBegin(); it!=urlList.constEnd(); ++it) { const QString path = it->toLocalFile(); const QModelIndex index = d->filterModel->indexForPath(path); if (!index.isValid()) { qCWarning(DIGIKAM_IMPORTUI_LOG) << "no QModelIndex found for" << *it; } else { // TODO: is there a better way? mySelection.select(index, index); } } clearSelection(); selectionModel()->select(mySelection, QItemSelectionModel::Select); } void ImportCategorizedView::setSelectedCamItemInfos(const QList& infos) { QItemSelection mySelection; foreach(const CamItemInfo& info, infos) { QModelIndex index = d->filterModel->indexForCamItemInfo(info); mySelection.select(index, index); } selectionModel()->select(mySelection, QItemSelectionModel::ClearAndSelect); } void ImportCategorizedView::hintAt(const CamItemInfo& info) { if (info.isNull()) { return; } QModelIndex index = d->filterModel->indexForCamItemInfo(info); if (!index.isValid()) { return; } selectionModel()->setCurrentIndex(index, QItemSelectionModel::NoUpdate); scrollTo(index); } void ImportCategorizedView::addOverlay(ItemDelegateOverlay* overlay, ImportDelegate* delegate) { if (!delegate) { delegate = d->delegate; } delegate->installOverlay(overlay); if (delegate == d->delegate) { overlay->setView(this); overlay->setActive(true); } } void ImportCategorizedView::removeOverlay(ItemDelegateOverlay* overlay) { ImportDelegate* delegate = dynamic_cast(overlay->delegate()); if (delegate) { delegate->removeOverlay(overlay); } overlay->setView(0); } void ImportCategorizedView::updateGeometries() { ItemViewCategorized::updateGeometries(); d->delayedEnterTimer->start(); } void ImportCategorizedView::slotDelayedEnter() { // re-emit entered() for index under mouse (after layout). QModelIndex mouseIndex = indexAt(mapFromGlobal(QCursor::pos())); if (mouseIndex.isValid()) { emit DCategorizedView::entered(mouseIndex); } } void ImportCategorizedView::addSelectionOverlay(ImportDelegate* delegate) { addOverlay(new ItemSelectionOverlay(this), delegate); } void ImportCategorizedView::scrollToStoredItem() { if (d->scrollToItemId) { if (d->model->hasImage(d->scrollToItemId)) { QModelIndex index = d->filterModel->indexForCamItemId(d->scrollToItemId); setCurrentIndex(index); scrollToRelaxed(index, QAbstractItemView::PositionAtCenter); d->scrollToItemId = 0; } } } void ImportCategorizedView::slotCamItemInfosAdded() { if (d->scrollToItemId) { scrollToStoredItem(); } } void ImportCategorizedView::slotFileChanged(const QString& filePath) { QModelIndex index = d->filterModel->indexForPath(filePath); if (index.isValid()) { update(index); } } -void ImportCategorizedView::indexActivated(const QModelIndex& index, Qt::KeyboardModifiers modifiers) +void ImportCategorizedView::indexActivated(const QModelIndex& index, QMouseEvent* const event) { CamItemInfo info = d->filterModel->camItemInfo(index); if (!info.isNull()) { - activated(info, modifiers); + activated(info, event); emit camItemInfoActivated(info); } } void ImportCategorizedView::currentChanged(const QModelIndex& index, const QModelIndex& previous) { ItemViewCategorized::currentChanged(index, previous); emit currentChanged(d->filterModel->camItemInfo(index)); } void ImportCategorizedView::selectionChanged(const QItemSelection& selectedItems, const QItemSelection& deselectedItems) { ItemViewCategorized::selectionChanged(selectedItems, deselectedItems); if (!selectedItems.isEmpty()) { emit selected(d->filterModel->camItemInfos(selectedItems.indexes())); } if (!deselectedItems.isEmpty()) { emit deselected(d->filterModel->camItemInfos(deselectedItems.indexes())); } } -void ImportCategorizedView::activated(const CamItemInfo&, Qt::KeyboardModifiers) +void ImportCategorizedView::activated(const CamItemInfo&, QMouseEvent*) { // implemented in subclass } void ImportCategorizedView::showContextMenuOnIndex(QContextMenuEvent* event, const QModelIndex& index) { CamItemInfo info = d->filterModel->camItemInfo(index); showContextMenuOnInfo(event, info); } void ImportCategorizedView::showContextMenuOnInfo(QContextMenuEvent*, const CamItemInfo&) { // implemented in subclass } void ImportCategorizedView::paintEvent(QPaintEvent* e) { ItemViewCategorized::paintEvent(e); } QItemSelectionModel* ImportCategorizedView::getSelectionModel() const { return selectionModel(); } AbstractItemDragDropHandler* ImportCategorizedView::dragDropHandler() const { return d->model->dragDropHandler(); } void ImportCategorizedView::slotIccSettingsChanged(const ICCSettingsContainer&, const ICCSettingsContainer&) { viewport()->update(); } } // namespace Digikam diff --git a/core/utilities/import/views/importcategorizedview.h b/core/utilities/import/views/importcategorizedview.h index f6d46b0c16..1c30c24fda 100644 --- a/core/utilities/import/views/importcategorizedview.h +++ b/core/utilities/import/views/importcategorizedview.h @@ -1,188 +1,188 @@ /* ============================================================ * * This file is a part of digiKam project * http://www.digikam.org * * Date : 2012-07-13 * Description : Qt categorized item view for camera items * * Copyright (C) 2012 by Islam Wazery * * This program is free software; you can redistribute it * and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation; * either version 2, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * ============================================================ */ #ifndef DIGIKAM_IMPORT_CATEGORIZED_VIEW_H #define DIGIKAM_IMPORT_CATEGORIZED_VIEW_H // Local includes #include "itemviewcategorized.h" #include "importimagemodel.h" #include "importfiltermodel.h" #include "importthumbnailmodel.h" #include "itemdelegateoverlay.h" #include "camiteminfo.h" #include "digikam_export.h" namespace Digikam { class ImportDelegate; class ICCSettingsContainer; class DIGIKAM_EXPORT ImportCategorizedView : public ItemViewCategorized { Q_OBJECT public: explicit ImportCategorizedView(QWidget* const parent = 0); ~ImportCategorizedView(); void setModels(ImportItemModel* model, ImportSortFilterModel* filterModel); ImportItemModel* importItemModel() const; ImportSortFilterModel* importSortFilterModel() const; QItemSelectionModel* getSelectionModel() const; /// Returns any ImportFilterModel in chain. May not be sourceModel() ImportFilterModel* importFilterModel() const; /// Returns 0 if the ImportItemModel is not an ImportThumbnailModel ImportThumbnailModel* importThumbnailModel() const; ImportDelegate* delegate() const; CamItemInfo currentInfo() const; QUrl currentUrl() const; QList selectedCamItemInfos() const; QList selectedCamItemInfosCurrentFirst() const; QList selectedUrls() const; QList camItemInfos() const; QList urls() const; /** Selects the index as current and scrolls to it */ void toIndex(const QUrl& url); /** Returns the n-th info after the given one. * Specifically, return the previous info for nth = -1 * and the next info for n = 1. * Returns a null info if either startingPoint or the nth info are * not contained in the model */ CamItemInfo nextInOrder(const CamItemInfo& startingPoint, int nth); CamItemInfo previousInfo(const CamItemInfo& info) { return nextInOrder(info, -1); } CamItemInfo nextInfo(const CamItemInfo& info) { return nextInOrder(info, 1); } /// Add and remove an overlay. It will as well be removed automatically when destroyed. /// Unless you pass a different delegate, the current delegate will be used. void addOverlay(ItemDelegateOverlay* overlay, ImportDelegate* delegate = 0); void removeOverlay(ItemDelegateOverlay* overlay); void addSelectionOverlay(ImportDelegate* delegate = 0); ThumbnailSize thumbnailSize() const; virtual void setThumbnailSize(const ThumbnailSize& size); public Q_SLOTS: void setThumbnailSize(int size); /** Scroll the view to the given item when it becomes available */ void setCurrentWhenAvailable(qlonglong camItemId); /** Set as current item the item identified by its file url */ void setCurrentUrl(const QUrl& url); /** Set as current item the item identified by the CamItemInfo */ void setCurrentInfo(const CamItemInfo& info); /** Set selected items identified by their file urls */ void setSelectedUrls(const QList& urlList); /** Set selected items */ void setSelectedCamItemInfos(const QList& infos); /** Does something to gain attention for info, but not changing current selection */ void hintAt(const CamItemInfo& info); Q_SIGNALS: void currentChanged(const CamItemInfo& info); /// Emitted when new items are selected. The parameter includes only the newly selected infos, /// there may be other already selected infos. void selected(const QList& newSelectedInfos); /// Emitted when items are deselected. There may be other selected infos left. /// This signal is not emitted when the model is reset; then only selectionCleared is emitted. void deselected(const QList& nowDeselectedInfos); /// Emitted when the given CamItemInfo is activated. Info is never null. void camItemInfoActivated(const CamItemInfo& info); /// Emitted when a new model is set void modelChanged(); protected Q_SLOTS: void slotCamItemInfosAdded(); protected: // reimplemented from parent class QSortFilterProxyModel* filterModel() const; AbstractItemDragDropHandler* dragDropHandler() const; QModelIndex nextIndexHint(const QModelIndex& indexToAnchor, const QItemSelectionRange& removed) const; void setItemDelegate(ImportDelegate* delegate); - void indexActivated(const QModelIndex& index, Qt::KeyboardModifiers modifiers); + void indexActivated(const QModelIndex& index, QMouseEvent* const event); void currentChanged(const QModelIndex& index, const QModelIndex& previous); void paintEvent(QPaintEvent* e); void selectionChanged(const QItemSelection&, const QItemSelection&); void updateGeometries(); /// Reimplement these in a subclass - virtual void activated(const CamItemInfo& info, Qt::KeyboardModifiers modifiers); + virtual void activated(const CamItemInfo& info, QMouseEvent* const event); virtual void showContextMenuOnInfo(QContextMenuEvent* event, const CamItemInfo& info); virtual void showContextMenuOnIndex(QContextMenuEvent* event, const QModelIndex& index); private Q_SLOTS: void slotFileChanged(const QString& filePath); void slotDelayedEnter(); void slotIccSettingsChanged(const ICCSettingsContainer&, const ICCSettingsContainer&); private: void scrollToStoredItem(); private: class Private; Private* const d; }; } // namespace Digikam #endif // DIGIKAM_IMPORT_CATEGORIZED_VIEW_H diff --git a/core/utilities/import/views/importiconview.cpp b/core/utilities/import/views/importiconview.cpp index 3b82c3f5ac..4c88e75805 100644 --- a/core/utilities/import/views/importiconview.cpp +++ b/core/utilities/import/views/importiconview.cpp @@ -1,484 +1,484 @@ /* ============================================================ * * This file is a part of digiKam project * http://www.digikam.org * * Date : 2012-22-07 * Description : Icon view for import tool items * * Copyright (C) 2012 by Islam Wazery * Copyright (C) 2012-2018 by Gilles Caulier * * This program is free software you can redistribute it * and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation * either version 2, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * ============================================================ */ #include "importiconview.h" #include "importiconview_p.h" // Qt includes #include #include #include // Local includes #include "importcategorizedview.h" #include "importoverlays.h" #include "importsettings.h" #include "camitemsortsettings.h" #include "fileactionmngr.h" #include "importdelegate.h" #include "advancedrenamedialog.h" #include "advancedrenameprocessdialog.h" #include "itemviewutilities.h" #include "importcontextmenu.h" #include "importdragdrop.h" namespace Digikam { ImportIconView::ImportIconView(QWidget* const parent) : ImportCategorizedView(parent), d(new Private(this)) { ImportThumbnailModel* const model = new ImportThumbnailModel(this); ImportFilterModel* const filterModel = new ImportFilterModel(this); filterModel->setSourceImportModel(model); filterModel->sort(0); // an initial sorting is necessary setModels(model, filterModel); d->normalDelegate = new ImportNormalDelegate(this); setItemDelegate(d->normalDelegate); setSpacing(10); ImportSettings* const settings = ImportSettings::instance(); setThumbnailSize(ThumbnailSize(settings->getDefaultIconSize())); importItemModel()->setDragDropHandler(new ImportDragDropHandler(importItemModel())); setDragEnabled(true); setAcceptDrops(true); setDropIndicatorShown(false); setToolTipEnabled(settings->showToolTipsIsValid()); // selection overlay addSelectionOverlay(d->normalDelegate); //TODO: addSelectionOverlay(d->faceDelegate); // rotation overlays d->rotateLeftOverlay = ImportRotateOverlay::left(this); d->rotateRightOverlay = ImportRotateOverlay::right(this); addOverlay(new ImportDownloadOverlay(this)); addOverlay(new ImportLockOverlay(this)); addOverlay(new ImportCoordinatesOverlay(this)); d->updateOverlays(); // rating overlay ImportRatingOverlay* const ratingOverlay = new ImportRatingOverlay(this); addOverlay(ratingOverlay); //TODO: GroupIndicatorOverlay* groupOverlay = new GroupIndicatorOverlay(this); //TODO: addOverlay(groupOverlay); connect(ratingOverlay, SIGNAL(ratingEdited(QList,int)), this, SLOT(assignRating(QList,int))); //TODO: connect(groupOverlay, SIGNAL(toggleGroupOpen(QModelIndex)), //this, SLOT(groupIndicatorClicked(QModelIndex))); //TODO: connect(groupOverlay, SIGNAL(showButtonContextMenu(QModelIndex,QContextMenuEvent*)), //this, SLOT(showGroupContextMenu(QModelIndex,QContextMenuEvent*))); //TODO: connect(importItemModel()->dragDropHandler(), SIGNAL(assignTags(QList,QList)), //FileActionMngr::instance(), SLOT(assignTags(QList,QList))); //TODO: connect(importItemModel()->dragDropHandler(), SIGNAL(addToGroup(CamItemInfo,QList)), //FileActionMngr::instance(), SLOT(addToGroup(CamItemInfo,QList))); connect(settings, SIGNAL(setupChanged()), this, SLOT(slotSetupChanged())); slotSetupChanged(); } ImportIconView::~ImportIconView() { delete d; } ItemViewUtilities* ImportIconView::utilities() const { return d->utilities; } void ImportIconView::setThumbnailSize(const ThumbnailSize& size) { ImportCategorizedView::setThumbnailSize(size); } int ImportIconView::fitToWidthIcons() { return delegate()->calculatethumbSizeToFit(viewport()->size().width()); } CamItemInfo ImportIconView::camItemInfo(const QString& folder, const QString& file) { QUrl url = QUrl::fromLocalFile(folder); url = url.adjusted(QUrl::StripTrailingSlash); url.setPath(url.path() + QLatin1Char('/') + file); QModelIndex indexForCamItemInfo = importFilterModel()->indexForPath(url.toLocalFile()); if (indexForCamItemInfo.isValid()) { return importFilterModel()->camItemInfo(indexForCamItemInfo); } return CamItemInfo(); } CamItemInfo& ImportIconView::camItemInfoRef(const QString& folder, const QString& file) { QUrl url = QUrl::fromLocalFile(folder); url = url.adjusted(QUrl::StripTrailingSlash); url.setPath(url.path() + QLatin1Char('/') + file); QModelIndex indexForCamItemInfo = importFilterModel()->indexForPath(url.toLocalFile()); QModelIndex mappedIndex = importFilterModel()->mapToSource(indexForCamItemInfo); return importItemModel()->camItemInfoRef(mappedIndex); } void ImportIconView::slotSetupChanged() { setToolTipEnabled(ImportSettings::instance()->showToolTipsIsValid()); setFont(ImportSettings::instance()->getIconViewFont()); d->updateOverlays(); ImportCategorizedView::slotSetupChanged(); } void ImportIconView::rename() { QList urls = selectedUrls(); NewNamesList newNamesList; QPointer dlg = new AdvancedRenameDialog(this); dlg->slotAddImages(urls); if (dlg->exec() == QDialog::Accepted) { newNamesList = dlg->newNames(); } delete dlg; if (!newNamesList.isEmpty()) { QPointer dlg = new AdvancedRenameProcessDialog(newNamesList); dlg->exec(); delete dlg; } } void ImportIconView::deleteSelected(bool /*permanently*/) { CamItemInfoList camItemInfoList = selectedCamItemInfos(); //FIXME: This way of deletion may not working with camera items. /* if (d->utilities->deleteImages(camItemInfoList, permanently)) { awayFromSelection(); } */ } void ImportIconView::deleteSelectedDirectly(bool /*permanently*/) { CamItemInfoList camItemInfoList = selectedCamItemInfos(); //FIXME: This way of deletion may not working with camera items. //d->utilities->deleteImagesDirectly(camItemInfoList, permanently); awayFromSelection(); } void ImportIconView::createGroupFromSelection() { //TODO: Implement grouping in import tool. /* QList selectedInfos = selectedCamItemInfosCurrentFirst(); CamItemInfo groupLeader = selectedInfos.takeFirst(); FileActionMngr::instance()->addToGroup(groupLeader, selectedInfos); */ } void ImportIconView::createGroupByTimeFromSelection() { //TODO: Implement grouping in import tool. /* QList selectedInfos = selectedCamItemInfosCurrentFirst(); while (selectedInfos.size() > 0) { QList group; CamItemInfo groupLeader = selectedInfos.takeFirst(); QDateTime dateTime = groupLeader.dateTime(); while (selectedInfos.size() > 0 && abs(dateTime.secsTo(selectedInfos.first().dateTime())) < 2) { group.push_back(selectedInfos.takeFirst()); } FileActionMngr::instance()->addToGroup(groupLeader, group); } */ } void ImportIconView::ungroupSelected() { //TODO: Implement grouping in import tool. //FileActionMngr::instance()->ungroup(selectedCamItemInfos()); } void ImportIconView::removeSelectedFromGroup() { //TODO: Implement grouping in import tool. //FileActionMngr::instance()->removeFromGroup(selectedCamItemInfos()); } void ImportIconView::slotRotateLeft(const QList& /*indexes*/) { /* QList imageInfos; foreach(const QModelIndex& index, indexes) { ItemInfo imageInfo(importFilterModel()->camItemInfo(index).url()); imageInfos << imageInfo; } FileActionMngr::instance()->transform(imageInfos, MetaEngineRotation::Rotate270); */ } void ImportIconView::slotRotateRight(const QList& /*indexes*/) { /* QList imageInfos; foreach(const QModelIndex& index, indexes) { ItemInfo imageInfo(importFilterModel()->camItemInfo(index).url()); imageInfos << imageInfo; } FileActionMngr::instance()->transform(imageInfos, MetaEngineRotation::Rotate90); */ } -void ImportIconView::activated(const CamItemInfo& info, Qt::KeyboardModifiers) +void ImportIconView::activated(const CamItemInfo& info, QMouseEvent*) { if (info.isNull()) { return; } if (ImportSettings::instance()->getItemLeftClickAction() == ImportSettings::ShowPreview) { emit previewRequested(info, false); } else { //TODO: openFile(info); } } void ImportIconView::showContextMenuOnInfo(QContextMenuEvent* event, const CamItemInfo& /*info*/) { QList selectedInfos = selectedCamItemInfosCurrentFirst(); QList selectedItemIDs; foreach(const CamItemInfo& info, selectedInfos) { selectedItemIDs << info.id; } // -------------------------------------------------------- QMenu popmenu(this); ImportContextMenuHelper cmhelper(&popmenu); cmhelper.addAction(QLatin1String("importui_fullscreen")); cmhelper.addAction(QLatin1String("options_show_menubar")); cmhelper.addAction(QLatin1String("import_zoomfit2window")); cmhelper.addSeparator(); // -------------------------------------------------------- cmhelper.addAction(QLatin1String("importui_imagedownload")); cmhelper.addAction(QLatin1String("importui_imagemarkasdownloaded")); cmhelper.addAction(QLatin1String("importui_imagelock")); cmhelper.addAction(QLatin1String("importui_delete")); cmhelper.addSeparator(); cmhelper.addAction(QLatin1String("importui_item_view")); cmhelper.addServicesMenu(selectedUrls()); //TODO: cmhelper.addRotateMenu(selectedItemIDs); cmhelper.addSeparator(); // -------------------------------------------------------- cmhelper.addAction(QLatin1String("importui_selectall")); cmhelper.addAction(QLatin1String("importui_selectnone")); cmhelper.addAction(QLatin1String("importui_selectinvert")); cmhelper.addSeparator(); // -------------------------------------------------------- //cmhelper.addAssignTagsMenu(selectedItemIDs); //cmhelper.addRemoveTagsMenu(selectedItemIDs); //cmhelper.addSeparator(); // -------------------------------------------------------- cmhelper.addLabelsAction(); //if (!d->faceMode) //{ // cmhelper.addGroupMenu(selectedItemIDs); //} // special action handling -------------------------------- //connect(&cmhelper, SIGNAL(signalAssignTag(int)), // this, SLOT(assignTagToSelected(int))); //TODO: Implement tag view for import tool. //connect(&cmhelper, SIGNAL(signalPopupTagsView()), // this, SIGNAL(signalPopupTagsView())); //connect(&cmhelper, SIGNAL(signalRemoveTag(int)), // this, SLOT(removeTagFromSelected(int))); //connect(&cmhelper, SIGNAL(signalGotoTag(int)), //this, SIGNAL(gotoTagAndImageRequested(int))); connect(&cmhelper, SIGNAL(signalAssignPickLabel(int)), this, SLOT(assignPickLabelToSelected(int))); connect(&cmhelper, SIGNAL(signalAssignColorLabel(int)), this, SLOT(assignColorLabelToSelected(int))); connect(&cmhelper, SIGNAL(signalAssignRating(int)), this, SLOT(assignRatingToSelected(int))); //connect(&cmhelper, SIGNAL(signalAddToExistingQueue(int)), //this, SLOT(insertSelectedToExistingQueue(int))); //FIXME: connect(&cmhelper, SIGNAL(signalCreateGroup()), //this, SLOT(createGroupFromSelection())); //connect(&cmhelper, SIGNAL(signalUngroup()), //this, SLOT(ungroupSelected())); //connect(&cmhelper, SIGNAL(signalRemoveFromGroup()), //this, SLOT(removeSelectedFromGroup())); // -------------------------------------------------------- cmhelper.exec(event->globalPos()); } void ImportIconView::showContextMenu(QContextMenuEvent* event) { QMenu popmenu(this); ImportContextMenuHelper cmhelper(&popmenu); cmhelper.addAction(QLatin1String("importui_fullscreen")); cmhelper.addAction(QLatin1String("options_show_menubar")); cmhelper.addSeparator(); cmhelper.addAction(QLatin1String("importui_close")); // -------------------------------------------------------- cmhelper.exec(event->globalPos()); } void ImportIconView::assignTagToSelected(int tagID) { CamItemInfoList infos = selectedCamItemInfos(); foreach(const CamItemInfo& info, infos) { importItemModel()->camItemInfoRef(importItemModel()->indexForCamItemInfo(info)).tagIds.append(tagID); } } void ImportIconView::removeTagFromSelected(int tagID) { CamItemInfoList infos = selectedCamItemInfos(); foreach(const CamItemInfo& info, infos) { importItemModel()->camItemInfoRef(importItemModel()->indexForCamItemInfo(info)).tagIds.removeAll(tagID); } } void ImportIconView::assignPickLabel(const QModelIndex& index, int pickId) { importItemModel()->camItemInfoRef(index).pickLabel = pickId; } void ImportIconView::assignPickLabelToSelected(int pickId) { CamItemInfoList infos = selectedCamItemInfos(); foreach(const CamItemInfo& info, infos) { importItemModel()->camItemInfoRef(importItemModel()->indexForCamItemInfo(info)).pickLabel = pickId; } } void ImportIconView::assignColorLabel(const QModelIndex& index, int colorId) { importItemModel()->camItemInfoRef(index).colorLabel = colorId; } void ImportIconView::assignColorLabelToSelected(int colorId) { CamItemInfoList infos = selectedCamItemInfos(); foreach(const CamItemInfo& info, infos) { importItemModel()->camItemInfoRef(importItemModel()->indexForCamItemInfo(info)).colorLabel = colorId; } } void ImportIconView::assignRating(const QList& indexes, int rating) { foreach(const QModelIndex& index, indexes) { if (index.isValid()) { importItemModel()->camItemInfoRef(index).rating = rating; } } } void ImportIconView::assignRatingToSelected(int rating) { CamItemInfoList infos = selectedCamItemInfos(); foreach(const CamItemInfo& info, infos) { importItemModel()->camItemInfoRef(importItemModel()->indexForCamItemInfo(info)).rating = rating; } } } // namespace Digikam diff --git a/core/utilities/import/views/importiconview.h b/core/utilities/import/views/importiconview.h index 17dada10be..70012a87a6 100644 --- a/core/utilities/import/views/importiconview.h +++ b/core/utilities/import/views/importiconview.h @@ -1,104 +1,104 @@ /* ============================================================ * * This file is a part of digiKam project * http://www.digikam.org * * Date : 2012-22-07 * Description : Icon view for import tool items * * Copyright (C) 2012 by Islam Wazery * Copyright (C) 2012-2018 by Gilles Caulier * * This program is free software you can redistribute it * and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation * either version 2, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * ============================================================ */ #ifndef DIGIKAM_IMPORT_ICON_VIEW_H #define DIGIKAM_IMPORT_ICON_VIEW_H // Local includes #include "importcategorizedview.h" namespace Digikam { class ItemViewUtilities; class ImportIconView : public ImportCategorizedView { Q_OBJECT public: explicit ImportIconView(QWidget* const parent = 0); ~ImportIconView(); ItemViewUtilities* utilities() const; int fitToWidthIcons(); CamItemInfo camItemInfo(const QString& folder, const QString& file); CamItemInfo& camItemInfoRef(const QString& folder, const QString& file); virtual void setThumbnailSize(const ThumbnailSize& size); public Q_SLOTS: void deleteSelected(bool permanently = false); void deleteSelectedDirectly(bool permanently = false); void createGroupFromSelection(); void createGroupByTimeFromSelection(); void ungroupSelected(); void removeSelectedFromGroup(); void rename(); void assignTagToSelected(int tagID); void removeTagFromSelected(int tagID); void assignPickLabel(const QModelIndex& index, int pickId); void assignPickLabelToSelected(int pickId); void assignColorLabel(const QModelIndex& index, int colorId); void assignColorLabelToSelected(int colorId); void assignRating(const QList& index, int rating); void assignRatingToSelected(int rating); Q_SIGNALS: void previewRequested(const CamItemInfo& info, bool downloadPreview); //void signalPopupTagsView(); private Q_SLOTS: void slotRotateLeft(const QList&); void slotRotateRight(const QList&); //void slotInitProgressIndicator(); protected: - virtual void activated(const CamItemInfo& info, Qt::KeyboardModifiers modifiers); + virtual void activated(const CamItemInfo& info, QMouseEvent* const event); virtual void showContextMenuOnInfo(QContextMenuEvent* event, const CamItemInfo& info); virtual void showContextMenu(QContextMenuEvent* event); virtual void slotSetupChanged(); private: class Private; Private* const d; }; } // namespace Digikam #endif // DIGIKAM_IMPORT_ICON_VIEW_H