diff --git a/ui/annotationproxymodels.cpp b/ui/annotationproxymodels.cpp index d34d03983..eab99dc63 100644 --- a/ui/annotationproxymodels.cpp +++ b/ui/annotationproxymodels.cpp @@ -1,604 +1,618 @@ /*************************************************************************** * Copyright (C) 2007 by Tobias Koenig * * * * 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 "annotationproxymodels.h" #include #include #include #include "annotationmodel.h" #include "debug_ui.h" static quint32 mixIndex( int row, int column ) { return ( row << 4 ) | column; } PageFilterProxyModel::PageFilterProxyModel( QObject *parent ) : QSortFilterProxyModel( parent ), mGroupByCurrentPage( false ), mCurrentPage( -1 ) { setDynamicSortFilter( true ); } void PageFilterProxyModel::groupByCurrentPage( bool value ) { if ( mGroupByCurrentPage == value ) return; mGroupByCurrentPage = value; invalidateFilter(); } void PageFilterProxyModel::setCurrentPage( int page ) { if ( mCurrentPage == page ) return; mCurrentPage = page; // no need to invalidate when we're not showing the current page only if ( !mGroupByCurrentPage ) return; invalidateFilter(); } bool PageFilterProxyModel::filterAcceptsRow( int row, const QModelIndex &sourceParent ) const { if ( !mGroupByCurrentPage ) return true; const QModelIndex pageIndex = sourceModel()->index( row, 0, sourceParent ); int page = sourceModel()->data( pageIndex, AnnotationModel::PageRole ).toInt(); return (page == mCurrentPage); } PageGroupProxyModel::PageGroupProxyModel( QObject *parent ) : QAbstractProxyModel( parent ), mGroupByPage( false ) { } int PageGroupProxyModel::columnCount( const QModelIndex &parentIndex ) const { // For top-level and second level we have always only one column if ( mGroupByPage ) { if ( parentIndex.isValid() ) { if ( parentIndex.parent().isValid() ) return 0; else { return 1; // second-level } } else { return 1; // top-level } } else { if ( !parentIndex.isValid() ) // top-level return 1; else return 0; } return 1; } int PageGroupProxyModel::rowCount( const QModelIndex &parentIndex ) const { if ( mGroupByPage ) { if ( parentIndex.isValid() ) { if ( parentIndex.parent().isValid() ) return 0; else { return mTreeIndexes[ parentIndex.row() ].second.count(); // second-level } } else { return mTreeIndexes.count(); // top-level } } else { if ( !parentIndex.isValid() ) // top-level return mIndexes.count(); else return 0; } } QModelIndex PageGroupProxyModel::index( int row, int column, const QModelIndex &parentIndex ) const { if ( row < 0 || column != 0 ) return QModelIndex(); if ( mGroupByPage ) { if ( parentIndex.isValid() ) { if ( parentIndex.row() >= 0 && parentIndex.row() < mTreeIndexes.count() && row < mTreeIndexes[ parentIndex.row() ].second.count() ) return createIndex( row, column, qint32( parentIndex.row() + 1 ) ); else return QModelIndex(); } else { if ( row < mTreeIndexes.count() ) return createIndex( row, column ); else return QModelIndex(); } } else { if ( row < mIndexes.count() ) return createIndex( row, column, mixIndex( parentIndex.row(), parentIndex.column() ) ); else return QModelIndex(); } } QModelIndex PageGroupProxyModel::parent( const QModelIndex &idx ) const { if ( mGroupByPage ) { if ( idx.internalId() == 0 ) // top-level return QModelIndex(); else return index( idx.internalId() - 1, idx.column() ); } else { // We have only top-level items return QModelIndex(); } } QModelIndex PageGroupProxyModel::mapFromSource( const QModelIndex &sourceIndex ) const { if ( mGroupByPage ) { if ( sourceIndex.parent().isValid() ) { return index( sourceIndex.row(), sourceIndex.column(), sourceIndex.parent() ); } else { return index( sourceIndex.row(), sourceIndex.column() ); } } else { for ( int i = 0; i < mIndexes.count(); ++i ) { if ( mIndexes[ i ] == sourceIndex ) return index( i, 0 ); } return QModelIndex(); } } QModelIndex PageGroupProxyModel::mapToSource( const QModelIndex &proxyIndex ) const { if ( !proxyIndex.isValid() ) return QModelIndex(); if ( mGroupByPage ) { if ( proxyIndex.internalId() == 0 ) { if ( proxyIndex.row() >= mTreeIndexes.count() || proxyIndex.row() < 0 ) return QModelIndex(); return mTreeIndexes[ proxyIndex.row() ].first; } else { if ( qint32(proxyIndex.internalId()) - 1 >= mTreeIndexes.count() || proxyIndex.row() >= mTreeIndexes[ proxyIndex.internalId() - 1 ].second.count() ) return QModelIndex(); return mTreeIndexes[ proxyIndex.internalId() - 1 ].second[ proxyIndex.row() ]; } } else { if ( proxyIndex.column() > 0 || proxyIndex.row() >= mIndexes.count() ) return QModelIndex(); else { return mIndexes[ proxyIndex.row() ]; } } } void PageGroupProxyModel::setSourceModel( QAbstractItemModel *model ) { if ( sourceModel() ) { disconnect( sourceModel(), &QAbstractItemModel::layoutChanged, this, &PageGroupProxyModel::rebuildIndexes ); disconnect( sourceModel(), &QAbstractItemModel::modelReset, this, &PageGroupProxyModel::rebuildIndexes ); disconnect( sourceModel(), &QAbstractItemModel::rowsInserted, this, &PageGroupProxyModel::rebuildIndexes ); disconnect( sourceModel(), &QAbstractItemModel::rowsRemoved, this, &PageGroupProxyModel::rebuildIndexes ); + disconnect( sourceModel(), &QAbstractItemModel::dataChanged, this, &PageGroupProxyModel::sourceDataChanged ); } QAbstractProxyModel::setSourceModel( model ); connect( sourceModel(), &QAbstractItemModel::layoutChanged, this, &PageGroupProxyModel::rebuildIndexes ); connect( sourceModel(), &QAbstractItemModel::modelReset, this, &PageGroupProxyModel::rebuildIndexes ); connect( sourceModel(), &QAbstractItemModel::rowsInserted, this, &PageGroupProxyModel::rebuildIndexes ); connect( sourceModel(), &QAbstractItemModel::rowsRemoved, this, &PageGroupProxyModel::rebuildIndexes ); + connect( sourceModel(), &QAbstractItemModel::dataChanged, this, &PageGroupProxyModel::sourceDataChanged ); rebuildIndexes(); } void PageGroupProxyModel::rebuildIndexes() { beginResetModel(); if ( mGroupByPage ) { mTreeIndexes.clear(); for ( int row = 0; row < sourceModel()->rowCount(); ++row ) { const QModelIndex pageIndex = sourceModel()->index( row, 0 ); QList itemIndexes; for ( int subRow = 0; subRow < sourceModel()->rowCount( pageIndex ); ++subRow ) { itemIndexes.append( sourceModel()->index( subRow, 0, pageIndex ) ); } mTreeIndexes.append( QPair >( pageIndex, itemIndexes ) ); } } else { mIndexes.clear(); for ( int row = 0; row < sourceModel()->rowCount(); ++row ) { const QModelIndex pageIndex = sourceModel()->index( row, 0 ); for ( int subRow = 0; subRow < sourceModel()->rowCount( pageIndex ); ++subRow ) { mIndexes.append( sourceModel()->index( subRow, 0, pageIndex ) ); } } } endResetModel(); } +void PageGroupProxyModel::sourceDataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight, const QVector &roles) +{ + emit dataChanged(mapFromSource(topLeft), mapFromSource(bottomRight), roles); +} + void PageGroupProxyModel::groupByPage( bool value ) { if ( mGroupByPage == value ) return; mGroupByPage = value; rebuildIndexes(); } class AuthorGroupItem { public: enum Type { Page, Author, Annotation }; AuthorGroupItem( AuthorGroupItem *parent, Type type = Page, const QModelIndex &index = QModelIndex() ) : mParent( parent ), mType( type ), mIndex( index ) { } ~AuthorGroupItem() { qDeleteAll( mChilds ); } void appendChild( AuthorGroupItem *child ) { mChilds.append( child ); } AuthorGroupItem* parent() const { return mParent; } AuthorGroupItem* child( int row ) const { return mChilds.value( row ); } int childCount() const { return mChilds.count(); } void dump( int level = 0 ) { QString prefix; for ( int i = 0; i < level; ++i ) prefix += QLatin1Char(' '); qCDebug(OkularUiDebug, "%s%s", qPrintable( prefix ), ( mType == Page ? "Page" : (mType == Author ? "Author" : "Annotation") ) ); for ( int i = 0; i < mChilds.count(); ++i ) mChilds[ i ]->dump( level + 2 ); } const AuthorGroupItem* findIndex( const QModelIndex &index ) const { if ( index == mIndex ) return this; for ( int i = 0; i < mChilds.count(); ++i ) { const AuthorGroupItem *item = mChilds[ i ]->findIndex( index ); if ( item ) return item; } return nullptr; } int row() const { return ( mParent ? mParent->mChilds.indexOf( const_cast( this ) ) : 0 ); - } + } Type type() const { return mType; } QModelIndex index() const { return mIndex; } void setAuthor( const QString &author ) { mAuthor = author; } QString author() const { return mAuthor; } private: AuthorGroupItem *mParent; Type mType; QModelIndex mIndex; QList mChilds; QString mAuthor; }; class AuthorGroupProxyModel::Private { public: Private( AuthorGroupProxyModel *parent ) : mParent( parent ), mRoot( nullptr ), mGroupByAuthor( false ) { } ~Private() { delete mRoot; } AuthorGroupProxyModel *mParent; AuthorGroupItem *mRoot; bool mGroupByAuthor; }; AuthorGroupProxyModel::AuthorGroupProxyModel( QObject *parent ) : QAbstractProxyModel( parent ), d( new Private( this ) ) { } AuthorGroupProxyModel::~AuthorGroupProxyModel() { delete d; } int AuthorGroupProxyModel::columnCount( const QModelIndex& ) const { return 1; } int AuthorGroupProxyModel::rowCount( const QModelIndex &parentIndex ) const { AuthorGroupItem *item = nullptr; if ( !parentIndex.isValid() ) item = d->mRoot; else item = static_cast( parentIndex.internalPointer() ); return item ? item->childCount() : 0; } QModelIndex AuthorGroupProxyModel::index( int row, int column, const QModelIndex &parentIndex ) const { if ( !hasIndex( row, column, parentIndex ) ) return QModelIndex(); AuthorGroupItem *parentItem = nullptr; if ( !parentIndex.isValid() ) parentItem = d->mRoot; else parentItem = static_cast( parentIndex.internalPointer() ); AuthorGroupItem *child = parentItem->child( row ); if ( child ) return createIndex( row, column, child ); else return QModelIndex(); } QModelIndex AuthorGroupProxyModel::parent( const QModelIndex &index ) const { if ( !index.isValid() ) return QModelIndex(); AuthorGroupItem *childItem = static_cast( index.internalPointer() ); AuthorGroupItem *parentItem = childItem->parent(); if ( parentItem == d->mRoot ) return QModelIndex(); else return createIndex( parentItem->row(), 0, parentItem ); } QModelIndex AuthorGroupProxyModel::mapFromSource( const QModelIndex &sourceIndex ) const { if ( !sourceIndex.isValid() ) return QModelIndex(); const AuthorGroupItem *item = d->mRoot->findIndex( sourceIndex ); if ( !item ) return QModelIndex(); return createIndex( item->row(), 0, const_cast( item ) ); } QModelIndex AuthorGroupProxyModel::mapToSource( const QModelIndex &proxyIndex ) const { if ( !proxyIndex.isValid() ) return QModelIndex(); AuthorGroupItem *item = static_cast( proxyIndex.internalPointer() ); return item->index(); } void AuthorGroupProxyModel::setSourceModel( QAbstractItemModel *model ) { if ( sourceModel() ) { disconnect( sourceModel(), &QAbstractItemModel::layoutChanged, this, &AuthorGroupProxyModel::rebuildIndexes ); disconnect( sourceModel(), &QAbstractItemModel::modelReset, this, &AuthorGroupProxyModel::rebuildIndexes ); disconnect( sourceModel(), &QAbstractItemModel::rowsInserted, this, &AuthorGroupProxyModel::rebuildIndexes ); disconnect( sourceModel(), &QAbstractItemModel::rowsRemoved, this, &AuthorGroupProxyModel::rebuildIndexes ); + disconnect( sourceModel(), &QAbstractItemModel::dataChanged, this, &AuthorGroupProxyModel::sourceDataChanged ); } QAbstractProxyModel::setSourceModel( model ); connect( sourceModel(), &QAbstractItemModel::layoutChanged, this, &AuthorGroupProxyModel::rebuildIndexes ); connect( sourceModel(), &QAbstractItemModel::modelReset, this, &AuthorGroupProxyModel::rebuildIndexes ); connect( sourceModel(), &QAbstractItemModel::rowsInserted, this, &AuthorGroupProxyModel::rebuildIndexes ); connect( sourceModel(), &QAbstractItemModel::rowsRemoved, this, &AuthorGroupProxyModel::rebuildIndexes ); + connect( sourceModel(), &QAbstractItemModel::dataChanged, this, &AuthorGroupProxyModel::sourceDataChanged ); rebuildIndexes(); } static bool isAuthorItem( const QModelIndex &index ) { if ( !index.isValid() ) { return false; } AuthorGroupItem *item = static_cast( index.internalPointer() ); return (item->type() == AuthorGroupItem::Author); } QItemSelection AuthorGroupProxyModel::mapSelectionToSource( const QItemSelection &selection ) const { QModelIndexList proxyIndexes = selection.indexes(); QItemSelection sourceSelection; for ( int i = 0; i < proxyIndexes.size(); ++i ) { if ( !isAuthorItem( proxyIndexes.at( i ) ) ) sourceSelection << QItemSelectionRange( mapToSource( proxyIndexes.at( i ) ) ); } return sourceSelection; } QItemSelection AuthorGroupProxyModel::mapSelectionFromSource( const QItemSelection &selection ) const { return QAbstractProxyModel::mapSelectionFromSource( selection ); } QVariant AuthorGroupProxyModel::data( const QModelIndex &proxyIndex, int role ) const { if ( isAuthorItem( proxyIndex ) ) { AuthorGroupItem *item = static_cast( proxyIndex.internalPointer() ); if ( role == Qt::DisplayRole ) return item->author(); else if ( role == Qt::DecorationRole ) return QIcon::fromTheme( item->author().isEmpty() ? QStringLiteral("user-away") : QStringLiteral("user-identity") ); else return QVariant(); } else { return QAbstractProxyModel::data( proxyIndex, role ); } } QMap AuthorGroupProxyModel::itemData( const QModelIndex &index ) const { if ( isAuthorItem( index ) ) { return QMap(); } else { return QAbstractProxyModel::itemData( index ); } } Qt::ItemFlags AuthorGroupProxyModel::flags( const QModelIndex &index ) const { if ( isAuthorItem( index ) ) { return Qt::ItemIsEnabled | Qt::ItemIsSelectable; } else { return QAbstractProxyModel::flags( index ); } } void AuthorGroupProxyModel::groupByAuthor( bool value ) { if ( d->mGroupByAuthor == value ) return; d->mGroupByAuthor = value; rebuildIndexes(); } void AuthorGroupProxyModel::rebuildIndexes() { beginResetModel(); delete d->mRoot; d->mRoot = new AuthorGroupItem( nullptr ); if ( d->mGroupByAuthor ) { QMap authorMap; for ( int row = 0; row < sourceModel()->rowCount(); ++row ) { const QModelIndex idx = sourceModel()->index( row, 0 ); const QString author = sourceModel()->data( idx, AnnotationModel::AuthorRole ).toString(); if ( !author.isEmpty() ) { // We have the annotations as top-level, so introduce authors as new // top-levels and append the annotations AuthorGroupItem *authorItem = authorMap.value( author, 0 ); if ( !authorItem ) { authorItem = new AuthorGroupItem( d->mRoot, AuthorGroupItem::Author ); authorItem->setAuthor( author ); // Add item to tree d->mRoot->appendChild( authorItem ); // Insert to lookup list authorMap.insert( author, authorItem ); } AuthorGroupItem *item = new AuthorGroupItem( authorItem, AuthorGroupItem::Annotation, idx ); authorItem->appendChild( item ); } else { // We have the pages as top-level, so we use them as top-level, append the // authors for all annotations of the page, and then the annotations themself AuthorGroupItem *pageItem = new AuthorGroupItem( d->mRoot, AuthorGroupItem::Page, idx ); d->mRoot->appendChild( pageItem ); // First collect all authors... QMap pageAuthorMap; for ( int subRow = 0; subRow < sourceModel()->rowCount( idx ); ++subRow ) { const QModelIndex annIdx = sourceModel()->index( subRow, 0, idx ); const QString author = sourceModel()->data( annIdx, AnnotationModel::AuthorRole ).toString(); AuthorGroupItem *authorItem = pageAuthorMap.value( author, 0 ); if ( !authorItem ) { authorItem = new AuthorGroupItem( pageItem, AuthorGroupItem::Author ); authorItem->setAuthor( author ); // Add item to tree pageItem->appendChild( authorItem ); // Insert to lookup list pageAuthorMap.insert( author, authorItem ); } AuthorGroupItem *item = new AuthorGroupItem( authorItem, AuthorGroupItem::Annotation, annIdx ); authorItem->appendChild( item ); } } } } else { for ( int row = 0; row < sourceModel()->rowCount(); ++row ) { const QModelIndex idx = sourceModel()->index( row, 0 ); const QString author = sourceModel()->data( idx, AnnotationModel::AuthorRole ).toString(); if ( !author.isEmpty() ) { // We have the annotations as top-level items AuthorGroupItem *item = new AuthorGroupItem( d->mRoot, AuthorGroupItem::Annotation, idx ); d->mRoot->appendChild( item ); } else { // We have the pages as top-level items AuthorGroupItem *pageItem = new AuthorGroupItem( d->mRoot, AuthorGroupItem::Page, idx ); d->mRoot->appendChild( pageItem ); // Append all annotations as second-level for ( int subRow = 0; subRow < sourceModel()->rowCount( idx ); ++subRow ) { const QModelIndex subIdx = sourceModel()->index( subRow, 0, idx ); AuthorGroupItem *item = new AuthorGroupItem( pageItem, AuthorGroupItem::Annotation, subIdx ); pageItem->appendChild( item ); } } } } endResetModel(); } +void AuthorGroupProxyModel::sourceDataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight, const QVector &roles) +{ + emit dataChanged(mapFromSource(topLeft), mapFromSource(bottomRight), roles); +} + #include "moc_annotationproxymodels.cpp" diff --git a/ui/annotationproxymodels.h b/ui/annotationproxymodels.h index 2a4534e48..8b914c25b 100644 --- a/ui/annotationproxymodels.h +++ b/ui/annotationproxymodels.h @@ -1,145 +1,147 @@ /*************************************************************************** * Copyright (C) 2007 by Tobias Koenig * * * * 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 ANNOTATIONPROXYMODEL_H #define ANNOTATIONPROXYMODEL_H #include #include /** * A proxy model, which filters out all pages except the * current one. */ class PageFilterProxyModel : public QSortFilterProxyModel { Q_OBJECT public: /** * Creates a new page filter proxy model. * * @param parent The parent object. */ explicit PageFilterProxyModel( QObject *parent = nullptr ); /** * Reimplemented from QSortFilterProxy. */ bool filterAcceptsRow( int, const QModelIndex& ) const override; public Q_SLOTS: /** * Sets whether the proxy model shall filter * by current page. */ void groupByCurrentPage( bool value ); /** * Sets the current page. */ void setCurrentPage( int page ); private: bool mGroupByCurrentPage; int mCurrentPage; }; /** * A proxy model which either groups the annotations by * pages or shows them all as list. */ class PageGroupProxyModel : public QAbstractProxyModel { Q_OBJECT public: /** * Creates a new page group proxy model. * * @param parent The parent object. */ explicit PageGroupProxyModel( QObject *parent = nullptr ); int columnCount( const QModelIndex &parentIndex ) const override; int rowCount( const QModelIndex &parentIndex ) const override; QModelIndex index( int row, int column, const QModelIndex &parentIndex = QModelIndex() ) const override; QModelIndex parent( const QModelIndex &index ) const override; QModelIndex mapFromSource( const QModelIndex &sourceIndex ) const override; QModelIndex mapToSource( const QModelIndex &proxyIndex ) const override; void setSourceModel( QAbstractItemModel *model ) override; public Q_SLOTS: /** * Sets whether the proxy model shall group * the annotations by page. */ void groupByPage( bool value ); private Q_SLOTS: void rebuildIndexes(); + void sourceDataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight, const QVector &roles); private: bool mGroupByPage; QList mIndexes; QList > > mTreeIndexes; }; /** * A proxy model which groups the annotations by author. */ class AuthorGroupProxyModel : public QAbstractProxyModel { Q_OBJECT public: /** * Creates a new author group proxy model. * * @param parent The parent object. */ explicit AuthorGroupProxyModel( QObject *parent = nullptr ); ~AuthorGroupProxyModel(); int columnCount( const QModelIndex &parentIndex ) const override; int rowCount( const QModelIndex &parentIndex ) const override; QModelIndex index( int row, int column, const QModelIndex &parentIndex = QModelIndex() ) const override; QModelIndex parent( const QModelIndex &index ) const override; QModelIndex mapFromSource( const QModelIndex &sourceIndex ) const override; QModelIndex mapToSource( const QModelIndex &proxyIndex ) const override; void setSourceModel( QAbstractItemModel *model ) override; QItemSelection mapSelectionToSource(const QItemSelection &selection) const override; QItemSelection mapSelectionFromSource(const QItemSelection &selection) const override; QVariant data(const QModelIndex &proxyIndex, int role = Qt::DisplayRole) const override; QMap itemData(const QModelIndex &index) const override; Qt::ItemFlags flags(const QModelIndex &index) const override; public Q_SLOTS: /** * Sets whether the proxy model shall group * the annotations by author. */ void groupByAuthor( bool value ); private Q_SLOTS: void rebuildIndexes(); + void sourceDataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight, const QVector &roles); private: class Private; Private* const d; }; #endif diff --git a/ui/guiutils.cpp b/ui/guiutils.cpp index 5308a9595..45656453b 100644 --- a/ui/guiutils.cpp +++ b/ui/guiutils.cpp @@ -1,278 +1,280 @@ /*************************************************************************** * Copyright (C) 2006-2007 by Pino Toscano * * * * 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 "guiutils.h" // qt/kde includes #include #include #include #include #include #include #include #include // local includes #include "core/action.h" #include "core/annotations.h" #include "core/document.h" #include struct GuiUtilsHelper { GuiUtilsHelper() { } QSvgRenderer* svgStamps(); QList il; std::unique_ptr< QSvgRenderer > svgStampFile; }; QSvgRenderer* GuiUtilsHelper::svgStamps() { if ( !svgStampFile.get() ) { const QString stampFile = QStandardPaths::locate(QStandardPaths::GenericDataLocation, QStringLiteral("okular/pics/stamps.svg") ); if ( !stampFile.isEmpty() ) { svgStampFile.reset( new QSvgRenderer( stampFile ) ); if ( !svgStampFile->isValid() ) { svgStampFile.reset(); } } } return svgStampFile.get(); } Q_GLOBAL_STATIC( GuiUtilsHelper, s_data ) namespace GuiUtils { QString captionForAnnotation( const Okular::Annotation * ann ) { Q_ASSERT( ann ); + const bool hasComment = !ann->contents().isEmpty(); + QString ret; switch( ann->subType() ) { case Okular::Annotation::AText: if( ( (Okular::TextAnnotation*)ann )->textType() == Okular::TextAnnotation::Linked ) ret = i18n( "Pop-up Note" ); else ret = i18n( "Inline Note" ); break; case Okular::Annotation::ALine: if( ( (Okular::LineAnnotation*)ann )->linePoints().count() == 2 ) - ret = i18n( "Straight Line" ); + ret = hasComment ? i18n( "Straight Line with Comment" ) : i18n( "Straight Line" ); else - ret = i18n( "Polygon" ); + ret = hasComment ? i18n( "Polygon with Comment" ) : i18n( "Polygon" ); break; case Okular::Annotation::AGeom: - ret = i18n( "Geometry" ); + ret = hasComment ? i18n( "Geometry with Comment" ) : i18n( "Geometry" ); break; case Okular::Annotation::AHighlight: switch ( ( (Okular::HighlightAnnotation*)ann )->highlightType() ) { case Okular::HighlightAnnotation::Highlight: - ret = i18n( "Highlight" ); + ret = hasComment ? i18n( "Highlight with Comment" ) : i18n( "Highlight" ); break; case Okular::HighlightAnnotation::Squiggly: - ret = i18n( "Squiggle" ); + ret = hasComment ? i18n( "Squiggle with Comment" ) : i18n( "Squiggle" ); break; case Okular::HighlightAnnotation::Underline: - ret = i18n( "Underline" ); + ret = hasComment ? i18n( "Underline with Comment" ) : i18n( "Underline" ); break; case Okular::HighlightAnnotation::StrikeOut: - ret = i18n( "Strike Out" ); + ret = hasComment ? i18n( "Strike Out with Comment" ) : i18n( "Strike Out" ); break; } break; case Okular::Annotation::AStamp: - ret = i18n( "Stamp" ); + ret = hasComment ? i18n( "Stamp with Comment" ) : i18n( "Stamp" ); break; case Okular::Annotation::AInk: - ret = i18n( "Freehand Line" ); + ret = hasComment ? i18n( "Freehand Line with Comment" ) : i18n( "Freehand Line" ); break; case Okular::Annotation::ACaret: ret = i18n( "Caret" ); break; case Okular::Annotation::AFileAttachment: ret = i18n( "File Attachment" ); break; case Okular::Annotation::ASound: ret = i18n( "Sound" ); break; case Okular::Annotation::AMovie: ret = i18n( "Movie" ); break; case Okular::Annotation::AScreen: ret = i18nc( "Caption for a screen annotation", "Screen" ); break; case Okular::Annotation::AWidget: ret = i18nc( "Caption for a widget annotation", "Widget" ); break; case Okular::Annotation::ARichMedia: ret = i18nc( "Caption for a rich media annotation", "Rich Media" ); break; case Okular::Annotation::A_BASE: break; } return ret; } QString authorForAnnotation( const Okular::Annotation * ann ) { Q_ASSERT( ann ); return !ann->author().isEmpty() ? ann->author() : i18nc( "Unknown author", "Unknown" ); } QString contentsHtml( const Okular::Annotation * ann ) { QString text = ann->contents().toHtmlEscaped(); text.replace( QLatin1Char('\n'), QLatin1String("
") ); return text; } QString prettyToolTip( const Okular::Annotation * ann ) { Q_ASSERT( ann ); QString author = authorForAnnotation( ann ); QString contents = contentsHtml( ann ); QString tooltip = QStringLiteral( "" ) + i18n( "Author: %1", author ) + QStringLiteral( "" ); if ( !contents.isEmpty() ) tooltip += QStringLiteral( "

