diff --git a/ui/side_reviews.cpp b/ui/side_reviews.cpp index e50d2f162..a315bb579 100644 --- a/ui/side_reviews.cpp +++ b/ui/side_reviews.cpp @@ -1,289 +1,309 @@ /*************************************************************************** * Copyright (C) 2005 by Enrico Ros * * * * 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 "side_reviews.h" // qt/kde includes #include #include #include #include #include #include #include #include #include #include #include #include #include // local includes #include "core/annotations.h" #include "core/document.h" #include "core/page.h" #include "settings.h" #include "annotationpopup.h" #include "annotationproxymodels.h" #include "annotationmodel.h" #include "ktreeviewsearchline.h" class TreeView : public QTreeView { Q_OBJECT public: TreeView( Okular::Document *document, QWidget *parent = Q_NULLPTR ) : QTreeView( parent ), m_document( document ) { } protected: void paintEvent( QPaintEvent *event ) override { bool hasAnnotations = false; for ( uint i = 0; i < m_document->pages(); ++i ) if ( m_document->page( i )->hasAnnotations() ) { hasAnnotations = true; break; } if ( !hasAnnotations ) { QPainter p( viewport() ); p.setRenderHint( QPainter::Antialiasing, true ); p.setClipRect( event->rect() ); QTextDocument document; document.setHtml( i18n( "

No annotations

" "To create new annotations press F6 or select Tools -> Review" " from the menu.
" ) ); document.setTextWidth( width() - 50 ); const uint w = document.size().width() + 20; const uint h = document.size().height() + 20; p.setBrush( palette().background() ); p.translate( 0.5, 0.5 ); p.drawRoundRect( 15, 15, w, h, (8*200)/w, (8*200)/h ); p.translate( 20, 20 ); document.drawContents( &p ); } else { QTreeView::paintEvent( event ); } } private: Okular::Document *m_document; }; Reviews::Reviews( QWidget * parent, Okular::Document * document ) : QWidget( parent ), m_document( document ) { // create widgets and layout them vertically QVBoxLayout * vLayout = new QVBoxLayout( this ); vLayout->setMargin( 0 ); vLayout->setSpacing( 6 ); m_view = new TreeView( m_document, this ); m_view->setAlternatingRowColors( true ); m_view->setSelectionMode( QAbstractItemView::ExtendedSelection ); m_view->header()->hide(); QToolBar *toolBar = new QToolBar( this ); toolBar->setObjectName( QStringLiteral( "reviewOptsBar" ) ); QSizePolicy sp = toolBar->sizePolicy(); sp.setVerticalPolicy( QSizePolicy::Minimum ); toolBar->setSizePolicy( sp ); m_model = new AnnotationModel( m_document, m_view ); m_filterProxy = new PageFilterProxyModel( m_view ); m_groupProxy = new PageGroupProxyModel( m_view ); m_authorProxy = new AuthorGroupProxyModel( m_view ); m_filterProxy->setSourceModel( m_model ); m_groupProxy->setSourceModel( m_filterProxy ); m_authorProxy->setSourceModel( m_groupProxy ); m_view->setModel( m_authorProxy ); m_searchLine = new KTreeViewSearchLine( this, m_view ); m_searchLine->setPlaceholderText(i18n( "Search..." )); m_searchLine->setCaseSensitivity( Okular::Settings::self()->reviewsSearchCaseSensitive() ? Qt::CaseSensitive : Qt::CaseInsensitive ); m_searchLine->setRegularExpression( Okular::Settings::self()->reviewsSearchRegularExpression() ); connect(m_searchLine, &KTreeViewSearchLine::searchOptionsChanged, this, &Reviews::saveSearchOptions); vLayout->addWidget( m_searchLine ); vLayout->addWidget( m_view ); vLayout->addWidget( toolBar ); toolBar->setIconSize( QSize( 16, 16 ) ); toolBar->setMovable( false ); // - add Page button QAction * groupByPageAction = toolBar->addAction( QIcon::fromTheme( QStringLiteral("text-x-generic") ), i18n( "Group by Page" ) ); groupByPageAction->setCheckable( true ); connect(groupByPageAction, &QAction::toggled, this, &Reviews::slotPageEnabled); groupByPageAction->setChecked( Okular::Settings::groupByPage() ); // - add Author button QAction * groupByAuthorAction = toolBar->addAction( QIcon::fromTheme( QStringLiteral("user-identity") ), i18n( "Group by Author" ) ); groupByAuthorAction->setCheckable( true ); connect(groupByAuthorAction, &QAction::toggled, this, &Reviews::slotAuthorEnabled); groupByAuthorAction->setChecked( Okular::Settings::groupByAuthor() ); // - add separator toolBar->addSeparator(); // - add Current Page Only button QAction * curPageOnlyAction = toolBar->addAction( QIcon::fromTheme( QStringLiteral("arrow-down") ), i18n( "Show reviews for current page only" ) ); curPageOnlyAction->setCheckable( true ); connect(curPageOnlyAction, &QAction::toggled, this, &Reviews::slotCurrentPageOnly); curPageOnlyAction->setChecked( Okular::Settings::currentPageOnly() ); + // Adds space between left actions, so that the next two buttons are aligned to the right + QWidget * spacer = new QWidget(); + spacer->setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Expanding ); + toolBar->addWidget( spacer ); + + QAction * expandAll = toolBar->addAction( QIcon::fromTheme( QStringLiteral("expand-all") ), i18n( "Expand all elements" ) ); + connect(expandAll, &QAction::triggered, this, &Reviews::slotExpandAll); + QAction * collapseAll = toolBar->addAction( QIcon::fromTheme( QStringLiteral("collapse-all") ), i18n( "Collapse all elements" ) ); + connect(collapseAll, &QAction::triggered, this, &Reviews::slotCollapseAll); + connect(m_view, &TreeView::activated, this, &Reviews::activated); m_view->setContextMenuPolicy( Qt::CustomContextMenu ); connect(m_view, &TreeView::customContextMenuRequested, this, &Reviews::contextMenuRequested); } Reviews::~Reviews() { m_document->removeObserver( this ); } //BEGIN DocumentObserver Notifies void Reviews::notifyCurrentPageChanged( int previousPage, int currentPage ) { Q_UNUSED( previousPage ) m_filterProxy->setCurrentPage( currentPage ); } //END DocumentObserver Notifies void Reviews::reparseConfig() { m_searchLine->setCaseSensitivity( Okular::Settings::reviewsSearchCaseSensitive() ? Qt::CaseSensitive : Qt::CaseInsensitive ); m_searchLine->setRegularExpression( Okular::Settings::reviewsSearchRegularExpression() ); m_view->update(); } //BEGIN GUI Slots -> requestListViewUpdate void Reviews::slotPageEnabled( bool on ) { // store toggle state in Settings and update the listview Okular::Settings::setGroupByPage( on ); m_groupProxy->groupByPage( on ); m_view->expandAll(); } void Reviews::slotAuthorEnabled( bool on ) { // store toggle state in Settings and update the listview Okular::Settings::setGroupByAuthor( on ); m_authorProxy->groupByAuthor( on ); m_view->expandAll(); } void Reviews::slotCurrentPageOnly( bool on ) { // store toggle state in Settings and update the listview Okular::Settings::setCurrentPageOnly( on ); m_filterProxy->groupByCurrentPage( on ); m_view->expandAll(); } + +void Reviews::slotExpandAll() +{ + m_view->expandAll(); +} + +void Reviews::slotCollapseAll() +{ + m_view->collapseAll(); +} //END GUI Slots void Reviews::activated( const QModelIndex &index ) { const QModelIndex authorIndex = m_authorProxy->mapToSource( index ); const QModelIndex filterIndex = m_groupProxy->mapToSource( authorIndex ); const QModelIndex annotIndex = m_filterProxy->mapToSource( filterIndex ); Okular::Annotation *annotation = m_model->annotationForIndex( annotIndex ); if ( !annotation ) return; int pageNumber = m_model->data( annotIndex, AnnotationModel::PageRole ).toInt(); const Okular::Page * page = m_document->page( pageNumber ); // calculating the right coordinates to center the view on the annotation QRect rect = Okular::AnnotationUtils::annotationGeometry( annotation, page->width(), page->height() ); Okular::NormalizedRect nr( rect, (int)page->width(), (int)page->height() ); // set the viewport parameters Okular::DocumentViewport vp; vp.pageNumber = pageNumber; vp.rePos.enabled = true; vp.rePos.pos = Okular::DocumentViewport::Center; vp.rePos.normalizedX = ( nr.right + nr.left ) / 2.0; vp.rePos.normalizedY = ( nr.bottom + nr.top ) / 2.0; // setting the viewport m_document->setViewport( vp, nullptr, true ); } QModelIndexList Reviews::retrieveAnnotations(const QModelIndex& idx) const { QModelIndexList ret; if ( idx.isValid() ) { if ( idx.model()->hasChildren( idx ) ) { int rowCount = idx.model()->rowCount( idx ); for ( int i = 0; i < rowCount; i++ ) { ret += retrieveAnnotations( idx.child( i, idx.column() ) ); } } else { ret += idx; } } return ret; } void Reviews::contextMenuRequested( const QPoint &pos ) { AnnotationPopup popup( m_document, AnnotationPopup::SingleAnnotationMode, this ); connect(&popup, &AnnotationPopup::openAnnotationWindow, this, &Reviews::openAnnotationWindow); QModelIndexList indexes = m_view->selectionModel()->selectedIndexes(); Q_FOREACH ( const QModelIndex &index, indexes ) { QModelIndexList annotations = retrieveAnnotations(index); Q_FOREACH ( const QModelIndex &idx, annotations ) { const QModelIndex authorIndex = m_authorProxy->mapToSource( idx ); const QModelIndex filterIndex = m_groupProxy->mapToSource( authorIndex ); const QModelIndex annotIndex = m_filterProxy->mapToSource( filterIndex ); Okular::Annotation *annotation = m_model->annotationForIndex( annotIndex ); if ( annotation ) { const int pageNumber = m_model->data( annotIndex, AnnotationModel::PageRole ).toInt(); popup.addAnnotation( annotation, pageNumber ); } } } popup.exec( m_view->viewport()->mapToGlobal( pos ) ); } void Reviews::saveSearchOptions() { Okular::Settings::setReviewsSearchRegularExpression( m_searchLine->regularExpression() ); Okular::Settings::setReviewsSearchCaseSensitive( m_searchLine->caseSensitivity() == Qt::CaseSensitive ? true : false ); Okular::Settings::self()->save(); } #include "side_reviews.moc" diff --git a/ui/side_reviews.h b/ui/side_reviews.h index 79c99df1d..63bc67d93 100644 --- a/ui/side_reviews.h +++ b/ui/side_reviews.h @@ -1,75 +1,77 @@ /*************************************************************************** * Copyright (C) 2005 by Enrico Ros * * * * 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 _OKULAR_SIDE_REVIEWS_H_ #define _OKULAR_SIDE_REVIEWS_H_ #include #include #include #include "core/observer.h" class QModelIndex; namespace Okular { class Annotation; class Document; } class AnnotationModel; class AuthorGroupProxyModel; class PageFilterProxyModel; class PageGroupProxyModel; class KTreeViewSearchLine; class TreeView; /** * @short ... */ class Reviews : public QWidget, public Okular::DocumentObserver { Q_OBJECT public: Reviews( QWidget * parent, Okular::Document * document ); ~Reviews(); // [INHERITED] from DocumentObserver void notifyCurrentPageChanged( int previous, int current ) override; void reparseConfig(); public Q_SLOTS: void slotPageEnabled( bool ); void slotAuthorEnabled( bool ); void slotCurrentPageOnly( bool ); + void slotExpandAll(); + void slotCollapseAll(); Q_SIGNALS: void openAnnotationWindow( Okular::Annotation *annotation, int pageNumber ); private Q_SLOTS: void activated( const QModelIndex& ); void contextMenuRequested( const QPoint& ); void saveSearchOptions(); private: QModelIndexList retrieveAnnotations(const QModelIndex& idx) const; // data fields (GUI) KTreeViewSearchLine *m_searchLine; TreeView * m_view; // internal storage Okular::Document * m_document; AnnotationModel * m_model; AuthorGroupProxyModel * m_authorProxy; PageFilterProxyModel * m_filterProxy; PageGroupProxyModel * m_groupProxy; }; #endif