add a model for the table of contents

todo: license issues
This commit is contained in:
Marco Martin 2012-07-31 16:28:54 +02:00
parent d11d5d8106
commit 9c000ce8b1
7 changed files with 533 additions and 0 deletions

View file

@ -0,0 +1,35 @@
/*
* Copyright 2011 Marco Martin <mart@kde.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Library 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 Library General Public License for more details
*
* You should have received a copy of the GNU Library 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.
*/
import QtQuick 1.1
import org.kde.plasma.components 0.1 as PlasmaComponents
import org.kde.plasma.core 0.1 as PlasmaCore
Column {
id: treeView
Repeater {
model: VisualDataModel {
id: tocModel
model: documentItem.tableOfContents
delegate: TreeDelegate {
sourceModel: tocModel
}
}
}
}

View file

@ -0,0 +1,62 @@
/*
* Copyright 2011 Marco Martin <mart@kde.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Library 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 Library General Public License for more details
*
* You should have received a copy of the GNU Library 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.
*/
import QtQuick 1.1
import org.kde.plasma.components 0.1 as PlasmaComponents
import org.kde.plasma.core 0.1 as PlasmaCore
Column {
id: treeDelegate
property variant sourceModel
property int rowIndex: index
Row {
spacing: 30
PlasmaComponents.Label {
id: label
text: display
}
PlasmaComponents.Label {
id: pageNumber
text: page
}
}
Column {
id: col
x: 20
property variant model: childrenModel
Repeater {
id: rep
model: VisualDataModel {
id: childrenModel
model: documentItem.tableOfContents
}
}
}
onParentChanged: {
if (treeDelegate.parent && treeDelegate.parent.model) {
sourceModel = treeDelegate.parent.model
}
childrenModel.rootIndex = sourceModel.modelIndex(index)
if (model.hasModelChildren) {
childrenModel.delegate = Qt.createComponent("TreeDelegate.qml")
}
}
}

View file

