diff --git a/conf/okular.kcfg b/conf/okular.kcfg index 332eafb7d..cd9b4df92 100644 --- a/conf/okular.kcfg +++ b/conf/okular.kcfg @@ -221,6 +221,14 @@ + + None + + + + + + Browse @@ -230,6 +238,7 @@ + diff --git a/part-viewermode.rc b/part-viewermode.rc index bccd4912f..97f13ef96 100644 --- a/part-viewermode.rc +++ b/part-viewermode.rc @@ -1,5 +1,5 @@ - + &File @@ -34,7 +34,7 @@ - + diff --git a/part.rc b/part.rc index 60f86e5ba..34a1160ef 100644 --- a/part.rc +++ b/part.rc @@ -1,5 +1,5 @@ - + &File @@ -43,7 +43,7 @@ - + diff --git a/ui/pageview.cpp b/ui/pageview.cpp index 5e7fc2917..dc9eab830 100644 --- a/ui/pageview.cpp +++ b/ui/pageview.cpp @@ -177,6 +177,9 @@ public: QTimer * refreshTimer; int refreshPage; + // bbox state for Trim to Selection mode + Okular::NormalizedRect trimBoundingBox; + // infinite resizing loop prevention bool verticalScrollBarVisible; bool horizontalScrollBarVisible; @@ -193,7 +196,9 @@ public: KAction * aRotateCounterClockwise; KAction * aRotateOriginal; KSelectAction * aPageSizes; + KActionMenu * aTrimMode; KToggleAction * aTrimMargins; + KToggleAction * aTrimToSelection; KAction * aMouseNormal; KAction * aMouseSelect; KAction * aMouseTextSelect; @@ -310,7 +315,9 @@ PageView::PageView( QWidget *parent, Okular::Document *document ) d->aRotateCounterClockwise = 0; d->aRotateOriginal = 0; d->aPageSizes = 0; + d->aTrimMode = 0; d->aTrimMargins = 0; + d->aTrimToSelection = 0; d->aMouseNormal = 0; d->aMouseSelect = 0; d->aMouseTextSelect = 0; @@ -332,6 +339,7 @@ PageView::PageView( QWidget *parent, Okular::Document *document ) d->penDown = false; d->aMouseMagnifier = 0; d->aFitWindowToPage = 0; + d->trimBoundingBox = Okular::NormalizedRect(); // Null box switch( Okular::Settings::zoomMode() ) { @@ -475,11 +483,25 @@ void PageView::setupViewerActions( KActionCollection * ac ) connect( d->aPageSizes , SIGNAL(triggered(int)), this, SLOT(slotPageSizes(int)) ); - d->aTrimMargins = new KToggleAction( i18n( "&Trim Margins" ), this ); - ac->addAction("view_trim_margins", d->aTrimMargins ); + // Trim View actions + d->aTrimMode = new KActionMenu(i18n( "&Trim View" ), this ); + d->aTrimMode->setDelayed( false ); + ac->addAction("view_trim_mode", d->aTrimMode ); + + d->aTrimMargins = new KToggleAction( i18n( "&Trim Margins" ), d->aTrimMode->menu() ); + d->aTrimMode->addAction( d->aTrimMargins ); + ac->addAction( "view_trim_margins", d->aTrimMargins ); + d->aTrimMargins->setData( qVariantFromValue( (int)Okular::Settings::EnumTrimMode::Margins ) ); connect( d->aTrimMargins, SIGNAL(toggled(bool)), SLOT(slotTrimMarginsToggled(bool)) ); d->aTrimMargins->setChecked( Okular::Settings::trimMargins() ); + d->aTrimToSelection = new KToggleAction( i18n( "Trim To &Selection" ), d->aTrimMode->menu() ); + d->aTrimMode->addAction( d->aTrimToSelection); + ac->addAction( "view_trim_selection", d->aTrimToSelection); + d->aTrimToSelection->setData( qVariantFromValue( (int)Okular::Settings::EnumTrimMode::Selection ) ); + connect( d->aTrimToSelection, SIGNAL(toggled(bool)), SLOT(slotTrimToSelectionToggled(bool)) ); + + // d->aZoomFitWidth = new KToggleAction(KIcon( "zoom-fit-width" ), i18n("Fit &Width"), this); ac->addAction("view_fit_to_width", d->aZoomFitWidth ); connect( d->aZoomFitWidth, SIGNAL(toggled(bool)), SLOT(slotFitToWidthToggled(bool)) ); @@ -1025,6 +1047,9 @@ void PageView::updateActionState( bool haspages, bool documentChanged, bool hasf if ( d->aTrimMargins ) d->aTrimMargins->setEnabled( haspages ); + if ( d->aTrimToSelection ) + d->aTrimToSelection->setEnabled( haspages ); + if ( d->aViewMode ) d->aViewMode->setEnabled( haspages ); @@ -2019,6 +2044,7 @@ void PageView::mouseMoveEvent( QMouseEvent * e ) case Okular::Settings::EnumMouseMode::Zoom: case Okular::Settings::EnumMouseMode::RectSelect: case Okular::Settings::EnumMouseMode::TableSelect: + case Okular::Settings::EnumMouseMode::TrimSelect: // set second corner of selection if ( d->mouseSelecting ) updateSelection( eventPos ); @@ -2181,6 +2207,7 @@ void PageView::mousePressEvent( QMouseEvent * e ) break; case Okular::Settings::EnumMouseMode::RectSelect: // set first corner of the selection rect + case Okular::Settings::EnumMouseMode::TrimSelect: if ( leftButton ) { selectionStart( eventPos, palette().color( QPalette::Active, QPalette::Highlight ).light( 120 ), false ); @@ -2534,6 +2561,54 @@ void PageView::mouseReleaseEvent( QMouseEvent * e ) d->magnifierView->hide(); break; + case Okular::Settings::EnumMouseMode::TrimSelect: + { + // if mouse is released and selection is null this is a rightClick + if ( rightButton && !d->mouseSelecting ) + { + break; + } + PageViewItem * pageItem = pickItemOnPoint(eventPos.x(), eventPos.y()); + // ensure end point rests within a page, or ignore + if (!pageItem) { + break; + } + QRect selectionRect = d->mouseSelectionRect.normalized(); + + double nLeft = pageItem->absToPageX(selectionRect.left()); + double nRight = pageItem->absToPageX(selectionRect.right()); + double nTop = pageItem->absToPageY(selectionRect.top()); + double nBottom = pageItem->absToPageY(selectionRect.bottom()); + if ( nLeft < 0 ) nLeft = 0; + if ( nTop < 0 ) nTop = 0; + if ( nRight > 1 ) nRight = 1; + if ( nBottom > 1 ) nBottom = 1; + d->trimBoundingBox = Okular::NormalizedRect(nLeft, nTop, nRight, nBottom); + + // Trim Selection successfully done, hide prompt + d->messageWindow->hide(); + + // clear widget selection and invalidate rect + selectionClear(); + + // When Trim selection bbox interaction is over, we should switch to another mousemode. + if ( d->aPrevAction ) + { + d->aPrevAction->trigger(); + d->aPrevAction = 0; + } else { + d->aMouseNormal->trigger(); + } + + // with d->trimBoundingBox defined, redraw for trim to take visual effect + if ( d->document->pages() > 0 ) + { + slotRelayoutPages(); + slotRequestVisiblePixmaps(); // TODO: slotRelayoutPages() may have done this already! + } + + break; + } case Okular::Settings::EnumMouseMode::RectSelect: { // if mouse is released and selection is null this is a rightClick @@ -3342,11 +3417,13 @@ void PageView::updateItemSize( PageViewItem * item, int colWidth, int rowHeight zoom = d->zoomFactor; Okular::NormalizedRect crop( 0., 0., 1., 1. ); - // Handle cropping - if ( Okular::Settings::trimMargins() && okularPage->isBoundingBoxKnown() - && !okularPage->boundingBox().isNull() ) + // Handle cropping, due to either "Trim Margin" or "Trim to Selection" cases + if (( Okular::Settings::trimMargins() && okularPage->isBoundingBoxKnown() + && !okularPage->boundingBox().isNull() ) || + ( d->aTrimToSelection && d->aTrimToSelection->isChecked() && !d->trimBoundingBox.isNull())) { - crop = okularPage->boundingBox(); + + crop = Okular::Settings::trimMargins() ? okularPage->boundingBox() : d->trimBoundingBox; // Rotate the bounding box for ( int i = okularPage->rotation(); i > 0; --i ) @@ -3358,20 +3435,28 @@ void PageView::updateItemSize( PageViewItem * item, int colWidth, int rowHeight crop.bottom = rot.right; } - // Expand the crop slightly beyond the bounding box - static const double cropExpandRatio = 0.04; - const double cropExpand = cropExpandRatio * ( (crop.right-crop.left) + (crop.bottom-crop.top) ) / 2; - crop = Okular::NormalizedRect( - crop.left - cropExpand, - crop.top - cropExpand, - crop.right + cropExpand, - crop.bottom + cropExpand ) & Okular::NormalizedRect( 0, 0, 1, 1 ); + // Expand the crop slightly beyond the bounding box (for Trim Margins only) + if (Okular::Settings::trimMargins()) { + static const double cropExpandRatio = 0.04; + const double cropExpand = cropExpandRatio * ( (crop.right-crop.left) + (crop.bottom-crop.top) ) / 2; + crop = Okular::NormalizedRect( + crop.left - cropExpand, + crop.top - cropExpand, + crop.right + cropExpand, + crop.bottom + cropExpand ) & Okular::NormalizedRect( 0, 0, 1, 1 ); + } // We currently generate a larger image and then crop it, so if the // crop rect is very small the generated image is huge. Hence, we shouldn't // let the crop rect become too small. - // Make sure we crop by at most 50% in either dimension: - static const double minCropRatio = 0.5; + static double minCropRatio; + if (Okular::Settings::trimMargins()) { + // Make sure we crop by at most 50% in either dimension: + minCropRatio = 0.5; + } else { + // Looser Constraint for "Trim Selection" + minCropRatio = 0.20; + } if ( ( crop.right - crop.left ) < minCropRatio ) { const double newLeft = ( crop.left + crop.right ) / 2 - minCropRatio/2; @@ -3848,6 +3933,8 @@ void PageView::updateCursor( const QPoint &p ) setCursor( Qt::CrossCursor ); else if ( d->mouseMode == Okular::Settings::EnumMouseMode::RectSelect ) setCursor( Qt::CrossCursor ); + else if ( d->mouseMode == Okular::Settings::EnumMouseMode::TrimSelect ) + setCursor( Qt::CrossCursor ); else if ( d->mouseAnn ) setCursor( Qt::ClosedHandCursor ); else if ( d->mouseMode == Okular::Settings::EnumMouseMode::Browse ) @@ -4904,8 +4991,24 @@ void PageView::slotPageSizes( int newsize ) d->document->setPageSize( d->document->pageSizes().at( newsize ) ); } +// Enforce mutual-exclusion between trim modes +// Each mode is uniquely identified by a single value +// From Okular::Settings::EnumTrimMode +void PageView::updateTrimMode( int except_id ) { + const QList trimModeActions = d->aTrimMode->menu()->actions(); + foreach(QAction *trimModeAction, trimModeActions) + { + if (trimModeAction->data().toInt() != except_id) + trimModeAction->setChecked( false ); + } +} + void PageView::slotTrimMarginsToggled( bool on ) { + if (on) { // Turn off any other Trim modes + updateTrimMode(d->aTrimMargins->data().toInt()); + } + if ( Okular::Settings::trimMargins() != on ) { Okular::Settings::setTrimMargins( on ); @@ -4918,6 +5021,49 @@ void PageView::slotTrimMarginsToggled( bool on ) } } +void PageView::slotTrimToSelectionToggled( bool on ) +{ + if ( on ) { // Turn off any other Trim modes + updateTrimMode(d->aTrimToSelection->data().toInt()); + + d->mouseMode = Okular::Settings::EnumMouseMode::TrimSelect; + // change the text in messageWindow (and show it if hidden) + d->messageWindow->display( i18n( "Draw a rectangle around the page area you wish to keep visible" ), QString(), PageViewMessage::Info, -1 ); + // force hiding of annotator toolbar + if ( d->aToggleAnnotator && d->aToggleAnnotator->isChecked() ) + { + d->aToggleAnnotator->trigger(); + d->annotator->setHidingForced( true ); + } + // force an update of the cursor + updateCursor(); + } else { + + // toggled off while making selection + if ( Okular::Settings::EnumMouseMode::TrimSelect == d->mouseMode ) { + // clear widget selection and invalidate rect + selectionClear(); + + // When Trim selection bbox interaction is over, we should switch to another mousemode. + if ( d->aPrevAction ) + { + d->aPrevAction->trigger(); + d->aPrevAction = 0; + } else { + d->aMouseNormal->trigger(); + } + } + + d->trimBoundingBox = Okular::NormalizedRect(); // invalidate box + if ( d->document->pages() > 0 ) + { + slotRelayoutPages(); + slotRequestVisiblePixmaps(); // TODO: slotRelayoutPages() may have done this already! + } + } + +} + void PageView::slotToggleForms() { toggleFormWidgets( !d->m_formsVisible ); diff --git a/ui/pageview.h b/ui/pageview.h index e65b57500..376bbdc5e 100644 --- a/ui/pageview.h +++ b/ui/pageview.h @@ -187,6 +187,9 @@ Q_OBJECT // used when selecting stuff, makes the view scroll as necessary to keep the mouse inside the view void scrollPosIntoView( const QPoint & pos ); + // called from slots to turn off trim modes mutually exclusive to id + void updateTrimMode( int except_id ); + // don't want to expose classes in here class PageViewPrivate * d; @@ -238,6 +241,7 @@ Q_OBJECT void slotRotateOriginal(); void slotPageSizes( int ); void slotTrimMarginsToggled( bool ); + void slotTrimToSelectionToggled( bool ); void slotToggleForms(); void slotFormChanged( int pageNumber ); void slotRefreshPage();