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.
digikam/digikam/digikam/tagfilterview.cpp

1430 lines
39 KiB

/* ============================================================
*
* This file is a part of digiKam project
* http://www.digikam.org
*
* Date : 2005-05-05
* Description : tags filter view
*
* Copyright (C) 2005 by Renchi Raju <renchi@pooh.tam.uiuc.edu>
* Copyright (C) 2006-2009 by Gilles Caulier <caulier dot gilles at gmail dot com>
* 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 <tqheader.h>
#include <tqpixmap.h>
#include <tqpainter.h>
#include <tqtimer.h>
#include <tqcursor.h>
// KDE includes.
#include <kabc/stdaddressbook.h>
#include <kpopupmenu.h>
#include <klocale.h>
#include <kapplication.h>
#include <tdeconfig.h>
#include <kiconloader.h>
#include <kglobalsettings.h>
#include <kcursor.h>
#include <kmessagebox.h>
// Local includes.
#include "ddebug.h"
#include "albummanager.h"
#include "albumsettings.h"
#include "albumdb.h"
#include "album.h"
#include "albumlister.h"
#include "albumthumbnailloader.h"
#include "syncjob.h"
#include "dragobjects.h"
#include "folderitem.h"
#include "imageattributeswatch.h"
#include "imageinfo.h"
#include "metadatahub.h"
#include "tageditdlg.h"
#include "statusprogressbar.h"
#include "tagfilterview.h"
#include "tagfilterview.moc"
// X11 includes.
extern "C"
{
#include <X11/Xlib.h>
}
namespace Digikam
{
class TagFilterViewItem : public FolderCheckListItem
{
public:
TagFilterViewItem(TQListView* parent, TAlbum* tag, bool untagged=false);
TagFilterViewItem(TQListViewItem* parent, TAlbum* tag);
TAlbum* album() const;
int id() const;
bool untagged() const;
void refresh();
void setOpen(bool o);
void setCount(int count);
int count();
int compare(TQListViewItem* i, int column, bool ascending) const;
private:
void stateChange(bool val);
void paintCell(TQPainter* p, const TQColorGroup & cg, int column, int width, int align);
private:
bool m_untagged;
int m_count;
TAlbum *m_album;
};
TagFilterViewItem::TagFilterViewItem(TQListView* parent, TAlbum* album, bool untagged)
: FolderCheckListItem(parent, album ? album->title() : i18n("Not Tagged"),
TQCheckListItem::CheckBox/*Controller*/)
{
m_album = album;
m_untagged = untagged;
m_count = 0;
setDragEnabled(!untagged);
if (m_album)
m_album->setExtraData(listView(), this);
}
TagFilterViewItem::TagFilterViewItem(TQListViewItem* parent, TAlbum* album)
: FolderCheckListItem(parent, album->title(),
TQCheckListItem::CheckBox/*Controller*/)
{
m_album = album;
m_untagged = false;
m_count = 0;
setDragEnabled(true);
if (m_album)
m_album->setExtraData(listView(), this);
}
void TagFilterViewItem::refresh()
{
if (!m_album) return;
if (AlbumSettings::instance()->getShowFolderTreeViewItemsCount())
{
if (isOpen())
setText(0, TQString("%1 (%2)").arg(m_album->title()).arg(m_count));
else
{
int countRecursive = m_count;
AlbumIterator it(m_album);
while ( it.current() )
{
TagFilterViewItem *item = (TagFilterViewItem*)it.current()->extraData(listView());
if (item)
countRecursive += item->count();
++it;
}
setText(0, TQString("%1 (%2)").arg(m_album->title()).arg(countRecursive));
}
}
else
{
setText(0, m_album->title());
}
}
void TagFilterViewItem::stateChange(bool val)
{
TQCheckListItem::stateChange(val);
/* NOTE G.Caulier 2007/01/08: this code is now disable because TagFilterViewItem
have been changed from TQCheckListItem::CheckBoxController
to TQCheckListItem::CheckBox.
// All TagFilterViewItems are CheckBoxControllers. If they have no children,
// they should be of type CheckBox, but that is not possible with our way of adding items.
// When clicked, children-less items first change to the NoChange state, and a second
// click is necessary to set them to On and make the filter take effect.
// So set them to On if the condition is met.
if (!firstChild() && state() == NoChange)
{
setState(On);
}
*/
((TagFilterView*)listView())->stateChanged(this);
}
int TagFilterViewItem::compare(TQListViewItem* i, int column, bool ascending) const
{
if (m_untagged)
return 1;
TagFilterViewItem* dItem = dynamic_cast<TagFilterViewItem*>(i);
if (!dItem)
return 0;
if (dItem && dItem->m_untagged)
return -1;
return TQListViewItem::compare(i, column, ascending);
}
void TagFilterViewItem::paintCell(TQPainter* p, const TQColorGroup & cg, int column, int width, int align)
{
if (!m_untagged)
{
FolderCheckListItem::paintCell(p, cg, column, width, align);
return;
}
TQFont f(listView()->font());
f.setBold(true);
f.setItalic(true);
p->setFont(f);
TQColorGroup mcg(cg);
mcg.setColor(TQColorGroup::Text, TQt::darkRed);
FolderCheckListItem::paintCell(p, mcg, column, width, align);
}
void TagFilterViewItem::setOpen(bool o)
{
TQListViewItem::setOpen(o);
refresh();
}
TAlbum* TagFilterViewItem::album() const
{
return m_album;
}
int TagFilterViewItem::id() const
{
return m_album ? m_album->id() : 0;
}
void TagFilterViewItem::setCount(int count)
{
m_count = count;
refresh();
}
int TagFilterViewItem::count()
{
return m_count;
}
bool TagFilterViewItem::untagged() const
{
return m_untagged;
}
// ---------------------------------------------------------------------
class TagFilterViewPrivate
{
public:
TagFilterViewPrivate()
{
ABCMenu = 0;
timer = 0;
toggleAutoTags = TagFilterView::NoToggleAuto;
matchingCond = AlbumLister::OrCondition;
}
TQTimer *timer;
TQPopupMenu *ABCMenu;
TagFilterView::ToggleAutoTags toggleAutoTags;
AlbumLister::MatchingCondition matchingCond;
};
TagFilterView::TagFilterView(TQWidget* parent)
: FolderView(parent, "TagFilterView")
{
d = new TagFilterViewPrivate;
d->timer = new TQTimer(this);
addColumn(i18n("Tag Filters"));
setResizeMode(TQListView::LastColumn);
setRootIsDecorated(true);
setAcceptDrops(true);
viewport()->setAcceptDrops(true);
TagFilterViewItem* notTaggedItem = new TagFilterViewItem(this, 0, true);
notTaggedItem->setPixmap(0, AlbumThumbnailLoader::instance()->getStandardTagIcon());
// ------------------------------------------------------------------------
connect(AlbumManager::instance(), TQT_SIGNAL(signalTAlbumsDirty(const TQMap<int, int>&)),
this, TQT_SLOT(slotRefresh(const TQMap<int, int>&)));
connect(AlbumManager::instance(), TQT_SIGNAL(signalAlbumAdded(Album*)),
this, TQT_SLOT(slotTagAdded(Album*)));
connect(AlbumManager::instance(), TQT_SIGNAL(signalAlbumDeleted(Album*)),
this, TQT_SLOT(slotTagDeleted(Album*)));
connect(AlbumManager::instance(), TQT_SIGNAL(signalAlbumRenamed(Album*)),
this, TQT_SLOT(slotTagRenamed(Album*)));
connect(AlbumManager::instance(), TQT_SIGNAL(signalAlbumsCleared()),
this, TQT_SLOT(slotClear()));
connect(AlbumManager::instance(), TQT_SIGNAL(signalAlbumIconChanged(Album*)),
this, TQT_SLOT(slotAlbumIconChanged(Album*)));
connect(AlbumManager::instance(), TQT_SIGNAL(signalTAlbumMoved(TAlbum*, TAlbum*)),
this, TQT_SLOT(slotTagMoved(TAlbum*, TAlbum*)));
// ------------------------------------------------------------------------
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(this, TQT_SIGNAL(contextMenuRequested(TQListViewItem*, const TQPoint&, int)),
this, TQT_SLOT(slotContextMenu(TQListViewItem*, const TQPoint&, int)));
connect(d->timer, TQT_SIGNAL(timeout()),
this, TQT_SLOT(slotTimeOut()));
// ------------------------------------------------------------------------
TDEConfig* config = kapp->config();
config->setGroup("Tag Filters View");
d->matchingCond = (AlbumLister::MatchingCondition)(config->readNumEntry("Matching Condition",
AlbumLister::OrCondition));
d->toggleAutoTags = (ToggleAutoTags)(config->readNumEntry("Toggle Auto Tags", NoToggleAuto));
}
TagFilterView::~TagFilterView()
{
TDEConfig* config = kapp->config();
config->setGroup("Tag Filters View");
config->writeEntry("Matching Condition", (int)(d->matchingCond));
config->writeEntry("Toggle Auto Tags", (int)(d->toggleAutoTags));
config->sync();
saveViewState();
delete d->timer;
delete d;
}
void TagFilterView::slotTextTagFilterChanged(const TQString& filter)
{
if (filter.isEmpty())
{
collapseView();
return;
}
TQString search = filter.lower();
bool atleastOneMatch = false;
AlbumList tList = AlbumManager::instance()->allTAlbums();
for (AlbumList::iterator it = tList.begin(); it != tList.end(); ++it)
{
TAlbum* talbum = (TAlbum*)(*it);
// don't touch the root Album
if (talbum->isRoot())
continue;
bool match = talbum->title().lower().contains(search);
bool doesExpand = false;
if (!match)
{
// check if any of the parents match the search
Album* parent = talbum->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(talbum);
while (it.current())
{
if ((*it)->title().lower().contains(search))
{
match = true;
doesExpand = true;
break;
}
++it;
}
}
TagFilterViewItem* viewItem = (TagFilterViewItem*) talbum->extraData(this);
if (match)
{
atleastOneMatch = true;
if (viewItem)
{
viewItem->setVisible(true);
viewItem->setOpen(doesExpand);
}
}
else
{
if (viewItem)
{
viewItem->setVisible(false);
viewItem->setOpen(false);
}
}
}
emit signalTextTagFilterMatch(atleastOneMatch);
}
void TagFilterView::stateChanged(TagFilterViewItem* item)
{
ToggleAutoTags oldAutoTags = d->toggleAutoTags;
switch(d->toggleAutoTags)
{
case Children:
d->toggleAutoTags = TagFilterView::NoToggleAuto;
toggleChildTags(item, item->isOn());
d->toggleAutoTags = oldAutoTags;
break;
case Parents:
d->toggleAutoTags = TagFilterView::NoToggleAuto;
toggleParentTags(item, item->isOn());
d->toggleAutoTags = oldAutoTags;
break;
case ChildrenAndParents:
d->toggleAutoTags = TagFilterView::NoToggleAuto;
toggleChildTags(item, item->isOn());
toggleParentTags(item, item->isOn());
d->toggleAutoTags = oldAutoTags;
break;
default:
break;
}
triggerChange();
}
void TagFilterView::triggerChange()
{
d->timer->start(50, true);
}
TQDragObject* TagFilterView::dragObject()
{
TagFilterViewItem *item = dynamic_cast<TagFilterViewItem*>(dragItem());
if(!item)
return 0;
TagDrag *t = new TagDrag(item->id(), this);
t->setPixmap(*item->pixmap(0));
return t;
}
bool TagFilterView::acceptDrop(const TQDropEvent *e) const
{
TQPoint vp = contentsToViewport(e->pos());
TagFilterViewItem *itemDrop = dynamic_cast<TagFilterViewItem*>(itemAt(vp));
TagFilterViewItem *itemDrag = dynamic_cast<TagFilterViewItem*>(dragItem());
if(TagDrag::canDecode(e) || TagListDrag::canDecode(e))
{
// Allow dragging at the root, to move the tag to the root
if(!itemDrop)
return true;
// Do not allow dragging at the "Not Tagged" item.
if (itemDrop->untagged())
return false;
// Dragging an item on itself makes no sense
if(itemDrag == itemDrop)
return false;
// Dragging a parent on its child makes no sense
if(itemDrag && itemDrag->album()->isAncestorOf(itemDrop->album()))
return false;
return true;
}
if (ItemDrag::canDecode(e) && itemDrop && !itemDrop->untagged())
{
TAlbum *tag = itemDrop->album();
if (tag)
{
if (tag->parent())
{
// Only other possibility is image items being dropped
// And allow this only if there is a Tag to be dropped
// on and also the Tag is not root or "Not Tagged" item.
return true;
}
}
}
return false;
}
void TagFilterView::contentsDropEvent(TQDropEvent *e)
{
FolderView::contentsDropEvent(e);
if (!acceptDrop(e))
return;
TQPoint vp = contentsToViewport(e->pos());
TagFilterViewItem *itemDrop = dynamic_cast<TagFilterViewItem*>(itemAt(vp));
if (!itemDrop || itemDrop->untagged())
return;
if(TagDrag::canDecode(e))
{
TQByteArray ba = e->encodedData("digikam/tag-id");
TQDataStream ds(ba, IO_ReadOnly);
int tagID;
ds >> tagID;
AlbumManager* man = AlbumManager::instance();
TAlbum* talbum = man->findTAlbum(tagID);
if(!talbum)
return;
if (talbum == itemDrop->album())
return;
KPopupMenu popMenu(this);
popMenu.insertTitle(SmallIcon("digikam"), i18n("Tag Filters"));
popMenu.insertItem(SmallIcon("goto"), i18n("&Move Here"), 10);
popMenu.insertSeparator(-1);
popMenu.insertItem(SmallIcon("cancel"), i18n("C&ancel"), 20);
popMenu.setMouseTracking(true);
int id = popMenu.exec(TQCursor::pos());
if(id == 10)
{
TAlbum *newParentTag = 0;
if (!itemDrop)
{
// move dragItem to the root
newParentTag = AlbumManager::instance()->findTAlbum(0);
}
else
{
// move dragItem as child of dropItem
newParentTag = itemDrop->album();
}
TQString errMsg;
if (!AlbumManager::instance()->moveTAlbum(talbum, newParentTag, errMsg))
{
KMessageBox::error(this, errMsg);
}
if(itemDrop && !itemDrop->isOpen())
itemDrop->setOpen(true);
}
return;
}
if (ItemDrag::canDecode(e))
{
TAlbum *destAlbum = itemDrop->album();
KURL::List urls;
KURL::List kioURLs;
TQValueList<int> albumIDs;
TQValueList<int> imageIDs;
if (!ItemDrag::decode(e, urls, kioURLs, albumIDs, imageIDs))
return;
if (urls.isEmpty() || kioURLs.isEmpty() || albumIDs.isEmpty() || imageIDs.isEmpty())
return;
int id = 0;
char keys_return[32];
XQueryKeymap(x11Display(), keys_return);
int key_1 = XKeysymToKeycode(x11Display(), 0xFFE3);
int key_2 = XKeysymToKeycode(x11Display(), 0xFFE4);
// If a ctrl key is pressed while dropping the drag object,
// the tag is assigned to the images without showing a
// popup menu.
if (((keys_return[key_1 / 8]) && (1 << (key_1 % 8))) ||
((keys_return[key_2 / 8]) && (1 << (key_2 % 8))))
{
id = 10;
}
else
{
KPopupMenu popMenu(this);
popMenu.insertTitle(SmallIcon("digikam"), i18n("Tag Filters"));
popMenu.insertItem(SmallIcon("tag"), i18n("Assign Tag '%1' to Items")
.arg(destAlbum->prettyURL()), 10) ;
popMenu.insertItem(i18n("Set as Tag Thumbnail"), 11);
popMenu.insertSeparator(-1);
popMenu.insertItem(SmallIcon("cancel"), i18n("C&ancel"));
popMenu.setMouseTracking(true);
id = popMenu.exec(TQCursor::pos());
}
if (id == 10)
{
emit signalProgressBarMode(StatusProgressBar::ProgressBarMode,
i18n("Assigning image tags. Please wait..."));
AlbumLister::instance()->blockSignals(true);
AlbumManager::instance()->albumDB()->beginTransaction();
int i=0;
for (TQValueList<int>::const_iterator it = imageIDs.begin();
it != imageIDs.end(); ++it)
{
// create temporary ImageInfo object
ImageInfo info(*it);
MetadataHub hub;
hub.load(&info);
hub.setTag(destAlbum, true);
hub.write(&info, MetadataHub::PartialWrite);
hub.write(info.filePath(), MetadataHub::FullWriteIfChanged);
emit signalProgressValue((int)((i++/(float)imageIDs.count())*100.0));
kapp->processEvents();
}
AlbumLister::instance()->blockSignals(false);
AlbumManager::instance()->albumDB()->commitTransaction();
ImageAttributesWatch::instance()->imagesChanged(destAlbum->id());
emit signalProgressBarMode(StatusProgressBar::TextMode, TQString());
}
else if(id == 11)
{
TQString errMsg;
AlbumManager::instance()->updateTAlbumIcon(destAlbum, TQString(),
imageIDs.first(), errMsg);
}
}
}
void TagFilterView::slotTagAdded(Album* album)
{
if (!album || album->isRoot())
return;
TAlbum* tag = dynamic_cast<TAlbum*>(album);
if (!tag)
return;
if (tag->parent()->isRoot())
{
new TagFilterViewItem(this, tag);
}
else
{
TagFilterViewItem* parent = (TagFilterViewItem*)(tag->parent()->extraData(this));
if (!parent)
{
DWarning() << k_funcinfo << " Failed to find parent for Tag "
<< tag->tagPath() << endl;
return;
}
new TagFilterViewItem(parent, tag);
}
setTagThumbnail(tag);
}
void TagFilterView::slotTagRenamed(Album* album)
{
if (!album)
return;
TAlbum* tag = dynamic_cast<TAlbum*>(album);
if (!tag)
return;
TagFilterViewItem* item = (TagFilterViewItem*)(tag->extraData(this));
if (item)
item->refresh();
}
void TagFilterView::slotTagMoved(TAlbum* tag, TAlbum* newParent)
{
if (!tag || !newParent)
return;
TagFilterViewItem* item = (TagFilterViewItem*)(tag->extraData(this));
if (!item)
return;
if (item->parent())
{
TQListViewItem* oldPItem = item->parent();
oldPItem->takeItem(item);
TagFilterViewItem* newPItem = (TagFilterViewItem*)(newParent->extraData(this));
if (newPItem)
newPItem->insertItem(item);
else
insertItem(item);
}
else
{
takeItem(item);
TagFilterViewItem* newPItem = (TagFilterViewItem*)(newParent->extraData(this));
if (newPItem)
newPItem->insertItem(item);
else
insertItem(item);
}
}
void TagFilterView::slotTagDeleted(Album* album)
{
if (!album || album->isRoot())
return;
TAlbum* tag = dynamic_cast<TAlbum*>(album);
if (!tag)
return;
TagFilterViewItem* item = (TagFilterViewItem*)(album->extraData(this));
if (!item)
return;
// NOTE: see B.K.O #158558: unselected tag filter and all childrens before to delete it.
toggleChildTags(item, false);
item->setOn(false);
album->removeExtraData(this);
delete item;
}
void TagFilterView::setTagThumbnail(TAlbum *album)
{
if(!album)
return;
TagFilterViewItem* item = (TagFilterViewItem*) album->extraData(this);
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);
}
}
else
{
// for the time being, set standard icon
item->setPixmap(0, loader->getStandardTagIcon(album));
}
}
void TagFilterView::slotGotThumbnailFromIcon(Album *album, const TQPixmap& thumbnail)
{
if(!album || album->type() != Album::TAG)
return;
TagFilterViewItem* item = (TagFilterViewItem*)album->extraData(this);
if(!item)
return;
AlbumThumbnailLoader *loader = AlbumThumbnailLoader::instance();
TQPixmap blendedIcon = loader->blendIcons(loader->getStandardTagIcon(), thumbnail);
item->setPixmap(0, blendedIcon);
}
void TagFilterView::slotThumbnailLost(Album *)
{
// we already set the standard icon before loading
}
void TagFilterView::slotReloadThumbnails()
{
AlbumList tList = AlbumManager::instance()->allTAlbums();
for (AlbumList::iterator it = tList.begin(); it != tList.end(); ++it)
{
TAlbum* tag = (TAlbum*)(*it);
setTagThumbnail(tag);
}
}
void TagFilterView::slotAlbumIconChanged(Album* album)
{
if(!album || album->type() != Album::TAG)
return;
setTagThumbnail((TAlbum *)album);
}
void TagFilterView::slotClear()
{
clear();
TagFilterViewItem* notTaggedItem = new TagFilterViewItem(this, 0, true);
notTaggedItem->setPixmap(0, AlbumThumbnailLoader::instance()->getStandardTagIcon());
}
void TagFilterView::slotTimeOut()
{
TQValueList<int> filterTags;
bool showUnTagged = false;
TQListViewItemIterator it(this, TQListViewItemIterator::Checked);
while (it.current())
{
TagFilterViewItem* item = (TagFilterViewItem*)it.current();
if (item->album())
filterTags.append(item->album()->id());
else if (item->untagged())
showUnTagged = true;
++it;
}
AlbumLister::instance()->setTagFilter(filterTags, d->matchingCond, showUnTagged);
}
void TagFilterView::slotContextMenu(TQListViewItem* it, const TQPoint&, int)
{
TagFilterViewItem *item = dynamic_cast<TagFilterViewItem*>(it);
if (item && item->untagged())
return;
d->ABCMenu = new TQPopupMenu;
connect(d->ABCMenu, TQT_SIGNAL( aboutToShow() ),
this, TQT_SLOT( slotABCContextMenu() ));
KPopupMenu popmenu(this);
popmenu.insertTitle(SmallIcon("digikam"), i18n("Tag Filters"));
popmenu.insertItem(SmallIcon("tag-new"), i18n("New Tag..."), 10);
popmenu.insertItem(SmallIcon("tag-addressbook"), i18n("Create Tag From AddressBook"), d->ABCMenu);
if (item)
{
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 (item)
{
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 (item)
{
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);
TQPopupMenu matchingCongMenu;
matchingCongMenu.setCheckable(true);
matchingCongMenu.insertItem(i18n("Or Between Tags"), 25);
matchingCongMenu.insertItem(i18n("And Between Tags"), 26);
matchingCongMenu.setItemChecked((d->matchingCond == AlbumLister::OrCondition) ? 25 : 26, true);
popmenu.insertItem(i18n("Matching Condition"), &matchingCongMenu);
ToggleAutoTags oldAutoTags = d->toggleAutoTags;
int choice = popmenu.exec((TQCursor::pos()));
switch( choice )
{
case 10: // New Tag.
{
tagNew(item);
break;
}
case 11: // Edit Tag Properties.
{
tagEdit(item);
break;
}
case 12: // Delete Tag.
{
tagDelete(item);
break;
}
case 13: // Reset Tag Icon.
{
TQString errMsg;
AlbumManager::instance()->updateTAlbumIcon(item->album(), TQString("tag"), 0, errMsg);
break;
}
case 14: // Select All Tags.
{
d->toggleAutoTags = TagFilterView::NoToggleAuto;
TQListViewItemIterator it(this, TQListViewItemIterator::NotChecked);
while (it.current())
{
TagFilterViewItem* item = (TagFilterViewItem*)it.current();
// Ignore "Not Tagged" tag filter.
if (!item->untagged())
item->setOn(true);
++it;
}
triggerChange();
d->toggleAutoTags = oldAutoTags;
break;
}
case 15: // Deselect All Tags.
{
d->toggleAutoTags = TagFilterView::NoToggleAuto;
TQListViewItemIterator it(this, TQListViewItemIterator::Checked);
while (it.current())
{
TagFilterViewItem* item = (TagFilterViewItem*)it.current();
// Ignore "Not Tagged" tag filter.
if (!item->untagged())
item->setOn(false);
++it;
}
triggerChange();
d->toggleAutoTags = oldAutoTags;
break;
}
case 16: // Invert All Tags Selection.
{
d->toggleAutoTags = TagFilterView::NoToggleAuto;
TQListViewItemIterator it(this);
while (it.current())
{
TagFilterViewItem* item = (TagFilterViewItem*)it.current();
// Ignore "Not Tagged" tag filter.
if (!item->untagged())
item->setOn(!item->isOn());
++it;
}
triggerChange();
d->toggleAutoTags = oldAutoTags;
break;
}
case 17: // Select Child Tags.
{
d->toggleAutoTags = TagFilterView::NoToggleAuto;
toggleChildTags(item, true);
TagFilterViewItem *tItem = (TagFilterViewItem*)item->album()->extraData(this);
tItem->setOn(true);
d->toggleAutoTags = oldAutoTags;
break;
}
case 18: // Deselect Child Tags.
{
d->toggleAutoTags = TagFilterView::NoToggleAuto;
toggleChildTags(item, false);
TagFilterViewItem *tItem = (TagFilterViewItem*)item->album()->extraData(this);
tItem->setOn(false);
d->toggleAutoTags = oldAutoTags;
break;
}
case 19: // Select Parent Tags.
{
d->toggleAutoTags = TagFilterView::NoToggleAuto;
toggleParentTags(item, true);
TagFilterViewItem *tItem = (TagFilterViewItem*)item->album()->extraData(this);
tItem->setOn(true);
d->toggleAutoTags = oldAutoTags;
break;
}
case 20: // Deselect Parent Tags.
{
d->toggleAutoTags = TagFilterView::NoToggleAuto;
toggleParentTags(item, false);
TagFilterViewItem *tItem = (TagFilterViewItem*)item->album()->extraData(this);
tItem->setOn(false);
d->toggleAutoTags = oldAutoTags;
break;
}
case 21: // No toggle auto tags.
{
d->toggleAutoTags = NoToggleAuto;
break;
}
case 22: // Toggle auto Children tags.
{
d->toggleAutoTags = Children;
break;
}
case 23: // Toggle auto Parents tags.
{
d->toggleAutoTags = Parents;
break;
}
case 24: // Toggle auto Children and Parents tags.
{
d->toggleAutoTags = ChildrenAndParents;
break;
}
case 25: // Or Between Tags.
{
d->matchingCond = AlbumLister::OrCondition;
triggerChange();
break;
}
case 26: // And Between Tags.
{
d->matchingCond = AlbumLister::AndCondition;
triggerChange();
break;
}
default:
break;
}
if ( choice > 100 )
{
tagNew(item, d->ABCMenu->text( choice ), "tag-people" );
}
delete d->ABCMenu;
d->ABCMenu = 0;
}
void TagFilterView::slotABCContextMenu()
{
d->ABCMenu->clear();
int counter = 100;
KABC::AddressBook* ab = KABC::StdAddressBook::self();
TQStringList names;
for ( KABC::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 TagFilterView::tagNew(TagFilterViewItem* item, const TQString& _title, const TQString& _icon)
{
TAlbum *parent;
TQString title = _title;
TQString icon = _icon;
AlbumManager *man = AlbumManager::instance();
if (!item)
parent = man->findTAlbum(0);
else
parent = item->album();
if (title.isNull())
{
if (!TagEditDlg::tagCreate(TQT_TQWIDGET(kapp->activeWindow()), parent, title, icon))
return;
}
TQMap<TQString, TQString> errMap;
AlbumList tList = TagEditDlg::createTAlbum(parent, title, icon, errMap);
TagEditDlg::showtagsListCreationError(TQT_TQWIDGET(kapp->activeWindow()), errMap);
for (AlbumList::iterator it = tList.begin(); it != tList.end(); ++it)
{
TagFilterViewItem* item = (TagFilterViewItem*)(*it)->extraData(this);
if (item)
{
clearSelection();
setSelected(item, true);
setCurrentItem(item);
ensureItemVisible(item);
}
}
}
void TagFilterView::tagEdit(TagFilterViewItem* item)
{
if (!item)
return;
TAlbum *tag = item->album();
if (!tag)
return;
TQString title, icon;
if (!TagEditDlg::tagEdit(TQT_TQWIDGET(kapp->activeWindow()), tag, title, icon))
{
return;
}
AlbumManager* man = AlbumManager::instance();
if (tag->title() != title)
{
TQString errMsg;
if(!man->renameTAlbum(tag, title, errMsg))
KMessageBox::error(0, errMsg);
else
item->refresh();
}
if (tag->icon() != icon)
{
TQString errMsg;
if (!man->updateTAlbumIcon(tag, icon, 0, errMsg))
KMessageBox::error(0, errMsg);
else
setTagThumbnail(tag);
}
}
void TagFilterView::tagDelete(TagFilterViewItem* item)
{
if (!item)
return;
TAlbum *tag = item->album();
if (!tag || tag->isRoot())
return;
// find number of subtags
int children = 0;
AlbumIterator iter(tag);
while(iter.current())
{
children++;
++iter;
}
AlbumManager* man = AlbumManager::instance();
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(tag->title()));
if(result != KMessageBox::Continue)
return;
}
TQString message;
LLongList assignedItems = man->albumDB()->getItemIDsInTag(tag->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(tag->title());
}
else
{
message = i18n("Delete '%1' tag?").arg(tag->title());
}
int result = KMessageBox::warningContinueCancel(0, message,
i18n("Delete Tag"),
KGuiItem(i18n("Delete"),
"editdelete"));
if (result == KMessageBox::Continue)
{
TQString errMsg;
if (!man->deleteTAlbum(tag, errMsg))
KMessageBox::error(0, errMsg);
}
}
void TagFilterView::toggleChildTags(TagFilterViewItem* tItem, bool b)
{
if (!tItem)
return;
TAlbum *album = tItem->album();
if (!album)
return;
AlbumIterator it(album);
while ( it.current() )
{
TAlbum *ta = (TAlbum*)it.current();
TagFilterViewItem *item = (TagFilterViewItem*)ta->extraData(this);
if (item)
{
if (item->isVisible())
item->setOn(b);
}
++it;
}
}
void TagFilterView::toggleParentTags(TagFilterViewItem* tItem, bool b)
{
if (!tItem)
return;
TAlbum *album = tItem->album();
if (!album)
return;
TQListViewItemIterator it(this);
while (it.current())
{
TagFilterViewItem* item = dynamic_cast<TagFilterViewItem*>(it.current());
if (item->isVisible())
{
Album *a = dynamic_cast<Album*>(item->album());
if (a)
{
if (a == album->parent())
{
item->setOn(b);
toggleParentTags(item, b);
}
}
}
++it;
}
}
void TagFilterView::refresh()
{
TQListViewItemIterator it(this);
while (it.current())
{
TagFilterViewItem* item = dynamic_cast<TagFilterViewItem*>(*it);
if (item)
item->refresh();
++it;
}
}
void TagFilterView::slotRefresh(const TQMap<int, int>& tagsStatMap)
{
TQListViewItemIterator it(this);
while (it.current())
{
TagFilterViewItem* item = dynamic_cast<TagFilterViewItem*>(*it);
if (item)
{
if (item->album())
{
int id = item->id();
TQMap<int, int>::const_iterator it2 = tagsStatMap.find(id);
if ( it2 != tagsStatMap.end() )
item->setCount(it2.data());
}
}
++it;
}
refresh();
}
void TagFilterView::slotResetTagFilters()
{
TQListViewItemIterator it(this);
while (it.current())
{
TagFilterViewItem* item = dynamic_cast<TagFilterViewItem*>(*it);
if (item && item->isOn())
item->setOn(false);
++it;
}
}
void TagFilterView::loadViewState()
{
TDEConfig *config = kapp->config();
config->setGroup(name());
int selectedItem = config->readNumEntry("LastSelectedItem", 0);
TQValueList<int> openFolders;
if(config->hasKey("OpenFolders"))
{
openFolders = config->readIntListEntry("OpenFolders");
}
TagFilterViewItem *item = 0;
TagFilterViewItem *foundItem = 0;
TQListViewItemIterator it(this->lastItem());
for( ; it.current(); --it)
{
item = dynamic_cast<TagFilterViewItem*>(it.current());
if(!item)
continue;
// Start the album root always open
if(openFolders.contains(item->id()) || item->id() == 0)
setOpen(item, true);
else
setOpen(item, false);
if(item->id() == selectedItem)
{
// Save the found selected item so that it can be made visible.
foundItem = item;
}
}
// Important note: this cannot be done inside the previous loop
// because opening folders prevents the visibility.
// Fixes bug #144815.
// (Looks a bit like a bug in TQt to me ...)
if (foundItem)
{
setSelected(foundItem, true);
ensureItemVisible(foundItem);
}
}
void TagFilterView::saveViewState()
{
TDEConfig *config = kapp->config();
config->setGroup(name());
TagFilterViewItem *item = dynamic_cast<TagFilterViewItem*>(selectedItem());
if(item)
config->writeEntry("LastSelectedItem", item->id());
else
config->writeEntry("LastSelectedItem", 0);
TQValueList<int> openFolders;
TQListViewItemIterator it(this);
for( ; it.current(); ++it)
{
item = dynamic_cast<TagFilterViewItem*>(it.current());
if(item && isOpen(item))
openFolders.push_back(item->id());
}
config->writeEntry("OpenFolders", openFolders);
}
} // namespace Digikam