mirror of
https://github.com/qgis/QGIS.git
synced 2025-12-04 00:06:46 -05:00
269 lines
9.1 KiB
C++
269 lines
9.1 KiB
C++
/***************************************************************************
|
|
qgslayoutmousehandles.cpp
|
|
------------------------
|
|
begin : September 2017
|
|
copyright : (C) 2017 by Nyall Dawson
|
|
email : nyall.dawson@gmail.com
|
|
***************************************************************************/
|
|
|
|
/***************************************************************************
|
|
* *
|
|
* 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 "qgslayoutmousehandles.h"
|
|
#include "qgis.h"
|
|
#include "qgslayout.h"
|
|
#include "qgslayoutitem.h"
|
|
#include "qgslayoututils.h"
|
|
#include "qgslayoutview.h"
|
|
#include "qgslayoutviewtoolselect.h"
|
|
#include "qgslayoutsnapper.h"
|
|
#include "qgslayoutitemgroup.h"
|
|
#include "qgslayoutundostack.h"
|
|
#include "qgslayoutrendercontext.h"
|
|
#include <QGraphicsView>
|
|
#include <QGraphicsSceneHoverEvent>
|
|
#include <QPainter>
|
|
#include <QWidget>
|
|
#include <limits>
|
|
|
|
///@cond PRIVATE
|
|
|
|
QgsLayoutMouseHandles::QgsLayoutMouseHandles( QgsLayout *layout, QgsLayoutView *view )
|
|
: QgsGraphicsViewMouseHandles( view )
|
|
, mLayout( layout )
|
|
, mView( view )
|
|
{
|
|
//listen for selection changes, and update handles accordingly
|
|
connect( mLayout, &QGraphicsScene::selectionChanged, this, &QgsLayoutMouseHandles::selectionChanged );
|
|
|
|
mHorizontalSnapLine = mView->createSnapLine();
|
|
mHorizontalSnapLine->hide();
|
|
layout->addItem( mHorizontalSnapLine );
|
|
mVerticalSnapLine = mView->createSnapLine();
|
|
mVerticalSnapLine->hide();
|
|
layout->addItem( mVerticalSnapLine );
|
|
}
|
|
|
|
void QgsLayoutMouseHandles::paint( QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget )
|
|
{
|
|
paintInternal( painter, mLayout->renderContext().isPreviewRender(),
|
|
mLayout->renderContext().boundingBoxesVisible(), true, option, widget );
|
|
}
|
|
|
|
void QgsLayoutMouseHandles::selectionChanged()
|
|
{
|
|
//listen out for selected items' size and rotation changed signals
|
|
const QList<QGraphicsItem *> itemList = layout()->items();
|
|
for ( QGraphicsItem *graphicsItem : itemList )
|
|
{
|
|
QgsLayoutItem *item = dynamic_cast<QgsLayoutItem *>( graphicsItem );
|
|
if ( !item )
|
|
continue;
|
|
|
|
if ( item->isSelected() )
|
|
{
|
|
connect( item, &QgsLayoutItem::sizePositionChanged, this, &QgsLayoutMouseHandles::selectedItemSizeChanged );
|
|
connect( item, &QgsLayoutItem::rotationChanged, this, &QgsLayoutMouseHandles::selectedItemRotationChanged );
|
|
connect( item, &QgsLayoutItem::frameChanged, this, &QgsLayoutMouseHandles::selectedItemSizeChanged );
|
|
connect( item, &QgsLayoutItem::lockChanged, this, &QgsLayoutMouseHandles::selectedItemSizeChanged );
|
|
}
|
|
else
|
|
{
|
|
disconnect( item, &QgsLayoutItem::sizePositionChanged, this, &QgsLayoutMouseHandles::selectedItemSizeChanged );
|
|
disconnect( item, &QgsLayoutItem::rotationChanged, this, &QgsLayoutMouseHandles::selectedItemRotationChanged );
|
|
disconnect( item, &QgsLayoutItem::frameChanged, this, &QgsLayoutMouseHandles::selectedItemSizeChanged );
|
|
disconnect( item, &QgsLayoutItem::lockChanged, this, &QgsLayoutMouseHandles::selectedItemSizeChanged );
|
|
}
|
|
}
|
|
|
|
resetStatusBar();
|
|
updateHandles();
|
|
}
|
|
|
|
void QgsLayoutMouseHandles::setViewportCursor( Qt::CursorShape cursor )
|
|
{
|
|
//workaround qt bug #3732 by setting cursor for QGraphicsView viewport,
|
|
//rather then setting it directly here
|
|
|
|
if ( qobject_cast< QgsLayoutViewToolSelect *>( mView->tool() ) )
|
|
{
|
|
mView->viewport()->setCursor( cursor );
|
|
}
|
|
}
|
|
|
|
QList<QGraphicsItem *> QgsLayoutMouseHandles::sceneItemsAtPoint( QPointF scenePoint )
|
|
{
|
|
QList< QGraphicsItem * > items;
|
|
if ( QgsLayoutViewToolSelect *tool = qobject_cast< QgsLayoutViewToolSelect *>( mView->tool() ) )
|
|
{
|
|
const double searchTolerance = tool->searchToleranceInLayoutUnits();
|
|
const QRectF area( scenePoint.x() - searchTolerance, scenePoint.y() - searchTolerance, 2 * searchTolerance, 2 * searchTolerance );
|
|
items = mLayout->items( area );
|
|
}
|
|
else
|
|
{
|
|
items = mLayout->items( scenePoint );
|
|
}
|
|
items.erase( std::remove_if( items.begin(), items.end(), []( QGraphicsItem * item )
|
|
{
|
|
return !dynamic_cast<QgsLayoutItem *>( item );
|
|
} ), items.end() );
|
|
|
|
return items;
|
|
}
|
|
|
|
QList<QGraphicsItem *> QgsLayoutMouseHandles::selectedSceneItems( bool includeLockedItems ) const
|
|
{
|
|
QList<QGraphicsItem *> res;
|
|
const QList<QgsLayoutItem *> layoutItems = mLayout->selectedLayoutItems( includeLockedItems );
|
|
res.reserve( layoutItems.size() );
|
|
for ( QgsLayoutItem *item : layoutItems )
|
|
res << item;
|
|
return res;
|
|
}
|
|
|
|
bool QgsLayoutMouseHandles::itemIsLocked( QGraphicsItem *item )
|
|
{
|
|
if ( QgsLayoutItem *layoutItem = dynamic_cast<QgsLayoutItem *>( item ) )
|
|
return layoutItem->isLocked();
|
|
else
|
|
return false;
|
|
}
|
|
|
|
bool QgsLayoutMouseHandles::itemIsGroupMember( QGraphicsItem *item )
|
|
{
|
|
if ( QgsLayoutItem *layoutItem = dynamic_cast<QgsLayoutItem *>( item ) )
|
|
return layoutItem->isGroupMember();
|
|
else
|
|
return false;
|
|
}
|
|
|
|
QRectF QgsLayoutMouseHandles::itemRect( QGraphicsItem *item ) const
|
|
{
|
|
if ( QgsLayoutItem *layoutItem = dynamic_cast<QgsLayoutItem *>( item ) )
|
|
return layoutItem->rectWithFrame();
|
|
else
|
|
return QRectF();
|
|
}
|
|
|
|
QPointF QgsLayoutMouseHandles::snapPoint( QPointF originalPoint, QgsLayoutMouseHandles::SnapGuideMode mode, bool snapHorizontal, bool snapVertical )
|
|
{
|
|
bool snapped = false;
|
|
|
|
const QList< QGraphicsItem * > selectedItems = selectedSceneItems();
|
|
QList< QGraphicsItem * > itemsToExclude;
|
|
expandItemList( selectedItems, itemsToExclude );
|
|
|
|
QList< QgsLayoutItem * > layoutItemsToExclude;
|
|
for ( QGraphicsItem *item : itemsToExclude )
|
|
layoutItemsToExclude << dynamic_cast< QgsLayoutItem * >( item );
|
|
|
|
//depending on the mode, we either snap just the single point, or all the bounds of the selection
|
|
QPointF snappedPoint;
|
|
switch ( mode )
|
|
{
|
|
case Item:
|
|
snappedPoint = mLayout->snapper().snapRect( rect().translated( originalPoint ), mView->transform().m11(), snapped, snapHorizontal ? mHorizontalSnapLine : nullptr,
|
|
snapVertical ? mVerticalSnapLine : nullptr, &layoutItemsToExclude ).topLeft();
|
|
break;
|
|
case Point:
|
|
snappedPoint = mLayout->snapper().snapPoint( originalPoint, mView->transform().m11(), snapped, snapHorizontal ? mHorizontalSnapLine : nullptr,
|
|
snapVertical ? mVerticalSnapLine : nullptr, &layoutItemsToExclude );
|
|
break;
|
|
}
|
|
|
|
return snapped ? snappedPoint : originalPoint;
|
|
}
|
|
|
|
void QgsLayoutMouseHandles::createItemCommand( QGraphicsItem *item )
|
|
{
|
|
mItemCommand.reset( qgis::down_cast< QgsLayoutItem * >( item )->createCommand( QString(), 0 ) );
|
|
mItemCommand->saveBeforeState();
|
|
}
|
|
|
|
void QgsLayoutMouseHandles::endItemCommand( QGraphicsItem * )
|
|
{
|
|
mItemCommand->saveAfterState();
|
|
mLayout->undoStack()->push( mItemCommand.release() );
|
|
}
|
|
|
|
void QgsLayoutMouseHandles::startMacroCommand( const QString &text )
|
|
{
|
|
mLayout->undoStack()->beginMacro( text );
|
|
|
|
}
|
|
|
|
void QgsLayoutMouseHandles::endMacroCommand()
|
|
{
|
|
mLayout->undoStack()->endMacro();
|
|
}
|
|
|
|
void QgsLayoutMouseHandles::hideAlignItems()
|
|
{
|
|
mHorizontalSnapLine->hide();
|
|
mVerticalSnapLine->hide();
|
|
}
|
|
|
|
void QgsLayoutMouseHandles::expandItemList( const QList<QGraphicsItem *> &items, QList<QGraphicsItem *> &collected ) const
|
|
{
|
|
for ( QGraphicsItem *item : items )
|
|
{
|
|
if ( item->type() == QgsLayoutItemRegistry::LayoutGroup )
|
|
{
|
|
// if a group is selected, we don't draw the bounds of the group - instead we draw the bounds of the grouped items
|
|
const QList<QgsLayoutItem *> groupItems = static_cast< QgsLayoutItemGroup * >( item )->items();
|
|
expandItemList( groupItems, collected );
|
|
}
|
|
else
|
|
{
|
|
collected << item;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void QgsLayoutMouseHandles::expandItemList( const QList<QgsLayoutItem *> &items, QList<QGraphicsItem *> &collected ) const
|
|
{
|
|
for ( QGraphicsItem *item : items )
|
|
{
|
|
if ( item->type() == QgsLayoutItemRegistry::LayoutGroup )
|
|
{
|
|
// if a group is selected, we don't draw the bounds of the group - instead we draw the bounds of the grouped items
|
|
const QList<QgsLayoutItem *> groupItems = static_cast< QgsLayoutItemGroup * >( item )->items();
|
|
expandItemList( groupItems, collected );
|
|
}
|
|
else
|
|
{
|
|
collected << item;
|
|
}
|
|
}
|
|
}
|
|
|
|
void QgsLayoutMouseHandles::moveItem( QGraphicsItem *item, double deltaX, double deltaY )
|
|
{
|
|
qgis::down_cast< QgsLayoutItem * >( item )->attemptMoveBy( deltaX, deltaY );
|
|
}
|
|
|
|
void QgsLayoutMouseHandles::setItemRect( QGraphicsItem *item, QRectF rect )
|
|
{
|
|
QgsLayoutItem *layoutItem = dynamic_cast< QgsLayoutItem * >( item );
|
|
layoutItem->attemptSetSceneRect( rect, true );
|
|
}
|
|
|
|
void QgsLayoutMouseHandles::showStatusMessage( const QString &message )
|
|
{
|
|
if ( !mView )
|
|
return;
|
|
|
|
mView->pushStatusMessage( message );
|
|
}
|
|
|
|
|
|
///@endcond PRIVATE
|