mirror of
https://invent.kde.org/graphics/okular
synced 2024-07-16 10:17:04 +00:00
Add "Trim To Selection" feature
Changes C1. Added submenu, moved "Trim margins" (TM mode) to it and added "Trim To Selection" (TS mode). C2. Activating "Trim To selection" enters a new mousemode, similar to RectSelect for defining a viewport. C3. Once a viewport has been defined, it serves as a viewport for all pages in the document. C4. Left/Right pages are not treated differently. Manual Testing T1. Switching between modes enforces at most one active. T2. Can deactivate a mode by selecting it again from the menu. T3. When draggin bbox selection, clicking outside page does not crash. T4. When in "Facing Pages" mode, mouse release must be over any page (or is ignored). T5. Normalized bbox coords are computed relative to page indicated by point of mouse release. T6. Behave as expected when switching between any pair of No Trim/Trim Margins/Trim To Selection. T7. TM mode persisted across app restarts (existing behavior). T8. TS mode forgotten across app restarts (as desired). T9. Exiting and reselectin "Trim To Selection" prompts for new bbox. T10. Choosing a small Trim bbox enforces minimium dimensions size (As percentag of total), as it does in TM mode, because of the "scale big and crop down" implementation, to avoid huge pixmaps. TS mode minimum set at 20% (vs. TM mode's 50%). REVIEW: 124716 BUGS: 351156
This commit is contained in:
parent
21dfb2127f
commit
172d78c6b3
|
@ -221,6 +221,14 @@
|
|||
<choice name="Summary" />
|
||||
</choices>
|
||||
</entry>
|
||||
<entry key="TrimMode" type="Enum" >
|
||||
<default>None</default>
|
||||
<choices>
|
||||
<choice name="None" />
|
||||
<choice name="Margins" />
|
||||
<choice name="Selection" />
|
||||
</choices>
|
||||
</entry>
|
||||
<entry key="MouseMode" type="Enum" >
|
||||
<default>Browse</default>
|
||||
<choices>
|
||||
|
@ -230,6 +238,7 @@
|
|||
<choice name="TextSelect" />
|
||||
<choice name="TableSelect" />
|
||||
<choice name="Magnifier" />
|
||||
<choice name="TrimSelect" />
|
||||
</choices>
|
||||
</entry>
|
||||
<entry key="ShowSourceLocationsGraphically" type="Bool" >
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<!DOCTYPE kpartgui SYSTEM "kpartgui.dtd">
|
||||
<kpartgui name="okular_part_viewermode" version="2">
|
||||
<kpartgui name="okular_part_viewermode" version="3">
|
||||
|
||||
<MenuBar>
|
||||
<Menu name="file"><text>&File</text>
|
||||
|
@ -34,7 +34,7 @@
|
|||
<Action name="view_orientation_original" group="viewer_menu_merge"/>
|
||||
</Menu>
|
||||
<Action name="view_pagesizes" group="viewer_menu_merge"/>
|
||||
<Action name="view_trim_margins" group="viewer_menu_merge"/>
|
||||
<Action name="view_trim" group="viewer_menu_merge"/>
|
||||
<Separator group="viewer_menu_merge"/>
|
||||
<Action name="go_previous" group="viewer_menu_merge"/>
|
||||
<Action name="go_next" group="viewer_menu_merge"/>
|
||||
|
|
4
part.rc
4
part.rc
|
@ -1,5 +1,5 @@
|
|||
<!DOCTYPE kpartgui SYSTEM "kpartgui.dtd">
|
||||
<kpartgui name="okular_part" version="36">
|
||||
<kpartgui name="okular_part" version="37">
|
||||
<MenuBar>
|
||||
<Menu name="file"><text>&File</text>
|
||||
<Action name="get_new_stuff" group="file_open"/>
|
||||
|
@ -43,7 +43,7 @@
|
|||
<Action name="view_orientation_original"/>
|
||||
</Menu>
|
||||
<Action name="view_pagesizes"/>
|
||||
<Action name="view_trim_margins"/>
|
||||
<Action name="view_trim_mode"/>
|
||||
<Separator/>
|
||||
<Action name="view_toggle_forms"/>
|
||||
</Menu>
|
||||
|
|
178
ui/pageview.cpp
178
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<QAction *> 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 );
|
||||
|
|
|
@ -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();
|
||||
|
|
Loading…
Reference in a new issue