diff --git a/NEWS b/NEWS index 8c5f9de54d..1037e9aaee 100644 --- a/NEWS +++ b/NEWS @@ -1,682 +1,683 @@ digiKam 6.0.0 - Release date: 2019-02-10 ***************************************************************************************************** NEW FEATURES: ImageEditor : Add Web services Import and Export tools. Showfoto : Add Web services Import and Export tools. LightTable : Add Web services Import and Export tools. Database : Similarity database has been moved to a dedicated file to not bloat core database with computed finger-prints. This will speed-up query in core database in case of Similarity feature is used. Database : New video metadata parser based on ffmpeg to populate database. Search : Add video support to find files based on properties registered on database. General : Add new Exiv2 0.27 support. General : Add new Lensfun 0.4 support. General : Use DrMinGW has crash handler under Windows. General : Port all export/import tool to OAuth2 authentification. General : New tool to adjust quickly time and date information from items. Tool will be available in AlbumView, ImageEditor, LightTable, and Showfoto. General : New function to group images by timelapse / burst General : New tool to export to Pinterest social network. General : New tool to export to OneDrive webservice. General : New tool to export to Box webservice. General : New tool to export to Ipfs webservice. General : All bundles use last Lenfun 0.3.95 to process automatic lens correction. General : Add QWebEngine support. General : Fix all Krazy static analyzer reports. General : Update internal Libraw to last 0.19.2. New camera supported: * Apple: Phone 8, iPhone 8 plus, iPhone X * BlackMagic: URSA Mini 4k, URSA Mini 4.6k, URSA Mini Pro 4.6k * Canon: PowerShot A410, A540, D10, ELPH 130 IS, ELPH 160 IS, SD750, SX100 IS,SX130 IS, SX160 IS, SX510 HS, SX10 IS, IXUS 900Ti PowerShot G1 X Mark III, G9 X Mark II, EOS 6D Mark II, EOS 77D, EOS 200D, EOS 800D, EOS M6, EOS M100 * Casio: EX-ZR4100/5100 * DJI: Phantom4 Pro/Pro+, Zenmuse X5, Zenmuse X5R * FujiFilm: S6500fd, GFX 50S, X100f, X-A3, X-A5, X-A10, X-A20, X-E3, X-H1, X-T20 * Hasselblad: H6D-100c, A6D-100c * Huawei: P9 (EVA-L09/AL00), Honor6a, Honor9, Mate10 (BLA-L29) * Leica: CL, M10, TL2 * LG: V20 (F800K), VS995 * Nikon: D850, D5600, D7500, Coolpix B700 * Olympus: E-PL9, E-M10 Mark III, TG-5 * OnePlus: A3303, A5000 * Panasonic: DMC-FZ45, DMC-FZ72, DC-FZ80/82, DC-G9 (std. res mode only), DC-GF10/GF90, DC-GH5, DC-GX9, DC-GX800/850/GF9, DMC-LX1, DC-ZS70 (DC-TZ90/91/92, DC-T93), DC-TZ100/101/ZS100, DC-TZ200/ZS200 * PARROT: Bebop 2, Bebop Drone * Pentax: KP * PhaseOne: IQ3 100MP Trichromatic * Samsung: Galaxy Nexus, Galaxy S3, S6 (SM-G920F), S7, S7 Edge, S8 (SM-G950U) * Sony: A7R III, A9, DSC-RX0, DSC-RX10IV * Yi: M1 * YUNEEC: CGO3, CGO3P * Xiaoyi: YIAC3 (YI 4k) Collection : Add tool button to update an existing collection to the new drive or path. Tags : Add possibility to merge tags by drag & drop. IconView : Add capability to re-organize the contents manually. IconView : Add capability to separate the contents by month. HTML Gallery : New Vanilla theme to emulate export to HTML from Adobe LightRoom. HTML Gallery : New Blue Frame theme. HTML Gallery : New Kiosk-Mode theme. ***************************************************************************************************** BUGFIXES: 001 ==> 172650 - No export tools available. 002 ==> 149591 - The export menu is blank. 003 ==> 300424 - Export tools not detected. 004 ==> 327743 - MediaWiki export not displayed. 005 ==> 348146 - Export tools configuration module. 006 ==> 243275 - Crashing when calling configuration. 007 ==> 257134 - Crashes when entering its settings. 008 ==> 230666 - Crash during start. 009 ==> 306698 - Crashes after update to KDE. 010 ==> 207640 - Crashes immediately at startup, sometimes at closing. 011 ==> 245628 - Crash when enabling/disabling Facebook import/export. 012 ==> 097317 - sigsegv [New Thread 1100241184 (LWP 5665)]. 013 ==> 245776 - Crashes when selecting Settings. 014 ==> 245775 - Crashes even without export tools installed. 015 ==> 254283 - Crash as soon as i click settings. 016 ==> 255733 - crash when reopening configuration dialog. 017 ==> 262050 - Crash after new install and scan to mysql database. 018 ==> 263871 - Crashed after searching for duplicates. 019 ==> 268242 - Crashes after I clicked on 'Settings'. 020 ==> 276882 - Add export tool buttons to toolbar. 021 ==> 284801 - Export to picasaweb crashes. 022 ==> 277669 - Crash after files moved. 023 ==> 281250 - Crash after disabling Export tools. 024 ==> 202637 - Crash trying to start export tool. 025 ==> 306693 - after update to KDE host application crashes on startup. 026 ==> 282781 - Crashes when reopening configuration dialog after disabling export tools. 027 ==> 285499 - Crash when settings window opened. 028 ==> 297629 - Crashed when importing from Nikon P7000. 029 ==> 303338 - Crashes when clicking "send to" button. 030 ==> 307619 - Refuses to load Export tools. 031 ==> 311812 - Export tools not loading, SO version not defined. 032 ==> 313186 - Crashes on attempt to use the "Send to" menu. 033 ==> 313356 - Crashed when clicking the "send to" button. 034 ==> 313577 - Crashes when pressing the "send to" button. 035 ==> 313890 - Crash when clicking "Send to...". 036 ==> 315033 - Crashes on pressing Send To... button. 037 ==> 315914 - The facebook tool crashes everytime on initialization. 038 ==> 326556 - Export tools are not loaded when starting host application for second time. 039 ==> 095175 - crash on loading, signal 11 SIGSEGV. 040 ==> 175844 - Crashes at startup loading Export tools. 041 ==> 306511 - Crash during start. 042 ==> 234021 - Crash on loading. 043 ==> 219772 - Opening the application causes crash. 044 ==> 294173 - Crash after Image resize start. 045 ==> 306881 - Crashed when attempting to open Export tools. 046 ==> 306495 - Crash changing settings linux ubuntu lucid. 047 ==> 306497 - Crash after changing settings segmentation fault possible 2nd report? 048 ==> 185470 - "Import from facebook" is listed twice in import menu. 049 ==> 334045 - MediaWiki option not available in Export menu when plugin activated. 050 ==> 142112 - Can't save on my webspace with ShowFoto. 051 ==> 167417 - Showfoto cannot save files of CIFS mount. 052 ==> 125164 - Flickr export tool should respect host application selection. 053 ==> 238927 - Host application quits when uploading to Flickr. 054 ==> 326740 - Selection of tools is set to default after each update. 055 ==> 233063 - Add progress indicator when moving or copy files [patch]. 056 ==> 361829 - Rotated MP4 video with "Orientation" flag are not played back in the correct rotation angle. 057 ==> 329854 - digiKam doesn't rotate video thumbnail. 058 ==> 376661 - When importing ~200,000 video files Digikam crashes in about 2-5 seconds of starting. 059 ==> 377072 - Cannot read video metadata. 060 ==> 374453 - Extract right video date from MP4 files metadata. 061 ==> 377177 - Geolocation / GPS information doesn't appear in video metadata. 062 ==> 383588 - Imported video files have time shifted exactly 2 hours later. 063 ==> 380847 - MP4 video not importing with correct date. 064 ==> 373682 - geolocalisation filter does not take care of the videos geolocalisation tags. 065 ==> 340925 - digiKam crash when start it. 066 ==> 331506 - digiKam crashes on startup. 067 ==> 335816 - Crash when trying to add a big collection. 068 ==> 353295 - digiKam repeatedly crashes while importing pictures. 069 ==> 341433 - Crash when opening digiKam application. 070 ==> 375562 - digiKam crashes while scanning images into sqlite database. 071 ==> 334782 - Crash while doing nothing. 072 ==> 362672 - Crash on start of digiKam. 073 ==> 341023 - Crash after startup during check for updated images. 074 ==> 386891 - Crashed while adding pictures. 075 ==> 342666 - digiKam crashes during find new items. 076 ==> 341274 - digiKam crash on startup. 077 ==> 343708 - Crash when scanning album. 078 ==> 332721 - Crash when reading a certain MP4 video file. 079 ==> 343736 - Crashes when rebuilding thumbnails from database. 080 ==> 346356 - digiKam crashes when adding 90.000 pictures to library. 081 ==> 343714 - digiKam crash when scanning for new items. 082 ==> 341091 - digiKam crashes when updating the MySQL database of a a hudge photo collection. 083 ==> 340879 - digiKam crashes after getting unexpected but reasonable output from libexiv2. 084 ==> 342712 - Crash on collection scanning. 085 ==> 356704 - digiKam still crashes while scanning a new photo directory and subdirs. 086 ==> 339269 - Segfault when opening a folder that contains unknown file types (mov, avi, xcf). 087 ==> 364635 - digiKam crashes on startup. 088 ==> 357356 - digiKam crash on startup while scanning photos. 089 ==> 341554 - digiKam crashed by Data-Import from NFS. 090 ==> 345457 - digiKam crashes at "loading tools". 091 ==> 349635 - Crash of digiKam - moving album. 092 ==> 342604 - digiKam crash. 093 ==> 331450 - Signal 8 on album opening. 094 ==> 342030 - digiKam crashes when checking an AVI video file using exiv2. 095 ==> 352777 - Crash during scan. 096 ==> 352944 - digiKam crashes on start. 097 ==> 343643 - digiKam crashes while perform initial scanning of custom photo folder. 098 ==> 342000 - digiKam crash when opening folder with Videos (Album or SD Card import). 099 ==> 353447 - digiKam crashes when scanning files. 100 ==> 346807 - Crashes on startup. 101 ==> 364639 - digiKam crashed while opening database. 102 ==> 341504 - Crash while using application. 103 ==> 367691 - Searching for pictures crashes at 30% every time. 104 ==> 334604 - Crash after changing disk partions. 105 ==> 351689 - Crash on opening digiKam. 106 ==> 149267 - digiKam crashes after finding avi and so on. 107 ==> 170387 - Add movies management. 108 ==> 369629 - digiKam does not use GPS data from video files. 109 ==> 367880 - Nexus 5X videos show up upside-down in digiKam. 110 ==> 330116 - digiKam does not take care about GPS info stored in MP4 video files. 111 ==> 339150 - digikam crashes when trying to display video file. 112 ==> 344406 - Crash at start. 113 ==> 339909 - digiKam Segmentation Fault on open. 114 ==> 343231 - Crash at scanning for new fotos. 115 ==> 340373 - Crash on scanning video directory. 116 ==> 134679 - Video and audio files are not imported. 117 ==> 375357 - No video preview. 118 ==> 261773 - Batch renaming does not complete when MP4 video file is processed. 119 ==> 185915 - Album View: "Created" time of video set to "00:00". 120 ==> 303218 - digiKam import crashes when you select video files. 121 ==> 374241 - Bad video date rename. 122 ==> 375646 - Be able to scan only photo, not video and audio. 123 ==> 262499 - Cannot rename .AVI files. 124 ==> 199261 - Import avi movies from sdcard wrong date and no thumbnail. 125 ==> 181521 - NEF's in descending order, AVI in ascending order in import from SD-card. 126 ==> 392019 - Two persons can point to the same face tag in pictures. 127 ==> 392013 - Metadata explorer does not show XMP face rectangles. 128 ==> 389508 - Dates Side Menu Is Not Updated Automatically After Exif Date Change [patch]. 129 ==> 331864 - Merge Tags with same name when moving to same hierarchy level. 130 ==> 347302 - Reassign name to face. 131 ==> 391747 - BQM Tool "Remove Metadata" doesn't remove all metadata from image. 132 ==> 285683 - Already imported pictures not recognized after daylight savings time. 133 ==> 392309 - Icons are pixelated when my display scale factor is 1.2 134 ==> 392405 - Function 'getImageIdsFromArea' argument order different. 135 ==> 386224 - Metadata is not updated when moving tags. 136 ==> 370245 - Be able to rename tags which have been setted in pictures. 137 ==> 374470 - Deleted tags are not removed from file metadata. 138 ==> 374516 - Persons metadata are not updated after a tag removed. 139 ==> 392436 - Count Files in directory. 140 ==> 363859 - digiKam core port from QWebKit to QWebEngine [patch]. 141 ==> 392427 - Cannot add collection on network drive. 142 ==> 392022 - Position of a face tag appears on top of bottom of the list, instead of being sorted alphabetically. 143 ==> 372763 - Rename does not give options on Conflict. 144 ==> 391533 - Feature request: add "NOT" tag matching condition in "Filters" panel. 145 ==> 381222 - digiKam crash on fuzzy search. 146 ==> 386275 - Crash caused by QtAV. 147 ==> 372342 - Face tag area is very short [patch]. 148 ==> 391348 - People Side Menu Shows Only Faces Not People Tagged Images. 149 ==> 385630 - Views Requiring Maps Takes ~30s to Launch. 150 ==> 192908 - Allow to split icon-view in order to show multiple albums at the same time. 151 ==> 339088 - GIT master: crash when clicking through images in preview, with face recognition running in background. 152 ==> 341605 - Crash if I attempt to use left-sidebar tags tab. 153 ==> 227266 - Handle Video Date from metadata. 154 ==> 227259 - Needs to Edit Video Date. 155 ==> 373284 - digiKam crashed with SIGSEGV in QSortFilterProxyModel::parent(). 156 ==> 384807 - digikam 5.7.0 AppImage bundle : provide a more recent ffmpeg version for video support. 157 ==> 391835 - Deleted pictures still appear in group. 158 ==> 387483 - Elegant theme: Selected frame colors swapped [patch]. 159 ==> 375424 - Thumbnails are not being removed from AlbumsView after moving the images to Trash. 160 ==> 368796 - Problem with Exif-tags: ImageDescription and UserComment. 161 ==> 392417 - AppImage (5.9.0-01-x86-64) does not support "--install" cli parameter. 162 ==> 392922 - digikam-6.0.0 fail to start. 163 ==> 391399 - Not possible to add location bookmarks in Digikam >5.6.0. 164 ==> 380876 - Tags in Digikam DB maintained after being removed from file and file re-scanned. 165 ==> 392017 - Merging, renaming and removing face tags. 166 ==> 352711 - Externally removed tags are not removed from digiKam. 167 ==> 393108 - Tags not always visible when selecting multiple pictures in a group. 168 ==> 392656 - Selecting a root album for face scan doesn't include subfolders, but rather scans an unexpected album set. 169 ==> 329438 - Rename function with Date & Time does not work with NTFS. 170 ==> 376473 - Can"t set empty IPTC country code when using metadata templates. 171 ==> 380289 - Cannot write to Albums residing on NFS. 172 ==> 384465 - With Compact Flash Card Created date in thumbnails is wrong. 173 ==> 381958 - Cannot add additional collection. 174 ==> 383747 - "Rotate only by setting a flag" Changes Image Instead. 175 ==> 387977 - No icon only view of "Extras sidebar": sidebar taking up a lot of space. 176 ==> 277502 - All versions of version set always displayed in Album view [patch]. 177 ==> 393283 - Caption not updating Exif.Image.ImageDescription field. 178 ==> 391060 - Crashes on undo of very large tif. 179 ==> 366305 - Add a message at startup about the lack of temporary space to perform Undo operations. 180 ==> 366391 - Rotating an image seems to forget to reset the orientation flag. 181 ==> 393654 - Not able to select gpx file. 182 ==> 367596 - Sub-folder count images but don't show them (unsupported JPEG file?). 183 ==> 379922 - Digikam won't remove tags set by Windows Explorer. 184 ==> 379081 - GPS data are in file but geolocation indicator is not shown and map view empty. 185 ==> 354819 - Specific pictures not showing up in digikam. 186 ==> 393855 - MySQL/MariaDB upgrade fails. 187 ==> 384603 - Camera Creation Date not set from EXIF data. 188 ==> 386959 - Properties view: wrong creation date [patch]. 189 ==> 393970 - No mts video thumbnails. 190 ==> 393728 - Reread metadata from Video uses sidecar only. 191 ==> 393925 - UpdateSchemaFromV7ToV9 fails due to duplicate key in album_2. 192 ==> 393773 - showfoto crashes when geotagging. 193 ==> 388199 - No context menu to copy coordinates from map. 194 ==> 393399 - Windows defender freaks out in windows 10 and Edge. 195 ==> 392134 - SIGSEGV While Scanning Faces [patch]. 196 ==> 394168 - OSM Search Yields No Results. 197 ==> 377719 - Cannot rename file with overwrite [patch]. 198 ==> 388002 - remove kio related legacy [patch] 199 ==> 394242 - Import settings unneccesarily asks to overwrite image database, and crashes when I decline. 200 ==> 394278 - A slideshow theme for kiosk mode. 201 ==> 340389 - digiKam crashes while editing pictures for color balancing on OSX [patch]. 202 ==> 394413 - Unify group handling [patch]. 203 ==> 394573 - Revers geodata from open street map does not work. 204 ==> 394590 - Feature request: being able to filter on all metadatas fields. 205 ==> 394671 - Distortion on Panasonic DMC-LX15. 206 ==> 393205 - Advanced rename very slow. 207 ==> 382474 - Thumbnail regeneration. 208 ==> 394865 - digikam suspicious crash on exit. 209 ==> 390541 - Tooltip background cannot be changed. 210 ==> 391521 - "Tool-tip" box difficult to read due to default color scheme. 211 ==> 377849 - Albums disappear when the network is interrupted. 212 ==> 394988 - PgDown and PgUp hardcoded in preview mode. 213 ==> 366312 - Efficient photo tagging workflow got lost in transition from 4.x to 5. 214 ==> 395093 - Being able to export a list of paths from a selection of thumbnails. 215 ==> 395144 - When zooming in preview, face tags show on wrong places. 216 ==> 275671 - Scan single image for faces. 217 ==> 395199 - Uploading large video files to flickr fails. 218 ==> 348274 - "Change Account" immediately opens web page in browser, before I click Continue 219 ==> 263347 - Print wizard ignores selected paper size, reverts to A4. 220 ==> 395579 - Only one tag being exported to flickr. 221 ==> 395790 - Rename with nested tags breaks due to | bar character. 222 ==> 395875 - ImageEditor window is blank when opened a second time. 223 ==> 385822 - [Suggested feature] Re-use thumbnails from the database for the items in Trash. 224 ==> 382174 - Not creating thumbnails and not editing (F4) files created for Samsung panorama jpgs [patch]. 225 ==> 376124 - Some Photos are not previewed. 226 ==> 386188 - Preview Does Not Display Some JPGs Editor Crashes Program. 227 ==> 394906 - Toggling "use file metadata" for input will make Digikam forget all imported photos. 228 ==> 388391 - Windows x64 installer crashes at 60%. 229 ==> 377433 - Crash on opening settings. 230 ==> 390286 - Please update version of lensfun. 231 ==> 386649 - Crash on opening RAW file from OnePlus One. 232 ==> 172836 - No menu item for lensfun. 233 ==> 319462 - Crash while applying lensfun distortion correction a second time. 234 ==> 380844 - Demosaicing choosing VCD & AHD then update. 235 ==> 380843 - Demosaicing. 236 ==> 301219 - digiKam crash. 237 ==> 216013 - try to open extras/ batch raw converter was followed by a crash. 238 ==> 102045 - RAW conversion in digikam fails to convert or generate preview. 239 ==> 137281 - Cannot convert nef into raw. 240 ==> 230763 - Exif ISO data missing when converting Olympus ORF. 241 ==> 137278 - 16-bit/channel workspace when working with RAW conversions. 242 ==> 133004 - Canon CRW portrait preview upside-down. 243 ==> 221345 - Do not process search immediately when enter text to query. 244 ==> 396234 - Add a feature which allow to switch latitude and longitude. 245 ==> 396283 - Missing images in album preview. 246 ==> 396352 - Some iptc tags are not displayed in the metadata part. 247 ==> 396434 - Uncontrolled log messages oversize log file up to run out of disk space. 248 ==> 396482 - Empty folder selector window after select and click on "Import selected elements". 249 ==> 396170 - Gallery creation error. 250 ==> 140374 - HTML Gallery export fails to parse xml from non-UTF8 metadata in jpeg. 251 ==> 396712 - "Save Search" does not work. 252 ==> 396944 - Integrity constraint violation on Albums.icons when migrating from sqlite to mysql. 253 ==> 396952 - Cut and Paste Into Caption Has Incorrect Font Initially. 254 ==> 091562 - Change order of pictures in the virtual album manually. 255 ==> 098340 - Re-sort images in albums 256 ==> 177355 - Feature request: export slideshow in lighttable. 257 ==> 158520 - Wishlist sort thumbnails manually and rename images. 258 ==> 191000 - Wish: Visual tool to change the sort order of photos. 259 ==> 216802 - digiKam ability to sort album with "picture is before picture". 260 ==> 230136 - It lacks the ability to sort photos manually. 261 ==> 236249 - Wish: custom sort in album view. 262 ==> 323559 - Wish: Albums like Playlists in Amarok. 263 ==> 337002 - Manual image sort. 264 ==> 397167 - Some tags are not displayed on the preview. 265 ==> 397207 - Too many open files, when reverse geocoding many images. 266 ==> 397278 - DNG conversion not possible. 267 ==> 397311 - DIN A relation in "Aspect Ratio Crop". 268 ==> 222716 - digiKam does not start. Showing: Reading database. 269 ==> 276633 - Crash when using map view inside of digiKam. 270 ==> 224706 - digiKam for KDE on Windows "Failed to rename Album" (KDirWatch Relevant). 271 ==> 383016 - digiKam with incomplete German translation. 272 ==> 244982 - digiKam crashes at initial configuration. 273 ==> 366453 - digiKam crash while using mouse over title bar. 274 ==> 278490 - Incorrect tab layout with slider/spinbox/reset button. 275 ==> 324642 - digiKam crash when starting a bug report while offline. 276 ==> 238392 - digiKam Camera interface : rename dialog interprets alt-s as plain s when button greyed out. 277 ==> 397177 - image editor does not export exif and IPTC data when saving to PNG. 278 ==> 341276 - Picture bigger than 5MB are not previewed. 279 ==> 388908 - Error while scanning with HP F300 series. 280 ==> 381723 - digiKam crashed when I tried to close the window opened by clicking import button. 281 ==> 345288 - Crash from digiKam when try customizing a shortcut to a Tag. 282 ==> 381193 - digiKam crashes after uploading photos from smartphone via USB. 283 ==> 275670 - Face preview image incorrect on 'rotated' images. 284 ==> 395199 - Uploading large video files to flickr fails [patch]. 285 ==> 240144 - digiKam : When I press the left button of the mouse on a photo, a menu appears, but no items are seen. 286 ==> 240229 - White contextual menu problem. 287 ==> 309508 - Export to Flickr does not send the geodata. 288 ==> 383987 - Flickr tool no longer authenticates to Flickr. 289 ==> 338333 - Account problem. 290 ==> 397001 - The "maximise" button is missing in decoration of the geolocation editor window [patch]. 291 ==> 397406 - Digikam shouldn't use parentheses when renaming files. 292 ==> 397411 - Not being able to move files in a removable media collection which is not mounted. 293 ==> 227566 - digiKam does not allow smb:// shared folder as picture collection. 294 ==> 395201 - SSL broken in 5.9.0 AppImage bundle. 295 ==> 385363 - digiKam crashes when authorizing access to Google photo because of SSL error in AppImage. 296 ==> 348277 - Please focus auth code input field in the GDrive authentication dialog. 297 ==> 383174 - Google photo, export stops after a random quantity of photos. 298 ==> 387422 - Flickr export authorization does not work. 299 ==> 397126 - Fail to replace a photo in google photo. 300 ==> 387201 - kioslave needed by flickr export but not included in appimage. 301 ==> 389785 - 'Open in Filemanager' dolphin not working. 302 ==> 396619 - All Exports fail - cannot find ioslave. 303 ==> 254512 - Crash when initializing the export to Flickr.com. 304 ==> 237818 - Crash when uploading multiple images to Flickr. 305 ==> 286754 - Crash when when entering collection name in flickr uploader. 306 ==> 337980 - Flickr Export tool can no longer obtain a new token. 307 ==> 151018 - Requires new token with every invokation. 308 ==> 132922 - Export to flickr doesn't work. 309 ==> 149864 - Dialog box pops up saying 'Error Occured: Missing signature. We can not proceed further". 310 ==> 196179 - Flickr export does not work. 311 ==> 391734 - Can't export to google photos: SSL Handshake failed. 312 ==> 386402 - Not all page sizes for selected printer can be selected. 313 ==> 395557 - Printformat not selectable. 314 ==> 340644 - Allow to use standard photo paper sizes. 315 ==> 330906 - Crashes when exporting to HTML. 316 ==> 309316 - Html gallery export crash and other bugs. 317 ==> 314248 - digiKam crashing when exporting pictures to html. 318 ==> 225725 - Crash using HTML export. 319 ==> 396092 - Don't init search till user press a buttom specifically for this task. 320 ==> 397376 - Search in Geolocation doesn't work for Streets / location, only for City. 321 ==> 393259 - Configuration conflicts. 322 ==> 391329 - Two actions that wants to use same shortcut (Ctrl+Shift+,). 323 ==> 391655 - Kubuntu 18.04 beta, Digikam 5.6.0 Ambiguous Shortcuts - Zoom to 100% and Configure Digikam share the same shortcut (Ctrl+Shift+,). 324 ==> 393031 - Two actions for digikam[helpfully identified as a bug]. 325 ==> 395518 - When manually typing face name, sort the filtered tags by recent usage. 326 ==> 304202 - No option to re-read metadata from XMP sidecar file. 327 ==> 289445 - Allow to create Contact Sheet from an album. 328 ==> 164750 - Picture alignment and background picture. 329 ==> 338180 - Add "Toggle auto-refresh" option in search view. 330 ==> 359235 - digiKam doesn't response after start when StatusBar=Disabled in digikamrc. 331 ==> 090550 - Linking error with jpeg. 332 ==> 393974 - Appimage bundle does not automatically recognize Gphoto2 devices connected. 333 ==> 141288 - Error KIoexec usb camera. 334 ==> 397554 - digiKam crashes while running BatchQueueManager. 335 ==> 379261 - Crash on attempting camera import. 336 ==> 379335 - digiKam crashes when deleting images in the download window [patch]. 337 ==> 394291 - Cannot upgrade mysql db from v7 to v9. 338 ==> 389468 - Immediate Crash On Metadata Sync. 339 ==> 379807 - ShowFoto crashes when clicking Color Effects from menu. 340 ==> 257301 - showfoto reaches an assert when saving file with "~/" in the path. 341 ==> 183629 - Digikam/Showfoto Dark Theme: The selected photo filename is nearly invisible. 342 ==> 249379 - All versions of Digikam/Showfoto/KDE download is missing a file. 343 ==> 237286 - digiKam crash after to scan collection (upgrate from 9.10 to 10.04). 344 ==> 254878 - digiKam error segmentation fault when open it. 345 ==> 255759 - digikam crash when open it kunbuntu lucid. 346 ==> 249009 - digiKam crashes at initialisation. 347 ==> 236647 - After upgrading to Ubuntu 10.04 digiKam crashes everytime it is launched. 348 ==> 235905 - digiKam crash after upgrade. 349 ==> 185265 - digiKam constantly using some CPU in a poll loop due a timeout 350 ==> 250364 - digiKam crashes at start-up in xubuntu 10.04. 351 ==> 253205 - digiKam crashes while loading on Ubunu 10.04. 352 ==> 249033 - After upgrade to 10.04 digiKam crashes after starting always. 353 ==> 300713 - Crash while editing tag in digiKam. 354 ==> 184443 - digiKam save as "JPEG" appends a ".JPEG" file extension. Bad! 355 ==> 189084 - Crash while editing toolbars (here from digiKam camera GUI). 356 ==> 205275 - After changing symbols in bar on preview digikam closes. 357 ==> 214418 - Crash while importing into digiKam. 358 ==> 279909 - digiKam crashed when I tried to import photos from my iPhone 4. 359 ==> 287616 - digiKam crash when selecting folder to store photos. 360 ==> 327714 - digiKam crashes when starting up [Qt Bmp Image IO]. 361 ==> 396892 - digiKam font sizes cannot be changed. Mostly too small. 362 ==> 371726 - Dates view empty using MYSQL due to SQL query error (fix supplied). 363 ==> 382217 - Use normalized connects [patch]. 364 ==> 113692 - digiKam thumbnail hangs on .mov movie. 365 ==> 185638 - digiKam crash on startup. 366 ==> 350404 - digiKam crashes at startup. 367 ==> 301583 - digiKam crash on preview avi. 368 ==> 236960 - No video in digiKam embedded viewer no way to change codec? 369 ==> 227113 - digiKam crashes when selecting a movie. 370 ==> 274333 - digiKam crashed upon startup. 371 ==> 247019 - digiKam crashes in Album view with Canon IXUS 90 video clip. 372 ==> 238525 - digiKam crashes on browsing in album view. 373 ==> 317437 - digiKam crashes on startup. 374 ==> 237183 - digiKam crashes on playing Quicktime movies. 375 ==> 256644 - digiKam crashes after adding photos to light table. 376 ==> 245033 - digiKam falls on video files. 377 ==> 252411 - digiKam crashes when .AVI videoclip is launched. 378 ==> 247399 - digiKam crashes when viewing videos. 379 ==> 261706 - digiKam crashed after downloading pictures. 380 ==> 172170 - digiKam embedded video player - video not synchronized with sound. 381 ==> 293173 - digiKam does not play videos. 382 ==> 182401 - Wrong color palette on playback videos within digiKam. 383 ==> 219419 - digiKam crashes after splash screen with PTP. 384 ==> 397565 - Unable to launch in OS X after install. 385 ==> 337978 - digiKam crashes on startup - always. 386 ==> 215673 - digiKam movie no preview. 387 ==> 204481 - digikam: Wizard not translated in French. 388 ==> 365694 - digiKam 5.0.0 : every menus are in French, excepted the menu bar. 389 ==> 394434 - Installation problem mysql internal on mac os high sierra [patch]. 390 ==> 397696 - [Website] On Fedora, use dnf instead of yum. 391 ==> 392970 - Website: Mention KDE Familly. 392 ==> 394694 - Showfoto crashes on change picture size. 393 ==> 326006 - digiKam Raw engine generates pink & black stripe on the right. 394 ==> 379984 - ASSERT failure in Q_GLOBAL_STATIC: "The global static was used after being destroyed". 395 ==> 373572 - File synchronization. 396 ==> 296768 - When creating thumbnails, digiKam uses 80% CPU - quadcore 3800, 9GB RAM. 397 ==> 397777 - Need to default to native filesystem browser on first run to see external drives. 398 ==> 279818 - digiKam crashed when updating fingerprints. 399 ==> 275931 - digiKam crashed when trying to stop a batch queue. 400 ==> 272144 - Alphabetically sorting of the tools in BQM. 401 ==> 271531 - Cannot edit canon crw-pics. 402 ==> 397739 - Menu icons are not scaled correctly on external/secondary screen (2560x1440) while using MacBookPro. 403 ==> 380841 - Crash while correcting color manually. 404 ==> 378176 - Using tool causes program to crash. 405 ==> 395842 - digiKam crashes adjusting color balance on a RAW file. 406 ==> 388608 - Faces engine hangs and crashes. 407 ==> 397855 - Not compatible with Windows 10. 408 ==> 391039 - digiKam stalled after trying to make a panorama. 409 ==> 184318 - Starting digiKam it crashed and caused the signal 11 (SIGSEGV). 410 ==> 222740 - Starting digiKam cause sound system to report pulseaudio is removed. 411 ==> 378697 - Add image carousel to front page screenshots. 412 ==> 180375 - Web page: Wrong link to Alpenglow Webpage. 413 ==> 331141 - Broken link to database schema in web site digikam.org pointing to project.kde.org. 414 ==> 397862 - Reordering geolocation bookmarks will remove them from the list. 415 ==> 223348 - digiKam crash during album view browsing AVIs with PgUp/PgDown. 416 ==> 156146 - Reading data base, digiKam don't start. 417 ==> 118090 - digiKam crashed while attempting to save scanned (tiff) image. 418 ==> 397828 - Operation cancelled when exporting to flickr. 419 ==> 397924 - Merge tag names of people. 420 ==> 397425 - Bug when trying find duplicates. 421 ==> 397496 - Wish: Read facetags from jpg files edited in Mylio. 422 ==> 397727 - Don't show "Mod."" label when no modified timestamp exists on an image. 423 ==> 397893 - MacOS : digiKam do not link with libksane 18.08.0. 424 ==> 397990 - Carousel images too large. 425 ==> 389273 - Copy/move selected items to anothor Album doesn't work correctly [patch]. 426 ==> 261471 - Crashes when trying to delete photos or albums. 427 ==> 342108 - Crashes when trying to import images 428 ==> 341268 - digiKam freeze each time I select a photograph in a collection. 429 ==> 388551 - Error message at startup showing that Marble plugins are not loading but they do. 430 ==> 342110 - Bug reporting feature gives error wont report bug. 431 ==> 387047 - Easily accessible signatures. 432 ==> 286286 - digiKam crashes if I request fullscreen mode. 433 ==> 292522 - digiKam crashes on startup. 434 ==> 294791 - digiKam and Showfoto crashes while switching to fullscreen mode. 435 ==> 347589 - Menu icons size is not unified - text overlapping. 436 ==> 382053 - digiKam preferences when using mysql host port resets to default (3306) when accessing preferences. 437 ==> 388227 - Image version always shown. 438 ==> 386561 - Cannot import images from mac /Volumes/. 439 ==> 396491 - Versions are not grouped despite the correct setting. 440 ==> 389505 - Database port reset to 3306 when oppening digiKam setup (working with maiaDB10). 441 ==> 266165 - Advanced slideshow crash. 442 ==> 387372 - With Digikam Version 5.7 is no preview possible. 443 ==> 286341 - Crash at startup with a "Bus Error". 444 ==> 329050 - Application crashes while starts. 445 ==> 396374 - editing a picture (F4) presents an empty screen. 446 ==> 336874 - After exiting full screen, Menu Bar disappear and crash. 447 ==> 237494 - B&W JPEG displayed incorrectly. 448 ==> 265749 - Blank context menus. 449 ==> 274733 - Delete file permanentry works wrong. 450 ==> 383711 - All Albums Disappear. 451 ==> 378929 - DNG conversion fails in Windows 10 for Olympus ORF files. 452 ==> 170458 - Object position on pictures. 453 ==> 184638 - Generate XMP for raw files. 454 ==> 200380 - Usability of editing exif comments. 455 ==> 195090 - Thumbnails bar should have no empty space. 456 ==> 196730 - Add Color Space converter to batch queue tool. 457 ==> 207921 - Using image editing tools in fullscreen modes leaves right sidebar open. 458 ==> 211066 - TIFFs written by digiKam not readable with GIMP. 459 ==> 224603 - One-touch download when connecting media. 460 ==> 282021 - Impossible to start... 461 ==> 202430 - Crash after startup on Mac OS X. 462 ==> 139153 - Can't compile from source. 463 ==> 380971 - Albums disappear when the network is interrupted. (5.6.0). 464 ==> 342673 - Rebuild fingerprints crashes with large number (>110000) of images. 465 ==> 261134 - Drag a folder into album view - deletes source folder. 466 ==> 365809 - Menu bar disappears after having used full screen mode. 467 ==> 383924 - Tags syncing and reading from file not working. 468 ==> 395788 - If tags have a in them rename effectively deletes the file. 469 ==> 388596 - Main area remains empty with Qt 5.9.3. 470 ==> 387552 - digiKam suddenly is unable to read or display any images. 471 ==> 397972 - Unable to navigate in DK during flickr export. 472 ==> 390228 - No clear rule on where digikam wants to import. 473 ==> 187274 - The dialogue to export to an HTML gallery has an album selected but the Next button is greyed out. 474 ==> 129762 - Flickr export tool should export whole host application albums. 475 ==> 130966 - Exporting to html gallery doesn't include subalbums. 476 ==> 097396 - Export to HTML stops/ slow. 477 ==> 128035 - HTML export adds extra .jpg extention to all image filenames. 478 ==> 129870 - Album sent to HTMLGallery tool doesn't respect the view order. 479 ==> 111462 - Exporting multiple "tag" albums in HTML produces invalid hyperlinks on main webpage. 480 ==> 147770 - Use a hierarchical treeview in HTMLExport (instead of a listview). 481 ==> 259748 - Xorg crashes when launching the "Export to flickr" tool. 482 ==> 398206 - Strongly underexposed areas of the jpeg file are white rather than pure black. 483 ==> 127498 - Reduce image size delete exif data. 484 ==> 219009 - Crashed when selecting photos for upload. 485 ==> 234395 - Unable to get list of albums. 486 ==> 241587 - Flickr export reencode jpeg before sending them. 487 ==> 278890 - Showfoto crashes with qt 4.8. 488 ==> 355744 - Segfault on SmugMug upload. 489 ==> 358704 - Request authorization does not occur and upload becomes impossible. 490 ==> 175233 - Showfoto crashes on windows. 491 ==> 259623 - Application: showFoto (showfoto), signal: Segmentation fault (opening jpg). 492 ==> 194832 - Showfoto open and crash after one second. 493 ==> 390287 - digiKam crashes when I do batch renames. 494 ==> 390290 - Trying to use "red eyes" reduction function make showfoto crash. 495 ==> 388542 - DNG crash when i open a .nef files. 496 ==> 390580 - First crashes when saving tiff file, then crashes when starting. 497 ==> 391721 - No photography is displayed in the main view. 498 ==> 392813 - Showfoto crashed when clicked on close button in title bar. 499 ==> 393069 - I do not see images in the file browser, only in calendar. 500 ==> 392189 - Database upgrade fails. 501 ==> 386653 - Message error on face detectior. 502 ==> 398287 - Print creator causes digiKam crash when orientation is changed. 503 ==> 397305 - Failed to add tag to database. 504 ==> 398462 - IPTC from Ligthroom. 505 ==> 397340 - Update image file timestamps if metadata is written to sidecar file [patch]. 506 ==> 398331 - Xmp sidecar files are not reloaded on change. 507 ==> 380341 - Modified sidecar not rescanned. 508 ==> 339342 - SCAN : Search for new items does not recognize modified tags in sidecar files. 509 ==> 387351 - Meta data for RAW files checkbox disabled. 510 ==> 398676 - Time Adjust in Batch Queue Manager jumps from hour to second. 511 ==> 338533 - Sort by date should use picture created date not modified date. 512 ==> 398675 - Image Rotation Inflates File Size. 513 ==> 309520 - GROUP : add 'group by month' into 'View.. | group by...' menu. 514 ==> 398714 - MicrosoftPhoto:LastKeywordXMP does not show up in digkam tags list. 515 ==> 388386 - Time adjustment tool is misleading and potentially leads to data loss. 516 ==> 366777 - No preview for Time Adjust tool. 517 ==> 306092 - Creation date as file name - doesn't work in BQM. 518 ==> 089993 - Allow setting time to specific date/time. 519 ==> 099894 - TimeAdjust only modifies file modification date, not EXIF. 520 ==> 119232 - Mass changing date for jpegs without EXIF. 521 ==> 119634 - Batch operation to change the date to several images at the same time. 522 ==> 134306 - Image mass tagging feature. 523 ==> 211845 - Missing progress bar while changing images dates. 524 ==> 249320 - Unable to change time file. 525 ==> 282559 - Graphical interface for changing dates and time. 526 ==> 354668 - Crashes when adjusting time and date on two or more photos at a time. 527 ==> 360770 - Crash when adjusting time on many pictures. 528 ==> 362104 - digiKam crashes when changing date and time of more than 25 selected pictures 529 ==> 366450 - Batch time adjust is ok, but time not adjusted in digikam thumbnail. 530 ==> 378006 - Preview of the time adjust result. 531 ==> 398624 - Cannot edit Date in XMP video metadata. 532 ==> 398810 - "Dates" panel is not refreshed after a photo with a new year is added. 533 ==> 398986 - Deleting a tag while using it as filter removes all images from the current view. 534 ==> 370093 - Opening a picture (double-click) in Showfoto (default viewer) does not show the other pictures in the folder (similar to Bug 221245). 535 ==> 398479 - digiKam crashing at startup Thread 2 Crashed:: Digikam::ScanController. 536 ==> 397808 - digiKam Mac Package crashes after resume when screen configuration changes. 537 ==> 399071 - "Adjust Time Date" tool shows all times as "00:00:00". 538 ==> 399134 - Progress bar or status when importing from camera/card reader. 539 ==> 399153 - Selecting a picture with a tag and another without that tag, the checkbox is black instead of gray. 540 ==> 384759 - Can't adjust timestamp of RAW files. 541 ==> 399221 - Cannot scroll in videos by clicking on the progress bar (only by moving the progress slider). 542 ==> 399315 - Horizontal scroll bar aligned to the right by default. 543 ==> 397962 - digiKam 6.0 does not start after install. 544 ==> 399336 - Google maps – "For development purposes only" printed on the map. 545 ==> 399338 - Count of items in Tree Views does not match total number of items. 546 ==> 399292 - Video file datetime not shown correctly after editing. 547 ==> 370553 - Adjust date - not possible to update "digiKam timestamp". 548 ==> 329091 - MySQL : needs to set "max-allowed-packet" server settings from 4.096 to 16.777.216 to prevent Maintenance tool crash. 549 ==> 399406 - digiKam Crash on advance rename. 550 ==> 397386 - Add a separate checkbox to "Show Rating". 551 ==> 335870 - Nexus 4 in PTP mode gets error: The specified camera ("/org/kde/solid/udev/...") is not supported. 552 ==> 399917 - Fails to build with QT5.7.1 553 ==> 399762 - digiKam Geolocation Correlator ignores time offset. 554 ==> 400148 - Files exported in google photos does not have their filename as name, but their date. 555 ==> 373678 - Videos exported in google photos are not available (still processing state). 556 ==> 400139 - Titles are not exported anymore on google photos. 557 ==> 400216 - Components Information window: no standardized uppercase/lowercase. 558 ==> 400217 - Incorrect Video date/time information: shifted by 2 hours. 559 ==> 396961 - Empty space on interface on thumbnail view. 560 ==> 400436 - Crash when saving jpeg files with metadata. 561 ==> 400492 - Rotation does not work. 562 ==> 246727 - Add undo functionality to move to trash action. 563 ==> 400637 - IPFS icons have non-square size, causing them to get blurred. 564 ==> 400434 - Trash content disappears after selecting 'Map' or 'Table' view. 565 ==> 394660 - Crashes when refreshing. 566 ==> 400712 - File modification timestamp is updated when images are moved to collection on removable media. 567 ==> 394214 - Import .mp4 files does not preserve file mtime as date stamp. 568 ==> 400762 - Trash - "Delete All Permanently" button not visible. 569 ==> 400792 - Failure to export to JPEG 2000. 570 ==> 400777 - Group images by timelapse / burst [patch]. 571 ==> 400902 - NAS Mysql cannot connect with instructions given by Dmitry Popov. 572 ==> 400918 - Tags are erroneously copied to untagged images in a selection when new tag added. 573 ==> 375197 - It is possible to rotate video thumbnails (but the video isn't rotated). 574 ==> 334678 - Thumbnails fail to generate for certain AVI files - infinite loop. 575 ==> 400917 - Rotation of picture in Preview mode: thumbnail not rotated. 576 ==> 400960 - Do not reset horizontal scroll bar slider position when selecting album. 577 ==> 400766 - Export to Onedrive: not possible to export into subfolder. 578 ==> 398207 - Box.com video upload fails despite successful authentication. 579 ==> 401160 - Build against lensfun-devel. 580 ==> 401173 - Wish for file saving: set changed datetime to exif datetime. 581 ==> 401423 - Video sidecar files nor read or written. 582 ==> 401438 - Calendar plugin prints improper month names (Czech localization). 583 ==> 401515 - Export to OneDrive: created subfolders not correctly managed. 584 ==> 401516 - Export to (Onedrive, Dropbox ...): list of subfolders not correctly displayed (problems if several subfolders have the same name). 585 ==> 401645 - digiKam crashes on possibly damaged video-file. 586 ==> 401676 - BQM resize tool : allow to resize by percentage (%). 587 ==> 401709 - Double clicking a name in a face-frame closes image preview. 588 ==> 401811 - DB migration (SQLite -> MySQL) fails if images are in trash. 589 ==> 401767 - digiKam unable to find existing path. 590 ==> 215486 - Collection not found in location on disk with UUID (LVM volume). 591 ==> 401834 - The "Scanning Faces" dialog box is too large and can't be resized in french language. 592 ==> 401664 - Trash shows list view only no thumbnails. 593 ==> 402029 - Find New Items process takes significantly longer to finish on 6.0.0. 594 ==> 402286 - Metadata settings don't display the combo value options. 595 ==> 402288 - The default Xmp.lr.hierarchicalSubject setting does not read darktable tags. 596 ==> 402300 - Template string in Help > About. 597 ==> 402283 - Duplicate entries drive to unpredictable performance. 598 ==> 402380 - Database schema upgrade to V10 is incomplete. 599 ==> 402379 - Moving location of RAW file causes loss of metadata. 600 ==> 366211 - When changing database, cannot open thumbnails into edit window. 601 ==> 402496 - digiKam crashed after a while during face recognition. 602 ==> 402726 - Piwigo export is missing. 603 ==> 401301 - digiKam Close crash. 604 ==> 401837 - Button Caption transcription error in french. 605 ==> 398880 - digiKam stays as a background process after closing. 606 ==> 398129 - ExpoBlend tool filename dialog shows previous file's name. 607 ==> 402556 - digikam-data conflict with dropbox installation. 608 ==> 402733 - Launch failure on MacOS with Digkam 6 beta3. 609 ==> 388705 - Unexpected crash on adding new items to the collection from an external application - 5.7.0 suse packages. 610 ==> 402800 - Main album view does not update when moving images between albums. 611 ==> 402811 - Drag-n-drop disabled in Geolocation editor. -612 ==> +612 ==> 402812 - Use RED "recycle bin" icon from Albums context menus in all other 'delete' context menu items too. +613 ==> diff --git a/core/app/items/utils/contextmenuhelper.cpp b/core/app/items/utils/contextmenuhelper.cpp index 769586e643..4f59992d7d 100644 --- a/core/app/items/utils/contextmenuhelper.cpp +++ b/core/app/items/utils/contextmenuhelper.cpp @@ -1,1164 +1,1164 @@ /* ============================================================ * * This file is a part of digiKam project * http://www.digikam.org * * Date : 2009-02-15 * Description : contextmenu helper class * * Copyright (C) 2009-2011 by Andi Clemens * Copyright (C) 2010-2019 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 "contextmenuhelper.h" // Qt includes #include #include #include #include #include #include #include #include #include #include // KDE includes #include #include #ifdef HAVE_KIO # include #endif // Local includes #include "digikam_debug.h" #include "album.h" #include "coredb.h" #include "albummanager.h" #include "albumpointer.h" #include "albummodificationhelper.h" #include "abstractalbummodel.h" #include "coredbaccess.h" #include "digikamapp.h" #include "dfileoperations.h" #include "iteminfo.h" #include "itemfiltermodel.h" #include "lighttablewindow.h" #include "queuemgrwindow.h" #include "picklabelwidget.h" #include "colorlabelwidget.h" #include "ratingwidget.h" #include "tagmodificationhelper.h" #include "tagspopupmenu.h" #include "fileactionmngr.h" #include "tagscache.h" #include "dimg.h" #include "dxmlguiwindow.h" #ifdef HAVE_AKONADICONTACT # include "akonadiiface.h" #endif namespace Digikam { class Q_DECL_HIDDEN ContextMenuHelper::Private { public: explicit Private(ContextMenuHelper* const q) : gotoAlbumAction(0), gotoDateAction(0), setThumbnailAction(0), imageFilterModel(0), albumModel(0), parent(0), stdActionCollection(0), q(q) { } QAction* gotoAlbumAction; QAction* gotoDateAction; QAction* setThumbnailAction; QList selectedIds; QList selectedItems; QMap queueActions; QMap servicesMap; ItemFilterModel* imageFilterModel; AbstractCheckableAlbumModel* albumModel; QMenu* parent; KActionCollection* stdActionCollection; ContextMenuHelper* q; public: QModelIndex indexForAlbumFromAction(QObject* sender) const { QAction* action = 0; if ((action = qobject_cast(sender))) { Album* const album = action->data().value >(); return albumModel->indexForAlbum(album); } return QModelIndex(); } QAction* copyFromMainCollection(const QString& name) const { QAction* const mainAction = stdActionCollection->action(name); if (!mainAction) { return 0; } QAction* const action = new QAction(mainAction->icon(), mainAction->text(), q); action->setShortcut(mainAction->shortcut()); action->setToolTip(mainAction->toolTip()); return action; } }; ContextMenuHelper::ContextMenuHelper(QMenu* const parent, KActionCollection* const actionCollection) : QObject(parent), d(new Private(this)) { d->parent = parent; if (!actionCollection) { d->stdActionCollection = DigikamApp::instance()->actionCollection(); } else { d->stdActionCollection = actionCollection; } } ContextMenuHelper::~ContextMenuHelper() { delete d; } void ContextMenuHelper::addAction(const QString& name, bool addDisabled) { QAction* const action = d->stdActionCollection->action(name); addAction(action, addDisabled); } void ContextMenuHelper::addAction(QAction* action, bool addDisabled) { if (!action) { return; } if (action->isEnabled() || addDisabled) { d->parent->addAction(action); } } void ContextMenuHelper::addSubMenu(QMenu* subMenu) { d->parent->addMenu(subMenu); } void ContextMenuHelper::addSeparator() { d->parent->addSeparator(); } void ContextMenuHelper::addAction(QAction* action, QObject* recv, const char* slot, bool addDisabled) { if (!action) { return; } connect(action, SIGNAL(triggered()), recv, slot); addAction(action, addDisabled); } void ContextMenuHelper::addStandardActionLightTable() { QAction* action = 0; QStringList ltActionNames; ltActionNames << QLatin1String("image_add_to_lighttable") << QLatin1String("image_lighttable"); if (LightTableWindow::lightTableWindowCreated() && !LightTableWindow::lightTableWindow()->isEmpty()) { action = d->stdActionCollection->action(ltActionNames.at(0)); } else { action = d->stdActionCollection->action(ltActionNames.at(1)); } addAction(action); } void ContextMenuHelper::addStandardActionThumbnail(const imageIds& ids, Album* album) { if (d->setThumbnailAction) { return; } setSelectedIds(ids); if (album && ids.count() == 1) { if (album->type() == Album::PHYSICAL) { d->setThumbnailAction = new QAction(i18n("Set as Album Thumbnail"), this); } else if (album->type() == Album::TAG) { d->setThumbnailAction = new QAction(i18n("Set as Tag Thumbnail"), this); } addAction(d->setThumbnailAction); d->parent->addSeparator(); } } void ContextMenuHelper::addOpenAndNavigateActions(const imageIds &ids) { addAction(QLatin1String("image_edit")); addServicesMenu(ItemInfoList(ids).toImageUrlList()); addAction(QLatin1String("move_selection_to_album")); addGotoMenu(ids); } void ContextMenuHelper::addServicesMenu(const QList& selectedItems) { setSelectedItems(selectedItems); KService::List offers = DFileOperations::servicesForOpenWith(selectedItems); if (!offers.isEmpty()) { QMenu* const servicesMenu = new QMenu(d->parent); qDeleteAll(servicesMenu->actions()); QAction* const serviceAction = servicesMenu->menuAction(); serviceAction->setText(i18n("Open With")); foreach (const KService::Ptr& service, offers) { QString name = service->name().replace(QLatin1Char('&'), QLatin1String("&&")); QAction* const action = servicesMenu->addAction(name); action->setIcon(QIcon::fromTheme(service->icon())); action->setData(service->name()); d->servicesMap[name] = service; } #ifdef HAVE_KIO servicesMenu->addSeparator(); servicesMenu->addAction(i18n("Other...")); addAction(serviceAction); connect(servicesMenu, SIGNAL(triggered(QAction*)), this, SLOT(slotOpenWith(QAction*))); } else { QAction* const serviceAction = new QAction(i18n("Open With..."), this); addAction(serviceAction); connect(serviceAction, SIGNAL(triggered()), this, SLOT(slotOpenWith())); #endif // HAVE_KIO } } void ContextMenuHelper::slotOpenWith() { // call the slot with an "empty" action slotOpenWith(0); } void ContextMenuHelper::slotOpenWith(QAction* action) { KService::Ptr service; QList list = d->selectedItems; QString name = action ? action->data().toString() : QString(); #ifdef HAVE_KIO if (name.isEmpty()) { QPointer dlg = new KOpenWithDialog(list); if (dlg->exec() != KOpenWithDialog::Accepted) { delete dlg; return; } service = dlg->service(); if (!service) { // User entered a custom command if (!dlg->text().isEmpty()) { DFileOperations::runFiles(dlg->text(), list); } delete dlg; return; } delete dlg; } else #endif // HAVE_KIO { service = d->servicesMap[name]; } DFileOperations::runFiles(service.data(), list); } bool ContextMenuHelper::imageIdsHaveSameCategory(const imageIds& ids, DatabaseItem::Category category) { bool sameCategory = true; QVariantList varList; foreach (const qlonglong& id, ids) { varList = CoreDbAccess().db()->getImagesFields(id, DatabaseFields::Category); if (varList.isEmpty() || (DatabaseItem::Category)varList.first().toInt() != category) { sameCategory = false; break; } } return sameCategory; } void ContextMenuHelper::addActionNewTag(TagModificationHelper* helper, TAlbum* tag) { QAction* const newTagAction = new QAction(QIcon::fromTheme(QLatin1String("tag-new")), i18n("New Tag..."), this); addAction(newTagAction); helper->bindTag(newTagAction, tag); connect(newTagAction, SIGNAL(triggered()), helper, SLOT(slotTagNew())); } void ContextMenuHelper::addActionDeleteTag(TagModificationHelper* helper, TAlbum* tag) { - QAction* const deleteTagAction = new QAction(QIcon::fromTheme(QLatin1String("user-trash")), i18n("Delete Tag"), this); + QAction* const deleteTagAction = new QAction(QIcon::fromTheme(QLatin1String("edit-delete")), i18n("Delete Tag"), this); addAction(deleteTagAction); helper->bindTag(deleteTagAction, tag); connect(deleteTagAction, SIGNAL(triggered()), helper, SLOT(slotTagDelete())); } void ContextMenuHelper::addActionDeleteTags(Digikam::TagModificationHelper* helper, QList< TAlbum* > tags) { - QAction* const deleteTagsAction = new QAction(QIcon::fromTheme(QLatin1String("user-trash")), i18n("Delete Tags"), this); + QAction* const deleteTagsAction = new QAction(QIcon::fromTheme(QLatin1String("edit-delete")), i18n("Delete Tags"), this); addAction(deleteTagsAction); helper->bindMultipleTags(deleteTagsAction, tags); connect(deleteTagsAction, SIGNAL(triggered()), helper, SLOT(slotMultipleTagDel())); } void ContextMenuHelper::addActionTagToFaceTag(TagModificationHelper* helper, TAlbum* tag) { QAction* const tagToFaceTagAction = new QAction(QIcon::fromTheme(QLatin1String("tag-properties")), i18n("Mark As Face Tag"), this); addAction(tagToFaceTagAction); helper->bindTag(tagToFaceTagAction, tag); connect(tagToFaceTagAction, SIGNAL(triggered()), helper, SLOT(slotTagToFaceTag())); } void ContextMenuHelper::addActionTagsToFaceTags(TagModificationHelper* helper, QList< TAlbum* > tags) { QAction* const tagToFaceTagsAction = new QAction(QIcon::fromTheme(QLatin1String("tag-properties")), i18n("Mark As Face Tags"), this); addAction(tagToFaceTagsAction); helper->bindMultipleTags(tagToFaceTagsAction, tags); connect(tagToFaceTagsAction, SIGNAL(triggered()), helper, SLOT(slotMultipleTagsToFaceTags())); } void ContextMenuHelper::addActionEditTag(TagModificationHelper* helper, TAlbum* tag) { QAction* const editTagAction = new QAction(QIcon::fromTheme(QLatin1String("tag-properties")), i18nc("Edit Tag Properties", "Properties..."), this); addAction(editTagAction); helper->bindTag(editTagAction, tag); connect(editTagAction, SIGNAL(triggered()), helper, SLOT(slotTagEdit())); } void ContextMenuHelper::addActionDeleteFaceTag(TagModificationHelper* helper, TAlbum* tag) { QAction* const deleteFaceTagAction = new QAction(QIcon::fromTheme(QLatin1String("user-trash")), i18n("Remove Face Tag"), this); deleteFaceTagAction->setWhatsThis(i18n("Removes the face property from the selected tag and the face region from the contained images. Can also untag the images if wished.")); addAction(deleteFaceTagAction); helper->bindTag(deleteFaceTagAction, tag); connect(deleteFaceTagAction, SIGNAL(triggered()), helper, SLOT(slotFaceTagDelete())); } void ContextMenuHelper::addActionDeleteFaceTags(TagModificationHelper* helper, QList< TAlbum* > tags) { QAction* const deleteFaceTagsAction = new QAction(QIcon::fromTheme(QLatin1String("user-trash")), i18n("Remove Face Tags"), this); deleteFaceTagsAction->setWhatsThis(i18n("Removes the face property from the selected tags and the face region from the contained images. Can also untag the images if wished.")); addAction(deleteFaceTagsAction); helper->bindMultipleTags(deleteFaceTagsAction, tags); connect(deleteFaceTagsAction, SIGNAL(triggered()), helper, SLOT(slotMultipleFaceTagDel())); } void ContextMenuHelper::addActionNewAlbum(AlbumModificationHelper* helper, PAlbum* parentAlbum) { QAction* const action = d->copyFromMainCollection(QLatin1String("album_new")); addAction(action); helper->bindAlbum(action, parentAlbum); connect(action, SIGNAL(triggered()), helper, SLOT(slotAlbumNew())); } void ContextMenuHelper::addActionDeleteAlbum(AlbumModificationHelper* helper, PAlbum* album) { QAction* const action = d->copyFromMainCollection(QLatin1String("album_delete")); addAction(action, !(album->isRoot() || album->isAlbumRoot())); helper->bindAlbum(action, album); connect(action, SIGNAL(triggered()), helper, SLOT(slotAlbumDelete())); } void ContextMenuHelper::addActionEditAlbum(AlbumModificationHelper* helper, PAlbum* album) { QAction* const action = d->copyFromMainCollection(QLatin1String("album_propsEdit")); addAction(action, !album->isRoot()); helper->bindAlbum(action, album); connect(action, SIGNAL(triggered()), helper, SLOT(slotAlbumEdit())); } void ContextMenuHelper::addActionRenameAlbum(AlbumModificationHelper* helper, PAlbum* album) { QAction* const action = d->copyFromMainCollection(QLatin1String("album_rename")); addAction(action, !(album->isRoot() || album->isAlbumRoot())); helper->bindAlbum(action, album); connect(action, SIGNAL(triggered()), helper, SLOT(slotAlbumRename())); } void ContextMenuHelper::addActionResetAlbumIcon(AlbumModificationHelper* helper, PAlbum* album) { QAction* const action = new QAction(QIcon::fromTheme(QLatin1String("view-refresh")), i18n("Reset Album Icon"), this); addAction(action, !album->isRoot()); helper->bindAlbum(action, album); connect(action, SIGNAL(triggered()), helper, SLOT(slotAlbumResetIcon())); } void ContextMenuHelper::addAssignTagsMenu(const imageIds &ids) { setSelectedIds(ids); QMenu* const assignTagsPopup = new TagsPopupMenu(ids, TagsPopupMenu::RECENTLYASSIGNED, d->parent); assignTagsPopup->menuAction()->setText(i18n("A&ssign Tag")); assignTagsPopup->menuAction()->setIcon(QIcon::fromTheme(QLatin1String("tag"))); d->parent->addMenu(assignTagsPopup); connect(assignTagsPopup, SIGNAL(signalTagActivated(int)), this, SIGNAL(signalAssignTag(int))); connect(assignTagsPopup, SIGNAL(signalPopupTagsView()), this, SIGNAL(signalPopupTagsView())); } void ContextMenuHelper::addRemoveTagsMenu(const imageIds &ids) { setSelectedIds(ids); QMenu* const removeTagsPopup = new TagsPopupMenu(ids, TagsPopupMenu::REMOVE, d->parent); removeTagsPopup->menuAction()->setText(i18n("R&emove Tag")); removeTagsPopup->menuAction()->setIcon(QIcon::fromTheme(QLatin1String("tag"))); d->parent->addMenu(removeTagsPopup); // Performance: Only check for tags if there are <250 images selected // Otherwise enable it regardless if there are tags or not if (ids.count() < 250) { QList tagIDs = CoreDbAccess().db()->getItemCommonTagIDs(ids); bool enable = false; foreach (int tag, tagIDs) { if (TagsCache::instance()->colorLabelForTag(tag) == -1 && TagsCache::instance()->pickLabelForTag(tag) == -1 && TagsCache::instance()->isInternalTag(tag) == false) { enable = true; break; } } removeTagsPopup->menuAction()->setEnabled(enable); } connect(removeTagsPopup, SIGNAL(signalTagActivated(int)), this, SIGNAL(signalRemoveTag(int))); } void ContextMenuHelper::addLabelsAction() { QMenu* const menuLabels = new QMenu(i18n("Assign Labe&ls"), d->parent); PickLabelMenuAction* const pmenu = new PickLabelMenuAction(d->parent); ColorLabelMenuAction* const cmenu = new ColorLabelMenuAction(d->parent); RatingMenuAction* const rmenu = new RatingMenuAction(d->parent); menuLabels->addAction(pmenu->menuAction()); menuLabels->addAction(cmenu->menuAction()); menuLabels->addAction(rmenu->menuAction()); addSubMenu(menuLabels); connect(pmenu, SIGNAL(signalPickLabelChanged(int)), this, SIGNAL(signalAssignPickLabel(int))); connect(cmenu, SIGNAL(signalColorLabelChanged(int)), this, SIGNAL(signalAssignColorLabel(int))); connect(rmenu, SIGNAL(signalRatingChanged(int)), this, SIGNAL(signalAssignRating(int))); } void ContextMenuHelper::addCreateTagFromAddressbookMenu() { #ifdef HAVE_AKONADICONTACT AkonadiIface* const abc = new AkonadiIface(d->parent); connect(abc, SIGNAL(signalContactTriggered(QString)), this, SIGNAL(signalAddNewTagFromABCMenu(QString))); // AkonadiIface instance will be deleted with d->parent. #endif } void ContextMenuHelper::slotDeselectAllAlbumItems() { QAction* const selectNoneAction = d->stdActionCollection->action(QLatin1String("selectNone")); QTimer::singleShot(75, selectNoneAction, SIGNAL(triggered())); } void ContextMenuHelper::addImportMenu() { QMenu* const menuImport = new QMenu(i18n("Import"), d->parent); QList importActions = DigikamApp::instance()->importActions(); if (!importActions.isEmpty()) { menuImport->addActions(importActions); } else { QAction* const notools = new QAction(i18n("No import tool available"), this); notools->setEnabled(false); menuImport->addAction(notools); } d->parent->addMenu(menuImport); } void ContextMenuHelper::addExportMenu() { QMenu* const menuExport = new QMenu(i18n("Export"), d->parent); QList exportActions = DigikamApp::instance()->exportActions(); #if 0 QAction* selectAllAction = 0; selectAllAction = d->stdActionCollection->action("selectAll"); #endif if (!exportActions.isEmpty()) { menuExport->addActions(exportActions); } else { QAction* const notools = new QAction(i18n("No export tool available"), this); notools->setEnabled(false); menuExport->addAction(notools); } d->parent->addMenu(menuExport); } void ContextMenuHelper::addAlbumActions() { QList albumActions; if (!albumActions.isEmpty()) { d->parent->addActions(albumActions); } } void ContextMenuHelper::addGotoMenu(const imageIds &ids) { if (d->gotoAlbumAction && d->gotoDateAction) { return; } setSelectedIds(ids); // the currently selected image is always the first item ItemInfo item; if (!d->selectedIds.isEmpty()) { item = ItemInfo(d->selectedIds.first()); } if (item.isNull()) { return; } // when more then one item is selected, don't add the menu if (d->selectedIds.count() > 1) { return; } d->gotoAlbumAction = new QAction(QIcon::fromTheme(QLatin1String("folder-pictures")), i18n("Album"), this); d->gotoDateAction = new QAction(QIcon::fromTheme(QLatin1String("view-calendar")), i18n("Date"), this); QMenu* const gotoMenu = new QMenu(d->parent); gotoMenu->addAction(d->gotoAlbumAction); gotoMenu->addAction(d->gotoDateAction); TagsPopupMenu* const gotoTagsPopup = new TagsPopupMenu(d->selectedIds, TagsPopupMenu::DISPLAY, gotoMenu); QAction* const gotoTag = gotoMenu->addMenu(gotoTagsPopup); gotoTag->setIcon(QIcon::fromTheme(QLatin1String("tag"))); gotoTag->setText(i18n("Tag")); // Disable the goto Tag popup menu, if there are no tags at all. if (!CoreDbAccess().db()->hasTags(d->selectedIds)) { gotoTag->setEnabled(false); } /** * TODO:tags to be ported to multiple selection */ QList albumList = AlbumManager::instance()->currentAlbums(); Album* currentAlbum = 0; if(!albumList.isEmpty()) { currentAlbum = albumList.first(); } else { return; } if (currentAlbum->type() == Album::PHYSICAL) { // If the currently selected album is the same as album to // which the image belongs, then disable the "Go To" Album. // (Note that in recursive album view these can be different). if (item.albumId() == currentAlbum->id()) { d->gotoAlbumAction->setEnabled(false); } } else if (currentAlbum->type() == Album::DATE) { d->gotoDateAction->setEnabled(false); } QAction* const gotoMenuAction = gotoMenu->menuAction(); gotoMenuAction->setIcon(QIcon::fromTheme(QLatin1String("go-jump"))); gotoMenuAction->setText(i18n("Go To")); connect(gotoTagsPopup, SIGNAL(signalTagActivated(int)), this, SIGNAL(signalGotoTag(int))); addAction(gotoMenuAction); } void ContextMenuHelper::addQueueManagerMenu() { QMenu* const bqmMenu = new QMenu(i18n("Batch Queue Manager"), d->parent); bqmMenu->menuAction()->setIcon(QIcon::fromTheme(QLatin1String("run-build"))); bqmMenu->addAction(d->stdActionCollection->action(QLatin1String("image_add_to_current_queue"))); bqmMenu->addAction(d->stdActionCollection->action(QLatin1String("image_add_to_new_queue"))); // if queue list is empty, do not display the queue submenu if (QueueMgrWindow::queueManagerWindowCreated() && !QueueMgrWindow::queueManagerWindow()->queuesMap().isEmpty()) { QueueMgrWindow* const qmw = QueueMgrWindow::queueManagerWindow(); QMenu* const queueMenu = new QMenu(i18n("Add to Existing Queue"), bqmMenu); // queueActions is used by the exec() method to emit an appropriate signal. // Reset the map before filling in the actions. if (!d->queueActions.isEmpty()) { d->queueActions.clear(); } QList queueList; // get queue list from BQM window, do not access it directly, it might crash // when the list is changed QMap qmwMap = qmw->queuesMap(); for (QMap::const_iterator it = qmwMap.constBegin(); it != qmwMap.constEnd(); ++it) { QAction* const action = new QAction(it.value(), this); queueList << action; d->queueActions[it.key()] = action; } queueMenu->addActions(queueList); bqmMenu->addMenu(queueMenu); } d->parent->addMenu(bqmMenu); // NOTE: see bug #252130 : we need to disable new items to add on BQM is this one is running. bqmMenu->setDisabled(QueueMgrWindow::queueManagerWindow()->isBusy()); } void ContextMenuHelper::setAlbumModel(AbstractCheckableAlbumModel* model) { d->albumModel = model; } void ContextMenuHelper::addAlbumCheckUncheckActions(Album* album) { bool enabled = false; QString allString = i18n("All Albums"); QVariant albumData; if (album) { enabled = true; albumData = QVariant::fromValue(AlbumPointer<>(album)); if (album->type() == Album::TAG) allString = i18n("All Tags"); } QMenu* const selectTagsMenu = new QMenu(i18nc("select tags menu", "Select")); addSubMenu(selectTagsMenu); selectTagsMenu->addAction(allString, d->albumModel, SLOT(checkAllAlbums())); selectTagsMenu->addSeparator(); QAction* const selectChildrenAction = selectTagsMenu->addAction(i18n("Children"), this, SLOT(slotSelectChildren())); QAction* const selectParentsAction = selectTagsMenu->addAction(i18n("Parents"), this, SLOT(slotSelectParents())); selectChildrenAction->setData(albumData); selectParentsAction->setData(albumData); QMenu* const deselectTagsMenu = new QMenu(i18nc("deselect tags menu", "Deselect")); addSubMenu(deselectTagsMenu); deselectTagsMenu->addAction(allString, d->albumModel, SLOT(resetAllCheckedAlbums())); deselectTagsMenu->addSeparator(); QAction* const deselectChildrenAction = deselectTagsMenu->addAction(i18n("Children"), this, SLOT(slotDeselectChildren())); QAction* const deselectParentsAction = deselectTagsMenu->addAction(i18n("Parents"), this, SLOT(slotDeselectParents())); deselectChildrenAction->setData(albumData); deselectParentsAction->setData(albumData); d->parent->addAction(i18n("Invert Selection"), d->albumModel, SLOT(invertCheckedAlbums())); selectChildrenAction->setEnabled(enabled); selectParentsAction->setEnabled(enabled); deselectChildrenAction->setEnabled(enabled); deselectParentsAction->setEnabled(enabled); } void ContextMenuHelper::slotSelectChildren() { if (!d->albumModel) { return; } d->albumModel->checkAllAlbums(d->indexForAlbumFromAction(sender())); } void ContextMenuHelper::slotDeselectChildren() { if (!d->albumModel) { return; } d->albumModel->resetCheckedAlbums(d->indexForAlbumFromAction(sender())); } void ContextMenuHelper::slotSelectParents() { if (!d->albumModel) { return; } d->albumModel->checkAllParentAlbums(d->indexForAlbumFromAction(sender())); } void ContextMenuHelper::slotDeselectParents() { if (!d->albumModel) { return; } d->albumModel->resetCheckedParentAlbums(d->indexForAlbumFromAction(sender())); } void ContextMenuHelper::addGroupMenu(const imageIds &ids, const QList& extraMenuItems) { QList actions = groupMenuActions(ids); if (actions.isEmpty() && extraMenuItems.isEmpty()) { return; } if (!extraMenuItems.isEmpty()) { if (!actions.isEmpty()) { QAction* separator = new QAction(this); separator->setSeparator(true); actions << separator; } actions << extraMenuItems; } QMenu* const menu = new QMenu(i18n("Group")); foreach (QAction* const action, actions) { menu->addAction(action); } d->parent->addMenu(menu); } void ContextMenuHelper::addGroupActions(const imageIds &ids) { foreach (QAction* const action, groupMenuActions(ids)) { d->parent->addAction(action); } } void ContextMenuHelper::setItemFilterModel(ItemFilterModel* model) { d->imageFilterModel = model; } QList ContextMenuHelper::groupMenuActions(const imageIds &ids) { setSelectedIds(ids); QList actions; if (ids.isEmpty()) { if (d->imageFilterModel) { if (!d->imageFilterModel->isAllGroupsOpen()) { QAction* const openAction = new QAction(i18nc("@action:inmenu", "Open All Groups"), this); connect(openAction, SIGNAL(triggered()), this, SLOT(slotOpenGroups())); actions << openAction; } else { QAction* const closeAction = new QAction(i18nc("@action:inmenu", "Close All Groups"), this); connect(closeAction, SIGNAL(triggered()), this, SLOT(slotCloseGroups())); actions << closeAction; } } return actions; } ItemInfo info(ids.first()); if (ids.size() == 1) { if (info.hasGroupedImages()) { if (d->imageFilterModel) { if (!d->imageFilterModel->isGroupOpen(info.id())) { QAction* const action = new QAction(i18nc("@action:inmenu", "Show Grouped Images"), this); connect(action, SIGNAL(triggered()), this, SLOT(slotOpenGroups())); actions << action; } else { QAction* const action = new QAction(i18nc("@action:inmenu", "Hide Grouped Images"), this); connect(action, SIGNAL(triggered()), this, SLOT(slotCloseGroups())); actions << action; } } QAction* const separator = new QAction(this); separator->setSeparator(true); actions << separator; QAction* const clearAction = new QAction(i18nc("@action:inmenu", "Ungroup"), this); connect(clearAction, SIGNAL(triggered()), this, SIGNAL(signalUngroup())); actions << clearAction; } else if (info.isGrouped()) { QAction* const action = new QAction(i18nc("@action:inmenu", "Remove From Group"), this); connect(action, SIGNAL(triggered()), this, SIGNAL(signalRemoveFromGroup())); actions << action; // TODO: set as group leader / pick image } } else { QAction* const closeAction = new QAction(i18nc("@action:inmenu", "Group Selected Here"), this); connect(closeAction, SIGNAL(triggered()), this, SIGNAL(signalCreateGroup())); actions << closeAction; QAction* const closeActionDate = new QAction(i18nc("@action:inmenu", "Group Selected By Time"), this); connect(closeActionDate, SIGNAL(triggered()), this, SIGNAL(signalCreateGroupByTime())); actions << closeActionDate; QAction* const closeActionType = new QAction(i18nc("@action:inmenu", "Group Selected By Filename"), this); connect(closeActionType, SIGNAL(triggered()), this, SIGNAL(signalCreateGroupByFilename())); actions << closeActionType; QAction* const closeActionTimelapse = new QAction(i18nc("@action:inmenu", "Group Selected By Timelapse / Burst"), this); connect(closeActionTimelapse, SIGNAL(triggered()), this, SIGNAL(signalCreateGroupByTimelapse())); actions << closeActionTimelapse; QAction* const separator = new QAction(this); separator->setSeparator(true); actions << separator; if (d->imageFilterModel) { QAction* const openAction = new QAction(i18nc("@action:inmenu", "Show Grouped Images"), this); connect(openAction, SIGNAL(triggered()), this, SLOT(slotOpenGroups())); actions << openAction; QAction* const closeAction = new QAction(i18nc("@action:inmenu", "Hide Grouped Images"), this); connect(closeAction, SIGNAL(triggered()), this, SLOT(slotCloseGroups())); actions << closeAction; QAction* const separator2 = new QAction(this); separator2->setSeparator(true); actions << separator2; } QAction* const removeAction = new QAction(i18nc("@action:inmenu", "Remove Selected From Groups"), this); connect(removeAction, SIGNAL(triggered()), this, SIGNAL(signalRemoveFromGroup())); actions << removeAction; QAction* const clearAction = new QAction(i18nc("@action:inmenu", "Ungroup Selected"), this); connect(clearAction, SIGNAL(triggered()), this, SIGNAL(signalUngroup())); actions << clearAction; } return actions; } void ContextMenuHelper::setGroupsOpen(bool open) { if (!d->imageFilterModel || d->selectedIds.isEmpty()) { return; } GroupItemFilterSettings settings = d->imageFilterModel->groupItemFilterSettings(); foreach (const qlonglong& id, d->selectedIds) { ItemInfo info(id); if (info.hasGroupedImages()) { settings.setOpen(id, open); } } d->imageFilterModel->setGroupItemFilterSettings(settings); } void ContextMenuHelper::slotOpenGroups() { setGroupsOpen(true); } void ContextMenuHelper::slotCloseGroups() { setGroupsOpen(false); } void ContextMenuHelper::slotOpenAllGroups() { if (!d->imageFilterModel) { return; } d->imageFilterModel->setAllGroupsOpen(true); } void ContextMenuHelper::slotCloseAllGroups() { if (!d->imageFilterModel) { return; } d->imageFilterModel->setAllGroupsOpen(false); } void ContextMenuHelper::addStandardActionCut(QObject* recv, const char* slot) { QAction* const cut = DXmlGuiWindow::buildStdAction(StdCutAction, recv, slot, d->parent); addAction(cut); } void ContextMenuHelper::addStandardActionCopy(QObject* recv, const char* slot) { QAction* const copy = DXmlGuiWindow::buildStdAction(StdCopyAction, recv, slot, d->parent); addAction(copy); } void ContextMenuHelper::addStandardActionPaste(QObject* recv, const char* slot) { QAction* const paste = DXmlGuiWindow::buildStdAction(StdPasteAction, recv, slot, d->parent); const QMimeData* const data = qApp->clipboard()->mimeData(QClipboard::Clipboard); if (!data || !data->hasUrls()) { paste->setEnabled(false); } addAction(paste, true); } void ContextMenuHelper::addStandardActionItemDelete(QObject* recv, const char* slot, int quantity) { - QAction* const trashAction = new QAction(QIcon::fromTheme(QLatin1String("user-trash")), i18ncp("@action:inmenu Pluralized", + QAction* const trashAction = new QAction(QIcon::fromTheme(QLatin1String("user-trash-full")), i18ncp("@action:inmenu Pluralized", "Move to Trash", "Move %1 Files to Trash", quantity), d->parent); connect(trashAction, SIGNAL(triggered()), recv, slot); addAction(trashAction); } QAction* ContextMenuHelper::exec(const QPoint& pos, QAction* at) { QAction* const choice = d->parent->exec(pos, at); if (choice) { if (d->selectedIds.count() == 1) { ItemInfo selectedItem(d->selectedIds.first()); if (choice == d->gotoAlbumAction) { emit signalGotoAlbum(selectedItem); } else if (choice == d->gotoDateAction) { emit signalGotoDate(selectedItem); } else if (choice == d->setThumbnailAction) { emit signalSetThumbnail(selectedItem); } } // check if a BQM action has been triggered for (QMap::const_iterator it = d->queueActions.constBegin(); it != d->queueActions.constEnd(); ++it) { if (choice == it.value()) { emit signalAddToExistingQueue(it.key()); return choice; } } } return choice; } void ContextMenuHelper::setSelectedIds(const imageIds &ids) { if (d->selectedIds.isEmpty()) { d->selectedIds = ids; } } void ContextMenuHelper::setSelectedItems(const QList& urls) { if (d->selectedItems.isEmpty()) { d->selectedItems = urls; } } } // namespace Digikam diff --git a/core/app/main/digikamapp_setup.cpp b/core/app/main/digikamapp_setup.cpp index 6868edf784..7c5ca20f23 100644 --- a/core/app/main/digikamapp_setup.cpp +++ b/core/app/main/digikamapp_setup.cpp @@ -1,1110 +1,1110 @@ /* ============================================================ * * This file is a part of digiKam project * http://www.digikam.org * * Date : 2002-16-10 * Description : main digiKam interface implementation - Internal setup * * Copyright (C) 2002-2019 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 "digikamapp.h" #include "digikamapp_p.h" namespace Digikam { void DigikamApp::rebuild() { QString file = xmlFile(); if (!file.isEmpty()) { setXMLGUIBuildDocument(QDomDocument()); loadStandardsXmlFile(); setXMLFile(file, true); } } void DigikamApp::setupView() { if (d->splashScreen) { d->splashScreen->setMessage(i18n("Initializing Main View...")); } d->view = new ItemIconView(this, d->modelCollection); setCentralWidget(d->view); d->view->applySettings(); } void DigikamApp::setupViewConnections() { connect(d->view, SIGNAL(signalAlbumSelected(Album*)), this, SLOT(slotAlbumSelected(Album*))); connect(d->view, SIGNAL(signalSelectionChanged(int)), this, SLOT(slotSelectionChanged(int))); connect(d->view, SIGNAL(signalImageSelected(ItemInfoList,ItemInfoList)), this, SLOT(slotImageSelected(ItemInfoList,ItemInfoList))); connect(d->view, SIGNAL(signalSwitchedToPreview()), this, SLOT(slotSwitchedToPreview())); connect(d->view, SIGNAL(signalSwitchedToIconView()), this, SLOT(slotSwitchedToIconView())); connect(d->view, SIGNAL(signalSwitchedToMapView()), this, SLOT(slotSwitchedToMapView())); connect(d->view, SIGNAL(signalSwitchedToTableView()), this, SLOT(slotSwitchedToTableView())); connect(d->view, SIGNAL(signalSwitchedToTrashView()), this, SLOT(slotSwitchedToTrashView())); } void DigikamApp::setupStatusBar() { d->statusLabel = new DAdjustableLabel(statusBar()); d->statusLabel->setAlignment(Qt::AlignLeft|Qt::AlignVCenter); statusBar()->addWidget(d->statusLabel, 80); //------------------------------------------------------------------------------ d->metadataStatusBar = new MetadataStatusBar(statusBar()); statusBar()->addWidget(d->metadataStatusBar, 50); //------------------------------------------------------------------------------ d->filterStatusBar = new FilterStatusBar(statusBar()); statusBar()->addWidget(d->filterStatusBar, 50); d->view->connectIconViewFilter(d->filterStatusBar); //------------------------------------------------------------------------------ ProgressView* const view = new ProgressView(statusBar(), this); view->hide(); StatusbarProgressWidget* const littleProgress = new StatusbarProgressWidget(view, statusBar()); littleProgress->show(); statusBar()->addPermanentWidget(littleProgress); //------------------------------------------------------------------------------ d->zoomBar = new DZoomBar(statusBar()); d->zoomBar->setZoomToFitAction(d->zoomFitToWindowAction); d->zoomBar->setZoomTo100Action(d->zoomTo100percents); d->zoomBar->setZoomPlusAction(d->zoomPlusAction); d->zoomBar->setZoomMinusAction(d->zoomMinusAction); d->zoomBar->setBarMode(DZoomBar::ThumbsSizeCtrl); statusBar()->addPermanentWidget(d->zoomBar); //------------------------------------------------------------------------------ connect(d->zoomBar, SIGNAL(signalZoomSliderChanged(int)), this, SLOT(slotZoomSliderChanged(int))); connect(this, SIGNAL(signalWindowHasMoved()), d->zoomBar, SLOT(slotUpdateTrackerPos())); connect(d->zoomBar, SIGNAL(signalZoomValueEdited(double)), d->view, SLOT(setZoomFactor(double))); connect(d->view, SIGNAL(signalZoomChanged(double)), this, SLOT(slotZoomChanged(double))); connect(d->view, SIGNAL(signalThumbSizeChanged(int)), this, SLOT(slotThumbSizeChanged(int))); } void DigikamApp::setupActions() { KActionCollection* const ac = actionCollection(); d->solidCameraActionGroup = new QActionGroup(this); connect(d->solidCameraActionGroup, SIGNAL(triggered(QAction*)), this, SLOT(slotOpenSolidCamera(QAction*))); d->solidUsmActionGroup = new QActionGroup(this); connect(d->solidUsmActionGroup, SIGNAL(triggered(QAction*)), this, SLOT(slotOpenSolidUsmDevice(QAction*))); d->manualCameraActionGroup = new QActionGroup(this); connect(d->manualCameraActionGroup, SIGNAL(triggered(QAction*)), this, SLOT(slotOpenManualCamera(QAction*))); // ----------------------------------------------------------------- d->backwardActionMenu = new KToolBarPopupAction(QIcon::fromTheme(QLatin1String("go-previous")), i18n("&Back"), this); d->backwardActionMenu->setEnabled(false); ac->addAction(QLatin1String("album_back"), d->backwardActionMenu); ac->setDefaultShortcut(d->backwardActionMenu, Qt::ALT+Qt::Key_Left); connect(d->backwardActionMenu->menu(), SIGNAL(aboutToShow()), this, SLOT(slotAboutToShowBackwardMenu())); // we are using a signal mapper to identify which of a bunch of actions was triggered d->backwardSignalMapper = new QSignalMapper(this); // connect mapper to view connect(d->backwardSignalMapper, SIGNAL(mapped(int)), d->view, SLOT(slotAlbumHistoryBack(int))); // connect action to mapper connect(d->backwardActionMenu, SIGNAL(triggered()), d->backwardSignalMapper, SLOT(map())); // inform mapper about number of steps d->backwardSignalMapper->setMapping(d->backwardActionMenu, 1); // ----------------------------------------------------------------- d->forwardActionMenu = new KToolBarPopupAction(QIcon::fromTheme(QLatin1String("go-next")), i18n("Forward"), this); d->forwardActionMenu->setEnabled(false); ac->addAction(QLatin1String("album_forward"), d->forwardActionMenu); ac->setDefaultShortcut(d->forwardActionMenu, Qt::ALT+Qt::Key_Right); connect(d->forwardActionMenu->menu(), SIGNAL(aboutToShow()), this, SLOT(slotAboutToShowForwardMenu())); d->forwardSignalMapper = new QSignalMapper(this); connect(d->forwardSignalMapper, SIGNAL(mapped(int)), d->view, SLOT(slotAlbumHistoryForward(int))); connect(d->forwardActionMenu, SIGNAL(triggered()), d->forwardSignalMapper, SLOT(map())); d->forwardSignalMapper->setMapping(d->forwardActionMenu, 1); // ----------------------------------------------------------------- d->refreshAction = new QAction(QIcon::fromTheme(QLatin1String("view-refresh")), i18n("Refresh"), this); d->refreshAction->setWhatsThis(i18n("Refresh the current contents.")); connect(d->refreshAction, SIGNAL(triggered()), d->view, SLOT(slotRefresh())); ac->addAction(QLatin1String("view_refresh"), d->refreshAction); ac->setDefaultShortcut(d->refreshAction, Qt::Key_F5); // ----------------------------------------------------------------- QSignalMapper* const browseActionsMapper = new QSignalMapper(this); connect(browseActionsMapper, SIGNAL(mapped(QWidget*)), d->view, SLOT(slotLeftSideBarActivate(QWidget*))); foreach(SidebarWidget* const leftWidget, d->view->leftSidebarWidgets()) { QString actionName = QLatin1String("browse_") + leftWidget->objectName() .remove(QLatin1Char(' ')) .remove(QLatin1String("Sidebar")) .remove(QLatin1String("FolderView")) .remove(QLatin1String("View")).toLower(); qCDebug(DIGIKAM_GENERAL_LOG) << actionName; QAction* const action = new QAction(leftWidget->getIcon(), leftWidget->getCaption(), this); ac->addAction(actionName, action); ac->setDefaultShortcut(action, QKeySequence(leftWidget->property("Shortcut").toInt())); connect(action, SIGNAL(triggered()), browseActionsMapper, SLOT(map())); browseActionsMapper->setMapping(action, leftWidget); } // ----------------------------------------------------------------- d->newAction = new QAction(QIcon::fromTheme(QLatin1String("folder-new")), i18n("&New..."), this); d->newAction->setWhatsThis(i18n("Creates a new empty Album in the collection.")); connect(d->newAction, SIGNAL(triggered()), d->view, SLOT(slotNewAlbum())); ac->addAction(QLatin1String("album_new"), d->newAction); ac->setDefaultShortcuts(d->newAction, QList() << Qt::CTRL + Qt::Key_N); // ----------------------------------------------------------------- d->moveSelectionToAlbumAction = new QAction(QIcon::fromTheme(QLatin1String("folder-new")), i18n("&Move to Album..."), this); d->moveSelectionToAlbumAction->setWhatsThis(i18n("Move selected images into an album.")); connect(d->moveSelectionToAlbumAction, SIGNAL(triggered()), d->view, SLOT(slotMoveSelectionToAlbum())); ac->addAction(QLatin1String("move_selection_to_album"), d->moveSelectionToAlbumAction); // ----------------------------------------------------------------- d->deleteAction = new QAction(QIcon::fromTheme(QLatin1String("edit-delete")), i18n("Delete Album"), this); connect(d->deleteAction, SIGNAL(triggered()), d->view, SLOT(slotDeleteAlbum())); ac->addAction(QLatin1String("album_delete"), d->deleteAction); // ----------------------------------------------------------------- d->renameAction = new QAction(QIcon::fromTheme(QLatin1String("document-edit")), i18n("Rename..."), this); connect(d->renameAction, SIGNAL(triggered()), d->view, SLOT(slotRenameAlbum())); ac->addAction(QLatin1String("album_rename"), d->renameAction); ac->setDefaultShortcut(d->renameAction, Qt::SHIFT + Qt::Key_F2); // ----------------------------------------------------------------- d->propsEditAction = new QAction(QIcon::fromTheme(QLatin1String("configure")), i18n("Properties"), this); d->propsEditAction->setWhatsThis(i18n("Edit album properties and collection information.")); connect(d->propsEditAction, SIGNAL(triggered()), d->view, SLOT(slotAlbumPropsEdit())); ac->addAction(QLatin1String("album_propsEdit"), d->propsEditAction); ac->setDefaultShortcut(d->propsEditAction, Qt::ALT + Qt::Key_Return); // ----------------------------------------------------------------- d->writeAlbumMetadataAction = new QAction(QIcon::fromTheme(QLatin1String("document-edit")), i18n("Write Metadata to Files"), this); d->writeAlbumMetadataAction->setWhatsThis(i18n("Updates metadata of files in the current " "album with the contents of digiKam database " "(file metadata will be overwritten with data from " "the database).")); connect(d->writeAlbumMetadataAction, SIGNAL(triggered()), d->view, SLOT(slotAlbumWriteMetadata())); ac->addAction(QLatin1String("album_write_metadata"), d->writeAlbumMetadataAction); // ----------------------------------------------------------------- d->readAlbumMetadataAction = new QAction(QIcon::fromTheme(QLatin1String("edit-redo")), i18n("Reread Metadata From Files"), this); d->readAlbumMetadataAction->setWhatsThis(i18n("Updates the digiKam database from the metadata " "of the files in the current album " "(information in the database will be overwritten with data from " "the files' metadata).")); connect(d->readAlbumMetadataAction, SIGNAL(triggered()), d->view, SLOT(slotAlbumReadMetadata())); ac->addAction(QLatin1String("album_read_metadata"), d->readAlbumMetadataAction); // ----------------------------------------------------------------- d->openInFileManagerAction = new QAction(QIcon::fromTheme(QLatin1String("folder-open")), i18n("Open in File Manager"), this); connect(d->openInFileManagerAction, SIGNAL(triggered()), d->view, SLOT(slotAlbumOpenInFileManager())); ac->addAction(QLatin1String("album_openinfilemanager"), d->openInFileManagerAction); // ----------------------------------------------------------- d->openTagMngrAction = new QAction(QIcon::fromTheme(QLatin1String("tag")), i18n("Tag Manager"), this); connect(d->openTagMngrAction, SIGNAL(triggered()), d->view, SLOT(slotOpenTagsManager())); ac->addAction(QLatin1String("open_tag_mngr"), d->openTagMngrAction); // ----------------------------------------------------------- d->newTagAction = new QAction(QIcon::fromTheme(QLatin1String("tag-new")), i18nc("new tag", "N&ew..."), this); connect(d->newTagAction, SIGNAL(triggered()), d->view, SLOT(slotNewTag())); ac->addAction(QLatin1String("tag_new"), d->newTagAction); // ----------------------------------------------------------- d->editTagAction = new QAction(QIcon::fromTheme(QLatin1String("tag-properties")), i18n("Properties"), this); connect(d->editTagAction, SIGNAL(triggered()), d->view, SLOT(slotEditTag())); ac->addAction(QLatin1String("tag_edit"), d->editTagAction); // ----------------------------------------------------------- - d->deleteTagAction = new QAction(QIcon::fromTheme(QLatin1String("user-trash")), i18n("Delete"), this); + d->deleteTagAction = new QAction(QIcon::fromTheme(QLatin1String("edit-delete")), i18n("Delete"), this); connect(d->deleteTagAction, SIGNAL(triggered()), d->view, SLOT(slotDeleteTag())); ac->addAction(QLatin1String("tag_delete"), d->deleteTagAction); // ----------------------------------------------------------- d->assignTagAction = new QAction(QIcon::fromTheme(QLatin1String("tag-new")), i18n("Assign Tag"), this); connect(d->assignTagAction, SIGNAL(triggered()), d->view, SLOT(slotAssignTag())); ac->addAction(QLatin1String("tag_assign"), d->assignTagAction); ac->setDefaultShortcut(d->assignTagAction, Qt::Key_T); // ----------------------------------------------------------- d->imageViewSelectionAction = new KSelectAction(QIcon::fromTheme(QLatin1String("view-preview")), i18n("Views"), this); ac->addAction(QLatin1String("view_selection"), d->imageViewSelectionAction); d->imageIconViewAction = new QAction(QIcon::fromTheme(QLatin1String("view-list-icons")), i18nc("@action Go to thumbnails (icon) view", "Thumbnails"), this); d->imageIconViewAction->setCheckable(true); ac->addAction(QLatin1String("icon_view"), d->imageIconViewAction); connect(d->imageIconViewAction, SIGNAL(triggered()), d->view, SLOT(slotIconView())); d->imageViewSelectionAction->addAction(d->imageIconViewAction); d->imagePreviewAction = new QAction(QIcon::fromTheme(QLatin1String("view-preview")), i18nc("View the selected image", "Preview"), this); d->imagePreviewAction->setCheckable(true); ac->addAction(QLatin1String("image_view"), d->imagePreviewAction); ac->setDefaultShortcut(d->imagePreviewAction, Qt::Key_F3); connect(d->imagePreviewAction, SIGNAL(triggered()), d->view, SLOT(slotImagePreview())); d->imageViewSelectionAction->addAction(d->imagePreviewAction); #ifdef HAVE_MARBLE d->imageMapViewAction = new QAction(QIcon::fromTheme(QLatin1String("globe")), i18nc("@action Switch to map view", "Map"), this); d->imageMapViewAction->setCheckable(true); ac->addAction(QLatin1String("map_view"), d->imageMapViewAction); connect(d->imageMapViewAction, SIGNAL(triggered()), d->view, SLOT(slotMapWidgetView())); d->imageViewSelectionAction->addAction(d->imageMapViewAction); #endif // HAVE_MARBLE d->imageTableViewAction = new QAction(QIcon::fromTheme(QLatin1String("view-list-details")), i18nc("@action Switch to table view", "Table"), this); d->imageTableViewAction->setCheckable(true); ac->addAction(QLatin1String("table_view"), d->imageTableViewAction); connect(d->imageTableViewAction, SIGNAL(triggered()), d->view, SLOT(slotTableView())); d->imageViewSelectionAction->addAction(d->imageTableViewAction); // ----------------------------------------------------------- d->imageViewAction = new QAction(QIcon::fromTheme(QLatin1String("quickopen-file")), i18n("Open..."), this); d->imageViewAction->setWhatsThis(i18n("Open the selected item.")); connect(d->imageViewAction, SIGNAL(triggered()), d->view, SLOT(slotImageEdit())); ac->addAction(QLatin1String("image_edit"), d->imageViewAction); ac->setDefaultShortcut(d->imageViewAction, Qt::Key_F4); d->openWithAction = new QAction(QIcon::fromTheme(QLatin1String("preferences-desktop-filetype-association")), i18n("Open With Default Application"), this); d->openWithAction->setWhatsThis(i18n("Open the selected item with default assigned application.")); connect(d->openWithAction, SIGNAL(triggered()), d->view, SLOT(slotFileWithDefaultApplication())); ac->addAction(QLatin1String("open_with_default_application"), d->openWithAction); ac->setDefaultShortcut(d->openWithAction, Qt::META + Qt::Key_F4); d->ieAction = new QAction(QIcon::fromTheme(QLatin1String("document-edit")), i18n("Image Editor"), this); d->ieAction->setWhatsThis(i18n("Open the image editor.")); connect(d->ieAction, SIGNAL(triggered()), d->view, SLOT(slotEditor())); ac->addAction(QLatin1String("imageeditor"), d->ieAction); // ----------------------------------------------------------- d->ltAction = new QAction(QIcon::fromTheme(QLatin1String("lighttable")), i18n("Light Table"), this); connect(d->ltAction, SIGNAL(triggered()), d->view, SLOT(slotLightTable())); ac->addAction(QLatin1String("light_table"), d->ltAction); ac->setDefaultShortcut(d->ltAction, Qt::Key_L); d->imageLightTableAction = new QAction(QIcon::fromTheme(QLatin1String("lighttable")), i18n("Place onto Light Table"), this); d->imageLightTableAction->setWhatsThis(i18n("Place the selected items on the light table thumbbar.")); connect(d->imageLightTableAction, SIGNAL(triggered()), d->view, SLOT(slotImageLightTable())); ac->addAction(QLatin1String("image_lighttable"), d->imageLightTableAction); ac->setDefaultShortcut(d->imageLightTableAction, Qt::CTRL+Qt::Key_L); d->imageAddLightTableAction = new QAction(QIcon::fromTheme(QLatin1String("list-add")), i18n("Add to Light Table"), this); d->imageAddLightTableAction->setWhatsThis(i18n("Add selected items to the light table thumbbar.")); connect(d->imageAddLightTableAction, SIGNAL(triggered()), d->view, SLOT(slotImageAddToLightTable())); ac->addAction(QLatin1String("image_add_to_lighttable"), d->imageAddLightTableAction); ac->setDefaultShortcut(d->imageAddLightTableAction, Qt::SHIFT+Qt::CTRL+Qt::Key_L); // ----------------------------------------------------------- d->bqmAction = new QAction(QIcon::fromTheme(QLatin1String("run-build")), i18n("Batch Queue Manager"), this); connect(d->bqmAction, SIGNAL(triggered()), d->view, SLOT(slotQueueMgr())); ac->addAction(QLatin1String("queue_manager"), d->bqmAction); ac->setDefaultShortcut(d->bqmAction, Qt::Key_B); d->imageAddCurrentQueueAction = new QAction(QIcon::fromTheme(QLatin1String("go-up")), i18n("Add to Current Queue"), this); d->imageAddCurrentQueueAction->setWhatsThis(i18n("Add selected items to current queue from batch manager.")); connect(d->imageAddCurrentQueueAction, SIGNAL(triggered()), d->view, SLOT(slotImageAddToCurrentQueue())); ac->addAction(QLatin1String("image_add_to_current_queue"), d->imageAddCurrentQueueAction); ac->setDefaultShortcut(d->imageAddCurrentQueueAction, Qt::CTRL+Qt::Key_B); d->imageAddNewQueueAction = new QAction(QIcon::fromTheme(QLatin1String("list-add")), i18n("Add to New Queue"), this); d->imageAddNewQueueAction->setWhatsThis(i18n("Add selected items to a new queue from batch manager.")); connect(d->imageAddNewQueueAction, SIGNAL(triggered()), d->view, SLOT(slotImageAddToNewQueue())); ac->addAction(QLatin1String("image_add_to_new_queue"), d->imageAddNewQueueAction); ac->setDefaultShortcut(d->imageAddNewQueueAction, Qt::SHIFT+Qt::CTRL+Qt::Key_B); // ----------------------------------------------------------------- d->quickImportMenu->setTitle(i18nc("@action Import photos from camera", "Import")); d->quickImportMenu->setIcon(QIcon::fromTheme(QLatin1String("camera-photo"))); ac->addAction(QLatin1String("import_auto"), d->quickImportMenu->menuAction()); // ----------------------------------------------------------------- d->imageWriteMetadataAction = new QAction(QIcon::fromTheme(QLatin1String("document-edit")), i18n("Write Metadata to Selected Files"), this); d->imageWriteMetadataAction->setWhatsThis(i18n("Updates metadata of files in the current " "album with the contents of digiKam database " "(file metadata will be overwritten with data from " "the database).")); connect(d->imageWriteMetadataAction, SIGNAL(triggered()), d->view, SLOT(slotImageWriteMetadata())); ac->addAction(QLatin1String("image_write_metadata"), d->imageWriteMetadataAction); // ----------------------------------------------------------------- d->imageReadMetadataAction = new QAction(QIcon::fromTheme(QLatin1String("edit-redo")), i18n("Reread Metadata From Selected Files"), this); d->imageReadMetadataAction->setWhatsThis(i18n("Updates the digiKam database from the metadata " "of the files in the current album " "(information in the database will be overwritten with data from " "the files' metadata).")); connect(d->imageReadMetadataAction, SIGNAL(triggered()), d->view, SLOT(slotImageReadMetadata())); ac->addAction(QLatin1String("image_read_metadata"), d->imageReadMetadataAction); // ----------------------------------------------------------- d->imageScanForFacesAction = new QAction(QIcon::fromTheme(QLatin1String("list-add-user")), i18n("Scan for Faces"), this); connect(d->imageScanForFacesAction, SIGNAL(triggered()), d->view, SLOT(slotImageScanForFaces())); ac->addAction(QLatin1String("image_scan_for_faces"), d->imageScanForFacesAction); // ----------------------------------------------------------- d->imageFindSimilarAction = new QAction(QIcon::fromTheme(QLatin1String("tools-wizard")), i18n("Find Similar..."), this); d->imageFindSimilarAction->setWhatsThis(i18n("Find similar images using selected item as reference.")); connect(d->imageFindSimilarAction, SIGNAL(triggered()), d->view, SLOT(slotImageFindSimilar())); ac->addAction(QLatin1String("image_find_similar"), d->imageFindSimilarAction); // ----------------------------------------------------------- d->imageRenameAction = new QAction(QIcon::fromTheme(QLatin1String("document-edit")), i18n("Rename..."), this); d->imageRenameAction->setWhatsThis(i18n("Change the filename of the currently selected item.")); connect(d->imageRenameAction, SIGNAL(triggered()), d->view, SLOT(slotImageRename())); ac->addAction(QLatin1String("image_rename"), d->imageRenameAction); ac->setDefaultShortcut(d->imageRenameAction, Qt::Key_F2); // ----------------------------------------------------------- // Pop up dialog to ask user whether to move to trash - d->imageDeleteAction = new QAction(QIcon::fromTheme(QLatin1String("user-trash")), i18nc("Non-pluralized", "Move to Trash"), this); + d->imageDeleteAction = new QAction(QIcon::fromTheme(QLatin1String("user-trash-full")), i18nc("Non-pluralized", "Move to Trash"), this); connect(d->imageDeleteAction, SIGNAL(triggered()), d->view, SLOT(slotImageDelete())); ac->addAction(QLatin1String("image_delete"), d->imageDeleteAction); ac->setDefaultShortcut(d->imageDeleteAction, Qt::Key_Delete); // ----------------------------------------------------------- // Pop up dialog to ask user whether to permanently delete // FIXME: This action is never used?? How can someone delete a album directly, without moving it to the trash first? // This is especially important when deleting from a different partition or from a net source. // Also note that we use the wrong icon for the default album delete action, which should have a trashcan icon instead // of a red cross, it confuses users. d->imageDeletePermanentlyAction = new QAction(QIcon::fromTheme(QLatin1String("edit-delete")), i18n("Delete Permanently"), this); connect(d->imageDeletePermanentlyAction, SIGNAL(triggered()), d->view, SLOT(slotImageDeletePermanently())); ac->addAction(QLatin1String("image_delete_permanently"), d->imageDeletePermanentlyAction); ac->setDefaultShortcut(d->imageDeletePermanentlyAction, Qt::SHIFT+Qt::Key_Delete); // ----------------------------------------------------------- // These two actions are hidden, no menu entry, no toolbar entry, no shortcut. // Power users may add them. d->imageDeletePermanentlyDirectlyAction = new QAction(QIcon::fromTheme(QLatin1String("edit-delete")), i18n("Delete permanently without confirmation"), this); connect(d->imageDeletePermanentlyDirectlyAction, SIGNAL(triggered()), d->view, SLOT(slotImageDeletePermanentlyDirectly())); ac->addAction(QLatin1String("image_delete_permanently_directly"), d->imageDeletePermanentlyDirectlyAction); // ----------------------------------------------------------- - d->imageTrashDirectlyAction = new QAction(QIcon::fromTheme(QLatin1String("user-trash")), + d->imageTrashDirectlyAction = new QAction(QIcon::fromTheme(QLatin1String("user-trash-full")), i18n("Move to trash without confirmation"), this); connect(d->imageTrashDirectlyAction, SIGNAL(triggered()), d->view, SLOT(slotImageTrashDirectly())); ac->addAction(QLatin1String("image_trash_directly"), d->imageTrashDirectlyAction); // ----------------------------------------------------------------- d->albumSortAction = new KSelectAction(i18n("&Sort Albums"), this); d->albumSortAction->setWhatsThis(i18n("Sort Albums in tree-view.")); connect(d->albumSortAction, SIGNAL(triggered(int)), d->view, SLOT(slotSortAlbums(int))); ac->addAction(QLatin1String("album_sort"), d->albumSortAction); // Use same list order as in applicationsettings enum QStringList sortActionList; sortActionList.append(i18n("By Folder")); sortActionList.append(i18n("By Category")); sortActionList.append(i18n("By Date")); d->albumSortAction->setItems(sortActionList); // ----------------------------------------------------------- d->recurseAlbumsAction = new QAction(i18n("Include Album Sub-Tree"), this); d->recurseAlbumsAction->setCheckable(true); d->recurseAlbumsAction->setWhatsThis(i18n("Activate this option to show all sub-albums below " "the current album.")); connect(d->recurseAlbumsAction, SIGNAL(toggled(bool)), this, SLOT(slotRecurseAlbums(bool))); ac->addAction(QLatin1String("albums_recursive"), d->recurseAlbumsAction); d->recurseTagsAction = new QAction(i18n("Include Tag Sub-Tree"), this); d->recurseTagsAction->setCheckable(true); d->recurseTagsAction->setWhatsThis(i18n("Activate this option to show all images marked by the given tag " "and all its sub-tags.")); connect(d->recurseTagsAction, SIGNAL(toggled(bool)), this, SLOT(slotRecurseTags(bool))); ac->addAction(QLatin1String("tags_recursive"), d->recurseTagsAction); // ----------------------------------------------------------- d->imageSortAction = new KSelectAction(i18n("&Sort Items"), this); d->imageSortAction->setWhatsThis(i18n("The value by which the images in one album are sorted in the thumbnail view")); QSignalMapper* const imageSortMapper = new QSignalMapper(this); connect(imageSortMapper, SIGNAL(mapped(int)), d->view, SLOT(slotSortImages(int))); ac->addAction(QLatin1String("image_sort"), d->imageSortAction); // map to ItemSortSettings enum QAction* const sortByNameAction = d->imageSortAction->addAction(i18n("By Name")); QAction* const sortByPathAction = d->imageSortAction->addAction(i18n("By Path")); QAction* const sortByDateAction = d->imageSortAction->addAction(i18n("By Date")); QAction* const sortByFileSizeAction = d->imageSortAction->addAction(i18n("By File Size")); QAction* const sortByRatingAction = d->imageSortAction->addAction(i18n("By Rating")); QAction* const sortByImageSizeAction = d->imageSortAction->addAction(i18n("By Image Size")); QAction* const sortByAspectRatioAction = d->imageSortAction->addAction(i18n("By Aspect Ratio")); QAction* const sortBySimilarityAction = d->imageSortAction->addAction(i18n("By Similarity")); QAction* const sortByManualOrderAction = d->imageSortAction->addAction(i18n("By Manual")); // activate the sort by similarity if the fuzzy search sidebar is active. Deactivate at start. sortBySimilarityAction->setEnabled(false); connect(d->view, SIGNAL(signalFuzzySidebarActive(bool)), sortBySimilarityAction, SLOT(setEnabled(bool))); connect(sortByNameAction, SIGNAL(triggered()), imageSortMapper, SLOT(map())); connect(sortByPathAction, SIGNAL(triggered()), imageSortMapper, SLOT(map())); connect(sortByDateAction, SIGNAL(triggered()), imageSortMapper, SLOT(map())); connect(sortByFileSizeAction, SIGNAL(triggered()), imageSortMapper, SLOT(map())); connect(sortByRatingAction, SIGNAL(triggered()), imageSortMapper, SLOT(map())); connect(sortByImageSizeAction, SIGNAL(triggered()), imageSortMapper, SLOT(map())); connect(sortByAspectRatioAction, SIGNAL(triggered()), imageSortMapper, SLOT(map())); connect(sortBySimilarityAction, SIGNAL(triggered()), imageSortMapper, SLOT(map())); connect(sortByManualOrderAction, SIGNAL(triggered()), imageSortMapper, SLOT(map())); imageSortMapper->setMapping(sortByNameAction, (int)ItemSortSettings::SortByFileName); imageSortMapper->setMapping(sortByPathAction, (int)ItemSortSettings::SortByFilePath); imageSortMapper->setMapping(sortByDateAction, (int)ItemSortSettings::SortByCreationDate); imageSortMapper->setMapping(sortByFileSizeAction, (int)ItemSortSettings::SortByFileSize); imageSortMapper->setMapping(sortByRatingAction, (int)ItemSortSettings::SortByRating); imageSortMapper->setMapping(sortByImageSizeAction, (int)ItemSortSettings::SortByImageSize); imageSortMapper->setMapping(sortByAspectRatioAction, (int)ItemSortSettings::SortByAspectRatio); imageSortMapper->setMapping(sortBySimilarityAction, (int)ItemSortSettings::SortBySimilarity); imageSortMapper->setMapping(sortByManualOrderAction, (int)ItemSortSettings::SortByManualOrder); // ----------------------------------------------------------- d->imageSortOrderAction = new KSelectAction(i18n("Item Sort &Order"), this); d->imageSortOrderAction->setWhatsThis(i18n("Defines whether images are sorted in ascending or descending manner.")); QSignalMapper* const imageSortOrderMapper = new QSignalMapper(this); connect(imageSortOrderMapper, SIGNAL(mapped(int)), d->view, SLOT(slotSortImagesOrder(int))); ac->addAction(QLatin1String("image_sort_order"), d->imageSortOrderAction); QAction* const sortAscendingAction = d->imageSortOrderAction->addAction(QIcon::fromTheme(QLatin1String("view-sort-ascending")), i18n("Ascending")); QAction* const sortDescendingAction = d->imageSortOrderAction->addAction(QIcon::fromTheme(QLatin1String("view-sort-descending")), i18n("Descending")); connect(sortAscendingAction, SIGNAL(triggered()), imageSortOrderMapper, SLOT(map())); connect(sortDescendingAction, SIGNAL(triggered()), imageSortOrderMapper, SLOT(map())); imageSortOrderMapper->setMapping(sortAscendingAction, (int)ItemSortSettings::AscendingOrder); imageSortOrderMapper->setMapping(sortDescendingAction, (int)ItemSortSettings::DescendingOrder); // ----------------------------------------------------------- d->imageSeparationAction = new KSelectAction(i18n("Separate Items"), this); d->imageSeparationAction->setWhatsThis(i18n("The categories in which the images in the thumbnail view are displayed")); QSignalMapper* const imageSeparationMapper = new QSignalMapper(this); connect(imageSeparationMapper, SIGNAL(mapped(int)), d->view, SLOT(slotSeparateImages(int))); ac->addAction(QLatin1String("image_separation"), d->imageSeparationAction); // map to ItemSortSettings enum QAction* const noCategoriesAction = d->imageSeparationAction->addAction(i18n("Flat List")); QAction* const separateByAlbumAction = d->imageSeparationAction->addAction(i18n("By Album")); QAction* const separateByFormatAction = d->imageSeparationAction->addAction(i18n("By Format")); QAction* const separateByMonthAction = d->imageSeparationAction->addAction(i18n("By Month")); connect(noCategoriesAction, SIGNAL(triggered()), imageSeparationMapper, SLOT(map())); connect(separateByAlbumAction, SIGNAL(triggered()), imageSeparationMapper, SLOT(map())); connect(separateByFormatAction, SIGNAL(triggered()), imageSeparationMapper, SLOT(map())); connect(separateByMonthAction, SIGNAL(triggered()), imageSeparationMapper, SLOT(map())); imageSeparationMapper->setMapping(noCategoriesAction, (int)ItemSortSettings::OneCategory); imageSeparationMapper->setMapping(separateByAlbumAction, (int)ItemSortSettings::CategoryByAlbum); imageSeparationMapper->setMapping(separateByFormatAction, (int)ItemSortSettings::CategoryByFormat); imageSeparationMapper->setMapping(separateByMonthAction, (int)ItemSortSettings::CategoryByMonth); // ----------------------------------------------------------------- d->imageSeparationSortOrderAction = new KSelectAction(i18n("Item Separation Order"), this); d->imageSeparationSortOrderAction->setWhatsThis(i18n("The sort order of the groups of separated items")); QSignalMapper* const imageSeparationSortOrderMapper = new QSignalMapper(this); connect(imageSeparationSortOrderMapper, SIGNAL(mapped(int)), d->view, SLOT(slotImageSeparationSortOrder(int))); ac->addAction(QLatin1String("image_separation_sort_order"), d->imageSeparationSortOrderAction); QAction* const sortSeparationsAscending = d->imageSeparationSortOrderAction->addAction(QIcon::fromTheme(QLatin1String("view-sort-ascending")), i18n("Ascending")); QAction* const sortSeparationsDescending = d->imageSeparationSortOrderAction->addAction(QIcon::fromTheme(QLatin1String("view-sort-descending")), i18n("Descending")); connect(sortSeparationsAscending, SIGNAL(triggered()), imageSeparationSortOrderMapper, SLOT(map())); connect(sortSeparationsDescending, SIGNAL(triggered()), imageSeparationSortOrderMapper, SLOT(map())); imageSeparationSortOrderMapper->setMapping(sortSeparationsAscending, (int)ItemSortSettings::AscendingOrder); imageSeparationSortOrderMapper->setMapping(sortSeparationsDescending, (int)ItemSortSettings::DescendingOrder); // ----------------------------------------------------------------- setupImageTransformActions(); setupExifOrientationActions(); createMetadataEditAction(); createGeolocationEditAction(); createTimeAdjustAction(); createExportActions(); createImportActions(); // ----------------------------------------------------------------- d->selectAllAction = new QAction(i18n("Select All"), this); connect(d->selectAllAction, SIGNAL(triggered()), d->view, SLOT(slotSelectAll())); ac->addAction(QLatin1String("selectAll"), d->selectAllAction); ac->setDefaultShortcut(d->selectAllAction, Qt::CTRL+Qt::Key_A); // ----------------------------------------------------------------- d->selectNoneAction = new QAction(i18n("Select None"), this); connect(d->selectNoneAction, SIGNAL(triggered()), d->view, SLOT(slotSelectNone())); ac->addAction(QLatin1String("selectNone"), d->selectNoneAction); ac->setDefaultShortcut(d->selectNoneAction, Qt::CTRL+Qt::SHIFT+Qt::Key_A); // ----------------------------------------------------------------- d->selectInvertAction = new QAction(i18n("Invert Selection"), this); connect(d->selectInvertAction, SIGNAL(triggered()), d->view, SLOT(slotSelectInvert())); ac->addAction(QLatin1String("selectInvert"), d->selectInvertAction); ac->setDefaultShortcut(d->selectInvertAction, Qt::CTRL+Qt::Key_I); // ----------------------------------------------------------- d->showBarAction = new QAction(QIcon::fromTheme(QLatin1String("view-choose")), i18n("Show Thumbbar"), this); d->showBarAction->setCheckable(true); connect(d->showBarAction, SIGNAL(triggered()), this, SLOT(slotToggleShowBar())); ac->addAction(QLatin1String("showthumbs"), d->showBarAction); ac->setDefaultShortcut(d->showBarAction, Qt::CTRL+Qt::Key_T); // Provides a menu entry that allows showing/hiding the toolbar(s) setStandardToolBarMenuEnabled(true); // Provides a menu entry that allows showing/hiding the statusbar createStandardStatusBarAction(); // Standard 'Configure' menu actions createSettingsActions(); // ----------------------------------------------------------- d->zoomPlusAction = buildStdAction(StdZoomInAction, d->view, SLOT(slotZoomIn()), this); ac->addAction(QLatin1String("album_zoomin"), d->zoomPlusAction); // ----------------------------------------------------------- d->zoomMinusAction = buildStdAction(StdZoomOutAction, d->view, SLOT(slotZoomOut()), this); ac->addAction(QLatin1String("album_zoomout"), d->zoomMinusAction); // ----------------------------------------------------------- d->zoomTo100percents = new QAction(QIcon::fromTheme(QLatin1String("zoom-original")), i18n("Zoom to 100%"), this); connect(d->zoomTo100percents, SIGNAL(triggered()), d->view, SLOT(slotZoomTo100Percents())); ac->addAction(QLatin1String("album_zoomto100percents"), d->zoomTo100percents); ac->setDefaultShortcut(d->zoomTo100percents, Qt::CTRL + Qt::Key_Period); // ----------------------------------------------------------- d->zoomFitToWindowAction = new QAction(QIcon::fromTheme(QLatin1String("zoom-fit-best")), i18n("Fit to &Window"), this); connect(d->zoomFitToWindowAction, SIGNAL(triggered()), d->view, SLOT(slotFitToWindow())); ac->addAction(QLatin1String("album_zoomfit2window"), d->zoomFitToWindowAction); ac->setDefaultShortcut(d->zoomFitToWindowAction, Qt::ALT + Qt::CTRL + Qt::Key_E); // ----------------------------------------------------------- createFullScreenAction(QLatin1String("full_screen")); createSidebarActions(); // ----------------------------------------------------------- d->slideShowAction = new QMenu(i18n("Slideshow"), this); d->slideShowAction->setIcon(QIcon::fromTheme(QLatin1String("view-presentation"))); ac->addAction(QLatin1String("slideshow"), d->slideShowAction->menuAction()); d->slideShowAllAction = new QAction(i18n("All"), this); connect(d->slideShowAllAction, SIGNAL(triggered()), d->view, SLOT(slotSlideShowAll())); ac->addAction(QLatin1String("slideshow_all"), d->slideShowAllAction); ac->setDefaultShortcut(d->slideShowAllAction, Qt::Key_F9); d->slideShowAction->addAction(d->slideShowAllAction); d->slideShowSelectionAction = new QAction(i18n("Selection"), this); connect(d->slideShowSelectionAction, SIGNAL(triggered()), d->view, SLOT(slotSlideShowSelection())); ac->addAction(QLatin1String("slideshow_selected"), d->slideShowSelectionAction); ac->setDefaultShortcut(d->slideShowSelectionAction, Qt::ALT+Qt::Key_F9); d->slideShowAction->addAction(d->slideShowSelectionAction); d->slideShowRecursiveAction = new QAction(i18n("With All Sub-Albums"), this); connect(d->slideShowRecursiveAction, SIGNAL(triggered()), d->view, SLOT(slotSlideShowRecursive())); ac->addAction(QLatin1String("slideshow_recursive"), d->slideShowRecursiveAction); ac->setDefaultShortcut(d->slideShowRecursiveAction, Qt::SHIFT+Qt::Key_F9); d->slideShowAction->addAction(d->slideShowRecursiveAction); createPresentationAction(); // ----------------------------------------------------------- d->viewCMViewAction = new QAction(QIcon::fromTheme(QLatin1String("video-display")), i18n("Color-Managed View"), this); d->viewCMViewAction->setCheckable(true); connect(d->viewCMViewAction, SIGNAL(triggered()), this, SLOT(slotToggleColorManagedView())); ac->addAction(QLatin1String("color_managed_view"), d->viewCMViewAction); ac->setDefaultShortcut(d->viewCMViewAction, Qt::Key_F12); // ----------------------------------------------------------- d->quitAction = buildStdAction(StdQuitAction, this, SLOT(slotExit()), this); ac->addAction(QLatin1String("app_exit"), d->quitAction); // ----------------------------------------------------------- createHelpActions(); // ----------------------------------------------------------- QAction* const findAction = new QAction(QIcon::fromTheme(QLatin1String("edit-find")), i18n("Search..."), this); connect(findAction, SIGNAL(triggered()), d->view, SLOT(slotNewKeywordSearch())); ac->addAction(QLatin1String("search_quick"), findAction); ac->setDefaultShortcut(findAction, Qt::CTRL+Qt::Key_F); // ----------------------------------------------------------- d->advSearchAction = new QAction(QIcon::fromTheme(QLatin1String("edit-find")), i18n("Advanced Search..."), this); connect(d->advSearchAction, SIGNAL(triggered()), d->view, SLOT(slotNewAdvancedSearch())); ac->addAction(QLatin1String("search_advanced"), d->advSearchAction); ac->setDefaultShortcut(d->advSearchAction, Qt::CTRL+Qt::ALT+Qt::Key_F); // ----------------------------------------------------------- QAction* const duplicatesAction = new QAction(QIcon::fromTheme(QLatin1String("tools-wizard")), i18n("Find Duplicates..."), this); connect(duplicatesAction, SIGNAL(triggered()), d->view, SLOT(slotNewDuplicatesSearch())); ac->addAction(QLatin1String("find_duplicates"), duplicatesAction); ac->setDefaultShortcut(duplicatesAction, Qt::CTRL+Qt::Key_D); // ----------------------------------------------------------- #ifdef HAVE_MYSQLSUPPORT QAction* const databaseMigrationAction = new QAction(QIcon::fromTheme(QLatin1String("network-server-database")), i18n("Database Migration..."), this); connect(databaseMigrationAction, SIGNAL(triggered()), this, SLOT(slotDatabaseMigration())); ac->addAction(QLatin1String("database_migration"), databaseMigrationAction); #endif // ----------------------------------------------------------- d->maintenanceAction = new QAction(QIcon::fromTheme(QLatin1String("run-build-prune")), i18n("Maintenance..."), this); connect(d->maintenanceAction, SIGNAL(triggered()), this, SLOT(slotMaintenance())); ac->addAction(QLatin1String("maintenance"), d->maintenanceAction); createExpoBlendingAction(); createPanoramaAction(); createHtmlGalleryAction(); createCalendarAction(); createVideoSlideshowAction(); createSendByMailAction(); createPrintCreatorAction(); createMediaServerAction(); // ----------------------------------------------------------- QAction* const cameraAction = new QAction(i18n("Add Camera Manually..."), this); connect(cameraAction, SIGNAL(triggered()), this, SLOT(slotSetupCamera())); ac->addAction(QLatin1String("camera_add"), cameraAction); // ----------------------------------------------------------- // Load Cameras -- do this before the createGUI so that the cameras // are plugged into the toolbar at startup if (d->splashScreen) { d->splashScreen->setMessage(i18n("Loading cameras...")); } loadCameras(); // Load Themes populateThemes(); createGUI(xmlFile()); cleanupActions(); // NOTE: see bug #252130 and #283281 : we need to disable these actions when BQM is running. // These connections must be done after loading color theme else theme menu cannot be plugged to Settings menu, connect(QueueMgrWindow::queueManagerWindow(), SIGNAL(signalBqmIsBusy(bool)), d->bqmAction, SLOT(setDisabled(bool))); connect(QueueMgrWindow::queueManagerWindow(), SIGNAL(signalBqmIsBusy(bool)), d->imageAddCurrentQueueAction, SLOT(setDisabled(bool))); connect(QueueMgrWindow::queueManagerWindow(), SIGNAL(signalBqmIsBusy(bool)), d->imageAddNewQueueAction, SLOT(setDisabled(bool))); } void DigikamApp::setupAccelerators() { KActionCollection* const ac = actionCollection(); // Action are added by tag in ui.rc XML file QAction* const escapeAction = new QAction(i18n("Exit Preview Mode"), this); ac->addAction(QLatin1String("exit_preview_mode"), escapeAction); ac->setDefaultShortcut(escapeAction, Qt::Key_Escape); connect(escapeAction, SIGNAL(triggered()), this, SIGNAL(signalEscapePressed())); QAction* const nextImageAction = new QAction(i18n("Next Image"), this); nextImageAction->setIcon(QIcon::fromTheme(QLatin1String("go-next"))); ac->addAction(QLatin1String("next_image"), nextImageAction); ac->setDefaultShortcut(nextImageAction, Qt::Key_Space); connect(nextImageAction, SIGNAL(triggered()), this, SIGNAL(signalNextItem())); QAction* const previousImageAction = new QAction(i18n("Previous Image"), this); previousImageAction->setIcon(QIcon::fromTheme(QLatin1String("go-previous"))); ac->addAction(QLatin1String("previous_image"), previousImageAction); ac->setDefaultShortcuts(previousImageAction, QList() << Qt::Key_Backspace << Qt::SHIFT+Qt::Key_Space); connect(previousImageAction, SIGNAL(triggered()), this, SIGNAL(signalPrevItem())); QAction* const firstImageAction = new QAction(i18n("First Image"), this); ac->addAction(QLatin1String("first_image"), firstImageAction); ac->setDefaultShortcuts(firstImageAction, QList() << Qt::CTRL + Qt::Key_Home); connect(firstImageAction, SIGNAL(triggered()), this, SIGNAL(signalFirstItem())); QAction* const lastImageAction = new QAction(i18n("Last Image"), this); ac->addAction(QLatin1String("last_image"), lastImageAction); ac->setDefaultShortcuts(lastImageAction, QList() << Qt::CTRL + Qt::Key_End); connect(lastImageAction, SIGNAL(triggered()), this, SIGNAL(signalLastItem())); d->cutItemsAction = new QAction(i18n("Cu&t"), this); d->cutItemsAction->setIcon(QIcon::fromTheme(QLatin1String("edit-cut"))); d->cutItemsAction->setWhatsThis(i18n("Cut selection to clipboard")); ac->addAction(QLatin1String("cut_album_selection"), d->cutItemsAction); // NOTE: shift+del keyboard shortcut must not be assigned to Cut action // else the shortcut for Delete permanently collides with secondary shortcut of Cut ac->setDefaultShortcut(d->cutItemsAction, Qt::CTRL + Qt::Key_X); connect(d->cutItemsAction, SIGNAL(triggered()), this, SIGNAL(signalCutAlbumItemsSelection())); d->copyItemsAction = buildStdAction(StdCopyAction, this, SIGNAL(signalCopyAlbumItemsSelection()), this); ac->addAction(QLatin1String("copy_album_selection"), d->copyItemsAction); d->pasteItemsAction = buildStdAction(StdPasteAction, this, SIGNAL(signalPasteAlbumItemsSelection()), this); ac->addAction(QLatin1String("paste_album_selection"), d->pasteItemsAction); // Labels shortcuts must be registered here to be saved in XML GUI files if user customize it. d->tagsActionManager->registerLabelsActions(ac); QAction* const editTitles = new QAction(i18n("Edit Titles"), this); ac->addAction(QLatin1String("edit_titles"), editTitles); ac->setDefaultShortcut(editTitles, Qt::META + Qt::Key_T); connect(editTitles, SIGNAL(triggered()), d->view, SLOT(slotRightSideBarActivateTitles())); QAction* const editComments = new QAction(i18n("Edit Comments"), this); ac->addAction(QLatin1String("edit_comments"), editComments); ac->setDefaultShortcut(editComments, Qt::META + Qt::Key_C); connect(editComments, SIGNAL(triggered()), d->view, SLOT(slotRightSideBarActivateComments())); QAction* const assignedTags = new QAction(i18n("Show Assigned Tags"), this); ac->addAction(QLatin1String("assigned _tags"), assignedTags); ac->setDefaultShortcut(assignedTags, Qt::META + Qt::Key_A); connect(assignedTags, SIGNAL(triggered()), d->view, SLOT(slotRightSideBarActivateAssignedTags())); } void DigikamApp::setupExifOrientationActions() { KActionCollection* const ac = actionCollection(); QSignalMapper* const exifOrientationMapper = new QSignalMapper(d->view); connect(exifOrientationMapper, SIGNAL(mapped(int)), d->view, SLOT(slotImageExifOrientation(int))); d->imageExifOrientationActionMenu = new QMenu(i18n("Adjust Exif Orientation Tag"), this); ac->addAction(QLatin1String("image_set_exif_orientation"), d->imageExifOrientationActionMenu->menuAction()); d->imageSetExifOrientation1Action = new QAction(i18nc("normal exif orientation", "Normal"), this); d->imageSetExifOrientation1Action->setCheckable(true); d->imageSetExifOrientation2Action = new QAction(i18n("Flipped Horizontally"), this); d->imageSetExifOrientation2Action->setCheckable(true); d->imageSetExifOrientation3Action = new QAction(i18n("Rotated Upside Down"), this); d->imageSetExifOrientation3Action->setCheckable(true); d->imageSetExifOrientation4Action = new QAction(i18n("Flipped Vertically"), this); d->imageSetExifOrientation4Action->setCheckable(true); d->imageSetExifOrientation5Action = new QAction(i18n("Rotated Right / Horiz. Flipped"), this); d->imageSetExifOrientation5Action->setCheckable(true); d->imageSetExifOrientation6Action = new QAction(i18n("Rotated Right"), this); d->imageSetExifOrientation6Action->setCheckable(true); d->imageSetExifOrientation7Action = new QAction(i18n("Rotated Right / Vert. Flipped"), this); d->imageSetExifOrientation7Action->setCheckable(true); d->imageSetExifOrientation8Action = new QAction(i18n("Rotated Left"), this); d->imageSetExifOrientation8Action->setCheckable(true); d->exifOrientationActionGroup = new QActionGroup(d->imageExifOrientationActionMenu); d->exifOrientationActionGroup->addAction(d->imageSetExifOrientation1Action); d->exifOrientationActionGroup->addAction(d->imageSetExifOrientation2Action); d->exifOrientationActionGroup->addAction(d->imageSetExifOrientation3Action); d->exifOrientationActionGroup->addAction(d->imageSetExifOrientation4Action); d->exifOrientationActionGroup->addAction(d->imageSetExifOrientation5Action); d->exifOrientationActionGroup->addAction(d->imageSetExifOrientation6Action); d->exifOrientationActionGroup->addAction(d->imageSetExifOrientation7Action); d->exifOrientationActionGroup->addAction(d->imageSetExifOrientation8Action); d->imageSetExifOrientation1Action->setChecked(true); ac->addAction(QLatin1String("image_set_exif_orientation_normal"), d->imageSetExifOrientation1Action); ac->addAction(QLatin1String("image_set_exif_orientation_flipped_horizontal"), d->imageSetExifOrientation2Action); ac->addAction(QLatin1String("image_set_exif_orientation_rotated_upside_down"), d->imageSetExifOrientation3Action); ac->addAction(QLatin1String("image_set_exif_orientation_flipped_vertically"), d->imageSetExifOrientation4Action); ac->addAction(QLatin1String("image_set_exif_orientation_rotated_right_hor_flipped"), d->imageSetExifOrientation5Action); ac->addAction(QLatin1String("image_set_exif_orientation_rotated_right"), d->imageSetExifOrientation6Action); ac->addAction(QLatin1String("image_set_exif_orientation_rotated_right_ver_flipped"), d->imageSetExifOrientation7Action); ac->addAction(QLatin1String("image_set_exif_orientation_rotated_left"), d->imageSetExifOrientation8Action); d->imageExifOrientationActionMenu->addAction(d->imageSetExifOrientation1Action); d->imageExifOrientationActionMenu->addAction(d->imageSetExifOrientation2Action); d->imageExifOrientationActionMenu->addAction(d->imageSetExifOrientation3Action); d->imageExifOrientationActionMenu->addAction(d->imageSetExifOrientation4Action); d->imageExifOrientationActionMenu->addAction(d->imageSetExifOrientation5Action); d->imageExifOrientationActionMenu->addAction(d->imageSetExifOrientation6Action); d->imageExifOrientationActionMenu->addAction(d->imageSetExifOrientation7Action); d->imageExifOrientationActionMenu->addAction(d->imageSetExifOrientation8Action); connect(d->imageSetExifOrientation1Action, SIGNAL(triggered()), exifOrientationMapper, SLOT(map())); connect(d->imageSetExifOrientation2Action, SIGNAL(triggered()), exifOrientationMapper, SLOT(map())); connect(d->imageSetExifOrientation3Action, SIGNAL(triggered()), exifOrientationMapper, SLOT(map())); connect(d->imageSetExifOrientation4Action, SIGNAL(triggered()), exifOrientationMapper, SLOT(map())); connect(d->imageSetExifOrientation5Action, SIGNAL(triggered()), exifOrientationMapper, SLOT(map())); connect(d->imageSetExifOrientation6Action, SIGNAL(triggered()), exifOrientationMapper, SLOT(map())); connect(d->imageSetExifOrientation7Action, SIGNAL(triggered()), exifOrientationMapper, SLOT(map())); connect(d->imageSetExifOrientation8Action, SIGNAL(triggered()), exifOrientationMapper, SLOT(map())); exifOrientationMapper->setMapping(d->imageSetExifOrientation1Action, 1); exifOrientationMapper->setMapping(d->imageSetExifOrientation2Action, 2); exifOrientationMapper->setMapping(d->imageSetExifOrientation3Action, 3); exifOrientationMapper->setMapping(d->imageSetExifOrientation4Action, 4); exifOrientationMapper->setMapping(d->imageSetExifOrientation5Action, 5); exifOrientationMapper->setMapping(d->imageSetExifOrientation6Action, 6); exifOrientationMapper->setMapping(d->imageSetExifOrientation7Action, 7); exifOrientationMapper->setMapping(d->imageSetExifOrientation8Action, 8); } void DigikamApp::setupImageTransformActions() { KActionCollection* const ac = actionCollection(); d->imageRotateActionMenu = new QMenu(i18n("Rotate"), this); d->imageRotateActionMenu->setIcon(QIcon::fromTheme(QLatin1String("object-rotate-right"))); QAction* const left = ac->addAction(QLatin1String("rotate_ccw")); left->setText(i18nc("rotate image left", "Left")); ac->setDefaultShortcut(left, Qt::SHIFT+Qt::CTRL+Qt::Key_Left); connect(left, SIGNAL(triggered(bool)), this, SLOT(slotTransformAction())); d->imageRotateActionMenu->addAction(left); QAction* const right = ac->addAction(QLatin1String("rotate_cw")); right->setText(i18nc("rotate image right", "Right")); ac->setDefaultShortcut(right, Qt::SHIFT+Qt::CTRL+Qt::Key_Right); connect(right, SIGNAL(triggered(bool)), this, SLOT(slotTransformAction())); d->imageRotateActionMenu->addAction(right); ac->addAction(QLatin1String("image_rotate"), d->imageRotateActionMenu->menuAction()); // ----------------------------------------------------------------------------------- d->imageFlipActionMenu = new QMenu(i18n("Flip"), this); d->imageFlipActionMenu->setIcon(QIcon::fromTheme(QLatin1String("flip-horizontal"))); QAction* const hori = ac->addAction(QLatin1String("flip_horizontal")); hori->setText(i18n("Horizontally")); ac->setDefaultShortcut(hori, Qt::CTRL+Qt::Key_Asterisk); connect(hori, SIGNAL(triggered(bool)), this, SLOT(slotTransformAction())); d->imageFlipActionMenu->addAction(hori); QAction* const verti = ac->addAction(QLatin1String("flip_vertical")); verti->setText(i18n("Vertically")); ac->setDefaultShortcut(verti, Qt::CTRL+Qt::Key_Slash); connect(verti, SIGNAL(triggered(bool)), this, SLOT(slotTransformAction())); d->imageFlipActionMenu->addAction(verti); ac->addAction(QLatin1String("image_flip"), d->imageFlipActionMenu->menuAction()); // ----------------------------------------------------------------------------------- d->imageAutoExifActionMenu = new QAction(i18n("Auto Rotate/Flip Using Exif Information"), this); connect(d->imageAutoExifActionMenu, SIGNAL(triggered(bool)), this, SLOT(slotTransformAction())); ac->addAction(QLatin1String("image_transform_exif"), d->imageAutoExifActionMenu); } void DigikamApp::populateThemes() { if (d->splashScreen) { d->splashScreen->setMessage(i18n("Loading themes...")); } ThemeManager::instance()->setThemeMenuAction(new QMenu(i18n("&Themes"), this)); ThemeManager::instance()->registerThemeActions(this); ThemeManager::instance()->setCurrentTheme(ApplicationSettings::instance()->getCurrentTheme()); connect(ThemeManager::instance(), SIGNAL(signalThemeChanged()), this, SLOT(slotThemeChanged())); } void DigikamApp::preloadWindows() { if (d->splashScreen) { d->splashScreen->setMessage(i18n("Loading tools...")); } QueueMgrWindow::queueManagerWindow(); ImageWindow::imageWindow(); LightTableWindow::lightTableWindow(); d->tagsActionManager->registerTagsActionCollections(); } void DigikamApp::initGui() { // Initialize Actions --------------------------------------- d->deleteAction->setEnabled(false); d->renameAction->setEnabled(false); d->addImagesAction->setEnabled(false); d->propsEditAction->setEnabled(false); d->openInFileManagerAction->setEnabled(false); d->imageViewAction->setEnabled(false); d->imagePreviewAction->setEnabled(false); d->imageLightTableAction->setEnabled(false); d->imageAddLightTableAction->setEnabled(false); d->imageScanForFacesAction->setEnabled(false); d->imageFindSimilarAction->setEnabled(false); d->imageRenameAction->setEnabled(false); d->imageDeleteAction->setEnabled(false); d->imageExifOrientationActionMenu->setEnabled(false); d->openWithAction->setEnabled(false); d->slideShowSelectionAction->setEnabled(false); m_metadataEditAction->setEnabled(false); m_timeAdjustAction->setEnabled(false); d->imageAutoExifActionMenu->setEnabled(false); #ifdef HAVE_MARBLE m_geolocationEditAction->setEnabled(false); #endif d->albumSortAction->setCurrentItem((int)ApplicationSettings::instance()->getAlbumSortRole()); d->imageSortAction->setCurrentItem((int)ApplicationSettings::instance()->getImageSortOrder()); d->imageSortOrderAction->setCurrentItem((int)ApplicationSettings::instance()->getImageSorting()); d->imageSeparationAction->setCurrentItem((int)ApplicationSettings::instance()->getImageSeparationMode()-1); // no action for enum 0 d->imageSeparationSortOrderAction->setCurrentItem((int)ApplicationSettings::instance()->getImageSeparationSortOrder()); d->recurseAlbumsAction->setChecked(ApplicationSettings::instance()->getRecurseAlbums()); d->recurseTagsAction->setChecked(ApplicationSettings::instance()->getRecurseTags()); d->showBarAction->setChecked(ApplicationSettings::instance()->getShowThumbbar()); showMenuBarAction()->setChecked(!menuBar()->isHidden()); // NOTE: workaround for bug #171080 slotSwitchedToIconView(); } } // namespace Digikam diff --git a/core/libs/tags/manager/models/tagmngrlistview.cpp b/core/libs/tags/manager/models/tagmngrlistview.cpp index e6c003001d..63323cd6e4 100644 --- a/core/libs/tags/manager/models/tagmngrlistview.cpp +++ b/core/libs/tags/manager/models/tagmngrlistview.cpp @@ -1,182 +1,182 @@ /* ============================================================ * * This file is a part of digiKam project * http://www.digikam.org * * Date : 20013-08-22 * Description : Reimplemented QListView for Tags Manager, with support for * drag-n-drop * * Copyright (C) 2013 by Veaceslav Munteanu * * This program is free software; you can redistribute it * and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation; * either version 2, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * ============================================================ */ #include "tagmngrlistview.h" // Qt includes #include #include #include #include #include #include #include // KDE includes #include // Local includes #include "digikam_debug.h" #include "contextmenuhelper.h" #include "tagmngrlistmodel.h" #include "tagmngrlistitem.h" #include "taglist.h" namespace Digikam { TagMngrListView::TagMngrListView(QWidget* const parent) : QTreeView(parent) { setRootIsDecorated(false); setAlternatingRowColors(true); } void TagMngrListView::startDrag(Qt::DropActions supportedActions) { QModelIndexList list = selectionModel()->selectedIndexes(); TagMngrListModel* const tagmodel = dynamic_cast(model()); if (!tagmodel) { qCDebug(DIGIKAM_GENERAL_LOG) << "Error! no model available!"; return; } QMimeData* const data = tagmodel->mimeData(list); if (!data) { qCDebug(DIGIKAM_GENERAL_LOG) << "Error! no data obtained!"; return; } QDrag* const drag = new QDrag(this); drag->setMimeData(data); drag->exec(supportedActions, Qt::IgnoreAction); } QModelIndexList TagMngrListView::mySelectedIndexes() { return selectedIndexes(); } void TagMngrListView::dropEvent(QDropEvent* e) { QModelIndex index = indexVisuallyAt(e->pos()); TagMngrListModel* const tagmodel = dynamic_cast(model()); if (!tagmodel) { qCDebug(DIGIKAM_GENERAL_LOG) << "Error! no model available!"; return; } tagmodel->dropMimeData(e->mimeData(), e->dropAction(), index.row(), index.column(), index.parent()); QList toSel = tagmodel->getDragNewSelection(); if (toSel.size() != 2) { return; } QItemSelectionModel* const model = selectionModel(); model->clearSelection(); setCurrentIndex(tagmodel->index(toSel.first()+1, 0)); for (int it = toSel.first()+1 ; it <= toSel.last() ; ++it) { model->select(tagmodel->index(it, 0), model->Select); } } QModelIndex TagMngrListView::indexVisuallyAt(const QPoint& p) { if (viewport()->rect().contains(p)) { QModelIndex index = indexAt(p); if (index.isValid() && visualRect(index).contains(p)) { return index; } } return QModelIndex(); } void TagMngrListView::contextMenuEvent(QContextMenuEvent* event) { Q_UNUSED(event); QMenu popmenu(this); ContextMenuHelper cmhelper(&popmenu); TagList* const tagList = dynamic_cast(parent()); if (!tagList) { return; } - QAction* const delAction = new QAction(QIcon::fromTheme(QLatin1String("user-trash")), + QAction* const delAction = new QAction(QIcon::fromTheme(QLatin1String("edit-delete")), i18n("Delete Selected from List"), this); cmhelper.addAction(delAction, tagList, SLOT(slotDeleteSelected()), false); QModelIndexList sel = selectionModel()->selectedIndexes(); if (sel.size() == 1 && sel.first().row() == 0) delAction->setDisabled(true); cmhelper.exec(QCursor::pos()); } void TagMngrListView::slotDeleteSelected() { QModelIndexList sel = selectionModel()->selectedIndexes(); if (sel.isEmpty()) return; TagMngrListModel* const tagmodel = dynamic_cast(model()); if (!tagmodel) { qCDebug(DIGIKAM_GENERAL_LOG) << "Error! no model available!"; return; } foreach(const QModelIndex& index, sel) { ListItem* const item = static_cast(index.internalPointer()); tagmodel->deleteItem(item); } } } // namespace Digikam diff --git a/core/libs/tags/manager/tagmngrtreeview.cpp b/core/libs/tags/manager/tagmngrtreeview.cpp index c0077b8465..625b84e0e3 100644 --- a/core/libs/tags/manager/tagmngrtreeview.cpp +++ b/core/libs/tags/manager/tagmngrtreeview.cpp @@ -1,241 +1,241 @@ /* ============================================================ * * This file is a part of digiKam project * http://www.digikam.org * * Date : 20013-08-05 * Description : Tag Manager Tree View derived from TagsFolderView to implement * a custom context menu and some batch view options, such as * expanding multiple items * * Copyright (C) 2013 by Veaceslav Munteanu * * This program is free software; you can redistribute it * and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation; * either version 2, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * ============================================================ */ #include "tagmngrtreeview.h" // Qt includes #include #include #include #include // Local includes #include "digikam_debug.h" #include "contextmenuhelper.h" #include "tagsmanager.h" namespace Digikam { class Q_DECL_HIDDEN TagMngrTreeView::Private { public: explicit Private() { tagMngr = 0; } TagsManager* tagMngr; }; TagMngrTreeView::TagMngrTreeView(TagsManager* const parent, TagModel* const model) : TagFolderView(parent, model), d(new Private()) { d->tagMngr = parent; setAlbumFilterModel(new TagsManagerFilterModel(this), albumFilterModel()); setSelectAlbumOnClick(false); expand(albumFilterModel()->rootAlbumIndex()); } TagMngrTreeView::~TagMngrTreeView() { delete d; } void TagMngrTreeView::contextMenuEvent(QContextMenuEvent* event) { QModelIndexList selectedItems = selectionModel()->selectedIndexes(); std::sort(selectedItems.begin(), selectedItems.end()); QList items; foreach(const QModelIndex& mIndex, selectedItems) { TAlbum* const temp = static_cast(albumForIndex(mIndex)); items.append(temp); } /** * Append root tag if no nodes are selected */ if (items.isEmpty()) { QModelIndex root = model()->index(0, 0); items.append(static_cast(albumForIndex(root))); } QMenu popmenu(this); popmenu.addSection(contextMenuIcon(), contextMenuTitle()); ContextMenuHelper cmhelper(&popmenu); setContexMenuItems(cmhelper, items); QAction* const choice = cmhelper.exec(QCursor::pos()); Q_UNUSED(choice); Q_UNUSED(event); } void TagMngrTreeView::setAlbumFilterModel(TagsManagerFilterModel* const filteredModel, CheckableAlbumFilterModel* const filterModel) { Q_UNUSED(filterModel); m_tfilteredModel = filteredModel; albumFilterModel()->setSourceFilterModel(m_tfilteredModel); } void TagMngrTreeView::setContexMenuItems(ContextMenuHelper& cmh, const QList& albums) { bool isRoot = false; if (albums.size() == 1) { TAlbum* const tag = dynamic_cast (albums.first()); if (!tag) { return; } if (tag->isRoot()) { isRoot = true; } cmh.addActionNewTag(tagModificationHelper(), tag); } if (!isRoot) { cmh.addActionDeleteTags(tagModificationHelper(), albums); } else { /** This is a dummy action, delete is disable for root tag **/ - QAction* deleteTagsAction = new QAction(QIcon::fromTheme(QLatin1String("user-trash")), + QAction* deleteTagsAction = new QAction(QIcon::fromTheme(QLatin1String("edit-delete")), i18n("Delete Tags"), this); cmh.addAction(deleteTagsAction); deleteTagsAction->setEnabled(false); } cmh.addSeparator(); QAction* const titleEdit = new QAction(QIcon::fromTheme(QLatin1String("document-edit")), i18n("Edit Tag Title"), this); titleEdit->setShortcut(QKeySequence(Qt::Key_F2)); QAction* const resetIcon = new QAction(QIcon::fromTheme(QLatin1String("view-refresh")), i18n("Reset Tag Icon"), this); QAction* const invSel = new QAction(QIcon::fromTheme(QLatin1String("tag-reset")), i18n("Invert Selection"), this); QAction* const expandTree = new QAction(QIcon::fromTheme(QLatin1String("format-indent-more")), i18n("Expand Tag Tree"), this); QAction* const expandSel = new QAction(QIcon::fromTheme(QLatin1String("format-indent-more")), i18n("Expand Selected Nodes"), this); QAction* const delTagFromImg = new QAction(QIcon::fromTheme(QLatin1String("tag-delete")), i18n("Remove Tag from Images"), this); cmh.addAction(titleEdit, d->tagMngr, SLOT(slotEditTagTitle()), false); cmh.addAction(resetIcon, d->tagMngr, SLOT(slotResetTagIcon()), false); cmh.addAction(invSel, d->tagMngr, SLOT(slotInvertSel()), false); cmh.addAction(expandTree, this, SLOT(slotExpandTree()), false); cmh.addAction(expandSel, this , SLOT(slotExpandSelected()), false); cmh.addAction(delTagFromImg, d->tagMngr, SLOT(slotRemoveTagsFromImgs()), false); if (isRoot) { titleEdit->setEnabled(false); resetIcon->setEnabled(false); delTagFromImg->setEnabled(false); } if (albums.size() != 1) { titleEdit->setEnabled(false); } } void TagMngrTreeView::slotExpandSelected() { QModelIndexList list = selectionModel()->selectedIndexes(); foreach(const QModelIndex& index, list) { expand(index); } } void TagMngrTreeView::slotExpandTree() { QModelIndex root = model()->index(0, 0); QItemSelectionModel* const model = selectionModel(); QModelIndexList selected = model->selectedIndexes(); QQueue greyNodes; greyNodes.append(root); while (!greyNodes.isEmpty()) { QModelIndex current = greyNodes.dequeue(); if (!current.isValid()) { continue; } if (isExpanded(current)) { int it = 0; QModelIndex child = current.child(it++, 0); while (child.isValid()) { if (isExpanded(child)) { greyNodes.enqueue(child); } else { expand(child); } child = current.child(it++, 0); } } else { expand(current); } } } } // namespace Digikam diff --git a/core/utilities/imageeditor/editor/editorwindow.cpp b/core/utilities/imageeditor/editor/editorwindow.cpp index 2bf89afccb..b6b61972a6 100644 --- a/core/utilities/imageeditor/editor/editorwindow.cpp +++ b/core/utilities/imageeditor/editor/editorwindow.cpp @@ -1,3632 +1,3632 @@ /* ============================================================ * * This file is a part of digiKam project * http://www.digikam.org * * Date : 2006-01-20 * Description : core image editor GUI implementation * * Copyright (C) 2006-2019 by Gilles Caulier * Copyright (C) 2009-2011 by Andi Clemens * Copyright (C) 2015 by Mohamed_Anwer * * This program is free software; you can redistribute it * and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation; * either version 2, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * ============================================================ */ #include "editorwindow.h" #include "editorwindow_p.h" // C++ includes #include // Qt includes #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // KDE includes #include #include #include #include #include #include #include #include #include #ifdef HAVE_KIO # include #endif // Local includes #include "digikam_debug.h" #include "digikam_globals.h" #include "dmessagebox.h" #include "applicationsettings.h" #include "actioncategorizedview.h" #include "canvas.h" #include "categorizeditemmodel.h" #include "colorcorrectiondlg.h" #include "editorcore.h" #include "dlogoaction.h" #include "dmetadata.h" #include "dzoombar.h" #include "drawdecoderwidget.h" #include "editorstackview.h" #include "editortool.h" #include "editortoolsettings.h" #include "editortooliface.h" #include "exposurecontainer.h" #include "dfileoperations.h" #include "filereadwritelock.h" #include "filesaveoptionsbox.h" #include "filesaveoptionsdlg.h" #include "iccpostloadingmanager.h" #include "iccsettings.h" #include "iccsettingscontainer.h" #include "icctransform.h" #include "imagedialog.h" #include "iofilesettings.h" #include "metaenginesettings.h" #include "libsinfodlg.h" #include "loadingcacheinterface.h" #include "printhelper.h" #include "jpegsettings.h" #include "pngsettings.h" #include "savingcontext.h" #include "sidebar.h" #include "slideshowsettings.h" #include "softproofdialog.h" #include "statusprogressbar.h" #include "thememanager.h" #include "thumbnailsize.h" #include "thumbnailloadthread.h" #include "versioningpromptusersavedlg.h" #include "undostate.h" #include "versionmanager.h" #include "dexpanderbox.h" #include "inserttexttool.h" #include "bordertool.h" #include "texturetool.h" #include "colorfxtool.h" #include "charcoaltool.h" #include "embosstool.h" #include "oilpainttool.h" #include "blurfxtool.h" #include "distortionfxtool.h" #include "raindroptool.h" #include "filmgraintool.h" #include "invertfilter.h" #include "imageiface.h" #include "iccprofilescombobox.h" #include "autocorrectiontool.h" #include "bcgtool.h" #include "bwsepiatool.h" #include "hsltool.h" #include "profileconversiontool.h" #include "cbtool.h" #include "whitebalancetool.h" #include "channelmixertool.h" #include "adjustcurvestool.h" #include "adjustlevelstool.h" #include "filmtool.h" #include "restorationtool.h" #include "blurtool.h" #include "healingclonetool.h" #include "sharpentool.h" #include "noisereductiontool.h" #include "localcontrasttool.h" #include "redeyetool.h" #include "antivignettingtool.h" #include "lensdistortiontool.h" #include "hotpixelstool.h" #include "perspectivetool.h" #include "freerotationtool.h" #include "sheartool.h" #include "resizetool.h" #include "ratiocroptool.h" #include "dfiledialog.h" #ifdef HAVE_LIBLQR_1 # include "contentawareresizetool.h" #endif #ifdef HAVE_LENSFUN # include "lensautofixtool.h" #endif namespace Digikam { EditorWindow::EditorWindow(const QString& name) : DXmlGuiWindow(0), d(new Private) { setConfigGroupName(QLatin1String("ImageViewer Settings")); setObjectName(name); setWindowFlags(Qt::Window); setFullScreenOptions(FS_EDITOR); m_nonDestructive = true; m_contextMenu = 0; m_servicesMenu = 0; m_serviceAction = 0; m_canvas = 0; m_openVersionAction = 0; m_saveAction = 0; m_saveAsAction = 0; m_saveCurrentVersionAction = 0; m_saveNewVersionAction = 0; m_saveNewVersionAsAction = 0; m_saveNewVersionInFormatAction = 0; m_resLabel = 0; m_nameLabel = 0; m_exportAction = 0; m_revertAction = 0; m_discardChangesAction = 0; m_fileDeleteAction = 0; m_forwardAction = 0; m_backwardAction = 0; m_firstAction = 0; m_lastAction = 0; m_applyToolAction = 0; m_closeToolAction = 0; m_undoAction = 0; m_redoAction = 0; m_showBarAction = 0; m_splitter = 0; m_vSplitter = 0; m_stackView = 0; m_setExifOrientationTag = true; m_editingOriginalImage = true; m_actionEnabledState = false; m_cancelSlideShow = false; // Settings containers instance. d->exposureSettings = new ExposureSettingsContainer(); d->toolIface = new EditorToolIface(this); m_IOFileSettings = new IOFileSettings(); //d->waitingLoop = new QEventLoop(this); } EditorWindow::~EditorWindow() { delete m_canvas; delete m_IOFileSettings; delete d->toolIface; delete d->exposureSettings; delete d; } EditorStackView* EditorWindow::editorStackView() const { return m_stackView; } ExposureSettingsContainer* EditorWindow::exposureSettings() const { return d->exposureSettings; } void EditorWindow::setupContextMenu() { m_contextMenu = new QMenu(this); addAction2ContextMenu(QLatin1String("editorwindow_fullscreen"), true); addAction2ContextMenu(QLatin1String("options_show_menubar"), true); m_contextMenu->addSeparator(); // -------------------------------------------------------- addAction2ContextMenu(QLatin1String("editorwindow_backward"), true); addAction2ContextMenu(QLatin1String("editorwindow_forward"), true); m_contextMenu->addSeparator(); // -------------------------------------------------------- addAction2ContextMenu(QLatin1String("editorwindow_slideshow"), true); addAction2ContextMenu(QLatin1String("editorwindow_transform_rotateleft"), true); addAction2ContextMenu(QLatin1String("editorwindow_transform_rotateright"), true); addAction2ContextMenu(QLatin1String("editorwindow_transform_crop"), true); m_contextMenu->addSeparator(); // -------------------------------------------------------- addAction2ContextMenu(QLatin1String("editorwindow_delete"), true); } void EditorWindow::setupStandardConnections() { connect(m_stackView, SIGNAL(signalToggleOffFitToWindow()), this, SLOT(slotToggleOffFitToWindow())); // -- Canvas connections ------------------------------------------------ connect(m_canvas, SIGNAL(signalShowNextImage()), this, SLOT(slotForward())); connect(m_canvas, SIGNAL(signalShowPrevImage()), this, SLOT(slotBackward())); connect(m_canvas, SIGNAL(signalRightButtonClicked()), this, SLOT(slotContextMenu())); connect(m_stackView, SIGNAL(signalZoomChanged(bool,bool,double)), this, SLOT(slotZoomChanged(bool,bool,double))); connect(m_canvas, SIGNAL(signalChanged()), this, SLOT(slotChanged())); connect(m_canvas, SIGNAL(signalAddedDropedItems(QDropEvent*)), this, SLOT(slotAddedDropedItems(QDropEvent*))); connect(m_canvas->interface(), SIGNAL(signalUndoStateChanged()), this, SLOT(slotUndoStateChanged())); connect(m_canvas, SIGNAL(signalSelected(bool)), this, SLOT(slotSelected(bool))); connect(m_canvas, SIGNAL(signalPrepareToLoad()), this, SLOT(slotPrepareToLoad())); connect(m_canvas, SIGNAL(signalLoadingStarted(QString)), this, SLOT(slotLoadingStarted(QString))); connect(m_canvas, SIGNAL(signalLoadingFinished(QString,bool)), this, SLOT(slotLoadingFinished(QString,bool))); connect(m_canvas, SIGNAL(signalLoadingProgress(QString,float)), this, SLOT(slotLoadingProgress(QString,float))); connect(m_canvas, SIGNAL(signalSavingStarted(QString)), this, SLOT(slotSavingStarted(QString))); connect(m_canvas, SIGNAL(signalSavingFinished(QString,bool)), this, SLOT(slotSavingFinished(QString,bool))); connect(m_canvas, SIGNAL(signalSavingProgress(QString,float)), this, SLOT(slotSavingProgress(QString,float))); connect(m_canvas, SIGNAL(signalSelectionChanged(QRect)), this, SLOT(slotSelectionChanged(QRect))); connect(m_canvas, SIGNAL(signalSelectionSetText(QRect)), this, SLOT(slotSelectionSetText(QRect))); connect(m_canvas->interface(), SIGNAL(signalFileOriginChanged(QString)), this, SLOT(slotFileOriginChanged(QString))); // -- status bar connections -------------------------------------- connect(m_nameLabel, SIGNAL(signalCancelButtonPressed()), this, SLOT(slotNameLabelCancelButtonPressed())); connect(m_nameLabel, SIGNAL(signalCancelButtonPressed()), d->toolIface, SLOT(slotToolAborted())); // -- Icc settings connections -------------------------------------- connect(IccSettings::instance(), SIGNAL(settingsChanged()), this, SLOT(slotColorManagementOptionsChanged())); } void EditorWindow::setupStandardActions() { // -- Standard 'File' menu actions --------------------------------------------- KActionCollection* const ac = actionCollection(); m_backwardAction = buildStdAction(StdBackAction, this, SLOT(slotBackward()), this); ac->addAction(QLatin1String("editorwindow_backward"), m_backwardAction); ac->setDefaultShortcuts(m_backwardAction, QList() << Qt::Key_PageUp << Qt::Key_Backspace << Qt::Key_Up << Qt::Key_Left); m_backwardAction->setEnabled(false); m_forwardAction = buildStdAction(StdForwardAction, this, SLOT(slotForward()), this); ac->addAction(QLatin1String("editorwindow_forward"), m_forwardAction); ac->setDefaultShortcuts(m_forwardAction, QList() << Qt::Key_PageDown << Qt::Key_Space << Qt::Key_Down << Qt::Key_Right); m_forwardAction->setEnabled(false); m_firstAction = new QAction(QIcon::fromTheme(QLatin1String("go-first")), i18n("&First"), this); connect(m_firstAction, SIGNAL(triggered()), this, SLOT(slotFirst())); ac->addAction(QLatin1String("editorwindow_first"), m_firstAction); ac->setDefaultShortcuts(m_firstAction, QList() << Qt::CTRL + Qt::Key_Home); m_firstAction->setEnabled(false); m_lastAction = new QAction(QIcon::fromTheme(QLatin1String("go-last")), i18n("&Last"), this); connect(m_lastAction, SIGNAL(triggered()), this, SLOT(slotLast())); ac->addAction(QLatin1String("editorwindow_last"), m_lastAction); ac->setDefaultShortcuts(m_lastAction, QList() << Qt::CTRL + Qt::Key_End); m_lastAction->setEnabled(false); m_openVersionAction = new QAction(QIcon::fromTheme(QLatin1String("view-preview")), i18nc("@action", "Open Original"), this); connect(m_openVersionAction, SIGNAL(triggered()), this, SLOT(slotOpenOriginal())); ac->addAction(QLatin1String("editorwindow_openversion"), m_openVersionAction); ac->setDefaultShortcuts(m_openVersionAction, QList() << Qt::CTRL + Qt::Key_End); m_saveAction = buildStdAction(StdSaveAction, this, SLOT(saveOrSaveAs()), this); ac->addAction(QLatin1String("editorwindow_save"), m_saveAction); m_saveAsAction = buildStdAction(StdSaveAsAction, this, SLOT(saveAs()), this); ac->addAction(QLatin1String("editorwindow_saveas"), m_saveAsAction); m_saveCurrentVersionAction = new QAction(QIcon::fromTheme(QLatin1String("dialog-ok-apply")), i18nc("@action Save changes to current version", "Save Changes"), this); m_saveCurrentVersionAction->setToolTip(i18nc("@info:tooltip", "Save the modifications to the current version of the file")); connect(m_saveCurrentVersionAction, SIGNAL(triggered()), this, SLOT(saveCurrentVersion())); ac->addAction(QLatin1String("editorwindow_savecurrentversion"), m_saveCurrentVersionAction); m_saveNewVersionAction = new KToolBarPopupAction(QIcon::fromTheme(QLatin1String("list-add")), i18nc("@action Save changes to a newly created version", "Save As New Version"), this); m_saveNewVersionAction->setToolTip(i18nc("@info:tooltip", "Save the current modifications to a new version of the file")); connect(m_saveNewVersionAction, SIGNAL(triggered()), this, SLOT(saveNewVersion())); ac->addAction(QLatin1String("editorwindow_savenewversion"), m_saveNewVersionAction); QAction* const m_saveNewVersionAsAction = new QAction(QIcon::fromTheme(QLatin1String("document-save-as")), i18nc("@action Save changes to a newly created version, specifying the filename and format", "Save New Version As..."), this); m_saveNewVersionAsAction->setToolTip(i18nc("@info:tooltip", "Save the current modifications to a new version of the file, " "specifying the filename and format")); connect(m_saveNewVersionAsAction, SIGNAL(triggered()), this, SLOT(saveNewVersionAs())); m_saveNewVersionInFormatAction = new QMenu(i18nc("@action Save As New Version...Save in format...", "Save in Format"), this); m_saveNewVersionInFormatAction->setIcon(QIcon::fromTheme(QLatin1String("view-preview"))); d->plugNewVersionInFormatAction(this, m_saveNewVersionInFormatAction, i18nc("@action:inmenu", "JPEG"), QLatin1String("JPG")); d->plugNewVersionInFormatAction(this, m_saveNewVersionInFormatAction, i18nc("@action:inmenu", "TIFF"), QLatin1String("TIFF")); d->plugNewVersionInFormatAction(this, m_saveNewVersionInFormatAction, i18nc("@action:inmenu", "PNG"), QLatin1String("PNG")); d->plugNewVersionInFormatAction(this, m_saveNewVersionInFormatAction, i18nc("@action:inmenu", "PGF"), QLatin1String("PGF")); #ifdef HAVE_JASPER d->plugNewVersionInFormatAction(this, m_saveNewVersionInFormatAction, i18nc("@action:inmenu", "JPEG 2000"), QLatin1String("JP2")); #endif // HAVE_JASPER m_saveNewVersionAction->menu()->addAction(m_saveNewVersionAsAction); m_saveNewVersionAction->menu()->addAction(m_saveNewVersionInFormatAction->menuAction()); // This also triggers saveAs, but in the context of non-destructive we want a slightly different appearance m_exportAction = new QAction(QIcon::fromTheme(QLatin1String("document-export")), i18nc("@action", "Export"), this); m_exportAction->setToolTip(i18nc("@info:tooltip", "Save the file in a folder outside your collection")); connect(m_exportAction, SIGNAL(triggered()), this, SLOT(saveAs())); ac->addAction(QLatin1String("editorwindow_export"), m_exportAction); ac->setDefaultShortcut(m_exportAction, Qt::CTRL + Qt::SHIFT + Qt::Key_E); // NOTE: Gimp shortcut m_revertAction = buildStdAction(StdRevertAction, this, SLOT(slotRevert()), this); ac->addAction(QLatin1String("editorwindow_revert"), m_revertAction); m_discardChangesAction = new QAction(QIcon::fromTheme(QLatin1String("task-reject")), i18nc("@action", "Discard Changes"), this); m_discardChangesAction->setToolTip(i18nc("@info:tooltip", "Discard all current changes to this file")); connect(m_discardChangesAction, SIGNAL(triggered()), this, SLOT(slotDiscardChanges())); ac->addAction(QLatin1String("editorwindow_discardchanges"), m_discardChangesAction); m_openVersionAction->setEnabled(false); m_saveAction->setEnabled(false); m_saveAsAction->setEnabled(false); m_saveCurrentVersionAction->setEnabled(false); m_saveNewVersionAction->setEnabled(false); m_revertAction->setEnabled(false); m_discardChangesAction->setEnabled(false); d->filePrintAction = new QAction(QIcon::fromTheme(QLatin1String("document-print-frame")), i18n("Print Image..."), this); connect(d->filePrintAction, SIGNAL(triggered()), this, SLOT(slotFilePrint())); ac->addAction(QLatin1String("editorwindow_print"), d->filePrintAction); ac->setDefaultShortcut(d->filePrintAction, Qt::CTRL + Qt::Key_P); d->filePrintAction->setEnabled(false); d->openWithAction = new QAction(QIcon::fromTheme(QLatin1String("preferences-desktop-filetype-association")), i18n("Open With Default Application"), this); d->openWithAction->setWhatsThis(i18n("Open the item with default assigned application.")); connect(d->openWithAction, SIGNAL(triggered()), this, SLOT(slotFileWithDefaultApplication())); ac->addAction(QLatin1String("open_with_default_application"), d->openWithAction); ac->setDefaultShortcut(d->openWithAction, Qt::META + Qt::Key_F4); d->openWithAction->setEnabled(false); - m_fileDeleteAction = new QAction(QIcon::fromTheme(QLatin1String("user-trash")), i18nc("Non-pluralized", "Move to Trash"), this); + m_fileDeleteAction = new QAction(QIcon::fromTheme(QLatin1String("user-trash-full")), i18nc("Non-pluralized", "Move to Trash"), this); connect(m_fileDeleteAction, SIGNAL(triggered()), this, SLOT(slotDeleteCurrentItem())); ac->addAction(QLatin1String("editorwindow_delete"), m_fileDeleteAction); ac->setDefaultShortcut(m_fileDeleteAction, Qt::Key_Delete); m_fileDeleteAction->setEnabled(false); QAction* const closeAction = buildStdAction(StdCloseAction, this, SLOT(close()), this); ac->addAction(QLatin1String("editorwindow_close"), closeAction); // -- Standard 'Edit' menu actions --------------------------------------------- d->copyAction = buildStdAction(StdCopyAction, m_canvas, SLOT(slotCopy()), this); ac->addAction(QLatin1String("editorwindow_copy"), d->copyAction); d->copyAction->setEnabled(false); m_undoAction = new KToolBarPopupAction(QIcon::fromTheme(QLatin1String("edit-undo")), i18n("Undo"), this); m_undoAction->setEnabled(false); ac->addAction(QLatin1String("editorwindow_undo"), m_undoAction); ac->setDefaultShortcuts(m_undoAction, QList() << Qt::CTRL + Qt::Key_Z); connect(m_undoAction->menu(), SIGNAL(aboutToShow()), this, SLOT(slotAboutToShowUndoMenu())); // we are using a signal mapper to identify which of a bunch of actions was triggered d->undoSignalMapper = new QSignalMapper(this); // connect mapper to view connect(d->undoSignalMapper, SIGNAL(mapped(int)), m_canvas, SLOT(slotUndo(int))); // connect simple undo action connect(m_undoAction, SIGNAL(triggered()), d->undoSignalMapper, SLOT(map())); d->undoSignalMapper->setMapping(m_undoAction, 1); m_redoAction = new KToolBarPopupAction(QIcon::fromTheme(QLatin1String("edit-redo")), i18n("Redo"), this); m_redoAction->setEnabled(false); ac->addAction(QLatin1String("editorwindow_redo"), m_redoAction); ac->setDefaultShortcuts(m_redoAction, QList() << Qt::CTRL + Qt::SHIFT + Qt::Key_Z); connect(m_redoAction->menu(), SIGNAL(aboutToShow()), this, SLOT(slotAboutToShowRedoMenu())); d->redoSignalMapper = new QSignalMapper(this); connect(d->redoSignalMapper, SIGNAL(mapped(int)), m_canvas, SLOT(slotRedo(int))); connect(m_redoAction, SIGNAL(triggered()), d->redoSignalMapper, SLOT(map())); d->redoSignalMapper->setMapping(m_redoAction, 1); d->selectAllAction = new QAction(i18nc("Create a selection containing the full image", "Select All"), this); connect(d->selectAllAction, SIGNAL(triggered()), m_canvas, SLOT(slotSelectAll())); ac->addAction(QLatin1String("editorwindow_selectAll"), d->selectAllAction); ac->setDefaultShortcut(d->selectAllAction, Qt::CTRL + Qt::Key_A); d->selectNoneAction = new QAction(i18n("Select None"), this); connect(d->selectNoneAction, SIGNAL(triggered()), m_canvas, SLOT(slotSelectNone())); ac->addAction(QLatin1String("editorwindow_selectNone"), d->selectNoneAction); ac->setDefaultShortcut(d->selectNoneAction, Qt::CTRL + Qt::SHIFT + Qt::Key_A); // -- Standard 'View' menu actions --------------------------------------------- d->zoomPlusAction = buildStdAction(StdZoomInAction, this, SLOT(slotIncreaseZoom()), this); ac->addAction(QLatin1String("editorwindow_zoomplus"), d->zoomPlusAction); d->zoomMinusAction = buildStdAction(StdZoomOutAction, this, SLOT(slotDecreaseZoom()), this); ac->addAction(QLatin1String("editorwindow_zoomminus"), d->zoomMinusAction); d->zoomTo100percents = new QAction(QIcon::fromTheme(QLatin1String("zoom-original")), i18n("Zoom to 100%"), this); connect(d->zoomTo100percents, SIGNAL(triggered()), this, SLOT(slotZoomTo100Percents())); ac->addAction(QLatin1String("editorwindow_zoomto100percents"), d->zoomTo100percents); ac->setDefaultShortcut(d->zoomTo100percents, Qt::CTRL + Qt::Key_Period); d->zoomFitToWindowAction = new QAction(QIcon::fromTheme(QLatin1String("zoom-fit-best")), i18n("Fit to &Window"), this); d->zoomFitToWindowAction->setCheckable(true); connect(d->zoomFitToWindowAction, SIGNAL(triggered()), this, SLOT(slotToggleFitToWindow())); ac->addAction(QLatin1String("editorwindow_zoomfit2window"), d->zoomFitToWindowAction); ac->setDefaultShortcut(d->zoomFitToWindowAction, Qt::ALT + Qt::CTRL + Qt::Key_E); d->zoomFitToSelectAction = new QAction(QIcon::fromTheme(QLatin1String("zoom-select-fit")), i18n("Fit to &Selection"), this); connect(d->zoomFitToSelectAction, SIGNAL(triggered()), this, SLOT(slotFitToSelect())); ac->addAction(QLatin1String("editorwindow_zoomfit2select"), d->zoomFitToSelectAction); ac->setDefaultShortcut(d->zoomFitToSelectAction, Qt::ALT + Qt::CTRL + Qt::Key_S); // NOTE: Photoshop 7 use ALT+CTRL+0 d->zoomFitToSelectAction->setEnabled(false); d->zoomFitToSelectAction->setWhatsThis(i18n("This option can be used to zoom the image to the " "current selection area.")); // -- Standard 'Decorate' menu actions --------------------------------------------- d->insertTextAction = new QAction(QIcon::fromTheme(QLatin1String("insert-text")), i18n("Insert Text..."), this); actionCollection()->addAction(QLatin1String("editorwindow_decorate_inserttext"), d->insertTextAction ); actionCollection()->setDefaultShortcut(d->insertTextAction, Qt::SHIFT+Qt::CTRL+Qt::Key_T); connect(d->insertTextAction, SIGNAL(triggered(bool)), this, SLOT(slotInsertText())); d->insertTextAction->setEnabled(false); d->borderAction = new QAction(QIcon::fromTheme(QLatin1String("bordertool")), i18n("Add Border..."), this); actionCollection()->addAction(QLatin1String("editorwindow_decorate_border"), d->borderAction ); connect(d->borderAction, SIGNAL(triggered(bool)), this, SLOT(slotBorder())); d->borderAction->setEnabled(false); d->textureAction = new QAction(QIcon::fromTheme(QLatin1String("texture")), i18n("Apply Texture..."), this); actionCollection()->addAction(QLatin1String("editorwindow_decorate_texture"), d->textureAction ); connect(d->textureAction, SIGNAL(triggered(bool)), this, SLOT(slotTexture())); d->textureAction->setEnabled(false); // -- Standard 'Effects' menu actions --------------------------------------------- d->colorEffectsAction = new QAction(QIcon::fromTheme(QLatin1String("colorfx")), i18n("Color Effects..."), this); actionCollection()->addAction(QLatin1String("editorwindow_filter_colorfx"), d->colorEffectsAction); connect(d->colorEffectsAction, SIGNAL(triggered(bool)), this, SLOT(slotColorEffects())); d->colorEffectsAction->setEnabled(false); d->charcoalAction = new QAction(QIcon::fromTheme(QLatin1String("charcoaltool")), i18n("Charcoal Drawing..."), this); actionCollection()->addAction(QLatin1String("editorwindow_filter_charcoal"), d->charcoalAction); connect(d->charcoalAction, SIGNAL(triggered(bool)), this, SLOT(slotCharcoal())); d->charcoalAction->setEnabled(false); d->embossAction = new QAction(QIcon::fromTheme(QLatin1String("embosstool")), i18n("Emboss..."), this); actionCollection()->addAction(QLatin1String("editorwindow_filter_emboss"), d->embossAction); connect(d->embossAction, SIGNAL(triggered(bool)), this, SLOT(slotEmboss())); d->embossAction->setEnabled(false); d->oilpaintAction = new QAction(QIcon::fromTheme(QLatin1String("oilpaint")), i18n("Oil Paint..."), this); actionCollection()->addAction(QLatin1String("editorwindow_filter_oilpaint"), d->oilpaintAction); connect(d->oilpaintAction, SIGNAL(triggered(bool)), this ,SLOT(slotOilPaint())); d->oilpaintAction->setEnabled(false); d->blurfxAction = new QAction(QIcon::fromTheme(QLatin1String("blurfx")), i18n("Blur Effects..."), this); actionCollection()->addAction(QLatin1String("editorwindow_filter_blurfx"), d->blurfxAction); connect(d->blurfxAction, SIGNAL(triggered(bool)), this, SLOT(slotBlurFX())); d->blurfxAction->setEnabled(false); d->distortionfxAction = new QAction(QIcon::fromTheme(QLatin1String("draw-spiral")), i18n("Distortion Effects..."), this); actionCollection()->addAction(QLatin1String("editorwindow_filter_distortionfx"), d->distortionfxAction ); connect(d->distortionfxAction, SIGNAL(triggered(bool)), this, SLOT(slotDistortionFX())); d->distortionfxAction->setEnabled(false); d->raindropAction = new QAction(QIcon::fromTheme(QLatin1String("raindrop")), i18n("Raindrops..."), this); actionCollection()->addAction(QLatin1String("editorwindow_filter_raindrop"), d->raindropAction); connect(d->raindropAction, SIGNAL(triggered(bool)), this, SLOT(slotRainDrop())); d->raindropAction->setEnabled(false); d->filmgrainAction = new QAction(QIcon::fromTheme(QLatin1String("filmgrain")), i18n("Add Film Grain..."), this); actionCollection()->addAction(QLatin1String("editorwindow_filter_filmgrain"), d->filmgrainAction); connect(d->filmgrainAction, SIGNAL(triggered(bool)), this, SLOT(slotFilmGrain())); d->filmgrainAction->setEnabled(false); // -- Standard 'Colors' menu actions --------------------------------------------- d->BCGAction = new QAction(QIcon::fromTheme(QLatin1String("contrast")), i18n("Brightness/Contrast/Gamma..."), this); actionCollection()->addAction(QLatin1String("editorwindow_color_bcg"), d->BCGAction); connect(d->BCGAction, SIGNAL(triggered(bool)), this, SLOT(slotBCG())); d->BCGAction->setEnabled(false); // NOTE: Photoshop 7 use CTRL+U. d->HSLAction = new QAction(QIcon::fromTheme(QLatin1String("adjusthsl")), i18n("Hue/Saturation/Lightness..."), this); actionCollection()->addAction(QLatin1String("editorwindow_color_hsl"), d->HSLAction); actionCollection()->setDefaultShortcut(d->HSLAction, Qt::CTRL+Qt::Key_U); connect(d->HSLAction, SIGNAL(triggered(bool)), this, SLOT(slotHSL())); d->HSLAction->setEnabled(false); // NOTE: Photoshop 7 use CTRL+B. d->CBAction = new QAction(QIcon::fromTheme(QLatin1String("adjustrgb")), i18n("Color Balance..."), this); actionCollection()->addAction(QLatin1String("editorwindow_color_rgb"), d->CBAction); actionCollection()->setDefaultShortcut(d->CBAction, Qt::CTRL+Qt::Key_B); connect(d->CBAction, SIGNAL(triggered(bool)), this, SLOT(slotCB())); d->CBAction->setEnabled(false); // NOTE: Photoshop 7 use CTRL+SHIFT+B with d->autoCorrectionAction = new QAction(QIcon::fromTheme(QLatin1String("autocorrection")), i18n("Auto-Correction..."), this); actionCollection()->addAction(QLatin1String("editorwindow_color_autocorrection"), d->autoCorrectionAction); actionCollection()->setDefaultShortcut(d->autoCorrectionAction, Qt::CTRL+Qt::SHIFT+Qt::Key_B); connect(d->autoCorrectionAction, SIGNAL(triggered(bool)), this, SLOT(slotAutoCorrection())); d->autoCorrectionAction->setEnabled(false); // NOTE: Photoshop 7 use CTRL+I. d->invertAction = new QAction(QIcon::fromTheme(QLatin1String("edit-select-invert")), i18n("Invert"), this); actionCollection()->addAction(QLatin1String("editorwindow_color_invert"), d->invertAction); actionCollection()->setDefaultShortcut(d->invertAction, Qt::CTRL+Qt::Key_I); connect(d->invertAction, SIGNAL(triggered(bool)), this, SLOT(slotInvert())); d->invertAction->setEnabled(false); d->convertTo8Bits = new QAction(QIcon::fromTheme(QLatin1String("depth16to8")), i18n("8 bits"), this); actionCollection()->addAction(QLatin1String("editorwindow_convertto8bits"), d->convertTo8Bits); connect(d->convertTo8Bits, SIGNAL(triggered(bool)), this, SLOT(slotConvertTo8Bits())); d->convertTo8Bits->setEnabled(false); d->convertTo16Bits = new QAction(QIcon::fromTheme(QLatin1String("depth8to16")), i18n("16 bits"), this); actionCollection()->addAction(QLatin1String("editorwindow_convertto16bits"), d->convertTo16Bits); connect(d->convertTo16Bits, SIGNAL(triggered(bool)), this, SLOT(slotConvertTo16Bits())); d->convertTo16Bits->setEnabled(false); d->profileMenuAction = new IccProfilesMenuAction(QIcon::fromTheme(QLatin1String("preferences-desktop-display-color")), i18n("Color Spaces"), this); actionCollection()->addAction(QLatin1String("editorwindow_colormanagement"), d->profileMenuAction->menuAction()); connect(d->profileMenuAction, SIGNAL(triggered(IccProfile)), this, SLOT(slotConvertToColorSpace(IccProfile))); d->profileMenuAction->setEnabled(false); connect(IccSettings::instance(), SIGNAL(settingsChanged()), this, SLOT(slotUpdateColorSpaceMenu())); d->colorSpaceConverter = new QAction(QIcon::fromTheme(QLatin1String("preferences-desktop-display-color")), i18n("Color Space Converter..."), this); connect(d->colorSpaceConverter, SIGNAL(triggered()), this, SLOT(slotProfileConversionTool())); d->colorSpaceConverter->setEnabled(false); slotUpdateColorSpaceMenu(); d->BWAction = new QAction(QIcon::fromTheme(QLatin1String("bwtonal")), i18n("Black && White..."), this); actionCollection()->addAction(QLatin1String("editorwindow_color_blackwhite"), d->BWAction); connect(d->BWAction, SIGNAL(triggered(bool)), this, SLOT(slotBW())); d->BWAction->setEnabled(false); d->whitebalanceAction = new QAction(QIcon::fromTheme(QLatin1String("bordertool")), i18n("White Balance..."), this); actionCollection()->addAction(QLatin1String("editorwindow_color_whitebalance"), d->whitebalanceAction); actionCollection()->setDefaultShortcut(d->whitebalanceAction, Qt::CTRL+Qt::SHIFT+Qt::Key_W); connect(d->whitebalanceAction, SIGNAL(triggered(bool)), this, SLOT(slotWhiteBalance())); d->whitebalanceAction->setEnabled(false); d->channelMixerAction = new QAction(QIcon::fromTheme(QLatin1String("channelmixer")), i18n("Channel Mixer..."), this); actionCollection()->addAction(QLatin1String("editorwindow_color_channelmixer"), d->channelMixerAction); actionCollection()->setDefaultShortcut(d->channelMixerAction, Qt::CTRL+Qt::Key_H); connect(d->channelMixerAction, SIGNAL(triggered(bool)), this, SLOT(slotChannelMixer())); d->channelMixerAction->setEnabled(false); d->curvesAction = new QAction(QIcon::fromTheme(QLatin1String("adjustcurves")), i18n("Curves Adjust..."), this); // NOTE: Photoshop 7 use CTRL+M (but it's used in KDE to toogle menu bar). actionCollection()->addAction(QLatin1String("editorwindow_color_adjustcurves"), d->curvesAction); actionCollection()->setDefaultShortcut(d->curvesAction, Qt::CTRL+Qt::SHIFT+Qt::Key_C); connect(d->curvesAction, SIGNAL(triggered(bool)), this, SLOT(slotCurvesAdjust())); d->curvesAction->setEnabled(false); d->levelsAction = new QAction(QIcon::fromTheme(QLatin1String("adjustlevels")), i18n("Levels Adjust..."), this); actionCollection()->addAction(QLatin1String("editorwindow_color_adjustlevels"), d->levelsAction); actionCollection()->setDefaultShortcut(d->levelsAction, Qt::CTRL+Qt::Key_L); connect(d->levelsAction, SIGNAL(triggered(bool)), this, SLOT(slotLevelsAdjust())); d->levelsAction->setEnabled(false); d->filmAction = new QAction(QIcon::fromTheme(QLatin1String("colorneg")), i18n("Color Negative..."), this); actionCollection()->addAction(QLatin1String("editorwindow_color_film"), d->filmAction); actionCollection()->setDefaultShortcut(d->filmAction, Qt::CTRL+Qt::SHIFT+Qt::Key_I); connect(d->filmAction, SIGNAL(triggered(bool)), this, SLOT(slotFilm())); d->filmAction->setEnabled(false); // -- Standard 'Enhance' menu actions --------------------------------------------- d->restorationAction = new QAction(QIcon::fromTheme(QLatin1String("restoration")), i18n("Restoration..."), this); actionCollection()->addAction(QLatin1String("editorwindow_enhance_restoration"), d->restorationAction); connect(d->restorationAction, SIGNAL(triggered(bool)), this, SLOT(slotRestoration())); d->restorationAction->setEnabled(false); d->sharpenAction = new QAction(QIcon::fromTheme(QLatin1String("sharpenimage")), i18n("Sharpen..."), this); actionCollection()->addAction(QLatin1String("editorwindow_enhance_sharpen"), d->sharpenAction); connect(d->sharpenAction, SIGNAL(triggered(bool)), this, SLOT(slotSharpen())); d->sharpenAction->setEnabled(false); d->blurAction = new QAction(QIcon::fromTheme(QLatin1String("blurimage")), i18n("Blur..."), this); actionCollection()->addAction(QLatin1String("editorwindow_enhance_blur"), d->blurAction); connect(d->blurAction, SIGNAL(triggered(bool)), this, SLOT(slotBlur())); d->blurAction->setEnabled(false); /* d->healCloneAction = new QAction(QIcon::fromTheme(QLatin1String("edit-clone")), i18n("Healing Clone..."), this); actionCollection()->addAction(QLatin1String("editorwindow_enhance_healingclone"), d->healCloneAction); d->healCloneAction->setWhatsThis( i18n( "This filter can be used to clone a part in a photo to erase unwanted region.") ); connect(d->healCloneAction, SIGNAL(triggered(bool)), this, SLOT(slotHealingClone())); d->healCloneAction->setEnabled(false); */ d->noiseReductionAction = new QAction(QIcon::fromTheme(QLatin1String("noisereduction")), i18n("Noise Reduction..."), this); actionCollection()->addAction(QLatin1String("editorwindow_enhance_noisereduction"), d->noiseReductionAction); connect(d->noiseReductionAction, SIGNAL(triggered(bool)), this, SLOT(slotNoiseReduction())); d->noiseReductionAction->setEnabled(false); d->localContrastAction = new QAction(QIcon::fromTheme(QLatin1String("contrast")), i18n("Local Contrast..."), this); actionCollection()->addAction(QLatin1String("editorwindow_enhance_localcontrast"), d->localContrastAction); connect(d->localContrastAction, SIGNAL(triggered(bool)), this, SLOT(slotLocalContrast())); d->localContrastAction->setEnabled(false); d->redeyeAction = new QAction(QIcon::fromTheme(QLatin1String("redeyes")), i18n("Red Eye..."), this); d->redeyeAction->setWhatsThis(i18n("This filter can be used to correct red eyes in a photo. " "Select a region including the eyes to use this option.")); actionCollection()->addAction(QLatin1String("editorwindow_enhance_redeye"), d->redeyeAction); connect(d->redeyeAction, SIGNAL(triggered(bool)), this, SLOT(slotRedEye())); d->redeyeAction->setEnabled(false); d->antivignettingAction = new QAction(QIcon::fromTheme(QLatin1String("antivignetting")), i18n("Vignetting Correction..."), this); actionCollection()->addAction(QLatin1String("editorwindow_enhance_antivignetting"), d->antivignettingAction); connect(d->antivignettingAction, SIGNAL(triggered(bool)), this, SLOT(slotAntiVignetting())); d->antivignettingAction->setEnabled(false); d->lensdistortionAction = new QAction(QIcon::fromTheme(QLatin1String("lensdistortion")), i18n("Distortion..."), this); actionCollection()->addAction(QLatin1String("editorwindow_enhance_lensdistortion"), d->lensdistortionAction); connect(d->lensdistortionAction, SIGNAL(triggered(bool)), this, SLOT(slotLensDistortion())); d->lensdistortionAction->setEnabled(false); d->hotpixelsAction = new QAction(QIcon::fromTheme(QLatin1String("hotpixels")), i18n("Hot Pixels..."), this); actionCollection()->addAction(QLatin1String("editorwindow_enhance_hotpixels"), d->hotpixelsAction); connect(d->hotpixelsAction, SIGNAL(triggered(bool)), this, SLOT(slotHotPixels())); d->hotpixelsAction->setEnabled(false); #ifdef HAVE_LENSFUN d->lensAutoFixAction = new QAction(QIcon::fromTheme(QLatin1String("lensautofix")), i18n("Auto-Correction..."), this); actionCollection()->addAction(QLatin1String("editorwindow_enhance_lensautofix"), d->lensAutoFixAction ); connect(d->lensAutoFixAction, SIGNAL(triggered(bool)), this, SLOT(slotLensAutoFix())); d->lensAutoFixAction->setEnabled(false); #endif // HAVE_LENSFUN HotPixelsTool::registerFilter(); // -- Standard 'Tools' menu actions --------------------------------------------- createMetadataEditAction(); createGeolocationEditAction(); createTimeAdjustAction(); createHtmlGalleryAction(); createPanoramaAction(); createExpoBlendingAction(); createCalendarAction(); createVideoSlideshowAction(); createSendByMailAction(); createPrintCreatorAction(); createMediaServerAction(); createExportActions(); createImportActions(); m_metadataEditAction->setEnabled(false); m_timeAdjustAction->setEnabled(false); m_expoBlendingAction->setEnabled(false); m_calendarAction->setEnabled(false); m_sendByMailAction->setEnabled(false); m_printCreatorAction->setEnabled(false); m_mediaServerAction->setEnabled(false); #ifdef HAVE_MARBLE m_geolocationEditAction->setEnabled(false); #endif #ifdef HAVE_HTMLGALLERY m_htmlGalleryAction->setEnabled(false); #endif #ifdef HAVE_PANORAMA m_panoramaAction->setEnabled(false); #endif #ifdef HAVE_MEDIAPLAYER m_videoslideshowAction->setEnabled(false); #endif foreach (QAction* const ac, exportActions()) ac->setEnabled(false); // -------------------------------------------------------- createFullScreenAction(QLatin1String("editorwindow_fullscreen")); createSidebarActions(); d->slideShowAction = new QAction(QIcon::fromTheme(QLatin1String("view-presentation")), i18n("Slideshow"), this); connect(d->slideShowAction, SIGNAL(triggered()), this, SLOT(slotToggleSlideShow())); ac->addAction(QLatin1String("editorwindow_slideshow"), d->slideShowAction); ac->setDefaultShortcut(d->slideShowAction, Qt::Key_F9); createPresentationAction(); d->viewUnderExpoAction = new QAction(QIcon::fromTheme(QLatin1String("underexposure")), i18n("Under-Exposure Indicator"), this); d->viewUnderExpoAction->setCheckable(true); d->viewUnderExpoAction->setWhatsThis(i18n("Set this option to display black " "overlaid on the image. This will help you to avoid " "under-exposing the image.")); connect(d->viewUnderExpoAction, SIGNAL(triggered(bool)), this, SLOT(slotSetUnderExposureIndicator(bool))); ac->addAction(QLatin1String("editorwindow_underexposure"), d->viewUnderExpoAction); ac->setDefaultShortcut(d->viewUnderExpoAction, Qt::Key_F10); d->viewOverExpoAction = new QAction(QIcon::fromTheme(QLatin1String("overexposure")), i18n("Over-Exposure Indicator"), this); d->viewOverExpoAction->setCheckable(true); d->viewOverExpoAction->setWhatsThis(i18n("Set this option to display white " "overlaid on the image. This will help you to avoid " "over-exposing the image.")); connect(d->viewOverExpoAction, SIGNAL(triggered(bool)), this, SLOT(slotSetOverExposureIndicator(bool))); ac->addAction(QLatin1String("editorwindow_overexposure"), d->viewOverExpoAction); ac->setDefaultShortcut(d->viewOverExpoAction, Qt::Key_F11); d->viewCMViewAction = new QAction(QIcon::fromTheme(QLatin1String("video-display")), i18n("Color-Managed View"), this); d->viewCMViewAction->setCheckable(true); connect(d->viewCMViewAction, SIGNAL(triggered()), this, SLOT(slotToggleColorManagedView())); ac->addAction(QLatin1String("editorwindow_cmview"), d->viewCMViewAction); ac->setDefaultShortcut(d->viewCMViewAction, Qt::Key_F12); d->softProofOptionsAction = new QAction(QIcon::fromTheme(QLatin1String("printer")), i18n("Soft Proofing Options..."), this); connect(d->softProofOptionsAction, SIGNAL(triggered()), this, SLOT(slotSoftProofingOptions())); ac->addAction(QLatin1String("editorwindow_softproofoptions"), d->softProofOptionsAction); d->viewSoftProofAction = new QAction(QIcon::fromTheme(QLatin1String("document-print-preview")), i18n("Soft Proofing View"), this); d->viewSoftProofAction->setCheckable(true); connect(d->viewSoftProofAction, SIGNAL(triggered()), this, SLOT(slotUpdateSoftProofingState())); ac->addAction(QLatin1String("editorwindow_softproofview"), d->viewSoftProofAction); // -- Standard 'Transform' menu actions --------------------------------------------- d->cropAction = new QAction(QIcon::fromTheme(QLatin1String("transform-crop-and-resize")), i18nc("@action", "Crop to Selection"), this); connect(d->cropAction, SIGNAL(triggered()), m_canvas, SLOT(slotCrop())); d->cropAction->setEnabled(false); d->cropAction->setWhatsThis(i18n("This option can be used to crop the image. " "Select a region of the image to enable this action.")); ac->addAction(QLatin1String("editorwindow_transform_crop"), d->cropAction); ac->setDefaultShortcut(d->cropAction, Qt::CTRL + Qt::Key_X); d->autoCropAction = new QAction(QIcon::fromTheme(QLatin1String("transform-crop")), i18nc("@action", "Auto-Crop"), this); d->autoCropAction->setWhatsThis(i18n("This option can be used to crop automatically the image.")); connect(d->autoCropAction, SIGNAL(triggered()), m_canvas, SLOT(slotAutoCrop())); d->autoCropAction->setEnabled(false); ac->addAction(QLatin1String("editorwindow_transform_autocrop"), d->autoCropAction); ac->setDefaultShortcut(d->autoCropAction, Qt::SHIFT + Qt::CTRL + Qt::Key_X); d->perspectiveAction = new QAction(QIcon::fromTheme(QLatin1String("perspective")), i18n("Perspective Adjustment..."), this); actionCollection()->addAction(QLatin1String("editorwindow_transform_perspective"), d->perspectiveAction); connect(d->perspectiveAction, SIGNAL(triggered(bool)), this, SLOT(slotPerspective())); d->perspectiveAction->setEnabled(false); d->sheartoolAction = new QAction(QIcon::fromTheme(QLatin1String("transform-shear-left")), i18n("Shear..."), this); actionCollection()->addAction(QLatin1String("editorwindow_transform_sheartool"), d->sheartoolAction); connect(d->sheartoolAction, SIGNAL(triggered(bool)), this, SLOT(slotShearTool())); d->sheartoolAction->setEnabled(false); d->resizeAction = new QAction(QIcon::fromTheme(QLatin1String("transform-scale")), i18n("&Resize..."), this); actionCollection()->addAction(QLatin1String("editorwindow_transform_resize"), d->resizeAction); connect(d->resizeAction, SIGNAL(triggered()), this, SLOT(slotResize())); d->resizeAction->setEnabled(false); d->aspectRatioCropAction = new QAction(QIcon::fromTheme(QLatin1String("transform-crop")), i18n("Aspect Ratio Crop..."), this); actionCollection()->addAction(QLatin1String("editorwindow_transform_ratiocrop"), d->aspectRatioCropAction); connect(d->aspectRatioCropAction, SIGNAL(triggered(bool)), this, SLOT(slotRatioCrop())); d->aspectRatioCropAction->setEnabled(false); #ifdef HAVE_LIBLQR_1 d->contentAwareResizingAction = new QAction(QIcon::fromTheme(QLatin1String("transform-scale")), i18n("Liquid Rescale..."), this); actionCollection()->addAction(QLatin1String("editorwindow_transform_contentawareresizing"), d->contentAwareResizingAction); connect(d->contentAwareResizingAction, SIGNAL(triggered(bool)), this, SLOT(slotContentAwareResizing())); d->contentAwareResizingAction->setEnabled(false); #endif /* HAVE_LIBLQR_1 */ //----------------------------------------------------------------------------------- d->freerotationAction = new QAction(QIcon::fromTheme(QLatin1String("transform-rotate")), i18n("Free Rotation..."), this); actionCollection()->addAction(QLatin1String("editorwindow_transform_freerotation"), d->freerotationAction ); connect(d->freerotationAction, SIGNAL(triggered(bool)), this, SLOT(slotFreeRotation())); d->freerotationAction->setEnabled(false); QAction* const point1Action = new QAction(i18n("Set Point 1"), this); actionCollection()->addAction(QLatin1String("editorwindow_transform_freerotation_point1"), point1Action); actionCollection()->setDefaultShortcut(point1Action, Qt::CTRL + Qt::SHIFT + Qt::Key_1); connect(point1Action, SIGNAL(triggered(bool)), this, SIGNAL(signalPoint1Action())); QAction* const point2Action = new QAction(i18n("Set Point 2"), this); actionCollection()->addAction(QLatin1String("editorwindow_transform_freerotation_point2"), point2Action); actionCollection()->setDefaultShortcut(point2Action, Qt::CTRL + Qt::SHIFT + Qt::Key_2); connect(point2Action, SIGNAL(triggered(bool)), this, SIGNAL(signalPoint2Action())); QAction* const autoAdjustAction = new QAction(i18n("Auto Adjust"), this); actionCollection()->addAction(QLatin1String("editorwindow_transform_freerotation_autoadjust"), autoAdjustAction); actionCollection()->setDefaultShortcut(autoAdjustAction, Qt::CTRL + Qt::SHIFT + Qt::Key_R); connect(autoAdjustAction, SIGNAL(triggered(bool)), this, SIGNAL(signalAutoAdjustAction())); // -- Standard 'Flip' menu actions --------------------------------------------- d->flipHorizAction = new QAction(QIcon::fromTheme(QLatin1String("object-flip-horizontal")), i18n("Flip Horizontally"), this); connect(d->flipHorizAction, SIGNAL(triggered()), m_canvas, SLOT(slotFlipHoriz())); connect(d->flipHorizAction, SIGNAL(triggered()), this, SLOT(slotFlipHIntoQue())); ac->addAction(QLatin1String("editorwindow_transform_fliphoriz"), d->flipHorizAction); ac->setDefaultShortcut(d->flipHorizAction, Qt::CTRL + Qt::Key_Asterisk); d->flipHorizAction->setEnabled(false); d->flipVertAction = new QAction(QIcon::fromTheme(QLatin1String("object-flip-vertical")), i18n("Flip Vertically"), this); connect(d->flipVertAction, SIGNAL(triggered()), m_canvas, SLOT(slotFlipVert())); connect(d->flipVertAction, SIGNAL(triggered()), this, SLOT(slotFlipVIntoQue())); ac->addAction(QLatin1String("editorwindow_transform_flipvert"), d->flipVertAction); ac->setDefaultShortcut(d->flipVertAction, Qt::CTRL + Qt::Key_Slash); d->flipVertAction->setEnabled(false); // -- Standard 'Rotate' menu actions ---------------------------------------- d->rotateLeftAction = new QAction(QIcon::fromTheme(QLatin1String("object-rotate-left")), i18n("Rotate Left"), this); connect(d->rotateLeftAction, SIGNAL(triggered()), m_canvas, SLOT(slotRotate270())); connect(d->rotateLeftAction, SIGNAL(triggered()), this, SLOT(slotRotateLeftIntoQue())); ac->addAction(QLatin1String("editorwindow_transform_rotateleft"), d->rotateLeftAction); ac->setDefaultShortcut(d->rotateLeftAction, Qt::SHIFT + Qt::CTRL + Qt::Key_Left); d->rotateLeftAction->setEnabled(false); d->rotateRightAction = new QAction(QIcon::fromTheme(QLatin1String("object-rotate-right")), i18n("Rotate Right"), this); connect(d->rotateRightAction, SIGNAL(triggered()), m_canvas, SLOT(slotRotate90())); connect(d->rotateRightAction, SIGNAL(triggered()), this, SLOT(slotRotateRightIntoQue())); ac->addAction(QLatin1String("editorwindow_transform_rotateright"), d->rotateRightAction); ac->setDefaultShortcut(d->rotateRightAction, Qt::SHIFT + Qt::CTRL + Qt::Key_Right); d->rotateRightAction->setEnabled(false); m_showBarAction = thumbBar()->getToggleAction(this); ac->addAction(QLatin1String("editorwindow_showthumbs"), m_showBarAction); ac->setDefaultShortcut(m_showBarAction, Qt::CTRL + Qt::Key_T); // Provides a menu entry that allows showing/hiding the toolbar(s) setStandardToolBarMenuEnabled(true); // Provides a menu entry that allows showing/hiding the statusbar createStandardStatusBarAction(); // Standard 'Configure' menu actions createSettingsActions(); // --------------------------------------------------------------------------------- ThemeManager::instance()->registerThemeActions(this); connect(ThemeManager::instance(), SIGNAL(signalThemeChanged()), this, SLOT(slotThemeChanged())); // -- Keyboard-only actions -------------------------------------------------------- QAction* const altBackwardAction = new QAction(i18n("Previous Image"), this); ac->addAction(QLatin1String("editorwindow_backward_shift_space"), altBackwardAction); ac->setDefaultShortcut(altBackwardAction, Qt::SHIFT + Qt::Key_Space); connect(altBackwardAction, SIGNAL(triggered()), this, SLOT(slotBackward())); // -- Tool control actions --------------------------------------------------------- m_applyToolAction = new QAction(QIcon::fromTheme(QLatin1String("dialog-ok-apply")), i18n("OK"), this); ac->addAction(QLatin1String("editorwindow_applytool"), m_applyToolAction); ac->setDefaultShortcut(m_applyToolAction, Qt::Key_Return); connect(m_applyToolAction, SIGNAL(triggered()), this, SLOT(slotApplyTool())); m_closeToolAction = new QAction(QIcon::fromTheme(QLatin1String("dialog-cancel")), i18n("Cancel"), this); ac->addAction(QLatin1String("editorwindow_closetool"), m_closeToolAction); ac->setDefaultShortcut(m_closeToolAction, Qt::Key_Escape); connect(m_closeToolAction, SIGNAL(triggered()), this, SLOT(slotCloseTool())); toggleNonDestructiveActions(); toggleToolActions(); } void EditorWindow::setupStatusBar() { m_nameLabel = new StatusProgressBar(statusBar()); m_nameLabel->setAlignment(Qt::AlignCenter); statusBar()->addWidget(m_nameLabel, 100); d->infoLabel = new DAdjustableLabel(statusBar()); d->infoLabel->setAdjustedText(i18n("No selection")); d->infoLabel->setAlignment(Qt::AlignCenter); statusBar()->addWidget(d->infoLabel, 100); d->infoLabel->setToolTip(i18n("Information about current image selection")); m_resLabel = new DAdjustableLabel(statusBar()); m_resLabel->setAlignment(Qt::AlignCenter); statusBar()->addWidget(m_resLabel, 100); m_resLabel->setToolTip(i18n("Information about image size")); d->zoomBar = new DZoomBar(statusBar()); d->zoomBar->setZoomToFitAction(d->zoomFitToWindowAction); d->zoomBar->setZoomTo100Action(d->zoomTo100percents); d->zoomBar->setZoomPlusAction(d->zoomPlusAction); d->zoomBar->setZoomMinusAction(d->zoomMinusAction); d->zoomBar->setBarMode(DZoomBar::PreviewZoomCtrl); statusBar()->addPermanentWidget(d->zoomBar); connect(d->zoomBar, SIGNAL(signalZoomSliderChanged(int)), m_stackView, SLOT(slotZoomSliderChanged(int))); connect(d->zoomBar, SIGNAL(signalZoomValueEdited(double)), m_stackView, SLOT(setZoomFactor(double))); d->previewToolBar = new PreviewToolBar(statusBar()); d->previewToolBar->registerMenuActionGroup(this); d->previewToolBar->setEnabled(false); statusBar()->addPermanentWidget(d->previewToolBar); connect(d->previewToolBar, SIGNAL(signalPreviewModeChanged(int)), this, SIGNAL(signalPreviewModeChanged(int))); QWidget* const buttonsBox = new QWidget(statusBar()); QHBoxLayout* const hlay = new QHBoxLayout(buttonsBox); QButtonGroup* const buttonsGrp = new QButtonGroup(buttonsBox); buttonsGrp->setExclusive(false); d->underExposureIndicator = new QToolButton(buttonsBox); d->underExposureIndicator->setDefaultAction(d->viewUnderExpoAction); d->underExposureIndicator->setFocusPolicy(Qt::NoFocus); d->overExposureIndicator = new QToolButton(buttonsBox); d->overExposureIndicator->setDefaultAction(d->viewOverExpoAction); d->overExposureIndicator->setFocusPolicy(Qt::NoFocus); d->cmViewIndicator = new QToolButton(buttonsBox); d->cmViewIndicator->setDefaultAction(d->viewCMViewAction); d->cmViewIndicator->setFocusPolicy(Qt::NoFocus); buttonsGrp->addButton(d->underExposureIndicator); buttonsGrp->addButton(d->overExposureIndicator); buttonsGrp->addButton(d->cmViewIndicator); hlay->setSpacing(0); hlay->setContentsMargins(QMargins()); hlay->addWidget(d->underExposureIndicator); hlay->addWidget(d->overExposureIndicator); hlay->addWidget(d->cmViewIndicator); statusBar()->addPermanentWidget(buttonsBox); } void EditorWindow::printImage(const QUrl&) { DImg* const image = m_canvas->interface()->getImg(); if (!image || image->isNull()) { return; } PrintHelper printHelp(this); printHelp.print(*image); } void EditorWindow::slotAboutToShowUndoMenu() { m_undoAction->menu()->clear(); QStringList titles = m_canvas->interface()->getUndoHistory(); for (int i = 0; i < titles.size(); ++i) { QAction* const action = m_undoAction->menu()->addAction(titles.at(i), d->undoSignalMapper, SLOT(map())); d->undoSignalMapper->setMapping(action, i + 1); } } void EditorWindow::slotAboutToShowRedoMenu() { m_redoAction->menu()->clear(); QStringList titles = m_canvas->interface()->getRedoHistory(); for (int i = 0; i < titles.size(); ++i) { QAction* const action = m_redoAction->menu()->addAction(titles.at(i), d->redoSignalMapper, SLOT(map())); d->redoSignalMapper->setMapping(action, i + 1); } } void EditorWindow::slotIncreaseZoom() { m_stackView->increaseZoom(); } void EditorWindow::slotDecreaseZoom() { m_stackView->decreaseZoom(); } void EditorWindow::slotToggleFitToWindow() { d->zoomPlusAction->setEnabled(true); d->zoomBar->setEnabled(true); d->zoomMinusAction->setEnabled(true); m_stackView->toggleFitToWindow(); } void EditorWindow::slotFitToSelect() { d->zoomPlusAction->setEnabled(true); d->zoomBar->setEnabled(true); d->zoomMinusAction->setEnabled(true); m_stackView->fitToSelect(); } void EditorWindow::slotZoomTo100Percents() { d->zoomPlusAction->setEnabled(true); d->zoomBar->setEnabled(true); d->zoomMinusAction->setEnabled(true); m_stackView->zoomTo100Percent(); } void EditorWindow::slotZoomChanged(bool isMax, bool isMin, double zoom) { //qCDebug(DIGIKAM_GENERAL_LOG) << "EditorWindow::slotZoomChanged"; d->zoomPlusAction->setEnabled(!isMax); d->zoomMinusAction->setEnabled(!isMin); double zmin = m_stackView->zoomMin(); double zmax = m_stackView->zoomMax(); d->zoomBar->setZoom(zoom, zmin, zmax); } void EditorWindow::slotToggleOffFitToWindow() { d->zoomFitToWindowAction->blockSignals(true); d->zoomFitToWindowAction->setChecked(false); d->zoomFitToWindowAction->blockSignals(false); } void EditorWindow::readStandardSettings() { KSharedConfig::Ptr config = KSharedConfig::openConfig(); KConfigGroup group = config->group(configGroupName()); // Restore Canvas layout if (group.hasKey(d->configVerticalSplitterSizesEntry) && m_vSplitter) { QByteArray state; state = group.readEntry(d->configVerticalSplitterStateEntry, state); m_vSplitter->restoreState(QByteArray::fromBase64(state)); } // Restore full screen Mode readFullScreenSettings(group); // Restore Auto zoom action bool autoZoom = group.readEntry(d->configAutoZoomEntry, true); if (autoZoom) { d->zoomFitToWindowAction->trigger(); } slotSetUnderExposureIndicator(group.readEntry(d->configUnderExposureIndicatorEntry, false)); slotSetOverExposureIndicator(group.readEntry(d->configOverExposureIndicatorEntry, false)); d->previewToolBar->readSettings(group); } void EditorWindow::applyStandardSettings() { applyColorManagementSettings(); d->toolIface->updateICCSettings(); applyIOSettings(); // -- GUI Settings ------------------------------------------------------- KConfigGroup group = KSharedConfig::openConfig()->group(configGroupName()); d->legacyUpdateSplitterState(group); m_splitter->restoreState(group); readFullScreenSettings(group); slotThemeChanged(); // -- Exposure Indicators Settings --------------------------------------- d->exposureSettings->underExposureColor = group.readEntry(d->configUnderExposureColorEntry, QColor(Qt::white)); d->exposureSettings->underExposurePercent = group.readEntry(d->configUnderExposurePercentsEntry, 1.0); d->exposureSettings->overExposureColor = group.readEntry(d->configOverExposureColorEntry, QColor(Qt::black)); d->exposureSettings->overExposurePercent = group.readEntry(d->configOverExposurePercentsEntry, 1.0); d->exposureSettings->exposureIndicatorMode = group.readEntry(d->configExpoIndicatorModeEntry, true); d->toolIface->updateExposureSettings(); // -- Metadata Settings -------------------------------------------------- MetaEngineSettingsContainer writeSettings = MetaEngineSettings::instance()->settings(); m_setExifOrientationTag = writeSettings.exifSetOrientation; m_canvas->setExifOrient(writeSettings.exifRotate); } void EditorWindow::applyIOSettings() { // -- JPEG, PNG, TIFF, JPEG2000, PGF files format settings ---------------- KConfigGroup group = KSharedConfig::openConfig()->group(configGroupName()); m_IOFileSettings->JPEGCompression = JPEGSettings::convertCompressionForLibJpeg(group.readEntry(d->configJpegCompressionEntry, 75)); m_IOFileSettings->JPEGSubSampling = group.readEntry(d->configJpegSubSamplingEntry, 1); // Medium subsampling m_IOFileSettings->PNGCompression = PNGSettings::convertCompressionForLibPng(group.readEntry(d->configPngCompressionEntry, 1)); // TIFF compression setting. m_IOFileSettings->TIFFCompression = group.readEntry(d->configTiffCompressionEntry, false); // JPEG2000 quality slider settings : 1 - 100 m_IOFileSettings->JPEG2000Compression = group.readEntry(d->configJpeg2000CompressionEntry, 100); // JPEG2000 LossLess setting. m_IOFileSettings->JPEG2000LossLess = group.readEntry(d->configJpeg2000LossLessEntry, true); // PGF quality slider settings : 1 - 9 m_IOFileSettings->PGFCompression = group.readEntry(d->configPgfCompressionEntry, 3); // PGF LossLess setting. m_IOFileSettings->PGFLossLess = group.readEntry(d->configPgfLossLessEntry, true); // -- RAW images decoding settings ------------------------------------------------------ m_IOFileSettings->useRAWImport = group.readEntry(d->configUseRawImportToolEntry, false); DRawDecoderWidget::readSettings(m_IOFileSettings->rawDecodingSettings.rawPrm, group); // Raw Color Management settings: // If digiKam Color Management is enabled, no need to correct color of decoded RAW image, // else, sRGB color workspace will be used. ICCSettingsContainer settings = IccSettings::instance()->settings(); if (settings.enableCM) { if (settings.defaultUncalibratedBehavior & ICCSettingsContainer::AutomaticColors) { m_IOFileSettings->rawDecodingSettings.rawPrm.outputColorSpace = DRawDecoderSettings::CUSTOMOUTPUTCS; m_IOFileSettings->rawDecodingSettings.rawPrm.outputProfile = settings.workspaceProfile; } else { m_IOFileSettings->rawDecodingSettings.rawPrm.outputColorSpace = DRawDecoderSettings::RAWCOLOR; } } else { m_IOFileSettings->rawDecodingSettings.rawPrm.outputColorSpace = DRawDecoderSettings::SRGB; } } void EditorWindow::applyColorManagementSettings() { ICCSettingsContainer settings = IccSettings::instance()->settings(); d->toolIface->updateICCSettings(); m_canvas->setICCSettings(settings); d->viewCMViewAction->blockSignals(true); d->viewCMViewAction->setEnabled(settings.enableCM); d->viewCMViewAction->setChecked(settings.useManagedView); setColorManagedViewIndicatorToolTip(settings.enableCM, settings.useManagedView); d->viewCMViewAction->blockSignals(false); d->viewSoftProofAction->setEnabled(settings.enableCM && !settings.defaultProofProfile.isEmpty()); d->softProofOptionsAction->setEnabled(settings.enableCM); } void EditorWindow::saveStandardSettings() { KSharedConfig::Ptr config = KSharedConfig::openConfig(); KConfigGroup group = config->group(configGroupName()); group.writeEntry(d->configAutoZoomEntry, d->zoomFitToWindowAction->isChecked()); m_splitter->saveState(group); if (m_vSplitter) { group.writeEntry(d->configVerticalSplitterStateEntry, m_vSplitter->saveState().toBase64()); } group.writeEntry("Show Thumbbar", thumbBar()->shouldBeVisible()); group.writeEntry(d->configUnderExposureIndicatorEntry, d->exposureSettings->underExposureIndicator); group.writeEntry(d->configOverExposureIndicatorEntry, d->exposureSettings->overExposureIndicator); d->previewToolBar->writeSettings(group); config->sync(); } /** Method used by Editor Tools. Only tools based on imageregionwidget support zooming. TODO: Fix this behavior when editor tool preview widgets will be factored. */ void EditorWindow::toggleZoomActions(bool val) { d->zoomMinusAction->setEnabled(val); d->zoomPlusAction->setEnabled(val); d->zoomTo100percents->setEnabled(val); d->zoomFitToWindowAction->setEnabled(val); d->zoomBar->setEnabled(val); } void EditorWindow::readSettings() { readStandardSettings(); } void EditorWindow::saveSettings() { saveStandardSettings(); } void EditorWindow::toggleActions(bool val) { toggleStandardActions(val); } void EditorWindow::toggleStandardActions(bool val) { d->zoomFitToSelectAction->setEnabled(val); toggleZoomActions(val); m_actionEnabledState = val; m_forwardAction->setEnabled(val); m_backwardAction->setEnabled(val); m_firstAction->setEnabled(val); m_lastAction->setEnabled(val); d->rotateLeftAction->setEnabled(val); d->rotateRightAction->setEnabled(val); d->flipHorizAction->setEnabled(val); d->flipVertAction->setEnabled(val); m_fileDeleteAction->setEnabled(val); m_saveAsAction->setEnabled(val); d->openWithAction->setEnabled(val); d->filePrintAction->setEnabled(val); m_metadataEditAction->setEnabled(val); m_timeAdjustAction->setEnabled(val); m_exportAction->setEnabled(val); d->selectAllAction->setEnabled(val); d->selectNoneAction->setEnabled(val); d->slideShowAction->setEnabled(val); m_presentationAction->setEnabled(val); m_calendarAction->setEnabled(val); m_expoBlendingAction->setEnabled(val); m_sendByMailAction->setEnabled(val); m_printCreatorAction->setEnabled(val); m_mediaServerAction->setEnabled(val); #ifdef HAVE_MARBLE m_geolocationEditAction->setEnabled(val); #endif #ifdef HAVE_HTMLGALLERY m_htmlGalleryAction->setEnabled(val); #endif #ifdef HAVE_PANORAMA m_panoramaAction->setEnabled(val); #endif #ifdef HAVE_MEDIAPLAYER m_videoslideshowAction->setEnabled(val); #endif foreach (QAction* const ac, exportActions()) ac->setEnabled(val); // these actions are special: They are turned off if val is false, // but if val is true, they may be turned on or off. if (val) { // Update actions by retrieving current values slotUndoStateChanged(); } else { m_openVersionAction->setEnabled(false); m_revertAction->setEnabled(false); m_saveAction->setEnabled(false); m_saveCurrentVersionAction->setEnabled(false); m_saveNewVersionAction->setEnabled(false); m_discardChangesAction->setEnabled(false); m_undoAction->setEnabled(false); m_redoAction->setEnabled(false); } // Tools actions d->insertTextAction->setEnabled(val); d->borderAction->setEnabled(val); d->textureAction->setEnabled(val); d->charcoalAction->setEnabled(val); d->colorEffectsAction->setEnabled(val); d->embossAction->setEnabled(val); d->oilpaintAction->setEnabled(val); d->blurfxAction->setEnabled(val); d->distortionfxAction->setEnabled(val); d->raindropAction->setEnabled(val); d->filmgrainAction->setEnabled(val); d->convertTo8Bits->setEnabled(val); d->convertTo16Bits->setEnabled(val); d->invertAction->setEnabled(val); d->BCGAction->setEnabled(val); d->CBAction->setEnabled(val); d->autoCorrectionAction->setEnabled(val); d->BWAction->setEnabled(val); d->HSLAction->setEnabled(val); d->profileMenuAction->setEnabled(val); d->colorSpaceConverter->setEnabled(val && IccSettings::instance()->isEnabled()); d->whitebalanceAction->setEnabled(val); d->channelMixerAction->setEnabled(val); d->curvesAction->setEnabled(val); d->levelsAction->setEnabled(val); d->filmAction->setEnabled(val); d->restorationAction->setEnabled(val); d->blurAction->setEnabled(val); //d->healCloneAction->setEnabled(val); d->sharpenAction->setEnabled(val); d->noiseReductionAction->setEnabled(val); d->localContrastAction->setEnabled(val); d->redeyeAction->setEnabled(val); d->lensdistortionAction->setEnabled(val); d->antivignettingAction->setEnabled(val); d->hotpixelsAction->setEnabled(val); d->resizeAction->setEnabled(val); d->autoCropAction->setEnabled(val); d->perspectiveAction->setEnabled(val); d->freerotationAction->setEnabled(val); d->sheartoolAction->setEnabled(val); d->aspectRatioCropAction->setEnabled(val); #ifdef HAVE_LENSFUN d->lensAutoFixAction->setEnabled(val); #endif #ifdef HAVE_LIBLQR_1 d->contentAwareResizingAction->setEnabled(val); #endif } void EditorWindow::toggleNonDestructiveActions() { m_saveAction->setVisible(!m_nonDestructive); m_saveAsAction->setVisible(!m_nonDestructive); m_revertAction->setVisible(!m_nonDestructive); m_openVersionAction->setVisible(m_nonDestructive); m_saveCurrentVersionAction->setVisible(m_nonDestructive); m_saveNewVersionAction->setVisible(m_nonDestructive); m_exportAction->setVisible(m_nonDestructive); m_discardChangesAction->setVisible(m_nonDestructive); } void EditorWindow::toggleToolActions(EditorTool* tool) { if (tool) { m_applyToolAction->setText(tool->toolSettings()->button(EditorToolSettings::Ok)->text()); m_applyToolAction->setIcon(tool->toolSettings()->button(EditorToolSettings::Ok)->icon()); m_applyToolAction->setToolTip(tool->toolSettings()->button(EditorToolSettings::Ok)->toolTip()); m_closeToolAction->setText(tool->toolSettings()->button(EditorToolSettings::Cancel)->text()); m_closeToolAction->setIcon(tool->toolSettings()->button(EditorToolSettings::Cancel)->icon()); m_closeToolAction->setToolTip(tool->toolSettings()->button(EditorToolSettings::Cancel)->toolTip()); } m_applyToolAction->setVisible(tool); m_closeToolAction->setVisible(tool); } void EditorWindow::slotLoadingProgress(const QString&, float progress) { m_nameLabel->setProgressValue((int)(progress * 100.0)); } void EditorWindow::slotSavingProgress(const QString&, float progress) { m_nameLabel->setProgressValue((int)(progress * 100.0)); if (m_savingProgressDialog) { m_savingProgressDialog->setValue((int)(progress * 100.0)); } } void EditorWindow::execSavingProgressDialog() { if (m_savingProgressDialog) { return; } m_savingProgressDialog = new QProgressDialog(this); m_savingProgressDialog->setWindowTitle(i18n("Saving image...")); m_savingProgressDialog->setLabelText(i18n("Please wait for the image to be saved...")); m_savingProgressDialog->setAttribute(Qt::WA_DeleteOnClose); m_savingProgressDialog->setAutoClose(true); m_savingProgressDialog->setMinimumDuration(1000); m_savingProgressDialog->setMaximum(100); // we must enter a fully modal dialog, no QEventLoop is sufficient for KWin to accept longer waiting times m_savingProgressDialog->setModal(true); m_savingProgressDialog->exec(); } bool EditorWindow::promptForOverWrite() { QUrl destination = saveDestinationUrl(); if (destination.isLocalFile()) { QFileInfo fi(m_canvas->currentImageFilePath()); QString warnMsg(i18n("About to overwrite file \"%1\"\nAre you sure?", QDir::toNativeSeparators(fi.fileName()))); return (DMessageBox::showContinueCancel(QMessageBox::Warning, this, i18n("Warning"), warnMsg, QLatin1String("editorWindowSaveOverwrite")) == QMessageBox::Yes); } else { // in this case it will handle the overwrite request return true; } } void EditorWindow::slotUndoStateChanged() { UndoState state = m_canvas->interface()->undoState(); // RAW conversion qualifies as a "non-undoable" action // You can save as new version, but cannot undo or revert m_undoAction->setEnabled(state.hasUndo); m_redoAction->setEnabled(state.hasRedo); m_revertAction->setEnabled(state.hasUndoableChanges); m_saveAction->setEnabled(state.hasChanges); m_saveCurrentVersionAction->setEnabled(state.hasChanges); m_saveNewVersionAction->setEnabled(state.hasChanges); m_discardChangesAction->setEnabled(state.hasUndoableChanges); m_openVersionAction->setEnabled(hasOriginalToRestore()); } bool EditorWindow::hasOriginalToRestore() { return m_canvas->interface()->getResolvedInitialHistory().hasOriginalReferredImage(); } DImageHistory EditorWindow::resolvedImageHistory(const DImageHistory& history) { // simple, database-less version DImageHistory r = history; QList::iterator it; for (it = r.entries().begin(); it != r.entries().end(); ++it) { QList::iterator hit; for (hit = it->referredImages.begin(); hit != it->referredImages.end();) { QFileInfo info(hit->m_filePath + QLatin1Char('/') + hit->m_fileName); if (!info.exists()) { hit = it->referredImages.erase(hit); } else { ++hit; } } } return r; } bool EditorWindow::promptUserSave(const QUrl& url, SaveAskMode mode, bool allowCancel) { if (d->currentWindowModalDialog) { d->currentWindowModalDialog->reject(); } if (m_canvas->interface()->undoState().hasUndoableChanges) { // if window is minimized, show it if (isMinimized()) { KWindowSystem::unminimizeWindow(winId()); } bool shallSave = true; bool shallDiscard = false; bool newVersion = false; if (mode == AskIfNeeded) { if (m_nonDestructive) { if (versionManager()->settings().editorClosingMode == VersionManagerSettings::AutoSave) { shallSave = true; } else { QPointer dialog = new VersioningPromptUserSaveDialog(this); dialog->exec(); if (!dialog) { return false; } shallSave = dialog->shallSave() || dialog->newVersion(); shallDiscard = dialog->shallDiscard(); newVersion = dialog->newVersion(); } } else { QString boxMessage; boxMessage = i18nc("@info", "The image %1 has been modified.
" "Do you want to save it?
", url.fileName()); int result; if (allowCancel) { result = QMessageBox::warning(this, qApp->applicationName(), boxMessage, QMessageBox::Save | QMessageBox::Discard | QMessageBox::Cancel); } else { result = QMessageBox::warning(this, qApp->applicationName(), boxMessage, QMessageBox::Save | QMessageBox::Discard); } shallSave = (result == QMessageBox::Save); shallDiscard = (result == QMessageBox::Discard); } } if (shallSave) { bool saving = false; switch (mode) { case AskIfNeeded: if (m_nonDestructive) { if (newVersion) { saving = saveNewVersion(); } else { // will know on its own if new version is required saving = saveCurrentVersion(); } } else { if (m_canvas->isReadOnly()) { saving = saveAs(); } else if (promptForOverWrite()) { saving = save(); } } break; case OverwriteWithoutAsking: if (m_nonDestructive) { if (newVersion) { saving = saveNewVersion(); } else { // will know on its own if new version is required saving = saveCurrentVersion(); } } else { if (m_canvas->isReadOnly()) { saving = saveAs(); } else { saving = save(); } } break; case AlwaysSaveAs: if (m_nonDestructive) { saving = saveNewVersion(); } else { saving = saveAs(); } break; } // save and saveAs return false if they were canceled and did not enter saving at all // In this case, do not call enterWaitingLoop because quitWaitingloop will not be called. if (saving) { // Waiting for asynchronous image file saving operation running in separate thread. m_savingContext.synchronizingState = SavingContext::SynchronousSaving; enterWaitingLoop(); m_savingContext.synchronizingState = SavingContext::NormalSaving; return m_savingContext.synchronousSavingResult; } else { return false; } } else if (shallDiscard) { // Discard m_saveAction->setEnabled(false); return true; } else { return false; } } return true; } bool EditorWindow::promptUserDelete(const QUrl& url) { if (d->currentWindowModalDialog) { d->currentWindowModalDialog->reject(); } if (m_canvas->interface()->undoState().hasUndoableChanges) { // if window is minimized, show it if (isMinimized()) { KWindowSystem::unminimizeWindow(winId()); } QString boxMessage = i18nc("@info", "The image %1 has been modified.
" "All changes will be lost.", url.fileName()); int result = DMessageBox::showContinueCancel(QMessageBox::Warning, this, QString(), boxMessage); if (result == QMessageBox::Cancel) { return false; } } return true; } bool EditorWindow::waitForSavingToComplete() { // avoid reentrancy - return false means we have reentered the loop already. if (m_savingContext.synchronizingState == SavingContext::SynchronousSaving) { return false; } if (m_savingContext.savingState != SavingContext::SavingStateNone) { // Waiting for asynchronous image file saving operation running in separate thread. m_savingContext.synchronizingState = SavingContext::SynchronousSaving; enterWaitingLoop(); m_savingContext.synchronizingState = SavingContext::NormalSaving; } return true; } void EditorWindow::enterWaitingLoop() { //d->waitingLoop->exec(QEventLoop::ExcludeUserInputEvents); execSavingProgressDialog(); } void EditorWindow::quitWaitingLoop() { //d->waitingLoop->quit(); if (m_savingProgressDialog) { m_savingProgressDialog->close(); } } void EditorWindow::slotSelected(bool val) { // Update menu actions. d->cropAction->setEnabled(val); d->zoomFitToSelectAction->setEnabled(val); d->copyAction->setEnabled(val); QRect sel = m_canvas->getSelectedArea(); // Update histogram into sidebar. emit signalSelectionChanged(sel); // Update status bar if (val) { slotSelectionSetText(sel); } else { setToolInfoMessage(i18n("No selection")); } } void EditorWindow::slotPrepareToLoad() { // Disable actions as appropriate during loading emit signalNoCurrentItem(); unsetCursor(); m_animLogo->stop(); toggleActions(false); slotUpdateItemInfo(); } void EditorWindow::slotLoadingStarted(const QString& /*filename*/) { setCursor(Qt::WaitCursor); toggleActions(false); m_animLogo->start(); m_nameLabel->setProgressBarMode(StatusProgressBar::ProgressBarMode, i18n("Loading:")); } void EditorWindow::slotLoadingFinished(const QString& filename, bool success) { m_nameLabel->setProgressBarMode(StatusProgressBar::TextMode); // Enable actions as appropriate after loading // No need to re-enable image properties sidebar here, it's will be done // automatically by a signal from canvas toggleActions(success); slotUpdateItemInfo(); unsetCursor(); m_animLogo->stop(); if (success) { colorManage(); // Set a history which contains all available files as referredImages DImageHistory resolved = resolvedImageHistory(m_canvas->interface()->getInitialImageHistory()); m_canvas->interface()->setResolvedInitialHistory(resolved); } else { DNotificationPopup::message(DNotificationPopup::Boxed, i18n("Cannot load \"%1\"", filename), m_canvas, m_canvas->mapToGlobal(QPoint(30, 30))); } } void EditorWindow::resetOrigin() { // With versioning, "only" resetting undo history does not work anymore // as we calculate undo state based on the initial history stored in the DImg resetOriginSwitchFile(); } void EditorWindow::resetOriginSwitchFile() { DImageHistory resolved = resolvedImageHistory(m_canvas->interface()->getItemHistory()); m_canvas->interface()->switchToLastSaved(resolved); } void EditorWindow::colorManage() { if (!IccSettings::instance()->isEnabled()) { return; } DImg image = m_canvas->currentImage(); if (image.isNull()) { return; } if (!IccManager::needsPostLoadingManagement(image)) { return; } IccPostLoadingManager manager(image, m_canvas->currentImageFilePath()); if (!manager.hasValidWorkspace()) { QString message = i18n("Cannot open the specified working space profile (\"%1\"). " "No color transformation will be applied. " "Please check the color management " "configuration in digiKam's setup.", IccSettings::instance()->settings().workspaceProfile); QMessageBox::information(this, qApp->applicationName(), message); } // Show dialog and get transform from user choice IccTransform trans = manager.postLoadingManage(this); // apply transform in thread. // Do _not_ test for willHaveEffect() here - there are more side effects when calling this method m_canvas->applyTransform(trans); slotUpdateItemInfo(); } void EditorWindow::slotNameLabelCancelButtonPressed() { // If we saving an image... if (m_savingContext.savingState != SavingContext::SavingStateNone) { m_savingContext.abortingSaving = true; m_canvas->abortSaving(); } // If we preparing SlideShow... m_cancelSlideShow = true; } void EditorWindow::slotFileOriginChanged(const QString&) { // implemented in subclass } bool EditorWindow::saveOrSaveAs() { if (m_canvas->isReadOnly()) { return saveAs(); } return save(); } void EditorWindow::slotSavingStarted(const QString& /*filename*/) { setCursor(Qt::WaitCursor); m_animLogo->start(); // Disable actions as appropriate during saving emit signalNoCurrentItem(); toggleActions(false); m_nameLabel->setProgressBarMode(StatusProgressBar::CancelProgressBarMode, i18n("Saving:")); } void EditorWindow::slotSavingFinished(const QString& filename, bool success) { Q_UNUSED(filename); qCDebug(DIGIKAM_GENERAL_LOG) << filename << success << (m_savingContext.savingState != SavingContext::SavingStateNone); // only handle this if we really wanted to save a file... if (m_savingContext.savingState != SavingContext::SavingStateNone) { m_savingContext.executedOperation = m_savingContext.savingState; m_savingContext.savingState = SavingContext::SavingStateNone; if (!success) { if (!m_savingContext.abortingSaving) { QMessageBox::critical(this, qApp->applicationName(), i18n("Failed to save file\n\"%1\"\nto\n\"%2\".", m_savingContext.destinationURL.fileName(), m_savingContext.destinationURL.toLocalFile())); } finishSaving(false); return; } moveFile(); } else { qCWarning(DIGIKAM_GENERAL_LOG) << "Why was slotSavingFinished called if we did not want to save a file?"; } } void EditorWindow::movingSaveFileFinished(bool successful) { if (!successful) { finishSaving(false); return; } // now that we know the real destination file name, pass it to be recorded in image history m_canvas->interface()->setLastSaved(m_savingContext.destinationURL.toLocalFile()); // remove image from cache since it has changed LoadingCacheInterface::fileChanged(m_savingContext.destinationURL.toLocalFile()); ThumbnailLoadThread::deleteThumbnail(m_savingContext.destinationURL.toLocalFile()); // restore state of disabled actions. saveIsComplete can start any other task // (loading!) which might itself in turn change states finishSaving(true); switch (m_savingContext.executedOperation) { case SavingContext::SavingStateNone: break; case SavingContext::SavingStateSave: saveIsComplete(); break; case SavingContext::SavingStateSaveAs: saveAsIsComplete(); break; case SavingContext::SavingStateVersion: saveVersionIsComplete(); break; } // Take all actions necessary to update information and re-enable sidebar slotChanged(); } void EditorWindow::finishSaving(bool success) { m_savingContext.synchronousSavingResult = success; delete m_savingContext.saveTempFile; m_savingContext.saveTempFile = 0; // Exit of internal Qt event loop to unlock promptUserSave() method. if (m_savingContext.synchronizingState == SavingContext::SynchronousSaving) { quitWaitingLoop(); } // Enable actions as appropriate after saving toggleActions(true); unsetCursor(); m_animLogo->stop(); m_nameLabel->setProgressBarMode(StatusProgressBar::TextMode); /*if (m_savingProgressDialog) { m_savingProgressDialog->close(); }*/ // On error, continue using current image if (!success) { /* Why this? * m_canvas->switchToLastSaved(m_savingContext.srcURL.toLocalFile());*/ } } void EditorWindow::setupTempSaveFile(const QUrl& url) { // if the destination url is on local file system, try to set the temp file // location to the destination folder, otherwise use a local default QString tempDir = url.adjusted(QUrl::RemoveFilename|QUrl::StripTrailingSlash).toLocalFile(); if (!url.isLocalFile() || tempDir.isEmpty()) { tempDir = QDir::tempPath(); } QFileInfo fi(url.toLocalFile()); QString suffix = fi.suffix(); // use magic file extension which tells the digikamalbums ioslave to ignore the file m_savingContext.saveTempFile = new SafeTemporaryFile(tempDir + QLatin1String("/EditorWindow-XXXXXX.digikamtempfile.") + suffix); m_savingContext.saveTempFile->setAutoRemove(false); if (!m_savingContext.saveTempFile->open()) { QMessageBox::critical(this, qApp->applicationName(), i18n("Could not open a temporary file in the folder \"%1\": %2 (%3)", QDir::toNativeSeparators(tempDir), m_savingContext.saveTempFile->errorString(), m_savingContext.saveTempFile->error())); return; } m_savingContext.saveTempFileName = m_savingContext.saveTempFile->fileName(); delete m_savingContext.saveTempFile; m_savingContext.saveTempFile = 0; } void EditorWindow::startingSave(const QUrl& url) { qCDebug(DIGIKAM_GENERAL_LOG) << "startSaving url = " << url; // avoid any reentrancy. Should be impossible anyway since actions will be disabled. if (m_savingContext.savingState != SavingContext::SavingStateNone) { return; } m_savingContext = SavingContext(); if (!checkPermissions(url)) { return; } setupTempSaveFile(url); m_savingContext.srcURL = url; m_savingContext.destinationURL = m_savingContext.srcURL; m_savingContext.destinationExisted = true; m_savingContext.originalFormat = m_canvas->currentImageFileFormat(); m_savingContext.format = m_savingContext.originalFormat; m_savingContext.abortingSaving = false; m_savingContext.savingState = SavingContext::SavingStateSave; m_savingContext.executedOperation = SavingContext::SavingStateNone; m_canvas->interface()->saveAs(m_savingContext.saveTempFileName, m_IOFileSettings, m_setExifOrientationTag && m_canvas->exifRotated(), m_savingContext.format, m_savingContext.destinationURL.toLocalFile()); } bool EditorWindow::showFileSaveDialog(const QUrl& initialUrl, QUrl& newURL) { QString all; QStringList list = supportedImageMimeTypes(QIODevice::WriteOnly, all); DFileDialog* const imageFileSaveDialog = new DFileDialog(this); imageFileSaveDialog->setWindowTitle(i18n("New Image File Name")); imageFileSaveDialog->setDirectoryUrl(initialUrl.adjusted(QUrl::RemoveFilename | QUrl::StripTrailingSlash)); imageFileSaveDialog->setAcceptMode(QFileDialog::AcceptSave); imageFileSaveDialog->setFileMode(QFileDialog::AnyFile); imageFileSaveDialog->setNameFilters(list); // restore old settings for the dialog KSharedConfig::Ptr config = KSharedConfig::openConfig(); KConfigGroup group = config->group(configGroupName()); const QString optionLastExtension = QLatin1String("LastSavedImageExtension"); QString ext = group.readEntry(optionLastExtension, "png"); foreach(const QString& s, list) { if (s.contains(QString::fromLatin1("*.%1").arg(ext))) { imageFileSaveDialog->selectNameFilter(s); break; } } // adjust extension of proposed filename QString fileName = initialUrl.fileName(); if (!fileName.isNull()) { int lastDot = fileName.lastIndexOf(QLatin1Char('.')); QString completeBaseName = (lastDot == -1) ? fileName : fileName.left(lastDot); fileName = completeBaseName + QLatin1Char('.') + ext; } if (!fileName.isNull()) { imageFileSaveDialog->selectFile(fileName); } // Start dialog and check if canceled. int result; if (d->currentWindowModalDialog) { // go application-modal - we will create utter confusion if descending into more than one window-modal dialog imageFileSaveDialog->setModal(true); result = imageFileSaveDialog->exec(); } else { imageFileSaveDialog->setWindowModality(Qt::WindowModal); d->currentWindowModalDialog = imageFileSaveDialog; result = imageFileSaveDialog->exec(); d->currentWindowModalDialog = 0; } if (result != QDialog::Accepted || !imageFileSaveDialog) { qCDebug(DIGIKAM_GENERAL_LOG) << "File Save Dialog rejected"; return false; } QList urls = imageFileSaveDialog->selectedUrls(); if (urls.isEmpty()) { qCDebug(DIGIKAM_GENERAL_LOG) << "no target url"; return false; } newURL = urls.first(); newURL.setPath(QDir::cleanPath(newURL.path())); QFileInfo fi(newURL.fileName()); if (fi.suffix().isEmpty()) { ext = imageFileSaveDialog->selectedNameFilter().section(QLatin1String("*."), 1, 1); ext = ext.left(ext.length() - 1); if (ext.isEmpty()) { ext = QLatin1String("jpg"); } newURL.setPath(newURL.path() + QLatin1Char('.') + ext); } qCDebug(DIGIKAM_GENERAL_LOG) << "Writing file to " << newURL; //-- Show Settings Dialog ---------------------------------------------- const QString configShowImageSettingsDialog = QLatin1String("ShowImageSettingsDialog"); bool showDialog = group.readEntry(configShowImageSettingsDialog, true); FileSaveOptionsBox* const options = new FileSaveOptionsBox(); if (showDialog && options->discoverFormat(newURL.fileName(), DImg::NONE) != DImg::NONE) { FileSaveOptionsDlg* const fileSaveOptionsDialog = new FileSaveOptionsDlg(this, options); options->setImageFileFormat(newURL.fileName()); if (d->currentWindowModalDialog) { // go application-modal - we will create utter confusion if descending into more than one window-modal dialog fileSaveOptionsDialog->setModal(true); result = fileSaveOptionsDialog->exec(); } else { fileSaveOptionsDialog->setWindowModality(Qt::WindowModal); d->currentWindowModalDialog = fileSaveOptionsDialog; result = fileSaveOptionsDialog->exec(); d->currentWindowModalDialog = 0; } if (result != QDialog::Accepted || !fileSaveOptionsDialog) { return false; } } // write settings to config options->applySettings(); // read settings from config to local container applyIOSettings(); // select the format to save the image with m_savingContext.format = selectValidSavingFormat(newURL); if (m_savingContext.format.isNull()) { QMessageBox::critical(this, qApp->applicationName(), i18n("Unable to determine the format to save the target image with.")); return false; } if (!newURL.isValid()) { QMessageBox::critical(this, qApp->applicationName(), i18n("Cannot Save: Found file path %1 is invalid.", newURL.toDisplayString())); qCWarning(DIGIKAM_GENERAL_LOG) << "target URL is not valid !"; return false; } group.writeEntry(optionLastExtension, m_savingContext.format); config->sync(); return true; } QString EditorWindow::selectValidSavingFormat(const QUrl& targetUrl) { qCDebug(DIGIKAM_GENERAL_LOG) << "Trying to find a saving format from targetUrl = " << targetUrl; // build a list of valid types QString all; supportedImageMimeTypes(QIODevice::WriteOnly, all); qCDebug(DIGIKAM_GENERAL_LOG) << "Qt Offered types: " << all; QStringList validTypes = all.split(QLatin1String("*."), QString::SkipEmptyParts); validTypes.replaceInStrings(QLatin1String(" "), QString()); qCDebug(DIGIKAM_GENERAL_LOG) << "Writable formats: " << validTypes; // determine the format to use the format provided in the filename QString suffix; if (targetUrl.isLocalFile()) { // for local files QFileInfo can be used QFileInfo fi(targetUrl.toLocalFile()); suffix = fi.suffix(); qCDebug(DIGIKAM_GENERAL_LOG) << "Possible format from local file: " << suffix; } else { // for remote files string manipulation is needed unfortunately QString fileName = targetUrl.fileName(); const int periodLocation = fileName.lastIndexOf(QLatin1Char('.')); if (periodLocation >= 0) { suffix = fileName.right(fileName.size() - periodLocation - 1); } qCDebug(DIGIKAM_GENERAL_LOG) << "Possible format from remote file: " << suffix; } if (!suffix.isEmpty() && validTypes.contains(suffix, Qt::CaseInsensitive)) { qCDebug(DIGIKAM_GENERAL_LOG) << "Using format from target url " << suffix; return suffix; } // another way to determine the format is to use the original file { QString originalFormat = QString::fromUtf8(QImageReader::imageFormat(m_savingContext.srcURL.toLocalFile())); if (validTypes.contains(originalFormat, Qt::CaseInsensitive)) { qCDebug(DIGIKAM_GENERAL_LOG) << "Using format from original file: " << originalFormat; return originalFormat; } } qCDebug(DIGIKAM_GENERAL_LOG) << "No suitable format found"; return QString(); } bool EditorWindow::startingSaveAs(const QUrl& url) { qCDebug(DIGIKAM_GENERAL_LOG) << "startSavingAs called"; if (m_savingContext.savingState != SavingContext::SavingStateNone) { return false; } m_savingContext = SavingContext(); m_savingContext.srcURL = url; QUrl suggested = m_savingContext.srcURL; // Run dialog ------------------------------------------------------------------- QUrl newURL; if (!showFileSaveDialog(suggested, newURL)) { return false; } // if new and original URL are equal use save() ------------------------------ QUrl currURL(m_savingContext.srcURL); currURL.setPath(QDir::cleanPath(currURL.path())); newURL.setPath(QDir::cleanPath(newURL.path())); if (currURL.matches(newURL, QUrl::None)) { save(); return false; } // Check for overwrite ---------------------------------------------------------- QFileInfo fi(newURL.toLocalFile()); m_savingContext.destinationExisted = fi.exists(); if (m_savingContext.destinationExisted) { if (!checkOverwrite(newURL)) { return false; } // There will be two message boxes if the file is not writable. // This may be controversial, and it may be changed, but it was a deliberate decision. if (!checkPermissions(newURL)) { return false; } } // Now do the actual saving ----------------------------------------------------- setupTempSaveFile(newURL); m_savingContext.destinationURL = newURL; m_savingContext.originalFormat = m_canvas->currentImageFileFormat(); m_savingContext.savingState = SavingContext::SavingStateSaveAs; m_savingContext.executedOperation = SavingContext::SavingStateNone; m_savingContext.abortingSaving = false; // in any case, destructive (Save as) or non (Export), mark as New Version m_canvas->interface()->setHistoryIsBranch(true); m_canvas->interface()->saveAs(m_savingContext.saveTempFileName, m_IOFileSettings, m_setExifOrientationTag && m_canvas->exifRotated(), m_savingContext.format.toLower(), m_savingContext.destinationURL.toLocalFile()); return true; } bool EditorWindow::startingSaveCurrentVersion(const QUrl& url) { return startingSaveVersion(url, false, false, QString()); } bool EditorWindow::startingSaveNewVersion(const QUrl& url) { return startingSaveVersion(url, true, false, QString()); } bool EditorWindow::startingSaveNewVersionAs(const QUrl& url) { return startingSaveVersion(url, true, true, QString()); } bool EditorWindow::startingSaveNewVersionInFormat(const QUrl& url, const QString& format) { return startingSaveVersion(url, true, false, format); } VersionFileOperation EditorWindow::saveVersionFileOperation(const QUrl& url, bool fork) { DImageHistory resolvedHistory = m_canvas->interface()->getResolvedInitialHistory(); DImageHistory history = m_canvas->interface()->getItemHistory(); VersionFileInfo currentName(url.adjusted(QUrl::RemoveFilename | QUrl::StripTrailingSlash).toLocalFile(), url.fileName(), m_canvas->currentImageFileFormat()); return versionManager()->operation(fork ? VersionManager::NewVersionName : VersionManager::CurrentVersionName, currentName, resolvedHistory, history); } VersionFileOperation EditorWindow::saveAsVersionFileOperation(const QUrl& url, const QUrl& saveUrl, const QString& format) { DImageHistory resolvedHistory = m_canvas->interface()->getResolvedInitialHistory(); DImageHistory history = m_canvas->interface()->getItemHistory(); VersionFileInfo currentName(url.adjusted(QUrl::RemoveFilename | QUrl::StripTrailingSlash).toLocalFile(), url.fileName(), m_canvas->currentImageFileFormat()); VersionFileInfo saveLocation(saveUrl.adjusted(QUrl::RemoveFilename).toLocalFile(), saveUrl.fileName(), format); return versionManager()->operationNewVersionAs(currentName, saveLocation, resolvedHistory, history); } VersionFileOperation EditorWindow::saveInFormatVersionFileOperation(const QUrl& url, const QString& format) { DImageHistory resolvedHistory = m_canvas->interface()->getResolvedInitialHistory(); DImageHistory history = m_canvas->interface()->getItemHistory(); VersionFileInfo currentName(url.adjusted(QUrl::RemoveFilename | QUrl::StripTrailingSlash).toLocalFile(), url.fileName(), m_canvas->currentImageFileFormat()); return versionManager()->operationNewVersionInFormat(currentName, format, resolvedHistory, history); } bool EditorWindow::startingSaveVersion(const QUrl& url, bool fork, bool saveAs, const QString& format) { qCDebug(DIGIKAM_GENERAL_LOG) << "Saving image" << url << "non-destructive, new version:" << fork << ", saveAs:" << saveAs << "format:" << format; if (m_savingContext.savingState != SavingContext::SavingStateNone) { return false; } m_savingContext = SavingContext(); m_savingContext.versionFileOperation = saveVersionFileOperation(url, fork); m_canvas->interface()->setHistoryIsBranch(fork); if (saveAs) { QUrl suggested = m_savingContext.versionFileOperation.saveFile.fileUrl(); QUrl selectedUrl; if (!showFileSaveDialog(suggested, selectedUrl)) { return false; } m_savingContext.versionFileOperation = saveAsVersionFileOperation(url, selectedUrl, m_savingContext.format); } else if (!format.isNull()) { m_savingContext.versionFileOperation = saveInFormatVersionFileOperation(url, format); } const QUrl newURL = m_savingContext.versionFileOperation.saveFile.fileUrl(); qCDebug(DIGIKAM_GENERAL_LOG) << "Writing file to " << newURL; if (!newURL.isValid()) { QMessageBox::critical(this, qApp->applicationName(), i18nc("@info", "Cannot save file %1 to " "the suggested version file name %2", url.fileName(), newURL.fileName())); qCWarning(DIGIKAM_GENERAL_LOG) << "target URL is not valid !"; return false; } QFileInfo fi(newURL.toLocalFile()); m_savingContext.destinationExisted = fi.exists(); // Check for overwrite (saveAs only) -------------------------------------------- if (m_savingContext.destinationExisted) { // So, should we refuse to overwrite the original? // It's a frontal crash against non-destructive principles. // It is tempting to refuse, yet I think the user has to decide in the end /*QUrl currURL(m_savingContext.srcURL); currURL.cleanPath(); newURL.cleanPath(); if (currURL.equals(newURL)) { ... return false; }*/ // check for overwrite, unless the operation explicitly tells us to overwrite if (!(m_savingContext.versionFileOperation.tasks & VersionFileOperation::Replace) && !checkOverwrite(newURL)) { return false; } // There will be two message boxes if the file is not writable. // This may be controversial, and it may be changed, but it was a deliberate decision. if (!checkPermissions(newURL)) { return false; } } setupTempSaveFile(newURL); m_savingContext.srcURL = url; m_savingContext.destinationURL = newURL; m_savingContext.originalFormat = m_canvas->currentImageFileFormat(); m_savingContext.format = m_savingContext.versionFileOperation.saveFile.format; m_savingContext.abortingSaving = false; m_savingContext.savingState = SavingContext::SavingStateVersion; m_savingContext.executedOperation = SavingContext::SavingStateNone; m_canvas->interface()->saveAs(m_savingContext.saveTempFileName, m_IOFileSettings, m_setExifOrientationTag && m_canvas->exifRotated(), m_savingContext.format.toLower(), m_savingContext.versionFileOperation); return true; } bool EditorWindow::checkPermissions(const QUrl& url) { //TODO: Check that the permissions can actually be changed // if write permissions are not available. QFileInfo fi(url.toLocalFile()); if (fi.exists() && !fi.isWritable()) { int result = QMessageBox::warning(this, i18n("Overwrite File?"), i18n("You do not have write permissions " "for the file named \"%1\". " "Are you sure you want " "to overwrite it?", url.fileName()), QMessageBox::Save | QMessageBox::Cancel); if (result != QMessageBox::Save) { return false; } } return true; } bool EditorWindow::checkOverwrite(const QUrl& url) { int result = QMessageBox::warning(this, i18n("Overwrite File?"), i18n("A file named \"%1\" already " "exists. Are you sure you want " "to overwrite it?", url.fileName()), QMessageBox::Save | QMessageBox::Cancel); return (result == QMessageBox::Save); } bool EditorWindow::moveLocalFile(const QString& org, const QString& dst) { QString sidecarOrg = DMetadata::sidecarFilePathForFile(org); QString source = m_savingContext.srcURL.toLocalFile(); if (QFileInfo(sidecarOrg).exists()) { QString sidecarDst = DMetadata::sidecarFilePathForFile(dst); if (!DFileOperations::localFileRename(source, sidecarOrg, sidecarDst)) { qCDebug(DIGIKAM_GENERAL_LOG) << "Failed to move sidecar file"; } } if (!DFileOperations::localFileRename(source, org, dst)) { QMessageBox::critical(this, i18n("Error Saving File"), i18n("Failed to overwrite original file")); return false; } return true; } void EditorWindow::moveFile() { // Move local file. if (m_savingContext.executedOperation == SavingContext::SavingStateVersion) { // check if we need to move the current file to an intermediate name if (m_savingContext.versionFileOperation.tasks & VersionFileOperation::MoveToIntermediate) { //qCDebug(DIGIKAM_GENERAL_LOG) << "MoveToIntermediate: Moving " << m_savingContext.srcURL.toLocalFile() << "to" // << m_savingContext.versionFileOperation.intermediateForLoadedFile.filePath() moveLocalFile(m_savingContext.srcURL.toLocalFile(), m_savingContext.versionFileOperation.intermediateForLoadedFile.filePath()); LoadingCacheInterface::fileChanged(m_savingContext.destinationURL.toLocalFile()); ThumbnailLoadThread::deleteThumbnail(m_savingContext.destinationURL.toLocalFile()); } } bool moveSuccessful = moveLocalFile(m_savingContext.saveTempFileName, m_savingContext.destinationURL.toLocalFile()); if (m_savingContext.executedOperation == SavingContext::SavingStateVersion) { if (moveSuccessful && m_savingContext.versionFileOperation.tasks & VersionFileOperation::SaveAndDelete) { QFile file(m_savingContext.versionFileOperation.loadedFile.filePath()); file.remove(); } } movingSaveFileFinished(moveSuccessful); } void EditorWindow::slotDiscardChanges() { m_canvas->interface()->rollbackToOrigin(); } void EditorWindow::slotOpenOriginal() { // no-op in this base class } void EditorWindow::slotColorManagementOptionsChanged() { applyColorManagementSettings(); applyIOSettings(); } void EditorWindow::slotToggleColorManagedView() { if (!IccSettings::instance()->isEnabled()) { return; } bool cmv = !IccSettings::instance()->settings().useManagedView; IccSettings::instance()->setUseManagedView(cmv); } void EditorWindow::setColorManagedViewIndicatorToolTip(bool available, bool cmv) { QString tooltip; if (available) { if (cmv) { tooltip = i18n("Color-Managed View is enabled."); } else { tooltip = i18n("Color-Managed View is disabled."); } } else { tooltip = i18n("Color Management is not configured, so the Color-Managed View is not available."); } d->cmViewIndicator->setToolTip(tooltip); } void EditorWindow::slotSoftProofingOptions() { // Adjusts global settings QPointer dlg = new SoftProofDialog(this); dlg->exec(); d->viewSoftProofAction->setChecked(dlg->shallEnableSoftProofView()); slotUpdateSoftProofingState(); delete dlg; } void EditorWindow::slotUpdateSoftProofingState() { bool on = d->viewSoftProofAction->isChecked(); m_canvas->setSoftProofingEnabled(on); d->toolIface->updateICCSettings(); } void EditorWindow::slotSetUnderExposureIndicator(bool on) { d->exposureSettings->underExposureIndicator = on; d->toolIface->updateExposureSettings(); d->viewUnderExpoAction->setChecked(on); setUnderExposureToolTip(on); } void EditorWindow::setUnderExposureToolTip(bool on) { d->underExposureIndicator->setToolTip( on ? i18n("Under-Exposure indicator is enabled") : i18n("Under-Exposure indicator is disabled")); } void EditorWindow::slotSetOverExposureIndicator(bool on) { d->exposureSettings->overExposureIndicator = on; d->toolIface->updateExposureSettings(); d->viewOverExpoAction->setChecked(on); setOverExposureToolTip(on); } void EditorWindow::setOverExposureToolTip(bool on) { d->overExposureIndicator->setToolTip( on ? i18n("Over-Exposure indicator is enabled") : i18n("Over-Exposure indicator is disabled")); } void EditorWindow::slotToggleSlideShow() { SlideShowSettings settings; settings.readFromConfig(); slideShow(settings); } void EditorWindow::slotSelectionChanged(const QRect& sel) { slotSelectionSetText(sel); emit signalSelectionChanged(sel); } void EditorWindow::slotSelectionSetText(const QRect& sel) { setToolInfoMessage(QString::fromLatin1("(%1, %2) (%3 x %4)").arg(sel.x()).arg(sel.y()).arg(sel.width()).arg(sel.height())); } void EditorWindow::slotComponentsInfo() { LibsInfoDlg* const dlg = new LibsInfoDlg(this); dlg->show(); } void EditorWindow::setToolStartProgress(const QString& toolName) { m_animLogo->start(); m_nameLabel->setProgressValue(0); m_nameLabel->setProgressBarMode(StatusProgressBar::CancelProgressBarMode, QString::fromUtf8("%1:").arg(toolName)); } void EditorWindow::setToolProgress(int progress) { m_nameLabel->setProgressValue(progress); } void EditorWindow::setToolStopProgress() { m_animLogo->stop(); m_nameLabel->setProgressValue(0); m_nameLabel->setProgressBarMode(StatusProgressBar::TextMode); slotUpdateItemInfo(); } void EditorWindow::slotCloseTool() { if (d->toolIface) { d->toolIface->slotCloseTool(); } } void EditorWindow::slotApplyTool() { if (d->toolIface) { d->toolIface->slotApplyTool(); } } void EditorWindow::setPreviewModeMask(int mask) { d->previewToolBar->setPreviewModeMask(mask); } PreviewToolBar::PreviewMode EditorWindow::previewMode() const { return d->previewToolBar->previewMode(); } void EditorWindow::setToolInfoMessage(const QString& txt) { d->infoLabel->setAdjustedText(txt); } VersionManager* EditorWindow::versionManager() const { return &d->defaultVersionManager; } void EditorWindow::setupSelectToolsAction() { // Create action model ActionItemModel* const actionModel = new ActionItemModel(this); actionModel->setMode(ActionItemModel::ToplevelMenuCategory | ActionItemModel::SortCategoriesByInsertionOrder); // Builtin actions QString transformCategory = i18nc("@title Image Transform", "Transform"); actionModel->addAction(d->rotateLeftAction, transformCategory); actionModel->addAction(d->rotateRightAction, transformCategory); actionModel->addAction(d->flipHorizAction, transformCategory); actionModel->addAction(d->flipVertAction, transformCategory); actionModel->addAction(d->cropAction, transformCategory); actionModel->addAction(d->autoCropAction, transformCategory); actionModel->addAction(d->aspectRatioCropAction, transformCategory); actionModel->addAction(d->resizeAction, transformCategory); actionModel->addAction(d->sheartoolAction, transformCategory); actionModel->addAction(d->freerotationAction, transformCategory); actionModel->addAction(d->perspectiveAction, transformCategory); #ifdef HAVE_LIBLQR_1 actionModel->addAction(d->contentAwareResizingAction, transformCategory); #endif QString decorateCategory = i18nc("@title Image Decorate", "Decorate"); actionModel->addAction(d->textureAction, decorateCategory); actionModel->addAction(d->borderAction, decorateCategory); actionModel->addAction(d->insertTextAction, decorateCategory); QString effectsCategory = i18nc("@title Image Effect", "Effects"); actionModel->addAction(d->filmgrainAction, effectsCategory); actionModel->addAction(d->raindropAction, effectsCategory); actionModel->addAction(d->distortionfxAction, effectsCategory); actionModel->addAction(d->blurfxAction, effectsCategory); actionModel->addAction(d->oilpaintAction, effectsCategory); actionModel->addAction(d->embossAction, effectsCategory); actionModel->addAction(d->charcoalAction, effectsCategory); actionModel->addAction(d->colorEffectsAction, effectsCategory); QString colorsCategory = i18nc("@title Image Colors", "Colors"); actionModel->addAction(d->convertTo8Bits, colorsCategory); actionModel->addAction(d->convertTo16Bits, colorsCategory); actionModel->addAction(d->invertAction, colorsCategory); actionModel->addAction(d->BCGAction, colorsCategory); actionModel->addAction(d->CBAction, colorsCategory); actionModel->addAction(d->autoCorrectionAction, colorsCategory); actionModel->addAction(d->BWAction, colorsCategory); actionModel->addAction(d->HSLAction, colorsCategory); actionModel->addAction(d->whitebalanceAction, colorsCategory); actionModel->addAction(d->channelMixerAction, colorsCategory); actionModel->addAction(d->curvesAction, colorsCategory); actionModel->addAction(d->levelsAction, colorsCategory); actionModel->addAction(d->filmAction, colorsCategory); actionModel->addAction(d->colorSpaceConverter, colorsCategory); QString enhanceCategory = i18nc("@title Image Enhance", "Enhance"); actionModel->addAction(d->restorationAction, enhanceCategory); actionModel->addAction(d->blurAction, enhanceCategory); //actionModel->addAction(d->healCloneAction, enhanceCategory); actionModel->addAction(d->sharpenAction, enhanceCategory); actionModel->addAction(d->noiseReductionAction, enhanceCategory); actionModel->addAction(d->localContrastAction, enhanceCategory); actionModel->addAction(d->redeyeAction, enhanceCategory); actionModel->addAction(d->lensdistortionAction, enhanceCategory); actionModel->addAction(d->antivignettingAction, enhanceCategory); actionModel->addAction(d->hotpixelsAction, enhanceCategory); #ifdef HAVE_LENSFUN actionModel->addAction(d->lensAutoFixAction, enhanceCategory); #endif QString postCategory = i18nc("@title Post Processing Tools", "Post-Processing"); actionModel->addAction(m_calendarAction, postCategory); actionModel->addAction(m_metadataEditAction, postCategory); actionModel->addAction(m_timeAdjustAction, postCategory); actionModel->addAction(m_presentationAction, postCategory); actionModel->addAction(m_expoBlendingAction, postCategory); actionModel->addAction(m_sendByMailAction, postCategory); actionModel->addAction(m_printCreatorAction, postCategory); actionModel->addAction(m_mediaServerAction, postCategory); #ifdef HAVE_HTMLGALLERY actionModel->addAction(m_htmlGalleryAction, postCategory); #endif #ifdef HAVE_PANORAMA actionModel->addAction(m_panoramaAction, postCategory); #endif #ifdef HAVE_MEDIAPLAYER actionModel->addAction(m_videoslideshowAction, postCategory); #endif #ifdef HAVE_MARBLE actionModel->addAction(m_geolocationEditAction, postCategory); #endif QString exportCategory = i18nc("@title Export Tools", "Export"); foreach(QAction* const ac, exportActions()) { actionModel->addAction(ac, exportCategory); } QString importCategory = i18nc("@title Import Tools", "Import"); foreach(QAction* const ac, importActions()) { actionModel->addAction(ac, importCategory); } // setup categorized view DCategorizedSortFilterProxyModel* const filterModel = actionModel->createFilterModel(); ActionCategorizedView* const selectToolsActionView = new ActionCategorizedView; selectToolsActionView->setupIconMode(); selectToolsActionView->setModel(filterModel); selectToolsActionView->adjustGridSize(); connect(selectToolsActionView, SIGNAL(clicked(QModelIndex)), actionModel, SLOT(trigger(QModelIndex))); EditorToolIface::editorToolIface()->setToolsIconView(selectToolsActionView); } void EditorWindow::slotThemeChanged() { KSharedConfig::Ptr config = KSharedConfig::openConfig(); KConfigGroup group = config->group(configGroupName()); if (!group.readEntry(d->configUseThemeBackgroundColorEntry, true)) { m_bgColor = group.readEntry(d->configBackgroundColorEntry, QColor(Qt::black)); } else { m_bgColor = palette().color(QPalette::Base); } m_canvas->setBackgroundBrush(QBrush(m_bgColor)); d->toolIface->themeChanged(); } void EditorWindow::addAction2ContextMenu(const QString& actionName, bool addDisabled) { if (!m_contextMenu) { return; } QAction* const action = actionCollection()->action(actionName); if (action && (action->isEnabled() || addDisabled)) { m_contextMenu->addAction(action); } } void EditorWindow::showSideBars(bool visible) { if (visible) { rightSideBar()->restore(QList() << thumbBar(), d->fullscreenSizeBackup); } else { // See bug #166472, a simple backup()/restore() will hide non-sidebar splitter child widgets // in horizontal mode thumbbar wont be member of the splitter, it is just ignored then rightSideBar()->backup(QList() << thumbBar(), &d->fullscreenSizeBackup); } } void EditorWindow::slotToggleRightSideBar() { rightSideBar()->isExpanded() ? rightSideBar()->shrink() : rightSideBar()->expand(); } void EditorWindow::slotPreviousRightSideBarTab() { rightSideBar()->activePreviousTab(); } void EditorWindow::slotNextRightSideBarTab() { rightSideBar()->activeNextTab(); } void EditorWindow::showThumbBar(bool visible) { visible ? thumbBar()->restoreVisibility() : thumbBar()->hide(); } bool EditorWindow::thumbbarVisibility() const { return thumbBar()->isVisible(); } void EditorWindow::customizedFullScreenMode(bool set) { set ? m_canvas->setBackgroundBrush(QBrush(Qt::black)) : m_canvas->setBackgroundBrush(QBrush(m_bgColor)); showStatusBarAction()->setEnabled(!set); toolBarMenuAction()->setEnabled(!set); showMenuBarAction()->setEnabled(!set); m_showBarAction->setEnabled(!set); } void EditorWindow::addServicesMenuForUrl(const QUrl& url) { KService::List offers = DFileOperations::servicesForOpenWith(QList() << url); qCDebug(DIGIKAM_GENERAL_LOG) << offers.count() << " services found to open " << url; if (m_servicesMenu) { delete m_servicesMenu; m_servicesMenu = 0; } if (m_serviceAction) { delete m_serviceAction; m_serviceAction = 0; } if (!offers.isEmpty()) { m_servicesMenu = new QMenu(this); QAction* const serviceAction = m_servicesMenu->menuAction(); serviceAction->setText(i18n("Open With")); foreach(const KService::Ptr& service, offers) { QString name = service->name().replace(QLatin1Char('&'), QLatin1String("&&")); QAction* const action = m_servicesMenu->addAction(name); action->setIcon(QIcon::fromTheme(service->icon())); action->setData(service->name()); d->servicesMap[name] = service; } #ifdef HAVE_KIO m_servicesMenu->addSeparator(); m_servicesMenu->addAction(i18n("Other...")); m_contextMenu->addAction(serviceAction); connect(m_servicesMenu, SIGNAL(triggered(QAction*)), this, SLOT(slotOpenWith(QAction*))); } else { m_serviceAction = new QAction(i18n("Open With..."), this); m_contextMenu->addAction(m_serviceAction); connect(m_servicesMenu, SIGNAL(triggered()), this, SLOT(slotOpenWith())); #endif // HAVE_KIO } } void EditorWindow::openWith(const QUrl& url, QAction* action) { KService::Ptr service; QString name = action ? action->data().toString() : QString(); #ifdef HAVE_KIO if (name.isEmpty()) { QPointer dlg = new KOpenWithDialog(QList() << url); if (dlg->exec() != KOpenWithDialog::Accepted) { delete dlg; return; } service = dlg->service(); if (!service) { // User entered a custom command if (!dlg->text().isEmpty()) { DFileOperations::runFiles(dlg->text(), QList() << url); } delete dlg; return; } delete dlg; } else #endif // HAVE_KIO { service = d->servicesMap[name]; } DFileOperations::runFiles(service.data(), QList() << url); } void EditorWindow::loadTool(EditorTool* const tool) { EditorToolIface::editorToolIface()->loadTool(tool); connect(tool, SIGNAL(okClicked()), this, SLOT(slotToolDone())); connect(tool, SIGNAL(cancelClicked()), this, SLOT(slotToolDone())); } void EditorWindow::slotToolDone() { EditorToolIface::editorToolIface()->unLoadTool(); } void EditorWindow::slotInsertText() { loadTool(new InsertTextTool(this)); } void EditorWindow::slotBorder() { loadTool(new BorderTool(this)); } void EditorWindow::slotTexture() { loadTool(new TextureTool(this)); } void EditorWindow::slotColorEffects() { loadTool(new ColorFxTool(this)); } void EditorWindow::slotCharcoal() { loadTool(new CharcoalTool(this)); } void EditorWindow::slotEmboss() { loadTool(new EmbossTool(this)); } void EditorWindow::slotOilPaint() { loadTool(new OilPaintTool(this)); } void EditorWindow::slotBlurFX() { loadTool(new BlurFXTool(this)); } void EditorWindow::slotDistortionFX() { loadTool(new DistortionFXTool(this)); } void EditorWindow::slotRainDrop() { loadTool(new RainDropTool(this)); } void EditorWindow::slotFilmGrain() { loadTool(new FilmGrainTool(this)); } void EditorWindow::slotInvert() { qApp->setOverrideCursor(Qt::WaitCursor); ImageIface iface; InvertFilter invert(iface.original(), 0L); invert.startFilterDirectly(); iface.setOriginal(i18n("Invert"), invert.filterAction(), invert.getTargetImage()); qApp->restoreOverrideCursor(); } void EditorWindow::slotConvertTo8Bits() { ImageIface iface; if (!iface.originalSixteenBit()) { QMessageBox::critical(qApp->activeWindow(), qApp->applicationName(), i18n("This image is already using a depth of 8 bits / color / pixel.")); return; } else { if (DMessageBox::showContinueCancel(QMessageBox::Warning, qApp->activeWindow(), qApp->applicationName(), i18n("Performing this operation will reduce image color quality. " "Do you want to continue?"), QLatin1String("ToolColor16To8Bits")) == QMessageBox::Cancel) { return; } } qApp->setOverrideCursor(Qt::WaitCursor); iface.convertOriginalColorDepth(32); qApp->restoreOverrideCursor(); } void EditorWindow::slotConvertTo16Bits() { ImageIface iface; if (iface.originalSixteenBit()) { QMessageBox::critical(qApp->activeWindow(), qApp->applicationName(), i18n("This image is already using a depth of 16 bits / color / pixel.")); return; } qApp->setOverrideCursor(Qt::WaitCursor); iface.convertOriginalColorDepth(64); qApp->restoreOverrideCursor(); } void EditorWindow::slotConvertToColorSpace(const IccProfile& profile) { ImageIface iface; if (iface.originalIccProfile().isNull()) { QMessageBox::critical(qApp->activeWindow(), qApp->applicationName(), i18n("This image is not color managed.")); return; } qApp->setOverrideCursor(Qt::WaitCursor); ProfileConversionTool::fastConversion(profile); qApp->restoreOverrideCursor(); } void EditorWindow::slotUpdateColorSpaceMenu() { d->profileMenuAction->clear(); if (!IccSettings::instance()->isEnabled()) { QAction* const action = new QAction(i18n("Color Management is disabled..."), this); d->profileMenuAction->addAction(action); connect(action, SIGNAL(triggered()), this, SLOT(slotSetupICC())); } else { ICCSettingsContainer settings = IccSettings::instance()->settings(); QList standardProfiles, favoriteProfiles; QSet standardProfilePaths, favoriteProfilePaths; standardProfiles << IccProfile::sRGB() << IccProfile::adobeRGB() << IccProfile::wideGamutRGB() << IccProfile::proPhotoRGB(); foreach(IccProfile profile, standardProfiles) // krazy:exclude=foreach { d->profileMenuAction->addProfile(profile, profile.description()); standardProfilePaths << profile.filePath(); } d->profileMenuAction->addSeparator(); favoriteProfilePaths = QSet::fromList(ProfileConversionTool::favoriteProfiles()); favoriteProfilePaths -= standardProfilePaths; foreach(const QString& path, favoriteProfilePaths) { favoriteProfiles << IccProfile(path); } d->profileMenuAction->addProfiles(favoriteProfiles); } d->profileMenuAction->addSeparator(); d->profileMenuAction->addAction(d->colorSpaceConverter); d->colorSpaceConverter->setEnabled(m_actionEnabledState && IccSettings::instance()->isEnabled()); } void EditorWindow::slotProfileConversionTool() { ProfileConversionTool* const tool = new ProfileConversionTool(this); connect(tool, SIGNAL(okClicked()), this, SLOT(slotUpdateColorSpaceMenu())); loadTool(tool); } void EditorWindow::slotBW() { loadTool(new BWSepiaTool(this)); } void EditorWindow::slotHSL() { loadTool(new HSLTool(this)); } void EditorWindow::slotWhiteBalance() { loadTool(new WhiteBalanceTool(this)); } void EditorWindow::slotChannelMixer() { loadTool(new ChannelMixerTool(this)); } void EditorWindow::slotCurvesAdjust() { loadTool(new AdjustCurvesTool(this)); } void EditorWindow::slotLevelsAdjust() { loadTool(new AdjustLevelsTool(this)); } void EditorWindow::slotFilm() { loadTool(new FilmTool(this)); } void EditorWindow::slotBCG() { loadTool(new BCGTool(this)); } void EditorWindow::slotCB() { loadTool(new CBTool(this)); } void EditorWindow::slotAutoCorrection() { loadTool(new AutoCorrectionTool(this)); } void EditorWindow::slotHotPixels() { loadTool(new HotPixelsTool(this)); } void EditorWindow::slotLensDistortion() { loadTool(new LensDistortionTool(this)); } void EditorWindow::slotRestoration() { loadTool(new RestorationTool(this)); } void EditorWindow::slotBlur() { loadTool(new BlurTool(this)); } void EditorWindow::slotHealingClone() { loadTool(new HealingCloneTool(this)); } void EditorWindow::slotSharpen() { loadTool(new SharpenTool(this)); } void EditorWindow::slotNoiseReduction() { loadTool(new NoiseReductionTool(this)); } void EditorWindow::slotLocalContrast() { loadTool(new LocalContrastTool(this)); } void EditorWindow::slotRedEye() { loadTool(new RedEyeTool(this)); } void EditorWindow::slotLensAutoFix() { #ifdef HAVE_LENSFUN loadTool(new LensAutoFixTool(this)); #endif } void EditorWindow::slotAntiVignetting() { loadTool(new AntiVignettingTool(this)); } void EditorWindow::slotPerspective() { loadTool(new PerspectiveTool(this)); } void EditorWindow::slotShearTool() { loadTool(new ShearTool(this)); } void EditorWindow::slotResize() { loadTool(new ResizeTool(this)); } void EditorWindow::slotRatioCrop() { loadTool(new RatioCropTool(this)); } void EditorWindow::slotContentAwareResizing() { #ifdef HAVE_LIBLQR_1 loadTool(new ContentAwareResizeTool(this)); #endif } void EditorWindow::slotFreeRotation() { FreeRotationTool* const tool = new FreeRotationTool(this); connect(this, SIGNAL(signalPoint1Action()), tool, SLOT(slotAutoAdjustP1Clicked())); connect(this, SIGNAL(signalPoint2Action()), tool, SLOT(slotAutoAdjustP2Clicked())); connect(this, SIGNAL(signalAutoAdjustAction()), tool, SLOT(slotAutoAdjustClicked())); loadTool(tool); } void EditorWindow::slotRotateLeftIntoQue() { m_transformQue.append(TransformType::RotateLeft); } void EditorWindow::slotRotateRightIntoQue() { m_transformQue.append(TransformType::RotateRight); } void EditorWindow::slotFlipHIntoQue() { m_transformQue.append(TransformType::FlipHorizontal); } void EditorWindow::slotFlipVIntoQue() { m_transformQue.append(TransformType::FlipVertical); } } // namespace Digikam diff --git a/core/utilities/imageeditor/main/imagewindow_setup.cpp b/core/utilities/imageeditor/main/imagewindow_setup.cpp index d03eb9310e..3be84191fe 100644 --- a/core/utilities/imageeditor/main/imagewindow_setup.cpp +++ b/core/utilities/imageeditor/main/imagewindow_setup.cpp @@ -1,310 +1,310 @@ /* ============================================================ * * This file is a part of digiKam project * http://www.digikam.org * * Date : 2004-11-22 * Description : digiKam image editor - Internal setup * * Copyright (C) 2004-2019 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 "imagewindow.h" #include "imagewindow_p.h" namespace Digikam { void ImageWindow::setupActions() { setupStandardActions(); KActionCollection* const ac = actionCollection(); d->toMainWindowAction = new QAction(QIcon::fromTheme(QLatin1String("view-list-icons")), i18nc("@action Finish editing, close editor, back to main window", "Close Editor"), this); connect(d->toMainWindowAction, SIGNAL(triggered()), this, SLOT(slotToMainWindow())); ac->addAction(QLatin1String("imageview_tomainwindow"), d->toMainWindowAction); // -- Special Delete actions --------------------------------------------------------------- // Pop up dialog to ask user whether to permanently delete d->fileDeletePermanentlyAction = new QAction(QIcon::fromTheme(QLatin1String("edit-delete")), i18n("Delete File Permanently"), this); connect(d->fileDeletePermanentlyAction, SIGNAL(triggered()), this, SLOT(slotDeleteCurrentItemPermanently())); ac->addAction(QLatin1String("image_delete_permanently"), d->fileDeletePermanentlyAction); ac->setDefaultShortcut(d->fileDeletePermanentlyAction, Qt::SHIFT + Qt::Key_Delete); // These two actions are hidden, no menu entry, no toolbar entry, no shortcut. // Power users may add them. d->fileDeletePermanentlyDirectlyAction = new QAction(QIcon::fromTheme(QLatin1String("edit-delete")), i18n("Delete Permanently without Confirmation"), this); connect(d->fileDeletePermanentlyDirectlyAction, SIGNAL(triggered()), this, SLOT(slotDeleteCurrentItemPermanentlyDirectly())); ac->addAction(QLatin1String("image_delete_permanently_directly"), d->fileDeletePermanentlyDirectlyAction); - d->fileTrashDirectlyAction = new QAction(QIcon::fromTheme(QLatin1String("user-trash")), + d->fileTrashDirectlyAction = new QAction(QIcon::fromTheme(QLatin1String("user-trash-full")), i18n("Move to Trash without Confirmation"), this); connect(d->fileTrashDirectlyAction, SIGNAL(triggered()), this, SLOT(slotTrashCurrentItemDirectly())); ac->addAction(QLatin1String("image_trash_directly"), d->fileTrashDirectlyAction); // --------------------------------------------------------------------------------- createHelpActions(); // Labels shortcuts must be registered here to be saved in XML GUI files if user customize it. TagsActionMngr::defaultManager()->registerLabelsActions(ac); QAction* const editTitles = new QAction(i18n("Edit Titles"), this); ac->addAction(QLatin1String("edit_titles"), editTitles); ac->setDefaultShortcut(editTitles, Qt::META + Qt::Key_T); connect(editTitles, SIGNAL(triggered()), this, SLOT(slotRightSideBarActivateTitles())); QAction* const editComments = new QAction(i18n("Edit Comments"), this); ac->addAction(QLatin1String("edit_comments"), editComments); ac->setDefaultShortcut(editComments, Qt::META + Qt::Key_C); connect(editComments, SIGNAL(triggered()), this, SLOT(slotRightSideBarActivateComments())); QAction* const assignedTags = new QAction(i18n("Show Assigned Tags"), this); ac->addAction(QLatin1String("assigned _tags"), assignedTags); ac->setDefaultShortcut(assignedTags, Qt::META + Qt::Key_A); connect(assignedTags, SIGNAL(triggered()), this, SLOT(slotRightSideBarActivateAssignedTags())); } void ImageWindow::setupConnections() { setupStandardConnections(); connect(this, SIGNAL(loadCurrentLater()), this, SLOT(slotLoadCurrent()), Qt::QueuedConnection); // To toggle properly keyboards shortcuts from comments & tags side bar tab. connect(d->rightSideBar, SIGNAL(signalNextItem()), this, SLOT(slotForward())); connect(d->rightSideBar, SIGNAL(signalPrevItem()), this, SLOT(slotBackward())); connect(d->rightSideBar->getFiltersHistoryTab(), SIGNAL(actionTriggered(ItemInfo)), this, SLOT(openImage(ItemInfo))); connect(this, SIGNAL(signalSelectionChanged(QRect)), d->rightSideBar, SLOT(slotImageSelectionChanged(QRect))); connect(this, SIGNAL(signalNoCurrentItem()), d->rightSideBar, SLOT(slotNoCurrentItem())); ItemAttributesWatch* watch = ItemAttributesWatch::instance(); connect(watch, SIGNAL(signalFileMetadataChanged(QUrl)), this, SLOT(slotFileMetadataChanged(QUrl))); /* connect(CoreDbAccess::databaseWatch(), SIGNAL(collectionImageChange(CollectionImageChangeset)), this, SLOT(slotCollectionImageChange(CollectionImageChangeset)), Qt::QueuedConnection); connect(d->imageFilterModel, SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int)), this, SLOT(slotRowsAboutToBeRemoved(QModelIndex,int,int))); */ connect(d->thumbBar, SIGNAL(currentChanged(ItemInfo)), this, SLOT(slotThumbBarImageSelected(ItemInfo))); connect(d->dragDropHandler, SIGNAL(itemInfosDropped(QList)), this, SLOT(slotDroppedOnThumbbar(QList))); connect(d->thumbBarDock, SIGNAL(dockLocationChanged(Qt::DockWidgetArea)), d->thumbBar, SLOT(slotDockLocationChanged(Qt::DockWidgetArea))); connect(d->imageInfoModel, SIGNAL(allRefreshingFinished()), this, SLOT(slotThumbBarModelReady())); connect(ApplicationSettings::instance(), SIGNAL(setupChanged()), this, SLOT(slotSetupChanged())); } void ImageWindow::setupUserArea() { KSharedConfig::Ptr config = KSharedConfig::openConfig(); KConfigGroup group = config->group(configGroupName()); QWidget* const widget = new QWidget(this); QHBoxLayout* const hlay = new QHBoxLayout(widget); m_splitter = new SidebarSplitter(widget); d->viewContainer = new KMainWindow(widget, Qt::Widget); m_splitter->addWidget(d->viewContainer); m_stackView = new EditorStackView(d->viewContainer); m_canvas = new Canvas(m_stackView); d->viewContainer->setCentralWidget(m_stackView); m_splitter->setFrameStyle(QFrame::NoFrame); m_splitter->setFrameShape(QFrame::NoFrame); m_splitter->setFrameShadow(QFrame::Plain); m_splitter->setStretchFactor(0, 10); // set Canvas default size to max. m_splitter->setOpaqueResize(false); m_canvas->makeDefaultEditingCanvas(); m_stackView->setCanvas(m_canvas); m_stackView->setViewMode(EditorStackView::CanvasMode); d->rightSideBar = new ItemPropertiesSideBarDB(widget, m_splitter, Qt::RightEdge, true); d->rightSideBar->setObjectName(QLatin1String("ImageEditor Right Sidebar")); d->rightSideBar->getFiltersHistoryTab()->addOpenImageAction(); hlay->addWidget(m_splitter); hlay->addWidget(d->rightSideBar); hlay->setContentsMargins(QMargins()); hlay->setSpacing(0); // Code to check for the now depreciated HorizontalThumbar directive. It // is found, it is honored and deleted. The state will from than on be saved // by d->viewContainers built-in mechanism. Qt::DockWidgetArea dockArea = Qt::LeftDockWidgetArea; if (group.hasKey(d->configHorizontalThumbbarEntry)) { if (group.readEntry(d->configHorizontalThumbbarEntry, true)) { // Horizontal thumbbar layout dockArea = Qt::TopDockWidgetArea; } group.deleteEntry(d->configHorizontalThumbbarEntry); } d->imageInfoModel = new ItemListModel(this); d->imageFilterModel = new ItemFilterModel(this); d->imageFilterModel->setSourceItemModel(d->imageInfoModel); d->imageInfoModel->setWatchFlags(d->imageFilterModel->suggestedWatchFlags()); d->imageInfoModel->setThumbnailLoadThread(ThumbnailLoadThread::defaultIconViewThread()); d->imageFilterModel->setCategorizationMode(ItemSortSettings::NoCategories); d->imageFilterModel->setStringTypeNatural(ApplicationSettings::instance()->isStringTypeNatural()); d->imageFilterModel->setSortRole((ItemSortSettings::SortRole)ApplicationSettings::instance()->getImageSortOrder()); d->imageFilterModel->setSortOrder((ItemSortSettings::SortOrder)ApplicationSettings::instance()->getImageSorting()); d->imageFilterModel->setAllGroupsOpen(true); // disable filtering out by group, see bug #283847 d->imageFilterModel->sort(0); // an initial sorting is necessary d->dragDropHandler = new ItemDragDropHandler(d->imageInfoModel); d->dragDropHandler->setReadOnlyDrop(true); d->imageInfoModel->setDragDropHandler(d->dragDropHandler); // The thumb bar is placed in a detachable/dockable widget. d->thumbBarDock = new ThumbBarDock(d->viewContainer, Qt::Tool); d->thumbBarDock->setObjectName(QLatin1String("editor_thumbbar")); d->thumbBarDock->setWindowTitle(i18n("Image Editor Thumbnail Dock")); d->thumbBar = new ItemThumbnailBar(d->thumbBarDock); d->thumbBar->setModels(d->imageInfoModel, d->imageFilterModel); d->thumbBarDock->setWidget(d->thumbBar); d->viewContainer->addDockWidget(dockArea, d->thumbBarDock); d->thumbBarDock->setFloating(false); //d->thumbBar->slotDockLocationChanged(dockArea); setCentralWidget(widget); } void ImageWindow::addServicesMenu() { addServicesMenuForUrl(d->currentUrl()); } void ImageWindow::slotContextMenu() { if (m_contextMenu) { m_contextMenu->addSeparator(); addServicesMenu(); m_contextMenu->addSeparator(); TagsPopupMenu* assignTagsMenu = 0; TagsPopupMenu* removeTagsMenu = 0; // Bulk assignment/removal of tags -------------------------- QList idList; idList << d->currentItemInfo.id(); assignTagsMenu = new TagsPopupMenu(idList, TagsPopupMenu::RECENTLYASSIGNED, this); removeTagsMenu = new TagsPopupMenu(idList, TagsPopupMenu::REMOVE, this); assignTagsMenu->menuAction()->setText(i18n("Assign Tag")); removeTagsMenu->menuAction()->setText(i18n("Remove Tag")); m_contextMenu->addSeparator(); m_contextMenu->addMenu(assignTagsMenu); m_contextMenu->addMenu(removeTagsMenu); connect(assignTagsMenu, SIGNAL(signalTagActivated(int)), this, SLOT(slotAssignTag(int))); connect(removeTagsMenu, SIGNAL(signalTagActivated(int)), this, SLOT(slotRemoveTag(int))); connect(assignTagsMenu, SIGNAL(signalPopupTagsView()), d->rightSideBar, SLOT(slotPopupTagsView())); if (!CoreDbAccess().db()->hasTags(idList)) { m_contextMenu->menuAction()->setEnabled(false); } m_contextMenu->addSeparator(); // Assign Labels ------------------------------------------- QMenu* const menuLabels = new QMenu(i18n("Assign Labels"), m_contextMenu); PickLabelMenuAction* const pmenu = new PickLabelMenuAction(m_contextMenu); ColorLabelMenuAction* const cmenu = new ColorLabelMenuAction(m_contextMenu); RatingMenuAction* const rmenu = new RatingMenuAction(m_contextMenu); menuLabels->addAction(pmenu->menuAction()); menuLabels->addAction(cmenu->menuAction()); menuLabels->addAction(rmenu->menuAction()); m_contextMenu->addMenu(menuLabels); connect(pmenu, SIGNAL(signalPickLabelChanged(int)), this, SLOT(slotAssignPickLabel(int))); connect(cmenu, SIGNAL(signalColorLabelChanged(int)), this, SLOT(slotAssignColorLabel(int))); connect(rmenu, SIGNAL(signalRatingChanged(int)), this, SLOT(slotAssignRating(int))); // -------------------------------------------------------------- m_contextMenu->exec(QCursor::pos()); delete assignTagsMenu; delete removeTagsMenu; delete cmenu; delete pmenu; delete rmenu; delete menuLabels; } } } // namespace Digikam diff --git a/core/utilities/import/main/importui.cpp b/core/utilities/import/main/importui.cpp index 4d02e54a0d..0a3202f926 100644 --- a/core/utilities/import/main/importui.cpp +++ b/core/utilities/import/main/importui.cpp @@ -1,2696 +1,2696 @@ /* ============================================================ * * This file is a part of digiKam project * http://www.digikam.org * * Date : 2004-09-16 * Description : Import tool interface * * Copyright (C) 2004-2005 by Renchi Raju * Copyright (C) 2006-2019 by Gilles Caulier * Copyright (C) 2006-2011 by Marcel Wiesweg * Copyright (C) 2012 by Andi Clemens * 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 "importui.h" #include "importui_p.h" // Qt includes #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // KDE includes #include #include // Local includes #include "drawdecoder.h" #include "dlayoutbox.h" #include "dexpanderbox.h" #include "dfileselector.h" #include "digikam_debug.h" #include "digikam_globals.h" #include "cameramessagebox.h" #include "advancedrenamemanager.h" #include "album.h" #include "albummanager.h" #include "applicationsettings.h" #include "albumselectdialog.h" #include "cameracontroller.h" #include "camerafolderdialog.h" #include "camerainfodialog.h" #include "cameralist.h" #include "cameranamehelper.h" #include "cameratype.h" #include "capturedlg.h" #include "collectionlocation.h" #include "collectionmanager.h" #include "collectionscanner.h" #include "componentsinfodlg.h" #include "dlogoaction.h" #include "coredbdownloadhistory.h" #include "dzoombar.h" #include "fileactionmngr.h" #include "freespacewidget.h" #include "iccsettings.h" #include "importitempropertiessidebar.h" #include "importsettings.h" #include "importview.h" #include "imagedialog.h" #include "dnotificationwrapper.h" #include "newitemsfinder.h" #include "parsesettings.h" #include "renamecustomizer.h" #include "scancontroller.h" #include "setup.h" #include "sidebar.h" #include "statusprogressbar.h" #include "thememanager.h" #include "thumbnailsize.h" #include "importthumbnailmodel.h" #include "itempropertiestab.h" namespace Digikam { ImportUI* ImportUI::m_instance = 0; ImportUI::ImportUI(const QString& cameraTitle, const QString& model, const QString& port, const QString& path, int startIndex) : DXmlGuiWindow(0), d(new Private) { setConfigGroupName(QLatin1String("Camera Settings")); setXMLFile(QLatin1String("importui5.rc")); setFullScreenOptions(FS_IMPORTUI); setWindowFlags(Qt::Window); m_instance = this; // -------------------------------------------------------- QString title = CameraNameHelper::cameraName(cameraTitle); d->cameraTitle = (title.isEmpty()) ? cameraTitle : title; setCaption(d->cameraTitle); setupCameraController(model, port, path); setupUserArea(); setInitialSorting(); setupActions(); setupStatusBar(); setupAccelerators(); // -- Make signals/slots connections --------------------------------- setupConnections(); sidebarTabTitleStyleChanged(); slotColorManagementOptionsChanged(); // -- Read settings -------------------------------------------------- readSettings(); setAutoSaveSettings(configGroupName(), true); // ------------------------------------------------------------------- //d->historyUpdater = new CameraHistoryUpdater(this); //connect (d->historyUpdater, SIGNAL(signalHistoryMap(CHUpdateItemMap)), //this, SLOT(slotRefreshIconView(CHUpdateItemMap))); //connect(d->historyUpdater, SIGNAL(signalBusy(bool)), // this, SLOT(slotBusy(bool))); // -------------------------------------------------------- d->progressTimer = new QTimer(this); connect(d->progressTimer, SIGNAL(timeout()), this, SLOT(slotProgressTimerDone())); // -------------------------------------------------------- d->renameCustomizer->setStartIndex(startIndex); d->view->setFocus(); // -- Init icon view zoom factor -------------------------- slotThumbSizeChanged(ImportSettings::instance()->getDefaultIconSize()); slotZoomSliderChanged(ImportSettings::instance()->getDefaultIconSize()); // try to connect in the end, this allows us not to block the UI to show up.. QTimer::singleShot(0, d->controller, SLOT(slotConnect())); } ImportUI::~ImportUI() { saveSettings(); m_instance = 0; disconnect(d->view, 0, this, 0); delete d->view; delete d->rightSideBar; delete d->controller; delete d; } ImportUI* ImportUI::instance() { return m_instance; } void ImportUI::setupUserArea() { DHBox* const widget = new DHBox(this); d->splitter = new SidebarSplitter(widget); DVBox* const vbox = new DVBox(d->splitter); d->view = new ImportView(this, vbox); d->view->importFilterModel()->setCameraThumbsController(d->camThumbsCtrl); d->view->importFilterModel()->setStringTypeNatural(ApplicationSettings::instance()->isStringTypeNatural()); d->historyView = new DHistoryView(vbox); d->rightSideBar = new ImportItemPropertiesSideBarImport(widget, d->splitter, Qt::RightEdge, true); d->rightSideBar->setObjectName(QLatin1String("CameraGui Sidebar Right")); d->splitter->setFrameStyle(QFrame::NoFrame); d->splitter->setFrameShadow(QFrame::Plain); d->splitter->setFrameShape(QFrame::NoFrame); d->splitter->setOpaqueResize(false); d->splitter->setStretchFactor(0, 10); // set iconview default size to max. vbox->setStretchFactor(d->view, 10); vbox->setStretchFactor(d->historyView, 2); vbox->setContentsMargins(QMargins()); vbox->setSpacing(0); d->errorWidget = new DNotificationWidget(vbox); d->errorWidget->setMessageType(DNotificationWidget::Error); d->errorWidget->setCloseButtonVisible(false); d->errorWidget->hide(); // ------------------------------------------------------------------------- d->advBox = new DExpanderBox(d->rightSideBar); d->advBox->setObjectName(QLatin1String("Camera Settings Expander")); d->renameCustomizer = new RenameCustomizer(d->advBox, d->cameraTitle); d->renameCustomizer->setWhatsThis(i18n("Set how digiKam will rename files as they are downloaded.")); d->advBox->addItem(d->renameCustomizer, QIcon::fromTheme(QLatin1String("insert-image")), i18n("File Renaming Options"), QLatin1String("RenameCustomizer"), true); // -- Albums Auto-creation options ----------------------------------------- d->albumCustomizer = new AlbumCustomizer(d->advBox); d->advBox->addItem(d->albumCustomizer, QIcon::fromTheme(QLatin1String("folder-new")), i18n("Auto-creation of Albums"), QLatin1String("AlbumBox"), false); // -- On the Fly options --------------------------------------------------- d->advancedSettings = new AdvancedSettings(d->advBox); d->advBox->addItem(d->advancedSettings, QIcon::fromTheme(QLatin1String("system-run")), i18n("On the Fly Operations (JPEG only)"), QLatin1String("OnFlyBox"), true); // -- DNG convert options -------------------------------------------------- d->dngConvertSettings = new DNGConvertSettings(d->advBox); d->advBox->addItem(d->dngConvertSettings, QIcon::fromTheme(QLatin1String("image-x-adobe-dng")), i18n("DNG Convert Options"), QLatin1String("DNGSettings"), false); // -- Scripting options ---------------------------------------------------- d->scriptingSettings = new ScriptingSettings(d->advBox); d->advBox->addItem(d->scriptingSettings, QIcon::fromTheme(QLatin1String("utilities-terminal")), i18n("Scripting"), QLatin1String("ScriptingBox"), false); d->advBox->addStretch(); d->rightSideBar->appendTab(d->advBox, QIcon::fromTheme(QLatin1String("configure")), i18n("Settings")); d->rightSideBar->loadState(); // ------------------------------------------------------------------------- setCentralWidget(widget); } void ImportUI::setupActions() { d->cameraActions = new QActionGroup(this); KActionCollection* const ac = actionCollection(); // -- File menu ---------------------------------------------------- d->cameraCancelAction = new QAction(QIcon::fromTheme(QLatin1String("process-stop")), i18nc("@action Cancel process", "Cancel"), this); connect(d->cameraCancelAction, SIGNAL(triggered()), this, SLOT(slotCancelButton())); ac->addAction(QLatin1String("importui_cancelprocess"), d->cameraCancelAction); d->cameraCancelAction->setEnabled(false); // ----------------------------------------------------------------- d->cameraInfoAction = new QAction(QIcon::fromTheme(QLatin1String("camera-photo")), i18nc("@action Information about camera", "Information"), this); connect(d->cameraInfoAction, SIGNAL(triggered()), this, SLOT(slotInformation())); ac->addAction(QLatin1String("importui_info"), d->cameraInfoAction); d->cameraActions->addAction(d->cameraInfoAction); // ----------------------------------------------------------------- d->cameraCaptureAction = new QAction(QIcon::fromTheme(QLatin1String("webcamreceive")), i18nc("@action Capture photo from camera", "Capture"), this); connect(d->cameraCaptureAction, SIGNAL(triggered()), this, SLOT(slotCapture())); ac->addAction(QLatin1String("importui_capture"), d->cameraCaptureAction); d->cameraActions->addAction(d->cameraCaptureAction); // ----------------------------------------------------------------- QAction* const closeAction = buildStdAction(StdCloseAction, this, SLOT(close()), this); ac->addAction(QLatin1String("importui_close"), closeAction); // -- Edit menu ---------------------------------------------------- d->selectAllAction = new QAction(i18nc("@action:inmenu", "Select All"), this); connect(d->selectAllAction, SIGNAL(triggered()), d->view, SLOT(slotSelectAll())); ac->addAction(QLatin1String("importui_selectall"), d->selectAllAction); ac->setDefaultShortcut(d->selectAllAction, Qt::CTRL + Qt::Key_A); d->cameraActions->addAction(d->selectAllAction); // ----------------------------------------------------------------- d->selectNoneAction = new QAction(i18nc("@action:inmenu", "Select None"), this); connect(d->selectNoneAction, SIGNAL(triggered()), d->view, SLOT(slotSelectNone())); ac->addAction(QLatin1String("importui_selectnone"), d->selectNoneAction); ac->setDefaultShortcut(d->selectNoneAction, Qt::CTRL + Qt::SHIFT + Qt::Key_A); d->cameraActions->addAction(d->selectNoneAction); // ----------------------------------------------------------------- d->selectInvertAction = new QAction(i18nc("@action:inmenu", "Invert Selection"), this); connect(d->selectInvertAction, SIGNAL(triggered()), d->view, SLOT(slotSelectInvert())); ac->addAction(QLatin1String("importui_selectinvert"), d->selectInvertAction); ac->setDefaultShortcut(d->selectInvertAction, Qt::CTRL + Qt::Key_Asterisk); d->cameraActions->addAction(d->selectInvertAction); // ----------------------------------------------------------- d->selectNewItemsAction = new QAction(QIcon::fromTheme(QLatin1String("folder-favorites")), i18nc("@action:inmenu", "Select New Items"), this); connect(d->selectNewItemsAction, SIGNAL(triggered()), this, SLOT(slotSelectNew())); ac->addAction(QLatin1String("importui_selectnewitems"), d->selectNewItemsAction); d->cameraActions->addAction(d->selectNewItemsAction); // ----------------------------------------------------------- d->selectLockedItemsAction = new QAction(QIcon::fromTheme(QLatin1String("object-locked")), i18nc("@action:inmenu", "Select Locked Items"), this); connect(d->selectLockedItemsAction, SIGNAL(triggered()), this, SLOT(slotSelectLocked())); ac->addAction(QLatin1String("importui_selectlockeditems"), d->selectLockedItemsAction); ac->setDefaultShortcut(d->selectLockedItemsAction, Qt::CTRL + Qt::Key_L); d->cameraActions->addAction(d->selectLockedItemsAction); // --- Download actions ---------------------------------------------------- d->downloadAction = new QMenu(i18nc("@title:menu", "Download"), this); d->downloadAction->setIcon(QIcon::fromTheme(QLatin1String("document-save"))); ac->addAction(QLatin1String("importui_imagedownload"), d->downloadAction->menuAction()); d->cameraActions->addAction(d->downloadAction->menuAction()); d->downloadNewAction = new QAction(QIcon::fromTheme(QLatin1String("folder-favorites")), i18nc("@action", "Download New"), this); connect(d->downloadNewAction, SIGNAL(triggered()), this, SLOT(slotDownloadNew())); ac->addAction(QLatin1String("importui_imagedownloadnew"), d->downloadNewAction); ac->setDefaultShortcut(d->downloadNewAction, Qt::CTRL + Qt::Key_N); d->downloadAction->addAction(d->downloadNewAction); d->cameraActions->addAction(d->downloadNewAction); d->downloadSelectedAction = new QAction(QIcon::fromTheme(QLatin1String("document-save")), i18nc("@action", "Download Selected"), this); connect(d->downloadSelectedAction, SIGNAL(triggered()), this, SLOT(slotDownloadSelected())); ac->addAction(QLatin1String("importui_imagedownloadselected"), d->downloadSelectedAction); d->downloadSelectedAction->setEnabled(false); d->downloadAction->addAction(d->downloadSelectedAction); d->cameraActions->addAction(d->downloadSelectedAction); d->downloadAllAction = new QAction(QIcon::fromTheme(QLatin1String("document-save")), i18nc("@action", "Download All"), this); connect(d->downloadAllAction, SIGNAL(triggered()), this, SLOT(slotDownloadAll())); ac->addAction(QLatin1String("importui_imagedownloadall"), d->downloadAllAction); d->downloadAction->addAction(d->downloadAllAction); d->cameraActions->addAction(d->downloadAllAction); // ------------------------------------------------------------------------- d->downloadDelNewAction = new QAction(i18nc("@action", "Download && Delete New"), this); connect(d->downloadDelNewAction, SIGNAL(triggered()), this, SLOT(slotDownloadAndDeleteNew())); ac->addAction(QLatin1String("importui_imagedownloaddeletenew"), d->downloadDelNewAction); ac->setDefaultShortcut(d->downloadDelNewAction, Qt::CTRL + Qt::SHIFT + Qt::Key_N); d->cameraActions->addAction(d->downloadDelNewAction); // ----------------------------------------------------------------- d->downloadDelSelectedAction = new QAction(i18nc("@action", "Download && Delete Selected"), this); connect(d->downloadDelSelectedAction, SIGNAL(triggered()), this, SLOT(slotDownloadAndDeleteSelected())); ac->addAction(QLatin1String("importui_imagedownloaddeleteselected"), d->downloadDelSelectedAction); d->downloadDelSelectedAction->setEnabled(false); d->cameraActions->addAction(d->downloadDelSelectedAction); // ------------------------------------------------------------------------- d->downloadDelAllAction = new QAction(i18nc("@action", "Download && Delete All"), this); connect(d->downloadDelAllAction, SIGNAL(triggered()), this, SLOT(slotDownloadAndDeleteAll())); ac->addAction(QLatin1String("importui_imagedownloaddeleteall"), d->downloadDelAllAction); d->cameraActions->addAction(d->downloadDelAllAction); // ------------------------------------------------------------------------- d->uploadAction = new QAction(QIcon::fromTheme(QLatin1String("media-flash-sd-mmc")), i18nc("@action", "Upload..."), this); connect(d->uploadAction, SIGNAL(triggered()), this, SLOT(slotUpload())); ac->addAction(QLatin1String("importui_imageupload"), d->uploadAction); ac->setDefaultShortcut(d->uploadAction, Qt::CTRL + Qt::Key_U); d->cameraActions->addAction(d->uploadAction); // ------------------------------------------------------------------------- d->lockAction = new QAction(QIcon::fromTheme(QLatin1String("object-locked")), i18nc("@action", "Toggle Lock"), this); connect(d->lockAction, SIGNAL(triggered()), this, SLOT(slotToggleLock())); ac->addAction(QLatin1String("importui_imagelock"), d->lockAction); ac->setDefaultShortcut(d->lockAction, Qt::CTRL + Qt::Key_G); d->cameraActions->addAction(d->lockAction); // ------------------------------------------------------------------------- d->markAsDownloadedAction = new QAction(QIcon::fromTheme(QLatin1String("dialog-ok-apply")), i18nc("@action", "Mark as downloaded"), this); connect(d->markAsDownloadedAction, SIGNAL(triggered()), this, SLOT(slotMarkAsDownloaded())); ac->addAction(QLatin1String("importui_imagemarkasdownloaded"), d->markAsDownloadedAction); d->cameraActions->addAction(d->markAsDownloadedAction); // --- Delete actions ------------------------------------------------------ d->deleteAction = new QMenu(i18nc("@title:menu", "Delete"), this); - d->deleteAction->setIcon(QIcon::fromTheme(QLatin1String("user-trash"))); + d->deleteAction->setIcon(QIcon::fromTheme(QLatin1String("edit-delete"))); ac->addAction(QLatin1String("importui_delete"), d->deleteAction->menuAction()); d->cameraActions->addAction(d->deleteAction->menuAction()); d->deleteSelectedAction = new QAction(QIcon::fromTheme(QLatin1String("edit-delete")), i18nc("@action", "Delete Selected"), this); connect(d->deleteSelectedAction, SIGNAL(triggered()), this, SLOT(slotDeleteSelected())); ac->addAction(QLatin1String("importui_imagedeleteselected"), d->deleteSelectedAction); ac->setDefaultShortcut(d->deleteSelectedAction, Qt::Key_Delete); d->deleteSelectedAction->setEnabled(false); d->deleteAction->addAction(d->deleteSelectedAction); d->cameraActions->addAction(d->deleteSelectedAction); d->deleteAllAction = new QAction(QIcon::fromTheme(QLatin1String("edit-delete")), i18nc("@action", "Delete All"), this); connect(d->deleteAllAction, SIGNAL(triggered()), this, SLOT(slotDeleteAll())); ac->addAction(QLatin1String("importui_imagedeleteall"), d->deleteAllAction); d->deleteAction->addAction(d->deleteAllAction); d->cameraActions->addAction(d->deleteAllAction); d->deleteNewAction = new QAction(QIcon::fromTheme(QLatin1String("edit-delete")), i18nc("@action", "Delete New"), this); connect(d->deleteNewAction, SIGNAL(triggered()), this, SLOT(slotDeleteNew())); ac->addAction(QLatin1String("importui_imagedeletenew"), d->deleteNewAction); d->deleteAction->addAction(d->deleteNewAction); d->cameraActions->addAction(d->deleteNewAction); // --- Icon view, items preview, and map actions ------------------------------------------------------ d->imageViewSelectionAction = new KSelectAction(QIcon::fromTheme(QLatin1String("view-preview")), i18nc("@title:group", "Views"), this); ac->addAction(QLatin1String("importui_view_selection"), d->imageViewSelectionAction); d->iconViewAction = new QAction(QIcon::fromTheme(QLatin1String("view-list-icons")), i18nc("@action Go to thumbnails (icon) view", "Thumbnails"), this); d->iconViewAction->setCheckable(true); ac->addAction(QLatin1String("importui_icon_view"), d->iconViewAction); connect(d->iconViewAction, SIGNAL(triggered()), d->view, SLOT(slotIconView())); d->imageViewSelectionAction->addAction(d->iconViewAction); d->camItemPreviewAction = new QAction(QIcon::fromTheme(QLatin1String("view-preview")), i18nc("@action View the selected image", "Preview Item"), this); d->camItemPreviewAction->setCheckable(true); ac->addAction(QLatin1String("importui_item_view"), d->camItemPreviewAction); ac->setDefaultShortcut(d->camItemPreviewAction, Qt::Key_F3); connect(d->camItemPreviewAction, SIGNAL(triggered()), d->view, SLOT(slotImagePreview())); d->imageViewSelectionAction->addAction(d->camItemPreviewAction); #ifdef HAVE_MARBLE d->mapViewAction = new QAction(QIcon::fromTheme(QLatin1String("globe")), i18nc("@action Switch to map view", "Map"), this); d->mapViewAction->setCheckable(true); ac->addAction(QLatin1String("importui_map_view"), d->mapViewAction); connect(d->mapViewAction, SIGNAL(triggered()), d->view, SLOT(slotMapWidgetView())); d->imageViewSelectionAction->addAction(d->mapViewAction); #endif // HAVE_MARBLE /// @todo Add table view stuff here // -- Item Sorting ------------------------------------------------------------ d->itemSortAction = new KSelectAction(i18nc("@title:menu", "&Sort Items"), this); d->itemSortAction->setWhatsThis(i18nc("@info:whatsthis", "The value by which the items are sorted in the thumbnail view")); QSignalMapper* const imageSortMapper = new QSignalMapper(this); connect(imageSortMapper, SIGNAL(mapped(int)), d->view, SLOT(slotSortImagesBy(int))); ac->addAction(QLatin1String("item_sort"), d->itemSortAction); // map to CamItemSortSettings enum QAction* const sortByNameAction = d->itemSortAction->addAction(i18nc("item:inmenu Sort by", "By Name")); QAction* const sortByPathAction = d->itemSortAction->addAction(i18nc("item:inmenu Sort by", "By Path")); QAction* const sortByDateAction = d->itemSortAction->addAction(i18nc("item:inmenu Sort by", "By Date")); //TODO: Implement sort by creation date. QAction* const sortByFileSizeAction = d->itemSortAction->addAction(i18nc("item:inmenu Sort by", "By Size")); QAction* const sortByRatingAction = d->itemSortAction->addAction(i18nc("item:inmenu Sort by", "By Rating")); QAction* const sortByDownloadAction = d->itemSortAction->addAction(i18nc("item:inmenu Sort by", "By Download State")); connect(sortByNameAction, SIGNAL(triggered()), imageSortMapper, SLOT(map())); connect(sortByPathAction, SIGNAL(triggered()), imageSortMapper, SLOT(map())); connect(sortByDateAction, SIGNAL(triggered()), imageSortMapper, SLOT(map())); //TODO: Implement sort by creation date. connect(sortByFileSizeAction, SIGNAL(triggered()), imageSortMapper, SLOT(map())); connect(sortByRatingAction, SIGNAL(triggered()), imageSortMapper, SLOT(map())); connect(sortByDownloadAction, SIGNAL(triggered()), imageSortMapper, SLOT(map())); imageSortMapper->setMapping(sortByNameAction, (int)CamItemSortSettings::SortByFileName); imageSortMapper->setMapping(sortByPathAction, (int)CamItemSortSettings::SortByFilePath); imageSortMapper->setMapping(sortByDateAction, (int)CamItemSortSettings::SortByCreationDate); //TODO: Implement sort by creation date. imageSortMapper->setMapping(sortByFileSizeAction, (int)CamItemSortSettings::SortByFileSize); imageSortMapper->setMapping(sortByRatingAction, (int)CamItemSortSettings::SortByRating); imageSortMapper->setMapping(sortByDownloadAction, (int)CamItemSortSettings::SortByDownloadState); d->itemSortAction->setCurrentItem(ImportSettings::instance()->getImageSortBy()); // -- Item Sort Order ------------------------------------------------------------ d->itemSortOrderAction = new KSelectAction(i18nc("@title:inmenu", "Item Sorting &Order"), this); d->itemSortOrderAction->setWhatsThis(i18nc("@info:whatsthis", "Defines whether items are sorted in ascending or descending manner.")); QSignalMapper* const imageSortOrderMapper = new QSignalMapper(this); connect(imageSortOrderMapper, SIGNAL(mapped(int)), d->view, SLOT(slotSortImagesOrder(int))); ac->addAction(QLatin1String("item_sort_order"), d->itemSortOrderAction); QAction* const sortAscendingAction = d->itemSortOrderAction->addAction(QIcon::fromTheme(QLatin1String("view-sort-ascending")), i18nc("@item:inmenu Sorting Order", "Ascending")); QAction* const sortDescendingAction = d->itemSortOrderAction->addAction(QIcon::fromTheme(QLatin1String("view-sort-descending")), i18nc("@item:inmenu Sorting Order", "Descending")); connect(sortAscendingAction, SIGNAL(triggered()), imageSortOrderMapper, SLOT(map())); connect(sortDescendingAction, SIGNAL(triggered()), imageSortOrderMapper, SLOT(map())); imageSortOrderMapper->setMapping(sortAscendingAction, (int)CamItemSortSettings::AscendingOrder); imageSortOrderMapper->setMapping(sortDescendingAction, (int)CamItemSortSettings::DescendingOrder); d->itemSortOrderAction->setCurrentItem(ImportSettings::instance()->getImageSortOrder()); // -- Item Grouping ------------------------------------------------------------ d->itemsGroupAction = new KSelectAction(i18nc("@title:menu", "&Group Items"), this); d->itemsGroupAction->setWhatsThis(i18nc("@info:whatsthis", "The categories in which the items in the thumbnail view are displayed")); QSignalMapper* const itemSeparationMapper = new QSignalMapper(this); connect(itemSeparationMapper, SIGNAL(mapped(int)), d->view, SLOT(slotSeparateImages(int))); ac->addAction(QLatin1String("item_group"), d->itemsGroupAction); // map to CamItemSortSettings enum QAction* const noCategoriesAction = d->itemsGroupAction->addAction(i18nc("@item:inmenu Group Items", "Flat List")); QAction* const groupByFolderAction = d->itemsGroupAction->addAction(i18nc("@item:inmenu Group Items", "By Folder")); QAction* const groupByFormatAction = d->itemsGroupAction->addAction(i18nc("@item:inmenu Group Items", "By Format")); QAction* const groupByDateAction = d->itemsGroupAction->addAction(i18nc("@item:inmenu Group Items", "By Date")); connect(noCategoriesAction, SIGNAL(triggered()), itemSeparationMapper, SLOT(map())); connect(groupByFolderAction, SIGNAL(triggered()), itemSeparationMapper, SLOT(map())); connect(groupByFormatAction, SIGNAL(triggered()), itemSeparationMapper, SLOT(map())); connect(groupByDateAction, SIGNAL(triggered()), itemSeparationMapper, SLOT(map())); itemSeparationMapper->setMapping(noCategoriesAction, (int)CamItemSortSettings::NoCategories); itemSeparationMapper->setMapping(groupByFolderAction, (int)CamItemSortSettings::CategoryByFolder); itemSeparationMapper->setMapping(groupByFormatAction, (int)CamItemSortSettings::CategoryByFormat); itemSeparationMapper->setMapping(groupByDateAction, (int)CamItemSortSettings::CategoryByDate); d->itemsGroupAction->setCurrentItem(ImportSettings::instance()->getImageSeparationMode()); // -- Standard 'View' menu actions --------------------------------------------- d->increaseThumbsAction = buildStdAction(StdZoomInAction, d->view, SLOT(slotZoomIn()), this); d->increaseThumbsAction->setEnabled(false); ac->addAction(QLatin1String("importui_zoomplus"), d->increaseThumbsAction); d->decreaseThumbsAction = buildStdAction(StdZoomOutAction, d->view, SLOT(slotZoomOut()), this); d->decreaseThumbsAction->setEnabled(false); ac->addAction(QLatin1String("importui_zoomminus"), d->decreaseThumbsAction); d->zoomFitToWindowAction = new QAction(QIcon::fromTheme(QLatin1String("zoom-fit-best")), i18nc("@action:inmenu", "Fit to &Window"), this); connect(d->zoomFitToWindowAction, SIGNAL(triggered()), d->view, SLOT(slotFitToWindow())); ac->addAction(QLatin1String("import_zoomfit2window"), d->zoomFitToWindowAction); ac->setDefaultShortcut(d->zoomFitToWindowAction, Qt::ALT + Qt::CTRL + Qt::Key_E); d->zoomTo100percents = new QAction(QIcon::fromTheme(QLatin1String("zoom-original")), i18nc("@action:inmenu", "Zoom to 100%"), this); connect(d->zoomTo100percents, SIGNAL(triggered()), d->view, SLOT(slotZoomTo100Percents())); ac->addAction(QLatin1String("import_zoomto100percents"), d->zoomTo100percents); ac->setDefaultShortcut(d->zoomTo100percents, Qt::CTRL + Qt::Key_Period); // ------------------------------------------------------------------------------------------------ d->viewCMViewAction = new QAction(QIcon::fromTheme(QLatin1String("video-display")), i18n("Color-Managed View"), this); d->viewCMViewAction->setCheckable(true); connect(d->viewCMViewAction, SIGNAL(triggered()), this, SLOT(slotToggleColorManagedView())); ac->addAction(QLatin1String("color_managed_view"), d->viewCMViewAction); ac->setDefaultShortcut(d->viewCMViewAction, Qt::Key_F12); // ------------------------------------------------------------------------------------------------ createFullScreenAction(QLatin1String("importui_fullscreen")); createSidebarActions(); d->showLogAction = new QAction(QIcon::fromTheme(QLatin1String("edit-find")), i18nc("@option:check", "Show History"), this); d->showLogAction->setCheckable(true); connect(d->showLogAction, SIGNAL(triggered()), this, SLOT(slotShowLog())); ac->addAction(QLatin1String("importui_showlog"), d->showLogAction); ac->setDefaultShortcut(d->showLogAction, Qt::CTRL + Qt::Key_H); d->showBarAction = new QAction(QIcon::fromTheme(QLatin1String("view-choose")), i18nc("@option:check", "Show Thumbbar"), this); d->showBarAction->setCheckable(true); connect(d->showBarAction, SIGNAL(triggered()), this, SLOT(slotToggleShowBar())); ac->addAction(QLatin1String("showthumbs"), d->showBarAction); ac->setDefaultShortcut(d->showBarAction, Qt::CTRL+Qt::Key_T); d->showBarAction->setEnabled(false); // --------------------------------------------------------------------------------- ThemeManager::instance()->registerThemeActions(this); // Standard 'Help' menu actions createHelpActions(); // Provides a menu entry that allows showing/hiding the toolbar(s) setStandardToolBarMenuEnabled(true); // Provides a menu entry that allows showing/hiding the statusbar createStandardStatusBarAction(); // Standard 'Configure' menu actions createSettingsActions(); // -- Keyboard-only actions added to ---------------------------------- QAction* const altBackwardAction = new QAction(i18nc("@action", "Previous Image"), this); ac->addAction(QLatin1String("importui_backward_shift_space"), altBackwardAction); ac->setDefaultShortcut(altBackwardAction, Qt::SHIFT + Qt::Key_Space); connect(altBackwardAction, SIGNAL(triggered()), d->view, SLOT(slotPrevItem())); // --------------------------------------------------------------------------------- d->connectAction = new QAction(QIcon::fromTheme(QLatin1String("view-refresh")), i18nc("@action Connection failed, try again?", "Retry"), this); connect(d->connectAction, SIGNAL(triggered()), d->controller, SLOT(slotConnect())); createGUI(xmlFile()); cleanupActions(); showMenuBarAction()->setChecked(!menuBar()->isHidden()); // NOTE: workaround for bug #171080 } void ImportUI::updateActions() { CamItemInfoList list = d->view->selectedCamItemInfos(); bool hasSelection = list.count() > 0; d->downloadDelSelectedAction->setEnabled(hasSelection && d->controller->cameraDeleteSupport()); d->deleteSelectedAction->setEnabled(hasSelection && d->controller->cameraDeleteSupport()); d->camItemPreviewAction->setEnabled(hasSelection && cameraUseUMSDriver()); d->downloadSelectedAction->setEnabled(hasSelection); d->lockAction->setEnabled(hasSelection); if (hasSelection) { // only enable "Mark as downloaded" if at least one // selected image has not been downloaded bool haveNotDownloadedItem = false; foreach (const CamItemInfo& info, list) { haveNotDownloadedItem = !(info.downloaded == CamItemInfo::DownloadedYes); if (haveNotDownloadedItem) { break; } } d->markAsDownloadedAction->setEnabled(haveNotDownloadedItem); } else { d->markAsDownloadedAction->setEnabled(false); } } void ImportUI::setupConnections() { //TODO: Needs testing. connect(d->advancedSettings, SIGNAL(signalDownloadNameChanged()), this, SLOT(slotUpdateDownloadName())); connect(d->dngConvertSettings, SIGNAL(signalDownloadNameChanged()), this, SLOT(slotUpdateDownloadName())); connect(d->historyView, SIGNAL(signalEntryClicked(QVariant)), this, SLOT(slotHistoryEntryClicked(QVariant))); connect(IccSettings::instance(), SIGNAL(settingsChanged()), this, SLOT(slotColorManagementOptionsChanged())); // ------------------------------------------------------------------------- connect(d->view, SIGNAL(signalImageSelected(CamItemInfoList,CamItemInfoList)), this, SLOT(slotImageSelected(CamItemInfoList,CamItemInfoList))); connect(d->view, SIGNAL(signalSwitchedToPreview()), this, SLOT(slotSwitchedToPreview())); connect(d->view, SIGNAL(signalSwitchedToIconView()), this, SLOT(slotSwitchedToIconView())); connect(d->view, SIGNAL(signalSwitchedToMapView()), this, SLOT(slotSwitchedToMapView())); connect(d->view, SIGNAL(signalNewSelection(bool)), this, SLOT(slotNewSelection(bool))); // ------------------------------------------------------------------------- connect(d->view, SIGNAL(signalThumbSizeChanged(int)), this, SLOT(slotThumbSizeChanged(int))); connect(d->view, SIGNAL(signalZoomChanged(double)), this, SLOT(slotZoomChanged(double))); connect(d->zoomBar, SIGNAL(signalZoomSliderChanged(int)), this, SLOT(slotZoomSliderChanged(int))); connect(d->zoomBar, SIGNAL(signalZoomValueEdited(double)), d->view, SLOT(setZoomFactor(double))); connect(this, SIGNAL(signalWindowHasMoved()), d->zoomBar, SLOT(slotUpdateTrackerPos())); // ------------------------------------------------------------------------- connect(CollectionManager::instance(), SIGNAL(locationStatusChanged(CollectionLocation,int)), this, SLOT(slotCollectionLocationStatusChanged(CollectionLocation,int))); connect(ApplicationSettings::instance(), SIGNAL(setupChanged()), this, SLOT(slotSetupChanged())); connect(d->renameCustomizer, SIGNAL(signalChanged()), this, SLOT(slotUpdateDownloadName())); } void ImportUI::setupStatusBar() { d->statusProgressBar = new StatusProgressBar(statusBar()); d->statusProgressBar->setAlignment(Qt::AlignLeft | Qt::AlignVCenter); d->statusProgressBar->setNotificationTitle(d->cameraTitle, QIcon::fromTheme(QLatin1String("camera-photo"))); statusBar()->addWidget(d->statusProgressBar, 100); //------------------------------------------------------------------------------ d->cameraFreeSpace = new FreeSpaceWidget(statusBar(), 100); if (cameraUseGPhotoDriver()) { d->cameraFreeSpace->setMode(FreeSpaceWidget::GPhotoCamera); connect(d->controller, SIGNAL(signalFreeSpace(ulong,ulong)), this, SLOT(slotCameraFreeSpaceInfo(ulong,ulong))); } else { d->cameraFreeSpace->setMode(FreeSpaceWidget::UMSCamera); d->cameraFreeSpace->setPath(d->controller->cameraPath()); } statusBar()->addWidget(d->cameraFreeSpace, 1); //------------------------------------------------------------------------------ d->albumLibraryFreeSpace = new FreeSpaceWidget(statusBar(), 100); d->albumLibraryFreeSpace->setMode(FreeSpaceWidget::AlbumLibrary); statusBar()->addWidget(d->albumLibraryFreeSpace, 1); refreshCollectionFreeSpace(); //------------------------------------------------------------------------------ //TODO: Replace it with FilterStatusBar after advanced filtering is implemented. d->filterComboBox = new FilterComboBox(statusBar()); setFilter(d->filterComboBox->currentFilter()); statusBar()->addWidget(d->filterComboBox, 1); connect(d->filterComboBox, SIGNAL(filterChanged(Filter*)), this, SLOT(setFilter(Filter*))); //------------------------------------------------------------------------------ d->zoomBar = new DZoomBar(statusBar()); d->zoomBar->setZoomToFitAction(d->zoomFitToWindowAction); d->zoomBar->setZoomTo100Action(d->zoomTo100percents); d->zoomBar->setZoomPlusAction(d->increaseThumbsAction); d->zoomBar->setZoomMinusAction(d->decreaseThumbsAction); d->zoomBar->setBarMode(DZoomBar::ThumbsSizeCtrl); statusBar()->addPermanentWidget(d->zoomBar, 1); } void ImportUI::setupCameraController(const QString& model, const QString& port, const QString& path) { d->controller = new CameraController(this, d->cameraTitle, model, port, path); connect(d->controller, SIGNAL(signalConnected(bool)), this, SLOT(slotConnected(bool))); connect(d->controller, SIGNAL(signalLogMsg(QString,DHistoryView::EntryType,QString,QString)), this, SLOT(slotLogMsg(QString,DHistoryView::EntryType,QString,QString))); connect(d->controller, SIGNAL(signalCameraInformation(QString,QString,QString)), this, SLOT(slotCameraInformation(QString,QString,QString))); connect(d->controller, SIGNAL(signalBusy(bool)), this, SLOT(slotBusy(bool))); connect(d->controller, SIGNAL(signalFolderList(QStringList)), this, SLOT(slotFolderList(QStringList))); connect(d->controller, SIGNAL(signalDownloaded(QString,QString,int)), this, SLOT(slotDownloaded(QString,QString,int))); connect(d->controller, SIGNAL(signalDownloadComplete(QString,QString,QString,QString)), this, SLOT(slotDownloadComplete(QString,QString,QString,QString))); connect(d->controller, SIGNAL(signalSkipped(QString,QString)), this, SLOT(slotSkipped(QString,QString))); connect(d->controller, SIGNAL(signalDeleted(QString,QString,bool)), this, SLOT(slotDeleted(QString,QString,bool))); connect(d->controller, SIGNAL(signalLocked(QString,QString,bool)), this, SLOT(slotLocked(QString,QString,bool))); connect(d->controller, SIGNAL(signalMetadata(QString,QString,DMetadata)), this, SLOT(slotMetadata(QString,QString,DMetadata))); connect(d->controller, SIGNAL(signalUploaded(CamItemInfo)), this, SLOT(slotUploaded(CamItemInfo))); d->controller->start(); // Setup Thumbnails controller ------------------------------------------------------- d->camThumbsCtrl = new CameraThumbsCtrl(d->controller, this); } CameraThumbsCtrl* ImportUI::getCameraThumbsCtrl() const { return d->camThumbsCtrl; } void ImportUI::setupAccelerators() { KActionCollection* const ac = actionCollection(); QAction* const escapeAction = new QAction(i18nc("@action", "Exit Preview Mode"), this); ac->addAction(QLatin1String("exit_preview_mode"), escapeAction); ac->setDefaultShortcut(escapeAction, Qt::Key_Escape); connect(escapeAction, SIGNAL(triggered()), this, SIGNAL(signalEscapePressed())); QAction* const nextImageAction = new QAction(i18nc("@action","Next Image"), this); nextImageAction->setIcon(QIcon::fromTheme(QLatin1String("go-next"))); ac->addAction(QLatin1String("next_image"), nextImageAction); ac->setDefaultShortcut(nextImageAction, Qt::Key_Space); connect(nextImageAction, SIGNAL(triggered()), d->view, SLOT(slotNextItem())); QAction* const previousImageAction = new QAction(i18nc("@action", "Previous Image"), this); previousImageAction->setIcon(QIcon::fromTheme(QLatin1String("go-previous"))); ac->addAction(QLatin1String("previous_image"), previousImageAction); ac->setDefaultShortcuts(previousImageAction, QList() << Qt::Key_Backspace << Qt::SHIFT+Qt::Key_Space); connect(previousImageAction, SIGNAL(triggered()), d->view, SLOT(slotPrevItem())); QAction* const firstImageAction = new QAction(i18nc("@action Go to first image", "First Image"), this); ac->addAction(QLatin1String("first_image"), firstImageAction); ac->setDefaultShortcut(firstImageAction, Qt::Key_Home); connect(firstImageAction, SIGNAL(triggered()), d->view, SLOT(slotFirstItem())); QAction* const lastImageAction = new QAction(i18nc("@action Go to last image", "Last Image"), this); ac->addAction(QLatin1String("last_image"), lastImageAction); ac->setDefaultShortcut(lastImageAction, Qt::Key_End); connect(lastImageAction, SIGNAL(triggered()), d->view, SLOT(slotLastItem())); } void ImportUI::readSettings() { KSharedConfig::Ptr config = KSharedConfig::openConfig(); KConfigGroup group = config->group(d->configGroupName); readFullScreenSettings(group); d->showBarAction->setChecked(ImportSettings::instance()->getShowThumbbar()); d->showLogAction->setChecked(group.readEntry(QLatin1String("ShowLog"), false)); d->albumCustomizer->readSettings(group); d->advancedSettings->readSettings(group); d->dngConvertSettings->readSettings(group); d->scriptingSettings->readSettings(group); d->advBox->readSettings(group); d->splitter->restoreState(group); slotShowLog(); } void ImportUI::saveSettings() { KSharedConfig::Ptr config = KSharedConfig::openConfig(); KConfigGroup group = config->group(d->configGroupName); ImportSettings::instance()->setShowThumbbar(d->showBarAction->isChecked()); ImportSettings::instance()->saveSettings(); group.writeEntry(QLatin1String("ShowLog"), d->showLogAction->isChecked()); d->albumCustomizer->saveSettings(group); d->advancedSettings->saveSettings(group); d->dngConvertSettings->saveSettings(group); d->scriptingSettings->saveSettings(group); d->advBox->writeSettings(group); d->rightSideBar->saveState(); d->splitter->saveState(group); d->filterComboBox->saveSettings(); config->sync(); } bool ImportUI::isBusy() const { return d->busy; } bool ImportUI::isClosed() const { return d->closed; } QString ImportUI::cameraTitle() const { return d->cameraTitle; } DownloadSettings ImportUI::downloadSettings() const { DownloadSettings settings = d->advancedSettings->settings(); d->dngConvertSettings->settings(&settings); d->scriptingSettings->settings(&settings); return settings; } void ImportUI::setInitialSorting() { d->view->slotSeparateImages(ImportSettings::instance()->getImageSeparationMode()); d->view->slotSortImagesBy(ImportSettings::instance()->getImageSortBy()); d->view->slotSortImagesOrder(ImportSettings::instance()->getImageSortOrder()); } void ImportUI::slotCancelButton() { d->statusProgressBar->setProgressBarMode(StatusProgressBar::TextMode, i18nc("@info:status", "Canceling current operation, please wait...")); d->controller->slotCancel(); //d->historyUpdater->slotCancel(); d->currentlyDeleting.clear(); refreshFreeSpace(); } void ImportUI::refreshFreeSpace() { if (cameraUseGPhotoDriver()) { d->controller->getFreeSpace(); } else { d->cameraFreeSpace->refresh(); } } void ImportUI::closeEvent(QCloseEvent* e) { if (dialogClosed()) { DXmlGuiWindow::closeEvent(e); } else { e->ignore(); } } void ImportUI::moveEvent(QMoveEvent* e) { Q_UNUSED(e) emit signalWindowHasMoved(); } void ImportUI::slotClose() { /* FIXME if (dialogClosed()) reject(); */ } bool ImportUI::dialogClosed() { if (d->closed) { return true; } if (isBusy()) { if (QMessageBox::question(this, qApp->applicationName(), i18nc("@info", "Do you want to close the dialog " "and cancel the current operation?"), QMessageBox::Yes | QMessageBox::No ) == QMessageBox::No) { return false; } } d->statusProgressBar->setProgressBarMode(StatusProgressBar::TextMode, i18nc("@info:status", "Disconnecting from camera, please wait...")); if (isBusy()) { d->controller->slotCancel(); // will be read in slotBusy later and finishDialog // will be called only when everything is finished d->closed = true; } else { d->closed = true; finishDialog(); } return true; } void ImportUI::finishDialog() { // Look if an item have been downloaded to computer during camera GUI session. // If yes, update the starting number value used to rename camera items from camera list. if (d->view->downloadedCamItemInfos() > 0) { CameraList* const clist = CameraList::defaultList(); if (clist) { clist->changeCameraStartIndex(d->cameraTitle, d->renameCustomizer->startIndex()); } } if (!d->foldersToScan.isEmpty()) { // TODO is this note valid anymore with new progress handling? // When a directory is created, a watch is put on it to spot new files // but it can occur that the file is copied there before the watch is // completely setup. That is why as an extra safeguard run CollectionScanner // over the folders we used. Bug: 119201 d->statusProgressBar->setProgressBarMode(StatusProgressBar::TextMode, i18nc("@info:status", "Scanning for new files, please wait...")); NewItemsFinder* const tool = new NewItemsFinder(NewItemsFinder::ScheduleCollectionScan, d->foldersToScan.toList()); tool->start(); d->foldersToScan.clear(); } deleteLater(); if (!d->lastDestURL.isEmpty()) { emit signalLastDestination(d->lastDestURL); } saveSettings(); } void ImportUI::slotBusy(bool val) { if (!val) // Camera is available for actions. { if (!d->busy) { return; } d->busy = false; d->cameraCancelAction->setEnabled(false); d->cameraActions->setEnabled(true); d->advBox->setEnabled(true); d->view->setEnabled(true); // selection-dependent update of lockAction, markAsDownloadedAction, // downloadSelectedAction, downloadDelSelectedAction, deleteSelectedAction updateActions(); m_animLogo->stop(); d->statusProgressBar->setProgressValue(0); d->statusProgressBar->setProgressBarMode(StatusProgressBar::TextMode, i18nc("@info:status", "Ready")); // like WDestructiveClose, but after camera controller operation has safely finished if (d->closed) { finishDialog(); } } else // Camera is busy. { if (d->busy) { return; } if (!m_animLogo->running()) { m_animLogo->start(); } d->busy = true; d->cameraActions->setEnabled(false); } } void ImportUI::slotZoomSliderChanged(int size) { d->view->setThumbSize(size); } void ImportUI::slotZoomChanged(double zoom) { double zmin = d->view->zoomMin(); double zmax = d->view->zoomMax(); d->zoomBar->setZoom(zoom, zmin, zmax); if (!fullScreenIsActive()) { d->zoomBar->triggerZoomTrackerToolTip(); } } void ImportUI::slotThumbSizeChanged(int size) { d->zoomBar->setThumbsSize(size); if (!fullScreenIsActive()) { d->zoomBar->triggerZoomTrackerToolTip(); } } void ImportUI::slotConnected(bool val) { if (!val) { d->errorWidget->setText(i18nc("@info", "Failed to connect to the camera. " "Please make sure it is connected " "properly and turned on.")); d->errorWidget->actions().clear(); d->errorWidget->addAction(d->connectAction); d->errorWidget->addAction(d->showPreferencesAction); d->errorWidget->animatedShow(); } else { // disable unsupported actions d->uploadAction->setEnabled(d->controller->cameraUploadSupport()); d->cameraCaptureAction->setEnabled(d->controller->cameraCaptureImageSupport()); d->errorWidget->hide(); refreshFreeSpace(); // FIXME ugly c&p from slotFolderList KSharedConfig::Ptr config = KSharedConfig::openConfig(); KConfigGroup group = config->group(d->configGroupName); bool useMetadata = group.readEntry(d->configUseFileMetadata, false); d->controller->listRootFolder(useMetadata); } } void ImportUI::slotFolderList(const QStringList& folderList) { if (d->closed) { return; } d->statusProgressBar->setProgressValue(0); d->statusProgressBar->setProgressTotalSteps(0); KSharedConfig::Ptr config = KSharedConfig::openConfig(); KConfigGroup group = config->group(d->configGroupName); bool useMetadata = group.readEntry(d->configUseFileMetadata, false); // when getting a list of subfolders, request their contents and also their subfolders for (QStringList::const_iterator it = folderList.constBegin() ; it != folderList.constEnd() ; ++it) { d->controller->listFiles(*it, useMetadata); d->controller->listFolders(*it); } } void ImportUI::setFilter(Filter *filter) { d->view->importFilterModel()->setFilter(filter); } void ImportUI::slotCapture() { if (d->busy) { return; } CaptureDlg* const captureDlg = new CaptureDlg(this, d->controller, d->cameraTitle); captureDlg->show(); } void ImportUI::slotInformation() { if (d->busy) { return; } d->controller->getCameraInformation(); } void ImportUI::slotCameraInformation(const QString& summary, const QString& manual, const QString& about) { CameraInfoDialog* const infoDlg = new CameraInfoDialog(this, summary, manual, about); infoDlg->show(); } void ImportUI::slotUpload() { if (d->busy) { return; } QList urls = ImageDialog::getImageURLs(this, QUrl::fromLocalFile(CollectionManager::instance()->oneAlbumRootPath()), i18nc("@title:window", "Select Image to Upload")); if (!urls.isEmpty()) { slotUploadItems(urls); } } void ImportUI::slotUploadItems(const QList& urls) { if (d->busy) { return; } if (urls.isEmpty()) { return; } if (d->cameraFreeSpace->isValid()) { // Check if space require to upload new items in camera is enough. quint64 totalKbSize = 0; for (QList::const_iterator it = urls.constBegin() ; it != urls.constEnd() ; ++it) { QFileInfo fi((*it).toLocalFile()); totalKbSize += fi.size() / 1024; } if (totalKbSize >= d->cameraFreeSpace->kBAvail()) { QMessageBox::critical(this, qApp->applicationName(), i18nc("@info", "There is not enough free space on the Camera Medium " "to upload pictures.\n\n" "Space require: %1\n" "Available free space: %2", ItemPropertiesTab::humanReadableBytesCount(totalKbSize * 1024), ItemPropertiesTab::humanReadableBytesCount(d->cameraFreeSpace->kBAvail() * 1024))); return; } } QMap map = countItemsByFolders(); QPointer dlg = new CameraFolderDialog(this, map, d->controller->cameraTitle(), d->controller->cameraPath()); if (dlg->exec() != QDialog::Accepted) { delete dlg; return; } // since we access members here, check if the pointer is still valid if (!dlg) { return; } QString cameraFolder = dlg->selectedFolderPath(); for (QList::const_iterator it = urls.constBegin(); it != urls.constEnd(); ++it) { QFileInfo fi((*it).toLocalFile()); if (!fi.exists()) { continue; } if (fi.isDir()) { continue; } QString ext = QLatin1Char('.') + fi.completeSuffix(); QString name = fi.fileName(); name.truncate(fi.fileName().length() - ext.length()); bool ok; CamItemInfo uploadInfo; uploadInfo.folder = cameraFolder; uploadInfo.name = name + ext; while (d->view->hasImage(uploadInfo)) { QString msg(i18nc("@info", "Camera Folder %1 already contains the item %2.
" "Please enter a new filename (without extension):
", QDir::toNativeSeparators(cameraFolder), fi.fileName())); uploadInfo.name = QInputDialog::getText(this, i18nc("@title:window", "File already exists"), msg, QLineEdit::Normal, name, &ok) + ext; if (!ok) { return; } } d->controller->upload(fi, uploadInfo.name, cameraFolder); } delete dlg; } void ImportUI::slotUploaded(const CamItemInfo& /*itemInfo*/) { if (d->closed) { return; } refreshFreeSpace(); } void ImportUI::slotDownloadNew() { slotSelectNew(); QTimer::singleShot(0, this, SLOT(slotDownloadSelected())); } void ImportUI::slotDownloadAndDeleteNew() { slotSelectNew(); QTimer::singleShot(0, this, SLOT(slotDownloadAndDeleteSelected())); } void ImportUI::slotDownloadSelected() { slotDownload(true, false); } void ImportUI::slotDownloadAndDeleteSelected() { slotDownload(true, true); } void ImportUI::slotDownloadAll() { slotDownload(false, false); } void ImportUI::slotDownloadAndDeleteAll() { slotDownload(false, true); } void ImportUI::slotDownload(bool onlySelected, bool deleteAfter, Album* album) { if (d->albumCustomizer->folderDateFormat() == AlbumCustomizer::CustomDateFormat && !d->albumCustomizer->customDateFormatIsValid()) { QMessageBox::information(this, qApp->applicationName(), i18nc("@info", "Your custom target album date format is not valid. Please check your settings...")); return; } // See bug #143934: force to select all items to prevent problem // when !renameCustomizer->useDefault() ==> iconItem->getDownloadName() // can return an empty string in this case because it depends on selection. if (!onlySelected) { d->view->slotSelectAll(); } // Update the download names. slotNewSelection(d->view->selectedUrls().count() > 0); // -- Get the destination album from digiKam library --------------- PAlbum* pAlbum = 0; if (!album) { AlbumManager* const man = AlbumManager::instance(); // Check if default target album option is enabled. KSharedConfig::Ptr config = KSharedConfig::openConfig(); KConfigGroup group = config->group(d->configGroupName); bool useDefaultTarget = group.readEntry(d->configUseDefaultTargetAlbum, false); if (useDefaultTarget) { PAlbum* const pa = man->findPAlbum(group.readEntry(d->configDefaultTargetAlbumId, 0)); if (pa) { CollectionLocation cl = CollectionManager::instance()->locationForAlbumRootId(pa->albumRootId()); if (!cl.isAvailable() || cl.isNull()) { QMessageBox::information(this,qApp->applicationName(), i18nc("@info", "Collection which host your default target album set to process " "download from camera device is not available. Please select another one from " "camera configuration dialog.")); return; } } else { QMessageBox::information(this, qApp->applicationName(), i18nc("@info", "Your default target album set to process download " "from camera device is not available. Please select another one from " "camera configuration dialog.")); return; } pAlbum = pa; } else { AlbumList list = man->currentAlbums(); int albumId = 0; if (!list.isEmpty()) { albumId = group.readEntry(d->configLastTargetAlbum, list.first()->globalID()); } album = man->findAlbum(albumId); if (album && album->type() != Album::PHYSICAL) { album = 0; } QString header(i18nc("@info", "

Please select the destination album from the digiKam library to " "import the camera pictures into.

")); album = AlbumSelectDialog::selectAlbum(this, dynamic_cast(album), header); if (!album) { return; } pAlbum = dynamic_cast(album); group.writeEntry(d->configLastTargetAlbum, album->globalID()); } } else { pAlbum = dynamic_cast(album); } if (!pAlbum) { qCDebug(DIGIKAM_IMPORTUI_LOG) << "Destination Album is null"; return; } // -- Check disk space ------------------------ // See bug #139519: Always check free space available before to // download items selection from camera. if (!checkDiskSpace(pAlbum)) { return; } // -- Prepare and download camera items ------------------------ // Since we show camera items in reverse order, downloading need to be done also in reverse order. downloadCameraItems(pAlbum, onlySelected, deleteAfter); } void ImportUI::slotDownloaded(const QString& folder, const QString& file, int status) { // Is auto-rotate option checked? bool autoRotate = downloadSettings().autoRotate; bool previewItems = ImportSettings::instance()->getPreviewItemsWhileDownload(); CamItemInfo& info = d->view->camItemInfoRef(folder, file); if (!info.isNull()) { setDownloaded(info, status); if (status == CamItemInfo::DownloadStarted && previewItems) { emit signalPreviewRequested(info, true); } if (d->rightSideBar->url() == info.url()) { updateRightSideBar(info); } if (info.downloaded == CamItemInfo::DownloadedYes) { int curr = d->statusProgressBar->progressValue(); d->statusProgressBar->setProgressValue(curr + 1); d->renameCustomizer->setStartIndex(d->renameCustomizer->startIndex() + 1); CoreDbDownloadHistory::setDownloaded(QString::fromUtf8(d->controller->cameraMD5ID()), info.name, info.size, info.ctime); } } // Download all items is complete ? if (d->statusProgressBar->progressValue() == d->statusProgressBar->progressTotalSteps()) { if (d->deleteAfter) { // No need passive pop-up here, because we will ask to confirm items deletion with dialog. deleteItems(true, true); } else { // Pop-up a notification to inform user when all is done, and inform if auto-rotation will take place. if (autoRotate) { DNotificationWrapper(QLatin1String("cameradownloaded"), i18nc("@info Popup notification", "Images download finished, you can now detach your camera while the images will be auto-rotated"), this, windowTitle()); } else { DNotificationWrapper(QLatin1String("cameradownloaded"), i18nc("@info Popup notification", "Images download finished"), this, windowTitle()); } } } } void ImportUI::slotDownloadComplete(const QString&, const QString&, const QString& destFolder, const QString&) { ScanController::instance()->scheduleCollectionScanRelaxed(destFolder); autoRotateItems(); } void ImportUI::slotSkipped(const QString& folder, const QString& file) { CamItemInfo info = d->view->camItemInfo(folder, file); if (!info.isNull()) { setDownloaded(info, CamItemInfo::DownloadedNo); } int curr = d->statusProgressBar->progressValue(); d->statusProgressBar->setProgressValue(curr + 1); } void ImportUI::slotMarkAsDownloaded() { CamItemInfoList list = d->view->selectedCamItemInfos(); foreach(const CamItemInfo& info, list) { setDownloaded(d->view->camItemInfoRef(info.folder, info.name), CamItemInfo::DownloadedYes); CoreDbDownloadHistory::setDownloaded(QString::fromUtf8(d->controller->cameraMD5ID()), info.name, info.size, info.ctime); } } void ImportUI::slotToggleLock() { CamItemInfoList list = d->view->selectedCamItemInfos(); int count = list.count(); if (count > 0) { d->statusProgressBar->setProgressValue(0); d->statusProgressBar->setProgressTotalSteps(count); d->statusProgressBar->setProgressBarMode(StatusProgressBar::ProgressBarMode); } foreach(const CamItemInfo& info, list) { QString folder = info.folder; QString file = info.name; int writePerm = info.writePermissions; bool lock = true; // If item is currently locked, unlock it. if (writePerm == 0) { lock = false; } d->controller->lockFile(folder, file, lock); } } void ImportUI::slotLocked(const QString& folder, const QString& file, bool status) { if (status) { CamItemInfo& info = d->view->camItemInfoRef(folder, file); if (!info.isNull()) { toggleLock(info); if (d->rightSideBar->url() == info.url()) { updateRightSideBar(info); } } } int curr = d->statusProgressBar->progressValue(); d->statusProgressBar->setProgressValue(curr + 1); } void ImportUI::slotUpdateDownloadName() { QList selected = d->view->selectedUrls(); bool hasNoSelection = selected.count() == 0; CamItemInfoList list = d->view->allItems(); DownloadSettings settings = downloadSettings(); QString newName; foreach(const CamItemInfo& info, list) { CamItemInfo& refInfo = d->view->camItemInfoRef(info.folder, info.name); // qCDebug(DIGIKAM_IMPORTUI_LOG) << "slotDownloadNameChanged, old: " << refInfo.downloadName; newName = info.name; if (hasNoSelection || selected.contains(info.url())) { if (d->renameCustomizer->useDefault()) { newName = d->renameCustomizer->newName(info.name); } else if (d->renameCustomizer->isEnabled()) { newName = d->renameCustomizer->newName(info.url().toLocalFile()); } else if (!refInfo.downloadName.isEmpty()) { newName = refInfo.downloadName; } // Renaming files for the converting jpg to a lossless format // is from cameracontroller.cpp moved here. if (settings.convertJpeg && info.mime == QLatin1String("image/jpeg")) { QFileInfo fi(newName); QString ext = fi.suffix(); if (!ext.isEmpty()) { if (ext[0].isUpper() && ext[ext.length()-1].isUpper()) { ext = settings.losslessFormat.toUpper(); } else if (ext[0].isUpper()) { ext = settings.losslessFormat.toLower(); ext[0] = ext[0].toUpper(); } else { ext = settings.losslessFormat.toLower(); } newName = fi.completeBaseName() + QLatin1Char('.') + ext; } else { newName = newName + QLatin1Char('.') + settings.losslessFormat.toLower(); } } else if (settings.convertDng && info.mime == QLatin1String("image/x-raw")) { QFileInfo fi(newName); QString ext = fi.suffix(); if (!ext.isEmpty()) { if (ext[0].isUpper() && (ext[ext.length()-1].isUpper() || ext[ext.length()-1].isDigit())) { ext = QLatin1String("DNG"); } else if (ext[0].isUpper()) { ext = QLatin1String("Dng"); } else { ext = QLatin1String("dng"); } newName = fi.completeBaseName() + QLatin1Char('.') + ext; } else { newName = newName + QLatin1Char('.') + QLatin1String("dng"); } } } refInfo.downloadName = newName; // qCDebug(DIGIKAM_IMPORTUI_LOG) << "slotDownloadNameChanged, new: " << refInfo.downloadName; } d->view->updateIconView(); } //FIXME: the new pictures are marked by CameraHistoryUpdater which is not working yet. void ImportUI::slotSelectNew() { CamItemInfoList infos = d->view->allItems(); CamItemInfoList toBeSelected; foreach (const CamItemInfo& info, infos) { if (info.downloaded == CamItemInfo::DownloadedNo) { toBeSelected << info; } } d->view->setSelectedCamItemInfos(toBeSelected); } void ImportUI::slotSelectLocked() { CamItemInfoList allItems = d->view->allItems(); CamItemInfoList toBeSelected; foreach (const CamItemInfo& info, allItems) { if (info.writePermissions == 0) { toBeSelected << info; } } d->view->setSelectedCamItemInfos(toBeSelected); } void ImportUI::toggleLock(CamItemInfo& info) { if (!info.isNull()) { if (info.writePermissions == 0) { info.writePermissions = 1; } else { info.writePermissions = 0; } } } // TODO is this really necessary? why not just use the folders from listfolders call? QMap ImportUI::countItemsByFolders() const { QString path; QMap map; QMap::iterator it; CamItemInfoList infos = d->view->allItems(); foreach (const CamItemInfo& info, infos) { path = info.folder; if (!path.isEmpty() && path.endsWith(QLatin1Char('/'))) { path.truncate(path.length() - 1); } it = map.find(path); if (it == map.end()) { map.insert(path, 1); } else { it.value() ++; } } return map; } void ImportUI::setDownloaded(CamItemInfo& itemInfo, int status) { itemInfo.downloaded = status; d->progressValue = 0; if (itemInfo.downloaded == CamItemInfo::DownloadStarted) { d->progressTimer->start(500); } else { d->progressTimer->stop(); } } void ImportUI::slotProgressTimerDone() { d->progressTimer->start(300); } void ImportUI::itemsSelectionSizeInfo(unsigned long& fSizeKB, unsigned long& dSizeKB) { qint64 fSize = 0; // Files size qint64 dSize = 0; // Estimated space requires to download and process files. QList selected = d->view->selectedUrls(); CamItemInfoList list = d->view->allItems(); DownloadSettings settings = downloadSettings(); foreach (const CamItemInfo& info, list) { if (selected.contains(info.url())) { qint64 size = info.size; if (size < 0) // -1 if size is not provided by camera { continue; } fSize += size; if (info.mime == QLatin1String("image/jpeg")) { if (settings.convertJpeg) { // Estimated size is around 5 x original size when JPEG=>PNG. dSize += size * 5; } else if (settings.autoRotate) { // We need a double size to perform rotation. dSize += size * 2; } else { // Real file size is added. dSize += size; } } else if (settings.convertDng && info.mime == QLatin1String("image/x-raw")) { // Estimated size is around 2 x original size when RAW=>DNG. dSize += size * 2; } else { dSize += size; } } } fSizeKB = fSize / 1024; dSizeKB = dSize / 1024; } void ImportUI::checkItem4Deletion(const CamItemInfo& info, QStringList& folders, QStringList& files, CamItemInfoList& deleteList, CamItemInfoList& lockedList) { if (info.writePermissions != 0) // Item not locked ? { QString folder = info.folder; QString file = info.name; folders.append(folder); files.append(file); deleteList.append(info); } else { lockedList.append(info); } } void ImportUI::deleteItems(bool onlySelected, bool onlyDownloaded) { QStringList folders; QStringList files; CamItemInfoList deleteList; CamItemInfoList lockedList; CamItemInfoList list = onlySelected ? d->view->selectedCamItemInfos() : d->view->allItems(); foreach (const CamItemInfo& info, list) { if (onlyDownloaded) { if (info.downloaded == CamItemInfo::DownloadedYes) { checkItem4Deletion(info, folders, files, deleteList, lockedList); } } else { checkItem4Deletion(info, folders, files, deleteList, lockedList); } } // If we want to delete some locked files, just give a feedback to user. if (!lockedList.isEmpty()) { QString infoMsg(i18nc("@info", "The items listed below are locked by camera (read-only). " "These items will not be deleted. If you really want to delete these items, " "please unlock them and try again.")); CameraMessageBox::informationList(d->camThumbsCtrl, this, i18n("Information"), infoMsg, lockedList); } if (folders.isEmpty()) { return; } QString warnMsg(i18ncp("@info", "About to delete this image. " "Deleted file is unrecoverable. " "Are you sure?", "About to delete these %1 images. " "Deleted files are unrecoverable. " "Are you sure?", deleteList.count())); if (CameraMessageBox::warningContinueCancelList(d->camThumbsCtrl, this, i18n("Warning"), warnMsg, deleteList, QLatin1String("DontAskAgainToDeleteItemsFromCamera")) == QMessageBox::Yes) { QStringList::const_iterator itFolder = folders.constBegin(); QStringList::const_iterator itFile = files.constBegin(); d->statusProgressBar->setProgressValue(0); d->statusProgressBar->setProgressTotalSteps(deleteList.count()); d->statusProgressBar->setProgressBarMode(StatusProgressBar::ProgressBarMode); // enable cancel action. d->cameraCancelAction->setEnabled(true); for ( ; itFolder != folders.constEnd() ; ++itFolder, ++itFile) { d->controller->deleteFile(*itFolder, *itFile); // the currentlyDeleting list is used to prevent loading items which // will immanently be deleted into the sidebar and wasting time d->currentlyDeleting.append(*itFolder + *itFile); } } } bool ImportUI::checkDiskSpace(PAlbum *pAlbum) { if (!pAlbum) { return false; } unsigned long fSize = 0; unsigned long dSize = 0; itemsSelectionSizeInfo(fSize, dSize); QString albumRootPath = pAlbum->albumRootPath(); unsigned long kBAvail = d->albumLibraryFreeSpace->kBAvail(albumRootPath); if (dSize >= kBAvail) { int result = QMessageBox::warning(this, i18nc("@title:window", "Insufficient Disk Space"), i18nc("@info", "There is not enough free space on the disk of the album you selected " "to download and process the selected pictures from the camera.\n\n" "Estimated space required: %1\n" "Available free space: %2\n\n" "Try Anyway?", ItemPropertiesTab::humanReadableBytesCount(dSize * 1024), ItemPropertiesTab::humanReadableBytesCount(kBAvail * 1024)), QMessageBox::Yes | QMessageBox::No); if (result == QMessageBox::No) { return false; } } return true; } bool ImportUI::downloadCameraItems(PAlbum* pAlbum, bool onlySelected, bool deleteAfter) { KSharedConfig::Ptr config = KSharedConfig::openConfig(); KConfigGroup group = config->group(d->configGroupName); SetupCamera::ConflictRule rule = (SetupCamera::ConflictRule)group.readEntry(d->configFileSaveConflictRule, (int)SetupCamera::DIFFNAME); d->controller->downloadPrep(rule); QString downloadName; DownloadSettingsList allItems; DownloadSettings settings = downloadSettings(); QUrl url = pAlbum->fileUrl(); int downloadedItems = 0; // -- Download camera items ------------------------------- QList selected = d->view->selectedUrls(); CamItemInfoList list = d->view->allItems(); QSet usedDownloadPaths; foreach (const CamItemInfo& info, list) { if (onlySelected && !(selected.contains(info.url()))) { continue; } settings.folder = info.folder; settings.file = info.name; settings.mime = info.mime; settings.pickLabel = info.pickLabel; settings.colorLabel = info.colorLabel; settings.rating = info.rating; // downloadName should already be set by now downloadName = info.downloadName; QUrl downloadUrl(url); if (!createSubAlbums(downloadUrl, info)) { return false; } d->foldersToScan << downloadUrl.toLocalFile(); if (downloadName.isEmpty()) { downloadUrl = downloadUrl.adjusted(QUrl::StripTrailingSlash); downloadUrl.setPath(downloadUrl.path() + QLatin1Char('/') + settings.file); } else { // when using custom renaming (e.g. by date, see bug 179902) // make sure that we create unique names downloadUrl = downloadUrl.adjusted(QUrl::StripTrailingSlash); downloadUrl.setPath(downloadUrl.path() + QLatin1Char('/') + downloadName); QString suggestedPath = downloadUrl.toLocalFile(); if (usedDownloadPaths.contains(suggestedPath)) { QFileInfo fi(downloadName); QString suffix = QLatin1Char('.') + fi.suffix(); QString pathWithoutSuffix(suggestedPath); pathWithoutSuffix.chop(suffix.length()); QString currentVariant; int counter = 1; do { currentVariant = pathWithoutSuffix + QLatin1Char('-') + QString::number(counter++) + suffix; } while (usedDownloadPaths.contains(currentVariant)); usedDownloadPaths << currentVariant; downloadUrl = QUrl::fromLocalFile(currentVariant); } else { usedDownloadPaths << suggestedPath; } } settings.dest = downloadUrl.toLocalFile(); allItems.append(settings); if (settings.autoRotate && settings.mime == QLatin1String("image/jpeg")) { d->autoRotateItemsList << downloadUrl.toLocalFile(); qCDebug(DIGIKAM_IMPORTUI_LOG) << "autorotating for " << downloadUrl; } ++downloadedItems; } if (downloadedItems <= 0) { return false; } d->lastDestURL = url; d->statusProgressBar->setNotify(true); d->statusProgressBar->setProgressValue(0); d->statusProgressBar->setProgressTotalSteps(downloadedItems); d->statusProgressBar->setProgressBarMode(StatusProgressBar::ProgressBarMode); // enable cancel action. d->cameraCancelAction->setEnabled(true); // disable settings tab here instead of slotBusy: // Only needs to be disabled while downloading d->advBox->setEnabled(false); d->view->setEnabled(false); d->deleteAfter = deleteAfter; d->controller->download(allItems); return true; } bool ImportUI::createSubAlbums(QUrl& downloadUrl, const CamItemInfo& info) { bool success = true; if (d->albumCustomizer->autoAlbumDateEnabled()) { success &= createDateBasedSubAlbum(downloadUrl, info); } if (d->albumCustomizer->autoAlbumExtEnabled()) { success &= createExtBasedSubAlbum(downloadUrl, info); } return success; } bool ImportUI::createSubAlbum(QUrl& downloadUrl, const QString& subalbum, const QDate& date) { QString errMsg; if (!createAutoAlbum(downloadUrl, subalbum, date, errMsg)) { QMessageBox::critical(this, qApp->applicationName(), errMsg); return false; } downloadUrl = downloadUrl.adjusted(QUrl::StripTrailingSlash); downloadUrl.setPath(downloadUrl.path() + QLatin1Char('/') + subalbum); return true; } bool ImportUI::createDateBasedSubAlbum(QUrl& downloadUrl, const CamItemInfo& info) { QString dirName; QDateTime dateTime = info.ctime; switch (d->albumCustomizer->folderDateFormat()) { case AlbumCustomizer::TextDateFormat: dirName = dateTime.date().toString(Qt::TextDate); break; case AlbumCustomizer::LocalDateFormat: dirName = dateTime.date().toString(Qt::LocalDate); break; case AlbumCustomizer::IsoDateFormat: dirName = dateTime.date().toString(Qt::ISODate); break; default: // Custom dirName = dateTime.date().toString(d->albumCustomizer->customDateFormat()); break; } return createSubAlbum(downloadUrl, dirName, dateTime.date()); } bool ImportUI::createExtBasedSubAlbum(QUrl& downloadUrl, const CamItemInfo& info) { // We use the target file name to compute sub-albums name to take a care about // conversion on the fly option. QFileInfo fi(info.downloadName.isEmpty() ? info.name : info.downloadName); QString subAlbum = fi.suffix().toUpper(); if (fi.suffix().toUpper() == QLatin1String("JPEG") || fi.suffix().toUpper() == QLatin1String("JPE")) { subAlbum = QLatin1String("JPG"); } if (fi.suffix().toUpper() == QLatin1String("TIFF")) { subAlbum = QLatin1String("TIF"); } if (fi.suffix().toUpper() == QLatin1String("MPEG") || fi.suffix().toUpper() == QLatin1String("MPE") || fi.suffix().toUpper() == QLatin1String("MPO")) { subAlbum = QLatin1String("MPG"); } return createSubAlbum(downloadUrl, subAlbum, info.ctime.date()); } void ImportUI::slotDeleteNew() { slotSelectNew(); QTimer::singleShot(0, this, SLOT(slotDeleteSelected())); } void ImportUI::slotDeleteSelected() { deleteItems(true, false); } void ImportUI::slotDeleteAll() { deleteItems(false, false); } void ImportUI::slotDeleted(const QString& folder, const QString& file, bool status) { if (status) { // do this after removeItem. d->currentlyDeleting.removeAll(folder + file); } int curr = d->statusProgressBar->progressValue(); d->statusProgressBar->setProgressTotalSteps(curr + 1); refreshFreeSpace(); } void ImportUI::slotMetadata(const QString& folder, const QString& file, const DMetadata& meta) { CamItemInfo info = d->view->camItemInfo(folder, file); if (!info.isNull()) { d->rightSideBar->itemChanged(info, meta); } } void ImportUI::slotNewSelection(bool hasSelection) { updateActions(); QList renameFiles; CamItemInfoList list = hasSelection ? d->view->selectedCamItemInfos() : d->view->allItems(); foreach(const CamItemInfo& info, list) { ParseSettings parseSettings; parseSettings.fileUrl = info.url(); parseSettings.creationTime = info.ctime; renameFiles.append(parseSettings); } d->renameCustomizer->renameManager()->reset(); d->renameCustomizer->renameManager()->addFiles(renameFiles); d->renameCustomizer->renameManager()->parseFiles(); slotUpdateDownloadName(); unsigned long fSize = 0; unsigned long dSize = 0; itemsSelectionSizeInfo(fSize, dSize); d->albumLibraryFreeSpace->setEstimatedDSizeKb(dSize); } void ImportUI::slotImageSelected(const CamItemInfoList& selection, const CamItemInfoList& listAll) { if (d->cameraCancelAction->isEnabled()) { return; } int num_images = listAll.count(); switch (selection.count()) { case 0: { d->statusProgressBar->setProgressBarMode(StatusProgressBar::TextMode, i18ncp("@info:status", "No item selected (%1 item)", "No item selected (%1 items)", num_images)); d->rightSideBar->slotNoCurrentItem(); break; } case 1: { // if selected item is in the list of item which will be deleted, set no current item if (!d->currentlyDeleting.contains(selection.first().folder + selection.first().name)) { updateRightSideBar(selection.first()); int index = listAll.indexOf(selection.first()) + 1; d->statusProgressBar->setProgressBarMode(StatusProgressBar::TextMode, i18nc("@info:status Filename of first selected item of number of items", "%1 (%2 of %3)", selection.first().url().fileName(), index, num_images)); } else { d->rightSideBar->slotNoCurrentItem(); d->statusProgressBar->setProgressBarMode(StatusProgressBar::TextMode, i18ncp("@info:status", "No item selected (%1 item)", "No item selected (%1 items)", num_images)); } break; } default: { d->statusProgressBar->setProgressBarMode(StatusProgressBar::TextMode, i18ncp("@info:status", "%2/%1 item selected", "%2/%1 items selected", num_images, selection.count())); break; } } slotNewSelection(d->view->selectedCamItemInfos().count() > 0); } void ImportUI::updateRightSideBar(const CamItemInfo& info) { d->rightSideBar->itemChanged(info, DMetadata()); if (!d->busy) { d->controller->getMetadata(info.folder, info.name); } } QString ImportUI::identifyCategoryforMime(const QString& mime) { return mime.split(QLatin1Char('/')).at(0); } void ImportUI::autoRotateItems() { if (d->statusProgressBar->progressValue() != d->statusProgressBar->progressTotalSteps()) { return; } if (d->autoRotateItemsList.isEmpty()) { return; } ItemInfoList list; CollectionScanner scanner; foreach (const QString& downloadPath, d->autoRotateItemsList) { qlonglong id = scanner.scanFile(downloadPath, CollectionScanner::NormalScan); list << ItemInfo(id); } FileActionMngr::instance()->transform(list, MetaEngineRotation::NoTransformation); d->autoRotateItemsList.clear(); } bool ImportUI::createAutoAlbum(const QUrl& parentURL, const QString& sub, const QDate& date, QString& errMsg) const { QUrl url(parentURL); url = url.adjusted(QUrl::StripTrailingSlash); url.setPath(url.path() + QLatin1Char('/') + sub); // first stat to see if the album exists QFileInfo info(url.toLocalFile()); if (info.exists()) { // now check if its really a directory if (info.isDir()) { return true; } else { errMsg = i18nc("@info", "A file with the same name (%1) already exists in folder %2.", sub, QDir::toNativeSeparators(parentURL.toLocalFile())); return false; } } // looks like the directory does not exist, try to create it. // First we make sure that the parent exists. PAlbum* parent = AlbumManager::instance()->findPAlbum(parentURL); if (!parent) { errMsg = i18nc("@info", "Failed to find Album for path %1.", QDir::toNativeSeparators(parentURL.toLocalFile())); return false; } // Create the album, with any parent albums required for the structure QUrl albumUrl(parentURL); foreach (const QString& folder, sub.split(QLatin1Char('/'), QString::SkipEmptyParts)) { albumUrl = albumUrl.adjusted(QUrl::StripTrailingSlash); albumUrl.setPath(albumUrl.path() + QLatin1Char('/') + folder); PAlbum* album = AlbumManager::instance()->findPAlbum(albumUrl); if (!album) { album = AlbumManager::instance()->createPAlbum(parent, folder, QString(), date, QString(), errMsg); if (!album) { return false; } } parent = album; } return true; } void ImportUI::slotSetup() { Setup::execDialog(this); } void ImportUI::slotCameraFreeSpaceInfo(unsigned long kBSize, unsigned long kBAvail) { d->cameraFreeSpace->addInformation(kBSize, kBSize - kBAvail, kBAvail, QString()); } bool ImportUI::cameraDeleteSupport() const { return d->controller->cameraDeleteSupport(); } bool ImportUI::cameraUploadSupport() const { return d->controller->cameraUploadSupport(); } bool ImportUI::cameraMkDirSupport() const { return d->controller->cameraMkDirSupport(); } bool ImportUI::cameraDelDirSupport() const { return d->controller->cameraDelDirSupport(); } bool ImportUI::cameraUseUMSDriver() const { return d->controller->cameraDriverType() == DKCamera::UMSDriver; } bool ImportUI::cameraUseGPhotoDriver() const { return d->controller->cameraDriverType() == DKCamera::GPhotoDriver; } void ImportUI::enableZoomPlusAction(bool val) { d->increaseThumbsAction->setEnabled(val); } void ImportUI::enableZoomMinusAction(bool val) { d->decreaseThumbsAction->setEnabled(val); } void ImportUI::slotComponentsInfo() { showDigikamComponentsInfo(); } void ImportUI::slotDBStat() { showDigikamDatabaseStat(); } void ImportUI::refreshCollectionFreeSpace() { d->albumLibraryFreeSpace->setPaths(CollectionManager::instance()->allAvailableAlbumRootPaths()); } void ImportUI::slotCollectionLocationStatusChanged(const CollectionLocation&, int) { refreshCollectionFreeSpace(); } void ImportUI::slotToggleShowBar() { showThumbBar(d->showBarAction->isChecked()); } void ImportUI::slotLogMsg(const QString& msg, DHistoryView::EntryType type, const QString& folder, const QString& file) { d->statusProgressBar->setProgressText(msg); QStringList meta; meta << folder << file; d->historyView->addEntry(msg, type, QVariant(meta)); } void ImportUI::slotShowLog() { d->showLogAction->isChecked() ? d->historyView->show() : d->historyView->hide(); } void ImportUI::slotHistoryEntryClicked(const QVariant& metadata) { QStringList meta = metadata.toStringList(); QString folder = meta.at(0); QString file = meta.at(1); d->view->scrollTo(folder, file); } void ImportUI::showSideBars(bool visible) { visible ? d->rightSideBar->restore() : d->rightSideBar->backup(); } void ImportUI::slotToggleRightSideBar() { d->rightSideBar->isExpanded() ? d->rightSideBar->shrink() : d->rightSideBar->expand(); } void ImportUI::slotPreviousRightSideBarTab() { d->rightSideBar->activePreviousTab(); } void ImportUI::slotNextRightSideBarTab() { d->rightSideBar->activeNextTab(); } void ImportUI::showThumbBar(bool visible) { d->view->toggleShowBar(visible); } bool ImportUI::thumbbarVisibility() const { return d->showBarAction->isChecked(); } void ImportUI::slotSwitchedToPreview() { d->zoomBar->setBarMode(DZoomBar::PreviewZoomCtrl); d->imageViewSelectionAction->setCurrentAction(d->camItemPreviewAction); toogleShowBar(); } void ImportUI::slotSwitchedToIconView() { d->zoomBar->setBarMode(DZoomBar::ThumbsSizeCtrl); d->imageViewSelectionAction->setCurrentAction(d->iconViewAction); toogleShowBar(); } void ImportUI::slotSwitchedToMapView() { d->zoomBar->setBarMode(DZoomBar::ThumbsSizeCtrl); #ifdef HAVE_MARBLE d->imageViewSelectionAction->setCurrentAction(d->mapViewAction); #endif toogleShowBar(); } void ImportUI::customizedFullScreenMode(bool set) { toolBarMenuAction()->setEnabled(!set); showMenuBarAction()->setEnabled(!set); showStatusBarAction()->setEnabled(!set); set ? d->showBarAction->setEnabled(false) : toogleShowBar(); d->view->toggleFullScreen(set); } void ImportUI::toogleShowBar() { switch (d->view->viewMode()) { case ImportStackedView::PreviewImageMode: case ImportStackedView::MediaPlayerMode: d->showBarAction->setEnabled(true); break; default: d->showBarAction->setEnabled(false); break; } } void ImportUI::slotSetupChanged() { d->view->importFilterModel()->setStringTypeNatural(ApplicationSettings::instance()->isStringTypeNatural()); // Load full-screen options KConfigGroup group = KSharedConfig::openConfig()->group(ApplicationSettings::instance()->generalConfigGroupName()); readFullScreenSettings(group); d->view->applySettings(); sidebarTabTitleStyleChanged(); } void ImportUI::sidebarTabTitleStyleChanged() { d->rightSideBar->setStyle(ApplicationSettings::instance()->getSidebarTitleStyle()); d->rightSideBar->applySettings(); } void ImportUI::slotToggleColorManagedView() { if (!IccSettings::instance()->isEnabled()) { return; } bool cmv = !IccSettings::instance()->settings().useManagedPreviews; IccSettings::instance()->setUseManagedPreviews(cmv); d->camThumbsCtrl->clearCache(); } void ImportUI::slotColorManagementOptionsChanged() { ICCSettingsContainer settings = IccSettings::instance()->settings(); d->viewCMViewAction->blockSignals(true); d->viewCMViewAction->setEnabled(settings.enableCM); d->viewCMViewAction->setChecked(settings.useManagedPreviews); d->viewCMViewAction->blockSignals(false); } } // namespace Digikam diff --git a/core/utilities/lighttable/lighttablewindow_setup.cpp b/core/utilities/lighttable/lighttablewindow_setup.cpp index 1ae9ae0adb..2e1a9a2511 100644 --- a/core/utilities/lighttable/lighttablewindow_setup.cpp +++ b/core/utilities/lighttable/lighttablewindow_setup.cpp @@ -1,479 +1,479 @@ /* ============================================================ * * This file is a part of digiKam project * http://www.digikam.org * * Date : 2004-11-22 * Description : digiKam light table - Configure * * Copyright (C) 2007-2019 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 "lighttablewindow.h" #include "lighttablewindow_p.h" namespace Digikam { void LightTableWindow::setupActions() { // -- Standard 'File' menu actions --------------------------------------------- KActionCollection* const ac = actionCollection(); d->backwardAction = buildStdAction(StdBackAction, this, SLOT(slotBackward()), this); ac->addAction(QLatin1String("lighttable_backward"), d->backwardAction); ac->setDefaultShortcuts(d->backwardAction, QList() << Qt::Key_PageUp << Qt::Key_Backspace); d->forwardAction = buildStdAction(StdForwardAction, this, SLOT(slotForward()), this); ac->addAction(QLatin1String("lighttable_forward"), d->forwardAction); ac->setDefaultShortcuts(d->forwardAction, QList() << Qt::Key_PageDown << Qt::Key_Space); d->forwardAction->setEnabled(false); d->firstAction = new QAction(QIcon::fromTheme(QLatin1String("go-first")), i18n("&First"), this); d->firstAction->setEnabled(false); connect(d->firstAction, SIGNAL(triggered()), this, SLOT(slotFirst())); ac->addAction(QLatin1String("lighttable_first"), d->firstAction); ac->setDefaultShortcuts(d->firstAction, QList() << Qt::CTRL + Qt::Key_Home); d->lastAction = new QAction(QIcon::fromTheme(QLatin1String("go-last")), i18n("&Last"), this); d->lastAction->setEnabled(false); connect(d->lastAction, SIGNAL(triggered()), this, SLOT(slotLast())); ac->addAction(QLatin1String("lighttable_last"), d->lastAction); ac->setDefaultShortcuts(d->lastAction, QList() << Qt::CTRL + Qt::Key_End); d->setItemLeftAction = new QAction(QIcon::fromTheme(QLatin1String("go-previous")), i18n("On left"), this); d->setItemLeftAction->setEnabled(false); d->setItemLeftAction->setWhatsThis(i18n("Show item on left panel")); connect(d->setItemLeftAction, SIGNAL(triggered()), this, SLOT(slotSetItemLeft())); ac->addAction(QLatin1String("lighttable_setitemleft"), d->setItemLeftAction); ac->setDefaultShortcut(d->setItemLeftAction, Qt::CTRL + Qt::Key_L); d->setItemRightAction = new QAction(QIcon::fromTheme(QLatin1String("go-next")), i18n("On right"), this); d->setItemRightAction->setEnabled(false); d->setItemRightAction->setWhatsThis(i18n("Show item on right panel")); connect(d->setItemRightAction, SIGNAL(triggered()), this, SLOT(slotSetItemRight())); ac->addAction(QLatin1String("lighttable_setitemright"), d->setItemRightAction); ac->setDefaultShortcut(d->setItemRightAction, Qt::CTRL + Qt::Key_R); d->editItemAction = new QAction(QIcon::fromTheme(QLatin1String("document-edit")), i18n("Edit"), this); d->editItemAction->setEnabled(false); connect(d->editItemAction, SIGNAL(triggered()), this, SLOT(slotEditItem())); ac->addAction(QLatin1String("lighttable_edititem"), d->editItemAction); ac->setDefaultShortcut(d->editItemAction, Qt::Key_F4); QAction* const openWithAction = new QAction(QIcon::fromTheme(QLatin1String("preferences-desktop-filetype-association")), i18n("Open With Default Application"), this); openWithAction->setWhatsThis(i18n("Open the item with default assigned application.")); connect(openWithAction, SIGNAL(triggered()), this, SLOT(slotFileWithDefaultApplication())); ac->addAction(QLatin1String("open_with_default_application"), openWithAction); ac->setDefaultShortcut(openWithAction, Qt::META + Qt::Key_F4); d->removeItemAction = new QAction(QIcon::fromTheme(QLatin1String("list-remove")), i18n("Remove item from LightTable"), this); d->removeItemAction->setEnabled(false); connect(d->removeItemAction, SIGNAL(triggered()), this, SLOT(slotRemoveItem())); ac->addAction(QLatin1String("lighttable_removeitem"), d->removeItemAction); ac->setDefaultShortcut(d->removeItemAction, Qt::CTRL + Qt::Key_K); d->clearListAction = new QAction(QIcon::fromTheme(QLatin1String("edit-clear")), i18n("Remove all items from LightTable"), this); d->clearListAction->setEnabled(false); connect(d->clearListAction, SIGNAL(triggered()), this, SLOT(slotClearItemsList())); ac->addAction(QLatin1String("lighttable_clearlist"), d->clearListAction); ac->setDefaultShortcut(d->clearListAction, Qt::CTRL + Qt::SHIFT + Qt::Key_K); - d->fileDeleteAction = new QAction(QIcon::fromTheme(QLatin1String("user-trash")), i18nc("Non-pluralized", "Move to Trash"), this); + d->fileDeleteAction = new QAction(QIcon::fromTheme(QLatin1String("user-trash-full")), i18nc("Non-pluralized", "Move to Trash"), this); d->fileDeleteAction->setEnabled(false); connect(d->fileDeleteAction, SIGNAL(triggered()), this, SLOT(slotDeleteItem())); ac->addAction(QLatin1String("lighttable_filedelete"), d->fileDeleteAction); ac->setDefaultShortcut(d->fileDeleteAction, Qt::Key_Delete); d->fileDeleteFinalAction = new QAction(QIcon::fromTheme(QLatin1String("edit-delete")), i18n("Delete immediately"), this); d->fileDeleteFinalAction->setEnabled(false); connect(d->fileDeleteFinalAction, SIGNAL(triggered()), this, SLOT(slotDeleteFinalItem())); ac->addAction(QLatin1String("lighttable_filefinaldelete"), d->fileDeleteFinalAction); ac->setDefaultShortcut(d->fileDeleteFinalAction, Qt::SHIFT + Qt::Key_Delete); QAction* const closeAction = buildStdAction(StdCloseAction, this, SLOT(close()), this); ac->addAction(QLatin1String("lighttable_close"), closeAction); // -- Standard 'View' menu actions --------------------------------------------- d->syncPreviewAction = new QAction(QIcon::fromTheme(QLatin1String("view-split-left-right")), i18n("Synchronize"), this); d->syncPreviewAction->setEnabled(false); d->syncPreviewAction->setCheckable(true); d->syncPreviewAction->setWhatsThis(i18n("Synchronize preview from left and right panels")); connect(d->syncPreviewAction, SIGNAL(triggered()), this, SLOT(slotToggleSyncPreview())); ac->addAction(QLatin1String("lighttable_syncpreview"), d->syncPreviewAction); ac->setDefaultShortcut(d->syncPreviewAction, Qt::CTRL + Qt::SHIFT + Qt::Key_Y); d->navigateByPairAction = new QAction(QIcon::fromTheme(QLatin1String("system-run")), i18n("By Pair"), this); d->navigateByPairAction->setEnabled(false); d->navigateByPairAction->setCheckable(true); d->navigateByPairAction->setWhatsThis(i18n("Navigate by pairs with all items")); connect(d->navigateByPairAction, SIGNAL(triggered()), this, SLOT(slotToggleNavigateByPair())); ac->addAction(QLatin1String("lighttable_navigatebypair"), d->navigateByPairAction); ac->setDefaultShortcut(d->navigateByPairAction, Qt::CTRL + Qt::SHIFT + Qt::Key_P); d->clearOnCloseAction = new QAction(QIcon::fromTheme(QLatin1String("edit-clear")), i18n("Clear On Close"), this); d->clearOnCloseAction->setEnabled(true); d->clearOnCloseAction->setCheckable(true); d->clearOnCloseAction->setToolTip(i18n("Clear light table when it is closed")); d->clearOnCloseAction->setWhatsThis(i18n("Remove all images from the light table when it is closed")); ac->addAction(QLatin1String("lighttable_clearonclose"), d->clearOnCloseAction); ac->setDefaultShortcut(d->clearOnCloseAction, Qt::CTRL + Qt::SHIFT + Qt::Key_C); d->showBarAction = d->barViewDock->getToggleAction(this); ac->addAction(QLatin1String("lighttable_showthumbbar"), d->showBarAction); ac->setDefaultShortcut(d->showBarAction, Qt::CTRL + Qt::Key_T); createFullScreenAction(QLatin1String("lighttable_fullscreen")); createSidebarActions(); d->slideShowAction = new QAction(QIcon::fromTheme(QLatin1String("view-presentation")), i18n("Slideshow"), this); connect(d->slideShowAction, SIGNAL(triggered()), this, SLOT(slotSlideShowAll())); ac->addAction(QLatin1String("lighttable_slideshow"), d->slideShowAction); ac->setDefaultShortcut(d->slideShowAction, Qt::Key_F9); createPresentationAction(); // -- Standard 'Tools' menu actions ------------------------ createMetadataEditAction(); createGeolocationEditAction(); createTimeAdjustAction(); createHtmlGalleryAction(); createPanoramaAction(); createExpoBlendingAction(); createCalendarAction(); createVideoSlideshowAction(); createSendByMailAction(); createPrintCreatorAction(); createMediaServerAction(); createExportActions(); createImportActions(); // Left Panel Zoom Actions d->leftZoomPlusAction = buildStdAction(StdZoomInAction, d->previewView, SLOT(slotIncreaseLeftZoom()), this); d->leftZoomPlusAction->setEnabled(false); ac->addAction(QLatin1String("lighttable_zoomplus_left"), d->leftZoomPlusAction); d->leftZoomMinusAction = buildStdAction(StdZoomOutAction, d->previewView, SLOT(slotDecreaseLeftZoom()), this); d->leftZoomMinusAction->setEnabled(false); ac->addAction(QLatin1String("lighttable_zoomminus_left"), d->leftZoomMinusAction); d->leftZoomTo100percents = new QAction(QIcon::fromTheme(QLatin1String("zoom-original")), i18n("Zoom to 100%"), this); connect(d->leftZoomTo100percents, SIGNAL(triggered()), d->previewView, SLOT(slotLeftZoomTo100())); ac->addAction(QLatin1String("lighttable_zoomto100percents_left"), d->leftZoomTo100percents); ac->setDefaultShortcut(d->leftZoomTo100percents, Qt::CTRL + Qt::Key_Period); d->leftZoomFitToWindowAction = new QAction(QIcon::fromTheme(QLatin1String("zoom-fit-best")), i18n("Fit to &Window"), this); connect(d->leftZoomFitToWindowAction, SIGNAL(triggered()), d->previewView, SLOT(slotLeftFitToWindow())); ac->addAction(QLatin1String("lighttable_zoomfit2window_left"), d->leftZoomFitToWindowAction); ac->setDefaultShortcut(d->leftZoomFitToWindowAction, Qt::ALT + Qt::CTRL + Qt::Key_E); // Right Panel Zoom Actions d->rightZoomPlusAction = buildStdAction(StdZoomInAction, d->previewView, SLOT(slotIncreaseRightZoom()), this); d->rightZoomPlusAction->setEnabled(false); ac->addAction(QLatin1String("lighttable_zoomplus_right"), d->rightZoomPlusAction); ac->setDefaultShortcut(d->rightZoomPlusAction, Qt::SHIFT + d->rightZoomPlusAction->shortcut()[0]); d->rightZoomMinusAction = buildStdAction(StdZoomOutAction, d->previewView, SLOT(slotDecreaseRightZoom()), this); d->rightZoomMinusAction->setEnabled(false); ac->addAction(QLatin1String("lighttable_zoomminus_right"), d->rightZoomMinusAction); ac->setDefaultShortcut(d->rightZoomMinusAction, Qt::SHIFT + d->rightZoomMinusAction->shortcut()[0]); d->rightZoomTo100percents = new QAction(QIcon::fromTheme(QLatin1String("zoom-original")), i18n("Zoom to 100%"), this); connect(d->rightZoomTo100percents, SIGNAL(triggered()), d->previewView, SLOT(slotRightZoomTo100())); ac->addAction(QLatin1String("lighttable_zoomto100percents_right"), d->rightZoomTo100percents); ac->setDefaultShortcut(d->rightZoomTo100percents, Qt::SHIFT + Qt::CTRL + Qt::Key_Period); d->rightZoomFitToWindowAction = new QAction(QIcon::fromTheme(QLatin1String("zoom-fit-best")), i18n("Fit to &Window"), this); connect(d->rightZoomFitToWindowAction, SIGNAL(triggered()), d->previewView, SLOT(slotRightFitToWindow())); ac->addAction(QLatin1String("lighttable_zoomfit2window_right"), d->rightZoomFitToWindowAction); ac->setDefaultShortcut(d->rightZoomFitToWindowAction, Qt::SHIFT + Qt::CTRL + Qt::Key_E); // ----------------------------------------------------------- d->viewCMViewAction = new QAction(QIcon::fromTheme(QLatin1String("video-display")), i18n("Color-Managed View"), this); d->viewCMViewAction->setCheckable(true); connect(d->viewCMViewAction, SIGNAL(triggered()), this, SLOT(slotToggleColorManagedView())); ac->addAction(QLatin1String("color_managed_view"), d->viewCMViewAction); ac->setDefaultShortcut(d->viewCMViewAction, Qt::Key_F12); // ----------------------------------------------------------------------------- ThemeManager::instance()->registerThemeActions(this); // Standard 'Help' menu actions createHelpActions(); // Provides a menu entry that allows showing/hiding the toolbar(s) setStandardToolBarMenuEnabled(true); // Provides a menu entry that allows showing/hiding the statusbar createStandardStatusBarAction(); // Standard 'Configure' menu actions createSettingsActions(); // -- Keyboard-only actions ---------------------------------------------------- d->addPageUpDownActions(this, this); QAction* const altBackwardAction = new QAction(i18n("Previous Image"), this); ac->addAction(QLatin1String("lighttable_backward_shift_space"), altBackwardAction); ac->setDefaultShortcut(altBackwardAction, Qt::SHIFT + Qt::Key_Space); connect(altBackwardAction, SIGNAL(triggered()), this, SLOT(slotBackward())); // Labels shortcuts must be registered here to be saved in XML GUI files if user customize it. TagsActionMngr::defaultManager()->registerLabelsActions(ac); QAction* const editTitlesRight = new QAction(i18n("Edit Titles on the Right"), this); ac->addAction(QLatin1String("edit_titles_right"), editTitlesRight); ac->setDefaultShortcut(editTitlesRight, Qt::META + Qt::Key_T); connect(editTitlesRight, SIGNAL(triggered()), this, SLOT(slotRightSideBarActivateTitles())); QAction* const editCommentsRight = new QAction(i18n("Edit Comments on the Right"), this); ac->addAction(QLatin1String("edit_comments_right"), editCommentsRight); ac->setDefaultShortcut(editCommentsRight, Qt::META + Qt::Key_C); connect(editCommentsRight, SIGNAL(triggered()), this, SLOT(slotRightSideBarActivateComments())); QAction* const editTitlesLeft = new QAction(i18n("Edit Titles on the Left"), this); ac->addAction(QLatin1String("edit_titles_left"), editTitlesLeft); ac->setDefaultShortcut(editTitlesLeft, Qt::SHIFT + Qt::META + Qt::Key_T); connect(editTitlesLeft, SIGNAL(triggered()), this, SLOT(slotLeftSideBarActivateTitles())); QAction* const editCommentsLeft = new QAction(i18n("Edit Comments on the Left"), this); ac->addAction(QLatin1String("edit_comments_left"), editCommentsLeft); ac->setDefaultShortcut(editCommentsLeft, Qt::SHIFT + Qt::META + Qt::Key_C); connect(editCommentsLeft, SIGNAL(triggered()), this, SLOT(slotLeftSideBarActivateComments())); QAction* const assignedTagsRight = new QAction(i18n("Show Assigned Tags on the Right"), this); ac->addAction(QLatin1String("assigned _tags_right"), assignedTagsRight); ac->setDefaultShortcut(assignedTagsRight, Qt::META + Qt::Key_A); connect(assignedTagsRight, SIGNAL(triggered()), this, SLOT(slotRightSideBarActivateAssignedTags())); QAction* const assignedTagsLeft = new QAction(i18n("Show Assigned Tags on the Left"), this); ac->addAction(QLatin1String("assigned _tags_left"), assignedTagsLeft); ac->setDefaultShortcut(assignedTagsLeft, Qt::SHIFT + Qt::META + Qt::Key_A); connect(assignedTagsLeft, SIGNAL(triggered()), this, SLOT(slotLeftSideBarActivateAssignedTags())); // --------------------------------------------------------------------------------- createGUI(xmlFile()); cleanupActions(); showMenuBarAction()->setChecked(!menuBar()->isHidden()); // NOTE: workaround for bug #171080 } void LightTableWindow::setupStatusBar() { d->leftZoomBar = new DZoomBar(statusBar()); d->leftZoomBar->setZoomToFitAction(d->leftZoomFitToWindowAction); d->leftZoomBar->setZoomTo100Action(d->leftZoomTo100percents); d->leftZoomBar->setZoomPlusAction(d->leftZoomPlusAction); d->leftZoomBar->setZoomMinusAction(d->leftZoomMinusAction); d->leftZoomBar->setBarMode(DZoomBar::PreviewZoomCtrl); d->leftZoomBar->setEnabled(false); statusBar()->addWidget(d->leftZoomBar, 1); d->leftFileName = new DAdjustableLabel(statusBar()); d->leftFileName->setAlignment(Qt::AlignLeft | Qt::AlignVCenter); statusBar()->addWidget(d->leftFileName, 10); d->statusProgressBar = new StatusProgressBar(statusBar()); d->statusProgressBar->setAlignment(Qt::AlignCenter); statusBar()->addWidget(d->statusProgressBar, 10); d->rightFileName = new DAdjustableLabel(statusBar()); d->rightFileName->setAlignment(Qt::AlignRight | Qt::AlignVCenter); statusBar()->addWidget(d->rightFileName, 10); d->rightZoomBar = new DZoomBar(statusBar()); d->rightZoomBar->setZoomToFitAction(d->rightZoomFitToWindowAction); d->rightZoomBar->setZoomTo100Action(d->rightZoomTo100percents); d->rightZoomBar->setZoomPlusAction(d->rightZoomPlusAction); d->rightZoomBar->setZoomMinusAction(d->rightZoomMinusAction); d->rightZoomBar->setBarMode(DZoomBar::PreviewZoomCtrl); d->rightZoomBar->setEnabled(false); statusBar()->addWidget(d->rightZoomBar, 1); } void LightTableWindow::setupConnections() { connect(ApplicationSettings::instance(), SIGNAL(setupChanged()), this, SLOT(slotApplicationSettingsChanged())); connect(ThemeManager::instance(), SIGNAL(signalThemeChanged()), this, SLOT(slotThemeChanged())); connect(IccSettings::instance(), SIGNAL(settingsChanged()), this, SLOT(slotColorManagementOptionsChanged())); // Thumbs bar connections --------------------------------------- connect(d->thumbView, SIGNAL(signalSetItemOnLeftPanel(ItemInfo)), this, SLOT(slotSetItemOnLeftPanel(ItemInfo))); connect(d->thumbView, SIGNAL(signalSetItemOnRightPanel(ItemInfo)), this, SLOT(slotSetItemOnRightPanel(ItemInfo))); connect(d->thumbView, SIGNAL(signalRemoveItem(ItemInfo)), this, SLOT(slotRemoveItem(ItemInfo))); connect(d->thumbView, SIGNAL(signalEditItem(ItemInfo)), this, SLOT(slotEditItem(ItemInfo))); connect(d->thumbView, SIGNAL(signalClearAll()), this, SLOT(slotClearItemsList())); connect(d->thumbView, SIGNAL(signalDroppedItems(QList)), this, SLOT(slotThumbbarDroppedItems(QList))); connect(d->thumbView, SIGNAL(currentChanged(ItemInfo)), this, SLOT(slotItemSelected(ItemInfo))); connect(d->thumbView, SIGNAL(signalContentChanged()), this, SLOT(slotRefreshStatusBar())); // Zoom bars connections ----------------------------------------- connect(d->leftZoomBar, SIGNAL(signalZoomSliderChanged(int)), d->previewView, SLOT(slotLeftZoomSliderChanged(int))); connect(d->leftZoomBar, SIGNAL(signalZoomValueEdited(double)), d->previewView, SLOT(setLeftZoomFactor(double))); connect(d->rightZoomBar, SIGNAL(signalZoomSliderChanged(int)), d->previewView, SLOT(slotRightZoomSliderChanged(int))); connect(d->rightZoomBar, SIGNAL(signalZoomValueEdited(double)), d->previewView, SLOT(setRightZoomFactor(double))); // View connections --------------------------------------------- connect(d->previewView, SIGNAL(signalLeftPopupTagsView()), d->leftSideBar, SLOT(slotPopupTagsView())); connect(d->previewView, SIGNAL(signalRightPopupTagsView()), d->rightSideBar, SLOT(slotPopupTagsView())); connect(d->previewView, SIGNAL(signalLeftZoomFactorChanged(double)), this, SLOT(slotLeftZoomFactorChanged(double))); connect(d->previewView, SIGNAL(signalRightZoomFactorChanged(double)), this, SLOT(slotRightZoomFactorChanged(double))); connect(d->previewView, SIGNAL(signalEditItem(ItemInfo)), this, SLOT(slotEditItem(ItemInfo))); connect(d->previewView, SIGNAL(signalDeleteItem(ItemInfo)), this, SLOT(slotDeleteItem(ItemInfo))); connect(d->previewView, SIGNAL(signalLeftSlideShowCurrent()), this, SLOT(slotLeftSlideShowManualFromCurrent())); connect(d->previewView, SIGNAL(signalRightSlideShowCurrent()), this, SLOT(slotRightSlideShowManualFromCurrent())); connect(d->previewView, SIGNAL(signalLeftDroppedItems(ItemInfoList)), this, SLOT(slotLeftDroppedItems(ItemInfoList))); connect(d->previewView, SIGNAL(signalRightDroppedItems(ItemInfoList)), this, SLOT(slotRightDroppedItems(ItemInfoList))); connect(d->previewView, SIGNAL(signalToggleOnSyncPreview(bool)), this, SLOT(slotToggleOnSyncPreview(bool))); connect(d->previewView, SIGNAL(signalLeftPreviewLoaded(bool)), this, SLOT(slotLeftPreviewLoaded(bool))); connect(d->previewView, SIGNAL(signalRightPreviewLoaded(bool)), this, SLOT(slotRightPreviewLoaded(bool))); connect(d->previewView, SIGNAL(signalLeftPanelLeftButtonClicked()), this, SLOT(slotLeftPanelLeftButtonClicked())); connect(d->previewView, SIGNAL(signalRightPanelLeftButtonClicked()), this, SLOT(slotRightPanelLeftButtonClicked())); connect(this, SIGNAL(signalWindowHasMoved()), d->leftZoomBar, SLOT(slotUpdateTrackerPos())); connect(this, SIGNAL(signalWindowHasMoved()), d->rightZoomBar, SLOT(slotUpdateTrackerPos())); // -- FileWatch connections ------------------------------ LoadingCacheInterface::connectToSignalFileChanged(this, SLOT(slotFileChanged(QString))); } void LightTableWindow::setupUserArea() { QWidget* const mainW = new QWidget(this); d->hSplitter = new SidebarSplitter(Qt::Horizontal, mainW); QHBoxLayout* const hlay = new QHBoxLayout(mainW); // The left sidebar d->leftSideBar = new ItemPropertiesSideBarDB(mainW, d->hSplitter, Qt::LeftEdge, true); // The central preview is wrapped in a KMainWindow so that the thumbnail // bar can float around it. KMainWindow* const viewContainer = new KMainWindow(mainW, Qt::Widget); d->hSplitter->addWidget(viewContainer); d->previewView = new LightTableView(viewContainer); viewContainer->setCentralWidget(d->previewView); // The right sidebar. d->rightSideBar = new ItemPropertiesSideBarDB(mainW, d->hSplitter, Qt::RightEdge, true); hlay->addWidget(d->leftSideBar); hlay->addWidget(d->hSplitter); hlay->addWidget(d->rightSideBar); hlay->setSpacing(0); hlay->setContentsMargins(QMargins()); hlay->setStretchFactor(d->hSplitter, 10); d->hSplitter->setFrameStyle(QFrame::NoFrame); d->hSplitter->setFrameShadow(QFrame::Plain); d->hSplitter->setFrameShape(QFrame::NoFrame); d->hSplitter->setOpaqueResize(false); d->hSplitter->setStretchFactor(1, 10); // set previewview+thumbbar container default size to max. // The thumb bar is placed in a detachable/dockable widget. d->barViewDock = new ThumbBarDock(viewContainer, Qt::Tool); d->barViewDock->setObjectName(QLatin1String("lighttable_thumbbar")); d->barViewDock->setWindowTitle(i18n("Light Table Thumbnail Dock")); d->thumbView = new LightTableThumbBar(d->barViewDock); d->barViewDock->setWidget(d->thumbView); viewContainer->addDockWidget(Qt::TopDockWidgetArea, d->barViewDock); d->barViewDock->setFloating(false); // Restore the previous state. This doesn't emit the proper signals to the // dock widget, so it has to be manually reinitialized. viewContainer->setAutoSaveSettings(QLatin1String("LightTable Thumbbar"), true); connect(d->barViewDock, SIGNAL(dockLocationChanged(Qt::DockWidgetArea)), d->thumbView, SLOT(slotDockLocationChanged(Qt::DockWidgetArea))); d->barViewDock->reInitialize(); setCentralWidget(mainW); } } // namespace Digikam