You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1764 lines
55 KiB
1764 lines
55 KiB
/* ============================================================
|
|
*
|
|
* This file is a part of digiKam project
|
|
* http://www.digikam.org
|
|
*
|
|
* Date : 2003-03-09
|
|
* Description : Captions, Tags, and Rating properties editor
|
|
*
|
|
* Copyright (C) 2003-2005 by Renchi Raju <renchi@pooh.tam.uiuc.edu>
|
|
* Copyright (C) 2003-2009 by Gilles Caulier <caulier dot gilles at gmail dot com>
|
|
* Copyright (C) 2006-2009 by Marcel Wiesweg <marcel.wiesweg@gmx.de>
|
|
* Copyright (C) 2009 by Andi Clemens <andi dot clemens at gmx dot net>
|
|
*
|
|
* 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.
|
|
*
|
|
* ============================================================ */
|
|
|
|
// TQt includes.
|
|
|
|
#include <tqhbox.h>
|
|
#include <tqvbox.h>
|
|
#include <tqlabel.h>
|
|
#include <tqlayout.h>
|
|
#include <tqtoolbutton.h>
|
|
#include <tqpushbutton.h>
|
|
#include <tqiconset.h>
|
|
#include <tqwhatsthis.h>
|
|
#include <tqtooltip.h>
|
|
#include <tqscrollview.h>
|
|
|
|
// KDE includes.
|
|
|
|
#include <tdeabc/stdaddressbook.h>
|
|
#include <tdepopupmenu.h>
|
|
#include <tdelocale.h>
|
|
#include <kurl.h>
|
|
#include <kcursor.h>
|
|
#include <tdeapplication.h>
|
|
#include <kiconloader.h>
|
|
#include <tdemessagebox.h>
|
|
#include <ktextedit.h>
|
|
#include <tdeconfig.h>
|
|
#include <klineedit.h>
|
|
#include <kdialogbase.h>
|
|
|
|
// Local includes.
|
|
|
|
#include "ddebug.h"
|
|
#include "dmetadata.h"
|
|
#include "kdatetimeedit.h"
|
|
#include "albumiconitem.h"
|
|
#include "albumdb.h"
|
|
#include "album.h"
|
|
#include "albumsettings.h"
|
|
#include "albumlister.h"
|
|
#include "albumthumbnailloader.h"
|
|
#include "tageditdlg.h"
|
|
#include "navigatebarwidget.h"
|
|
#include "ratingwidget.h"
|
|
#include "talbumlistview.h"
|
|
#include "tagfilterview.h"
|
|
#include "imageinfo.h"
|
|
#include "imageattributeswatch.h"
|
|
#include "metadatahub.h"
|
|
#include "statusprogressbar.h"
|
|
#include "searchtextbar.h"
|
|
#include "imagedescedittab.h"
|
|
#include "imagedescedittab.moc"
|
|
|
|
namespace Digikam
|
|
{
|
|
|
|
class ImageDescEditTabPriv
|
|
{
|
|
|
|
public:
|
|
|
|
ImageDescEditTabPriv()
|
|
{
|
|
modified = false;
|
|
ignoreImageAttributesWatch = false;
|
|
recentTagsBtn = 0;
|
|
commentsEdit = 0;
|
|
tagsSearchBar = 0;
|
|
dateTimeEdit = 0;
|
|
tagsView = 0;
|
|
ratingWidget = 0;
|
|
ABCMenu = 0;
|
|
assignedTagsBtn = 0;
|
|
applyBtn = 0;
|
|
revertBtn = 0;
|
|
newTagEdit = 0;
|
|
toggleAutoTags = TagFilterView::NoToggleAuto;
|
|
}
|
|
|
|
bool modified;
|
|
bool ignoreImageAttributesWatch;
|
|
|
|
TQToolButton *recentTagsBtn;
|
|
TQToolButton *assignedTagsBtn;
|
|
TQToolButton *revertBtn;
|
|
|
|
TQPopupMenu *ABCMenu;
|
|
|
|
TQPushButton *applyBtn;
|
|
|
|
TQPushButton *moreButton;
|
|
TQPopupMenu *moreMenu;
|
|
|
|
KTextEdit *commentsEdit;
|
|
|
|
KDateTimeEdit *dateTimeEdit;
|
|
|
|
SearchTextBar *tagsSearchBar;
|
|
SearchTextBar *newTagEdit;
|
|
|
|
TQPtrList<ImageInfo> currInfos;
|
|
|
|
TAlbumListView *tagsView;
|
|
|
|
RatingWidget *ratingWidget;
|
|
|
|
TagFilterView::ToggleAutoTags toggleAutoTags;
|
|
|
|
MetadataHub hub;
|
|
};
|
|
|
|
ImageDescEditTab::ImageDescEditTab(TQWidget *parent, bool navBar)
|
|
: NavigateBarTab(parent)
|
|
{
|
|
d = new ImageDescEditTabPriv;
|
|
|
|
setupNavigateBar(navBar);
|
|
|
|
TQScrollView *sv = new TQScrollView(this);
|
|
sv->viewport()->setBackgroundMode(TQt::PaletteBackground);
|
|
sv->setResizePolicy(TQScrollView::AutoOneFit);
|
|
sv->setFrameStyle(TQFrame::NoFrame);
|
|
|
|
TQWidget *settingsArea = new TQWidget(sv->viewport());
|
|
sv->addChild(settingsArea);
|
|
m_navigateBarLayout->addWidget(sv);
|
|
|
|
TQGridLayout *settingsLayout = new TQGridLayout(settingsArea, 6, 1,
|
|
KDialog::spacingHint(), KDialog::spacingHint());
|
|
|
|
// Captions/Date/Rating view -----------------------------------
|
|
|
|
TQVBox *commentsBox = new TQVBox(settingsArea);
|
|
new TQLabel(i18n("Caption:"), commentsBox);
|
|
d->commentsEdit = new KTextEdit(commentsBox);
|
|
d->commentsEdit->setTextFormat(TQTextEdit::PlainText);
|
|
d->commentsEdit->setCheckSpellingEnabled(true);
|
|
d->commentsEdit->setFixedHeight(100);
|
|
|
|
TQHBox *dateBox = new TQHBox(settingsArea);
|
|
new TQLabel(i18n("Date:"), dateBox);
|
|
d->dateTimeEdit = new KDateTimeEdit(dateBox, "datepicker");
|
|
|
|
TQHBox *ratingBox = new TQHBox(settingsArea);
|
|
new TQLabel(i18n("Rating:"), ratingBox);
|
|
d->ratingWidget = new RatingWidget(ratingBox);
|
|
|
|
// Tags view ---------------------------------------------------
|
|
|
|
d->newTagEdit = new SearchTextBar(settingsArea, "ImageDescEditTabNewTagEdit", i18n("Enter new tag here..."));
|
|
TQWhatsThis::add(d->newTagEdit, i18n("Enter here the text used to create new tags. "
|
|
"'/' can be used here to create a hierarchy of tags. "
|
|
"',' can be used here to create more than one hierarchy at the same time."));
|
|
|
|
d->tagsView = new TAlbumListView(settingsArea);
|
|
|
|
TQHBox *tagsSearch = new TQHBox(settingsArea);
|
|
tagsSearch->setSpacing(KDialog::spacingHint());
|
|
|
|
d->tagsSearchBar = new SearchTextBar(tagsSearch, "ImageDescEditTabTagsSearchBar");
|
|
|
|
d->assignedTagsBtn = new TQToolButton(tagsSearch);
|
|
TQToolTip::add(d->assignedTagsBtn, i18n("Tags already assigned"));
|
|
d->assignedTagsBtn->setIconSet(kapp->iconLoader()->loadIcon("tag-assigned",
|
|
TDEIcon::NoGroup, TDEIcon::SizeSmall,
|
|
TDEIcon::DefaultState, 0, true));
|
|
d->assignedTagsBtn->setToggleButton(true);
|
|
|
|
d->recentTagsBtn = new TQToolButton(tagsSearch);
|
|
TQPopupMenu *popupMenu = new TQPopupMenu(d->recentTagsBtn);
|
|
TQToolTip::add(d->recentTagsBtn, i18n("Recent Tags"));
|
|
d->recentTagsBtn->setIconSet(kapp->iconLoader()->loadIcon("tag-recents",
|
|
TDEIcon::NoGroup, TDEIcon::SizeSmall,
|
|
TDEIcon::DefaultState, 0, true));
|
|
d->recentTagsBtn->setUsesBigPixmap(false);
|
|
d->recentTagsBtn->setPopup(popupMenu);
|
|
d->recentTagsBtn->setPopupDelay(1);
|
|
|
|
// Buttons -----------------------------------------
|
|
|
|
TQHBox *buttonsBox = new TQHBox(settingsArea);
|
|
buttonsBox->setSpacing(KDialog::spacingHint());
|
|
|
|
d->revertBtn = new TQToolButton(buttonsBox);
|
|
d->revertBtn->setIconSet(SmallIcon("reload_page"));
|
|
TQToolTip::add(d->revertBtn, i18n("Revert all changes"));
|
|
d->revertBtn->setEnabled(false);
|
|
|
|
d->applyBtn = new TQPushButton(i18n("Apply"), buttonsBox);
|
|
d->applyBtn->setIconSet(SmallIcon("button_ok"));
|
|
d->applyBtn->setEnabled(false);
|
|
TQToolTip::add(d->applyBtn, i18n("Apply all changes to images"));
|
|
buttonsBox->setStretchFactor(d->applyBtn, 10);
|
|
|
|
d->moreButton = new TQPushButton(i18n("More"), buttonsBox);
|
|
d->moreMenu = new TQPopupMenu(this);
|
|
d->moreButton->setPopup(d->moreMenu);
|
|
|
|
// --------------------------------------------------
|
|
|
|
settingsLayout->addMultiCellWidget(commentsBox, 0, 0, 0, 1);
|
|
settingsLayout->addMultiCellWidget(dateBox, 1, 1, 0, 1);
|
|
settingsLayout->addMultiCellWidget(ratingBox, 2, 2, 0, 1);
|
|
settingsLayout->addMultiCellWidget(d->newTagEdit, 3, 3, 0, 1);
|
|
settingsLayout->addMultiCellWidget(d->tagsView, 4, 4, 0, 1);
|
|
settingsLayout->addMultiCellWidget(tagsSearch, 5, 5, 0, 1);
|
|
settingsLayout->addMultiCellWidget(buttonsBox, 6, 6, 0, 1);
|
|
settingsLayout->setRowStretch(4, 10);
|
|
|
|
// --------------------------------------------------
|
|
|
|
connect(d->tagsView, TQT_SIGNAL(signalProgressBarMode(int, const TQString&)),
|
|
this, TQT_SIGNAL(signalProgressBarMode(int, const TQString&)));
|
|
|
|
connect(d->tagsView, TQT_SIGNAL(signalProgressValue(int)),
|
|
this, TQT_SIGNAL(signalProgressValue(int)));
|
|
|
|
connect(popupMenu, TQT_SIGNAL(activated(int)),
|
|
this, TQT_SLOT(slotRecentTagsMenuActivated(int)));
|
|
|
|
connect(d->tagsView, TQT_SIGNAL(signalItemStateChanged(TAlbumCheckListItem *)),
|
|
this, TQT_SLOT(slotItemStateChanged(TAlbumCheckListItem *)));
|
|
|
|
connect(d->commentsEdit, TQT_SIGNAL(textChanged()),
|
|
this, TQT_SLOT(slotCommentChanged()));
|
|
|
|
connect(d->dateTimeEdit, TQT_SIGNAL(dateTimeChanged(const TQDateTime& )),
|
|
this, TQT_SLOT(slotDateTimeChanged(const TQDateTime&)));
|
|
|
|
connect(d->ratingWidget, TQT_SIGNAL(signalRatingChanged(int)),
|
|
this, TQT_SLOT(slotRatingChanged(int)));
|
|
|
|
connect(d->tagsView, TQT_SIGNAL(rightButtonClicked(TQListViewItem*, const TQPoint &, int)),
|
|
this, TQT_SLOT(slotRightButtonClicked(TQListViewItem*, const TQPoint&, int)));
|
|
|
|
connect(d->tagsSearchBar, TQT_SIGNAL(signalTextChanged(const TQString&)),
|
|
this, TQT_SLOT(slotTagsSearchChanged(const TQString&)));
|
|
|
|
connect(this, TQT_SIGNAL(signalTagFilterMatch(bool)),
|
|
d->tagsSearchBar, TQT_SLOT(slotSearchResult(bool)));
|
|
|
|
connect(d->assignedTagsBtn, TQT_SIGNAL(toggled(bool)),
|
|
this, TQT_SLOT(slotAssignedTagsToggled(bool)));
|
|
|
|
connect(d->newTagEdit->lineEdit(), TQT_SIGNAL(returnPressed(const TQString&)),
|
|
this, TQT_SLOT(slotCreateNewTag()));
|
|
|
|
connect(d->applyBtn, TQT_SIGNAL(clicked()),
|
|
this, TQT_SLOT(slotApplyAllChanges()));
|
|
|
|
connect(d->revertBtn, TQT_SIGNAL(clicked()),
|
|
this, TQT_SLOT(slotRevertAllChanges()));
|
|
|
|
connect(d->moreMenu, TQT_SIGNAL(aboutToShow()),
|
|
this, TQT_SLOT(slotMoreMenu()));
|
|
|
|
// Initialize ---------------------------------------------
|
|
|
|
d->commentsEdit->installEventFilter(this);
|
|
d->dateTimeEdit->installEventFilter(this);
|
|
d->ratingWidget->installEventFilter(this);
|
|
d->tagsView->installEventFilter(this);
|
|
updateRecentTags();
|
|
|
|
// Connect to album manager -----------------------------
|
|
|
|
AlbumManager* man = AlbumManager::instance();
|
|
|
|
connect(man, TQT_SIGNAL(signalAlbumAdded(Album*)),
|
|
this, TQT_SLOT(slotAlbumAdded(Album*)));
|
|
|
|
connect(man, TQT_SIGNAL(signalAlbumDeleted(Album*)),
|
|
this, TQT_SLOT(slotAlbumDeleted(Album*)));
|
|
|
|
connect(man, TQT_SIGNAL(signalAlbumRenamed(Album*)),
|
|
this, TQT_SLOT(slotAlbumRenamed(Album*)));
|
|
|
|
connect(man, TQT_SIGNAL(signalAlbumsCleared()),
|
|
this, TQT_SLOT(slotAlbumsCleared()));
|
|
|
|
connect(man, TQT_SIGNAL(signalAlbumIconChanged(Album*)),
|
|
this, TQT_SLOT(slotAlbumIconChanged(Album*)));
|
|
|
|
connect(man, TQT_SIGNAL(signalTAlbumMoved(TAlbum*, TAlbum*)),
|
|
this, TQT_SLOT(slotAlbumMoved(TAlbum*, TAlbum*)));
|
|
|
|
// Connect to thumbnail loader -----------------------------
|
|
|
|
AlbumThumbnailLoader *loader = AlbumThumbnailLoader::instance();
|
|
|
|
connect(loader, TQT_SIGNAL(signalThumbnail(Album *, const TQPixmap&)),
|
|
this, TQT_SLOT(slotGotThumbnailFromIcon(Album *, const TQPixmap&)));
|
|
|
|
connect(loader, TQT_SIGNAL(signalFailed(Album *)),
|
|
this, TQT_SLOT(slotThumbnailLost(Album *)));
|
|
|
|
connect(loader, TQT_SIGNAL(signalReloadThumbnails()),
|
|
this, TQT_SLOT(slotReloadThumbnails()));
|
|
|
|
// Connect to attribute watch ------------------------------
|
|
|
|
ImageAttributesWatch *watch = ImageAttributesWatch::instance();
|
|
|
|
connect(watch, TQT_SIGNAL(signalImageTagsChanged(TQ_LLONG)),
|
|
this, TQT_SLOT(slotImageTagsChanged(TQ_LLONG)));
|
|
|
|
connect(watch, TQT_SIGNAL(signalImagesChanged(int)),
|
|
this, TQT_SLOT(slotImagesChanged(int)));
|
|
|
|
connect(watch, TQT_SIGNAL(signalImageRatingChanged(TQ_LLONG)),
|
|
this, TQT_SLOT(slotImageRatingChanged(TQ_LLONG)));
|
|
|
|
connect(watch, TQT_SIGNAL(signalImageDateChanged(TQ_LLONG)),
|
|
this, TQT_SLOT(slotImageDateChanged(TQ_LLONG)));
|
|
|
|
connect(watch, TQT_SIGNAL(signalImageCaptionChanged(TQ_LLONG)),
|
|
this, TQT_SLOT(slotImageCaptionChanged(TQ_LLONG)));
|
|
|
|
// -- read config ---------------------------------------------------------
|
|
|
|
TDEConfig* config = kapp->config();
|
|
config->setGroup("Tag List View");
|
|
d->toggleAutoTags = (TagFilterView::ToggleAutoTags)(config->readNumEntry("Toggle Auto Tags",
|
|
TagFilterView::NoToggleAuto));
|
|
}
|
|
|
|
ImageDescEditTab::~ImageDescEditTab()
|
|
{
|
|
slotChangingItems();
|
|
|
|
/*
|
|
AlbumList tList = AlbumManager::instance()->allTAlbums();
|
|
for (AlbumList::iterator it = tList.begin(); it != tList.end(); ++it)
|
|
{
|
|
(*it)->removeExtraData(this);
|
|
}
|
|
*/
|
|
|
|
TDEConfig* config = kapp->config();
|
|
config->setGroup("Tag List View");
|
|
config->writeEntry("Toggle Auto Tags", (int)(d->toggleAutoTags));
|
|
config->sync();
|
|
|
|
delete d;
|
|
}
|
|
|
|
bool ImageDescEditTab::singleSelection() const
|
|
{
|
|
return (d->currInfos.count() == 1);
|
|
}
|
|
|
|
void ImageDescEditTab::slotChangingItems()
|
|
{
|
|
if (!d->modified)
|
|
return;
|
|
|
|
if (d->currInfos.isEmpty())
|
|
return;
|
|
|
|
if (!AlbumSettings::instance()->getApplySidebarChangesDirectly())
|
|
{
|
|
KDialogBase *dialog = new KDialogBase(i18n("Apply changes?"),
|
|
KDialogBase::Yes | KDialogBase::No,
|
|
KDialogBase::Yes, KDialogBase::No,
|
|
this, "applyChanges",
|
|
true, true,
|
|
KStdGuiItem::yes(), KStdGuiItem::discard());
|
|
|
|
int changedFields = 0;
|
|
if (d->hub.commentChanged())
|
|
changedFields++;
|
|
if (d->hub.dateTimeChanged())
|
|
changedFields++;
|
|
if (d->hub.ratingChanged())
|
|
changedFields++;
|
|
if (d->hub.tagsChanged())
|
|
changedFields++;
|
|
|
|
TQString text;
|
|
if (changedFields == 1)
|
|
{
|
|
if (d->hub.commentChanged())
|
|
text = i18n("<qt><p>You have edited the comment of the image. ",
|
|
"<qt><p>You have edited the comment of %n images. ",
|
|
d->currInfos.count());
|
|
else if (d->hub.dateTimeChanged())
|
|
text = i18n("<qt><p>You have edited the date of the image. ",
|
|
"<qt><p>You have edited the date of %n images. ",
|
|
d->currInfos.count());
|
|
else if (d->hub.ratingChanged())
|
|
text = i18n("<qt><p>You have edited the rating of the image. ",
|
|
"<qt><p>You have edited the rating of %n images. ",
|
|
d->currInfos.count());
|
|
else if (d->hub.tagsChanged())
|
|
text = i18n("<qt><p>You have edited the tags of the image. ",
|
|
"<qt><p>You have edited the tags of %n images. ",
|
|
d->currInfos.count());
|
|
|
|
text += i18n("Do you want to apply your changes?</p></qt>");
|
|
}
|
|
else
|
|
{
|
|
text = i18n("<qt><p>You have edited the metadata of the image: </p><ul>",
|
|
"<qt><p>You have edited the metadata of %n images: </p><ul>",
|
|
d->currInfos.count());
|
|
|
|
if (d->hub.commentChanged())
|
|
text += i18n("<li>comment</li>");
|
|
if (d->hub.dateTimeChanged())
|
|
text += i18n("<li>date</li>");
|
|
if (d->hub.ratingChanged())
|
|
text += i18n("<li>rating</li>");
|
|
if (d->hub.tagsChanged())
|
|
text += i18n("<li>tags</li>");
|
|
|
|
text += "</ul><p>";
|
|
|
|
text += i18n("Do you want to apply your changes?</p></qt>");
|
|
}
|
|
|
|
bool alwaysApply = false;
|
|
int returnCode = KMessageBox::createKMessageBox
|
|
(dialog, TQMessageBox::Information,
|
|
text, TQStringList(),
|
|
i18n("Always apply changes without confirmation"),
|
|
&alwaysApply, KMessageBox::Notify);
|
|
|
|
if (alwaysApply)
|
|
AlbumSettings::instance()->setApplySidebarChangesDirectly(true);
|
|
|
|
if (returnCode == KDialogBase::User1)
|
|
return;
|
|
// otherwise apply
|
|
}
|
|
|
|
slotApplyAllChanges();
|
|
}
|
|
|
|
void ImageDescEditTab::slotApplyAllChanges()
|
|
{
|
|
if (!d->modified)
|
|
return;
|
|
|
|
if (d->currInfos.isEmpty())
|
|
return;
|
|
|
|
bool progressInfo = (d->currInfos.count() > 1);
|
|
emit signalProgressBarMode(StatusProgressBar::ProgressBarMode,
|
|
i18n("Applying changes to images. Please wait..."));
|
|
MetadataWriteSettings writeSettings = MetadataHub::defaultWriteSettings();
|
|
|
|
// debugging - use this to indicate reentry from event loop (kapp->processEvents)
|
|
// remove before final release
|
|
if (d->ignoreImageAttributesWatch)
|
|
{
|
|
DWarning() << "ImageDescEditTab::slotApplyAllChanges(): re-entering from event loop!" << endl;
|
|
}
|
|
|
|
// we are now changing attributes ourselves
|
|
d->ignoreImageAttributesWatch = true;
|
|
AlbumLister::instance()->blockSignals(true);
|
|
AlbumManager::instance()->albumDB()->beginTransaction();
|
|
int i=0;
|
|
for (ImageInfo *info = d->currInfos.first(); info; info = d->currInfos.next())
|
|
{
|
|
// apply to database
|
|
d->hub.write(info);
|
|
// apply to file metadata
|
|
d->hub.write(info->filePath(), MetadataHub::FullWrite, writeSettings);
|
|
|
|
emit signalProgressValue((int)((i++/(float)d->currInfos.count())*100.0));
|
|
if (progressInfo)
|
|
kapp->processEvents();
|
|
}
|
|
AlbumLister::instance()->blockSignals(false);
|
|
AlbumManager::instance()->albumDB()->commitTransaction();
|
|
|
|
d->ignoreImageAttributesWatch = false;
|
|
|
|
emit signalProgressBarMode(StatusProgressBar::TextMode, TQString());
|
|
|
|
d->modified = false;
|
|
d->hub.resetChanged();
|
|
d->applyBtn->setEnabled(false);
|
|
d->revertBtn->setEnabled(false);
|
|
|
|
updateRecentTags();
|
|
updateTagsView();
|
|
}
|
|
|
|
void ImageDescEditTab::slotRevertAllChanges()
|
|
{
|
|
if (!d->modified)
|
|
return;
|
|
|
|
if (d->currInfos.isEmpty())
|
|
return;
|
|
|
|
setInfos(d->currInfos);
|
|
}
|
|
|
|
void ImageDescEditTab::setItem(ImageInfo *info)
|
|
{
|
|
slotChangingItems();
|
|
TQPtrList<ImageInfo> list;
|
|
if (info)
|
|
list.append(info);
|
|
setInfos(list);
|
|
}
|
|
|
|
void ImageDescEditTab::setItems(TQPtrList<ImageInfo> infos)
|
|
{
|
|
slotChangingItems();
|
|
setInfos(infos);
|
|
}
|
|
|
|
void ImageDescEditTab::setInfos(TQPtrList<ImageInfo> infos)
|
|
{
|
|
if (infos.isEmpty())
|
|
{
|
|
d->hub = MetadataHub();
|
|
d->commentsEdit->blockSignals(true);
|
|
d->commentsEdit->clear();
|
|
d->commentsEdit->blockSignals(false);
|
|
d->currInfos.clear();
|
|
setEnabled(false);
|
|
return;
|
|
}
|
|
|
|
setEnabled(true);
|
|
d->currInfos = infos;
|
|
d->modified = false;
|
|
d->hub = MetadataHub();
|
|
d->applyBtn->setEnabled(false);
|
|
d->revertBtn->setEnabled(false);
|
|
|
|
for (ImageInfo *info = d->currInfos.first(); info; info = d->currInfos.next())
|
|
{
|
|
d->hub.load(info);
|
|
}
|
|
|
|
updateComments();
|
|
updateRating();
|
|
updateDate();
|
|
updateTagsView();
|
|
}
|
|
|
|
void ImageDescEditTab::slotReadFromFileMetadataToDatabase()
|
|
{
|
|
emit signalProgressBarMode(StatusProgressBar::ProgressBarMode,
|
|
i18n("Reading metadata from files. Please wait..."));
|
|
|
|
d->ignoreImageAttributesWatch = true;
|
|
AlbumManager::instance()->albumDB()->beginTransaction();
|
|
int i=0;
|
|
for (ImageInfo *info = d->currInfos.first(); info; info = d->currInfos.next())
|
|
{
|
|
// A batch operation: a hub for each single file, not the common hub
|
|
MetadataHub fileHub(MetadataHub::NewTagsImport);
|
|
// read in from DMetadata
|
|
fileHub.load(info->filePath());
|
|
// write out to database
|
|
fileHub.write(info);
|
|
|
|
emit signalProgressValue((int)((i++/(float)d->currInfos.count())*100.0));
|
|
kapp->processEvents();
|
|
}
|
|
AlbumManager::instance()->albumDB()->commitTransaction();
|
|
d->ignoreImageAttributesWatch = false;
|
|
|
|
emit signalProgressBarMode(StatusProgressBar::TextMode, TQString());
|
|
|
|
// reload everything
|
|
setInfos(d->currInfos);
|
|
}
|
|
|
|
void ImageDescEditTab::slotWriteToFileMetadataFromDatabase()
|
|
{
|
|
emit signalProgressBarMode(StatusProgressBar::ProgressBarMode,
|
|
i18n("Writing metadata to files. Please wait..."));
|
|
MetadataWriteSettings writeSettings = MetadataHub::defaultWriteSettings();
|
|
|
|
int i=0;
|
|
for (ImageInfo *info = d->currInfos.first(); info; info = d->currInfos.next())
|
|
{
|
|
MetadataHub fileHub;
|
|
// read in from database
|
|
fileHub.load(info);
|
|
// write out to file DMetadata
|
|
fileHub.write(info->filePath());
|
|
|
|
emit signalProgressValue((int)((i++/(float)d->currInfos.count())*100.0));
|
|
kapp->processEvents();
|
|
}
|
|
|
|
emit signalProgressBarMode(StatusProgressBar::TextMode, TQString());
|
|
}
|
|
|
|
bool ImageDescEditTab::eventFilter(TQObject *, TQEvent *e)
|
|
{
|
|
if ( e->type() == TQEvent::KeyPress )
|
|
{
|
|
TQKeyEvent *k = (TQKeyEvent *)e;
|
|
if (k->state() == TQt::ControlButton &&
|
|
(k->key() == TQt::Key_Enter || k->key() == TQt::Key_Return))
|
|
{
|
|
emit signalNextItem();
|
|
return true;
|
|
}
|
|
else if (k->state() == TQt::ShiftButton &&
|
|
(k->key() == TQt::Key_Enter || k->key() == TQt::Key_Return))
|
|
{
|
|
emit signalPrevItem();
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
void ImageDescEditTab::populateTags()
|
|
{
|
|
d->tagsView->clear();
|
|
|
|
AlbumList tList = AlbumManager::instance()->allTAlbums();
|
|
for (AlbumList::iterator it = tList.begin(); it != tList.end(); ++it)
|
|
{
|
|
TAlbum *tag = (TAlbum*)(*it);
|
|
slotAlbumAdded(tag);
|
|
}
|
|
|
|
d->tagsView->loadViewState();
|
|
}
|
|
|
|
void ImageDescEditTab::slotItemStateChanged(TAlbumCheckListItem *item)
|
|
{
|
|
TagFilterView::ToggleAutoTags oldAutoTags = d->toggleAutoTags;
|
|
|
|
switch(d->toggleAutoTags)
|
|
{
|
|
case TagFilterView::Children:
|
|
d->toggleAutoTags = TagFilterView::NoToggleAuto;
|
|
toggleChildTags(item->album(), item->isOn());
|
|
d->toggleAutoTags = oldAutoTags;
|
|
break;
|
|
case TagFilterView::Parents:
|
|
d->toggleAutoTags = TagFilterView::NoToggleAuto;
|
|
toggleParentTags(item->album(), item->isOn());
|
|
d->toggleAutoTags = oldAutoTags;
|
|
break;
|
|
case TagFilterView::ChildrenAndParents:
|
|
d->toggleAutoTags = TagFilterView::NoToggleAuto;
|
|
toggleChildTags(item->album(), item->isOn());
|
|
toggleParentTags(item->album(), item->isOn());
|
|
d->toggleAutoTags = oldAutoTags;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
d->hub.setTag(item->album(), item->isOn());
|
|
|
|
d->tagsView->blockSignals(true);
|
|
item->setStatus(d->hub.tagStatus(item->album()));
|
|
d->tagsView->blockSignals(false);
|
|
|
|
slotModified();
|
|
}
|
|
|
|
void ImageDescEditTab::slotCommentChanged()
|
|
{
|
|
// we cannot trust that the text actually changed
|
|
// (there are bogus signals caused by spell checking, see bug 141663)
|
|
// so we have to check before marking the metadata as modified
|
|
if (d->hub.comment() == d->commentsEdit->text())
|
|
return;
|
|
|
|
d->hub.setComment(d->commentsEdit->text());
|
|
setMetadataWidgetStatus(d->hub.commentStatus(), d->commentsEdit);
|
|
slotModified();
|
|
}
|
|
|
|
void ImageDescEditTab::slotDateTimeChanged(const TQDateTime& dateTime)
|
|
{
|
|
d->hub.setDateTime(dateTime);
|
|
setMetadataWidgetStatus(d->hub.dateTimeStatus(), d->dateTimeEdit);
|
|
slotModified();
|
|
}
|
|
|
|
void ImageDescEditTab::slotRatingChanged(int rating)
|
|
{
|
|
d->hub.setRating(rating);
|
|
// no handling for MetadataDisjoint needed for rating,
|
|
// we set it to 0 when disjoint, see below
|
|
slotModified();
|
|
}
|
|
|
|
void ImageDescEditTab::slotModified()
|
|
{
|
|
d->modified = true;
|
|
d->applyBtn->setEnabled(true);
|
|
d->revertBtn->setEnabled(true);
|
|
}
|
|
|
|
void ImageDescEditTab::assignRating(int rating)
|
|
{
|
|
d->ratingWidget->setRating(rating);
|
|
}
|
|
|
|
void ImageDescEditTab::updateTagsView()
|
|
{
|
|
d->tagsView->blockSignals(true);
|
|
|
|
TQListViewItemIterator it( d->tagsView);
|
|
while (it.current())
|
|
{
|
|
TAlbumCheckListItem* tItem = dynamic_cast<TAlbumCheckListItem*>(it.current());
|
|
if (tItem)
|
|
tItem->setStatus(d->hub.tagStatus(tItem->album()));
|
|
++it;
|
|
}
|
|
|
|
// The condition is a temporary fix not to destroy name filtering on image change.
|
|
// See comments in these methods.
|
|
if (d->assignedTagsBtn->isOn())
|
|
slotAssignedTagsToggled(d->assignedTagsBtn->isOn());
|
|
|
|
d->tagsView->blockSignals(false);
|
|
}
|
|
|
|
void ImageDescEditTab::updateComments()
|
|
{
|
|
d->commentsEdit->blockSignals(true);
|
|
d->commentsEdit->setText(d->hub.comment());
|
|
setMetadataWidgetStatus(d->hub.commentStatus(), d->commentsEdit);
|
|
d->commentsEdit->blockSignals(false);
|
|
}
|
|
|
|
void ImageDescEditTab::updateRating()
|
|
{
|
|
d->ratingWidget->blockSignals(true);
|
|
if (d->hub.ratingStatus() == MetadataHub::MetadataDisjoint)
|
|
d->ratingWidget->setRating(0);
|
|
else
|
|
d->ratingWidget->setRating(d->hub.rating());
|
|
d->ratingWidget->blockSignals(false);
|
|
}
|
|
|
|
void ImageDescEditTab::updateDate()
|
|
{
|
|
d->dateTimeEdit->blockSignals(true);
|
|
d->dateTimeEdit->setDateTime(d->hub.dateTime());
|
|
setMetadataWidgetStatus(d->hub.dateTimeStatus(), d->dateTimeEdit);
|
|
d->dateTimeEdit->blockSignals(false);
|
|
}
|
|
|
|
void ImageDescEditTab::setMetadataWidgetStatus(int status, TQWidget *widget)
|
|
{
|
|
if (status == MetadataHub::MetadataDisjoint)
|
|
{
|
|
// For text widgets: Set text color to color of disabled text
|
|
TQPalette palette = widget->palette();
|
|
palette.setColor(TQColorGroup::Text, palette.color(TQPalette::Disabled, TQColorGroup::Text));
|
|
widget->setPalette(palette);
|
|
}
|
|
else
|
|
{
|
|
widget->unsetPalette();
|
|
}
|
|
}
|
|
|
|
void ImageDescEditTab::slotRightButtonClicked(TQListViewItem *item, const TQPoint &, int )
|
|
{
|
|
TAlbum *album;
|
|
|
|
if (!item)
|
|
{
|
|
album = AlbumManager::instance()->findTAlbum(0);
|
|
}
|
|
else
|
|
{
|
|
TAlbumCheckListItem* viewItem = dynamic_cast<TAlbumCheckListItem*>(item);
|
|
|
|
if(!viewItem)
|
|
album = AlbumManager::instance()->findTAlbum(0);
|
|
else
|
|
album = viewItem->album();
|
|
}
|
|
|
|
if(!album)
|
|
return;
|
|
|
|
d->ABCMenu = new TQPopupMenu;
|
|
|
|
connect(d->ABCMenu, TQT_SIGNAL( aboutToShow() ),
|
|
this, TQT_SLOT( slotABCContextMenu() ));
|
|
|
|
TDEPopupMenu popmenu(this);
|
|
popmenu.insertTitle(SmallIcon("digikam"), i18n("Tags"));
|
|
popmenu.insertItem(SmallIcon("tag-new"), i18n("New Tag..."), 10);
|
|
popmenu.insertItem(SmallIcon("tag-addressbook"), i18n("Create Tag From AddressBook"), d->ABCMenu);
|
|
|
|
if (!album->isRoot())
|
|
{
|
|
popmenu.insertItem(SmallIcon("tag-properties"), i18n("Edit Tag Properties..."), 11);
|
|
popmenu.insertItem(SmallIcon("tag-reset"), i18n("Reset Tag Icon"), 13);
|
|
popmenu.insertSeparator(-1);
|
|
popmenu.insertItem(SmallIcon("tag-delete"), i18n("Delete Tag"), 12);
|
|
}
|
|
|
|
popmenu.insertSeparator(-1);
|
|
|
|
TQPopupMenu selectTagsMenu;
|
|
selectTagsMenu.insertItem(i18n("All Tags"), 14);
|
|
if (!album->isRoot())
|
|
{
|
|
selectTagsMenu.insertSeparator(-1);
|
|
selectTagsMenu.insertItem(i18n("Children"), 17);
|
|
selectTagsMenu.insertItem(i18n("Parents"), 19);
|
|
}
|
|
popmenu.insertItem(i18n("Select"), &selectTagsMenu);
|
|
|
|
TQPopupMenu deselectTagsMenu;
|
|
deselectTagsMenu.insertItem(i18n("All Tags"), 15);
|
|
if (!album->isRoot())
|
|
{
|
|
deselectTagsMenu.insertSeparator(-1);
|
|
deselectTagsMenu.insertItem(i18n("Children"), 18);
|
|
deselectTagsMenu.insertItem(i18n("Parents"), 20);
|
|
}
|
|
popmenu.insertItem(i18n("Deselect"), &deselectTagsMenu);
|
|
|
|
popmenu.insertItem(i18n("Invert Selection"), 16);
|
|
popmenu.insertSeparator(-1);
|
|
|
|
TQPopupMenu toggleAutoMenu;
|
|
toggleAutoMenu.setCheckable(true);
|
|
toggleAutoMenu.insertItem(i18n("None"), 21);
|
|
toggleAutoMenu.insertSeparator(-1);
|
|
toggleAutoMenu.insertItem(i18n("Children"), 22);
|
|
toggleAutoMenu.insertItem(i18n("Parents"), 23);
|
|
toggleAutoMenu.insertItem(i18n("Both"), 24);
|
|
toggleAutoMenu.setItemChecked(21 + d->toggleAutoTags, true);
|
|
popmenu.insertItem(i18n("Toggle Auto"), &toggleAutoMenu);
|
|
|
|
TagFilterView::ToggleAutoTags oldAutoTags = d->toggleAutoTags;
|
|
|
|
int choice = popmenu.exec((TQCursor::pos()));
|
|
switch( choice )
|
|
{
|
|
case 10: // New Tag.
|
|
{
|
|
tagNew(album);
|
|
break;
|
|
}
|
|
case 11: // Edit Tag Properties.
|
|
{
|
|
if (!album->isRoot())
|
|
tagEdit(album);
|
|
break;
|
|
}
|
|
case 12: // Delete Tag.
|
|
{
|
|
if (!album->isRoot())
|
|
tagDelete(album);
|
|
break;
|
|
}
|
|
case 13: // Reset Tag Icon.
|
|
{
|
|
TQString errMsg;
|
|
AlbumManager::instance()->updateTAlbumIcon(album, TQString("tag"), 0, errMsg);
|
|
break;
|
|
}
|
|
case 14: // Select All Tags.
|
|
{
|
|
d->toggleAutoTags = TagFilterView::NoToggleAuto;
|
|
TQListViewItemIterator it(d->tagsView, TQListViewItemIterator::NotChecked);
|
|
while (it.current())
|
|
{
|
|
TAlbumCheckListItem* item = dynamic_cast<TAlbumCheckListItem*>(it.current());
|
|
if (item->isVisible())
|
|
item->setOn(true);
|
|
++it;
|
|
}
|
|
d->toggleAutoTags = oldAutoTags;
|
|
break;
|
|
}
|
|
case 15: // Deselect All Tags.
|
|
{
|
|
d->toggleAutoTags = TagFilterView::NoToggleAuto;
|
|
TQListViewItemIterator it(d->tagsView, TQListViewItemIterator::Checked);
|
|
while (it.current())
|
|
{
|
|
TAlbumCheckListItem* item = dynamic_cast<TAlbumCheckListItem*>(it.current());
|
|
if (item->isVisible())
|
|
item->setOn(false);
|
|
++it;
|
|
}
|
|
d->toggleAutoTags = oldAutoTags;
|
|
break;
|
|
}
|
|
case 16: // Invert All Tags Selection.
|
|
{
|
|
d->toggleAutoTags = TagFilterView::NoToggleAuto;
|
|
TQListViewItemIterator it(d->tagsView);
|
|
while (it.current())
|
|
{
|
|
TAlbumCheckListItem* item = dynamic_cast<TAlbumCheckListItem*>(it.current());
|
|
if (item->isVisible())
|
|
item->setOn(!item->isOn());
|
|
++it;
|
|
}
|
|
d->toggleAutoTags = oldAutoTags;
|
|
break;
|
|
}
|
|
case 17: // Select Child Tags.
|
|
{
|
|
d->toggleAutoTags = TagFilterView::NoToggleAuto;
|
|
toggleChildTags(album, true);
|
|
TAlbumCheckListItem *item = (TAlbumCheckListItem*)album->extraData(d->tagsView);
|
|
item->setOn(true);
|
|
d->toggleAutoTags = oldAutoTags;
|
|
break;
|
|
}
|
|
case 18: // Deselect Child Tags.
|
|
{
|
|
d->toggleAutoTags = TagFilterView::NoToggleAuto;
|
|
toggleChildTags(album, false);
|
|
TAlbumCheckListItem *item = (TAlbumCheckListItem*)album->extraData(d->tagsView);
|
|
item->setOn(false);
|
|
d->toggleAutoTags = oldAutoTags;
|
|
break;
|
|
}
|
|
case 19: // Select Parent Tags.
|
|
{
|
|
d->toggleAutoTags = TagFilterView::NoToggleAuto;
|
|
toggleParentTags(album, true);
|
|
TAlbumCheckListItem *item = (TAlbumCheckListItem*)album->extraData(d->tagsView);
|
|
item->setOn(true);
|
|
d->toggleAutoTags = oldAutoTags;
|
|
break;
|
|
}
|
|
case 20: // Deselect Parent Tags.
|
|
{
|
|
d->toggleAutoTags = TagFilterView::NoToggleAuto;
|
|
toggleParentTags(album, false);
|
|
TAlbumCheckListItem *item = (TAlbumCheckListItem*)album->extraData(d->tagsView);
|
|
item->setOn(false);
|
|
d->toggleAutoTags = oldAutoTags;
|
|
break;
|
|
}
|
|
case 21: // No toggle auto tags.
|
|
{
|
|
d->toggleAutoTags = TagFilterView::NoToggleAuto;
|
|
break;
|
|
}
|
|
case 22: // Toggle auto Children tags.
|
|
{
|
|
d->toggleAutoTags = TagFilterView::Children;
|
|
break;
|
|
}
|
|
case 23: // Toggle auto Parents tags.
|
|
{
|
|
d->toggleAutoTags = TagFilterView::Parents;
|
|
break;
|
|
}
|
|
case 24: // Toggle auto Children and Parents tags.
|
|
{
|
|
d->toggleAutoTags = TagFilterView::ChildrenAndParents;
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if ( choice > 100 )
|
|
{
|
|
tagNew(album, d->ABCMenu->text( choice ), "tag-people" );
|
|
}
|
|
|
|
delete d->ABCMenu;
|
|
d->ABCMenu = 0;
|
|
}
|
|
|
|
void ImageDescEditTab::slotABCContextMenu()
|
|
{
|
|
d->ABCMenu->clear();
|
|
|
|
int counter = 100;
|
|
TDEABC::AddressBook* ab = TDEABC::StdAddressBook::self();
|
|
TQStringList names;
|
|
for ( TDEABC::AddressBook::Iterator it = ab->begin(); it != ab->end(); ++it )
|
|
{
|
|
names.push_back(it->formattedName());
|
|
}
|
|
|
|
qHeapSort(names);
|
|
|
|
for ( TQStringList::Iterator it = names.begin(); it != names.end(); ++it )
|
|
{
|
|
TQString name = *it;
|
|
if ( !name.isNull() )
|
|
d->ABCMenu->insertItem( name, ++counter );
|
|
}
|
|
|
|
if (counter == 100)
|
|
{
|
|
d->ABCMenu->insertItem( i18n("No AddressBook Entries Found"), ++counter );
|
|
d->ABCMenu->setItemEnabled( counter, false );
|
|
}
|
|
}
|
|
|
|
void ImageDescEditTab::slotMoreMenu()
|
|
{
|
|
d->moreMenu->clear();
|
|
|
|
if (singleSelection())
|
|
{
|
|
d->moreMenu->insertItem(i18n("Read metadata from file to database"), this, TQT_SLOT(slotReadFromFileMetadataToDatabase()));
|
|
int writeActionId = d->moreMenu->insertItem(i18n("Write metadata to each file"), this, TQT_SLOT(slotWriteToFileMetadataFromDatabase()));
|
|
// we do not need a "Write to file" action here because the apply button will do just that
|
|
// if selection is a single file.
|
|
// Adding the option will confuse users: Does the apply button not write to file?
|
|
// Removing the option will confuse users: There is not option to write to file! (not visible in single selection)
|
|
// Disabling will confuse users: Why is it disabled?
|
|
d->moreMenu->setItemEnabled(writeActionId, false);
|
|
}
|
|
else
|
|
{
|
|
// We need to make clear that this action is different from the Apply button,
|
|
// which saves the same changes to all files. These batch operations operate on each single file.
|
|
d->moreMenu->insertItem(i18n("Read metadata from each file to database"), this, TQT_SLOT(slotReadFromFileMetadataToDatabase()));
|
|
d->moreMenu->insertItem(i18n("Write metadata to each file"), this, TQT_SLOT(slotWriteToFileMetadataFromDatabase()));
|
|
}
|
|
}
|
|
|
|
void ImageDescEditTab::tagNew(TAlbum* parAlbum, const TQString& _title, const TQString& _icon) const
|
|
{
|
|
if (!parAlbum)
|
|
return;
|
|
|
|
TQString title = _title;
|
|
TQString icon = _icon;
|
|
|
|
if (title.isNull())
|
|
{
|
|
if (!TagEditDlg::tagCreate(TQT_TQWIDGET(TQT_TQWIDGET(kapp->activeWindow())), parAlbum, title, icon))
|
|
return;
|
|
}
|
|
|
|
TQMap<TQString, TQString> errMap;
|
|
AlbumList tList = TagEditDlg::createTAlbum(parAlbum, title, icon, errMap);
|
|
TagEditDlg::showtagsListCreationError(TQT_TQWIDGET(kapp->activeWindow()), errMap);
|
|
|
|
for (AlbumList::iterator it = tList.begin(); it != tList.end(); ++it)
|
|
{
|
|
TAlbumCheckListItem* item = (TAlbumCheckListItem*)(*it)->extraData(d->tagsView);
|
|
if (item)
|
|
{
|
|
item->setOn(true);
|
|
d->tagsView->setSelected(item, true);
|
|
d->tagsView->ensureItemVisible(item);
|
|
}
|
|
}
|
|
}
|
|
|
|
void ImageDescEditTab::tagDelete(TAlbum *album)
|
|
{
|
|
if (!album || album->isRoot())
|
|
return;
|
|
|
|
AlbumManager *albumMan = AlbumManager::instance();
|
|
|
|
if (album == albumMan->currentAlbum() ||
|
|
album->isAncestorOf(albumMan->currentAlbum()))
|
|
{
|
|
KMessageBox::error(this, i18n("You are currently viewing items in the "
|
|
"tag '%1' that you are about to delete. "
|
|
"You will need to apply change first "
|
|
"if you want to delete the tag." )
|
|
.arg(album->title()));
|
|
return;
|
|
}
|
|
|
|
// find number of subtags
|
|
int children = 0;
|
|
AlbumIterator iter(album);
|
|
while(iter.current())
|
|
{
|
|
children++;
|
|
++iter;
|
|
}
|
|
|
|
if(children)
|
|
{
|
|
int result = KMessageBox::warningContinueCancel(this,
|
|
i18n("Tag '%1' has one subtag. "
|
|
"Deleting this will also delete "
|
|
"the subtag. "
|
|
"Do you want to continue?",
|
|
"Tag '%1' has %n subtags. "
|
|
"Deleting this will also delete "
|
|
"the subtags. "
|
|
"Do you want to continue?",
|
|
children).arg(album->title()));
|
|
|
|
if(result != KMessageBox::Continue)
|
|
return;
|
|
}
|
|
|
|
TQString message;
|
|
LLongList assignedItems = albumMan->albumDB()->getItemIDsInTag(album->id());
|
|
if (!assignedItems.isEmpty())
|
|
{
|
|
message = i18n("Tag '%1' is assigned to one item. "
|
|
"Do you want to continue?",
|
|
"Tag '%1' is assigned to %n items. "
|
|
"Do you want to continue?",
|
|
assignedItems.count()).arg(album->title());
|
|
}
|
|
else
|
|
{
|
|
message = i18n("Delete '%1' tag?").arg(album->title());
|
|
}
|
|
|
|
int result = KMessageBox::warningContinueCancel(this, message,
|
|
i18n("Delete Tag"),
|
|
KGuiItem(i18n("Delete"),
|
|
"edit-delete"));
|
|
|
|
if (result == KMessageBox::Continue)
|
|
{
|
|
TQString errMsg;
|
|
if (!albumMan->deleteTAlbum(album, errMsg))
|
|
KMessageBox::error(this, errMsg);
|
|
}
|
|
}
|
|
|
|
void ImageDescEditTab::tagEdit(TAlbum* album)
|
|
{
|
|
if (!album || album->isRoot())
|
|
return;
|
|
|
|
TQString title;
|
|
TQString icon;
|
|
|
|
if (!TagEditDlg::tagEdit(TQT_TQWIDGET(kapp->activeWindow()), album, title, icon))
|
|
return;
|
|
|
|
AlbumManager *albumMan = AlbumManager::instance();
|
|
if (album->title() != title)
|
|
{
|
|
TQString errMsg;
|
|
if (!albumMan->renameTAlbum(album, title, errMsg))
|
|
{
|
|
KMessageBox::error(this, errMsg);
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (album->icon() != icon)
|
|
{
|
|
TQString errMsg;
|
|
if (!albumMan->updateTAlbumIcon(album, icon, 0, errMsg))
|
|
{
|
|
KMessageBox::error(this, errMsg);
|
|
}
|
|
}
|
|
}
|
|
|
|
void ImageDescEditTab::slotAlbumAdded(Album* a)
|
|
{
|
|
if (!a || a->type() != Album::TAG)
|
|
return;
|
|
|
|
TAlbumCheckListItem* viewItem = 0;
|
|
|
|
TAlbum* tag = dynamic_cast<TAlbum*>(a);
|
|
if (!tag)
|
|
return;
|
|
|
|
if (tag->isRoot())
|
|
{
|
|
viewItem = new TAlbumCheckListItem(d->tagsView, tag);
|
|
}
|
|
else
|
|
{
|
|
TAlbumCheckListItem* parent = (TAlbumCheckListItem*)(tag->parent()->extraData(d->tagsView));
|
|
if (!parent)
|
|
{
|
|
DWarning() << k_funcinfo << "Failed to find parent for Tag " << tag->title()
|
|
<< endl;
|
|
return;
|
|
}
|
|
|
|
viewItem = new TAlbumCheckListItem(parent, tag);
|
|
d->tagsSearchBar->lineEdit()->completionObject()->addItem(tag->title());
|
|
d->newTagEdit->lineEdit()->completionObject()->addItem(tag->tagPath());
|
|
d->newTagEdit->lineEdit()->completionObject()->addItem(tag->tagPath().remove(0, 1)); // without root "/"
|
|
}
|
|
|
|
if (viewItem)
|
|
{
|
|
// commenting this out due to the issues described in bug 148166.
|
|
// viewItem->setOpen(true);
|
|
setTagThumbnail(tag);
|
|
}
|
|
}
|
|
|
|
void ImageDescEditTab::slotAlbumDeleted(Album* a)
|
|
{
|
|
if (!a || a->isRoot() || a->type() != Album::TAG)
|
|
return;
|
|
|
|
TAlbum* album = (TAlbum*)a;
|
|
|
|
d->tagsSearchBar->lineEdit()->completionObject()->removeItem(album->title());
|
|
d->newTagEdit->lineEdit()->completionObject()->removeItem(album->tagPath());
|
|
d->newTagEdit->lineEdit()->completionObject()->removeItem(album->tagPath().remove(0, 1)); // without root "/"
|
|
TAlbumCheckListItem* viewItem = (TAlbumCheckListItem*)album->extraData(d->tagsView);
|
|
delete viewItem;
|
|
album->removeExtraData(this);
|
|
d->hub.setTag(album, false, MetadataHub::MetadataDisjoint);
|
|
}
|
|
|
|
void ImageDescEditTab::slotAlbumsCleared()
|
|
{
|
|
d->tagsView->clear();
|
|
d->tagsSearchBar->lineEdit()->completionObject()->clear();
|
|
d->newTagEdit->lineEdit()->completionObject()->clear();
|
|
}
|
|
|
|
void ImageDescEditTab::slotAlbumIconChanged(Album* a)
|
|
{
|
|
if (!a || a->isRoot() || a->type() != Album::TAG)
|
|
return;
|
|
|
|
setTagThumbnail((TAlbum *)a);
|
|
}
|
|
|
|
void ImageDescEditTab::slotAlbumMoved(TAlbum* tag, TAlbum* newParent)
|
|
{
|
|
if (!tag || !newParent)
|
|
return;
|
|
|
|
TAlbumCheckListItem* item = (TAlbumCheckListItem*)tag->extraData(d->tagsView);
|
|
if (!item)
|
|
return;
|
|
|
|
if (item->parent())
|
|
{
|
|
TQListViewItem* oldPItem = item->parent();
|
|
oldPItem->takeItem(item);
|
|
}
|
|
else
|
|
{
|
|
d->tagsView->takeItem(item);
|
|
}
|
|
|
|
TAlbumCheckListItem* newPItem = (TAlbumCheckListItem*)newParent->extraData(d->tagsView);
|
|
if (newPItem)
|
|
newPItem->insertItem(item);
|
|
else
|
|
d->tagsView->insertItem(item);
|
|
}
|
|
|
|
void ImageDescEditTab::slotAlbumRenamed(Album* album)
|
|
{
|
|
if (!album || album->isRoot() || album->type() != Album::TAG)
|
|
return;
|
|
|
|
TAlbum* tag = (TAlbum*)album;
|
|
d->tagsSearchBar->lineEdit()->completionObject()->addItem(tag->title());
|
|
d->newTagEdit->lineEdit()->completionObject()->addItem(tag->tagPath());
|
|
d->newTagEdit->lineEdit()->completionObject()->addItem(tag->tagPath().remove(0, 1)); // without root "/"
|
|
slotTagsSearchChanged(d->tagsSearchBar->lineEdit()->text());
|
|
TAlbumCheckListItem* item = (TAlbumCheckListItem*)(tag->extraData(d->tagsView));
|
|
if (item)
|
|
item->refresh();
|
|
}
|
|
|
|
void ImageDescEditTab::toggleChildTags(TAlbum *album, bool b)
|
|
{
|
|
if (!album)
|
|
return;
|
|
|
|
AlbumIterator it(album);
|
|
while ( it.current() )
|
|
{
|
|
TAlbum *ta = (TAlbum*)it.current();
|
|
TAlbumCheckListItem *item = (TAlbumCheckListItem*)(ta->extraData(d->tagsView));
|
|
if (item)
|
|
if (item->isVisible())
|
|
item->setOn(b);
|
|
++it;
|
|
}
|
|
}
|
|
|
|
void ImageDescEditTab::toggleParentTags(TAlbum *album, bool b)
|
|
{
|
|
if (!album)
|
|
return;
|
|
|
|
TQListViewItemIterator it(d->tagsView);
|
|
while (it.current())
|
|
{
|
|
TAlbumCheckListItem* item = dynamic_cast<TAlbumCheckListItem*>(it.current());
|
|
if (item->isVisible())
|
|
{
|
|
if (!item->album())
|
|
continue;
|
|
if (item->album() == album->parent())
|
|
{
|
|
item->setOn(b);
|
|
toggleParentTags(item->album() , b);
|
|
}
|
|
}
|
|
++it;
|
|
}
|
|
}
|
|
|
|
void ImageDescEditTab::setTagThumbnail(TAlbum *album)
|
|
{
|
|
if(!album)
|
|
return;
|
|
|
|
TAlbumCheckListItem* item = (TAlbumCheckListItem*)album->extraData(d->tagsView);
|
|
|
|
if(!item)
|
|
return;
|
|
|
|
AlbumThumbnailLoader *loader = AlbumThumbnailLoader::instance();
|
|
TQPixmap icon;
|
|
if (!loader->getTagThumbnail(album, icon))
|
|
{
|
|
if (icon.isNull())
|
|
{
|
|
item->setPixmap(0, loader->getStandardTagIcon(album));
|
|
}
|
|
else
|
|
{
|
|
TQPixmap blendedIcon = loader->blendIcons(loader->getStandardTagIcon(), icon);
|
|
item->setPixmap(0, blendedIcon);
|
|
}
|
|
}
|
|
}
|
|
|
|
void ImageDescEditTab::slotGotThumbnailFromIcon(Album *album, const TQPixmap& thumbnail)
|
|
{
|
|
if(!album || album->type() != Album::TAG)
|
|
return;
|
|
|
|
// update item in tags tree
|
|
TAlbumCheckListItem* item = (TAlbumCheckListItem*)album->extraData(d->tagsView);
|
|
if(!item)
|
|
return;
|
|
|
|
AlbumThumbnailLoader *loader = AlbumThumbnailLoader::instance();
|
|
TQPixmap blendedIcon = loader->blendIcons(loader->getStandardTagIcon(), thumbnail);
|
|
item->setPixmap(0, blendedIcon);
|
|
|
|
// update item in recent tags popup menu, if found there in
|
|
TQPopupMenu *menu = d->recentTagsBtn->popup();
|
|
if (menu->indexOf(album->id()) != -1)
|
|
{
|
|
menu->changeItem(album->id(), thumbnail, menu->text(album->id()));
|
|
}
|
|
}
|
|
|
|
void ImageDescEditTab::slotThumbnailLost(Album *)
|
|
{
|
|
// we already set the standard icon before loading
|
|
}
|
|
|
|
void ImageDescEditTab::slotReloadThumbnails()
|
|
{
|
|
AlbumList tList = AlbumManager::instance()->allTAlbums();
|
|
for (AlbumList::iterator it = tList.begin(); it != tList.end(); ++it)
|
|
{
|
|
TAlbum* tag = (TAlbum*)(*it);
|
|
setTagThumbnail(tag);
|
|
}
|
|
}
|
|
|
|
void ImageDescEditTab::slotImageTagsChanged(TQ_LLONG imageId)
|
|
{
|
|
// don't lose modifications
|
|
if (d->ignoreImageAttributesWatch || d->modified)
|
|
return;
|
|
|
|
reloadForMetadataChange(imageId);
|
|
}
|
|
|
|
void ImageDescEditTab::slotImagesChanged(int albumId)
|
|
{
|
|
if (d->ignoreImageAttributesWatch || d->modified)
|
|
return;
|
|
|
|
Album *a = AlbumManager::instance()->findAlbum(albumId);
|
|
if (d->currInfos.isEmpty() || !a || a->isRoot() || a->type() != Album::TAG)
|
|
return;
|
|
|
|
setInfos(d->currInfos);
|
|
}
|
|
|
|
void ImageDescEditTab::slotImageRatingChanged(TQ_LLONG imageId)
|
|
{
|
|
if (d->ignoreImageAttributesWatch || d->modified)
|
|
return;
|
|
|
|
reloadForMetadataChange(imageId);
|
|
}
|
|
|
|
void ImageDescEditTab::slotImageCaptionChanged(TQ_LLONG imageId)
|
|
{
|
|
if (d->ignoreImageAttributesWatch || d->modified)
|
|
return;
|
|
|
|
reloadForMetadataChange(imageId);
|
|
}
|
|
|
|
void ImageDescEditTab::slotImageDateChanged(TQ_LLONG imageId)
|
|
{
|
|
if (d->ignoreImageAttributesWatch || d->modified)
|
|
return;
|
|
|
|
reloadForMetadataChange(imageId);
|
|
}
|
|
|
|
// private common code for above methods
|
|
void ImageDescEditTab::reloadForMetadataChange(TQ_LLONG imageId)
|
|
{
|
|
if (d->currInfos.isEmpty())
|
|
return;
|
|
|
|
if (singleSelection())
|
|
{
|
|
if (d->currInfos.first()->id() == imageId)
|
|
setInfos(d->currInfos);
|
|
}
|
|
else
|
|
{
|
|
// if image id is in our list, update
|
|
for (ImageInfo *info = d->currInfos.first(); info; info = d->currInfos.next())
|
|
{
|
|
if (info->id() == imageId)
|
|
{
|
|
setInfos(d->currInfos);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void ImageDescEditTab::updateRecentTags()
|
|
{
|
|
TQPopupMenu *menu = d->recentTagsBtn->popup();
|
|
menu->clear();
|
|
|
|
AlbumManager* albumMan = AlbumManager::instance();
|
|
IntList recentTags = albumMan->albumDB()->getRecentlyAssignedTags();
|
|
|
|
if (recentTags.isEmpty())
|
|
{
|
|
menu->insertItem(i18n("No Recently Assigned Tags"), 0);
|
|
menu->setItemEnabled(0, false);
|
|
}
|
|
else
|
|
{
|
|
for (IntList::const_iterator it = recentTags.begin();
|
|
it != recentTags.end(); ++it)
|
|
{
|
|
TAlbum* album = albumMan->findTAlbum(*it);
|
|
if (album)
|
|
{
|
|
AlbumThumbnailLoader *loader = AlbumThumbnailLoader::instance();
|
|
TQPixmap icon;
|
|
if (!loader->getTagThumbnail(album, icon))
|
|
{
|
|
if (icon.isNull())
|
|
{
|
|
icon = loader->getStandardTagIcon(album, AlbumThumbnailLoader::SmallerSize);
|
|
}
|
|
}
|
|
TQString text = album->title() + " (" + ((TAlbum*)album->parent())->prettyURL() + ')';
|
|
menu->insertItem(icon, text, album->id());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void ImageDescEditTab::slotRecentTagsMenuActivated(int id)
|
|
{
|
|
AlbumManager* albumMan = AlbumManager::instance();
|
|
|
|
if (id > 0)
|
|
{
|
|
TAlbum* album = albumMan->findTAlbum(id);
|
|
if (album)
|
|
{
|
|
TAlbumCheckListItem* viewItem = (TAlbumCheckListItem*)album->extraData(d->tagsView);
|
|
if (viewItem)
|
|
{
|
|
viewItem->setOn(true);
|
|
d->tagsView->setSelected(viewItem, true);
|
|
d->tagsView->ensureItemVisible(viewItem);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void ImageDescEditTab::slotTagsSearchChanged(const TQString& filter)
|
|
{
|
|
if (filter.isEmpty())
|
|
{
|
|
d->tagsView->collapseView(FolderView::OmitRoot);
|
|
return;
|
|
}
|
|
|
|
//TODO: this will destroy assigned-tags filtering. Unify in one method.
|
|
TQString search = filter.lower();
|
|
|
|
bool atleastOneMatch = false;
|
|
|
|
AlbumList tList = AlbumManager::instance()->allTAlbums();
|
|
for (AlbumList::iterator it = tList.begin(); it != tList.end(); ++it)
|
|
{
|
|
TAlbum* tag = (TAlbum*)(*it);
|
|
|
|
// don't touch the root Tag
|
|
if (tag->isRoot())
|
|
continue;
|
|
|
|
bool match = tag->title().lower().contains(search);
|
|
bool doesExpand = false;
|
|
if (!match)
|
|
{
|
|
// check if any of the parents match the search
|
|
Album* parent = tag->parent();
|
|
while (parent && !parent->isRoot())
|
|
{
|
|
if (parent->title().lower().contains(search))
|
|
{
|
|
match = true;
|
|
break;
|
|
}
|
|
|
|
parent = parent->parent();
|
|
}
|
|
}
|
|
|
|
if (!match)
|
|
{
|
|
// check if any of the children match the search
|
|
AlbumIterator it(tag);
|
|
while (it.current())
|
|
{
|
|
if ((*it)->title().lower().contains(search))
|
|
{
|
|
match = true;
|
|
doesExpand = true;
|
|
break;
|
|
}
|
|
++it;
|
|
}
|
|
}
|
|
|
|
TAlbumCheckListItem* viewItem = (TAlbumCheckListItem*)(tag->extraData(d->tagsView));
|
|
|
|
if (match)
|
|
{
|
|
atleastOneMatch = true;
|
|
|
|
if (viewItem)
|
|
{
|
|
viewItem->setVisible(true);
|
|
viewItem->setOpen(doesExpand);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (viewItem)
|
|
{
|
|
viewItem->setVisible(false);
|
|
viewItem->setOpen(false);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (search.isEmpty())
|
|
{
|
|
TAlbum* root = AlbumManager::instance()->findTAlbum(0);
|
|
TAlbumCheckListItem* rootItem = (TAlbumCheckListItem*)(root->extraData(d->tagsView));
|
|
if (rootItem)
|
|
rootItem->setText(0, root->title());
|
|
}
|
|
else
|
|
{
|
|
TAlbum* root = AlbumManager::instance()->findTAlbum(0);
|
|
TAlbumCheckListItem* rootItem = (TAlbumCheckListItem*)(root->extraData(d->tagsView));
|
|
if (rootItem)
|
|
rootItem->setText(0, i18n("Found Tags"));
|
|
}
|
|
|
|
emit signalTagFilterMatch(atleastOneMatch);
|
|
}
|
|
|
|
void ImageDescEditTab::slotAssignedTagsToggled(bool t)
|
|
{
|
|
//TODO: this will destroy name filtering. Unify in one method.
|
|
TQListViewItemIterator it(d->tagsView);
|
|
while (it.current())
|
|
{
|
|
TAlbumCheckListItem* item = dynamic_cast<TAlbumCheckListItem*>(it.current());
|
|
TAlbum *tag = item->album();
|
|
if (tag)
|
|
{
|
|
if (!tag->isRoot())
|
|
{
|
|
if (t)
|
|
{
|
|
MetadataHub::TagStatus status = d->hub.tagStatus(item->album());
|
|
bool tagAssigned = (status == MetadataHub::MetadataAvailable && status.hasTag)
|
|
|| status == MetadataHub::MetadataDisjoint;
|
|
item->setVisible(tagAssigned);
|
|
|
|
if (tagAssigned)
|
|
{
|
|
Album* parent = tag->parent();
|
|
while (parent && !parent->isRoot())
|
|
{
|
|
TAlbumCheckListItem *pitem = (TAlbumCheckListItem*)parent->extraData(d->tagsView);
|
|
pitem->setVisible(true);
|
|
parent = parent->parent();
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
item->setVisible(true);
|
|
}
|
|
}
|
|
}
|
|
++it;
|
|
}
|
|
|
|
// correct visibilities afterwards:
|
|
// As TQListViewItem::setVisible works recursively on all it's children
|
|
// we have to correct this
|
|
if (t)
|
|
{
|
|
it = d->tagsView;
|
|
while (it.current())
|
|
{
|
|
TAlbumCheckListItem* item = dynamic_cast<TAlbumCheckListItem*>(it.current());
|
|
TAlbum *tag = item->album();
|
|
if (tag)
|
|
{
|
|
if (!tag->isRoot())
|
|
{
|
|
// only if the current item is not marked as tagged, check all children
|
|
MetadataHub::TagStatus status = d->hub.tagStatus(item->album());
|
|
bool tagAssigned = (status == MetadataHub::MetadataAvailable && status.hasTag)
|
|
|| status == MetadataHub::MetadataDisjoint;
|
|
if (!tagAssigned)
|
|
{
|
|
bool somethingIsSet = false;
|
|
TQListViewItem* nextSibling = (*it)->nextSibling();
|
|
TQListViewItemIterator tmpIt = it;
|
|
++tmpIt;
|
|
while (*tmpIt != nextSibling )
|
|
{
|
|
TAlbumCheckListItem* tmpItem = dynamic_cast<TAlbumCheckListItem*>(tmpIt.current());
|
|
MetadataHub::TagStatus tmpStatus = d->hub.tagStatus(tmpItem->album());
|
|
bool tmpTagAssigned = (tmpStatus == MetadataHub::MetadataAvailable && tmpStatus.hasTag)
|
|
|| tmpStatus == MetadataHub::MetadataDisjoint;
|
|
if(tmpTagAssigned)
|
|
{
|
|
somethingIsSet = true;
|
|
}
|
|
++tmpIt;
|
|
}
|
|
if (!somethingIsSet)
|
|
{
|
|
item->setVisible(false);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
++it;
|
|
}
|
|
}
|
|
|
|
TAlbum *root = AlbumManager::instance()->findTAlbum(0);
|
|
TAlbumCheckListItem *rootItem = (TAlbumCheckListItem*)(root->extraData(d->tagsView));
|
|
if (rootItem)
|
|
{
|
|
if (t)
|
|
rootItem->setText(0, i18n("Assigned Tags"));
|
|
else
|
|
rootItem->setText(0, root->title());
|
|
}
|
|
}
|
|
|
|
void ImageDescEditTab::refreshTagsView()
|
|
{
|
|
d->tagsView->refresh();
|
|
}
|
|
|
|
void ImageDescEditTab::slotCreateNewTag()
|
|
{
|
|
TQString tagStr = d->newTagEdit->text();
|
|
if (tagStr.isEmpty()) return;
|
|
|
|
TAlbum *mainRootAlbum = 0;
|
|
TAlbumCheckListItem* item = dynamic_cast<TAlbumCheckListItem*>(d->tagsView->selectedItem());
|
|
if (item)
|
|
mainRootAlbum = item->album();
|
|
|
|
TQMap<TQString, TQString> errMap;
|
|
AlbumList tList = TagEditDlg::createTAlbum(mainRootAlbum, tagStr, TQString("tag"), errMap);
|
|
|
|
for (AlbumList::iterator it = tList.begin(); it != tList.end(); ++it)
|
|
{
|
|
TAlbumCheckListItem* item = (TAlbumCheckListItem*)(*it)->extraData(d->tagsView);
|
|
if (item)
|
|
{
|
|
item->setOn(true);
|
|
d->tagsView->ensureItemVisible(item);
|
|
}
|
|
}
|
|
|
|
d->newTagEdit->lineEdit()->clear();
|
|
}
|
|
|
|
} // NameSpace Digikam
|