| /* poppler-optcontent.cc: qt interface to poppler |
| * |
| * Copyright (C) 2007, Brad Hards <bradh@kde.org> |
| * |
| * 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. |
| * |
| * You should have received a copy of the GNU General Public License |
| * along with this program; if not, write to the Free Software |
| * Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA. |
| */ |
| |
| #include "poppler-optcontent.h" |
| |
| #include "poppler-optcontent-private.h" |
| |
| #include "poppler-private.h" |
| |
| #include <QtCore/QDebug> |
| #include <QtCore/QtAlgorithms> |
| |
| #include "poppler/OptionalContent.h" |
| |
| namespace Poppler |
| { |
| |
| RadioButtonGroup::RadioButtonGroup( OptContentModelPrivate *ocModel, Array *rbarray ) |
| { |
| for (int i = 0; i < rbarray->getLength(); ++i) { |
| Object ref; |
| rbarray->getNF( i, &ref ); |
| if ( ! ref.isRef() ) { |
| qDebug() << "expected ref, but got:" << ref.getType(); |
| } |
| OptContentItem *item = ocModel->itemFromRef( QString::number(ref.getRefNum() ) ); |
| itemsInGroup.append( item ); |
| } |
| for (int i = 0; i < itemsInGroup.size(); ++i) { |
| OptContentItem *item = itemsInGroup.at(i); |
| item->appendRBGroup( this ); |
| } |
| } |
| |
| RadioButtonGroup::~RadioButtonGroup() |
| { |
| } |
| |
| QSet<OptContentItem *> RadioButtonGroup::setItemOn( OptContentItem *itemToSetOn ) |
| { |
| QSet<OptContentItem *> changedItems; |
| for (int i = 0; i < itemsInGroup.size(); ++i) { |
| OptContentItem *thisItem = itemsInGroup.at(i); |
| if (thisItem != itemToSetOn) { |
| QSet<OptContentItem *> newChangedItems; |
| thisItem->setState(OptContentItem::Off, newChangedItems); |
| changedItems += newChangedItems; |
| } |
| } |
| return changedItems; |
| } |
| |
| |
| |
| OptContentItem::OptContentItem( OptionalContentGroup *group ) |
| { |
| m_group = group; |
| m_parent = 0; |
| m_name = UnicodeParsedString( group->name() ); |
| if ( group->state() == OptionalContentGroup::On ) { |
| m_state = OptContentItem::On; |
| } else { |
| m_state = OptContentItem::Off; |
| } |
| m_stateBackup = m_state; |
| m_enabled = true; |
| } |
| |
| OptContentItem::OptContentItem( const QString &label ) |
| { |
| m_parent = 0; |
| m_name = label; |
| m_group = 0; |
| m_state = OptContentItem::HeadingOnly; |
| m_stateBackup = m_state; |
| m_enabled = true; |
| } |
| |
| OptContentItem::OptContentItem() : |
| m_parent( 0 ), m_enabled(true) |
| { |
| } |
| |
| OptContentItem::~OptContentItem() |
| { |
| } |
| |
| void OptContentItem::appendRBGroup( RadioButtonGroup *rbgroup ) |
| { |
| m_rbGroups.append( rbgroup ); |
| } |
| |
| |
| bool OptContentItem::setState(ItemState state, QSet<OptContentItem *> &changedItems) |
| { |
| m_state = state; |
| m_stateBackup = m_state; |
| changedItems.insert(this); |
| QSet<OptContentItem *> empty; |
| Q_FOREACH (OptContentItem *child, m_children) { |
| ItemState oldState = child->m_stateBackup; |
| child->setState(state == OptContentItem::On ? child->m_stateBackup : OptContentItem::Off, empty); |
| child->m_enabled = state == OptContentItem::On; |
| child->m_stateBackup = oldState; |
| } |
| if (!m_group) { |
| return false; |
| } |
| if ( state == OptContentItem::On ) { |
| m_group->setState( OptionalContentGroup::On ); |
| for (int i = 0; i < m_rbGroups.size(); ++i) { |
| RadioButtonGroup *rbgroup = m_rbGroups.at(i); |
| changedItems += rbgroup->setItemOn( this ); |
| } |
| } else if ( state == OptContentItem::Off ) { |
| m_group->setState( OptionalContentGroup::Off ); |
| } |
| return true; |
| } |
| |
| void OptContentItem::addChild( OptContentItem *child ) |
| { |
| m_children += child; |
| child->setParent( this ); |
| } |
| |
| QSet<OptContentItem*> OptContentItem::recurseListChildren(bool includeMe) const |
| { |
| QSet<OptContentItem*> ret; |
| if (includeMe) { |
| ret.insert(const_cast<OptContentItem*>(this)); |
| } |
| Q_FOREACH (OptContentItem *child, m_children) { |
| ret += child->recurseListChildren(true); |
| } |
| return ret; |
| } |
| |
| OptContentModelPrivate::OptContentModelPrivate( OptContentModel *qq, OCGs *optContent ) |
| : q(qq) |
| { |
| m_rootNode = new OptContentItem(); |
| GooList *ocgs = optContent->getOCGs(); |
| |
| for (int i = 0; i < ocgs->getLength(); ++i) { |
| OptionalContentGroup *ocg = static_cast<OptionalContentGroup*>(ocgs->get(i)); |
| OptContentItem *node = new OptContentItem( ocg ); |
| m_optContentItems.insert( QString::number(ocg->ref().num), node); |
| } |
| |
| if ( optContent->getOrderArray() == 0 ) { |
| // no Order array, so drop them all at the top level |
| QMapIterator<QString, OptContentItem*> i(m_optContentItems); |
| while ( i.hasNext() ) { |
| i.next(); |
| qDebug() << "iterator" << i.key() << ":" << i.value(); |
| addChild( i.value(), m_rootNode ); |
| } |
| } else { |
| parseOrderArray( m_rootNode, optContent->getOrderArray() ); |
| } |
| |
| parseRBGroupsArray( optContent->getRBGroupsArray() ); |
| } |
| |
| OptContentModelPrivate::~OptContentModelPrivate() |
| { |
| qDeleteAll( m_optContentItems ); |
| qDeleteAll( m_rbgroups ); |
| delete m_rootNode; |
| } |
| |
| void OptContentModelPrivate::parseOrderArray( OptContentItem *parentNode, Array *orderArray ) |
| { |
| OptContentItem *lastItem = parentNode; |
| for (int i = 0; i < orderArray->getLength(); ++i) { |
| Object orderItem; |
| orderArray->get(i, &orderItem); |
| if ( orderItem.isDict() ) { |
| Object item; |
| orderArray->getNF(i, &item); |
| if (item.isRef() ) { |
| OptContentItem *ocItem = m_optContentItems.value(QString::number(item.getRefNum()), 0); |
| if (ocItem) { |
| addChild( parentNode, ocItem ); |
| lastItem = ocItem; |
| } else { |
| qDebug() << "could not find group for object" << item.getRefNum(); |
| } |
| } |
| item.free(); |
| } else if ( (orderItem.isArray()) && (orderItem.arrayGetLength() > 0) ) { |
| parseOrderArray(lastItem, orderItem.getArray()); |
| } else if ( orderItem.isString() ) { |
| GooString *label = orderItem.getString(); |
| OptContentItem *header = new OptContentItem ( UnicodeParsedString ( label ) ); |
| addChild( parentNode, header ); |
| parentNode = header; |
| lastItem = header; |
| } else { |
| qDebug() << "something unexpected"; |
| } |
| orderItem.free(); |
| } |
| } |
| |
| void OptContentModelPrivate::parseRBGroupsArray( Array *rBGroupArray ) |
| { |
| if (! rBGroupArray) { |
| return; |
| } |
| // This is an array of array(s) |
| for (int i = 0; i < rBGroupArray->getLength(); ++i) { |
| Object rbObj; |
| rBGroupArray->get(i, &rbObj); |
| if ( ! rbObj.isArray() ) { |
| qDebug() << "expected inner array, got:" << rbObj.getType(); |
| return; |
| } |
| Array *rbarray = rbObj.getArray(); |
| RadioButtonGroup *rbg = new RadioButtonGroup( this, rbarray ); |
| m_rbgroups.append( rbg ); |
| rbObj.free(); |
| } |
| } |
| |
| OptContentModel::OptContentModel( OCGs *optContent, QObject *parent) |
| : QAbstractItemModel(parent) |
| { |
| d = new OptContentModelPrivate( this, optContent ); |
| } |
| |
| OptContentModel::~OptContentModel() |
| { |
| delete d; |
| } |
| |
| void OptContentModelPrivate::setRootNode(OptContentItem *node) |
| { |
| delete m_rootNode; |
| m_rootNode = node; |
| q->reset(); |
| } |
| |
| QModelIndex OptContentModel::index(int row, int column, const QModelIndex &parent) const |
| { |
| if (row < 0 || column != 0) { |
| return QModelIndex(); |
| } |
| |
| OptContentItem *parentNode = d->nodeFromIndex( parent ); |
| if (row < parentNode->childList().count()) { |
| return createIndex(row, column, parentNode->childList().at(row)); |
| } |
| return QModelIndex(); |
| } |
| |
| QModelIndex OptContentModel::parent(const QModelIndex &child) const |
| { |
| OptContentItem *childNode = d->nodeFromIndex( child ); |
| if (!childNode) { |
| return QModelIndex(); |
| } |
| return d->indexFromItem(childNode->parent(), child.column()); |
| } |
| |
| QModelIndex OptContentModelPrivate::indexFromItem(OptContentItem *node, int column) const |
| { |
| if (!node) { |
| return QModelIndex(); |
| } |
| OptContentItem *parentNode = node->parent(); |
| if (!parentNode) { |
| return QModelIndex(); |
| } |
| const int row = parentNode->childList().indexOf(node); |
| return q->createIndex(row, column, node); |
| } |
| |
| int OptContentModel::rowCount(const QModelIndex &parent) const |
| { |
| OptContentItem *parentNode = d->nodeFromIndex( parent ); |
| if (!parentNode) { |
| return 0; |
| } else { |
| return parentNode->childList().count(); |
| } |
| } |
| |
| int OptContentModel::columnCount(const QModelIndex &parent) const |
| { |
| return 1; |
| } |
| |
| |
| QVariant OptContentModel::data(const QModelIndex &index, int role) const |
| { |
| OptContentItem *node = d->nodeFromIndex(index, true); |
| if (!node) { |
| return QVariant(); |
| } |
| |
| switch (role) { |
| case Qt::DisplayRole: |
| return node->name(); |
| break; |
| case Qt::EditRole: |
| if (node->state() == OptContentItem::On) { |
| return true; |
| } else if (node->state() == OptContentItem::Off) { |
| return false; |
| } |
| break; |
| case Qt::CheckStateRole: |
| if (node->state() == OptContentItem::On) { |
| return Qt::Checked; |
| } else if (node->state() == OptContentItem::Off) { |
| return Qt::Unchecked; |
| } |
| break; |
| } |
| |
| return QVariant(); |
| } |
| |
| bool OptContentModel::setData ( const QModelIndex & index, const QVariant & value, int role ) |
| { |
| OptContentItem *node = d->nodeFromIndex(index, true); |
| if (!node) { |
| return false; |
| } |
| |
| switch (role) { |
| case Qt::CheckStateRole: |
| { |
| const bool newvalue = value.toBool(); |
| if (newvalue) { |
| if (node->state() != OptContentItem::On) { |
| QSet<OptContentItem *> changedItems; |
| node->setState(OptContentItem::On, changedItems); |
| changedItems += node->recurseListChildren(false); |
| QModelIndexList indexes; |
| Q_FOREACH (OptContentItem *item, changedItems) { |
| indexes.append(d->indexFromItem(item, 0)); |
| } |
| qStableSort(indexes); |
| Q_FOREACH (const QModelIndex &changedIndex, indexes) { |
| emit dataChanged(changedIndex, changedIndex); |
| } |
| return true; |
| } |
| } else { |
| if (node->state() != OptContentItem::Off) { |
| QSet<OptContentItem *> changedItems; |
| node->setState(OptContentItem::Off, changedItems); |
| changedItems += node->recurseListChildren(false); |
| QModelIndexList indexes; |
| Q_FOREACH (OptContentItem *item, changedItems) { |
| indexes.append(d->indexFromItem(item, 0)); |
| } |
| qStableSort(indexes); |
| Q_FOREACH (const QModelIndex &changedIndex, indexes) { |
| emit dataChanged(changedIndex, changedIndex); |
| } |
| return true; |
| } |
| } |
| break; |
| } |
| } |
| |
| return false; |
| } |
| |
| Qt::ItemFlags OptContentModel::flags ( const QModelIndex & index ) const |
| { |
| OptContentItem *node = d->nodeFromIndex(index); |
| Qt::ItemFlags itemFlags = Qt::ItemIsSelectable | Qt::ItemIsUserCheckable; |
| if (node->isEnabled()) { |
| itemFlags |= Qt::ItemIsEnabled; |
| } |
| return itemFlags; |
| } |
| |
| QVariant OptContentModel::headerData( int section, Qt::Orientation orientation, int role ) const |
| { |
| return QAbstractItemModel::headerData( section, orientation, role ); |
| } |
| |
| void OptContentModelPrivate::addChild( OptContentItem *parent, OptContentItem *child ) |
| { |
| parent->addChild( child ); |
| } |
| |
| OptContentItem* OptContentModelPrivate::itemFromRef( const QString &ref ) const |
| { |
| return m_optContentItems.value(ref, 0); |
| } |
| |
| OptContentItem* OptContentModelPrivate::nodeFromIndex(const QModelIndex &index, bool canBeNull) const |
| { |
| if (index.isValid()) { |
| return static_cast<OptContentItem *>(index.internalPointer()); |
| } else { |
| return canBeNull ? 0 : m_rootNode; |
| } |
| } |
| } |
| |
| #include "poppler-optcontent.moc" |