" ) + contents; tooltip += QLatin1String("
"); return tooltip; } QPixmap loadStamp( const QString& _name, const QSize& size, int iconSize ) { const QString name = _name.toLower(); QSvgRenderer * r = nullptr; if ( ( r = s_data->svgStamps() ) && r->elementExists( name ) ) { const QRectF stampElemRect = r->boundsOnElement( name ); const QRectF stampRect( size.isValid() ? QRectF( QPointF( 0, 0 ), size ) : stampElemRect ); QPixmap pixmap( stampRect.size().toSize() ); pixmap.fill( Qt::transparent ); QPainter p( &pixmap ); r->render( &p, name ); p.end(); return pixmap; } QPixmap pixmap; const KIconLoader * il = iconLoader(); QString path; const int minSize = iconSize > 0 ? iconSize : qMin( size.width(), size.height() ); pixmap = il->loadIcon( name, KIconLoader::User, minSize, KIconLoader::DefaultState, QStringList(), &path, true ); if ( path.isEmpty() ) pixmap = il->loadIcon( name, KIconLoader::NoGroup, minSize ); return pixmap; } void addIconLoader( KIconLoader * loader ) { s_data->il.append( loader ); } void removeIconLoader( KIconLoader * loader ) { s_data->il.removeAll( loader ); } KIconLoader* iconLoader() { return s_data->il.isEmpty() ? KIconLoader::global() : s_data->il.back(); } void saveEmbeddedFile( Okular::EmbeddedFile *ef, QWidget *parent ) { const QString caption = i18n( "Where do you want to save %1?", ef->name() ); const QString path = QFileDialog::getSaveFileName( parent, caption, ef->name() ); if ( path.isEmpty() ) return; QFile targetFile( path ); writeEmbeddedFile( ef, parent, targetFile ); } void writeEmbeddedFile( Okular::EmbeddedFile *ef, QWidget *parent, QFile& target ) { if ( !target.open( QIODevice::WriteOnly ) ) { KMessageBox::error( parent, i18n( "Could not open \"%1\" for writing. File was not saved.", target.fileName() ) ); return; } target.write( ef->data() ); target.close(); } Okular::Movie* renditionMovieFromScreenAnnotation( const Okular::ScreenAnnotation *annotation ) { if ( !annotation ) return nullptr; if ( annotation->action() && annotation->action()->actionType() == Okular::Action::Rendition ) { Okular::RenditionAction *renditionAction = static_cast< Okular::RenditionAction * >( annotation->action() ); return renditionAction->movie(); } return nullptr; } // from Arthur - qt4 static inline int qt_div_255(int x) { return (x + (x>>8) + 0x80) >> 8; } void colorizeImage( QImage & grayImage, const QColor & color, unsigned int destAlpha ) { // Make sure that the image is Format_ARGB32_Premultiplied if ( grayImage.format() != QImage::Format_ARGB32_Premultiplied ) grayImage = grayImage.convertToFormat( QImage::Format_ARGB32_Premultiplied ); // iterate over all pixels changing the alpha component value unsigned int * data = (unsigned int *)grayImage.bits(); unsigned int pixels = grayImage.width() * grayImage.height(); int red = color.red(), green = color.green(), blue = color.blue(); int source, sourceSat, sourceAlpha; for( unsigned int i = 0; i < pixels; ++i ) { // optimize this loop keeping byte order into account source = data[i]; sourceSat = qRed( source ); int newR = qt_div_255( sourceSat * red ), newG = qt_div_255( sourceSat * green ), newB = qt_div_255( sourceSat * blue ); if ( (sourceAlpha = qAlpha( source )) == 255 ) { // use destAlpha data[i] = qRgba( newR, newG, newB, destAlpha ); } else { // use destAlpha * sourceAlpha product if ( destAlpha < 255 ) sourceAlpha = qt_div_255( destAlpha * sourceAlpha ); data[i] = qRgba( newR, newG, newB, sourceAlpha ); } } } }