diff options
author | tpearson <tpearson@283d02a7-25f6-0310-bc7c-ecb5cbfe19da> | 2010-03-01 19:17:32 +0000 |
---|---|---|
committer | tpearson <tpearson@283d02a7-25f6-0310-bc7c-ecb5cbfe19da> | 2010-03-01 19:17:32 +0000 |
commit | e38d2351b83fa65c66ccde443777647ef5cb6cff (patch) | |
tree | 1897fc20e9f73a81c520a5b9f76f8ed042124883 /src/groupview.cpp | |
download | tellico-e38d2351b83fa65c66ccde443777647ef5cb6cff.tar.gz tellico-e38d2351b83fa65c66ccde443777647ef5cb6cff.zip |
Added KDE3 version of Tellico
git-svn-id: svn://anonsvn.kde.org/home/kde/branches/trinity/applications/tellico@1097620 283d02a7-25f6-0310-bc7c-ecb5cbfe19da
Diffstat (limited to 'src/groupview.cpp')
-rw-r--r-- | src/groupview.cpp | 495 |
1 files changed, 495 insertions, 0 deletions
diff --git a/src/groupview.cpp b/src/groupview.cpp new file mode 100644 index 0000000..67b1a40 --- /dev/null +++ b/src/groupview.cpp @@ -0,0 +1,495 @@ +/*************************************************************************** + copyright : (C) 2001-2007 by Robby Stephenson + email : robby@periapsis.org + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of version 2 of the GNU General Public License as * + * published by the Free Software Foundation; * + * * + ***************************************************************************/ + +#include "groupview.h" +#include "collection.h" +#include "document.h" +#include "field.h" +#include "filter.h" +#include "controller.h" +#include "entryitem.h" +#include "entrygroupitem.h" +#include "entry.h" +#include "field.h" +#include "filter.h" +#include "tellico_kernel.h" +#include "listviewcomparison.h" +#include "../tellico_debug.h" + +#include <kpopupmenu.h> +#include <klocale.h> +#include <kiconloader.h> +#include <kaction.h> + +#include <qstringlist.h> +#include <qcolor.h> +#include <qregexp.h> +#include <qheader.h> + +using Tellico::GroupView; + +GroupView::GroupView(QWidget* parent_, const char* name_/*=0*/) + : GUI::ListView(parent_, name_), m_notSortedYet(true), m_coll(0) { + addColumn(QString::null); // header text gets updated later + header()->setStretchEnabled(true, 0); + setResizeMode(QListView::NoColumn); + setRootIsDecorated(true); + setShowSortIndicator(true); + setTreeStepSize(15); + setFullWidth(true); + + connect(this, SIGNAL(contextMenuRequested(QListViewItem*, const QPoint&, int)), + SLOT(contextMenuRequested(QListViewItem*, const QPoint&, int))); + + connect(this, SIGNAL(expanded(QListViewItem*)), + SLOT(slotExpanded(QListViewItem*))); + + connect(this, SIGNAL(collapsed(QListViewItem*)), + SLOT(slotCollapsed(QListViewItem*))); + + m_groupOpenPixmap = SmallIcon(QString::fromLatin1("folder_open")); + m_groupClosedPixmap = SmallIcon(QString::fromLatin1("folder")); +} + +Tellico::EntryGroupItem* GroupView::addGroup(Data::EntryGroup* group_) { + if(group_->isEmpty()) { + return 0; + } + int type = -1; + if(m_coll && m_coll->hasField(group_->fieldName())) { + type = m_coll->fieldByName(group_->fieldName())->type(); + } + EntryGroupItem* item = new EntryGroupItem(this, group_, type); + if(group_->groupName() == i18n(Data::Collection::s_emptyGroupTitle)) { + item->setPixmap(0, SmallIcon(QString::fromLatin1("folder_red"))); + item->setSortWeight(10); + } else { + item->setPixmap(0, m_groupClosedPixmap); + } + + m_groupDict.insert(group_->groupName(), item); + item->setExpandable(!group_->isEmpty()); + + return item; +} + +void GroupView::slotReset() { + clear(); + m_groupDict.clear(); +} + +void GroupView::removeCollection(Data::CollPtr coll_) { + if(!coll_) { + kdWarning() << "GroupView::removeCollection() - null coll pointer!" << endl; + return; + } + +// myDebug() << "GroupView::removeCollection() - " << coll_->title() << endl; + + blockSignals(true); + slotReset(); + blockSignals(false); +} + +void GroupView::slotModifyGroups(Data::CollPtr coll_, PtrVector<Data::EntryGroup> groups_) { + if(!coll_ || groups_.isEmpty()) { + kdWarning() << "GroupView::slotModifyGroups() - null coll or group pointer!" << endl; + return; + } + + for(PtrVector<Data::EntryGroup>::Iterator group = groups_.begin(); group != groups_.end(); ++group) { + // if the entries aren't grouped by field of the modified group, + // we don't care, so return + if(m_groupBy != group->fieldName()) { + continue; + } + +// myDebug() << "GroupView::slotModifyGroups() - " << group->fieldName() << "/" << group->groupName() << endl; + EntryGroupItem* par = m_groupDict.find(group->groupName()); + if(par) { + if(group->isEmpty()) { + m_groupDict.remove(par->text(0)); + delete par; + continue; + } + // the group might get deleted and recreated out from under us, + // so do a sanity check + par->setGroup(group.ptr()); + } else { + if(group->isEmpty()) { + myDebug() << "GroupView::slotModifyGroups() - trying to add empty group" << endl; + continue; + } + par = addGroup(group.ptr()); + } + + setUpdatesEnabled(false); + bool open = par->isOpen(); + par->setOpen(false); // closing and opening the item will clear the items + par->setOpen(open); + setUpdatesEnabled(true); + } + // don't want any selected + clearSelection(); + sort(); // in case the count changed, or group name +} + +// don't 'shadow' QListView::setSelected +void GroupView::setEntrySelected(Data::EntryPtr entry_) { +// myDebug() << "GroupView::slotSetSelected()" << endl; + // if entry_ is null pointer, set no selected + if(!entry_) { + // don't move this one outside the block since it calls setCurrentItem(0) + clearSelection(); + return; + } + + // if the selected entry is the same as the current one, just return + GUI::ListViewItem* it = static_cast<GUI::ListViewItem*>(currentItem()); + if(it && it->isEntryItem() && entry_ == static_cast<EntryItem*>(it)->entry()) { + return; + } + + // have to find a group whose field is the same as currently shown + if(m_groupBy.isEmpty()) { + myDebug() << "GroupView::slotSetSelected() - no group field" << endl; + return; + } + + const Data::EntryGroup* group = 0; + for(PtrVector<Data::EntryGroup>::ConstIterator it = entry_->groups().begin(); it != entry_->groups().end(); ++it) { + if(it->fieldName() == m_groupBy) { + group = it.ptr(); + break; + } + } + if(!group) { + myDebug() << "GroupView::slotSetSelected() - entry is not in any current groups!" << endl; + return; + } + + EntryGroupItem* groupItem = m_groupDict.find(group->groupName()); + if(!groupItem) { + return; + } + + clearSelection(); + for(QListViewItem* item = groupItem->firstChild(); item; item = item->nextSibling()) { + EntryItem* entryItem = static_cast<EntryItem*>(item); + if(entryItem->entry() == entry_) { + blockSignals(true); + setSelected(item, true); + setCurrentItem(item); + blockSignals(false); + ensureItemVisible(item); + return; + } + } +} + +void GroupView::slotExpandAll(int depth_/*=-1*/) { + if(childCount() == 0) { + return; + } + setSiblingsOpen(depth_, true); +} + +void GroupView::slotCollapseAll(int depth_/*=-1*/) { + if(childCount() == 0) { + return; + } + setSiblingsOpen(depth_, false); +} + +void GroupView::setSiblingsOpen(int depth_, bool open_) { + QListViewItem* item = 0; + + if(depth_ == -1) { + item = currentItem(); + if(!item) { + return; + } + depth_ = item->depth(); + } + + switch(depth_) { + case 0: + item = firstChild(); + break; + + case 1: + item = firstChild()->firstChild(); + break; + + default: + return; + } + + for( ; item; item = item->nextSibling()) { + item->setOpen(open_); + } +} + +void GroupView::contextMenuRequested(QListViewItem* item_, const QPoint& point_, int) { + if(!item_) { + return; + } + + KPopupMenu menu(this); + GUI::ListViewItem* item = static_cast<GUI::ListViewItem*>(item_); + if(item->isEntryGroupItem()) { + menu.insertItem(SmallIconSet(QString::fromLatin1("2downarrow")), + i18n("Expand All Groups"), this, SLOT(slotExpandAll())); + menu.insertItem(SmallIconSet(QString::fromLatin1("2uparrow")), + i18n("Collapse All Groups"), this, SLOT(slotCollapseAll())); + menu.insertItem(SmallIconSet(QString::fromLatin1("filter")), + i18n("Filter by Group"), this, SLOT(slotFilterGroup())); + } else if(item->isEntryItem()) { + Controller::self()->plugEntryActions(&menu); + } + menu.exec(point_); +} + +void GroupView::slotCollapsed(QListViewItem* item_) { + // only change icon for group items + if(static_cast<GUI::ListViewItem*>(item_)->isEntryGroupItem()) { + if(item_->text(0) == i18n(Data::Collection::s_emptyGroupTitle)) { + item_->setPixmap(0, SmallIcon(QString::fromLatin1("folder_red"))); + } else { + item_->setPixmap(0, m_groupClosedPixmap); + } + static_cast<GUI::ListViewItem*>(item_)->clear(); + } +} + +void GroupView::slotExpanded(QListViewItem* item_) { + EntryGroupItem* item = static_cast<EntryGroupItem*>(item_); + // only change icon for group items + if(!item->isEntryGroupItem()) { + return; + } + + setUpdatesEnabled(false); + if(item->text(0) == i18n(Data::Collection::s_emptyGroupTitle)) { + item->setPixmap(0, SmallIcon(QString::fromLatin1("folder_red_open"))); + } else { + item->setPixmap(0, m_groupOpenPixmap); + } + + Data::EntryGroup* group = item->group(); + if(!group) { + myDebug() << "GroupView::slotExpanded() - no entry group! - " << item->text(0) << endl; + } else { + for(Data::EntryVecIt entryIt = group->begin(); entryIt != group->end(); ++entryIt) { + new EntryItem(item, entryIt); + } + } + + setUpdatesEnabled(true); + triggerUpdate(); +} + +void GroupView::addCollection(Data::CollPtr coll_) { +// myDebug() << "GroupView::addCollection()" << endl; + if(!coll_) { + kdWarning() << "GroupView::addCollection() - null coll pointer!" << endl; + return; + } + + m_coll = coll_; + // if the collection doesn't have the grouped field, and it's not the pseudo-group, + // change it to default + if(m_groupBy.isEmpty() || (!coll_->hasField(m_groupBy) && m_groupBy != Data::Collection::s_peopleGroupName)) { + m_groupBy = coll_->defaultGroupField(); + } + + // when the coll gets set for the first time, the pixmaps need to be updated + if((m_coll->hasField(m_groupBy) && m_coll->fieldByName(m_groupBy)->formatFlag() == Data::Field::FormatName) + || m_groupBy == Data::Collection::s_peopleGroupName) { + m_groupOpenPixmap = UserIcon(QString::fromLatin1("person-open")); + m_groupClosedPixmap = UserIcon(QString::fromLatin1("person")); + } + + Data::FieldPtr f = coll_->fieldByName(QString::fromLatin1("title")); + if(f) { + setComparison(0, ListViewComparison::create(f)); + } + + updateHeader(); + populateCollection(); + + slotCollapseAll(); +// myDebug() << "GroupView::addCollection - done" << endl; +} + +void GroupView::setGroupField(const QString& groupField_) { +// myDebug() << "GroupView::setGroupField - " << groupField_ << endl; + if(groupField_.isEmpty() || groupField_ == m_groupBy) { + return; + } + + m_groupBy = groupField_; + if(!m_coll) { + return; // can't do anything yet, but still need to set the variable + } + if((m_coll->hasField(groupField_) && m_coll->fieldByName(groupField_)->formatFlag() == Data::Field::FormatName) + || groupField_ == Data::Collection::s_peopleGroupName) { + m_groupOpenPixmap = UserIcon(QString::fromLatin1("person-open")); + m_groupClosedPixmap = UserIcon(QString::fromLatin1("person")); + } else { + m_groupOpenPixmap = SmallIcon(QString::fromLatin1("folder_open")); + m_groupClosedPixmap = SmallIcon(QString::fromLatin1("folder")); + } + updateHeader(); + populateCollection(); +} + +void GroupView::populateCollection() { + if(!m_coll) { + return; + } + +// myDebug() << "GroupView::populateCollection() - " << m_groupBy << endl; + if(m_groupBy.isEmpty()) { + m_groupBy = m_coll->defaultGroupField(); + } + + setUpdatesEnabled(false); + clear(); // delete all groups + m_groupDict.clear(); + + // if there's no group field, just return + if(m_groupBy.isEmpty()) { + setUpdatesEnabled(true); + return; + } + + Data::EntryGroupDict* dict = m_coll->entryGroupDictByName(m_groupBy); + if(!dict) { // could happen if m_groupBy is non empty, but there are no entries with a value + return; + } + + // iterate over all the groups in the dict + // e.g. if the dict is "author", loop over all the author groups + for(QDictIterator<Data::EntryGroup> it(*dict); it.current(); ++it) { + addGroup(it.current()); + } + + setUpdatesEnabled(true); + triggerUpdate(); +} + +void GroupView::slotFilterGroup() { + const GUI::ListViewItemList& items = selectedItems(); + GUI::ListViewItem* item = items.getFirst(); + // only works for entry groups + if(!item || !item->isEntryGroupItem()) { + return; + } + + FilterPtr filter = new Filter(Filter::MatchAny); + + for(GUI::ListViewItemListIt it(items); it.current(); ++it) { + if(static_cast<EntryGroupItem*>(it.current())->count() == 0) { //ignore empty items + continue; + } + // need to check for people group + if(m_groupBy == Data::Collection::s_peopleGroupName) { + Data::EntryPtr entry = static_cast<EntryItem*>(it.current()->firstChild())->entry(); + Data::FieldVec fields = entry->collection()->peopleFields(); + for(Data::FieldVec::Iterator fIt = fields.begin(); fIt != fields.end(); ++fIt) { + filter->append(new FilterRule(fIt->name(), it.current()->text(0), FilterRule::FuncContains)); + } + } else { + QString s = it.current()->text(0); + if(s != i18n(Data::Collection::s_emptyGroupTitle)) { + filter->append(new FilterRule(m_groupBy, it.current()->text(0), FilterRule::FuncContains)); + } + } + } + + if(!filter->isEmpty()) { + emit signalUpdateFilter(filter); + } +} + +// this gets called when header() is clicked, so cycle through +void GroupView::setSorting(int col_, bool asc_) { + if(asc_ && !m_notSortedYet) { // cycle through after ascending + if(sortStyle() == ListView::SortByText) { + setSortStyle(ListView::SortByCount); + } else { + setSortStyle(ListView::SortByText); + } + } + updateHeader(); + m_notSortedYet = false; + ListView::setSorting(col_, asc_); +} + +void GroupView::addField(Data::CollPtr, Data::FieldPtr) { + resetComparisons(); +} + +void GroupView::modifyField(Data::CollPtr, Data::FieldPtr, Data::FieldPtr newField_) { + if(newField_->name() == m_groupBy) { + updateHeader(newField_); + } + // if the grouping changed at all, our groups got deleted out from under us + // so check first pointer, too. The groups could be deleted if the format type + // changes, too, so not enough just to check group flag + if(childCount() > 0 && static_cast<EntryGroupItem*>(firstChild())->group() == 0) { + populateCollection(); + } + resetComparisons(); +} + +void GroupView::removeField(Data::CollPtr, Data::FieldPtr) { + resetComparisons(); +} + +void GroupView::updateHeader(Data::FieldPtr field_/*=0*/) { + QString t = field_ ? field_->title() : groupTitle(); + if(sortStyle() == ListView::SortByText) { + setColumnText(0, t); + } else { + setColumnText(0, i18n("%1 (Sort by Count)").arg(t)); + } +} + +QString GroupView::groupTitle() { + QString title; + if(!m_coll || m_groupBy.isEmpty()) { + title = i18n("Group Name Header", "Group"); + } else { + Data::FieldPtr f = m_coll->fieldByName(m_groupBy); + if(f) { + title = f->title(); + } else if(m_groupBy == Data::Collection::s_peopleGroupName) { + title = i18n("People"); + } + } + return title; +} + +void GroupView::resetComparisons() { + if(!m_coll) { + return; + } + Data::FieldPtr f = m_coll->fieldByName(QString::fromLatin1("title")); + if(f) { + setComparison(0, ListViewComparison::create(f)); + } +} + +#include "groupview.moc" |