diff --git a/NEWS b/NEWS index b566deb08d..7ca0395e9d 100644 --- a/NEWS +++ b/NEWS @@ -1,153 +1,154 @@ ***************************************************************************************************** digiKam 4.3.0 - Release date: 2014-09-07 NEW FEATURES: Database : Nepomuk support have been replaced by Baloo support (https://community.kde.org/Baloo). Map Searches : New option to display all non geolocated images from collections. General : New notification event when kioslave cannot be started. General : OSX event notifier is now used to dispatch notifications. AlbumGui : New Action To Exclude Items Without Rating with items filter. Details: http://mohamedanwer.wordpress.com/2014/08/28/new-action-to-exclude-items-without-rating/ BUGFIXES FROM KDE BUGZILLA (alias B.K.O | http://bugs.kde.org): 001 ==> 338048 - Separate Tags in Public and Private for Export. 002 ==> 338055 - libkdepimlibs not found. 003 ==> 317813 - SQLITE : digiKam do not accept previous DB Sqlite. 004 ==> 198063 - SCAN : digiKam startup is extremely slow. 005 ==> 262720 - SCAN : 'scan for new images' should work on just current Album. 006 ==> 317864 - Album rename problem. 007 ==> 230602 - Do not display photos, after the removal of the tag. 008 ==> 318201 - Assigned tags in IconView should be sorted. 009 ==> 336328 - Tags Manager's Sync Export DB to metadata, mirrors XMP but it only appends IPTC keywords data. 010 ==> 335624 - digiKam crash when I start face recognition. 011 ==> 338085 - digiKam 4.2 fails to start on windows. 012 ==> 327646 - MySQL : Failed to create tables in database" Error In digiKam [patch]. 013 ==> 195006 - SQLITE : UTF8 characters not accepted in database file path. 014 ==> 308931 - digiKam Nepomuk integration (v2). 015 ==> 225869 - Add option to found no geolocated images in database. 016 ==> 338429 - Removal of preposition 'My' from UI wording. 017 ==> 289204 - General performance problems because of lacking multithreading / multicore support. 018 ==> 332999 - Export shortcut. 019 ==> 333001 - JP2 compression has no effect. 020 ==> 327827 - Some pictures not visible in digiKam. 021 ==> 338138 - Preview uses low quality interpolation. 022 ==> 324323 - Hang on opening Geolocation pane on photos with XMP GPS tags. 023 ==> 284126 - Documentation required for compilation but should be optional. 024 ==> 258985 - digiKam does not store correct color profile in JPEG2000 or TIFF images. 025 ==> 338172 - After changing the Properties of an album, the selector in the album list (left bar) jumps, a random album is selected. 026 ==> 322789 - Enabling "color managed view" in editor slows down tool startup. 027 ==> 156186 - Sorting of albums other than alphabetically. 028 ==> 267511 - Export /print selection manager. 029 ==> 129132 - Hidden tags for archiving purposes. 030 ==> 266671 - Option to detect faces on all pictures that had no faces detected on previous runs. 031 ==> 265268 - Usability: Change Icon in Scanresults to mark as "not a face". 032 ==> 287621 - Right Click Face Tagging in Preview not working. 033 ==> 262170 - The remove button for faces should be more explicit. 034 ==> 286770 - Unknown face tag no longer shows any items, even though there are unlabelled face tags in images. 035 ==> 263397 - Usability enhancement on changing confirmed faces. 036 ==> 308642 - Faces assignement : user interface improvements. 037 ==> 279199 - Several UI suggestions for face-detection. 038 ==> 271841 - SCAN : KDirwatch and database : digikam very slow when editing iptc data. 039 ==> 290421 - digiKam should inform users when kio-slave fail to load. 040 ==> 289872 - Database will not open. 041 ==> 306158 - GROUP : Sorting the image-groups by name in main image view, when the images are grouped in albums. 042 ==> 338522 - digiKam doesn't save metadata to images. 043 ==> 307613 - Face tag input field does not show when no Icon View Options are enabled. 044 ==> 187651 - Improve wording and usability of first-run wizard. 045 ==> 327054 - Startup scan happens twice if splash screen turned off in settings->misc->splash screen. 046 ==> 279835 - Rating Filter : checkbox for including or excluding not rated images. 047 ==> 298158 - When working in digiKam, metadata erased whereas GIMP preserves metadata. 048 ==> 141014 - Time and dates in digiKam. 049 ==> 188925 - Write image metadata in background with user feedback. 050 ==> 126821 - Handle existing read-only photo trees without copying (i.e. virtual albums). 051 ==> 335256 - Add option to view custom GPS EXIF tags (IPTC). 052 ==> 213745 - Create date change when we modefy xmp metadata. 053 ==> 275351 - User Icons won't show on digikam restart. 054 ==> 278110 - Fuji X100 RAF RAW files doesn't show right under image editor. 055 ==> 308821 - GoogleMaps : Impossible to open map view. 056 ==> 338631 - Extending raw support. 057 ==> 161619 - Smart zoom steps in Image Editor. 058 ==> 284213 - Editing tools re-organized to sidepanel to save space with better usability. 059 ==> 148238 - Fast switching between preview modes. 060 ==> 155415 - PRINT : white lines in preview and output. 061 ==> 229670 - Curves Widget does not restore a "free curve" correctly (in BWSepia filter). 062 ==> 267216 - Enable auto detection of libjpeg turbo under fedora 14 or later. 063 ==> 291972 - HEAD compiling issues under FreeBSD 8.2 [patch]. 064 ==> 326112 - SCAN : Moving an album (directory) loses metadata for cr2 files. 065 ==> 322079 - Mouse wheel short cut to move through my pictures. 066 ==> 312741 - When scrolling through lots of pics, these apper too small. 067 ==> 293957 - Preview image not updated after rotation or flip. 068 ==> 209343 - Mouse-wheel should jump to next/previous picture if used when viewing a picture. 069 ==> 160591 - Forward / Back (Previous / Next) don't work in preview mode. 070 ==> 132047 - Faster display of images and/or prefetch wished for. 071 ==> 261562 - Drop tags over preview canvas. 072 ==> 292407 - Display image center when Zooming to 100%. 073 ==> 314377 - digiKam should display images without interpolation when zoom is > 200%. 074 ==> 316863 - Zoom keyboard shortcuts not working in preview mode. 075 ==> 154643 - Slideshow from digiKam could use some filter options. 076 ==> 255792 - Photo selection mode while browsing album with Slideshow. 077 ==> 321737 - Image editor always opens small window. 078 ==> 288614 - Tags filter and Taggin sidebar are too similar, easy to mistake. 079 ==> 291542 - Mouse scroll by itself does NOT take one to the next or previous picture in Reduced Size Preview. 080 ==> 140743 - More flexible and consistent grouping of icons. 081 ==> 248565 - Basic functionality not available via keyboard. 082 ==> 246726 - Ask for confirmation when deleting many images. 083 ==> 162552 - Compact image display in virtual folders. 084 ==> 318700 - Preview mode not updated after importing or deleting pictures. 085 ==> 333598 - Rebuild fingerprints crashes with large number (>40000) of images. 086 ==> 251664 - Allow to remove all duplicates except in one selected Album. 087 ==> 324900 - Impossible to select collection subfolder in duplicates search. 088 ==> 336327 - digiKam crashed when start to import pictures from usb-drive. 089 ==> 300020 - Picture not showing in the thumbnails after edition. 090 ==> 329524 - Moving versioned pictures to a different album ungroups them. 091 ==> 305359 - Advanced rename of lots of images is slow. 092 ==> 333356 - Crash on attemp to change modified date to created date. 093 ==> 134164 - Be able to use the settings from inside the .NEF file itself. 094 ==> 338081 - Support HDR DNG images from HDRMerge. 095 ==> 327628 - Add keyboard shortcut to rename album. 096 ==> 287081 - F2 renames image, not Album in Thumbnails mode. 097 ==> 320295 - Bad Exif orientation interpretation while importing. 098 ==> 314952 - Import window should not lock the interface while reading photos. 099 ==> 314953 - Import window and digiKam window freeze when clicking "Download all". 100 ==> 161988 - Collapse of keyword tree. 101 ==> 318577 - Batch queue fails to process multiple files simultaneously (in parallel). 102 ==> 323299 - Synchronize sort order of icon-view and album tree-view. 103 ==> 128333 - digiKam automatic photographic Labels. 104 ==> 291903 - Minor tweaks to input-field "Enter new tag here...". 105 ==> 115170 - MANAGER : add 'remove all tags' to menu hierarchy. 106 ==> 331695 - MANAGER : misleading menu entry may lead to unintentionall purging of all tags. 107 ==> 337688 - Reading/writing of keyword-tags to jpg and xmp corrupts tag hierarchy, duplicate root tag. 108 ==> 335723 - MANAGER : "Quick Access List" can not be resized. 109 ==> 329975 - Metadata tag is never (re-)read from image. 110 ==> 326935 - Cannot open tiff-files. 111 ==> 335718 - MANAGER : removing a tag turns entries in "Quick Access List" to "All tags". 112 ==> 225723 - Simple sharpen is awfully slow. 113 ==> 290442 - Unsharp Mask with radius less than 1.0 burns images. 114 ==> 127374 - Maximum Radius way too small in Unsharp Mask tool. 115 ==> 331523 - digiKam don't compile with next Lensfun version where CCI correction have been removed. 116 ==> 315025 - 0Byte sized files after using 2 actions in Batch Queue Manager. 117 ==> 314822 - BQM creates empty PNG output from PNG input. 118 ==> 330118 - Downloading back the same image with "download and deleted", import tool says "image already exists". 119 ==> 308836 - Add keyboard shortcuts to toggle display of left and right sidebar. 120 ==> 144651 - Add Keyboard shortcuts to navigate between tabs from left and right sidebar.* 121 ==> 286263 - digiKam automatically expands albums tree-view item while dragging over 122 ==> 307231 - Moving a tag from "my tag" collapse the tag under. 123 ==> 240231 - Add an option to context menu to restore menu visibility when it have been hidden. 124 ==> 201592 - Sorting images in queue from Batch Queue Manager. 125 ==> 265231 - Tooltip do not show whole camera information. 126 ==> 309471 - EXIF orientation is not determined for some JPEG files. 127 ==> 307357 - Metadata from DNG files generated Lightroom 3 and 4 are not displayed in Image Editor right side-bar tab. 128 ==> 324759 - When digiKam searches faces, it sets frames automatically. I can _NOT_ erase the "frames" any more. 129 ==> 193485 - Sort images by Caption, Headline, Comments, etc. 130 ==> 228394 - Sort images by EXIF tag. 131 ==> 124890 - Sort images by camera. 132 ==> 216634 - "Move" as default operation. 133 ==> 284585 - Preserve RSync compatibility when changing Exif orientation and not Modification time. 134 ==> 281574 - Black background for preview in fullscreen [patch]. 135 ==> 313855 - Double click over the preview canvas to leave preview mode. 136 ==> 300647 - F5 in preview mode jumps to top of album instead to refresh canvas. 137 ==> 324028 - Menu bar is missing after closing digiKam in fullscreen mode. 138 ==> 318831 - Double-clicking on video thumbnail has no effect. -139 ==> +139 ==> 275080 - Add keyboard shortcut for "Open With" default application. +140 ==> diff --git a/digikam/items/digikamimageview.cpp b/digikam/items/digikamimageview.cpp index 2241a816b6..b1502d9798 100644 --- a/digikam/items/digikamimageview.cpp +++ b/digikam/items/digikamimageview.cpp @@ -1,697 +1,698 @@ /* ============================================================ * * This file is a part of digiKam project * http://www.digikam.org * * Date : 2009-04-24 * Description : Qt item view for images * * Copyright (C) 2009-2011 by Marcel Wiesweg * Copyright (C) 2009-2014 by Gilles Caulier * Copyright (C) 2011 by Andi Clemens * Copyright (C) 2013 by Michael G. Hansen * Copyright (C) 2014 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 "digikamimageview_p.h" #include "digikamimageview.moc" // Qt includes #include #include #include // KDE includes #include #include #include #include #include #include #include #include #include #include #include #include #include #include // Local includes #include "albummanager.h" #include "albumdb.h" #include "advancedrenamedialog.h" #include "advancedrenameprocessdialog.h" #include "albumsettings.h" #include "assignnameoverlay.h" #include "contextmenuhelper.h" #include "databaseaccess.h" #include "ddragobjects.h" #include "digikamapp.h" #include "digikamimagedelegate.h" #include "digikamimagefacedelegate.h" #include "dio.h" #include "facerejectionoverlay.h" #include "groupindicatoroverlay.h" #include "imagealbumfiltermodel.h" #include "imagealbummodel.h" #include "imagedragdrop.h" #include "imageratingoverlay.h" #include "imagecoordinatesoverlay.h" #include "tagslineeditoverlay.h" #include "imageviewutilities.h" #include "imagewindow.h" #include "fileactionmngr.h" #include "fileactionprogress.h" #include "thumbnailloadthread.h" #include "tagregion.h" #include "addtagslineedit.h" namespace Digikam { DigikamImageView::DigikamImageView(QWidget* const parent) : ImageCategorizedView(parent), d(new Private(this)) { installDefaultModels(); d->editPipeline.plugDatabaseEditor(); d->editPipeline.plugTrainer(); d->editPipeline.construct(); connect(&d->editPipeline, SIGNAL(scheduled()), this, SLOT(slotInitProgressIndicator())); d->normalDelegate = new DigikamImageDelegate(this); d->faceDelegate = new DigikamImageFaceDelegate(this); setItemDelegate(d->normalDelegate); setSpacing(10); AlbumSettings* const settings = AlbumSettings::instance(); imageFilterModel()->setCategorizationMode(ImageSortSettings::CategoryByAlbum); imageAlbumModel()->setThumbnailLoadThread(ThumbnailLoadThread::defaultIconViewThread()); setThumbnailSize((ThumbnailSize::Size)settings->getDefaultIconSize()); imageAlbumModel()->setPreloadThumbnails(true); imageModel()->setDragDropHandler(new ImageDragDropHandler(imageModel())); setDragEnabled(true); setAcceptDrops(true); setDropIndicatorShown(false); setToolTipEnabled(settings->showToolTipsIsValid()); imageFilterModel()->setSortRole((ImageSortSettings::SortRole)settings->getImageSortOrder()); imageFilterModel()->setSortOrder((ImageSortSettings::SortOrder)settings->getImageSorting()); imageFilterModel()->setCategorizationMode((ImageSortSettings::CategorizationMode)settings->getImageGroupMode()); imageFilterModel()->setCategorizationSortOrder((ImageSortSettings::SortOrder) settings->getImageGroupSortOrder()); // selection overlay addSelectionOverlay(d->normalDelegate); addSelectionOverlay(d->faceDelegate); // rotation overlays d->rotateLeftOverlay = ImageRotateOverlay::left(this); d->rotateRightOverlay = ImageRotateOverlay::right(this); d->updateOverlays(); // rating overlay ImageRatingOverlay* const ratingOverlay = new ImageRatingOverlay(this); addOverlay(ratingOverlay); // face overlays // NOTE: order to plug this overlay is important, else rejection can be suitable (see B.K.O #324759). addAssignNameOverlay(d->faceDelegate); addRejectionOverlay(d->faceDelegate); GroupIndicatorOverlay* const groupOverlay = new GroupIndicatorOverlay(this); addOverlay(groupOverlay); addOverlay(new ImageCoordinatesOverlay(this)); connect(ratingOverlay, SIGNAL(ratingEdited(QList,int)), this, SLOT(assignRating(QList,int))); connect(groupOverlay, SIGNAL(toggleGroupOpen(QModelIndex)), this, SLOT(groupIndicatorClicked(QModelIndex))); connect(groupOverlay, SIGNAL(showButtonContextMenu(QModelIndex,QContextMenuEvent*)), this, SLOT(showGroupContextMenu(QModelIndex,QContextMenuEvent*))); d->utilities = new ImageViewUtilities(this); connect(d->utilities, SIGNAL(editorCurrentUrlChanged(KUrl)), this, SLOT(setCurrentUrl(KUrl))); connect(imageModel()->dragDropHandler(), SIGNAL(assignTags(QList,QList)), FileActionMngr::instance(), SLOT(assignTags(QList,QList))); connect(imageModel()->dragDropHandler(), SIGNAL(addToGroup(ImageInfo,QList)), FileActionMngr::instance(), SLOT(addToGroup(ImageInfo,QList))); connect(settings, SIGNAL(setupChanged()), this, SLOT(slotSetupChanged())); slotSetupChanged(); } DigikamImageView::~DigikamImageView() { delete d; } ImageViewUtilities* DigikamImageView::utilities() const { return d->utilities; } void DigikamImageView::setThumbnailSize(const ThumbnailSize& size) { imageThumbnailModel()->setPreloadThumbnailSize(size); ImageCategorizedView::setThumbnailSize(size); } int DigikamImageView::fitToWidthIcons() { return delegate()->calculatethumbSizeToFit(viewport()->size().width()); } void DigikamImageView::slotSetupChanged() { setToolTipEnabled(AlbumSettings::instance()->showToolTipsIsValid()); setFont(AlbumSettings::instance()->getIconViewFont()); d->updateOverlays(); ImageCategorizedView::slotSetupChanged(); } void DigikamImageView::setFaceMode(bool on) { d->faceMode = on; if (on) { // See ImageLister, which creates a search the implements listing tag in the ioslave imageAlbumModel()->setSpecialTagListing("faces"); setItemDelegate(d->faceDelegate); // grouping is not very much compatible with faces imageFilterModel()->setAllGroupsOpen(true); } else { imageAlbumModel()->setSpecialTagListing(QString()); setItemDelegate(d->normalDelegate); imageFilterModel()->setAllGroupsOpen(false); } } void DigikamImageView::addRejectionOverlay(ImageDelegate* delegate) { FaceRejectionOverlay* const rejectionOverlay = new FaceRejectionOverlay(this); connect(rejectionOverlay, SIGNAL(rejectFaces(QList)), this, SLOT(removeFaces(QList))); addOverlay(rejectionOverlay, delegate); } /* void DigikamImageView::addTagEditOverlay(ImageDelegate* delegate) { TagsLineEditOverlay* tagOverlay = new TagsLineEditOverlay(this); connect(tagOverlay, SIGNAL(tagEdited(QModelIndex,QString)), this, SLOT(assignTag(QModelIndex,QString))); addOverlay(tagOverlay, delegate); } */ void DigikamImageView::addAssignNameOverlay(ImageDelegate* delegate) { AssignNameOverlay* const nameOverlay = new AssignNameOverlay(this); addOverlay(nameOverlay, delegate); connect(nameOverlay, SIGNAL(confirmFaces(QList,int)), this, SLOT(confirmFaces(QList,int))); connect(nameOverlay, SIGNAL(removeFaces(QList)), this, SLOT(removeFaces(QList))); } void DigikamImageView::confirmFaces(const QList& indexes, int tagId) { QList infos; QList faces; QList sourceIndexes; // fast-remove in the "unknown person" view bool needFastRemove = false; if(imageAlbumModel()->currentAlbums().size() == 1) { needFastRemove = d->faceMode && (tagId != imageAlbumModel()->currentAlbums().first()->id()); } foreach(const QModelIndex& index, indexes) { infos << ImageModel::retrieveImageInfo(index); faces << d->faceDelegate->face(index); if (needFastRemove) { sourceIndexes << imageSortFilterModel()->mapToSourceImageModel(index); } } imageAlbumModel()->removeIndexes(sourceIndexes); for (int i=0; ieditPipeline.confirm(infos[i], faces[i], tagId); } } void DigikamImageView::removeFaces(const QList& indexes) { QList infos; QList faces; QList sourceIndexes; foreach(const QModelIndex& index, indexes) { infos << ImageModel::retrieveImageInfo(index); faces << d->faceDelegate->face(index); sourceIndexes << imageSortFilterModel()->mapToSourceImageModel(index); } imageAlbumModel()->removeIndexes(sourceIndexes); for (int i=0; ieditPipeline.remove(infos[i], faces[i]); } } void DigikamImageView::activated(const ImageInfo& info) { if (info.isNull()) { return; } if (AlbumSettings::instance()->getItemLeftClickAction() == AlbumSettings::ShowPreview) { emit previewRequested(info); } else { openFile(info); } } void DigikamImageView::showContextMenuOnInfo(QContextMenuEvent* event, const ImageInfo& info) { QList selectedInfos = selectedImageInfosCurrentFirst(); QList selectedImageIDs; foreach(const ImageInfo& info, selectedInfos) { selectedImageIDs << info.id(); } // Temporary actions -------------------------------------- QAction* const viewAction = new QAction(SmallIcon("viewimage"), i18nc("View the selected image", "Preview"), this); viewAction->setEnabled(selectedImageIDs.count() == 1); // -------------------------------------------------------- KMenu popmenu(this); ContextMenuHelper cmhelper(&popmenu); cmhelper.setImageFilterModel(imageFilterModel()); cmhelper.addAction("full_screen"); cmhelper.addAction("options_show_menubar"); cmhelper.addSeparator(); // -------------------------------------------------------- cmhelper.addAction("move_selection_to_album"); cmhelper.addAction(viewAction); cmhelper.addAction("image_edit"); + cmhelper.addAction("open_with_default_application"); cmhelper.addServicesMenu(selectedUrls()); cmhelper.addGotoMenu(selectedImageIDs); cmhelper.addAction("image_rotate"); cmhelper.addSeparator(); // -------------------------------------------------------- cmhelper.addAction("image_find_similar"); cmhelper.addStandardActionLightTable(); cmhelper.addQueueManagerMenu(); cmhelper.addSeparator(); // -------------------------------------------------------- cmhelper.addAction("image_rename"); cmhelper.addAction("cut_album_selection"); cmhelper.addAction("copy_album_selection"); cmhelper.addAction("paste_album_selection"); cmhelper.addStandardActionItemDelete(this, SLOT(deleteSelected()), selectedImageIDs.count()); cmhelper.addSeparator(); // -------------------------------------------------------- cmhelper.addStandardActionThumbnail(selectedImageIDs, currentAlbum()); // -------------------------------------------------------- cmhelper.addAssignTagsMenu(selectedImageIDs); cmhelper.addRemoveTagsMenu(selectedImageIDs); cmhelper.addSeparator(); // -------------------------------------------------------- cmhelper.addLabelsAction(); if (!d->faceMode) { cmhelper.addGroupMenu(selectedImageIDs); } // special action handling -------------------------------- connect(&cmhelper, SIGNAL(signalAssignTag(int)), this, SLOT(assignTagToSelected(int))); connect(&cmhelper, SIGNAL(signalPopupTagsView()), this, SIGNAL(signalPopupTagsView())); connect(&cmhelper, SIGNAL(signalRemoveTag(int)), this, SLOT(removeTagFromSelected(int))); connect(&cmhelper, SIGNAL(signalGotoTag(int)), this, SIGNAL(gotoTagAndImageRequested(int))); connect(&cmhelper, SIGNAL(signalGotoAlbum(ImageInfo)), this, SIGNAL(gotoAlbumAndImageRequested(ImageInfo))); connect(&cmhelper, SIGNAL(signalGotoDate(ImageInfo)), this, SIGNAL(gotoDateAndImageRequested(ImageInfo))); connect(&cmhelper, SIGNAL(signalAssignPickLabel(int)), this, SLOT(assignPickLabelToSelected(int))); connect(&cmhelper, SIGNAL(signalAssignColorLabel(int)), this, SLOT(assignColorLabelToSelected(int))); connect(&cmhelper, SIGNAL(signalAssignRating(int)), this, SLOT(assignRatingToSelected(int))); connect(&cmhelper, SIGNAL(signalSetThumbnail(ImageInfo)), this, SLOT(setAsAlbumThumbnail(ImageInfo))); connect(&cmhelper, SIGNAL(signalAddToExistingQueue(int)), this, SLOT(insertSelectedToExistingQueue(int))); connect(&cmhelper, SIGNAL(signalCreateGroup()), this, SLOT(createGroupFromSelection())); connect(&cmhelper, SIGNAL(signalCreateGroupByTime()), this, SLOT(createGroupByTimeFromSelection())); connect(&cmhelper, SIGNAL(signalUngroup()), this, SLOT(ungroupSelected())); connect(&cmhelper, SIGNAL(signalRemoveFromGroup()), this, SLOT(removeSelectedFromGroup())); // -------------------------------------------------------- QAction* const choice = cmhelper.exec(event->globalPos()); if (choice && (choice == viewAction)) { emit previewRequested(info); } } void DigikamImageView::showGroupContextMenu(const QModelIndex& index, QContextMenuEvent* event) { Q_UNUSED(index); QList selectedInfos = selectedImageInfosCurrentFirst(); QList selectedImageIDs; foreach(const ImageInfo& info, selectedInfos) { selectedImageIDs << info.id(); } KMenu popmenu(this); ContextMenuHelper cmhelper(&popmenu); cmhelper.setImageFilterModel(imageFilterModel()); cmhelper.addGroupActions(selectedImageIDs); // special action handling -------------------------------- connect(&cmhelper, SIGNAL(signalCreateGroup()), this, SLOT(createGroupFromSelection())); connect(&cmhelper, SIGNAL(signalCreateGroupByTime()), this, SLOT(createGroupByTimeFromSelection())); connect(&cmhelper, SIGNAL(signalUngroup()), this, SLOT(ungroupSelected())); connect(&cmhelper, SIGNAL(signalRemoveFromGroup()), this, SLOT(removeSelectedFromGroup())); cmhelper.exec(event->globalPos()); } void DigikamImageView::showContextMenu(QContextMenuEvent* event) { Album* const album = currentAlbum(); if (!album || album->isRoot() || (album->type() != Album::PHYSICAL && album->type() != Album::TAG) ) { return; } KMenu popmenu(this); ContextMenuHelper cmhelper(&popmenu); cmhelper.setImageFilterModel(imageFilterModel()); cmhelper.addAction("full_screen"); cmhelper.addAction("options_show_menubar"); cmhelper.addSeparator(); // -------------------------------------------------------- cmhelper.addStandardActionPaste(this, SLOT(paste())); // -------------------------------------------------------- cmhelper.exec(event->globalPos()); } void DigikamImageView::openFile(const ImageInfo& info) { d->utilities->openFile(info, imageInfos(), currentAlbum()); } void DigikamImageView::insertSelectedToCurrentQueue() { ImageInfoList imageInfoList = selectedImageInfos(); if (!imageInfoList.isEmpty()) { d->utilities->insertToQueueManager(imageInfoList, imageInfoList.first(), false); } } void DigikamImageView::insertSelectedToNewQueue() { ImageInfoList imageInfoList = selectedImageInfos(); if (!imageInfoList.isEmpty()) { d->utilities->insertToQueueManager(imageInfoList, imageInfoList.first(), true); } } void DigikamImageView::insertSelectedToExistingQueue(int queueid) { ImageInfoList imageInfoList = selectedImageInfos(); if (!imageInfoList.isEmpty()) { d->utilities->insertSilentToQueueManager(imageInfoList, imageInfoList.first(), queueid); } } void DigikamImageView::deleteSelected(const ImageViewUtilities::DeleteMode deleteMode) { ImageInfoList imageInfoList = selectedImageInfos(); if (d->utilities->deleteImages(imageInfoList, deleteMode)) { awayFromSelection(); } } void DigikamImageView::deleteSelectedDirectly(const ImageViewUtilities::DeleteMode deleteMode) { ImageInfoList imageInfoList = selectedImageInfos(); d->utilities->deleteImagesDirectly(imageInfoList, deleteMode); awayFromSelection(); } void DigikamImageView::assignTagToSelected(int tagID) { FileActionMngr::instance()->assignTags(selectedImageInfos(), QList() << tagID); } void DigikamImageView::removeTagFromSelected(int tagID) { FileActionMngr::instance()->removeTags(selectedImageInfos(), QList() << tagID); } void DigikamImageView::assignPickLabelToSelected(int pickId) { FileActionMngr::instance()->assignPickLabel(selectedImageInfos(), pickId); } void DigikamImageView::assignPickLabel(const QModelIndex& index, int pickId) { FileActionMngr::instance()->assignPickLabel(QList() << imageFilterModel()->imageInfo(index), pickId); } void DigikamImageView::assignColorLabelToSelected(int colorId) { FileActionMngr::instance()->assignColorLabel(selectedImageInfos(), colorId); } void DigikamImageView::assignColorLabel(const QModelIndex& index, int colorId) { FileActionMngr::instance()->assignColorLabel(QList() << imageFilterModel()->imageInfo(index), colorId); } void DigikamImageView::assignRatingToSelected(int rating) { FileActionMngr::instance()->assignRating(selectedImageInfos(), rating); } void DigikamImageView::assignRating(const QList& indexes, int rating) { FileActionMngr::instance()->assignRating(imageFilterModel()->imageInfos(indexes), rating); } void DigikamImageView::setAsAlbumThumbnail(const ImageInfo& setAsThumbnail) { d->utilities->setAsAlbumThumbnail(currentAlbum(), setAsThumbnail); } void DigikamImageView::createNewAlbumForSelected() { d->utilities->createNewAlbumForInfos(selectedImageInfos(), currentAlbum()); } void DigikamImageView::groupIndicatorClicked(const QModelIndex& index) { ImageInfo info = imageFilterModel()->imageInfo(index); if (info.isNull()) { return; } setCurrentIndex(index); imageFilterModel()->toggleGroupOpen(info.id()); imageAlbumModel()->ensureHasGroupedImages(info); } void DigikamImageView::createGroupFromSelection() { QList selectedInfos = selectedImageInfosCurrentFirst(); ImageInfo groupLeader = selectedInfos.takeFirst(); FileActionMngr::instance()->addToGroup(groupLeader, selectedInfos); } void DigikamImageView::createGroupByTimeFromSelection() { const QList selectedInfos = selectedImageInfos(); d->utilities->createGroupByTimeFromInfoList(selectedInfos); } void DigikamImageView::ungroupSelected() { FileActionMngr::instance()->ungroup(selectedImageInfos()); } void DigikamImageView::removeSelectedFromGroup() { FileActionMngr::instance()->removeFromGroup(selectedImageInfos()); } void DigikamImageView::rename() { KUrl::List urls = selectedUrls(); NewNamesList newNamesList; QPointer dlg = new AdvancedRenameDialog(this); dlg->slotAddImages(urls); if (dlg->exec() == KDialog::Accepted) { newNamesList = dlg->newNames(); } delete dlg; if (!newNamesList.isEmpty()) { QPointer dlg = new AdvancedRenameProcessDialog(newNamesList); dlg->exec(); delete dlg; } } void DigikamImageView::slotRotateLeft(const QList& indexes) { FileActionMngr::instance()->transform(QList() << imageFilterModel()->imageInfos(indexes), KExiv2Iface::RotationMatrix::Rotate270); } void DigikamImageView::slotRotateRight(const QList& indexes) { FileActionMngr::instance()->transform(QList() << imageFilterModel()->imageInfos(indexes), KExiv2Iface::RotationMatrix::Rotate90); } void DigikamImageView::slotInitProgressIndicator() { if (!ProgressManager::instance()->findItembyId("FaceActionProgress")) { FileActionProgress* const item = new FileActionProgress("FaceActionProgress"); connect(&d->editPipeline, SIGNAL(started(QString)), item, SLOT(slotProgressStatus(QString))); connect(&d->editPipeline, SIGNAL(progressValueChanged(float)), item, SLOT(slotProgressValue(float))); connect(&d->editPipeline, SIGNAL(finished()), item, SLOT(slotCompleted())); } } } // namespace Digikam diff --git a/digikam/items/imageviewutilities.cpp b/digikam/items/imageviewutilities.cpp index 2ab471a861..fc51afaec0 100644 --- a/digikam/items/imageviewutilities.cpp +++ b/digikam/items/imageviewutilities.cpp @@ -1,361 +1,375 @@ /* ============================================================ * * This file is a part of digiKam project * http://www.digikam.org * * Date : 2009-05-04 * Description : Various operations on images * * Copyright (C) 2002-2005 by Renchi Raju * Copyright (C) 2002-2014 by Gilles Caulier * Copyright (C) 2006-2010 by Marcel Wiesweg * Copyright (C) 2009-2010 by Andi Clemens * * 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 "imageviewutilities.moc" // Qt includes #include // KDE includes #include #include #include #include #include #include #include #include #include // Local includes #include "album.h" #include "albummanager.h" #include "albumselectdialog.h" #include "albumsettings.h" #include "deletedialog.h" #include "dio.h" #include "imageinfo.h" #include "imagewindow.h" #include "lighttablewindow.h" #include "loadingcacheinterface.h" #include "queuemgrwindow.h" #include "thumbnailloadthread.h" #include "fileactionmngr.h" namespace Digikam { ImageViewUtilities::ImageViewUtilities(QWidget* const parentWidget) : QObject(parentWidget) { m_widget = parentWidget; } void ImageViewUtilities::setAsAlbumThumbnail(Album* album, const ImageInfo& imageInfo) { if (!album) { return; } if (album->type() == Album::PHYSICAL) { PAlbum* const palbum = static_cast(album); QString err; AlbumManager::instance()->updatePAlbumIcon(palbum, imageInfo.id(), err); } else if (album->type() == Album::TAG) { TAlbum* const talbum = static_cast(album); QString err; AlbumManager::instance()->updateTAlbumIcon(talbum, QString(), imageInfo.id(), err); } } void ImageViewUtilities::rename(const KUrl& imageUrl, const QString& newName) { if (imageUrl.isEmpty() || !imageUrl.isLocalFile() || newName.isEmpty()) { return; } ImageInfo info(imageUrl.toLocalFile()); DIO::rename(info, newName); } bool ImageViewUtilities::deleteImages(const QList& infos, const DeleteMode deleteMode) { if (infos.isEmpty()) { return false; } KUrl::List urlList; foreach(const ImageInfo& info, infos) { urlList << info.fileUrl(); } DeleteDialog dialog(m_widget); DeleteDialogMode::DeleteMode deleteDialogMode = DeleteDialogMode::NoChoiceTrash; if (deleteMode == ImageViewUtilities::DeletePermanently) { deleteDialogMode = DeleteDialogMode::NoChoiceDeletePermanently; } if (!dialog.confirmDeleteList(urlList, DeleteDialogMode::Files, deleteDialogMode)) { return false; } const bool useTrash = !dialog.shouldDelete(); DIO::del(infos, useTrash); return true; } void ImageViewUtilities::deleteImagesDirectly(const QList& infos, const DeleteMode deleteMode) { // This method deletes the selected items directly, without confirmation. // It is not used in the default setup. if (infos.isEmpty()) { return; } const bool useTrash = (deleteMode==ImageViewUtilities::DeleteUseTrash); DIO::del(infos, useTrash); } void ImageViewUtilities::notifyFileContentChanged(const KUrl::List& urls) { foreach(const KUrl& url, urls) { QString path = url.toLocalFile(); ThumbnailLoadThread::deleteThumbnail(path); // clean LoadingCache as well - be pragmatic, do it here. LoadingCacheInterface::fileChanged(path); } } void ImageViewUtilities::createNewAlbumForInfos(const QList& infos, Album* currentAlbum) { if (infos.isEmpty()) { return; } if (currentAlbum && currentAlbum->type() != Album::PHYSICAL) { currentAlbum = 0; } QString header(i18n("

Please select the destination album from the digiKam library to " "move the selected images into.

")); Album* const album = AlbumSelectDialog::selectAlbum(m_widget, static_cast(currentAlbum), header); if (!album) { return; } DIO::move(infos, (PAlbum*)album); } void ImageViewUtilities::insertToLightTableAuto(const QList& all, const QList& selected, const ImageInfo& current) { ImageInfoList list = selected; ImageInfo singleInfo = current; if (list.isEmpty() || (list.size() == 1 && LightTableWindow::lightTableWindow()->isEmpty())) { list = all; } if (singleInfo.isNull() && !list.isEmpty()) { singleInfo = list.first(); } insertToLightTable(list, current, list.size() <= 1); } void ImageViewUtilities::insertToLightTable(const QList& list, const ImageInfo& current, bool addTo) { LightTableWindow* const ltview = LightTableWindow::lightTableWindow(); // If addTo is false, the light table will be emptied before adding // the images. ltview->loadImageInfos(list, current, addTo); ltview->setLeftRightItems(list, addTo); if (ltview->isHidden()) { ltview->show(); } if (ltview->isMinimized()) { KWindowSystem::unminimizeWindow(ltview->winId()); } KWindowSystem::activateWindow(ltview->winId()); } void ImageViewUtilities::insertToQueueManager(const QList& list, const ImageInfo& current, bool newQueue) { Q_UNUSED(current); QueueMgrWindow* const bqmview = QueueMgrWindow::queueManagerWindow(); if (bqmview->isHidden()) { bqmview->show(); } if (bqmview->isMinimized()) { KWindowSystem::unminimizeWindow(bqmview->winId()); } KWindowSystem::activateWindow(bqmview->winId()); if (newQueue) { bqmview->loadImageInfosToNewQueue(list); } else { bqmview->loadImageInfosToCurrentQueue(list); } } void ImageViewUtilities::insertSilentToQueueManager(const QList& list, const ImageInfo& /*current*/, int queueid) { QueueMgrWindow* const bqmview = QueueMgrWindow::queueManagerWindow(); bqmview->loadImageInfos(list, queueid); } void ImageViewUtilities::openFile(const ImageInfo& info, const QList& allInfosToOpen, Album* currentAlbum) { if (info.isNull()) { return; } QFileInfo fi(info.filePath()); QString imagefilter = AlbumSettings::instance()->getImageFileFilter(); imagefilter += AlbumSettings::instance()->getRawFileFilter(); // If the current item is not an image file. if ( !imagefilter.contains(fi.suffix().toLower()) ) { - const QString mimeType = KMimeType::findByUrl(info.fileUrl(), 0, true, true)->name(); - KService::List offers = KMimeTypeTrader::self()->query(mimeType, "Application"); - - if (offers.isEmpty()) - { - return; - } - - KService::Ptr ptr = offers.first(); - // Run the dedicated app to show the item. - KRun::run(*ptr, info.fileUrl(), m_widget); + // Openonly the first one from the list. + openFilesWithDefaultApplication(QList() << info); return; } // Run digiKam ImageEditor with all image from current Album. ImageWindow* const imview = ImageWindow::imageWindow(); imview->disconnect(this); connect(imview, SIGNAL(signalURLChanged(KUrl)), this, SIGNAL(editorCurrentUrlChanged(KUrl))); imview->loadImageInfos(allInfosToOpen, info, currentAlbum ? i18n("Album \"%1\"", currentAlbum->title()) : QString()); if (imview->isHidden()) { imview->show(); } if (imview->isMinimized()) { KWindowSystem::unminimizeWindow(imview->winId()); } KWindowSystem::activateWindow(imview->winId()); } +void ImageViewUtilities::openFilesWithDefaultApplication(const QList& infos) +{ + if (infos.isEmpty()) + { + return; + } + + foreach (ImageInfo inf, infos) + { + const QString mimeType = KMimeType::findByUrl(inf.fileUrl(), 0, true, true)->name(); + KService::List offers = KMimeTypeTrader::self()->query(mimeType, "Application"); + + if (offers.isEmpty()) + { + return; + } + + KService::Ptr ptr = offers.first(); + // Run the dedicated app to open the item. + KRun::run(*ptr, inf.fileUrl(), m_widget); + } +} + namespace { bool lessThanByTimeForImageInfo(const ImageInfo& a, const ImageInfo& b) { return a.dateTime() < b.dateTime(); } } // namespace void ImageViewUtilities::createGroupByTimeFromInfoList(const ImageInfoList& imageInfoList) { QList groupingList = imageInfoList; // sort by time qStableSort(groupingList.begin(), groupingList.end(), lessThanByTimeForImageInfo); QList::iterator it, it2; for (it = groupingList.begin(); it != groupingList.end(); ) { const ImageInfo& leader = *it; QList group; QDateTime time = it->dateTime(); for (it2 = it + 1; it2 != groupingList.end(); ++it2) { if (abs(time.secsTo(it2->dateTime())) < 2) { group << *it2; } else { break; } } // increment to next item not put in the group it = it2; if (!group.isEmpty()) { FileActionMngr::instance()->addToGroup(leader, group); } } } } // namespace Digikam diff --git a/digikam/items/imageviewutilities.h b/digikam/items/imageviewutilities.h index ff6a0c5442..d8812e5d02 100644 --- a/digikam/items/imageviewutilities.h +++ b/digikam/items/imageviewutilities.h @@ -1,99 +1,101 @@ /* ============================================================ * * This file is a part of digiKam project * http://www.digikam.org * * Date : 2009-05-04 * Description : Various operations on images * * Copyright (C) 2002-2014 by Gilles Caulier * Copyright (C) 2009-2010 by Marcel Wiesweg * * This program is free software; you can redistribute it * and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation; * either version 2, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * ============================================================ */ #ifndef IMAGEVIEWUTILITIES_H #define IMAGEVIEWUTILITIES_H // Qt includes #include #include // KDE includes #include // Local includes #include "imageinfo.h" class KJob; namespace KIO { class Job; } namespace Digikam { class Album; class ImageViewUtilities : public QObject { Q_OBJECT public: enum DeleteMode { DeletePermanently = 1, DeleteUseTrash = 2 }; explicit ImageViewUtilities(QWidget* const parentWidget); public Q_SLOTS: void createNewAlbumForInfos(const QList& infos, Album* currentAlbum); bool deleteImages(const QList& infos, const DeleteMode deleteMode); void deleteImagesDirectly(const QList& infos, const DeleteMode deleteMode); void insertToLightTableAuto(const QList& all, const QList& selected, const ImageInfo& current); void insertToLightTable(const QList& list, const ImageInfo& current, bool addTo); void insertToQueueManager(const QList& list, const ImageInfo& currentInfo, bool newQueue); void insertSilentToQueueManager(const QList& list, const ImageInfo& currentInfo, int queueid); void notifyFileContentChanged(const KUrl::List& urls); void openFile(const ImageInfo& info, const QList& allInfosToOpen, Album* currentAlbum); + void openFilesWithDefaultApplication(const QList& allInfosToOpen); + void rename(const KUrl& imageUrl, const QString& newName); void setAsAlbumThumbnail(Album* album, const ImageInfo& imageInfo); void createGroupByTimeFromInfoList(const ImageInfoList& imageInfoList); Q_SIGNALS: void editorCurrentUrlChanged(const KUrl& url); protected: QWidget* m_widget; }; } // namespace Digikam Q_DECLARE_METATYPE(Digikam::ImageViewUtilities::DeleteMode) #endif /* IMAGEVIEWUTILITIES_H */ diff --git a/digikam/main/digikamapp.cpp b/digikam/main/digikamapp.cpp index 3a9feb115a..7ae9f96167 100644 --- a/digikam/main/digikamapp.cpp +++ b/digikam/main/digikamapp.cpp @@ -1,3142 +1,3142 @@ /* ============================================================ * * This file is a part of digiKam project * http://www.digikam.org * * Date : 2002-16-10 * Description : main digiKam interface implementation * * Copyright (C) 2002-2005 by Renchi Raju * Copyright (C) 2006 by Tom Albers * Copyright (C) 2002-2014 by Gilles Caulier * Copyright (C) 2009-2012 by Andi Clemens * Copyright (C) 2013 by Michael G. Hansen * Copyright (C) 2014 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 "digikamapp.moc" /// @todo Order should be changed according to krazy2, but compilation fails. Try again later. MH #include "digikamapp_p.h" // Qt includes #include #include #include #include #include #include #include #include #include // KDE includes #include #if KDE_IS_VERSION(4,1,68) #include #endif #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 #include #include #include #include #include #include // Libkdcraw includes #include #include // Libkexiv2 #include // Local includes #include "album.h" #include "albumdb.h" #include "albumselectdialog.h" #include "albumthumbnailloader.h" #include "cameratype.h" #include "importui.h" #include "cameranamehelper.h" #include "collectionscanner.h" #include "componentsinfo.h" #include "databasethumbnailinfoprovider.h" #include "digikamadaptor.h" #include "dio.h" #include "dlogoaction.h" #include "fileactionmngr.h" #include "filterstatusbar.h" #include "iccsettings.h" #include "imageattributeswatch.h" #include "imageinfo.h" #include "imagewindow.h" #include "lighttablewindow.h" #include "queuemgrwindow.h" #include "loadingcache.h" #include "loadingcacheinterface.h" #include "loadsavethread.h" #include "scancontroller.h" #include "setup.h" #include "setupeditor.h" #include "setupicc.h" #include "thememanager.h" #include "thumbnailloadthread.h" #include "thumbnailsize.h" #include "dmetadata.h" #include "uifilevalidator.h" #include "tagscache.h" #include "tagsactionmngr.h" #include "databaseserverstarter.h" #include "metadatasettings.h" #include "statusbarprogresswidget.h" #include "migrationdlg.h" #include "progressmanager.h" #include "progressview.h" #include "maintenancedlg.h" #include "maintenancemngr.h" #include "newitemsfinder.h" #include "kipipluginloader.h" #include "imagepluginloader.h" #include "tagsmanager.h" #ifdef HAVE_BALOO #include "baloowrap.h" #endif #ifdef USE_SCRIPT_IFACE #include "scriptiface.h" #endif using KIO::Job; using KIO::UDSEntryList; using KIO::UDSEntry; namespace Digikam { DigikamApp* DigikamApp::m_instance = 0; DigikamApp::DigikamApp() : DXmlGuiWindow(0), d(new Private) { // -------------------------------------------------------- setFullScreenOptions(FS_ALBUMGUI); setXMLFile("digikamui.rc"); // -------------------------------------------------------- // To adept the global KDE toolbar style, the toolbar needs to be named "mainToolBar". // digiKam used to name the toolbars "ToolBar", which makes it not behave like other KDE applications do. // A simple rename in the *ui.rc files does not prevent users from the "faulty" behavior if they have a custom // *ui.rc file in their home directories. // In this case, we need to parse the ui files and alter the name on startup. See BKO: 210823 UiFileValidator validator(localXMLFile()); if (!validator.isValid()) { validator.fixConfigFile(); } // -------------------------------------------------------- m_instance = this; d->config = KGlobal::config(); setObjectName("Digikam"); KConfigGroup group = AlbumSettings::instance()->generalConfigGroup(); if (group.readEntry("Show Splash", true) && !kapp->isSessionRestored()) { d->splashScreen = new SplashScreen(); d->splashScreen->show(); } if (d->splashScreen) { d->splashScreen->message(i18n("Scanning Albums...")); } new DigikamAdaptor(this); QDBusConnection::sessionBus().registerObject("/Digikam", this); QDBusConnection::sessionBus().registerService("org.kde.digikam-" + QString::number(QCoreApplication::instance()->applicationPid())); // collection scan if (group.readEntry("Scan At Start", true) || !CollectionScanner::databaseInitialScanDone()) { ScanController::instance()->completeCollectionScanDeferFiles(d->splashScreen); } if (d->splashScreen) { d->splashScreen->message(i18n("Initializing...")); } // ensure creation AlbumManager::instance(); LoadingCacheInterface::initialize(); IccSettings::instance()->loadAllProfilesProperties(); MetadataSettings::instance(); ProgressManager::instance(); ThumbnailLoadThread::setDisplayingWidget(this); DIO::instance(); // creation of the engine on first use - when drawing - // can take considerable time and cause a noticeable hang in the UI thread. QFontMetrics fm(font()); fm.width("a"); connect(AlbumSettings::instance(), SIGNAL(setupChanged()), this, SLOT(slotSetupChanged())); d->cameraMenu = new KActionMenu(this); d->usbMediaMenu = new KActionMenu(this); d->cardReaderMenu = new KActionMenu(this); d->quickImportMenu = new KActionMenu(this); d->cameraList = new CameraList(this, KStandardDirs::locateLocal("appdata", "cameras.xml")); connect(d->cameraList, SIGNAL(signalCameraAdded(CameraType*)), this, SLOT(slotCameraAdded(CameraType*))); connect(d->cameraList, SIGNAL(signalCameraRemoved(KAction*)), this, SLOT(slotCameraRemoved(KAction*))); d->modelCollection = new DigikamModelCollection; // This manager must be created after collection setup and before accelerators setup. d->tagsActionManager = new TagsActionMngr(this); // First create everything, then connect. // Otherwise some items may send signals and the slots can try // to access items which were not created yet. setupView(); setupAccelerators(); setupActions(); setupStatusBar(); initGui(); setupViewConnections(); applyMainWindowSettings(group); // Check ICC profiles repository availability if (d->splashScreen) { d->splashScreen->message(i18n("Checking ICC repository...")); } d->validIccPath = SetupICC::iccRepositoryIsValid(); // Read albums from database if (d->splashScreen) { d->splashScreen->message(i18n("Reading database...")); } AlbumManager::instance()->startScan(); // Load Plugins. loadPlugins(); // preload additional windows preloadWindows(); readFullScreenSettings(group); #ifdef HAVE_BALOO //Create BalooWrap object, because it need to register a listener // to update digiKam data when changes in Baloo occur BalooWrap* const baloo = BalooWrap::instance(); Q_UNUSED(baloo); #endif //HAVE_BALOO setAutoSaveSettings(group, true); // Now, enable finished the collection scan as deferred process NewItemsFinder* const tool = new NewItemsFinder(NewItemsFinder::ScanDeferredFiles); tool->start(); LoadSaveThread::setInfoProvider(new DatabaseLoadSaveFileInfoProvider); } DigikamApp::~DigikamApp() { ProgressManager::instance()->slotAbortAll(); ImageAttributesWatch::shutDown(); // Close and delete image editor instance. if (ImageWindow::imageWindowCreated()) { // Delete after close ImageWindow::imageWindow()->setAttribute(Qt::WA_DeleteOnClose, true); // pass ownership of object - needed by ImageWindow destructor ImagePluginLoader::instance()->setParent(ImageWindow::imageWindow()); // close the window ImageWindow::imageWindow()->close(); } // Close and delete light table instance. if (LightTableWindow::lightTableWindowCreated()) { LightTableWindow::lightTableWindow()->setAttribute(Qt::WA_DeleteOnClose, true); LightTableWindow::lightTableWindow()->close(); } // Close and delete Batch Queue Manager instance. if (QueueMgrWindow::queueManagerWindowCreated()) { QueueMgrWindow::queueManagerWindow()->setAttribute(Qt::WA_DeleteOnClose, true); QueueMgrWindow::queueManagerWindow()->close(); } if(TagsManager::isCreated()) { TagsManager::instance()->close(); } #ifdef HAVE_BALOO if(BalooWrap::isCreated()) { delete BalooWrap::instance(); } #endif delete d->view; AlbumSettings::instance()->setRecurseAlbums(d->recurseAlbumsAction->isChecked()); AlbumSettings::instance()->setRecurseTags(d->recurseTagsAction->isChecked()); AlbumSettings::instance()->setShowThumbbar(d->showBarAction->isChecked()); AlbumSettings::instance()->saveSettings(); ScanController::instance()->shutDown(); AlbumManager::instance()->cleanUp(); ImageAttributesWatch::cleanUp(); ThumbnailLoadThread::cleanUp(); AlbumThumbnailLoader::instance()->cleanUp(); LoadingCacheInterface::cleanUp(); DIO::cleanUp(); // close database server if (AlbumSettings::instance()->getInternalDatabaseServer()) { DatabaseServerStarter::cleanUp(); } m_instance = 0; delete d->modelCollection; delete d; } DigikamApp* DigikamApp::instance() { return m_instance; } DigikamView* DigikamApp::view() const { return d->view; } void DigikamApp::show() { // Remove Splashscreen. if (d->splashScreen) { d->splashScreen->finish(this); delete d->splashScreen; d->splashScreen = 0; } // Display application window. KMainWindow::show(); // Report errors from ICC repository path. if (!d->validIccPath) { QString message = i18n("

The ICC profiles folder seems to be invalid.

" "

If you want to try setting it again, choose \"Yes\" here, otherwise " "choose \"No\", and the \"Color Management\" feature " "will be disabled until you solve this issue.

"); if (KMessageBox::warningYesNo(this, message) == KMessageBox::Yes) { if (!setupICC()) { d->config->group("Color Management").writeEntry("EnableCM", false); d->config->sync(); } } else { d->config->group("Color Management").writeEntry("EnableCM", false); d->config->sync(); } } // Init album icon view zoom factor. slotThumbSizeChanged(AlbumSettings::instance()->getDefaultIconSize()); slotZoomSliderChanged(AlbumSettings::instance()->getDefaultIconSize()); d->autoShowZoomToolTip = true; } void DigikamApp::restoreSession() { //TODO: show and restore ImageEditor, Lighttable, and Batch Queue Manager main windows if (qApp->isSessionRestored()) { int n = 1; while (KMainWindow::canBeRestored(n)) { const QString className = KMainWindow::classNameOfToplevel(n); if (className == QLatin1String(Digikam::DigikamApp::staticMetaObject.className())) { restore(n, false); break; } ++n; } } } void DigikamApp::closeEvent(QCloseEvent* e) { // may show a progress dialog to finish actions FileActionMngr::instance()->requestShutDown(); DXmlGuiWindow::closeEvent(e); e->accept(); } void DigikamApp::autoDetect() { // Called from main if command line option is set, or via DBus if (d->splashScreen) { d->splashScreen->message(i18n("Auto-Detecting Camera...")); } QTimer::singleShot(0, this, SLOT(slotCameraAutoDetect())); } void DigikamApp::downloadFrom(const QString& cameraGuiPath) { // Called from main if command line option is set, or via DBus if (!cameraGuiPath.isEmpty()) { if (d->splashScreen) { d->splashScreen->message(i18n("Opening Download Dialog...")); } emit queuedOpenCameraUiFromPath(cameraGuiPath); } } void DigikamApp::downloadFromUdi(const QString& udi) { // Called from main if command line option is set, or via DBus if (!udi.isEmpty()) { if (d->splashScreen) { d->splashScreen->message(i18n("Opening Download Dialog...")); } emit queuedOpenSolidDevice(udi); } } QString DigikamApp::currentDatabaseParameters() const { DatabaseParameters parameters = DatabaseAccess::parameters(); KUrl url; parameters.insertInUrl(url); return url.url(); } bool DigikamApp::queryClose() { bool ret = true; if (ImageWindow::imageWindowCreated()) { ret &= ImageWindow::imageWindow()->queryClose(); } if (QueueMgrWindow::queueManagerWindowCreated()) { ret &= QueueMgrWindow::queueManagerWindow()->queryClose(); } return ret; } void DigikamApp::setupView() { if (d->splashScreen) { d->splashScreen->message(i18n("Initializing Main View...")); } d->view = new DigikamView(this, d->modelCollection); setCentralWidget(d->view); d->view->applySettings(); } void DigikamApp::setupViewConnections() { connect(d->view, SIGNAL(signalAlbumSelected(bool)), this, SLOT(slotAlbumSelected(bool))); connect(d->view, SIGNAL(signalTagSelected(bool)), this, SLOT(slotTagSelected(bool))); connect(d->view, SIGNAL(signalSelectionChanged(int)), this, SLOT(slotSelectionChanged(int))); connect(d->view, SIGNAL(signalImageSelected(ImageInfoList,ImageInfoList)), this, SLOT(slotImageSelected(ImageInfoList,ImageInfoList))); 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())); } void DigikamApp::setupStatusBar() { d->statusLabel = new KSqueezedTextLabel(statusBar()); d->statusLabel->setAlignment(Qt::AlignLeft|Qt::AlignVCenter); statusBar()->addWidget(d->statusLabel, 100); //------------------------------------------------------------------------------ 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::setupAccelerators() { // Action are added by tag in ui.rc XML file KAction* const escapeAction = new KAction(i18n("Exit Preview Mode"), this); actionCollection()->addAction("exit_preview_mode", escapeAction); escapeAction->setShortcut( KShortcut(Qt::Key_Escape) ); connect(escapeAction, SIGNAL(triggered()), this, SIGNAL(signalEscapePressed())); KAction* const nextImageAction = new KAction(i18n("Next Image"), this); nextImageAction->setIcon(SmallIcon("go-next")); actionCollection()->addAction("next_image", nextImageAction); nextImageAction->setShortcut(KShortcut(Qt::Key_Space)); connect(nextImageAction, SIGNAL(triggered()), this, SIGNAL(signalNextItem())); KAction* const previousImageAction = new KAction(i18n("Previous Image"), this); previousImageAction->setIcon(SmallIcon("go-previous")); actionCollection()->addAction("previous_image", previousImageAction); previousImageAction->setShortcut(KShortcut(Qt::Key_Backspace)); connect(previousImageAction, SIGNAL(triggered()), this, SIGNAL(signalPrevItem())); KAction* const altpreviousImageAction = new KAction(i18n("Previous Image"), this); actionCollection()->addAction("alt_previous_image_shift_space", altpreviousImageAction); altpreviousImageAction->setShortcut( KShortcut(Qt::SHIFT+Qt::Key_Space) ); connect(altpreviousImageAction, SIGNAL(triggered()), this, SIGNAL(signalPrevItem())); KAction* const firstImageAction = new KAction(i18n("First Image"), this); actionCollection()->addAction("first_image", firstImageAction); firstImageAction->setShortcut(KShortcut(Qt::Key_Home) ); connect(firstImageAction, SIGNAL(triggered()), this, SIGNAL(signalFirstItem())); KAction* const lastImageAction = new KAction(i18n("Last Image"), this); actionCollection()->addAction("last_image", lastImageAction); lastImageAction->setShortcut(KShortcut(Qt::Key_End) ); connect(lastImageAction, SIGNAL(triggered()), this, SIGNAL(signalLastItem())); d->cutItemsAction = KStandardAction::cut(this, SIGNAL(signalCutAlbumItemsSelection()), this); // need to remove shift+del from cut action, // else the shortcut for Delete permanently collides with secondary shortcut of Cut KShortcut cutShortcut = d->cutItemsAction->shortcut(); cutShortcut.remove(Qt::SHIFT | Qt::Key_Delete, KShortcut::KeepEmpty); d->cutItemsAction->setShortcut(cutShortcut); actionCollection()->addAction("cut_album_selection", d->cutItemsAction); d->copyItemsAction = KStandardAction::copy(this, SIGNAL(signalCopyAlbumItemsSelection()), this); actionCollection()->addAction("copy_album_selection", d->copyItemsAction); d->pasteItemsAction = KStandardAction::paste(this, SIGNAL(signalPasteAlbumItemsSelection()), this); actionCollection()->addAction("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(actionCollection()); } void DigikamApp::setupActions() { 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(KIcon("go-previous"), i18n("&Back"), this); d->backwardActionMenu->setEnabled(false); d->backwardActionMenu->setShortcut(KShortcut(Qt::ALT+Qt::Key_Left)); actionCollection()->addAction("album_back", d->backwardActionMenu); 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(KIcon("go-next"), i18n("Forward"), this); d->forwardActionMenu->setEnabled(false); d->forwardActionMenu->setShortcut(KShortcut(Qt::ALT+Qt::Key_Right)); actionCollection()->addAction("album_forward", d->forwardActionMenu); 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 KAction(KIcon("view-refresh"), i18n("Refresh"), this); d->refreshAction->setShortcut(KShortcut(Qt::Key_F5)); d->refreshAction->setWhatsThis(i18n("Refresh the current contents.")); connect(d->refreshAction, SIGNAL(triggered()), d->view, SLOT(slotRefresh())); actionCollection()->addAction("view_refresh", d->refreshAction); // ----------------------------------------------------------------- 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 = "browse_" + leftWidget->objectName().remove(' ').remove("Sidebar").remove("FolderView").remove("View").toLower(); kDebug() << actionName; KAction* const action = new KAction(KIcon(leftWidget->getIcon()), leftWidget->getCaption(), this); action->setShortcut(KShortcut(leftWidget->property("Shortcut").toInt())); actionCollection()->addAction(actionName, action); connect(action, SIGNAL(triggered()), browseActionsMapper, SLOT(map())); browseActionsMapper->setMapping(action, leftWidget); } // ----------------------------------------------------------------- d->newAction = new KAction(KIcon("albumfolder-new"), i18n("&New..."), this); d->newAction->setShortcut(KStandardShortcut::openNew()); d->newAction->setWhatsThis(i18n("Creates a new empty Album in the collection.")); connect(d->newAction, SIGNAL(triggered()), d->view, SLOT(slotNewAlbum())); actionCollection()->addAction("album_new", d->newAction); // ----------------------------------------------------------------- d->moveSelectionToAlbumAction = new KAction(KIcon("albumfolder-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())); actionCollection()->addAction("move_selection_to_album", d->moveSelectionToAlbumAction); // ----------------------------------------------------------------- d->deleteAction = new KAction(KIcon("albumfolder-user-trash"), i18n("Delete Album"), this); connect(d->deleteAction, SIGNAL(triggered()), d->view, SLOT(slotDeleteAlbum())); actionCollection()->addAction("album_delete", d->deleteAction); // ----------------------------------------------------------------- d->renameAction = new KAction(KIcon("edit-rename"), i18n("Rename..."), this); d->renameAction->setShortcut(KShortcut(Qt::SHIFT + Qt::Key_F2)); connect(d->renameAction, SIGNAL(triggered()), d->view, SLOT(slotRenameAlbum())); actionCollection()->addAction("album_rename", d->renameAction); // ----------------------------------------------------------------- d->propsEditAction = new KAction(KIcon("albumfolder-properties"), i18n("Properties"), this); d->propsEditAction->setShortcut(KShortcut(Qt::ALT + Qt::Key_Return)); d->propsEditAction->setWhatsThis(i18n("Edit album properties and collection information.")); connect(d->propsEditAction, SIGNAL(triggered()), d->view, SLOT(slotAlbumPropsEdit())); actionCollection()->addAction("album_propsEdit", d->propsEditAction); // ----------------------------------------------------------------- d->writeAlbumMetadataAction = new KAction(KIcon("document-edit"), i18n("Write Metadata to Images"), this); d->writeAlbumMetadataAction->setWhatsThis(i18n("Updates metadata of images in the current " "album with the contents of digiKam database " "(image metadata will be overwritten with data from " "the database).")); connect(d->writeAlbumMetadataAction, SIGNAL(triggered()), d->view, SLOT(slotAlbumWriteMetadata())); actionCollection()->addAction("album_write_metadata", d->writeAlbumMetadataAction); // ----------------------------------------------------------------- d->readAlbumMetadataAction = new KAction(KIcon("edit-redo"), i18n("Reread Metadata From Images"), 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())); actionCollection()->addAction("album_read_metadata", d->readAlbumMetadataAction); // ----------------------------------------------------------------- d->openInFileManagerAction = new KAction(KIcon("folder-open"), i18n("Open in File Manager"), this); connect(d->openInFileManagerAction, SIGNAL(triggered()), d->view, SLOT(slotAlbumOpenInFileManager())); actionCollection()->addAction("album_openinfilemanager", d->openInFileManagerAction); // ----------------------------------------------------------- d->openInTerminalAction = new KAction(KIcon("utilities-terminal"), i18n("Open in Terminal"), this); connect(d->openInTerminalAction, SIGNAL(triggered()), d->view, SLOT(slotAlbumOpenInTerminal())); actionCollection()->addAction("album_openinterminal", d->openInTerminalAction); // ----------------------------------------------------------- d->browseTagsAction = new KAction(KIcon("tag"), i18n("Browse Tags..."), this); connect(d->browseTagsAction, SIGNAL(triggered()), d->view, SLOT(slotLeftSideBarActivateTags())); actionCollection()->addAction("browse_tag_long", d->browseTagsAction); // ----------------------------------------------------------- d->openTagMngrAction = new KAction(KIcon("tag"), i18n("Open Tag Manager"), this); connect(d->openTagMngrAction, SIGNAL(triggered()), d->view, SLOT(slotOpenTagsManager())); actionCollection()->addAction("open_tag_mngr", d->openTagMngrAction); // ----------------------------------------------------------- d->newTagAction = new KAction(KIcon("tag-new"), i18nc("new tag", "N&ew..."), this); connect(d->newTagAction, SIGNAL(triggered()), d->view, SLOT(slotNewTag())); actionCollection()->addAction("tag_new", d->newTagAction); // ----------------------------------------------------------- d->editTagAction = new KAction(KIcon("tag-properties"), i18n("Properties"), this); connect(d->editTagAction, SIGNAL(triggered()), d->view, SLOT(slotEditTag())); actionCollection()->addAction("tag_edit", d->editTagAction); // ----------------------------------------------------------- d->deleteTagAction = new KAction(KIcon("user-trash"), i18n("Delete"), this); connect(d->deleteTagAction, SIGNAL(triggered()), d->view, SLOT(slotDeleteTag())); actionCollection()->addAction("tag_delete", d->deleteTagAction); // ----------------------------------------------------------- d->assignTagAction = new KAction(KIcon("tag-new"), i18n("Assign Tag"), this); d->assignTagAction->setShortcut(KShortcut(Qt::Key_T)); connect(d->assignTagAction, SIGNAL(triggered()), d->view, SLOT(slotAssignTag())); actionCollection()->addAction("tag_assign", d->assignTagAction); // ----------------------------------------------------------- d->imageViewSelectionAction = new KSelectAction(KIcon("viewimage"), i18n("Views"), this); //d->imageViewSelectionAction->setDelayed(false); actionCollection()->addAction("view_selection", d->imageViewSelectionAction); d->imageIconViewAction = new KToggleAction(KIcon("view-list-icons"), i18nc("@action Go to thumbnails (icon) view", "Thumbnails"), this); actionCollection()->addAction("icon_view", d->imageIconViewAction); connect(d->imageIconViewAction, SIGNAL(triggered()), d->view, SLOT(slotIconView())); d->imageViewSelectionAction->addAction(d->imageIconViewAction); d->imagePreviewAction = new KToggleAction(KIcon("viewimage"), i18nc("View the selected image", "Preview Image"), this); d->imagePreviewAction->setShortcut(KShortcut(Qt::Key_F3)); actionCollection()->addAction("image_view", d->imagePreviewAction); connect(d->imagePreviewAction, SIGNAL(triggered()), d->view, SLOT(slotImagePreview())); d->imageViewSelectionAction->addAction(d->imagePreviewAction); d->imageMapViewAction = new KToggleAction(KIcon("applications-internet"), i18nc("@action Switch to map view", "Map"), this); actionCollection()->addAction("map_view", d->imageMapViewAction); connect(d->imageMapViewAction, SIGNAL(triggered()), d->view, SLOT(slotMapWidgetView())); d->imageViewSelectionAction->addAction(d->imageMapViewAction); d->imageTableViewAction = new KToggleAction(KIcon("view-list-details"), i18nc("@action Switch to table view", "Table"), this); actionCollection()->addAction("table_view", d->imageTableViewAction); connect(d->imageTableViewAction, SIGNAL(triggered()), d->view, SLOT(slotTableView())); d->imageViewSelectionAction->addAction(d->imageTableViewAction); // ----------------------------------------------------------- #ifdef USE_SCRIPT_IFACE d->scriptConsoleAction = new KAction(KIcon("application-x-shellscript"), i18n("Script Console"), this); d->scriptConsoleAction->setShortcut(KShortcut(Qt::CTRL+Qt::SHIFT+Qt::Key_S)); connect(d->scriptConsoleAction, SIGNAL(triggered()), this, SLOT(slotScriptConsole())); actionCollection()->addAction("script_console", d->scriptConsoleAction); #endif // ----------------------------------------------------------- - d->imageViewAction = new KAction(KIcon("editimage"), i18n("Edit..."), this); + d->imageViewAction = new KAction(KIcon("quickopen-file"), i18n("Open..."), this); d->imageViewAction->setShortcut(KShortcut(Qt::Key_F4)); - d->imageViewAction->setWhatsThis(i18n("Open the selected item in the image editor.")); + d->imageViewAction->setWhatsThis(i18n("Open the selected item.")); connect(d->imageViewAction, SIGNAL(triggered()), d->view, SLOT(slotImageEdit())); actionCollection()->addAction("image_edit", d->imageViewAction); - KAction* ieAction = new KAction(KIcon("editimage"), i18n("Image Editor"), this); + KAction* const openWithAction = new KAction(KIcon("editimage"), i18n("Open With Default Application"), this); + openWithAction->setShortcut(KShortcut(Qt::META + Qt::Key_F4)); + openWithAction->setWhatsThis(i18n("Open the selected item with default assigned application.")); + connect(openWithAction, SIGNAL(triggered()), d->view, SLOT(slotFileWithDefaultApplication())); + actionCollection()->addAction("open_with_default_application", openWithAction); + + KAction* const ieAction = new KAction(KIcon("editimage"), i18n("Image Editor"), this); ieAction->setWhatsThis(i18n("Open the image editor.")); connect(ieAction, SIGNAL(triggered()), d->view, SLOT(slotEditor())); actionCollection()->addAction("imageeditor", ieAction); // ----------------------------------------------------------- KAction* const ltAction = new KAction(KIcon("lighttable"), i18n("Light Table"), this); ltAction->setShortcut(KShortcut(Qt::Key_L)); connect(ltAction, SIGNAL(triggered()), d->view, SLOT(slotLightTable())); actionCollection()->addAction("light_table", ltAction); d->imageLightTableAction = new KAction(KIcon("lighttable"), i18n("Place onto Light Table"), this); d->imageLightTableAction->setShortcut(KShortcut(Qt::CTRL+Qt::Key_L)); d->imageLightTableAction->setWhatsThis(i18n("Place the selected items on the light table thumbbar.")); connect(d->imageLightTableAction, SIGNAL(triggered()), d->view, SLOT(slotImageLightTable())); actionCollection()->addAction("image_lighttable", d->imageLightTableAction); d->imageAddLightTableAction = new KAction(KIcon("lighttableadd"), i18n("Add to Light Table"), this); d->imageAddLightTableAction->setShortcut(KShortcut(Qt::SHIFT+Qt::CTRL+Qt::Key_L)); d->imageAddLightTableAction->setWhatsThis(i18n("Add selected items to the light table thumbbar.")); connect(d->imageAddLightTableAction, SIGNAL(triggered()), d->view, SLOT(slotImageAddToLightTable())); actionCollection()->addAction("image_add_to_lighttable", d->imageAddLightTableAction); // ----------------------------------------------------------- d->bqmAction = new KAction(KIcon("bqm-diff"), i18n("Batch Queue Manager"), this); d->bqmAction->setShortcut(KShortcut(Qt::Key_B)); connect(d->bqmAction, SIGNAL(triggered()), d->view, SLOT(slotQueueMgr())); actionCollection()->addAction("queue_manager", d->bqmAction); d->imageAddCurrentQueueAction = new KAction(KIcon("bqm-commit"), i18n("Add to Current Queue"), this); d->imageAddCurrentQueueAction->setShortcut(KShortcut(Qt::CTRL+Qt::Key_B)); d->imageAddCurrentQueueAction->setWhatsThis(i18n("Add selected items to current queue from batch manager.")); connect(d->imageAddCurrentQueueAction, SIGNAL(triggered()), d->view, SLOT(slotImageAddToCurrentQueue())); actionCollection()->addAction("image_add_to_current_queue", d->imageAddCurrentQueueAction); d->imageAddNewQueueAction = new KAction(KIcon("bqm-add"), i18n("Add to New Queue"), this); d->imageAddNewQueueAction->setShortcut(KShortcut(Qt::SHIFT+Qt::CTRL+Qt::Key_B)); d->imageAddNewQueueAction->setWhatsThis(i18n("Add selected items to a new queue from batch manager.")); connect(d->imageAddNewQueueAction, SIGNAL(triggered()), d->view, SLOT(slotImageAddToNewQueue())); actionCollection()->addAction("image_add_to_new_queue", d->imageAddNewQueueAction); // NOTE: see B.K.O #252130 and #283281 : we need to disable these actions when BQM is running. 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))); // ----------------------------------------------------------------- d->quickImportMenu->setText(i18nc("@action Import photos from camera", "Import")); d->quickImportMenu->setIcon(KIcon("camera-photo")); actionCollection()->addAction("import_auto", d->quickImportMenu); // ----------------------------------------------------------------- d->imageWriteMetadataAction = new KAction(KIcon("document-edit"), i18n("Write Metadata to Selected Images"), this); d->imageWriteMetadataAction->setWhatsThis(i18n("Updates metadata of images in the current " "album with the contents of digiKam database " "(image metadata will be overwritten with data from " "the database).")); connect(d->imageWriteMetadataAction, SIGNAL(triggered()), d->view, SLOT(slotImageWriteMetadata())); actionCollection()->addAction("image_write_metadata", d->imageWriteMetadataAction); // ----------------------------------------------------------------- d->imageReadMetadataAction = new KAction(KIcon("edit-redo"), i18n("Reread Metadata From Selected Images"), 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())); actionCollection()->addAction("image_read_metadata", d->imageReadMetadataAction); // ----------------------------------------------------------- d->imageFindSimilarAction = new KAction(KIcon("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())); actionCollection()->addAction("image_find_similar", d->imageFindSimilarAction); // ----------------------------------------------------------- d->imageRenameAction = new KAction(KIcon("edit-rename"), i18n("Rename..."), this); d->imageRenameAction->setShortcut(KShortcut(Qt::Key_F2)); d->imageRenameAction->setWhatsThis(i18n("Change the filename of the currently selected item.")); connect(d->imageRenameAction, SIGNAL(triggered()), d->view, SLOT(slotImageRename())); actionCollection()->addAction("image_rename", d->imageRenameAction); // ----------------------------------------------------------- // Pop up dialog to ask user whether to move to trash d->imageDeleteAction = new KAction(KIcon("user-trash"), i18nc("Non-pluralized", "Move to Trash"), this); d->imageDeleteAction->setShortcut(KShortcut(Qt::Key_Delete)); connect(d->imageDeleteAction, SIGNAL(triggered()), d->view, SLOT(slotImageDelete())); actionCollection()->addAction("image_delete", d->imageDeleteAction); // ----------------------------------------------------------- // 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 partiton or from a net source. // Also note that we use the wrong icon for the default album delete action, which should have a thrashcan icon instead // of a red cross, it confuses users. d->imageDeletePermanentlyAction = new KAction(KIcon("edit-delete"), i18n("Delete Permanently"), this); d->imageDeletePermanentlyAction->setShortcut(KShortcut(Qt::SHIFT+Qt::Key_Delete)); connect(d->imageDeletePermanentlyAction, SIGNAL(triggered()), d->view, SLOT(slotImageDeletePermanently())); actionCollection()->addAction("image_delete_permanently", d->imageDeletePermanentlyAction); // ----------------------------------------------------------- // These two actions are hidden, no menu entry, no toolbar entry, no shortcut. // Power users may add them. d->imageDeletePermanentlyDirectlyAction = new KAction(KIcon("edit-delete"), i18n("Delete permanently without confirmation"), this); connect(d->imageDeletePermanentlyDirectlyAction, SIGNAL(triggered()), d->view, SLOT(slotImageDeletePermanentlyDirectly())); actionCollection()->addAction("image_delete_permanently_directly", d->imageDeletePermanentlyDirectlyAction); // ----------------------------------------------------------- d->imageTrashDirectlyAction = new KAction(KIcon("user-trash"), i18n("Move to trash without confirmation"), this); connect(d->imageTrashDirectlyAction, SIGNAL(triggered()), d->view, SLOT(slotImageTrashDirectly())); actionCollection()->addAction("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))); actionCollection()->addAction("album_sort", d->albumSortAction); // Use same list order as in albumsettings 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 KToggleAction(i18n("Include Album Sub-Tree"), this); 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))); actionCollection()->addAction("albums_recursive", d->recurseAlbumsAction); d->recurseTagsAction = new KToggleAction(i18n("Include Tag Sub-Tree"), this); 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))); actionCollection()->addAction("tags_recursive", d->recurseTagsAction); // ----------------------------------------------------------- d->imageSortAction = new KSelectAction(i18n("&Sort Images"), 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))); actionCollection()->addAction("image_sort", d->imageSortAction); // map to ImageSortSettings 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")); 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())); imageSortMapper->setMapping(sortByNameAction, (int)ImageSortSettings::SortByFileName); imageSortMapper->setMapping(sortByPathAction, (int)ImageSortSettings::SortByFilePath); imageSortMapper->setMapping(sortByDateAction, (int)ImageSortSettings::SortByCreationDate); imageSortMapper->setMapping(sortByFileSizeAction, (int)ImageSortSettings::SortByFileSize); imageSortMapper->setMapping(sortByRatingAction, (int)ImageSortSettings::SortByRating); imageSortMapper->setMapping(sortByImageSizeAction, (int)ImageSortSettings::SortByImageSize); imageSortMapper->setMapping(sortByAspectRatioAction, (int)ImageSortSettings::SortByAspectRatio); // ----------------------------------------------------------- d->imageSortOrderAction = new KSelectAction(i18n("Image Sorting &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))); actionCollection()->addAction("image_sort_order", d->imageSortOrderAction); QAction* const sortAscendingAction = d->imageSortOrderAction->addAction(KIcon("view-sort-ascending"), i18n("Ascending")); QAction* const sortDescendingAction = d->imageSortOrderAction->addAction(KIcon("view-sort-descending"), i18n("Descending")); connect(sortAscendingAction, SIGNAL(triggered()), imageSortOrderMapper, SLOT(map())); connect(sortDescendingAction, SIGNAL(triggered()), imageSortOrderMapper, SLOT(map())); imageSortOrderMapper->setMapping(sortAscendingAction, (int)ImageSortSettings::AscendingOrder); imageSortOrderMapper->setMapping(sortDescendingAction, (int)ImageSortSettings::DescendingOrder); // ----------------------------------------------------------- d->imageGroupAction = new KSelectAction(i18n("&Group Images"), this); d->imageGroupAction->setWhatsThis(i18n("The categories in which the images in the thumbnail view are displayed")); QSignalMapper* const imageGroupMapper = new QSignalMapper(this); connect(imageGroupMapper, SIGNAL(mapped(int)), d->view, SLOT(slotGroupImages(int))); actionCollection()->addAction("image_group", d->imageGroupAction); // map to ImageSortSettings enum QAction* const noCategoriesAction = d->imageGroupAction->addAction(i18n("Flat List")); QAction* const groupByAlbumAction = d->imageGroupAction->addAction(i18n("By Album")); QAction* const groupByFormatAction = d->imageGroupAction->addAction(i18n("By Format")); connect(noCategoriesAction, SIGNAL(triggered()), imageGroupMapper, SLOT(map())); connect(groupByAlbumAction, SIGNAL(triggered()), imageGroupMapper, SLOT(map())); connect(groupByFormatAction, SIGNAL(triggered()), imageGroupMapper, SLOT(map())); imageGroupMapper->setMapping(noCategoriesAction, (int)ImageSortSettings::OneCategory); imageGroupMapper->setMapping(groupByAlbumAction, (int)ImageSortSettings::CategoryByAlbum); imageGroupMapper->setMapping(groupByFormatAction, (int)ImageSortSettings::CategoryByFormat); // ----------------------------------------------------------------- d->imageGroupSortOrderAction = new KSelectAction(i18n("Group Sorting Order"), this); d->imageGroupSortOrderAction->setWhatsThis(i18n("The sort order of images groups")); QSignalMapper* const imageGroupSortOrderMapper = new QSignalMapper(this); connect(imageGroupSortOrderMapper, SIGNAL(mapped(int)), d->view, SLOT(slotSortImageGroupOrder(int))); actionCollection()->addAction("image_group_sort_order", d->imageGroupSortOrderAction); QAction* const sortGroupsAscending = d->imageGroupSortOrderAction->addAction(KIcon("view-sort-ascending"), i18n("Ascending")); QAction* const sortGroupsDescending = d->imageGroupSortOrderAction->addAction(KIcon("view-sort-descending"), i18n("Descending")); connect(sortGroupsAscending, SIGNAL(triggered()), imageGroupSortOrderMapper, SLOT(map())); connect(sortGroupsDescending, SIGNAL(triggered()), imageGroupSortOrderMapper, SLOT(map())); imageGroupSortOrderMapper->setMapping(sortGroupsAscending, (int)ImageSortSettings::AscendingOrder); imageGroupSortOrderMapper->setMapping(sortGroupsDescending, (int)ImageSortSettings::DescendingOrder); // ----------------------------------------------------------------- setupImageTransformActions(); setupExifOrientationActions(); // ----------------------------------------------------------------- d->selectAllAction = new KAction(i18n("Select All"), this); d->selectAllAction->setShortcut(KShortcut(Qt::CTRL+Qt::Key_A)); connect(d->selectAllAction, SIGNAL(triggered()), d->view, SLOT(slotSelectAll())); actionCollection()->addAction("selectAll", d->selectAllAction); // ----------------------------------------------------------------- d->selectNoneAction = new KAction(i18n("Select None"), this); d->selectNoneAction->setShortcut(KShortcut(Qt::CTRL+Qt::SHIFT+Qt::Key_A)); connect(d->selectNoneAction, SIGNAL(triggered()), d->view, SLOT(slotSelectNone())); actionCollection()->addAction("selectNone", d->selectNoneAction); // ----------------------------------------------------------------- d->selectInvertAction = new KAction(i18n("Invert Selection"), this); d->selectInvertAction->setShortcut(KShortcut(Qt::CTRL+Qt::Key_I)); connect(d->selectInvertAction, SIGNAL(triggered()), d->view, SLOT(slotSelectInvert())); actionCollection()->addAction("selectInvert", d->selectInvertAction); // ----------------------------------------------------------- d->showBarAction = new KToggleAction(KIcon("view-choose"), i18n("Show Thumbbar"), this); d->showBarAction->setShortcut(KShortcut(Qt::CTRL+Qt::Key_T)); connect(d->showBarAction, SIGNAL(triggered()), this, SLOT(slotToggleShowBar())); actionCollection()->addAction("showthumbs", d->showBarAction); d->showMenuBarAction = KStandardAction::showMenubar(this, SLOT(slotShowMenuBar()), actionCollection()); KStandardAction::keyBindings(this, SLOT(slotEditKeys()), actionCollection()); KStandardAction::configureToolbars(this, SLOT(slotConfToolbars()), actionCollection()); KStandardAction::configureNotifications(this, SLOT(slotConfNotifications()), actionCollection()); KStandardAction::preferences(this, SLOT(slotSetup()), actionCollection()); // Provides a menu entry that allows showing/hiding the toolbar(s) setStandardToolBarMenuEnabled(true); // Provides a menu entry that allows showing/hiding the statusbar createStandardStatusBarAction(); // ----------------------------------------------------------- d->zoomPlusAction = KStandardAction::zoomIn(d->view, SLOT(slotZoomIn()), this); KShortcut keysPlus = d->zoomPlusAction->shortcut(); keysPlus.setAlternate(Qt::Key_Plus); d->zoomPlusAction->setShortcut(keysPlus); actionCollection()->addAction("album_zoomin", d->zoomPlusAction); // ----------------------------------------------------------- d->zoomMinusAction = KStandardAction::zoomOut(d->view, SLOT(slotZoomOut()), this); KShortcut keysMinus = d->zoomMinusAction->shortcut(); keysMinus.setAlternate(Qt::Key_Minus); d->zoomMinusAction->setShortcut(keysMinus); actionCollection()->addAction("album_zoomout", d->zoomMinusAction); // ----------------------------------------------------------- d->zoomTo100percents = new KAction(KIcon("zoom-original"), i18n("Zoom to 100%"), this); d->zoomTo100percents->setShortcut(KShortcut(Qt::CTRL + Qt::Key_Comma)); connect(d->zoomTo100percents, SIGNAL(triggered()), d->view, SLOT(slotZoomTo100Percents())); actionCollection()->addAction("album_zoomto100percents", d->zoomTo100percents); // ----------------------------------------------------------- d->zoomFitToWindowAction = new KAction(KIcon("zoom-fit-best"), i18n("Fit to &Window"), this); d->zoomFitToWindowAction->setShortcut(KShortcut(Qt::ALT + Qt::CTRL + Qt::Key_E)); connect(d->zoomFitToWindowAction, SIGNAL(triggered()), d->view, SLOT(slotFitToWindow())); actionCollection()->addAction("album_zoomfit2window", d->zoomFitToWindowAction); // ----------------------------------------------------------- createFullScreenAction("full_screen"); createSidebarActions(); // ----------------------------------------------------------- d->slideShowAction = new KActionMenu(KIcon("view-presentation"), i18n("Slideshow"), this); d->slideShowAction->setDelayed(false); actionCollection()->addAction("slideshow", d->slideShowAction); d->slideShowAllAction = new KAction(i18n("All"), this); d->slideShowAllAction->setShortcut(KShortcut(Qt::Key_F9)); connect(d->slideShowAllAction, SIGNAL(triggered()), d->view, SLOT(slotSlideShowAll())); actionCollection()->addAction("slideshow_all", d->slideShowAllAction); d->slideShowAction->addAction(d->slideShowAllAction); d->slideShowSelectionAction = new KAction(i18n("Selection"), this); d->slideShowSelectionAction->setShortcut(KShortcut(Qt::ALT+Qt::Key_F9)); connect(d->slideShowSelectionAction, SIGNAL(triggered()), d->view, SLOT(slotSlideShowSelection())); actionCollection()->addAction("slideshow_selected", d->slideShowSelectionAction); d->slideShowAction->addAction(d->slideShowSelectionAction); d->slideShowRecursiveAction = new KAction(i18n("With All Sub-Albums"), this); d->slideShowRecursiveAction->setShortcut(KShortcut(Qt::SHIFT+Qt::Key_F9)); connect(d->slideShowRecursiveAction, SIGNAL(triggered()), d->view, SLOT(slotSlideShowRecursive())); actionCollection()->addAction("slideshow_recursive", d->slideShowRecursiveAction); d->slideShowAction->addAction(d->slideShowRecursiveAction); #ifdef USE_PRESENTATION_MODE d->slideShowQmlAction = new KAction(i18n("Presentation View"), this); d->slideShowQmlAction->setShortcut(KShortcut(Qt::Key_F10)); connect(d->slideShowQmlAction, SIGNAL(triggered()), d->view, SLOT(slotSlideShowQml())); actionCollection()->addAction("slideshow_qml", d->slideShowQmlAction); d->slideShowAction->addAction(d->slideShowQmlAction); #endif // USE_PRESENTATION_MODE // ----------------------------------------------------------- d->quitAction = KStandardAction::quit(this, SLOT(slotExit()), this); actionCollection()->addAction("app_exit", d->quitAction); // ----------------------------------------------------------- createHelpActions(); // ----------------------------------------------------------- d->kipiHelpAction = new KAction(KIcon("kipi"), i18n("Kipi Plugins Handbook"), this); connect(d->kipiHelpAction, SIGNAL(triggered()), this, SLOT(slotShowKipiHelp())); actionCollection()->addAction("help_kipi", d->kipiHelpAction); // ----------------------------------------------------------- d->tipAction = actionCollection()->addAction(KStandardAction::TipofDay, "help_tipofday", this, SLOT(slotShowTip())); //------------------------------------------------------------ KAction* const findAction = new KAction(KIcon("system-search"), i18n("Search..."), this); findAction->setShortcut(KShortcut(Qt::CTRL+Qt::Key_F)); connect(findAction, SIGNAL(triggered()), d->view, SLOT(slotNewKeywordSearch())); actionCollection()->addAction("search_quick", findAction); // ----------------------------------------------------------- KAction* const advFindAction = new KAction(KIcon("system-search"), i18n("Advanced Search..."), this); advFindAction->setShortcut(KShortcut(Qt::CTRL+Qt::ALT+Qt::Key_F)); connect(advFindAction, SIGNAL(triggered()), d->view, SLOT(slotNewAdvancedSearch())); actionCollection()->addAction("search_advanced", advFindAction); // ----------------------------------------------------------- KAction* const duplicatesAction = new KAction(KIcon("tools-wizard"), i18n("Find Duplicates..."), this); duplicatesAction->setShortcut(KShortcut(Qt::CTRL+Qt::Key_D)); connect(duplicatesAction, SIGNAL(triggered()), d->view, SLOT(slotNewDuplicatesSearch())); actionCollection()->addAction("find_duplicates", duplicatesAction); // ----------------------------------------------------------- KAction* const databaseMigrationAction = new KAction(KIcon("server-database"), i18n("Database Migration..."), this); connect(databaseMigrationAction, SIGNAL(triggered()), this, SLOT(slotDatabaseMigration())); actionCollection()->addAction("database_migration", databaseMigrationAction); // ----------------------------------------------------------- d->maintenanceAction = new KAction(KIcon("run-build-prune"), i18n("Maintenance..."), this); connect(d->maintenanceAction, SIGNAL(triggered()), this, SLOT(slotMaintenance())); actionCollection()->addAction("maintenance", d->maintenanceAction); // ----------------------------------------------------------- KAction* const cameraAction = new KAction(i18n("Add Camera Manually..."), this); connect(cameraAction, SIGNAL(triggered()), this, SLOT(slotSetupCamera())); actionCollection()->addAction("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->message(i18n("Loading cameras...")); } loadCameras(); // Load Themes populateThemes(); createGUI(xmlFile()); } 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->openInTerminalAction->setEnabled(false); d->imageViewAction->setEnabled(false); d->imagePreviewAction->setEnabled(false); d->imageLightTableAction->setEnabled(false); d->imageAddLightTableAction->setEnabled(false); d->imageFindSimilarAction->setEnabled(false); d->imageRenameAction->setEnabled(false); d->imageDeleteAction->setEnabled(false); d->imageExifOrientationActionMenu->setEnabled(false); d->slideShowSelectionAction->setEnabled(false); d->albumSortAction->setCurrentItem((int)AlbumSettings::instance()->getAlbumSortOrder()); d->imageSortAction->setCurrentItem((int)AlbumSettings::instance()->getImageSortOrder()); d->imageSortOrderAction->setCurrentItem((int)AlbumSettings::instance()->getImageSorting()); d->imageGroupAction->setCurrentItem((int)AlbumSettings::instance()->getImageGroupMode()-1); // no action for enum 0 d->imageGroupSortOrderAction->setCurrentItem((int)AlbumSettings::instance()->getImageGroupSortOrder()); d->recurseAlbumsAction->setChecked(AlbumSettings::instance()->getRecurseAlbums()); d->recurseTagsAction->setChecked(AlbumSettings::instance()->getRecurseTags()); d->showBarAction->setChecked(AlbumSettings::instance()->getShowThumbbar()); d->showMenuBarAction->setChecked(!menuBar()->isHidden()); // NOTE: workaround for B.K.O #171080 slotSwitchedToIconView(); } void DigikamApp::enableZoomPlusAction(bool val) { d->zoomPlusAction->setEnabled(val); } void DigikamApp::enableZoomMinusAction(bool val) { d->zoomMinusAction->setEnabled(val); } void DigikamApp::enableAlbumBackwardHistory(bool enable) { d->backwardActionMenu->setEnabled(enable); } void DigikamApp::enableAlbumForwardHistory(bool enable) { d->forwardActionMenu->setEnabled(enable); } void DigikamApp::slotAboutToShowBackwardMenu() { d->backwardActionMenu->menu()->clear(); QStringList titles; d->view->getBackwardHistory(titles); for (int i = 0; i < titles.size(); ++i) { QAction* const action = d->backwardActionMenu->menu()->addAction(titles.at(i), d->backwardSignalMapper, SLOT(map())); d->backwardSignalMapper->setMapping(action, i + 1); } } void DigikamApp::slotAboutToShowForwardMenu() { d->forwardActionMenu->menu()->clear(); QStringList titles; d->view->getForwardHistory(titles); for (int i = 0; i < titles.size(); ++i) { QAction* const action = d->forwardActionMenu->menu()->addAction(titles.at(i), d->forwardSignalMapper, SLOT(map())); d->forwardSignalMapper->setMapping(action, i + 1); } } void DigikamApp::slotAlbumSelected(bool val) { // NOTE: val is true when a PAlbum is selected. QList albumList = AlbumManager::instance()->currentAlbums(); Album* album = 0; if(!albumList.isEmpty()) { album = albumList.first(); } if (album) { if (!val) { // Not a PAlbum is selected d->deleteAction->setEnabled(false); d->renameAction->setEnabled(false); d->addImagesAction->setEnabled(false); d->propsEditAction->setEnabled(false); d->openInFileManagerAction->setEnabled(false); d->openInTerminalAction->setEnabled(false); d->newAction->setEnabled(false); d->addFoldersAction->setEnabled(false); d->writeAlbumMetadataAction->setEnabled(true); d->readAlbumMetadataAction->setEnabled(true); d->pasteItemsAction->setEnabled(!album->isRoot()); } else { // We have either the abstract root album, // the album root album for collection base dirs, or normal albums. PAlbum* const palbum = static_cast(album); bool isRoot = palbum->isRoot(); bool isAlbumRoot = palbum->isAlbumRoot(); bool isNormalAlbum = !isRoot && !isAlbumRoot; d->deleteAction->setEnabled(isNormalAlbum); d->renameAction->setEnabled(isNormalAlbum); d->addImagesAction->setEnabled(isNormalAlbum || isAlbumRoot); d->propsEditAction->setEnabled(isNormalAlbum); d->openInFileManagerAction->setEnabled(isNormalAlbum || isAlbumRoot); d->openInTerminalAction->setEnabled(isNormalAlbum || isAlbumRoot); d->newAction->setEnabled(isNormalAlbum || isAlbumRoot); d->addFoldersAction->setEnabled(isNormalAlbum || isAlbumRoot); d->writeAlbumMetadataAction->setEnabled(isNormalAlbum || isAlbumRoot); d->readAlbumMetadataAction->setEnabled(isNormalAlbum || isAlbumRoot); d->pasteItemsAction->setEnabled(isNormalAlbum || isAlbumRoot); } } else { d->deleteAction->setEnabled(false); d->renameAction->setEnabled(false); d->addImagesAction->setEnabled(false); d->propsEditAction->setEnabled(false); d->openInFileManagerAction->setEnabled(false); d->openInTerminalAction->setEnabled(false); d->newAction->setEnabled(false); d->addFoldersAction->setEnabled(false); d->writeAlbumMetadataAction->setEnabled(false); d->readAlbumMetadataAction->setEnabled(false); d->pasteItemsAction->setEnabled(false); } } void DigikamApp::slotTagSelected(bool val) { QList albumList = AlbumManager::instance()->currentAlbums(); Album* album = 0; if(!albumList.isEmpty()) { album = albumList.first(); } if (!album) { return; } bool enabled = val && album && !album->isRoot(); d->browseTagsAction->setEnabled(!val); d->newTagAction->setEnabled(enabled); d->deleteTagAction->setEnabled(enabled); d->editTagAction->setEnabled(enabled); } void DigikamApp::slotImageSelected(const ImageInfoList& selection, const ImageInfoList& listAll) { /// @todo Currently only triggered by IconView, need to adapt to TableView int num_images = listAll.count(); QString text; switch (selection.count()) { case 0: { d->statusBarSelectionText = i18np("No item selected (%1 item)", "No item selected (%1 items)", num_images); break; } case 1: { - // check if the selected item is really an image, if not, disable the edit action - if (selection.first().category() != DatabaseItem::Image) - { - d->imageViewAction->setEnabled(false); - } - slotSetCheckedExifOrientationAction(selection.first()); int index = listAll.indexOf(selection.first()) + 1; d->statusBarSelectionText = selection.first().fileUrl().fileName() + i18n(" (%1 of %2)", index, num_images); break; } default: { d->statusBarSelectionText = i18np("%2/%1 item selected", "%2/%1 items selected", num_images, selection.count()); break; } } d->statusLabel->setText(d->statusBarSelectionText); } void DigikamApp::slotSelectionChanged(int selectionCount) { // The preview can either be activated when only one image is selected, // or if multiple images are selected, but one image is the 'current image'. bool hasAtLeastCurrent =(selectionCount == 1) || ( (selectionCount > 0) && d->view->hasCurrentItem()); d->imagePreviewAction->setEnabled(hasAtLeastCurrent); d->imageViewAction->setEnabled(hasAtLeastCurrent); d->imageFindSimilarAction->setEnabled(selectionCount == 1); d->imageRenameAction->setEnabled(selectionCount > 0); d->imageLightTableAction->setEnabled(selectionCount > 0); d->imageAddLightTableAction->setEnabled(selectionCount > 0); d->imageAddCurrentQueueAction->setEnabled((selectionCount > 0) && !QueueMgrWindow::queueManagerWindow()->isBusy()); d->imageAddNewQueueAction->setEnabled((selectionCount > 0) && !QueueMgrWindow::queueManagerWindow()->isBusy()); d->imageWriteMetadataAction->setEnabled(selectionCount > 0); d->imageReadMetadataAction->setEnabled(selectionCount > 0); d->imageDeleteAction->setEnabled(selectionCount > 0); d->imageRotateActionMenu->setEnabled(selectionCount > 0); d->imageFlipActionMenu->setEnabled(selectionCount > 0); d->imageExifOrientationActionMenu->setEnabled(selectionCount > 0); d->slideShowSelectionAction->setEnabled(selectionCount > 0); d->moveSelectionToAlbumAction->setEnabled(selectionCount > 0); d->cutItemsAction->setEnabled(selectionCount > 0); d->copyItemsAction->setEnabled(selectionCount > 0); if (selectionCount > 0) { d->imageWriteMetadataAction->setText(i18np("Write Metadata to Image", "Write Metadata to Selected Images", selectionCount)); d->imageReadMetadataAction->setText(i18np("Reread Metadata From Image", "Reread Metadata From Selected Images", selectionCount)); slotResetExifOrientationActions(); } } void DigikamApp::slotExit() { close(); } void DigikamApp::downloadImages( const QString& folder ) { if (!folder.isNull()) { // activate window when called by media menu and DCOP if (isMinimized()) { KWindowSystem::unminimizeWindow(winId()); } KWindowSystem::activateWindow(winId()); emit queuedOpenCameraUiFromPath(folder); } } void DigikamApp::cameraAutoDetect() { // activate window when called by media menu and DCOP if (isMinimized()) { KWindowSystem::unminimizeWindow(winId()); } KWindowSystem::activateWindow(winId()); slotCameraAutoDetect(); } void DigikamApp::loadCameras() { d->cameraMenu->setText(i18n("Cameras")); d->cameraMenu->setIcon(KIcon("camera-photo")); d->usbMediaMenu->setText(i18n("USB Storage Devices")); d->usbMediaMenu->setIcon(KIcon("drive-removable-media-usb")); d->cardReaderMenu->setText(i18n("Card Readers")); d->cardReaderMenu->setIcon(KIcon("media-flash-smart-media")); actionCollection()->addAction("cameras", d->cameraMenu); actionCollection()->addAction("usb_media", d->usbMediaMenu); actionCollection()->addAction("card_reader", d->cardReaderMenu); // ----------------------------------------------------------------- d->addImagesAction = new KAction(KIcon("albumfolder-importimages"), i18n("Add Images..."), this); d->addImagesAction->setShortcut(KShortcut(Qt::CTRL+Qt::ALT+Qt::Key_I)); d->addImagesAction->setWhatsThis(i18n("Adds new items to an Album.")); connect(d->addImagesAction, SIGNAL(triggered()), this, SLOT(slotImportAddImages())); actionCollection()->addAction("import_addImages", d->addImagesAction); // ----------------------------------------------------------------- d->addFoldersAction = new KAction(KIcon("albumfolder-importdir"), i18n("Add Folders..."), this); d->addFoldersAction->setWhatsThis(i18n("Adds new folders to Album library.")); connect(d->addFoldersAction, SIGNAL(triggered()), this, SLOT(slotImportAddFolders())); actionCollection()->addAction("import_addFolders", d->addFoldersAction); // -- fill manually added cameras ---------------------------------- d->cameraList->load(); // -- scan Solid devices ------------------------------------------- fillSolidMenus(); connect(Solid::DeviceNotifier::instance(), SIGNAL(deviceAdded(QString)), this, SLOT(slotSolidDeviceChanged(QString))); connect(Solid::DeviceNotifier::instance(), SIGNAL(deviceRemoved(QString)), this, SLOT(slotSolidDeviceChanged(QString))); // -- queued connections ------------------------------------------- connect(this, SIGNAL(queuedOpenCameraUiFromPath(QString)), this, SLOT(slotOpenCameraUiFromPath(QString)), Qt::QueuedConnection); connect(this, SIGNAL(queuedOpenSolidDevice(QString)), this, SLOT(slotOpenSolidDevice(QString)), Qt::QueuedConnection); } void DigikamApp::slotCameraAdded(CameraType* ctype) { if (!ctype) { return; } KAction* const cAction = new KAction(KIcon("camera-photo"), ctype->title(), d->manualCameraActionGroup); cAction->setData(ctype->title()); actionCollection()->addAction(ctype->title().toUtf8(), cAction); ctype->setAction(cAction); updateCameraMenu(); updateQuickImportAction(); } void DigikamApp::slotCameraRemoved(KAction* cAction) { if (cAction) { d->manualCameraActionGroup->removeAction(cAction); } updateCameraMenu(); updateQuickImportAction(); } void DigikamApp::slotCameraAutoDetect() { bool retry = false; CameraType* const ctype = d->cameraList->autoDetect(retry); if (!ctype && retry) { QTimer::singleShot(0, this, SLOT(slotCameraAutoDetect())); return; } if (ctype && ctype->action()) { ctype->action()->activate(QAction::Trigger); } } void DigikamApp::slotOpenCameraUiFromPath(const QString& path) { if (path.isEmpty()) { return; } // the ImportUI will delete itself when it has finished ImportUI* const cgui = new ImportUI(this, i18n("Images found in %1", path), "directory browse", "Fixed", path, 1); cgui->show(); connect(cgui, SIGNAL(signalLastDestination(KUrl)), d->view, SLOT(slotSelectAlbum(KUrl))); } void DigikamApp::slotOpenManualCamera(QAction* action) { CameraType* const ctype = d->cameraList->find(action->data().toString()); if (ctype) { // check not to open two dialogs for the same camera if (ctype->currentImportUI() && !ctype->currentImportUI()->isClosed()) { // show and raise dialog if (ctype->currentImportUI()->isMinimized()) { KWindowSystem::unminimizeWindow(ctype->currentImportUI()->winId()); } KWindowSystem::activateWindow(ctype->currentImportUI()->winId()); } else { // the ImportUI will delete itself when it has finished ImportUI* const cgui = new ImportUI(this, ctype->title(), ctype->model(), ctype->port(), ctype->path(), ctype->startingNumber()); ctype->setCurrentImportUI(cgui); cgui->show(); connect(cgui, SIGNAL(signalLastDestination(KUrl)), d->view, SLOT(slotSelectAlbum(KUrl))); } } } void DigikamApp::slotOpenSolidDevice(const QString& udi) { // Identifies device as either Camera or StorageAccess and calls methods accordingly Solid::Device device(udi); if (!device.isValid()) { KMessageBox::error(this, i18n("The specified device (\"%1\") is not valid.", udi)); return; } if (device.is()) { openSolidUsmDevice(udi); } else if (device.is()) { if (!checkSolidCamera(device)) { KMessageBox::error(this, i18n("The specified camera (\"%1\") is not supported.", udi)); return; } openSolidCamera(udi); } } void DigikamApp::slotOpenSolidCamera(QAction* action) { QString udi = action->data().toString(); openSolidCamera(udi, action->iconText()); } void DigikamApp::openSolidCamera(const QString& udi, const QString& cameraLabel) { // if there is already an open ImportUI for the device, show and raise it, and be done if (d->cameraUIMap.contains(udi)) { ImportUI* const ui = d->cameraUIMap.value(udi); if (ui && !ui->isClosed()) { if (ui->isMinimized()) { KWindowSystem::unminimizeWindow(ui->winId()); } KWindowSystem::activateWindow(ui->winId()); return; } } // recreate device from unambiguous UDI Solid::Device device(udi); if ( device.isValid() ) { if (cameraLabel.isNull()) { QString label = labelForSolidCamera(device); } Solid::Camera* const camera = device.as(); QList list = camera->driverHandle("gphoto").toList(); // all sanity checks have already been done when creating the action if (list.size() < 3) { return; } // NOTE: See B.K.O #262296: With KDE 4.6, Solid API return device vendor id // and product id in hexadecimal strings. #if KDE_IS_VERSION(4,5,90) bool ok; int vendorId = list.at(1).toString().toInt(&ok, 16); int productId = list.at(2).toString().toInt(&ok, 16); #else int vendorId = list.at(1).toInt(); int productId = list.at(2).toInt(); #endif QString model, port; if (CameraList::findConnectedCamera(vendorId, productId, model, port)) { kDebug() << "Found camera from ids " << vendorId << " " << productId << " camera is: " << model << " at " << port; // the ImportUI will delete itself when it has finished ImportUI* const cgui = new ImportUI(this, cameraLabel, model, port, "/", 1); d->cameraUIMap[udi] = cgui; cgui->show(); connect(cgui, SIGNAL(signalLastDestination(KUrl)), d->view, SLOT(slotSelectAlbum(KUrl))); } else { kError() << "Failed to detect camera with GPhoto2 from Solid information"; } } } void DigikamApp::slotOpenSolidUsmDevice(QAction* action) { QString udi = action->data().toString(); openSolidUsmDevice(udi, action->iconText()); } void DigikamApp::openSolidUsmDevice(const QString& udi, const QString& givenLabel) { QString mediaLabel = givenLabel; // if there is already an open ImportUI for the device, show and raise it if (d->cameraUIMap.contains(udi)) { ImportUI* const ui = d->cameraUIMap.value(udi); if (ui && !ui->isClosed()) { if (ui->isMinimized()) { KWindowSystem::unminimizeWindow(ui->winId()); } KWindowSystem::activateWindow(ui->winId()); return; } } // recreate device from unambiguous UDI Solid::Device device(udi); if ( device.isValid() ) { Solid::StorageAccess* const access = device.as(); if (!access) { return; } if (!access->isAccessible()) { QApplication::setOverrideCursor(Qt::WaitCursor); if (!access->setup()) { return; } d->eventLoop = new QEventLoop(this); connect(access, SIGNAL(setupDone(Solid::ErrorType,QVariant,QString)), this, SLOT(slotSolidSetupDone(Solid::ErrorType,QVariant,QString))); int returnCode = d->eventLoop->exec(QEventLoop::ExcludeUserInputEvents); delete d->eventLoop; d->eventLoop = 0; QApplication::restoreOverrideCursor(); if (returnCode == 1) { KMessageBox::error(this, d->solidErrorMessage); return; } } // Create Camera UI QString path = QDir::fromNativeSeparators(access->filePath()); if (mediaLabel.isNull()) { mediaLabel = path; } // the ImportUI will delete itself when it has finished ImportUI* const cgui = new ImportUI(this, i18n("Images on %1", mediaLabel), "directory browse", "Fixed", path, 1); d->cameraUIMap[udi] = cgui; cgui->show(); connect(cgui, SIGNAL(signalLastDestination(KUrl)), d->view, SLOT(slotSelectAlbum(KUrl))); } } void DigikamApp::slotSolidSetupDone(Solid::ErrorType errorType, QVariant errorData, const QString& /*udi*/) { if (!d->eventLoop) { return; } if (errorType == Solid::NoError) { d->eventLoop->exit(0); } else { d->solidErrorMessage = i18n("Cannot access the storage device.\n"); d->solidErrorMessage += errorData.toString(); d->eventLoop->exit(1); } } void DigikamApp::slotSolidDeviceChanged(const QString& udi) { Q_UNUSED(udi) fillSolidMenus(); } bool DigikamApp::checkSolidCamera(const Solid::Device& cameraDevice) { const Solid::Camera* const camera = cameraDevice.as(); if (!camera) { return false; } QStringList drivers = camera->supportedDrivers(); kDebug() << "fillSolidMenus: Found Camera " << cameraDevice.vendor() + ' ' + cameraDevice.product() << " protocols " << camera->supportedProtocols() << " drivers " << camera->supportedDrivers("ptp"); // We handle gphoto2 cameras in this loop if (! (camera->supportedDrivers().contains("gphoto") || camera->supportedProtocols().contains("ptp")) ) { return false; } QVariant driverHandle = camera->driverHandle("gphoto"); if (!driverHandle.canConvert(QVariant::List)) { kWarning() << "Solid returns unsupported driver handle for gphoto2"; return false; } QList driverHandleList = driverHandle.toList(); if (driverHandleList.size() < 3 || driverHandleList.at(0).toString() != "usb" || !driverHandleList.at(1).canConvert(QVariant::Int) || !driverHandleList.at(2).canConvert(QVariant::Int) ) { kWarning() << "Solid returns unsupported driver handle for gphoto2"; return false; } return true; } QString DigikamApp::labelForSolidCamera(const Solid::Device& cameraDevice) { QString vendor = cameraDevice.vendor(); QString product = cameraDevice.product(); if (product == "USB Imaging Interface" || product == "USB Vendor Specific Interface") { Solid::Device parentUsbDevice = cameraDevice.parent(); if (parentUsbDevice.isValid()) { vendor = parentUsbDevice.vendor(); product = parentUsbDevice.product(); if (!vendor.isEmpty() && !product.isEmpty()) { if (vendor == "Canon, Inc.") { vendor = "Canon"; if (product.startsWith(QLatin1String("Canon "))) { product = product.mid(6); // cut off another "Canon " from product } if (product.endsWith(QLatin1String(" (ptp)"))) { product.chop(6); // cut off " (ptp)" } } else if (vendor == "Fuji Photo Film Co., Ltd") { vendor = "Fuji"; } else if (vendor == "Nikon Corp.") { vendor = "Nikon"; if (product.startsWith(QLatin1String("NIKON "))) { product = product.mid(6); } } } } } return vendor + ' ' + product; } void DigikamApp::fillSolidMenus() { QHash newAppearanceTimes; d->usbMediaMenu->menu()->clear(); d->cardReaderMenu->menu()->clear(); // delete the actionGroups to avoid duplicate menu entries delete d->solidUsmActionGroup; delete d->solidCameraActionGroup; 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*))); // -------------------------------------------------------- QList cameraDevices = Solid::Device::listFromType(Solid::DeviceInterface::Camera); foreach(const Solid::Device& cameraDevice, cameraDevices) { // USM camera: will be handled below if (cameraDevice.is()) { continue; } if (!checkSolidCamera(cameraDevice)) { continue; } // -------------------------------------------------------- QString l = labelForSolidCamera(cameraDevice); QString label = CameraNameHelper::cameraNameAutoDetected(l.trimmed()); // -------------------------------------------------------- QString iconName = cameraDevice.icon(); if (iconName.isEmpty()) { iconName = "camera-photo"; } KAction* const action = new KAction(label, d->solidCameraActionGroup); action->setIcon(KIcon(iconName)); // set data to identify device in action slot slotSolidSetupDevice action->setData(cameraDevice.udi()); newAppearanceTimes[cameraDevice.udi()] = d->cameraAppearanceTimes.contains(cameraDevice.udi()) ? d->cameraAppearanceTimes.value(cameraDevice.udi()) : QDateTime::currentDateTime(); d->cameraMenu->addAction(action); } QList storageDevices = Solid::Device::listFromType(Solid::DeviceInterface::StorageAccess); foreach(const Solid::Device& accessDevice, storageDevices) { // check for StorageAccess if (!accessDevice.is()) { continue; } // check for StorageDrive Solid::Device driveDevice; for (Solid::Device currentDevice = accessDevice; currentDevice.isValid(); currentDevice = currentDevice.parent()) { if (currentDevice.is()) { driveDevice = currentDevice; break; } } if (!driveDevice.isValid()) { continue; } const Solid::StorageDrive* const drive = driveDevice.as(); QString driveType; bool isHarddisk = false; switch (drive->driveType()) { // skip these case Solid::StorageDrive::CdromDrive: case Solid::StorageDrive::Floppy: case Solid::StorageDrive::Tape: default: continue; // accept card readers case Solid::StorageDrive::CompactFlash: driveType = i18n("CompactFlash Card Reader"); break; case Solid::StorageDrive::MemoryStick: driveType = i18n("Memory Stick Reader"); break; case Solid::StorageDrive::SmartMedia: driveType = i18n("SmartMedia Card Reader"); break; case Solid::StorageDrive::SdMmc: driveType = i18n("SD / MMC Card Reader"); break; case Solid::StorageDrive::Xd: driveType = i18n("xD Card Reader"); break; case Solid::StorageDrive::HardDisk: // We don't want to list HardDisk partitions, but USB Mass Storage devices. // Don't know what is the exact difference between removable and hotpluggable. if (drive->isRemovable() || drive->isHotpluggable()) { isHarddisk = true; if (drive->bus() == Solid::StorageDrive::Usb) { driveType = i18n("USB Disk"); } else { driveType = i18nc("non-USB removable storage device", "Disk"); } break; } else { continue; } } // check for StorageVolume Solid::Device volumeDevice; for (Solid::Device currentDevice = accessDevice; currentDevice.isValid(); currentDevice = currentDevice.parent()) { if (currentDevice.is()) { volumeDevice = currentDevice; break; } } if (!volumeDevice.isValid()) { continue; } bool isCamera = accessDevice.is(); const Solid::StorageAccess* const access = accessDevice.as(); const Solid::StorageVolume* const volume = volumeDevice.as(); if (volume->isIgnored()) { continue; } QString label; if (isCamera) { label = accessDevice.vendor() + ' ' + accessDevice.product(); } else { QString labelOrProduct; if (!volume->label().isEmpty()) { labelOrProduct = volume->label(); } else if (!volumeDevice.product().isEmpty()) { labelOrProduct = volumeDevice.product(); } else if (!volumeDevice.vendor().isEmpty()) { labelOrProduct = volumeDevice.vendor(); } else if (!driveDevice.product().isEmpty()) { labelOrProduct = driveDevice.product(); } if (!labelOrProduct.isNull()) { if (!access->filePath().isEmpty()) label += i18nc(" \"\" at ", "%1 \"%2\" at %3", driveType, labelOrProduct, QDir::toNativeSeparators(access->filePath())); else label += i18nc(" \"\"", "%1 \"%2\"", driveType, labelOrProduct); } else { if (!access->filePath().isEmpty()) label += i18nc(" at ", "%1 at %2", driveType, QDir::toNativeSeparators(access->filePath())); else { label += driveType; } } if (volume->size()) label += i18nc("device label etc... ()", " (%1)", KGlobal::locale()->formatByteSize(volume->size())); } QString iconName; if (!driveDevice.icon().isEmpty()) { iconName = driveDevice.icon(); } else if (!accessDevice.icon().isEmpty()) { iconName = accessDevice.icon(); } else if (!volumeDevice.icon().isEmpty()) { iconName = volumeDevice.icon(); } KAction* const action = new KAction(label, d->solidUsmActionGroup); if (!iconName.isEmpty()) { action->setIcon(KIcon(iconName)); } // set data to identify device in action slot slotSolidSetupDevice action->setData(accessDevice.udi()); newAppearanceTimes[accessDevice.udi()] = d->cameraAppearanceTimes.contains(accessDevice.udi()) ? d->cameraAppearanceTimes.value(accessDevice.udi()) : QDateTime::currentDateTime(); if (isCamera) { d->cameraMenu->addAction(action); } if (isHarddisk) { d->usbMediaMenu->addAction(action); } else { d->cardReaderMenu->addAction(action); } } /* //TODO: Find best usable solution when no devices are connected: One entry, hide, or disable? // Add one entry telling that no device is available if (d->cameraSolidMenu->isEmpty()) { QAction* const action = d->cameraSolidMenu->addAction(i18n("No Camera Connected")); action->setEnabled(false); } if (d->usbMediaMenu->isEmpty()) { QAction* const action = d->usbMediaMenu->addAction(i18n("No Storage Devices Found")); action->setEnabled(false); } if (d->cardReaderMenu->isEmpty()) { QAction* const action = d->cardReaderMenu->addAction(i18n("No Card Readers Available")); action->setEnabled(false); } // hide empty menus d->cameraSolidMenu->menuAction()->setVisible(!d->cameraSolidMenu->isEmpty()); d->usbMediaMenu->menuAction()->setVisible(!d->usbMediaMenu->isEmpty()); d->cardReaderMenu->menuAction()->setVisible(!d->cardReaderMenu->isEmpty()); */ d->cameraAppearanceTimes = newAppearanceTimes; // disable empty menus d->usbMediaMenu->setEnabled(!d->usbMediaMenu->menu()->isEmpty()); d->cardReaderMenu->setEnabled(!d->cardReaderMenu->menu()->isEmpty()); updateCameraMenu(); updateQuickImportAction(); } void DigikamApp::slotSetup() { setup(); } bool DigikamApp::setup() { return Setup::exec(this, Setup::LastPageUsed); } bool DigikamApp::setupICC() { return Setup::execSinglePage(this, Setup::ICCPage); } void DigikamApp::slotSetupCamera() { Setup::execSinglePage(this, Setup::CameraPage); } void DigikamApp::slotSetupChanged() { // raw loading options might have changed LoadingCacheInterface::cleanCache(); // TODO: clear history when location changed //if(AlbumSettings::instance()->getAlbumLibraryPath() != AlbumManager::instance()->getLibraryPath()) // d->view->clearHistory(); if (!AlbumManager::instance()->databaseEqual(AlbumSettings::instance()->getDatabaseType(), AlbumSettings::instance()->getDatabaseName(), AlbumSettings::instance()->getDatabaseHostName(), AlbumSettings::instance()->getDatabasePort(), AlbumSettings::instance()->getInternalDatabaseServer())) { AlbumManager::instance()->changeDatabase(AlbumSettings::instance()->getDatabaseParameters()); } if (AlbumSettings::instance()->getShowFolderTreeViewItemsCount()) { AlbumManager::instance()->prepareItemCounts(); } // Load full-screen options KConfigGroup group = AlbumSettings::instance()->generalConfigGroup(); readFullScreenSettings(group); d->view->applySettings(); AlbumThumbnailLoader::instance()->setThumbnailSize(AlbumSettings::instance()->getTreeViewIconSize()); if (LightTableWindow::lightTableWindowCreated()) { LightTableWindow::lightTableWindow()->applySettings(); } if (QueueMgrWindow::queueManagerWindowCreated()) { QueueMgrWindow::queueManagerWindow()->applySettings(); } d->config->sync(); } void DigikamApp::slotEditKeys() { KShortcutsDialog dialog(KShortcutsEditor::AllActions, KShortcutsEditor::LetterShortcutsAllowed, this); dialog.addCollection(actionCollection(), i18nc("general keyboard shortcuts", "General")); dialog.addCollection(KipiPluginLoader::instance()->pluginsActionCollection(), i18nc("KIPI-Plugins keyboard shortcuts", "KIPI-Plugins")); dialog.configure(); } void DigikamApp::slotConfToolbars() { saveMainWindowSettings(AlbumSettings::instance()->generalConfigGroup()); KEditToolBar dlg(factory(), this); connect(&dlg, SIGNAL(newToolBarConfig()), this, SLOT(slotNewToolbarConfig())); dlg.exec(); } void DigikamApp::slotNewToolbarConfig() { applyMainWindowSettings(AlbumSettings::instance()->generalConfigGroup()); } void DigikamApp::slotConfNotifications() { KNotifyConfigWidget::configure(this); } void DigikamApp::slotShowTip() { QStringList tipsFiles; tipsFiles.append("digikam/tips"); tipsFiles.append("kipi/tips"); KTipDialog::showMultiTip(this, tipsFiles, true); } void DigikamApp::slotShowKipiHelp() { KToolInvocation::invokeHelp( QString(), "kipi-plugins" ); } void DigikamApp::slotDBStat() { showDigikamDatabaseStat(); } void DigikamApp::loadPlugins() { // Load KIPI plugins new KipiPluginLoader(this, d->splashScreen); // Setting the initial menu options after all plugins have been loaded QList albumList = AlbumManager::instance()->currentAlbums(); d->view->slotAlbumSelected(albumList); // Load Image Editor plugins. new ImagePluginLoader(this, d->splashScreen); } void DigikamApp::populateThemes() { if (d->splashScreen) { d->splashScreen->message(i18n("Loading themes...")); } ThemeManager::instance()->setThemeMenuAction(new KActionMenu(i18n("&Themes"), this)); ThemeManager::instance()->registerThemeActions(this); ThemeManager::instance()->setCurrentTheme(AlbumSettings::instance()->getCurrentTheme()); connect (ThemeManager::instance(), SIGNAL(signalThemeChanged()), this, SLOT(slotThemeChanged())); } void DigikamApp::slotThemeChanged() { AlbumSettings::instance()->setCurrentTheme(ThemeManager::instance()->currentThemeName()); } void DigikamApp::preloadWindows() { if (d->splashScreen) { d->splashScreen->message(i18n("Loading tools...")); } QueueMgrWindow::queueManagerWindow(); ImageWindow::imageWindow(); LightTableWindow::lightTableWindow(); d->tagsActionManager->registerTagsActionCollections(); } void DigikamApp::slotDatabaseMigration() { MigrationDlg dlg(this); dlg.exec(); } void DigikamApp::slotMaintenance() { MaintenanceDlg* const dlg = new MaintenanceDlg(this); if (dlg->exec() == QDialog::Accepted) { d->maintenanceAction->setEnabled(false); MaintenanceMngr* const mngr = new MaintenanceMngr(this); connect(mngr, SIGNAL(signalComplete()), this, SLOT(slotMaintenanceDone())); mngr->setSettings(dlg->settings()); } } void DigikamApp::slotMaintenanceDone() { d->maintenanceAction->setEnabled(true); d->view->refreshView(); if (LightTableWindow::lightTableWindowCreated()) { LightTableWindow::lightTableWindow()->refreshView(); } if (QueueMgrWindow::queueManagerWindowCreated()) { QueueMgrWindow::queueManagerWindow()->refreshView(); } } void DigikamApp::slotRecurseAlbums(bool checked) { d->view->setRecurseAlbums(checked); } void DigikamApp::slotRecurseTags(bool checked) { d->view->setRecurseTags(checked); } void DigikamApp::slotZoomSliderChanged(int size) { d->view->setThumbSize(size); } void DigikamApp::slotThumbSizeChanged(int size) { d->zoomBar->setThumbsSize(size); if (!fullScreenIsActive() && d->autoShowZoomToolTip) { d->zoomBar->triggerZoomTrackerToolTip(); } } void DigikamApp::slotZoomChanged(double zoom) { double zmin = d->view->zoomMin(); double zmax = d->view->zoomMax(); d->zoomBar->setZoom(zoom, zmin, zmax); if (!fullScreenIsActive() && d->autoShowZoomToolTip) { d->zoomBar->triggerZoomTrackerToolTip(); } } void DigikamApp::slotImportAddImages() { QString startingPath; #if KDE_IS_VERSION(4,1,61) startingPath = KGlobalSettings::picturesPath(); #else startingPath = QDesktopServices::storageLocation(QDesktopServices::PicturesLocation); #endif QString path = KFileDialog::getExistingDirectory(startingPath, this, i18n("Select folder to parse")); if (path.isEmpty()) { return; } // The folder contents will be parsed by Camera interface in "Directory Browse" mode. downloadFrom(path); } void DigikamApp::slotImportAddFolders() { QPointer dlg = new KFileDialog(KUrl(), "inode/directory", this); dlg->setCaption(i18n("Select folders to import into album")); dlg->setMode(KFile::Directory | KFile::Files); if (dlg->exec() != QDialog::Accepted) { delete dlg; return; } KUrl::List urls = dlg->selectedUrls(); delete dlg; if (urls.empty()) { return; } QList albumList = AlbumManager::instance()->currentAlbums(); Album* album = 0; if(!albumList.isEmpty()) { album = albumList.first(); } if (album && album->type() != Album::PHYSICAL) { album = 0; } QString header(i18n("

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

")); album = AlbumSelectDialog::selectAlbum(this, (PAlbum*)album, header); if (!album) { return; } PAlbum* const pAlbum = dynamic_cast(album); if (!pAlbum) { return; } DIO::copy(urls, pAlbum); } void DigikamApp::slotToggleShowBar() { d->view->toggleShowBar(d->showBarAction->isChecked()); } void DigikamApp::slotShowMenuBar() { menuBar()->setVisible(d->showMenuBarAction->isChecked()); } void DigikamApp::moveEvent(QMoveEvent*) { emit signalWindowHasMoved(); } void DigikamApp::updateCameraMenu() { d->cameraMenu->menu()->clear(); foreach(QAction* const action, d->solidCameraActionGroup->actions()) { d->cameraMenu->addAction(action); } d->cameraMenu->addSeparator(); foreach(QAction* const action, d->manualCameraActionGroup->actions()) { // remove duplicate entries, prefer manually added cameras foreach(QAction* const actionSolid, d->solidCameraActionGroup->actions()) { if (CameraNameHelper::sameDevices(actionSolid->iconText(), action->iconText())) { d->cameraMenu->removeAction(actionSolid); d->solidCameraActionGroup->removeAction(actionSolid); } } d->cameraMenu->addAction(action); } d->cameraMenu->addSeparator(); d->cameraMenu->addAction(actionCollection()->action("camera_add")); } void DigikamApp::updateQuickImportAction() { d->quickImportMenu->menu()->clear(); foreach(QAction* const action, d->solidCameraActionGroup->actions()) { d->quickImportMenu->addAction(action); } foreach(QAction* const action, d->solidUsmActionGroup->actions()) { d->quickImportMenu->addAction(action); } foreach(QAction* const action, d->manualCameraActionGroup->actions()) { d->quickImportMenu->addAction(action); } if (d->quickImportMenu->menu()->actions().isEmpty()) { d->quickImportMenu->setEnabled(false); } else { disconnect(d->quickImportMenu, SIGNAL(triggered()), 0, 0); QAction* primaryAction = 0; QDateTime latest; foreach(QAction* const action, d->quickImportMenu->menu()->actions()) { QDateTime appearanceTime = d->cameraAppearanceTimes.value(action->data().toString()); if (latest.isNull() || appearanceTime > latest) { primaryAction = action; latest = appearanceTime; } } if (!primaryAction) { primaryAction = d->quickImportMenu->menu()->actions().first(); } connect(d->quickImportMenu, SIGNAL(triggered()), primaryAction, SLOT(trigger())); d->quickImportMenu->setDelayed(d->quickImportMenu->menu()->actions().size() > 1); } } void DigikamApp::setupExifOrientationActions() { QSignalMapper* const exifOrientationMapper = new QSignalMapper(d->view); connect(exifOrientationMapper, SIGNAL(mapped(int)), d->view, SLOT(slotImageExifOrientation(int))); d->imageExifOrientationActionMenu = new KActionMenu(i18n("Adjust Exif Orientation Tag"), this); d->imageExifOrientationActionMenu->setDelayed(false); actionCollection()->addAction("image_set_exif_orientation", d->imageExifOrientationActionMenu); d->imageSetExifOrientation1Action = new KToggleAction(i18nc("normal exif orientation", "Normal"), this); d->imageSetExifOrientation2Action = new KToggleAction(i18n("Flipped Horizontally"), this); d->imageSetExifOrientation3Action = new KToggleAction(i18n("Rotated Upside Down"), this); d->imageSetExifOrientation4Action = new KToggleAction(i18n("Flipped Vertically"), this); d->imageSetExifOrientation5Action = new KToggleAction(i18n("Rotated Right / Horiz. Flipped"), this); d->imageSetExifOrientation6Action = new KToggleAction(i18n("Rotated Right"), this); d->imageSetExifOrientation7Action = new KToggleAction(i18n("Rotated Right / Vert. Flipped"), this); d->imageSetExifOrientation8Action = new KToggleAction(i18n("Rotated Left"), this); 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); actionCollection()->addAction("image_set_exif_orientation_normal", d->imageSetExifOrientation1Action); actionCollection()->addAction("image_set_exif_orientation_flipped_horizontal", d->imageSetExifOrientation2Action); actionCollection()->addAction("image_set_exif_orientation_rotated_upside_down", d->imageSetExifOrientation3Action); actionCollection()->addAction("image_set_exif_orientation_flipped_vertically", d->imageSetExifOrientation4Action); actionCollection()->addAction("image_set_exif_orientation_rotated_right_hor_flipped", d->imageSetExifOrientation5Action); actionCollection()->addAction("image_set_exif_orientation_rotated_right", d->imageSetExifOrientation6Action); actionCollection()->addAction("image_set_exif_orientation_rotated_right_ver_flipped", d->imageSetExifOrientation7Action); actionCollection()->addAction("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::slotResetExifOrientationActions() { d->imageSetExifOrientation1Action->setChecked(false); d->imageSetExifOrientation2Action->setChecked(false); d->imageSetExifOrientation3Action->setChecked(false); d->imageSetExifOrientation4Action->setChecked(false); d->imageSetExifOrientation5Action->setChecked(false); d->imageSetExifOrientation6Action->setChecked(false); d->imageSetExifOrientation7Action->setChecked(false); d->imageSetExifOrientation8Action->setChecked(false); } void DigikamApp::slotSetCheckedExifOrientationAction(const ImageInfo& info) { //DMetadata meta(info.fileUrl().toLocalFile()); //int orientation = (meta.isEmpty()) ? 0 : meta.getImageOrientation(); int orientation = info.orientation(); switch (orientation) { case 1: d->imageSetExifOrientation1Action->setChecked(true); break; case 2: d->imageSetExifOrientation2Action->setChecked(true); break; case 3: d->imageSetExifOrientation3Action->setChecked(true); break; case 4: d->imageSetExifOrientation4Action->setChecked(true); break; case 5: d->imageSetExifOrientation5Action->setChecked(true); break; case 6: d->imageSetExifOrientation6Action->setChecked(true); break; case 7: d->imageSetExifOrientation7Action->setChecked(true); break; case 8: d->imageSetExifOrientation8Action->setChecked(true); break; default: slotResetExifOrientationActions(); break; } } void DigikamApp::setupImageTransformActions() { d->imageRotateActionMenu = new KActionMenu(KIcon("object-rotate-right"), i18n("Rotate"), actionCollection()); d->imageRotateActionMenu->setDelayed(false); KAction* const left = actionCollection()->addAction("rotate_ccw"); left->setText(i18nc("rotate image left", "Left")); left->setShortcut(KShortcut(Qt::SHIFT+Qt::CTRL+Qt::Key_Left)); connect(left, SIGNAL(triggered(bool)), this, SLOT(slotTransformAction())); d->imageRotateActionMenu->addAction(left); KAction* const right = actionCollection()->addAction("rotate_cw"); right->setText(i18nc("rotate image right", "Right")); right->setShortcut(KShortcut(Qt::SHIFT+Qt::CTRL+Qt::Key_Right)); connect(right, SIGNAL(triggered(bool)), this, SLOT(slotTransformAction())); d->imageRotateActionMenu->addAction(right); actionCollection()->addAction("image_rotate", d->imageRotateActionMenu); // ----------------------------------------------------------------------------------- d->imageFlipActionMenu = new KActionMenu(KIcon("flip-horizontal"), i18n("Flip"), actionCollection()); d->imageFlipActionMenu->setDelayed(false); KAction* const hori = actionCollection()->addAction("flip_horizontal"); hori->setText(i18n("Horizontally")); hori->setShortcut(KShortcut(Qt::CTRL+Qt::Key_Asterisk)); connect(hori, SIGNAL(triggered(bool)), this, SLOT(slotTransformAction())); d->imageFlipActionMenu->addAction(hori); KAction* const verti = actionCollection()->addAction("flip_vertical"); verti->setText(i18n("Vertically")); verti->setShortcut(KShortcut(Qt::CTRL+Qt::Key_Slash)); connect(verti, SIGNAL(triggered(bool)), this, SLOT(slotTransformAction())); d->imageFlipActionMenu->addAction(verti); actionCollection()->addAction("image_flip", d->imageFlipActionMenu); // ----------------------------------------------------------------------------------- d->imageAutoExifActionMenu = new KAction(i18n("Auto Rotate/Flip Using Exif Information"), this); connect(d->imageAutoExifActionMenu, SIGNAL(triggered(bool)), this, SLOT(slotTransformAction())); actionCollection()->addAction("image_transform_exif", d->imageAutoExifActionMenu); } void DigikamApp::slotTransformAction() { if (sender()->objectName() == "rotate_ccw") { d->view->imageTransform(KExiv2Iface::RotationMatrix::Rotate270); } else if (sender()->objectName() == "rotate_cw") { d->view->imageTransform(KExiv2Iface::RotationMatrix::Rotate90); } else if (sender()->objectName() == "flip_horizontal") { d->view->imageTransform(KExiv2Iface::RotationMatrix::FlipHorizontal); } else if (sender()->objectName() == "flip_vertical") { d->view->imageTransform(KExiv2Iface::RotationMatrix::FlipVertical); } else if (sender()->objectName() == "image_transform_exif") { // special value for FileActionMngr d->view->imageTransform(KExiv2Iface::RotationMatrix::NoTransformation); } } KActionMenu* DigikamApp::slideShowMenu() const { return d->slideShowAction; } void DigikamApp::rebuild() { QString file = xmlFile(); if (!file.isEmpty()) { setXMLGUIBuildDocument(QDomDocument()); loadStandardsXmlFile(); setXMLFile(file, true); } } void DigikamApp::showSideBars(bool visible) { visible ? d->view->showSideBars() : d->view->hideSideBars(); } void DigikamApp::slotToggleLeftSideBar() { d->view->toggleLeftSidebar(); } void DigikamApp::slotToggleRightSideBar() { d->view->toggleRightSidebar(); } void DigikamApp::slotPreviousLeftSideBarTab() { d->view->previousLeftSideBarTab(); } void DigikamApp::slotNextLeftSideBarTab() { d->view->nextLeftSideBarTab(); } void DigikamApp::slotNextRightSideBarTab() { d->view->nextRightSideBarTab(); } void DigikamApp::slotPreviousRightSideBarTab() { d->view->previousRightSideBarTab(); } void DigikamApp::showThumbBar(bool visible) { view()->toggleShowBar(visible); } bool DigikamApp::thumbbarVisibility() const { return d->showBarAction->isChecked(); } void DigikamApp::slotSwitchedToPreview() { d->imagePreviewAction->setChecked(true); d->zoomBar->setBarMode(DZoomBar::PreviewZoomCtrl); toogleShowBar(); } void DigikamApp::slotSwitchedToIconView() { d->zoomBar->setBarMode(DZoomBar::ThumbsSizeCtrl); d->imageIconViewAction->setChecked(true); toogleShowBar(); } void DigikamApp::slotSwitchedToMapView() { //TODO: Link to map view's zoom actions d->zoomBar->setBarMode(DZoomBar::ThumbsSizeCtrl); d->imageMapViewAction->setChecked(true); toogleShowBar(); } void DigikamApp::slotSwitchedToTableView() { d->zoomBar->setBarMode(DZoomBar::ThumbsSizeCtrl); d->imageTableViewAction->setChecked(true); toogleShowBar(); } void DigikamApp::customizedFullScreenMode(bool set) { statusBarMenuAction()->setEnabled(!set); toolBarMenuAction()->setEnabled(!set); d->showMenuBarAction->setEnabled(!set); set ? d->showBarAction->setEnabled(false) : toogleShowBar(); d->view->toggleFullScreen(set); } void DigikamApp::toogleShowBar() { switch (d->view->viewMode()) { case StackedView::PreviewImageMode: case StackedView::MediaPlayerMode: d->showBarAction->setEnabled(true); break; default: d->showBarAction->setEnabled(false); break; } } void DigikamApp::slotComponentsInfo() { showDigikamComponentsInfo(); } #ifdef USE_SCRIPT_IFACE void DigikamApp::slotScriptConsole() { ScriptIface* const w = new ScriptIface(); w->show(); } #endif } // namespace Digikam diff --git a/digikam/main/digikamui.rc b/digikam/main/digikamui.rc index f6cac6df31..cfcd92289e 100644 --- a/digikam/main/digikamui.rc +++ b/digikam/main/digikamui.rc @@ -1,210 +1,211 @@ - + &Browse &Album T&ag &Image + &Edit &View &Tools I&mport &Export &Settings &Help Main Toolbar diff --git a/digikam/views/digikamview.cpp b/digikam/views/digikamview.cpp index c3a00cc162..ee59516159 100644 --- a/digikam/views/digikamview.cpp +++ b/digikam/views/digikamview.cpp @@ -1,2364 +1,2374 @@ /* ============================================================ * * This file is a part of digiKam project * http://www.digikam.org * * Date : 2002-16-10 * Description : implementation of album view interface. * * Copyright (C) 2002-2005 by Renchi Raju * Copyright (C) 2002-2014 by Gilles Caulier * Copyright (C) 2009-2011 by Johannes Wienke * Copyright (C) 2010-2011 by Andi Clemens * Copyright (C) 2011-2013 by Michael G. Hansen * Copyright (C) 2014 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 "digikamview.moc" // Qt includes #include // KDE includes #include #include #include #include #include #include // Local includes #include "albumhistory.h" #include "albumsettings.h" #include "metadatasynchronizer.h" #include "digikamapp.h" #include "digikamimageview.h" #include "dzoombar.h" #include "imagealbummodel.h" #include "imagedescedittab.h" #include "imagepreviewview.h" #include "imagepropertiessidebardb.h" #include "imagethumbnailbar.h" #include "imageviewutilities.h" #include "filterstatusbar.h" #include "leftsidebarwidgets.h" #include "loadingcacheinterface.h" #include "mapwidgetview.h" #include "mediaplayerview.h" #include "metadatasettings.h" #include "newitemsfinder.h" #include "globals.h" #include "metadatahub.h" #include "fileactionmngr.h" #include "queuemgrwindow.h" #include "scancontroller.h" #include "setup.h" #include "sidebar.h" #include "slideshow.h" #include "slideshowbuilder.h" #include "statusprogressbar.h" #include "filtersidebarwidget.h" #include "tagmodificationhelper.h" #include "imagepropertiesversionstab.h" #include "tagscache.h" #include "searchxml.h" #include "fileactionprogress.h" #include "versionmanagersettings.h" #include "tableview.h" #include "tagsmanager.h" #include "thumbsgenerator.h" #include "albumlabelstreeview.h" #ifdef USE_PRESENTATION_MODE #include "qmlshow.h" #endif // USE_PRESENTATION_MODE namespace Digikam { class DigikamView::Private { public: Private() : needDispatchSelection(false), useAlbumHistory(false), initialAlbumID(0), thumbSize(ThumbnailSize::Medium), dockArea(0), splitter(0), selectionTimer(0), thumbSizeTimer(0), albumFolderSideBar(0), tagViewSideBar(0), labelsSideBar(0), dateViewSideBar(0), timelineSideBar(0), searchSideBar(0), fuzzySearchSideBar(0), gpsSearchSideBar(0), peopleSideBar(0), parent(0), iconView(0), mapView(0), tableView(0), albumManager(0), albumHistory(0), stackedview(0), lastViewMode(StackedView::IconViewMode), albumModificationHelper(0), tagModificationHelper(0), searchModificationHelper(0), leftSideBar(0), rightSideBar(0), filterWidget(0), optionAlbumViewPrefix("AlbumView"), modelCollection(0), labelsSearchHandler(0) { } QString userPresentableAlbumTitle(const QString& album) const; void addPageUpDownActions(DigikamView* const q, QWidget* const w); public: bool needDispatchSelection; bool useAlbumHistory; int initialAlbumID; int thumbSize; QMainWindow* dockArea; SidebarSplitter* splitter; QTimer* selectionTimer; QTimer* thumbSizeTimer; // left side bar AlbumFolderViewSideBarWidget* albumFolderSideBar; TagViewSideBarWidget* tagViewSideBar; LabelsSideBarWidget* labelsSideBar; DateFolderViewSideBarWidget* dateViewSideBar; TimelineSideBarWidget* timelineSideBar; SearchSideBarWidget* searchSideBar; FuzzySearchSideBarWidget* fuzzySearchSideBar; GPSSearchSideBarWidget* gpsSearchSideBar; PeopleSideBarWidget* peopleSideBar; DigikamApp* parent; DigikamImageView* iconView; MapWidgetView* mapView; TableView* tableView; AlbumManager* albumManager; AlbumHistory* albumHistory; StackedView* stackedview; StackedView::StackedViewMode lastViewMode; AlbumModificationHelper* albumModificationHelper; TagModificationHelper* tagModificationHelper; SearchModificationHelper* searchModificationHelper; Sidebar* leftSideBar; ImagePropertiesSideBarDB* rightSideBar; FilterSideBarWidget* filterWidget; QString optionAlbumViewPrefix; QList leftSideBarWidgets; DigikamModelCollection* modelCollection; AlbumLabelsSearchHandler* labelsSearchHandler; }; QString DigikamView::Private::userPresentableAlbumTitle(const QString& title) const { if (title == SAlbum::getTemporaryHaarTitle(DatabaseSearch::HaarSketchSearch)) { return i18n("Fuzzy Sketch Search"); } else if (title == SAlbum::getTemporaryHaarTitle(DatabaseSearch::HaarImageSearch)) { return i18n("Fuzzy Image Search"); } else if (title == SAlbum::getTemporaryTitle(DatabaseSearch::MapSearch)) { return i18n("Map Search"); } else if (title == SAlbum::getTemporaryTitle(DatabaseSearch::AdvancedSearch) || title == SAlbum::getTemporaryTitle(DatabaseSearch::KeywordSearch)) { return i18n("Last Search"); } else if (title == SAlbum::getTemporaryTitle(DatabaseSearch::TimeLineSearch)) { return i18n("Timeline"); } return title; } void DigikamView::Private::addPageUpDownActions(DigikamView* const q, QWidget* const w) { QShortcut* const nextImageShortcut = new QShortcut(w); nextImageShortcut->setKey(Qt::Key_PageDown); nextImageShortcut->setContext(Qt::WidgetWithChildrenShortcut); QObject::connect(nextImageShortcut, SIGNAL(activated()), q, SLOT(slotNextItem())); QShortcut* const prevImageShortcut = new QShortcut(w); prevImageShortcut->setKey(Qt::Key_PageUp); prevImageShortcut->setContext(Qt::WidgetWithChildrenShortcut); QObject::connect(prevImageShortcut, SIGNAL(activated()), q, SLOT(slotPrevItem())); } // ------------------------------------------------------------------------------------------- DigikamView::DigikamView(QWidget* const parent, DigikamModelCollection* const modelCollection) : KHBox(parent), d(new Private) { qRegisterMetaType("SlideShowSettings"); d->parent = static_cast(parent); d->modelCollection = modelCollection; d->albumManager = AlbumManager::instance(); d->albumModificationHelper = new AlbumModificationHelper(this, this); d->tagModificationHelper = new TagModificationHelper(this, this); d->searchModificationHelper = new SearchModificationHelper(this, this); d->splitter = new SidebarSplitter; d->splitter->setFrameStyle( QFrame::NoFrame ); d->splitter->setFrameShadow( QFrame::Plain ); d->splitter->setFrameShape( QFrame::NoFrame ); d->splitter->setOpaqueResize(false); d->leftSideBar = new Sidebar(this, d->splitter, KMultiTabBar::Left); d->leftSideBar->setObjectName("Digikam Left Sidebar"); d->splitter->setParent(this); // The dock area where the thumbnail bar is allowed to go. d->dockArea = new QMainWindow(this, Qt::Widget); d->splitter->addWidget(d->dockArea); d->stackedview = new StackedView(d->dockArea); d->dockArea->setCentralWidget(d->stackedview); d->stackedview->setDockArea(d->dockArea); d->iconView = d->stackedview->imageIconView(); d->mapView = d->stackedview->mapWidgetView(); d->tableView = d->stackedview->tableView(); d->addPageUpDownActions(this, d->stackedview->imagePreviewView()); d->addPageUpDownActions(this, d->stackedview->thumbBar()); d->addPageUpDownActions(this, d->stackedview->mediaPlayerView()); d->rightSideBar = new ImagePropertiesSideBarDB(this, d->splitter, KMultiTabBar::Right, true); d->rightSideBar->setObjectName("Digikam Right Sidebar"); // album folder view d->albumFolderSideBar = new AlbumFolderViewSideBarWidget(d->leftSideBar, d->modelCollection->getAlbumModel(), d->albumModificationHelper); d->leftSideBarWidgets << d->albumFolderSideBar; connect(d->albumFolderSideBar, SIGNAL(signalFindDuplicatesInAlbum(Album*)), this, SLOT(slotNewDuplicatesSearch(Album*))); // Tags sidebar tab contents. d->tagViewSideBar = new TagViewSideBarWidget(d->leftSideBar, d->modelCollection->getTagModel()); d->leftSideBarWidgets << d->tagViewSideBar; connect(d->tagViewSideBar, SIGNAL(signalFindDuplicatesInAlbum(Album*)), this, SLOT(slotNewDuplicatesSearch(Album*))); // Labels sidebar d->labelsSideBar = new LabelsSideBarWidget(d->leftSideBar); d->leftSideBarWidgets << d->labelsSideBar; d->labelsSearchHandler = new AlbumLabelsSearchHandler(d->labelsSideBar->labelsTree()); // date view d->dateViewSideBar = new DateFolderViewSideBarWidget(d->leftSideBar, d->modelCollection->getDateAlbumModel(), d->iconView->imageAlbumFilterModel()); d->leftSideBarWidgets << d->dateViewSideBar; // timeline side bar d->timelineSideBar = new TimelineSideBarWidget(d->leftSideBar, d->modelCollection->getSearchModel(), d->searchModificationHelper); d->leftSideBarWidgets << d->timelineSideBar; // Search sidebar tab contents. d->searchSideBar = new SearchSideBarWidget(d->leftSideBar, d->modelCollection->getSearchModel(), d->searchModificationHelper); d->leftSideBarWidgets << d->searchSideBar; // Fuzzy search d->fuzzySearchSideBar = new FuzzySearchSideBarWidget(d->leftSideBar, d->modelCollection->getSearchModel(), d->searchModificationHelper); d->leftSideBarWidgets << d->fuzzySearchSideBar; d->gpsSearchSideBar = new GPSSearchSideBarWidget(d->leftSideBar, d->modelCollection->getSearchModel(), d->searchModificationHelper, d->iconView->imageFilterModel(),d->iconView->getSelectionModel()); d->leftSideBarWidgets << d->gpsSearchSideBar; // People Sidebar d->peopleSideBar = new PeopleSideBarWidget(d->leftSideBar, d->modelCollection->getTagFacesModel(), d->searchModificationHelper); connect(d->peopleSideBar, SIGNAL(requestFaceMode(bool)), d->iconView, SLOT(setFaceMode(bool))); d->leftSideBarWidgets << d->peopleSideBar; foreach(SidebarWidget* const leftWidget, d->leftSideBarWidgets) { d->leftSideBar->appendTab(leftWidget, leftWidget->getIcon(), leftWidget->getCaption()); connect(leftWidget, SIGNAL(requestActiveTab(SidebarWidget*)), this, SLOT(slotLeftSideBarActivate(SidebarWidget*))); } // To the right. d->addPageUpDownActions(this, d->rightSideBar->imageDescEditTab()); // Tags Filter sidebar tab contents. d->filterWidget = new FilterSideBarWidget(d->rightSideBar, d->modelCollection->getTagFilterModel()); d->rightSideBar->appendTab(d->filterWidget, SmallIcon("view-filter"), i18n("Filters")); // Versions sidebar overlays d->rightSideBar->getFiltersHistoryTab()->addOpenAlbumAction(d->iconView->imageModel()); d->rightSideBar->getFiltersHistoryTab()->addShowHideOverlay(); d->selectionTimer = new QTimer(this); d->selectionTimer->setSingleShot(true); d->selectionTimer->setInterval(75); d->thumbSizeTimer = new QTimer(this); d->thumbSizeTimer->setSingleShot(true); d->thumbSizeTimer->setInterval(300); d->albumHistory = new AlbumHistory(); slotSidebarTabTitleStyleChanged(); setupConnections(); connect(d->rightSideBar->imageDescEditTab()->getNewTagEdit(), SIGNAL(taggingActionFinished()), this, SLOT(slotFocusAndNextImage())); connect(d->rightSideBar, SIGNAL(signalSetupMetadataFilters(int)), this, SLOT(slotSetupMetadataFilters(int))); } DigikamView::~DigikamView() { saveViewState(); delete d->albumHistory; delete d; } void DigikamView::applySettings() { foreach(SidebarWidget* const sidebarWidget, d->leftSideBarWidgets) { sidebarWidget->applySettings(); } d->iconView->imageFilterModel()->setVersionImageFilterSettings(VersionImageFilterSettings(AlbumSettings::instance()->getVersionManagerSettings())); refreshView(); } void DigikamView::refreshView() { d->rightSideBar->refreshTagsView(); } void DigikamView::setupConnections() { // -- DigikamApp connections ---------------------------------- connect(d->parent, SIGNAL(signalEscapePressed()), this, SLOT(slotEscapePreview())); connect(d->parent, SIGNAL(signalEscapePressed()), d->stackedview, SLOT(slotEscapePreview())); connect(d->parent, SIGNAL(signalNextItem()), this, SLOT(slotNextItem())); connect(d->parent, SIGNAL(signalPrevItem()), this, SLOT(slotPrevItem())); connect(d->parent, SIGNAL(signalFirstItem()), this, SLOT(slotFirstItem())); connect(d->parent, SIGNAL(signalLastItem()), this, SLOT(slotLastItem())); connect(d->parent, SIGNAL(signalCutAlbumItemsSelection()), d->iconView, SLOT(cut())); connect(d->parent, SIGNAL(signalCopyAlbumItemsSelection()), d->iconView, SLOT(copy())); connect(d->parent, SIGNAL(signalPasteAlbumItemsSelection()), d->iconView, SLOT(paste())); // -- AlbumManager connections -------------------------------- connect(d->albumManager, SIGNAL(signalAlbumCurrentChanged(QList)), this, SLOT(slotAlbumSelected(QList))); connect(d->albumManager, SIGNAL(signalAllAlbumsLoaded()), this, SLOT(slotAllAlbumsLoaded())); connect(d->albumManager, SIGNAL(signalAlbumsCleared()), this, SLOT(slotAlbumsCleared())); // -- IconView Connections ------------------------------------- connect(d->iconView->model(), SIGNAL(rowsInserted(QModelIndex,int,int)), this, SLOT(slotImageSelected())); connect(d->iconView->model(), SIGNAL(rowsRemoved(QModelIndex,int,int)), this, SLOT(slotImageSelected())); connect(d->iconView->model(), SIGNAL(layoutChanged()), this, SLOT(slotImageSelected())); connect(d->iconView, SIGNAL(selectionChanged()), this, SLOT(slotImageSelected())); connect(d->iconView, SIGNAL(previewRequested(ImageInfo)), this, SLOT(slotTogglePreviewMode(ImageInfo))); connect(d->iconView, SIGNAL(gotoAlbumAndImageRequested(ImageInfo)), this, SLOT(slotGotoAlbumAndItem(ImageInfo))); connect(d->iconView, SIGNAL(gotoDateAndImageRequested(ImageInfo)), this, SLOT(slotGotoDateAndItem(ImageInfo))); connect(d->iconView, SIGNAL(gotoTagAndImageRequested(int)), this, SLOT(slotGotoTagAndItem(int))); connect(d->iconView, SIGNAL(zoomOutStep()), this, SLOT(slotZoomOut())); connect(d->iconView, SIGNAL(zoomInStep()), this, SLOT(slotZoomIn())); connect(d->iconView, SIGNAL(signalPopupTagsView()), d->rightSideBar, SLOT(slotPopupTagsView())); // -- TableView Connections ----------------------------------- connect(d->tableView, SIGNAL(signalPreviewRequested(ImageInfo)), this, SLOT(slotTogglePreviewMode(ImageInfo))); connect(d->tableView, SIGNAL(signalZoomOutStep()), this, SLOT(slotZoomOut())); connect(d->tableView, SIGNAL(signalZoomInStep()), this, SLOT(slotZoomIn())); connect(d->tableView, SIGNAL(signalPopupTagsView()), d->rightSideBar, SLOT(slotPopupTagsView())); connect(d->tableView, SIGNAL(signalGotoAlbumAndImageRequested(ImageInfo)), this, SLOT(slotGotoAlbumAndItem(ImageInfo))); connect(d->tableView, SIGNAL(signalGotoDateAndImageRequested(ImageInfo)), this, SLOT(slotGotoDateAndItem(ImageInfo))); connect(d->tableView, SIGNAL(signalGotoTagAndImageRequested(int)), this, SLOT(slotGotoTagAndItem(int))); // TableView::signalItemsChanged is emitted when something changes in the model that // DigikamView should care about, not only the selection. connect(d->tableView, SIGNAL(signalItemsChanged()), this, SLOT(slotImageSelected())); // -- Sidebar Connections ------------------------------------- connect(d->leftSideBar, SIGNAL(signalChangedTab(QWidget*)), this, SLOT(slotLeftSidebarChangedTab(QWidget*))); connect(d->rightSideBar, SIGNAL(signalFirstItem()), this, SLOT(slotFirstItem())); connect(d->rightSideBar, SIGNAL(signalNextItem()), this, SLOT(slotNextItem())); connect(d->rightSideBar, SIGNAL(signalPrevItem()), this, SLOT(slotPrevItem())); connect(d->rightSideBar, SIGNAL(signalLastItem()), this, SLOT(slotLastItem())); connect(this, SIGNAL(signalNoCurrentItem()), d->rightSideBar, SLOT(slotNoCurrentItem())); connect(d->gpsSearchSideBar, SIGNAL(signalMapSoloItems(QList,QString)), d->iconView->imageFilterModel(), SLOT(setIdWhitelist(QList,QString))); // -- Filter Bars Connections --------------------------------- ImageAlbumFilterModel* const model = d->iconView->imageAlbumFilterModel(); connect(d->filterWidget, SIGNAL(signalTagFilterChanged(QList,QList,ImageFilterSettings::MatchingCondition,bool,QList,QList)), d->iconView->imageFilterModel(), SLOT(setTagFilter(QList,QList,ImageFilterSettings::MatchingCondition,bool,QList,QList))); connect(d->filterWidget, SIGNAL(signalRatingFilterChanged(int,ImageFilterSettings::RatingCondition,bool)), model, SLOT(setRatingFilter(int,ImageFilterSettings::RatingCondition,bool))); connect(d->filterWidget, SIGNAL(signalSearchTextFilterChanged(SearchTextFilterSettings)), model, SLOT(setTextFilter(SearchTextFilterSettings))); connect(model, SIGNAL(filterMatchesForText(bool)), d->filterWidget, SLOT(slotFilterMatchesForText(bool))); connect(d->filterWidget, SIGNAL(signalMimeTypeFilterChanged(int)), model, SLOT(setMimeTypeFilter(int))); connect(d->filterWidget, SIGNAL(signalGeolocationFilterChanged(ImageFilterSettings::GeolocationCondition)), model, SLOT(setGeolocationFilter(ImageFilterSettings::GeolocationCondition))); // -- Preview image widget Connections ------------------------ connect(d->stackedview, SIGNAL(signalNextItem()), this, SLOT(slotNextItem())); connect(d->stackedview, SIGNAL(signalPrevItem()), this, SLOT(slotPrevItem())); connect(d->stackedview, SIGNAL(signalEditItem()), this, SLOT(slotImageEdit())); connect(d->stackedview, SIGNAL(signalDeleteItem()), this, SLOT(slotImageDelete())); connect(d->stackedview, SIGNAL(signalViewModeChanged()), this, SLOT(slotViewModeChanged())); connect(d->stackedview, SIGNAL(signalEscapePreview()), this, SLOT(slotEscapePreview())); connect(d->stackedview, SIGNAL(signalSlideShow()), this, SLOT(slotSlideShowAll())); connect(d->stackedview, SIGNAL(signalZoomFactorChanged(double)), this, SLOT(slotZoomFactorChanged(double))); connect(d->stackedview, SIGNAL(signalInsert2LightTable()), this, SLOT(slotImageAddToLightTable())); connect(d->stackedview, SIGNAL(signalInsert2QueueMgr()), this, SLOT(slotImageAddToCurrentQueue())); connect(d->stackedview, SIGNAL(signalFindSimilar()), this, SLOT(slotImageFindSimilar())); connect(d->stackedview, SIGNAL(signalAddToExistingQueue(int)), this, SLOT(slotImageAddToExistingQueue(int))); connect(d->stackedview, SIGNAL(signalGotoAlbumAndItem(ImageInfo)), this, SLOT(slotGotoAlbumAndItem(ImageInfo))); connect(d->stackedview, SIGNAL(signalGotoDateAndItem(ImageInfo)), this, SLOT(slotGotoDateAndItem(ImageInfo))); connect(d->stackedview, SIGNAL(signalGotoTagAndItem(int)), this, SLOT(slotGotoTagAndItem(int))); // -- FileActionMngr progress --------------- connect(FileActionMngr::instance(), SIGNAL(signalImageChangeFailed(QString,QStringList)), this, SLOT(slotImageChangeFailed(QString,QStringList))); // -- timers --------------- connect(d->selectionTimer, SIGNAL(timeout()), this, SLOT(slotDispatchImageSelected())); connect(d->thumbSizeTimer, SIGNAL(timeout()), this, SLOT(slotThumbSizeEffect()) ); // -- Album Settings ---------------- connect(AlbumSettings::instance(), SIGNAL(setupChanged()), this, SLOT(slotSidebarTabTitleStyleChanged())); // -- Album History ----------------- connect(this, SIGNAL(signalAlbumSelected(bool)), d->albumHistory, SLOT(slotAlbumSelected())); connect(this, SIGNAL(signalImageSelected(ImageInfoList,ImageInfoList)), d->albumHistory, SLOT(slotImageSelected(ImageInfoList))); connect(d->iconView, SIGNAL(currentChanged(ImageInfo)), d->albumHistory, SLOT(slotCurrentChange(ImageInfo))); connect(d->iconView, SIGNAL(gotoAlbumAndImageRequested(ImageInfo)), d->albumHistory, SLOT(slotClearSelectPAlbum(ImageInfo))); connect(d->iconView, SIGNAL(gotoTagAndImageRequested(int)), d->albumHistory, SLOT(slotClearSelectTAlbum(int))); connect(d->iconView->imageModel(), SIGNAL(imageInfosAdded(QList)), d->albumHistory, SLOT(slotAlbumCurrentChanged())); connect(d->albumHistory, SIGNAL(signalSetCurrent(qlonglong)), this, SLOT(slotSetCurrentWhenAvailable(qlonglong))); connect(d->albumHistory, SIGNAL(signalSetSelectedInfos(QList)), d->iconView, SLOT(setSelectedImageInfos(QList))); connect(d->albumManager, SIGNAL(signalAlbumDeleted(Album*)), d->albumHistory, SLOT(slotAlbumDeleted(Album*))); connect(d->albumManager, SIGNAL(signalAlbumsCleared()), d->albumHistory, SLOT(slotAlbumsCleared())); // -- Image versions ---------------- connect(d->rightSideBar->getFiltersHistoryTab(), SIGNAL(imageSelected(ImageInfo)), d->iconView, SLOT(hintAt(ImageInfo))); connect(d->rightSideBar->getFiltersHistoryTab(), SIGNAL(actionTriggered(ImageInfo)), this, SLOT(slotGotoAlbumAndItem(ImageInfo))); } void DigikamView::connectIconViewFilter(FilterStatusBar* const filterbar) { ImageAlbumFilterModel* const model = d->iconView->imageAlbumFilterModel(); connect(model, SIGNAL(filterMatches(bool)), filterbar, SLOT(slotFilterMatches(bool))); connect(model, SIGNAL(filterSettingsChanged(ImageFilterSettings)), filterbar, SLOT(slotFilterSettingsChanged(ImageFilterSettings))); connect(filterbar, SIGNAL(signalResetFilters()), d->filterWidget, SLOT(slotResetFilters())); connect(filterbar, SIGNAL(signalPopupFiltersView()), this, SLOT(slotPopupFiltersView())); } void DigikamView::slotPopupFiltersView() { d->rightSideBar->setActiveTab(d->filterWidget); d->filterWidget->setFocusToTextFilter(); } void DigikamView::loadViewState() { foreach(SidebarWidget* const widget, d->leftSideBarWidgets) { widget->loadState(); } d->filterWidget->loadState(); KSharedConfig::Ptr config = KGlobal::config(); KConfigGroup group = config->group("MainWindow"); // Restore the splitter d->splitter->restoreState(group); // Restore the thumbnail bar dock. QByteArray thumbbarState; thumbbarState = group.readEntry("ThumbbarState", thumbbarState); d->dockArea->restoreState(QByteArray::fromBase64(thumbbarState)); d->initialAlbumID = group.readEntry("InitialAlbumID", 0); d->mapView->loadState(); d->tableView->loadState(); d->rightSideBar->loadState(); } void DigikamView::saveViewState() { KSharedConfig::Ptr config = KGlobal::config(); KConfigGroup group = config->group("MainWindow"); foreach(SidebarWidget* const widget, d->leftSideBarWidgets) { widget->saveState(); } d->filterWidget->saveState(); // Save the splitter states. d->splitter->saveState(group); // Save the position and size of the thumbnail bar. The thumbnail bar dock // needs to be closed explicitly, because when it is floating and visible // (when the user is in image preview mode) when the layout is saved, it // also reappears when restoring the view, while it should always be hidden. d->stackedview->thumbBarDock()->close(); group.writeEntry("ThumbbarState", d->dockArea->saveState().toBase64()); QList albumList = AlbumManager::instance()->currentAlbums(); Album* album = 0; if(!albumList.isEmpty()) { album = albumList.first(); } if (album) { group.writeEntry("InitialAlbumID", album->globalID()); } else { group.writeEntry("InitialAlbumID", 0); } d->mapView->saveState(); d->tableView->saveState(); d->rightSideBar->saveState(); } QList DigikamView::leftSidebarWidgets() const { return d->leftSideBarWidgets; } KUrl::List DigikamView::allUrls() const { /// @todo This functions seems not to be used anywhere right now switch (viewMode()) { case StackedView::TableViewMode: return d->tableView->allUrls(); default: return d->iconView->urls(); } } KUrl::List DigikamView::selectedUrls() const { switch (viewMode()) { case StackedView::TableViewMode: return d->tableView->selectedUrls(); default: return d->iconView->selectedUrls(); } } void DigikamView::showSideBars() { d->leftSideBar->restore(); d->rightSideBar->restore(); } void DigikamView::hideSideBars() { d->leftSideBar->backup(); d->rightSideBar->backup(); } void DigikamView::toggleLeftSidebar() { d->leftSideBar->isExpanded() ? d->leftSideBar->shrink() : d->leftSideBar->expand(); } void DigikamView::toggleRightSidebar() { d->rightSideBar->isExpanded() ? d->rightSideBar->shrink() : d->rightSideBar->expand(); } void DigikamView::previousLeftSideBarTab() { d->leftSideBar->activePreviousTab(); } void DigikamView::nextLeftSideBarTab() { d->leftSideBar->activeNextTab(); } void DigikamView::previousRightSideBarTab() { d->rightSideBar->activePreviousTab(); } void DigikamView::nextRightSideBarTab() { d->rightSideBar->activeNextTab(); } void DigikamView::slotFirstItem() { switch (viewMode()) { case StackedView::TableViewMode: d->tableView->slotGoToRow(0, false); break; default: // all other views are tied to IconView's selection model d->iconView->toFirstIndex(); } } void DigikamView::slotPrevItem() { switch (viewMode()) { case StackedView::TableViewMode: d->tableView->slotGoToRow(-1, true); break; default: // all other views are tied to IconView's selection model d->iconView->toPreviousIndex(); } } void DigikamView::slotNextItem() { switch (viewMode()) { case StackedView::TableViewMode: d->tableView->slotGoToRow(1, true); break; default: // all other views are tied to IconView's selection model d->iconView->toNextIndex(); } } void DigikamView::slotLastItem() { switch (viewMode()) { case StackedView::TableViewMode: d->tableView->slotGoToRow(-1, false); break; default: // all other views are tied to IconView's selection model d->iconView->toLastIndex(); } } void DigikamView::slotSelectItemByUrl(const KUrl& url) { /// @todo This functions seems not to be used anywhere right now /// @todo Adapt to TableView d->iconView->toIndex(url); } void DigikamView::slotAllAlbumsLoaded() { disconnect(d->albumManager, SIGNAL(signalAllAlbumsLoaded()), this, SLOT(slotAllAlbumsLoaded())); loadViewState(); d->leftSideBar->loadState(); d->rightSideBar->loadState(); d->rightSideBar->populateTags(); // now that all albums have been loaded, activate the albumHistory d->useAlbumHistory = true; Album* const album = d->albumManager->findAlbum(d->initialAlbumID); d->albumManager->setCurrentAlbums(QList() << album); } void DigikamView::slotSortAlbums(int order) { AlbumSettings* settings = AlbumSettings::instance(); if (!settings) { return; } settings->setAlbumSortOrder((AlbumSettings::AlbumSortOrder) order); settings->saveSettings(); //A dummy way to force the tree view to resort if the album sort role changed PAlbum* albumBeforeSorting = d->albumFolderSideBar->currentAlbum(); settings->setAlbumSortChanged(true); d->albumFolderSideBar->doSaveState(); d->albumFolderSideBar->doLoadState(); d->albumFolderSideBar->doSaveState(); d->albumFolderSideBar->doLoadState(); settings->setAlbumSortChanged(false); if (d->leftSideBar->getActiveTab() == d->albumFolderSideBar) { d->albumFolderSideBar->setCurrentAlbum(albumBeforeSorting); } } void DigikamView::slotNewAlbum() { // TODO use the selection model of the view instead d->albumModificationHelper->slotAlbumNew(d->albumFolderSideBar->currentAlbum()); } void DigikamView::slotDeleteAlbum() { d->albumModificationHelper->slotAlbumDelete(d->albumFolderSideBar->currentAlbum()); } void DigikamView::slotRenameAlbum() { d->albumModificationHelper->slotAlbumRename(d->albumFolderSideBar->currentAlbum()); } void DigikamView::slotNewTag() { d->tagModificationHelper->slotTagNew(d->tagViewSideBar->currentAlbum()); } void DigikamView::slotDeleteTag() { d->tagModificationHelper->slotTagDelete(d->tagViewSideBar->currentAlbum()); } void DigikamView::slotEditTag() { d->tagModificationHelper->slotTagEdit(d->tagViewSideBar->currentAlbum()); } void DigikamView::slotOpenTagsManager() { TagsManager* tagMngr = TagsManager::instance(); tagMngr->show(); tagMngr->activateWindow(); tagMngr->raise(); } void DigikamView::slotAssignTag() { ImageDescEditTab* imageDescEditTab = d->rightSideBar->imageDescEditTab(); //activate image properties tab d->rightSideBar->setActiveTab(imageDescEditTab); //activate tags tab on properties tab imageDescEditTab->setFocusToNewTagEdit(); } void DigikamView::slotNewKeywordSearch() { slotLeftSideBarActivate(d->searchSideBar); d->searchSideBar->newKeywordSearch(); } void DigikamView::slotNewAdvancedSearch() { slotLeftSideBarActivate(d->searchSideBar); d->searchSideBar->newAdvancedSearch(); } void DigikamView::slotNewDuplicatesSearch(Album* album) { slotLeftSideBarActivate(d->fuzzySearchSideBar); d->fuzzySearchSideBar->newDuplicatesSearch(album); } void DigikamView::slotAlbumsCleared() { emit signalAlbumSelected(false); } void DigikamView::slotAlbumHistoryBack(int steps) { QList albums; QWidget* widget = 0; d->albumHistory->back(albums, &widget, steps); changeAlbumFromHistory(albums, widget); } void DigikamView::slotAlbumHistoryForward(int steps) { QList albums; QWidget* widget = 0; d->albumHistory->forward(albums, &widget, steps); changeAlbumFromHistory(albums , widget); } // TODO update, use SideBarWidget instead of QWidget void DigikamView::changeAlbumFromHistory(QList album, QWidget* const widget) { if (!(album.isEmpty()) && widget) { // TODO update, temporary casting until signature is changed SidebarWidget* const sideBarWidget = dynamic_cast(widget); if (sideBarWidget) { sideBarWidget->changeAlbumFromHistory(album); slotLeftSideBarActivate(sideBarWidget); if(sideBarWidget == d->labelsSideBar) { d->labelsSearchHandler->restoreSelectionFromHistory(d->albumHistory->neededLabels()); } } d->parent->enableAlbumBackwardHistory(d->useAlbumHistory && !d->albumHistory->isBackwardEmpty()); d->parent->enableAlbumForwardHistory(d->useAlbumHistory && !d->albumHistory->isForwardEmpty()); } } void DigikamView::clearHistory() { d->albumHistory->clearHistory(); d->parent->enableAlbumBackwardHistory(false); d->parent->enableAlbumForwardHistory(false); } void DigikamView::getBackwardHistory(QStringList& titles) { d->albumHistory->getBackwardHistory(titles); for (int i = 0; i < titles.size(); ++i) { titles[i] = d->userPresentableAlbumTitle(titles.at(i)); } } void DigikamView::getForwardHistory(QStringList& titles) { d->albumHistory->getForwardHistory(titles); for (int i = 0; i < titles.size(); ++i) { titles[i] = d->userPresentableAlbumTitle(titles.at(i)); } } void DigikamView::slotGotoAlbumAndItem(const ImageInfo& imageInfo) { kDebug() << "going to " << imageInfo; emit signalNoCurrentItem(); PAlbum* const album = AlbumManager::instance()->findPAlbum(imageInfo.albumId()); d->albumFolderSideBar->setCurrentAlbum(album); slotLeftSideBarActivate(d->albumFolderSideBar); // Set the activate item url to find in the Album View after // all items have be reloaded. slotSetCurrentWhenAvailable(imageInfo.id()); // And finally toggle album manager to handle album history and // reload all items. d->albumManager->setCurrentAlbums(QList() << album); } void DigikamView::slotGotoDateAndItem(const ImageInfo& imageInfo) { QDate date = imageInfo.dateTime().date(); emit signalNoCurrentItem(); // Change to Date Album view. // Note, that this also opens the side bar if it is closed; this is // considered as a feature, because it highlights that the view was changed. slotLeftSideBarActivate(d->dateViewSideBar); // Set the activate item url to find in the Album View after // all items have be reloaded. slotSetCurrentWhenAvailable(imageInfo.id()); // Change the year and month of the iconItem (day is unused). d->dateViewSideBar->gotoDate(date); } void DigikamView::slotGotoTagAndItem(int tagID) { // FIXME: Arnd: don't know yet how to get the iconItem passed through ... // then we would know how to use the following ... // KURL url( iconItem->imageInfo()->kurl() ); // url.cleanPath(); emit signalNoCurrentItem(); // Change to Tag Folder view. // Note, that this also opens the side bar if it is closed; this is // considered as a feature, because it highlights that the view was changed. slotLeftSideBarActivate(d->tagViewSideBar); // Set the current tag in the tag folder view. // TODO this slot should use a TAlbum pointer directly TAlbum* const tag = AlbumManager::instance()->findTAlbum(tagID); if (tag) { d->tagViewSideBar->setCurrentAlbum(tag); } else { kError() << "Could not find a tag album for tag id " << tagID; } // Set the activate item url to find in the Tag View after // all items have be reloaded. // FIXME: see above // d->iconView->setAlbumItemToFind(url); } void DigikamView::slotSelectAlbum(const KUrl& url) { PAlbum* const album = d->albumManager->findPAlbum(url); if (!album) { kWarning() << "Unable to find album for " << url; return; } slotLeftSideBarActivate(d->albumFolderSideBar); d->albumFolderSideBar->setCurrentAlbum(album); } void DigikamView::slotAlbumSelected(QList albums) { emit signalNoCurrentItem(); if (albums.isEmpty() || !(albums.first())) { d->iconView->openAlbum(QList()); d->mapView->openAlbum(0); emit signalAlbumSelected(false); emit signalTagSelected(false); slotTogglePreviewMode(ImageInfo()); return; } Album* album = albums.first(); if (album->type() == Album::PHYSICAL) { emit signalAlbumSelected(true); emit signalTagSelected(false); } else if (album->type() == Album::TAG) { emit signalAlbumSelected(false); /* kDebug()<<"Album "<title()<<" selected." ; // Now loop through children of the people album and check if this album is a child. Album* peopleAlbum = AlbumManager::instance()->findTAlbum(TagsCache::instance()->tagForPath("/People")); int thisAlbumId = album->id(); QList children = peopleAlbum->childAlbumIds(true); foreach(int id, children) { if(id == thisAlbumId) { kDebug()<<"Is a people tag"; showFaceAlbum(thisAlbumId); emit signalTagSelected(true); return; } } */ emit signalTagSelected(true); } else { emit signalAlbumSelected(false); emit signalTagSelected(false); } if (d->useAlbumHistory && !d->labelsSearchHandler->isRestoringSelectionFromHistory()) { if(!(d->leftSideBar->getActiveTab() == d->labelsSideBar)) { d->albumHistory->addAlbums(albums, d->leftSideBar->getActiveTab()); } else { if(albums.first()->isUsedByLabelsTree()) { d->albumHistory-> addAlbums(albums, d->leftSideBar->getActiveTab(), d->labelsSideBar->selectedLabels()); } } } d->parent->enableAlbumBackwardHistory(d->useAlbumHistory && !d->albumHistory->isBackwardEmpty()); d->parent->enableAlbumForwardHistory(d->useAlbumHistory && !d->albumHistory->isForwardEmpty()); d->iconView->openAlbum(albums); if (album->isRoot()) { d->stackedview->setViewMode(StackedView::WelcomePageMode); } else { switch (viewMode()) { case StackedView::PreviewImageMode: case StackedView::MediaPlayerMode: case StackedView::WelcomePageMode: slotTogglePreviewMode(ImageInfo()); break; default: break; } } } void DigikamView::slotAlbumOpenInFileManager() { Album* const album = d->albumManager->currentAlbums().first(); if (!album || album->type() != Album::PHYSICAL) { return; } if (album->isRoot()) { KMessageBox::error(this, i18n("Cannot open the root album. It is not a physical location.")); return; } PAlbum* const palbum = dynamic_cast(album); if (palbum) { new KRun(KUrl(palbum->folderPath()), this); // KRun will delete itself. } } void DigikamView::slotAlbumOpenInTerminal() { Album* const album = d->albumManager->currentAlbums().first(); if (!album || album->type() != Album::PHYSICAL) { return; } if (album->isRoot()) { KMessageBox::error(this, i18n("Cannot open the root. It is not a physical location.")); return; } PAlbum* const palbum = dynamic_cast(album); if (!palbum) { return; } QString dir(palbum->folderPath()); // If the given directory is not local, it can still be the URL of an // ioslave using UDS_LOCAL_PATH which to be converted first. KUrl url = KIO::NetAccess::mostLocalUrl(dir, this); //If the URL is local after the above conversion, set the directory. if (url.isLocalFile()) { dir = url.toLocalFile(); } KToolInvocation::invokeTerminal(QString(), dir); } void DigikamView::slotRefresh() { switch (viewMode()) { case StackedView::PreviewImageMode: d->stackedview->imagePreviewView()->reload(); break; case StackedView::MediaPlayerMode: d->stackedview->mediaPlayerView()->reload(); break; default: Album* const album = d->iconView->currentAlbum(); if (!album) return; // force reloading of thumbnails LoadingCacheInterface::cleanThumbnailCache(); ThumbsGenerator* const tool = new ThumbsGenerator(true, album->id()); tool->start(); // if physical album, schedule a collection scan of current album's path if (album->type() == Album::PHYSICAL) { NewItemsFinder* const tool = new NewItemsFinder(NewItemsFinder::ScheduleCollectionScan, QStringList() << static_cast(album)->folderPath()); connect(tool, SIGNAL(signalComplete()), this, SLOT(slotAlbumRefreshComplete())); tool->start(); } break; } } void DigikamView::slotAlbumRefreshComplete() { // force reload. Should normally not be necessary, but we may have bugs qlonglong currentId = currentInfo().id(); d->iconView->imageAlbumModel()->refresh(); if (currentId != -1) { slotSetCurrentWhenAvailable(currentId); } } void DigikamView::slotImageSelected() { // delay to slotDispatchImageSelected d->needDispatchSelection = true; d->selectionTimer->start(); switch (viewMode()) { case StackedView::TableViewMode: emit signalSelectionChanged(d->tableView->numberOfSelectedItems()); break; default: emit signalSelectionChanged(d->iconView->numberOfSelectedIndexes()); } } void DigikamView::slotDispatchImageSelected() { if (d->needDispatchSelection) { // the list of ImageInfos of currently selected items, currentItem first // since the iconView tracks the changes also while we are in map widget mode, // we can still pull the data from the iconView const ImageInfoList list = selectedInfoList(true); const ImageInfoList allImages = allInfo(); if (list.isEmpty()) { d->stackedview->setPreviewItem(); emit signalImageSelected(list, allImages); emit signalNoCurrentItem(); } else { d->rightSideBar->itemChanged(list); ImageInfo previousInfo; ImageInfo nextInfo; if (viewMode() == StackedView::TableViewMode) { previousInfo = d->tableView->previousInfo(); nextInfo = d->tableView->nextInfo(); } else { previousInfo = d->iconView->previousInfo(list.first()); nextInfo = d->iconView->nextInfo(list.first()); } if ((viewMode() != StackedView::IconViewMode) && (viewMode() != StackedView::MapWidgetMode) && (viewMode() != StackedView::TableViewMode) ) { d->stackedview->setPreviewItem(list.first(), previousInfo, nextInfo); } emit signalImageSelected(list, allImages); } d->needDispatchSelection = false; } } double DigikamView::zoomMin() const { return d->stackedview->zoomMin(); } double DigikamView::zoomMax() const { return d->stackedview->zoomMax(); } void DigikamView::setZoomFactor(double zoom) { d->stackedview->setZoomFactorSnapped(zoom); } void DigikamView::slotZoomFactorChanged(double zoom) { toggleZoomActions(); emit signalZoomChanged(zoom); } void DigikamView::setThumbSize(int size) { if (viewMode() == StackedView::PreviewImageMode) { double z = DZoomBar::zoomFromSize(size, zoomMin(), zoomMax()); setZoomFactor(z); } else if ( (viewMode() == StackedView::IconViewMode) || (viewMode() == StackedView::TableViewMode) ) { if (size > ThumbnailSize::maxThumbsSize()) { d->thumbSize = ThumbnailSize::maxThumbsSize(); } else if (size < ThumbnailSize::Small) { d->thumbSize = ThumbnailSize::Small; } else { d->thumbSize = size; } emit signalThumbSizeChanged(d->thumbSize); d->thumbSizeTimer->start(); } } void DigikamView::slotThumbSizeEffect() { d->iconView->setThumbnailSize(d->thumbSize); d->tableView->setThumbnailSize(d->thumbSize); toggleZoomActions(); AlbumSettings::instance()->setDefaultIconSize(d->thumbSize); } void DigikamView::toggleZoomActions() { if (viewMode() == StackedView::PreviewImageMode) { d->parent->enableZoomMinusAction(true); d->parent->enableZoomPlusAction(true); if (d->stackedview->maxZoom()) { d->parent->enableZoomPlusAction(false); } if (d->stackedview->minZoom()) { d->parent->enableZoomMinusAction(false); } } else if ( (viewMode() == StackedView::IconViewMode) || (viewMode() == StackedView::TableViewMode) ) { d->parent->enableZoomMinusAction(true); d->parent->enableZoomPlusAction(true); if (d->thumbSize >= ThumbnailSize::maxThumbsSize()) { d->parent->enableZoomPlusAction(false); } if (d->thumbSize <= ThumbnailSize::Small) { d->parent->enableZoomMinusAction(false); } } else { d->parent->enableZoomMinusAction(false); d->parent->enableZoomPlusAction(false); } } void DigikamView::slotZoomIn() { if ( (viewMode() == StackedView::IconViewMode) || (viewMode() == StackedView::TableViewMode) ) { setThumbSize(d->thumbSize + ThumbnailSize::Step); toggleZoomActions(); emit signalThumbSizeChanged(d->thumbSize); } else if (viewMode() == StackedView::PreviewImageMode) { d->stackedview->increaseZoom(); } } void DigikamView::slotZoomOut() { if ( (viewMode() == StackedView::IconViewMode) || (viewMode() == StackedView::TableViewMode) ) { setThumbSize(d->thumbSize - ThumbnailSize::Step); toggleZoomActions(); emit signalThumbSizeChanged(d->thumbSize); } else if (viewMode() == StackedView::PreviewImageMode) { d->stackedview->decreaseZoom(); } } void DigikamView::slotZoomTo100Percents() { if (viewMode() == StackedView::PreviewImageMode) { d->stackedview->toggleFitToWindowOr100(); } } void DigikamView::slotFitToWindow() { if (viewMode() == StackedView::TableViewMode) { /// @todo We should choose an appropriate thumbnail size here } else if (viewMode() == StackedView::IconViewMode) { int nts = d->iconView->fitToWidthIcons(); kDebug() << "new thumb size = " << nts; setThumbSize(nts); toggleZoomActions(); emit signalThumbSizeChanged(d->thumbSize); } else if (viewMode() == StackedView::PreviewImageMode) { d->stackedview->fitToWindow(); } } void DigikamView::slotAlbumPropsEdit() { d->albumModificationHelper->slotAlbumEdit(d->albumManager->currentPAlbum()); } void DigikamView::slotAlbumWriteMetadata() { Album* const album = d->albumManager->currentAlbums().first(); if (!album) { return; } MetadataSynchronizer* const tool = new MetadataSynchronizer(AlbumList() << album, MetadataSynchronizer::WriteFromDatabaseToFile); tool->start(); } void DigikamView::slotAlbumReadMetadata() { Album* const album = d->albumManager->currentAlbums().first(); if (!album) { return; } MetadataSynchronizer* const tool = new MetadataSynchronizer(AlbumList() << album, MetadataSynchronizer::ReadFromFileToDatabase); tool->start(); } void DigikamView::slotImageWriteMetadata() { const ImageInfoList selected = selectedInfoList(); MetadataSynchronizer* const tool = new MetadataSynchronizer(selected, MetadataSynchronizer::WriteFromDatabaseToFile); tool->start(); } void DigikamView::slotImageReadMetadata() { const ImageInfoList selected = selectedInfoList(); MetadataSynchronizer* const tool = new MetadataSynchronizer(selected, MetadataSynchronizer::ReadFromFileToDatabase); tool->start(); } // ---------------------------------------------------------------- void DigikamView::slotEscapePreview() { if (viewMode() == StackedView::IconViewMode || viewMode() == StackedView::MapWidgetMode || viewMode() == StackedView::TableViewMode || viewMode() == StackedView::WelcomePageMode) { return; } // pass a null image info, because we want to fall back to the old // view mode slotTogglePreviewMode(ImageInfo()); } void DigikamView::slotMapWidgetView() { d->stackedview->setViewMode(StackedView::MapWidgetMode); } void DigikamView::slotTableView() { d->stackedview->setViewMode(StackedView::TableViewMode); } void DigikamView::slotIconView() { if (viewMode() == StackedView::PreviewImageMode) { emit signalThumbSizeChanged(d->iconView->thumbnailSize().size()); } // and switch to icon view d->stackedview->setViewMode(StackedView::IconViewMode); // make sure the next/previous buttons are updated slotImageSelected(); } void DigikamView::slotImagePreview() { slotTogglePreviewMode(currentInfo()); } /** * @brief This method toggles between AlbumView/MapWidgetView and ImagePreview modes, depending on the context. */ void DigikamView::slotTogglePreviewMode(const ImageInfo& info) { if ( (viewMode() == StackedView::IconViewMode || viewMode() == StackedView::TableViewMode || viewMode() == StackedView::MapWidgetMode) && !info.isNull() ) { d->lastViewMode = viewMode(); if (viewMode() == StackedView::IconViewMode) { d->stackedview->setPreviewItem(info, d->iconView->previousInfo(info), d->iconView->nextInfo(info)); } else { d->stackedview->setPreviewItem(info, ImageInfo(), ImageInfo()); } } else { // go back to either AlbumViewMode or MapWidgetMode d->stackedview->setViewMode( d->lastViewMode ); } // make sure the next/previous buttons are updated slotImageSelected(); } void DigikamView::slotViewModeChanged() { toggleZoomActions(); switch (viewMode()) { case StackedView::IconViewMode: emit signalSwitchedToIconView(); emit signalThumbSizeChanged(d->iconView->thumbnailSize().size()); break; case StackedView::PreviewImageMode: emit signalSwitchedToPreview(); slotZoomFactorChanged(d->stackedview->zoomFactor()); break; case StackedView::WelcomePageMode: emit signalSwitchedToIconView(); break; case StackedView::MediaPlayerMode: emit signalSwitchedToPreview(); break; case StackedView::MapWidgetMode: emit signalSwitchedToMapView(); //TODO: connect map view's zoom buttons to main status bar zoom buttons break; case StackedView::TableViewMode: emit signalSwitchedToTableView(); emit signalThumbSizeChanged(d->tableView->getThumbnailSize().size()); break; } } void DigikamView::slotImageFindSimilar() { const ImageInfo current = currentInfo(); if (!current.isNull()) { d->fuzzySearchSideBar->newSimilarSearch(current); slotLeftSideBarActivate(d->fuzzySearchSideBar); } } void DigikamView::slotEditor() { const ImageInfoList imageInfoList = selectedInfoList(); - ImageInfo singleInfo = currentInfo(); + ImageInfo singleInfo = currentInfo(); + if (singleInfo.isNull() && !imageInfoList.isEmpty()) { singleInfo = imageInfoList.first(); } // the current album is the same for all views Album* const currentAlbum = d->iconView->currentAlbum(); d->iconView->utilities()->openFile(singleInfo, imageInfoList, currentAlbum); } +void DigikamView::slotFileWithDefaultApplication() +{ + d->iconView->utilities()->openFilesWithDefaultApplication(selectedInfoList()); +} + void DigikamView::slotLightTable() { const ImageInfoList allInfoList = allInfo(); const ImageInfoList selectedList = selectedInfoList(); const ImageInfo currentImageInfo = currentInfo(); d->iconView->utilities()->insertToLightTableAuto(allInfoList, selectedList, currentImageInfo); } void DigikamView::slotQueueMgr() { ImageInfoList imageInfoList = selectedInfoList(); ImageInfo singleInfo = currentInfo(); + if (singleInfo.isNull() && !imageInfoList.isEmpty()) { singleInfo = imageInfoList.first(); } + if (singleInfo.isNull()) { const ImageInfoList allItems = allInfo(); + if (!allItems.isEmpty()) { singleInfo = allItems.first(); } } + d->iconView->utilities()->insertToQueueManager(imageInfoList, singleInfo, true); } void DigikamView::slotImageEdit() { // Where is the difference to slotEditor? slotEditor(); } void DigikamView::slotImageLightTable() { const ImageInfoList selectedList = selectedInfoList(); const ImageInfo currentImageInfo = currentInfo(); // replace images in light table d->iconView->utilities()->insertToLightTable(selectedList, currentImageInfo, false); } void DigikamView::slotImageAddToLightTable() { const ImageInfoList selectedList = selectedInfoList(); const ImageInfo currentImageInfo = currentInfo(); // add to images in light table d->iconView->utilities()->insertToLightTable(selectedList, currentImageInfo, true); } void DigikamView::slotImageAddToCurrentQueue() { const ImageInfoList selectedList = selectedInfoList(); const ImageInfo currentImageInfo = currentInfo(); d->iconView->utilities()->insertToQueueManager(selectedList, currentImageInfo, false); } void DigikamView::slotImageAddToNewQueue() { const bool newQueue = QueueMgrWindow::queueManagerWindowCreated() && !QueueMgrWindow::queueManagerWindow()->queuesMap().isEmpty(); const ImageInfoList selectedList = selectedInfoList(); const ImageInfo currentImageInfo = currentInfo(); d->iconView->utilities()->insertToQueueManager(selectedList, currentImageInfo, newQueue); } void DigikamView::slotImageAddToExistingQueue(int queueid) { const ImageInfoList selectedList = selectedInfoList(); const ImageInfo currentImageInfo = currentInfo(); if (!selectedList.isEmpty()) { d->iconView->utilities()->insertSilentToQueueManager(selectedList, currentImageInfo, queueid); } } void DigikamView::slotImageRename() { d->iconView->rename(); } void DigikamView::slotImageDelete() { switch (viewMode()) { case StackedView::TableViewMode: d->tableView->slotDeleteSelected(ImageViewUtilities::DeleteUseTrash); break; default: d->iconView->deleteSelected(ImageViewUtilities::DeleteUseTrash); } } void DigikamView::slotImageDeletePermanently() { switch (viewMode()) { case StackedView::TableViewMode: d->tableView->slotDeleteSelected(ImageViewUtilities::DeletePermanently); break; default: d->iconView->deleteSelected(ImageViewUtilities::DeletePermanently); } } void DigikamView::slotImageDeletePermanentlyDirectly() { switch (viewMode()) { case StackedView::TableViewMode: d->tableView->slotDeleteSelectedWithoutConfirmation(ImageViewUtilities::DeletePermanently); break; default: d->iconView->deleteSelectedDirectly(ImageViewUtilities::DeletePermanently); } } void DigikamView::slotImageTrashDirectly() { switch (viewMode()) { case StackedView::TableViewMode: d->tableView->slotDeleteSelectedWithoutConfirmation(ImageViewUtilities::DeleteUseTrash); break; default: d->iconView->deleteSelectedDirectly(ImageViewUtilities::DeleteUseTrash); } } void DigikamView::slotSelectAll() { switch (viewMode()) { case StackedView::TableViewMode: d->tableView->selectAll(); break; default: d->iconView->selectAll(); } } void DigikamView::slotSelectNone() { switch (viewMode()) { case StackedView::TableViewMode: d->tableView->clearSelection(); break; default: d->iconView->clearSelection(); } } void DigikamView::slotSelectInvert() { switch (viewMode()) { case StackedView::TableViewMode: d->tableView->invertSelection(); break; default: d->iconView->invertSelection(); } } void DigikamView::slotSortImages(int sortRole) { AlbumSettings* const settings = AlbumSettings::instance(); if (!settings) { return; } settings->setImageSortOrder(sortRole); d->iconView->imageFilterModel()->setSortRole((ImageSortSettings::SortRole) sortRole); } void DigikamView::slotSortImagesOrder(int order) { AlbumSettings* const settings = AlbumSettings::instance(); if (!settings) { return; } settings->setImageSorting(order); d->iconView->imageFilterModel()->setSortOrder((ImageSortSettings::SortOrder) order); } void DigikamView::slotGroupImages(int categoryMode) { AlbumSettings* const settings = AlbumSettings::instance(); if (!settings) { return; } settings->setImageGroupMode(categoryMode); d->iconView->imageFilterModel()->setCategorizationMode((ImageSortSettings::CategorizationMode) categoryMode); } void DigikamView::slotSortImageGroupOrder(int order) { AlbumSettings* const settings = AlbumSettings::instance(); if (!settings) { return; } settings->setImageGroupSortOrder(order); d->iconView->imageFilterModel()->setCategorizationSortOrder((ImageSortSettings::SortOrder) order); } void DigikamView::slotMoveSelectionToAlbum() { d->iconView->createNewAlbumForSelected(); } void DigikamView::slotLeftSidebarChangedTab(QWidget* w) { // TODO update, temporary cast SidebarWidget* const widget = dynamic_cast(w); foreach(SidebarWidget* const sideBarWidget, d->leftSideBarWidgets) { bool active = (widget && (widget == sideBarWidget)); sideBarWidget->setActive(active); } } void DigikamView::toggleTag(int tagID) { ImageInfoList tagToRemove, tagToAssign; const ImageInfoList selectedList = selectedInfoList(); foreach(ImageInfo info, selectedList) { if (info.tagIds().contains(tagID)) tagToRemove.append(info); else tagToAssign.append(info); } FileActionMngr::instance()->assignTags(tagToAssign, QList() << tagID); FileActionMngr::instance()->removeTags(tagToRemove, QList() << tagID); } void DigikamView::slotAssignPickLabel(int pickId) { FileActionMngr::instance()->assignPickLabel(selectedInfoList(), pickId); } void DigikamView::slotAssignColorLabel(int colorId) { FileActionMngr::instance()->assignColorLabel(selectedInfoList(), colorId); } void DigikamView::slotAssignRating(int rating) { FileActionMngr::instance()->assignRating(selectedInfoList(), rating); } void DigikamView::slotSlideShowAll() { slideShow(allInfo()); } void DigikamView::slotSlideShowSelection() { slideShow(selectedInfoList()); } void DigikamView::slotSlideShowRecursive() { QList albumList = AlbumManager::instance()->currentAlbums(); Album* album = 0; if(!albumList.isEmpty()) { album = albumList.first(); } if (album) { SlideShowBuilder* const builder = new SlideShowBuilder(album); connect(builder, SIGNAL(signalComplete(SlideShowSettings)), this, SLOT(slotSlideShowBuilderComplete(SlideShowSettings))); } } void DigikamView::slideShow(const ImageInfoList& infoList) { SlideShowBuilder* const builder = new SlideShowBuilder(infoList); connect(builder, SIGNAL(signalComplete(SlideShowSettings)), this, SLOT(slotSlideShowBuilderComplete(SlideShowSettings))); } void DigikamView::slotSlideShowBuilderComplete(const SlideShowSettings& settings) { SlideShow* const slide = new SlideShow(settings); if (settings.startWithCurrent) { slide->setCurrent(currentUrl()); } connect(slide, SIGNAL(signalRatingChanged(KUrl,int)), this, SLOT(slotRatingChanged(KUrl,int))); connect(slide, SIGNAL(signalColorLabelChanged(KUrl,int)), this, SLOT(slotColorLabelChanged(KUrl,int))); connect(slide, SIGNAL(signalPickLabelChanged(KUrl,int)), this, SLOT(slotPickLabelChanged(KUrl,int))); slide->show(); } void DigikamView::toggleShowBar(bool b) { d->stackedview->thumbBarDock()->showThumbBar(b); // See B.K.O #319876 : force to reload current view mode to set thumbbar visibility properly. d->stackedview->setViewMode(viewMode()); } void DigikamView::setRecurseAlbums(bool recursive) { d->iconView->imageAlbumModel()->setRecurseAlbums(recursive); } void DigikamView::setRecurseTags(bool recursive) { d->iconView->imageAlbumModel()->setRecurseTags(recursive); } void DigikamView::slotSidebarTabTitleStyleChanged() { d->leftSideBar->setStyle(AlbumSettings::instance()->getSidebarTitleStyle()); d->rightSideBar->setStyle(AlbumSettings::instance()->getSidebarTitleStyle()); /// @todo Which settings actually have to be reloaded? // d->rightSideBar->applySettings(); } void DigikamView::slotImageChangeFailed(const QString& message, const QStringList& fileNames) { if (fileNames.isEmpty()) { return; } KMessageBox::errorList(0, message, fileNames); } void DigikamView::slotLeftSideBarActivateAlbums() { d->leftSideBar->setActiveTab(d->albumFolderSideBar); } void DigikamView::slotLeftSideBarActivateTags() { d->leftSideBar->setActiveTab(d->tagViewSideBar); } void DigikamView::slotLeftSideBarActivate(SidebarWidget* widget) { d->leftSideBar->setActiveTab(widget); } void DigikamView::slotLeftSideBarActivate(QWidget* widget) { slotLeftSideBarActivate(static_cast(widget)); } void DigikamView::slotRatingChanged(const KUrl& url, int rating) { rating = qMin(RatingMax, qMax(RatingMin, rating)); ImageInfo info(url); if (!info.isNull()) { MetadataHub hub; hub.load(info); hub.setRating(rating); hub.write(info, MetadataHub::PartialWrite); hub.write(info.filePath(), MetadataHub::FullWriteIfChanged); } } void DigikamView::slotColorLabelChanged(const KUrl& url, int color) { ImageInfo info(url); if (!info.isNull()) { MetadataHub hub; hub.load(info); hub.setColorLabel(color); hub.write(info, MetadataHub::PartialWrite); hub.write(info.filePath(), MetadataHub::FullWriteIfChanged); } } void DigikamView::slotPickLabelChanged(const KUrl& url, int pick) { ImageInfo info(url); if (!info.isNull()) { MetadataHub hub; hub.load(info); hub.setPickLabel(pick); hub.write(info, MetadataHub::PartialWrite); hub.write(info.filePath(), MetadataHub::FullWriteIfChanged); } } bool DigikamView::hasCurrentItem() const { return !currentInfo().isNull(); } void DigikamView::slotFocusAndNextImage() { //slot is called on pressing "return" a second time after assigning a tag d->stackedview->currentWidget()->setFocus(); //select next image, since the user is probably done tagging the current image d->iconView->toNextIndex(); } void DigikamView::slotImageExifOrientation(int orientation) { FileActionMngr::instance()->setExifOrientation(selectedInfoList(), orientation); } void DigikamView::imageTransform(RotationMatrix::TransformationAction transform) { FileActionMngr::instance()->transform(selectedInfoList(), transform); } ImageInfo DigikamView::currentInfo() const { switch (viewMode()) { case StackedView::TableViewMode: return d->tableView->currentInfo(); case StackedView::MapWidgetMode: return d->mapView->currentImageInfo(); case StackedView::MediaPlayerMode: case StackedView::PreviewImageMode: case StackedView::IconViewMode: // all of these modes use the same selection model and data as the IconViewMode return d->iconView->currentInfo(); default: return ImageInfo(); } } QList< ImageInfo > DigikamView::selectedInfoList(const bool currentFirst) const { switch (viewMode()) { case StackedView::TableViewMode: if (currentFirst) { return d->tableView->selectedImageInfosCurrentFirst(); } return d->tableView->selectedImageInfos(); case StackedView::PreviewImageMode: case StackedView::MediaPlayerMode: case StackedView::MapWidgetMode: case StackedView::IconViewMode: // all of these modes use the same selection model and data as the IconViewMode if (currentFirst) { return d->iconView->selectedImageInfosCurrentFirst(); } return d->iconView->selectedImageInfos(); default: return QList(); } } ImageInfoList DigikamView::allInfo() const { switch (viewMode()) { case StackedView::TableViewMode: return d->tableView->allInfo(); case StackedView::MapWidgetMode: case StackedView::PreviewImageMode: case StackedView::MediaPlayerMode: case StackedView::IconViewMode: // all of these modes use the same selection model and data as the IconViewMode return d->iconView->imageInfos(); default: return QList(); } } KUrl DigikamView::currentUrl() const { const ImageInfo cInfo = currentInfo(); return cInfo.fileUrl(); } void DigikamView::slotSetCurrentWhenAvailable(const qlonglong id) { switch (viewMode()) { case StackedView::TableViewMode: d->tableView->slotSetCurrentWhenAvailable(id); break; default: d->iconView->setCurrentWhenAvailable(id); } } void DigikamView::slotAwayFromSelection() { switch (viewMode()) { case StackedView::TableViewMode: d->tableView->slotAwayFromSelection(); break; default: d->iconView->awayFromSelection(); } } StackedView::StackedViewMode DigikamView::viewMode() const { return d->stackedview->viewMode(); } void DigikamView::slotSetupMetadataFilters(int tab) { Setup::execMetadataFilters(this, tab); } void DigikamView::toggleFullScreen(bool set) { d->stackedview->imagePreviewView()->toggleFullScreen(set); } #ifdef USE_PRESENTATION_MODE void DigikamView::slotSlideShowQml() { /* QStringList list; foreach (const ImageInfo& info, d->iconView->imageInfos()) { list << info.filePath(); } */ SlideShowSettings settings; settings.readFromConfig(); QmlShow* const qmlShow = new QmlShow(allInfo(), settings); qmlShow->setWindowState(Qt::WindowFullScreen); qmlShow->show(); } #endif // USE_PRESENTATION_MODE } // namespace Digikam diff --git a/digikam/views/digikamview.h b/digikam/views/digikamview.h index f05fc7a04b..558af02ad5 100644 --- a/digikam/views/digikamview.h +++ b/digikam/views/digikamview.h @@ -1,287 +1,288 @@ /* ============================================================ * * This file is a part of digiKam project * http://www.digikam.org * * Date : 2002-16-10 * Description : implementation of album view interface. * * Copyright (C) 2002-2005 by Renchi Raju * Copyright (C) 2002-2014 by Gilles Caulier * Copyright (C) 2009-2011 by Johannes Wienke * Copyright (C) 2010-2011 by Andi Clemens * * This program is free software; you can redistribute it * and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation; * either version 2, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * ============================================================ */ #ifndef DIGIKAMVIEW_H #define DIGIKAMVIEW_H // Qt includes #include #include #include // KDE includes #include #include // libkexiv2 includes #include // Local includes #include "config-digikam.h" #include "searchtextbar.h" #include "imageinfo.h" #include "digikammodelcollection.h" #include "sidebarwidget.h" #include "stackedview.h" using namespace KExiv2Iface; namespace Digikam { class AlbumIconItem; class AlbumSettings; class Album; class BatchSyncMetadata; class FilterStatusBar; class SlideShowSettings; class DigikamView : public KHBox { Q_OBJECT public: DigikamView(QWidget* const parent, DigikamModelCollection* const modelCollection); ~DigikamView(); void applySettings(); void refreshView(); void clearHistory(); void getForwardHistory(QStringList& titles); void getBackwardHistory(QStringList& titles); void showSideBars(); void hideSideBars(); void toggleLeftSidebar(); void toggleRightSidebar(); void previousLeftSideBarTab(); void nextLeftSideBarTab(); void previousRightSideBarTab(); void nextRightSideBarTab(); void setThumbSize(int size); void toggleShowBar(bool); void setRecurseAlbums(bool recursive); void setRecurseTags(bool recursive); void imageTransform(RotationMatrix::TransformationAction transform); void connectIconViewFilter(FilterStatusBar* const filter); KUrl::List allUrls() const; KUrl::List selectedUrls() const; KUrl currentUrl() const; bool hasCurrentItem() const; ImageInfo currentInfo() const; QList selectedInfoList(const bool currentFirst = false) const; ImageInfoList allInfo() const; double zoomMin() const; double zoomMax() const; void toggleTag(int tagID); void toggleFullScreen(bool set); QList leftSidebarWidgets() const; StackedView::StackedViewMode viewMode() const; Q_SIGNALS: void signalAlbumSelected(bool val); void signalTagSelected(bool val); void signalImageSelected(const ImageInfoList& selectedImage, const ImageInfoList& allImages); void signalNoCurrentItem(); void signalSelectionChanged(int numberOfSelectedItems); void signalThumbSizeChanged(int); void signalZoomChanged(double); void signalSwitchedToPreview(); void signalSwitchedToIconView(); void signalSwitchedToMapView(); void signalSwitchedToTableView(); void signalGotoAlbumAndItem(const ImageInfo&); void signalGotoDateAndItem(AlbumIconItem*); void signalGotoTagAndItem(int tagID); void signalChangedTab(QWidget*); public Q_SLOTS: void setZoomFactor(double zoom); // View Action slots void slotZoomIn(); void slotZoomOut(); void slotZoomTo100Percents(); void slotFitToWindow(); void slotSlideShowAll(); void slotSlideShowSelection(); void slotSlideShowRecursive(); // Album action slots void slotRefresh(); void slotNewAlbum(); void slotSortAlbums(int order); void slotDeleteAlbum(); void slotRenameAlbum(); void slotAlbumPropsEdit(); void slotAlbumOpenInFileManager(); void slotAlbumOpenInTerminal(); void slotAlbumHistoryBack(int steps=1); void slotAlbumHistoryForward(int steps=1); void slotAlbumWriteMetadata(); void slotAlbumReadMetadata(); void slotAlbumSelected(QList albums); void slotGotoAlbumAndItem(const ImageInfo& imageInfo); void slotGotoDateAndItem(const ImageInfo& imageInfo); void slotGotoTagAndItem(int tagID); void slotSelectAlbum(const KUrl& url); void slotSetCurrentWhenAvailable(const qlonglong id); // Tag action slots void slotNewTag(); void slotDeleteTag(); void slotEditTag(); void slotOpenTagsManager(); void slotAssignTag(); // Search action slots void slotNewKeywordSearch(); void slotNewAdvancedSearch(); void slotNewDuplicatesSearch(Album* album=0); // Image action slots void slotImageLightTable(); void slotImageAddToLightTable(); void slotImageAddToCurrentQueue(); void slotImageAddToNewQueue(); void slotImageAddToExistingQueue(int); void slotImagePreview(); void slotMapWidgetView(); void slotTableView(); void slotIconView(); void slotImageEdit(); void slotImageFindSimilar(); void slotImageExifOrientation(int orientation); void slotImageRename(); void slotImageDelete(); void slotImageDeletePermanently(); void slotImageDeletePermanentlyDirectly(); void slotImageTrashDirectly(); void slotImageWriteMetadata(); void slotImageReadMetadata(); void slotSelectAll(); void slotSelectNone(); void slotSelectInvert(); void slotSortImages(int order); void slotSortImagesOrder(int order); void slotGroupImages(int mode); void slotSortImageGroupOrder(int order); void slotMoveSelectionToAlbum(); void slotAssignPickLabel(int pickId); void slotAssignColorLabel(int colorId); void slotAssignRating(int rating); // Tools action slots. void slotEditor(); void slotLightTable(); void slotQueueMgr(); + void slotFileWithDefaultApplication(); void slotLeftSideBarActivate(QWidget* widget); void slotLeftSideBarActivate(SidebarWidget* widget); void slotLeftSideBarActivateAlbums(); void slotLeftSideBarActivateTags(); void slotFocusAndNextImage(); #ifdef USE_PRESENTATION_MODE void slotSlideShowQml(); #endif // USE_PRESENTATION_MODE private: void toggleZoomActions(); void setupConnections(); void loadViewState(); void saveViewState(); void changeAlbumFromHistory(QList album, QWidget* const widget); void slideShow(const ImageInfoList& infoList); private Q_SLOTS: void slotAllAlbumsLoaded(); void slotAlbumsCleared(); void slotImageSelected(); void slotTogglePreviewMode(const ImageInfo& info); void slotDispatchImageSelected(); void slotLeftSidebarChangedTab(QWidget* w); void slotFirstItem(); void slotPrevItem(); void slotNextItem(); void slotLastItem(); void slotSelectItemByUrl(const KUrl&); void slotAwayFromSelection(); void slotViewModeChanged(); void slotEscapePreview(); void slotSlideShowBuilderComplete(const SlideShowSettings& settings); void slotThumbSizeEffect(); void slotZoomFactorChanged(double); void slotSidebarTabTitleStyleChanged(); void slotImageChangeFailed(const QString& message, const QStringList& fileNames); void slotRatingChanged(const KUrl&, int); void slotColorLabelChanged(const KUrl&, int); void slotPickLabelChanged(const KUrl&, int); void slotPopupFiltersView(); void slotSetupMetadataFilters(int); void slotAlbumRefreshComplete(); private: class Private; Private* const d; }; } // namespace Digikam #endif // DIGIKAMVIEW_H diff --git a/digikam/views/imagepreviewview.cpp b/digikam/views/imagepreviewview.cpp index 3f163e8f71..2e5fe35adf 100644 --- a/digikam/views/imagepreviewview.cpp +++ b/digikam/views/imagepreviewview.cpp @@ -1,563 +1,564 @@ /* ============================================================ * * This file is a part of digiKam project * http://www.digikam.org * * Date : 2006-21-12 * Description : a embedded view to show the image preview widget. * * Copyright (C) 2006-2014 by Gilles Caulier * Copyright (C) 2009-2012 by Andi Clemens * Copyright (C) 2010-2011 by Aditya Bhatt * * 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 "imagepreviewview.moc" // Qt includes #include #include #include #include #include #include #include // KDE includes #include #include #include #include #include #include #include #include #include #include #include #include #include #include // Local includes #include "imagepreviewviewitem.h" #include "albumsettings.h" #include "contextmenuhelper.h" #include "ddragobjects.h" #include "digikamapp.h" #include "dimg.h" #include "dimgpreviewitem.h" #include "facegroup.h" #include "imageinfo.h" #include "fileactionmngr.h" #include "metadatasettings.h" #include "regionframeitem.h" #include "tagspopupmenu.h" #include "thememanager.h" #include "previewlayout.h" #include "tagscache.h" #include "imagetagpair.h" #include "albummanager.h" namespace Digikam { class ImagePreviewView::Private { public: Private() { peopleTagsShown = false; fullSize = 0; scale = 1.0; item = 0; isValid = false; rotationLock = false; toolBar = 0; prevAction = 0; nextAction = 0; rotLeftAction = 0; rotRightAction = 0; peopleToggleAction = 0; addPersonAction = 0; faceGroup = 0; forgetFacesAction = 0; mode = ImagePreviewView::IconViewPreview; } bool peopleTagsShown; bool fullSize; double scale; bool isValid; bool rotationLock; ImagePreviewView::Mode mode; ImagePreviewViewItem* item; QAction* prevAction; QAction* nextAction; QAction* rotLeftAction; QAction* rotRightAction; KToggleAction* peopleToggleAction; QAction* addPersonAction; QAction* forgetFacesAction; QToolBar* toolBar; FaceGroup* faceGroup; }; ImagePreviewView::ImagePreviewView(QWidget* const parent, Mode mode) : GraphicsDImgView(parent), d(new Private) { d->mode = mode; d->item = new ImagePreviewViewItem(); setItem(d->item); d->faceGroup = new FaceGroup(this); d->faceGroup->setShowOnHover(true); d->item->setFaceGroup(d->faceGroup); connect(d->item, SIGNAL(loaded()), this, SLOT(imageLoaded())); connect(d->item, SIGNAL(loadingFailed()), this, SLOT(imageLoadingFailed())); connect(d->item, SIGNAL(showContextMenu(QGraphicsSceneContextMenuEvent*)), this, SLOT(slotShowContextMenu(QGraphicsSceneContextMenuEvent*))); // set default zoom layout()->fitToWindow(); // ------------------------------------------------------------ installPanIcon(); setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); // ------------------------------------------------------------ d->prevAction = new QAction(SmallIcon("go-previous"), i18nc("go to previous image", "Back"), this); d->nextAction = new QAction(SmallIcon("go-next"), i18nc("go to next image", "Forward"), this); d->rotLeftAction = new QAction(SmallIcon("object-rotate-left"), i18nc("@info:tooltip", "Rotate Left"), this); d->rotRightAction = new QAction(SmallIcon("object-rotate-right"), i18nc("@info:tooltip", "Rotate Right"), this); d->addPersonAction = new QAction(SmallIcon("list-add-user"), i18n("Add a Face Tag"), this); d->forgetFacesAction = new QAction(SmallIcon("list-remove-user"), i18n("Clear all faces on this image"), this); d->peopleToggleAction = new KToggleAction(i18n("Show Face Tags"), this); d->peopleToggleAction->setIcon(SmallIcon("user-identity")); d->toolBar = new QToolBar(this); if (mode == IconViewPreview) { d->toolBar->addAction(d->prevAction); d->toolBar->addAction(d->nextAction); } d->toolBar->addAction(d->rotLeftAction); d->toolBar->addAction(d->rotRightAction); d->toolBar->addAction(d->peopleToggleAction); d->toolBar->addAction(d->addPersonAction); connect(d->prevAction, SIGNAL(triggered()), this, SIGNAL(toPreviousImage())); connect(d->nextAction, SIGNAL(triggered()), this, SIGNAL(toNextImage())); connect(d->rotLeftAction, SIGNAL(triggered()), this, SLOT(slotRotateLeft())); connect(d->rotRightAction, SIGNAL(triggered()), this, SLOT(slotRotateRight())); connect(d->peopleToggleAction, SIGNAL(toggled(bool)), d->faceGroup, SLOT(setVisible(bool))); connect(d->addPersonAction, SIGNAL(triggered()), d->faceGroup, SLOT(addFace())); connect(d->forgetFacesAction, SIGNAL(triggered()), d->faceGroup, SLOT(rejectAll())); // ------------------------------------------------------------ connect(this, SIGNAL(toNextImage()), this, SIGNAL(signalNextItem())); connect(this, SIGNAL(toPreviousImage()), this, SIGNAL(signalPrevItem())); connect(this, SIGNAL(leftButtonDoubleClicked()), this, SIGNAL(signalEscapePreview())); connect(ThemeManager::instance(), SIGNAL(signalThemeChanged()), this, SLOT(slotThemeChanged())); connect(AlbumSettings::instance(), SIGNAL(setupChanged()), this, SLOT(slotSetupChanged())); slotSetupChanged(); } ImagePreviewView::~ImagePreviewView() { delete d->item; delete d; } void ImagePreviewView::reload() { previewItem()->reload(); } void ImagePreviewView::imageLoaded() { emit signalPreviewLoaded(true); d->rotLeftAction->setEnabled(true); d->rotRightAction->setEnabled(true); d->faceGroup->setInfo(d->item->imageInfo()); connect(d->item, SIGNAL(imageChanged()), this, SLOT(slotUpdateFaces())); } void ImagePreviewView::imageLoadingFailed() { emit signalPreviewLoaded(false); d->rotLeftAction->setEnabled(false); d->rotRightAction->setEnabled(false); d->faceGroup->setInfo(ImageInfo()); } void ImagePreviewView::setImageInfo(const ImageInfo& info, const ImageInfo& previous, const ImageInfo& next) { d->faceGroup->aboutToSetInfo(info); d->item->setImageInfo(info); d->prevAction->setEnabled(!previous.isNull()); d->nextAction->setEnabled(!next.isNull()); QStringList previewPaths; if (next.category() == DatabaseItem::Image) { previewPaths << next.filePath(); } if (previous.category() == DatabaseItem::Image) { previewPaths << previous.filePath(); } d->item->setPreloadPaths(previewPaths); } ImageInfo ImagePreviewView::getImageInfo() const { return d->item->imageInfo(); } bool ImagePreviewView::acceptsMouseClick(QMouseEvent* e) { if (!GraphicsDImgView::acceptsMouseClick(e)) { return false; } return d->faceGroup->acceptsMouseClick(mapToScene(e->pos())); } void ImagePreviewView::enterEvent(QEvent* e) { d->faceGroup->enterEvent(e); } void ImagePreviewView::leaveEvent(QEvent* e) { d->faceGroup->leaveEvent(e); } void ImagePreviewView::showEvent(QShowEvent* e) { GraphicsDImgView::showEvent(e); d->faceGroup->setVisible(d->peopleToggleAction->isChecked()); } void ImagePreviewView::slotShowContextMenu(QGraphicsSceneContextMenuEvent* event) { ImageInfo info = d->item->imageInfo(); if (info.isNull()) { return; } event->accept(); QList idList; idList << info.id(); KUrl::List selectedItems; selectedItems << info.fileUrl(); // -------------------------------------------------------- KMenu popmenu(this); ContextMenuHelper cmhelper(&popmenu); cmhelper.addAction("full_screen"); cmhelper.addAction("options_show_menubar"); cmhelper.addSeparator(); // -------------------------------------------------------- if (d->mode == IconViewPreview) { cmhelper.addAction(d->prevAction, true); cmhelper.addAction(d->nextAction, true); cmhelper.addGotoMenu(idList); cmhelper.addSeparator(); } // -------------------------------------------------------- cmhelper.addAction(d->peopleToggleAction, true); cmhelper.addAction(d->addPersonAction, true); cmhelper.addAction(d->forgetFacesAction, true); cmhelper.addSeparator(); // -------------------------------------------------------- cmhelper.addAction("image_edit"); + cmhelper.addAction("open_with_default_application"); cmhelper.addServicesMenu(selectedItems); cmhelper.addAction("image_rotate"); cmhelper.addSeparator(); // -------------------------------------------------------- cmhelper.addAction("image_find_similar"); if (d->mode == IconViewPreview) { cmhelper.addStandardActionLightTable(); } cmhelper.addQueueManagerMenu(); cmhelper.addSeparator(); // -------------------------------------------------------- cmhelper.addStandardActionItemDelete(this, SLOT(slotDeleteItem())); cmhelper.addSeparator(); // -------------------------------------------------------- cmhelper.addAssignTagsMenu(idList); cmhelper.addRemoveTagsMenu(idList); cmhelper.addSeparator(); // -------------------------------------------------------- cmhelper.addLabelsAction(); // special action handling -------------------------------- connect(&cmhelper, SIGNAL(signalAssignTag(int)), this, SLOT(slotAssignTag(int))); connect(&cmhelper, SIGNAL(signalPopupTagsView()), this, SIGNAL(signalPopupTagsView())); connect(&cmhelper, SIGNAL(signalRemoveTag(int)), this, SLOT(slotRemoveTag(int))); connect(&cmhelper, SIGNAL(signalAssignPickLabel(int)), this, SLOT(slotAssignPickLabel(int))); connect(&cmhelper, SIGNAL(signalAssignColorLabel(int)), this, SLOT(slotAssignColorLabel(int))); connect(&cmhelper, SIGNAL(signalAssignRating(int)), this, SLOT(slotAssignRating(int))); connect(&cmhelper, SIGNAL(signalAddToExistingQueue(int)), this, SIGNAL(signalAddToExistingQueue(int))); connect(&cmhelper, SIGNAL(signalGotoTag(int)), this, SIGNAL(signalGotoTagAndItem(int))); connect(&cmhelper, SIGNAL(signalGotoAlbum(ImageInfo)), this, SIGNAL(signalGotoAlbumAndItem(ImageInfo))); connect(&cmhelper, SIGNAL(signalGotoDate(ImageInfo)), this, SIGNAL(signalGotoDateAndItem(ImageInfo))); cmhelper.exec(event->screenPos()); } void ImagePreviewView::slotAssignTag(int tagID) { FileActionMngr::instance()->assignTag(d->item->imageInfo(), tagID); } void ImagePreviewView::slotRemoveTag(int tagID) { FileActionMngr::instance()->removeTag(d->item->imageInfo(), tagID); } void ImagePreviewView::slotAssignPickLabel(int pickId) { FileActionMngr::instance()->assignPickLabel(d->item->imageInfo(), pickId); } void ImagePreviewView::slotAssignColorLabel(int colorId) { FileActionMngr::instance()->assignColorLabel(d->item->imageInfo(), colorId); } void ImagePreviewView::slotAssignRating(int rating) { FileActionMngr::instance()->assignRating(d->item->imageInfo(), rating); } void ImagePreviewView::slotThemeChanged() { QPalette plt(palette()); plt.setColor(backgroundRole(), kapp->palette().color(QPalette::Base)); setPalette(plt); } void ImagePreviewView::slotSetupChanged() { previewItem()->setLoadFullImageSize(AlbumSettings::instance()->getPreviewLoadFullImageSize()); d->toolBar->setVisible(AlbumSettings::instance()->getPreviewShowIcons()); setShowText(AlbumSettings::instance()->getPreviewShowIcons()); // pass auto-suggest? } void ImagePreviewView::slotRotateLeft() { if(d->rotationLock) return; d->rotationLock = true; /** * Setting lock won't allow mouse hover events in ImagePreviewViewItem class */ d->item->setAcceptHoverEvents(false); /** * aboutToSetInfo will delete all face tags from FaceGroup storage */ d->faceGroup->aboutToSetInfo(ImageInfo()); FileActionMngr::instance()->transform(QList() << d->item->imageInfo(), KExiv2Iface::RotationMatrix::Rotate270); } void ImagePreviewView::slotRotateRight() { if(d->rotationLock) return; d->rotationLock = true; /** * Setting lock won't allow mouse hover events in ImagePreviewViewItem class */ d->item->setAcceptHoverEvents(false); /** * aboutToSetInfo will delete all face tags from FaceGroup storage */ d->faceGroup->aboutToSetInfo(ImageInfo()); FileActionMngr::instance()->transform(QList() << d->item->imageInfo(), KExiv2Iface::RotationMatrix::Rotate90); } void ImagePreviewView::slotDeleteItem() { emit signalDeleteItem(); } void Digikam::ImagePreviewView::slotUpdateFaces() { d->faceGroup->aboutToSetInfo(ImageInfo()); d->item->setAcceptHoverEvents(true); /** * Release rotation lock after rotation */ d->rotationLock = false; } void ImagePreviewView::dragMoveEvent(QDragMoveEvent* e) { if (DTagListDrag::canDecode(e->mimeData())) { e->accept(); return; } e->ignore(); } void ImagePreviewView::dragEnterEvent(QDragEnterEvent* e) { if (DTagListDrag::canDecode(e->mimeData())) { e->accept(); return; } e->ignore(); } void ImagePreviewView::dropEvent(QDropEvent* e) { if (DTagListDrag::canDecode(e->mimeData())) { QList tagIDs; if (!DTagListDrag::decode(e->mimeData(), tagIDs)) { return; } KMenu popMenu(this); QAction* const assignToThisAction = popMenu.addAction(SmallIcon("tag"), i18n("Assign Tags to &This Item")); popMenu.addSeparator(); popMenu.addAction(SmallIcon("dialog-cancel"), i18n("&Cancel")); popMenu.setMouseTracking(true); QAction* const choice = popMenu.exec(this->mapToGlobal(e->pos())); if(choice == assignToThisAction) { FileActionMngr::instance()->assignTags(d->item->imageInfo(),tagIDs); } } e->accept(); return; } } // namespace Digikam diff --git a/digikam/views/tableview/tableview.cpp b/digikam/views/tableview/tableview.cpp index 8f9285262d..2cf2a269e4 100644 --- a/digikam/views/tableview/tableview.cpp +++ b/digikam/views/tableview/tableview.cpp @@ -1,759 +1,760 @@ /* ============================================================ * * This file is a part of digiKam project * http://www.digikam.org * * Date : 2013-02-11 * Description : Table view * * Copyright (C) 2013 by Michael G. Hansen * * 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 "tableview.moc" // Qt includes #include #include #include #include // KDE includes #include #include #include #include // local includes #include "contextmenuhelper.h" #include "digikam2kgeomap_database.h" #include "fileactionmngr.h" #include "album.h" #include "imageviewutilities.h" #include "tableview_columnfactory.h" #include "tableview_model.h" #include "tableview_selection_model_syncer.h" #include "tableview_shared.h" #include "tableview_treeview.h" namespace Digikam { class ImageAlbumModel; class ImageFilterModel; class TableView::Private { public: Private() : columnProfiles(), thumbnailSize(), imageViewUtilities(0) { } QList columnProfiles; ThumbnailSize thumbnailSize; ImageViewUtilities* imageViewUtilities; }; TableView::TableView( QItemSelectionModel* const selectionModel, KCategorizedSortFilterProxyModel* const imageFilterModel, QWidget* const parent ) : QWidget(parent), StateSavingObject(this), d(new Private()), s(new TableViewShared()) { s->isActive = false; s->tableView = this; s->thumbnailLoadThread = new ThumbnailLoadThread(this); s->imageFilterModel = dynamic_cast(imageFilterModel); s->imageModel = dynamic_cast(imageFilterModel->sourceModel()); s->imageFilterSelectionModel = selectionModel; s->columnFactory = new TableViewColumnFactory(s.data(), this); QVBoxLayout* const vbox1 = new QVBoxLayout(); s->tableViewModel = new TableViewModel(s.data(), this); s->tableViewSelectionModel = new QItemSelectionModel(s->tableViewModel); s->tableViewSelectionModelSyncer= new TableViewSelectionModelSyncer(s.data(), this); s->treeView = new TableViewTreeView(s.data(), this); s->treeView->installEventFilter(this); d->imageViewUtilities = new ImageViewUtilities(this); connect(s->treeView, SIGNAL(activated(QModelIndex)), this, SLOT(slotItemActivated(QModelIndex))); connect(s->treeView, SIGNAL(signalZoomInStep()), this, SIGNAL(signalZoomInStep())); connect(s->treeView, SIGNAL(signalZoomOutStep()), this, SIGNAL(signalZoomOutStep())); connect(s->tableViewSelectionModel, SIGNAL(selectionChanged(QItemSelection,QItemSelection)), this, SIGNAL(signalItemsChanged())); connect(s->tableViewModel, SIGNAL(rowsInserted(QModelIndex,int,int)), this, SIGNAL(signalItemsChanged())); connect(s->tableViewModel, SIGNAL(rowsRemoved(QModelIndex,int,int)), this, SIGNAL(signalItemsChanged())); connect(s->tableViewModel, SIGNAL(layoutChanged()), this, SIGNAL(signalItemsChanged())); connect(s->tableViewModel, SIGNAL(modelReset()), this, SIGNAL(signalItemsChanged())); vbox1->addWidget(s->treeView); setLayout(vbox1); } TableView::~TableView() { } void TableView::doLoadState() { const KConfigGroup group = getConfigGroup(); TableViewColumnProfile profile; const KConfigGroup groupCurrentProfile = group.group("Current Profile"); profile.loadSettings(groupCurrentProfile); s->tableViewModel->loadColumnProfile(profile); const TableViewModel::GroupingMode groupingMode = TableViewModel::GroupingMode(group.readEntry("Grouping mode", int(TableViewModel::GroupingShowSubItems))); s->tableViewModel->setGroupingMode(groupingMode); if (!profile.headerState.isEmpty()) { s->treeView->header()->restoreState(profile.headerState); } } void TableView::doSaveState() { KConfigGroup group = getConfigGroup(); TableViewColumnProfile profile = s->tableViewModel->getColumnProfile(); profile.headerState = s->treeView->header()->saveState(); KConfigGroup groupCurrentProfile = group.group("Current Profile"); profile.saveSettings(groupCurrentProfile); group.writeEntry("Grouping mode", int(s->tableViewModel->groupingMode())); } void TableView::slotItemActivated(const QModelIndex& tableViewIndex) { const ImageInfo info = s->tableViewModel->imageInfo(tableViewIndex); /// @todo Respect edit/preview setting emit signalPreviewRequested(info); } bool TableView::eventFilter(QObject* watched, QEvent* event) { // we are looking for context menu events for the table view if ((watched==s->treeView)&&(event->type()==QEvent::ContextMenu)) { QContextMenuEvent* const e = static_cast(event); e->accept(); const QModelIndex contextMenuIndex = s->treeView->indexAt(e->pos()); if (contextMenuIndex.isValid()) { showTreeViewContextMenuOnItem(e, contextMenuIndex); } else { showTreeViewContextMenuOnEmptyArea(e); } // event has been filtered by us return true; } return QObject::eventFilter(watched, event); } void TableView::showTreeViewContextMenuOnEmptyArea(QContextMenuEvent* const event) { Album* const album = currentAlbum(); if (!album || album->isRoot() || (album->type() != Album::PHYSICAL && album->type() != Album::TAG) ) { return; } KMenu menu(this); ContextMenuHelper cmHelper(&menu); cmHelper.addAction("full_screen"); cmHelper.addAction("options_show_menubar"); cmHelper.addSeparator(); cmHelper.addStandardActionPaste(this, SLOT(slotPaste())); cmHelper.addSeparator(); cmHelper.addGroupMenu(QList(), getExtraGroupingActions(&cmHelper)); cmHelper.exec(event->globalPos()); } void TableView::showTreeViewContextMenuOnItem(QContextMenuEvent* const event, const QModelIndex& indexAtMenu) { // get a list of currently selected images' ids const QList selectedImageIds = selectedImageIdsCurrentFirst(); // Temporary actions -------------------------------------- KAction* const viewAction = new KAction(i18nc("View the selected image", "Preview"), this); viewAction->setIcon(SmallIcon("viewimage")); viewAction->setEnabled(selectedImageIds.count() == 1); // Creation of the menu ----------------------------------- KMenu menu(this); ContextMenuHelper cmHelper(&menu); cmHelper.addAction("full_screen"); cmHelper.addAction("options_show_menubar"); cmHelper.addSeparator(); // --- cmHelper.addAction("move_selection_to_album"); cmHelper.addAction(viewAction); /// @todo image_edit is grayed out on first invocation of the menu for some reason cmHelper.addAction("image_edit"); + cmHelper.addAction("open_with_default_application"); cmHelper.addServicesMenu(s->tableViewModel->selectedUrls()); cmHelper.addGotoMenu(selectedImageIds); cmHelper.addAction("image_rotate"); cmHelper.addSeparator(); // --- cmHelper.addAction("image_find_similar"); cmHelper.addStandardActionLightTable(); cmHelper.addQueueManagerMenu(); cmHelper.addSeparator(); // --- cmHelper.addAction("image_rename"); cmHelper.addAction("cut_album_selection"); cmHelper.addAction("copy_album_selection"); cmHelper.addAction("paste_album_selection"); cmHelper.addStandardActionItemDelete(this, SLOT(slotDeleteSelected()), selectedImageIds.count()); cmHelper.addSeparator(); // --- cmHelper.addStandardActionThumbnail(selectedImageIds, currentAlbum()); // --- cmHelper.addAssignTagsMenu(selectedImageIds); cmHelper.addRemoveTagsMenu(selectedImageIds); cmHelper.addSeparator(); cmHelper.addLabelsAction(); /// @todo In digikamimageview.cpp, this is not available in face mode cmHelper.addGroupMenu(selectedImageIds, getExtraGroupingActions(&cmHelper)); connect(&cmHelper, SIGNAL(signalAssignColorLabel(int)), this, SLOT(slotAssignColorLabelToSelected(int))); connect(&cmHelper, SIGNAL(signalAssignPickLabel(int)), this, SLOT(slotAssignPickLabelToSelected(int))); connect(&cmHelper, SIGNAL(signalAssignRating(int)), this, SLOT(slotAssignRatingToSelected(int))); connect(&cmHelper, SIGNAL(signalAssignTag(int)), this, SLOT(slotAssignTagToSelected(int))); connect(&cmHelper, SIGNAL(signalRemoveTag(int)), this, SLOT(slotRemoveTagFromSelected(int))); connect(&cmHelper, SIGNAL(signalAddToExistingQueue(int)), this, SLOT(slotInsertSelectedToExistingQueue(int))); connect(&cmHelper, SIGNAL(signalPopupTagsView()), this, SIGNAL(signalPopupTagsView())); connect(&cmHelper, SIGNAL(signalGotoTag(int)), this, SIGNAL(signalGotoTagAndImageRequested(int))); connect(&cmHelper, SIGNAL(signalGotoAlbum(ImageInfo)), this, SIGNAL(signalGotoAlbumAndImageRequested(ImageInfo))); connect(&cmHelper, SIGNAL(signalGotoDate(ImageInfo)), this, SIGNAL(signalGotoDateAndImageRequested(ImageInfo))); connect(&cmHelper, SIGNAL(signalSetThumbnail(ImageInfo)), this, SLOT(slotSetAsAlbumThumbnail(ImageInfo))); connect(&cmHelper, SIGNAL(signalCreateGroup()), this, SLOT(slotCreateGroupFromSelection())); connect(&cmHelper, SIGNAL(signalRemoveFromGroup()), this, SLOT(slotRemoveSelectedFromGroup())); connect(&cmHelper, SIGNAL(signalUngroup()), this, SLOT(slotUngroupSelected())); connect(&cmHelper, SIGNAL(signalCreateGroupByTime()), this, SLOT(slotCreateGroupByTimeFromSelection())); QAction* const choice = cmHelper.exec(event->globalPos()); if (choice && (choice == viewAction) ) { emit(signalPreviewRequested(s->tableViewModel->imageInfo(indexAtMenu))); } } QList TableView::selectedImageInfos() const { const QModelIndexList selectedIndexes = s->tableViewSelectionModel->selectedRows(); return s->tableViewModel->imageInfos(selectedIndexes); } QList TableView::selectedImageInfosCurrentFirst() const { QModelIndexList selectedIndexes = s->tableViewSelectionModel->selectedRows(); const QModelIndex cIndex = s->tableViewSelectionModel->currentIndex(); if (!selectedIndexes.isEmpty()) { if (selectedIndexes.first()!=cIndex) { selectedIndexes.removeOne(cIndex); selectedIndexes.prepend(cIndex); } } return s->tableViewModel->imageInfos(selectedIndexes); } void TableView::slotAssignColorLabelToSelected(const int colorLabelID) { FileActionMngr::instance()->assignColorLabel(selectedImageInfos(), colorLabelID); } void TableView::slotAssignPickLabelToSelected(const int pickLabelID) { FileActionMngr::instance()->assignPickLabel(selectedImageInfos(), pickLabelID); } void TableView::slotAssignRatingToSelected(const int rating) { FileActionMngr::instance()->assignRating(selectedImageInfos(), rating); } void TableView::slotAssignTagToSelected(const int tagID) { FileActionMngr::instance()->assignTags(selectedImageInfos(), QList() << tagID); } void TableView::slotRemoveTagFromSelected(const int tagID) { FileActionMngr::instance()->removeTags(selectedImageInfos(), QList() << tagID); } void TableView::setThumbnailSize(const ThumbnailSize& size) { d->thumbnailSize = size; const QList columnObjects = s->tableViewModel->getColumnObjects(); Q_FOREACH(TableViewColumn* const iColumn, columnObjects) { iColumn->updateThumbnailSize(); } } ThumbnailSize TableView::getThumbnailSize() const { return d->thumbnailSize; } QList TableView::selectedImageIdsCurrentFirst() const { const QModelIndexList selectedIndexes = s->tableViewSelectionModel->selectedRows(); QList selectedImageIds = s->tableViewModel->imageIds(selectedIndexes); const QModelIndex currentIndex = s->tableViewSelectionModel->currentIndex(); qlonglong currentId = s->tableViewModel->imageId(currentIndex); if (currentId>=0) { if (selectedImageIds.first()!=currentId) { selectedImageIds.removeOne(currentId); selectedImageIds.prepend(currentId); } } return selectedImageIds; } void TableView::slotInsertSelectedToExistingQueue(const int queueId) { kDebug()<<"_________________________"<imageViewUtilities->insertSilentToQueueManager(imageInfoList, imageInfoList.first(), queueId); } } void TableView::slotSetAsAlbumThumbnail(const ImageInfo& info) { Album* const theCurrentAlbum = currentAlbum(); if (!theCurrentAlbum) { return; } d->imageViewUtilities->setAsAlbumThumbnail(theCurrentAlbum, info); } Album* TableView::currentAlbum() { ImageAlbumModel* const albumModel = qobject_cast(s->imageModel); if (!albumModel) { return 0; } if(albumModel->currentAlbums().isEmpty()) { return 0; } return albumModel->currentAlbums().first(); } void TableView::slotPaste() { DragDropViewImplementation* const dragDropViewImplementation = s->treeView; dragDropViewImplementation->paste(); } ImageInfo TableView::currentInfo() { return s->tableViewModel->imageInfo(s->tableViewSelectionModel->currentIndex()); } ImageInfoList TableView::allInfo() const { return s->tableViewModel->allImageInfo(); } void TableView::slotDeleteSelected(const ImageViewUtilities::DeleteMode deleteMode) { const ImageInfoList infoList = selectedImageInfos(); /// @todo Update parameter naming for deleteImages if (d->imageViewUtilities->deleteImages(infoList, deleteMode)) { slotAwayFromSelection(); } } void TableView::slotDeleteSelectedWithoutConfirmation(const ImageViewUtilities::DeleteMode deleteMode) { const ImageInfoList infoList = selectedImageInfos(); d->imageViewUtilities->deleteImagesDirectly(infoList, deleteMode); slotAwayFromSelection(); } void TableView::slotRemoveSelectedFromGroup() { FileActionMngr::instance()->removeFromGroup(selectedImageInfos()); } void TableView::slotUngroupSelected() { FileActionMngr::instance()->ungroup(selectedImageInfos()); } void TableView::slotCreateGroupFromSelection() { const QList selectedInfos = selectedImageInfos(); const ImageInfo groupLeader = currentInfo(); FileActionMngr::instance()->addToGroup(groupLeader, selectedInfos); } void TableView::slotCreateGroupByTimeFromSelection() { const QList selectedInfos = selectedImageInfos(); d->imageViewUtilities->createGroupByTimeFromInfoList(selectedInfos); } QList TableView::getExtraGroupingActions(QObject* const parentObject) const { QList actionList; const TableViewModel::GroupingMode currentGroupingMode = s->tableViewModel->groupingMode(); KAction* const actionHideGrouped = new KAction(i18n("Hide grouped items"), parentObject); actionHideGrouped->setCheckable(true); actionHideGrouped->setChecked(currentGroupingMode==TableViewModel::GroupingHideGrouped); actionHideGrouped->setData( QVariant::fromValue(TableViewModel::GroupingHideGrouped) ); connect(actionHideGrouped, SIGNAL(triggered(bool)), this, SLOT(slotGroupingModeActionTriggered())); actionList << actionHideGrouped; KAction* const actionIgnoreGrouping = new KAction(i18n("Ignore grouping"), parentObject); actionIgnoreGrouping->setCheckable(true); actionIgnoreGrouping->setChecked(currentGroupingMode==TableViewModel::GroupingIgnoreGrouping); actionIgnoreGrouping->setData( QVariant::fromValue(TableViewModel::GroupingIgnoreGrouping) ); connect(actionIgnoreGrouping, SIGNAL(triggered(bool)), this, SLOT(slotGroupingModeActionTriggered())); actionList << actionIgnoreGrouping; KAction* const actionShowSubItems = new KAction(i18n("Show grouping in tree"), parentObject); actionShowSubItems->setCheckable(true); actionShowSubItems->setChecked(currentGroupingMode==TableViewModel::GroupingShowSubItems); actionShowSubItems->setData( QVariant::fromValue(TableViewModel::GroupingShowSubItems) ); connect(actionShowSubItems, SIGNAL(triggered(bool)), this, SLOT(slotGroupingModeActionTriggered())); actionList << actionShowSubItems; return actionList; } void TableView::slotGroupingModeActionTriggered() { const QAction* const senderAction = qobject_cast(sender()); if (!senderAction) { return; } const TableViewModel::GroupingMode newGroupingMode = senderAction->data().value(); s->tableViewModel->setGroupingMode(newGroupingMode); } KUrl::List TableView::allUrls() const { const ImageInfoList allInfo = s->tableViewModel->allImageInfo(); KUrl::List resultList; Q_FOREACH(const ImageInfo& info, allInfo) { resultList << info.fileUrl(); } return resultList; } KUrl::List TableView::selectedUrls() const { return s->tableViewModel->selectedUrls(); } int TableView::numberOfSelectedItems() const { return s->tableViewSelectionModel->selectedRows().count(); } void TableView::slotGoToRow(const int rowNumber, const bool relativeMove) { int nextDeepRowNumber = rowNumber; if (relativeMove) { const QModelIndex currentTableViewIndex = s->tableViewSelectionModel->currentIndex(); const int currentDeepRowNumber = s->tableViewModel->indexToDeepRowNumber(currentTableViewIndex); nextDeepRowNumber+= currentDeepRowNumber; } const QModelIndex nextIndex = s->tableViewModel->deepRowIndex(nextDeepRowNumber); if (nextIndex.isValid()) { const QItemSelection rowSelection = s->tableViewSelectionModelSyncer->targetIndexToRowItemSelection(nextIndex); s->tableViewSelectionModel->select(rowSelection, QItemSelectionModel::ClearAndSelect); s->tableViewSelectionModel->setCurrentIndex(nextIndex, QItemSelectionModel::Select); } } ImageInfo TableView::deepRowImageInfo(const int rowNumber, const bool relative) const { int targetRowNumber = rowNumber; if (relative) { const QModelIndex& currentTableViewIndex = s->tableViewSelectionModel->currentIndex(); if (!currentTableViewIndex.isValid()) { return ImageInfo(); } const int currentDeepRowNumber = s->tableViewModel->indexToDeepRowNumber(currentTableViewIndex); targetRowNumber+= currentDeepRowNumber; } const QModelIndex targetIndex = s->tableViewModel->deepRowIndex(targetRowNumber); return s->tableViewModel->imageInfo(targetIndex); } ImageInfo TableView::nextInfo() const { const QModelIndex cIndex = s->tableViewSelectionModel->currentIndex(); const int currentDeepRowNumber = s->tableViewModel->indexToDeepRowNumber(cIndex); const int nextDeepRowNumber = currentDeepRowNumber + 1; if (nextDeepRowNumber>=s->tableViewModel->deepRowCount()) { return ImageInfo(); } const QModelIndex nextDeepRowIndex = s->tableViewModel->deepRowIndex(nextDeepRowNumber); return s->tableViewModel->imageInfo(nextDeepRowIndex); } ImageInfo TableView::previousInfo() const { const QModelIndex cIndex = s->tableViewSelectionModel->currentIndex(); const int currentDeepRowNumber = s->tableViewModel->indexToDeepRowNumber(cIndex); const int previousDeepRowNumber = currentDeepRowNumber - 1; if (previousDeepRowNumber<0) { return ImageInfo(); } const QModelIndex previousDeepRowIndex = s->tableViewModel->deepRowIndex(previousDeepRowNumber); return s->tableViewModel->imageInfo(previousDeepRowIndex); } void TableView::slotSetCurrentWhenAvailable(const qlonglong id) { const QModelIndex idx = s->tableViewModel->indexFromImageId(id, 0); if (!idx.isValid()) { /// @todo Actually buffer this request until the model is fully populated return; } s->tableViewSelectionModel->setCurrentIndex(idx, QItemSelectionModel::ClearAndSelect); } /** * @brief Unselects the current selection and changes the current item * * @todo This may not work correctly if grouped items are deleted, but are not selected */ void TableView::slotAwayFromSelection() { QModelIndexList selection = s->tableViewSelectionModel->selectedRows(0); if (selection.isEmpty()) { return; } const QModelIndex firstIndex = s->tableViewModel->deepRowIndex(0); const QModelIndex lastIndex = s->tableViewModel->deepRowIndex(-1); if (selection.contains(firstIndex) && selection.contains(lastIndex)) { // both the first and the last index are selected, we have to // select an index inbetween const int nextFreeDeepRow= s->tableViewModel->firstDeepRowNotInList(selection); if (nextFreeDeepRow<0) { s->tableViewSelectionModel->clearSelection(); s->tableViewSelectionModel->setCurrentIndex(QModelIndex(), QItemSelectionModel::ClearAndSelect); } else { const QModelIndex nextFreeIndex = s->tableViewModel->deepRowIndex(nextFreeDeepRow); s->tableViewSelectionModel->setCurrentIndex(nextFreeIndex, QItemSelectionModel::ClearAndSelect); const QItemSelection nextFreeIndexAsRow = s->tableViewSelectionModelSyncer->targetIndexToRowItemSelection(nextFreeIndex); s->tableViewSelectionModel->select(nextFreeIndexAsRow, QItemSelectionModel::ClearAndSelect); } } else if (selection.contains(lastIndex)) { const int firstSelectedRowNumber = s->tableViewModel->indexToDeepRowNumber(selection.first()); const QModelIndex newIndex = s->tableViewModel->deepRowIndex(firstSelectedRowNumber-1); s->tableViewSelectionModel->setCurrentIndex(newIndex, QItemSelectionModel::ClearAndSelect); const QItemSelection newIndexAsRow = s->tableViewSelectionModelSyncer->targetIndexToRowItemSelection(newIndex); s->tableViewSelectionModel->select(newIndexAsRow, QItemSelectionModel::ClearAndSelect); } else { const int lastSelectedRowNumber = s->tableViewModel->indexToDeepRowNumber(selection.last()); const QModelIndex newIndex = s->tableViewModel->deepRowIndex(lastSelectedRowNumber+1); s->tableViewSelectionModel->setCurrentIndex(newIndex, QItemSelectionModel::ClearAndSelect); const QItemSelection newIndexAsRow = s->tableViewSelectionModelSyncer->targetIndexToRowItemSelection(newIndex); s->tableViewSelectionModel->select(newIndexAsRow, QItemSelectionModel::ClearAndSelect); } } void TableView::clearSelection() { s->tableViewSelectionModel->clearSelection(); } void TableView::invertSelection() { const int deepRowCount = s->tableViewModel->deepRowCount(); QList rowsToSelect; int lastSelectedRow = -1; /// @todo Create a DeepRowIterator because there is a lot of overhead here for (int i = 0; itableViewModel->deepRowIndex(i); if (s->tableViewSelectionModel->isSelected(iIndex)) { if (i-1>lastSelectedRow) { for (int j=lastSelectedRow+1; jtableViewSelectionModel->clearSelection(); Q_FOREACH(const int i, rowsToSelect) { const QModelIndex iIndex = s->tableViewModel->deepRowIndex(i); const QItemSelection is = s->tableViewSelectionModelSyncer->targetIndexToRowItemSelection(iIndex); s->tableViewSelectionModel->select(is, QItemSelectionModel::Select); } } void TableView::selectAll() { /// @todo This only selects expanded items. s->treeView->selectAll(); } void TableView::slotSetActive(const bool isActive) { if (s->isActive!=isActive) { s->isActive = isActive; s->tableViewModel->slotSetActive(isActive); s->tableViewSelectionModelSyncer->slotSetActive(isActive); } } } /* namespace Digikam */