@ -14,6 +14,7 @@ set(okular_SRCS
pageitem.cpp
documentitem.cpp
thumbnailitem.cpp
tocmodel.cpp
)
qt4_automoc(${okular_SRCS})
@ -23,6 +24,7 @@ target_link_libraries(okularplugin
${QT_QTCORE_LIBRARY}
${QT_QTGUI_LIBRARY}
${QT_QTDECLARATIVE_LIBRARY}
${QT_QTXML_LIBRARY}
${KDE4_KDECORE_LIBRARY}
${KDE4_KDEUI_LIBRARY}
${QIMAGEBLITZ_LIBRARIES}

View file

@ -19,15 +19,21 @@
#include "documentitem.h"
#include <QtDeclarative/qdeclarative.h>
#include <core/page.h>
#include "tocmodel.h"
DocumentItem::DocumentItem(QObject *parent)
: QObject(parent),
m_searchInProgress(false)
{
qmlRegisterUncreatableType<TOCModel>("org.kde.okular", 1, 0, "TOCModel", QLatin1String("Do not create objects of this type."));
Okular::Settings::instance("okularproviderrc");
m_document = new Okular::Document(0);
m_tocModel = new TOCModel(m_document, this);
connect(m_document, SIGNAL(searchFinished(int,Okular::Document::SearchStatus)),
this, SLOT(searchFinished(int,Okular::Document::SearchStatus)));
}
@ -43,6 +49,8 @@ void DocumentItem::setPath(const QString &path)
//TODO: remote urls
m_document->openDocument(path, KUrl(path), KMimeType::findByUrl(KUrl(path)));
m_tocModel->fill(m_document->documentSynopsis());
m_matchingPages.clear();
for (uint i = 0; i < m_document->pages(); ++i) {
m_matchingPages << (int)i;
@ -85,6 +93,11 @@ QList<int> DocumentItem::matchingPages() const
return m_matchingPages;
}
TOCModel *DocumentItem::tableOfContents() const
{
return m_tocModel;
}
bool DocumentItem::supportsSearching() const
{
return m_document->supportsSearching();

View file

@ -32,6 +32,7 @@ namespace Okular {
}
class Observer;
class TOCModel;
class DocumentItem : public QObject
{
@ -44,6 +45,7 @@ class DocumentItem : public QObject
Q_PROPERTY(bool supportsSearching READ supportsSearching NOTIFY supportsSearchingChanged)
Q_PROPERTY(bool searchInProgress READ isSearchInProgress NOTIFY searchInProgressChanged)
Q_PROPERTY(QList<int> matchingPages READ matchingPages NOTIFY matchingPagesChanged)
Q_PROPERTY(TOCModel *tableOfContents READ tableOfContents CONSTANT)
public:
@ -66,6 +68,8 @@ public:
QList<int> matchingPages() const;
TOCModel *tableOfContents() const;
//Those could be a property, but maybe we want to have parameter for searchText
Q_INVOKABLE void searchText(const QString &text);
Q_INVOKABLE void resetSearch();
@ -88,6 +92,7 @@ private Q_SLOTS:
private:
Okular::Document *m_document;
TOCModel *m_tocModel;
QHash <int, Observer *> m_observers;
QList<int> m_matchingPages;
bool m_searchInProgress;

View file

@ -0,0 +1,355 @@
/***************************************************************************
* Copyright (C) 2007 by Pino Toscano <pino@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 of the License, or *
* (at your option) any later version. *
***************************************************************************/
#include "tocmodel.h"
#include <qapplication.h>
#include <qdom.h>
#include <qlist.h>
#include <kicon.h>
#include "core/document.h"
#include "core/page.h"
Q_DECLARE_METATYPE( QModelIndex )
struct TOCItem
{
TOCItem();
TOCItem( TOCItem *parent, const QDomElement &e );
~TOCItem();
QString text;
Okular::DocumentViewport viewport;
QString extFileName;
QString url;
bool highlight : 1;
TOCItem *parent;
QList< TOCItem* > children;
TOCModelPrivate *model;
};
class TOCModelPrivate
{
public:
TOCModelPrivate( TOCModel *qq );
~TOCModelPrivate();
void addChildren( const QDomNode &parentNode, TOCItem * parentItem );
QModelIndex indexForItem( TOCItem *item ) const;
void findViewport( const Okular::DocumentViewport &viewport, TOCItem *item, QList< TOCItem* > &list ) const;
TOCModel *q;
TOCItem *root;
bool dirty : 1;
Okular::Document *document;
QList< TOCItem* > itemsToOpen;
QList< TOCItem* > currentPage;
};
TOCItem::TOCItem()
: highlight( false ), parent( 0 ), model( 0 )
{
}
TOCItem::TOCItem( TOCItem *_parent, const QDomElement &e )
: highlight( false ), parent( _parent )
{
parent->children.append( this );
model = parent->model;
text = e.tagName();
// viewport loading
if ( e.hasAttribute( "Viewport" ) )
{
// if the node has a viewport, set it
viewport = Okular::DocumentViewport( e.attribute( "Viewport" ) );
}
else if ( e.hasAttribute( "ViewportName" ) )
{
// if the node references a viewport, get the reference and set it
const QString & page = e.attribute( "ViewportName" );
QString viewport_string = model->document->metaData( "NamedViewport", page ).toString();
if ( !viewport_string.isEmpty() )
viewport = Okular::DocumentViewport( viewport_string );
}
extFileName = e.attribute( "ExternalFileName" );
url = e.attribute( "URL" );
}
TOCItem::~TOCItem()
{
qDeleteAll( children );
}
TOCModelPrivate::TOCModelPrivate( TOCModel *qq )
: q( qq ), root( new TOCItem ), dirty( false )
{
root->model = this;
}
TOCModelPrivate::~TOCModelPrivate()
{
delete root;
}
void TOCModelPrivate::addChildren( const QDomNode & parentNode, TOCItem * parentItem )
{
TOCItem * currentItem = 0;
QDomNode n = parentNode.firstChild();
while( !n.isNull() )
{
// convert the node to an element (sure it is)
QDomElement e = n.toElement();
// insert the entry as top level (listview parented) or 2nd+ level
currentItem = new TOCItem( parentItem, e );
// descend recursively and advance to the next node
if ( e.hasChildNodes() )
addChildren( n, currentItem );
// open/keep close the item
bool isOpen = false;
if ( e.hasAttribute( "Open" ) )
isOpen = QVariant( e.attribute( "Open" ) ).toBool();
if ( isOpen )
itemsToOpen.append( currentItem );
n = n.nextSibling();
}
}
QModelIndex TOCModelPrivate::indexForItem( TOCItem *item ) const
{
if ( item->parent )
{
int id = item->parent->children.indexOf( item );
if ( id >= 0 && id < item->parent->children.count() )
return q->createIndex( id, 0, item );
}
return QModelIndex();
}
void TOCModelPrivate::findViewport( const Okular::DocumentViewport &viewport, TOCItem *item, QList< TOCItem* > &list ) const
{
if ( item->viewport.isValid() && item->viewport.pageNumber == viewport.pageNumber )
list.append( item );
foreach ( TOCItem *child, item->children )
findViewport( viewport, child, list );
}
TOCModel::TOCModel( Okular::Document *document, QObject *parent )
: QAbstractItemModel( parent ), d( new TOCModelPrivate( this ) )
{
d->document = document;
qRegisterMetaType< QModelIndex >();
QHash<int, QByteArray> roles = roleNames();
roles.insert(PageRole, "page");
roles.insert(PageLabelRole, "pageLabel");
setRoleNames(roles);
}
TOCModel::~TOCModel()
{
delete d;
}
int TOCModel::columnCount( const QModelIndex &parent ) const
{
Q_UNUSED( parent )
return 1;
}
QVariant TOCModel::data( const QModelIndex &index, int role ) const
{
if ( !index.isValid() )
return QVariant();
TOCItem *item = static_cast< TOCItem* >( index.internalPointer() );
switch ( role )
{
case Qt::DisplayRole:
case Qt::ToolTipRole:
return item->text;
break;
case Qt::DecorationRole:
if ( item->highlight )
return KIcon( QApplication::layoutDirection() == Qt::RightToLeft ? "arrow-left" : "arrow-right" );
break;
case PageRole:
if ( item->viewport.isValid() )
return item->viewport.pageNumber + 1;
break;
case PageLabelRole:
if ( item->viewport.isValid() && item->viewport.pageNumber < int(d->document->pages()) )
return d->document->page( item->viewport.pageNumber )->label();
break;
}
return QVariant();
}
bool TOCModel::hasChildren( const QModelIndex &parent ) const
{
if ( !parent.isValid() )
return true;
TOCItem *item = static_cast< TOCItem* >( parent.internalPointer() );
return !item->children.isEmpty();
}
QVariant TOCModel::headerData( int section, Qt::Orientation orientation, int role ) const
{
if ( orientation != Qt::Horizontal )
return QVariant();
if ( section == 0 && role == Qt::DisplayRole )
return "Topics";
return QVariant();
}
QModelIndex TOCModel::index( int row, int column, const QModelIndex &parent ) const
{
if ( row < 0 || column != 0 )
return QModelIndex();
TOCItem *item = parent.isValid() ? static_cast< TOCItem* >( parent.internalPointer() ) : d->root;
if ( row < item->children.count() )
return createIndex( row, column, item->children.at( row ) );
return QModelIndex();
}
QModelIndex TOCModel::parent( const QModelIndex &index ) const
{
if ( !index.isValid() )
return QModelIndex();
TOCItem *item = static_cast< TOCItem* >( index.internalPointer() );
return d->indexForItem( item->parent );
}
int TOCModel::rowCount( const QModelIndex &parent ) const
{
TOCItem *item = parent.isValid() ? static_cast< TOCItem* >( parent.internalPointer() ) : d->root;
return item->children.count();
}
void TOCModel::fill( const Okular::DocumentSynopsis *toc )
{
if ( !toc )
return;
clear();
emit layoutAboutToBeChanged();
d->addChildren( *toc, d->root );
d->dirty = true;
emit layoutChanged();
foreach ( TOCItem *item, d->itemsToOpen )
{
QModelIndex index = d->indexForItem( item );
if ( !index.isValid() )
continue;
QMetaObject::invokeMethod( QObject::parent(), "expand", Qt::QueuedConnection, Q_ARG( QModelIndex, index ) );
}
d->itemsToOpen.clear();
}
void TOCModel::clear()
{
if ( !d->dirty )
return;
qDeleteAll( d->root->children );
d->root->children.clear();
d->currentPage.clear();
reset();
d->dirty = false;
}
void TOCModel::setCurrentViewport( const Okular::DocumentViewport &viewport )
{
foreach ( TOCItem* item, d->currentPage )
{
QModelIndex index = d->indexForItem( item );
if ( !index.isValid() )
continue;
item->highlight = false;
emit dataChanged( index, index );
}
d->currentPage.clear();
QList< TOCItem* > newCurrentPage;
d->findViewport( viewport, d->root, newCurrentPage );
// HACK: for now, support only the first item found
if ( newCurrentPage.count() > 0 )
{
TOCItem *first = newCurrentPage.first();
newCurrentPage.clear();
newCurrentPage.append( first );
}
d->currentPage = newCurrentPage;
foreach ( TOCItem* item, d->currentPage )
{
QModelIndex index = d->indexForItem( item );
if ( !index.isValid() )
continue;
item->highlight = true;
emit dataChanged( index, index );
}
}
bool TOCModel::isEmpty() const
{
return d->root->children.isEmpty();
}
QString TOCModel::externalFileNameForIndex( const QModelIndex &index ) const
{
if ( !index.isValid() )
return QString();
TOCItem *item = static_cast< TOCItem* >( index.internalPointer() );
return item->extFileName;
}
Okular::DocumentViewport TOCModel::viewportForIndex( const QModelIndex &index ) const
{
if ( !index.isValid() )
return Okular::DocumentViewport();
TOCItem *item = static_cast< TOCItem* >( index.internalPointer() );
return item->viewport;
}
QString TOCModel::urlForIndex( const QModelIndex &index ) const
{
if ( !index.isValid() )
return QString();
TOCItem *item = static_cast< TOCItem* >( index.internalPointer() );
return item->url;
}
#include "tocmodel.moc"

View file

@ -0,0 +1,61 @@
/***************************************************************************
* Copyright (C) 2007 by Pino Toscano <pino@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 of the License, or *
* (at your option) any later version. *
***************************************************************************/
#ifndef TOCMODEL_H
#define TOCMODEL_H
#include <qabstractitemmodel.h>
namespace Okular {
class Document;
class DocumentSynopsis;
class DocumentViewport;
}
class TOCModelPrivate;
class TOCModel : public QAbstractItemModel
{
Q_OBJECT
public:
enum TOCRoles {
PageRole = Qt::UserRole + 1,
PageLabelRole = Qt::UserRole + 2
};
explicit TOCModel( Okular::Document *document, QObject *parent = 0 );
virtual ~TOCModel();
// reimplementations from QAbstractItemModel
virtual int columnCount( const QModelIndex &parent = QModelIndex() ) const;
virtual QVariant data( const QModelIndex &index, int role = Qt::DisplayRole ) const;
virtual bool hasChildren( const QModelIndex &parent = QModelIndex() ) const;
virtual QVariant headerData( int section, Qt::Orientation orientation, int role = Qt::DisplayRole ) const;
virtual QModelIndex index( int row, int column, const QModelIndex &parent = QModelIndex() ) const;
virtual QModelIndex parent( const QModelIndex &index ) const;
virtual int rowCount( const QModelIndex &parent = QModelIndex() ) const;
void fill( const Okular::DocumentSynopsis *toc );
void clear();
void setCurrentViewport( const Okular::DocumentViewport &viewport );
bool isEmpty() const;
QString externalFileNameForIndex( const QModelIndex &index ) const;
Okular::DocumentViewport viewportForIndex( const QModelIndex &index ) const;
QString urlForIndex( const QModelIndex &index ) const;
private:
// storage
friend class TOCModelPrivate;
TOCModelPrivate *const d;
};
#endif