You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
614 lines
16 KiB
614 lines
16 KiB
/* This file is part of the KDE project
|
|
Copyright (C) 2001, 2002, 2003 The Karbon Developers
|
|
|
|
This library is free software; you can redistribute it and/or
|
|
modify it under the terms of the GNU Library General Public
|
|
License as published by the Free Software Foundation; either
|
|
version 2 of the License, or (at your option) any later version.
|
|
|
|
This library is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
Library General Public License for more details.
|
|
|
|
You should have received a copy of the GNU Library General Public License
|
|
along with this library; see the file COPYING.LIB. If not, write to
|
|
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
|
* Boston, MA 02110-1301, USA.
|
|
*/
|
|
|
|
#include <math.h>
|
|
#include <stdlib.h>
|
|
|
|
#include <qcursor.h>
|
|
#include <qlabel.h>
|
|
#include <qradiobutton.h>
|
|
#include <qbuttongroup.h>
|
|
|
|
#include <KoPoint.h>
|
|
#include <KoRect.h>
|
|
#include <kdebug.h>
|
|
|
|
#include <karbon_part.h>
|
|
#include <karbon_view.h>
|
|
#include <render/vpainter.h>
|
|
#include <render/vpainterfactory.h>
|
|
#include <core/vselection.h>
|
|
#include "vselecttool.h"
|
|
#include <commands/vtransformcmd.h>
|
|
#include <visitors/vselectiondesc.h>
|
|
#include <visitors/vselectobjects.h>
|
|
#include <widgets/vcanvas.h>
|
|
|
|
VSelectOptionsWidget::VSelectOptionsWidget( KarbonPart *part )
|
|
: KDialogBase( 0L, "", true, i18n( "Selection" ), Ok | Cancel ), m_part( part )
|
|
{
|
|
QButtonGroup *group = new QButtonGroup( 1, Qt::Horizontal, i18n( "Selection Mode" ), this );
|
|
|
|
new QRadioButton( i18n( "Select in current layer" ), group );
|
|
new QRadioButton( i18n( "Select in visible layers" ), group );
|
|
new QRadioButton( i18n( "Select in selected layers" ), group );
|
|
|
|
group->setRadioButtonExclusive( true );
|
|
group->setButton( part->document().selectionMode() );
|
|
|
|
connect( group, SIGNAL( clicked( int ) ), this, SLOT( modeChange( int ) ) );
|
|
|
|
group->setInsideMargin( 4 );
|
|
group->setInsideSpacing( 2 );
|
|
|
|
setMainWidget( group );
|
|
setFixedSize( baseSize() );
|
|
} // VSelectOptionsWidget::VSelectOptionsWidget
|
|
|
|
void VSelectOptionsWidget::modeChange( int mode )
|
|
{
|
|
m_part->document().setSelectionMode( (VDocument::VSelectionMode)mode );
|
|
} // VSelectOptionsWidget::modeChanged
|
|
|
|
VSelectTool::VSelectTool( KarbonView *view )
|
|
: VTool( view, "tool_select" ), m_state( normal )
|
|
{
|
|
m_lock = false;
|
|
m_add = true;
|
|
m_objects.setAutoDelete( true );
|
|
m_optionsWidget = new VSelectOptionsWidget( view->part() );
|
|
registerTool( this );
|
|
connect( view, SIGNAL( selectionChange() ), this, SLOT( updateStatusBar() ) );
|
|
}
|
|
|
|
VSelectTool::~VSelectTool()
|
|
{
|
|
delete m_optionsWidget;
|
|
}
|
|
|
|
void
|
|
VSelectTool::activate()
|
|
{
|
|
VTool::activate();
|
|
view()->setCursor( QCursor( Qt::arrowCursor ) );
|
|
view()->part()->document().selection()->showHandle();
|
|
view()->part()->document().selection()->setSelectObjects();
|
|
view()->part()->document().selection()->setState( VObject::selected );
|
|
view()->part()->document().selection()->selectNodes();
|
|
view()->repaintAll( view()->part()->document().selection()->boundingBox() );
|
|
updateStatusBar();
|
|
}
|
|
|
|
QString
|
|
VSelectTool::statusText()
|
|
{
|
|
return i18n( "Select" );
|
|
}
|
|
|
|
QString VSelectTool::contextHelp()
|
|
{
|
|
QString s = i18n( "<qt><b>Selection tool:</b><br>" );
|
|
s += i18n( "<i>Select in current layer:</i><br>The selection is made in the layer selected in the layers docker.<br><br>" );
|
|
s += i18n( "<i>Select in visible layers:</i><br>The selection is made in the visible layers (eye in the layers docker).<br><br>" );
|
|
s += i18n( "<i>Select in selected layers:</i><br>The selection is made in the checked layers in the layers docker.<br><br>" );
|
|
s += i18n( "<i>Position using arrow keys</i><br>The selection can be positioned up, down, left and right using the corresponding arrow keys." );
|
|
return s;
|
|
} // VSelectTool::contextHelp
|
|
|
|
void
|
|
VSelectTool::draw()
|
|
{
|
|
VPainter *painter = view()->painterFactory()->editpainter();
|
|
//painter->setZoomFactor( view()->zoom() );
|
|
painter->setRasterOp( Qt::NotROP );
|
|
|
|
KoRect rect = view()->part()->document().selection()->boundingBox();
|
|
|
|
if( m_state != normal )
|
|
{
|
|
VObjectListIterator itr = m_objects;
|
|
for( ; itr.current(); ++itr )
|
|
{
|
|
itr.current()->draw( painter, &itr.current()->boundingBox() );
|
|
}
|
|
}
|
|
else if( m_state == normal )
|
|
{
|
|
painter->setPen( Qt::DotLine );
|
|
painter->newPath();
|
|
painter->moveTo( KoPoint( first().x(), first().y() ) );
|
|
painter->lineTo( KoPoint( m_current.x(), first().y() ) );
|
|
painter->lineTo( KoPoint( m_current.x(), m_current.y() ) );
|
|
painter->lineTo( KoPoint( first().x(), m_current.y() ) );
|
|
painter->lineTo( KoPoint( first().x(), first().y() ) );
|
|
painter->strokePath();
|
|
|
|
m_state = normal;
|
|
}
|
|
}
|
|
|
|
void
|
|
VSelectTool::setCursor() const
|
|
{
|
|
if( m_state != normal || !view() ) return;
|
|
switch( view()->part()->document().selection()->handleNode( last() ) )
|
|
{
|
|
case node_lt:
|
|
case node_rb:
|
|
view()->setCursor( QCursor( Qt::SizeFDiagCursor ) );
|
|
break;
|
|
case node_rt:
|
|
case node_lb:
|
|
view()->setCursor( QCursor( Qt::SizeBDiagCursor ) );
|
|
break;
|
|
case node_lm:
|
|
case node_rm:
|
|
view()->setCursor( QCursor( Qt::SizeHorCursor ) );
|
|
break;
|
|
case node_mt:
|
|
case node_mb:
|
|
view()->setCursor( QCursor( Qt::SizeVerCursor ) );
|
|
break;
|
|
default:
|
|
view()->setCursor( QCursor( Qt::arrowCursor ) );
|
|
}
|
|
}
|
|
|
|
void
|
|
VSelectTool::mouseButtonPress()
|
|
{
|
|
// we are adding to the selection
|
|
m_add = true;
|
|
|
|
m_current = first();
|
|
|
|
m_activeNode = view()->part()->document().selection()->handleNode( first() );
|
|
KoRect rect = view()->part()->document().selection()->boundingBox();
|
|
|
|
if( m_activeNode != node_none )
|
|
m_state = scaling;
|
|
else if( rect.contains( m_current ) && m_state == normal )
|
|
m_state = moving;
|
|
|
|
recalc();
|
|
|
|
// undraw selection bounding box
|
|
view()->part()->document().selection()->setState( VObject::edit );
|
|
view()->repaintAll( rect );
|
|
view()->part()->document().selection()->setState( VObject::selected );
|
|
|
|
draw();
|
|
}
|
|
|
|
void
|
|
VSelectTool::rightMouseButtonPress()
|
|
{
|
|
// we are removing from the selection
|
|
m_add = false;
|
|
|
|
m_current = first();
|
|
|
|
recalc();
|
|
|
|
// undraw selection bounding box
|
|
view()->part()->document().selection()->setState( VObject::edit );
|
|
view()->repaintAll( view()->part()->document().selection()->boundingBox() );
|
|
view()->part()->document().selection()->setState( VObject::selected );
|
|
|
|
draw();
|
|
}
|
|
|
|
void
|
|
VSelectTool::mouseDrag()
|
|
{
|
|
draw();
|
|
|
|
recalc();
|
|
|
|
draw();
|
|
}
|
|
|
|
void
|
|
VSelectTool::rightMouseButtonRelease()
|
|
{
|
|
m_state = normal;
|
|
m_add = true;
|
|
|
|
if( ctrlPressed() )
|
|
{
|
|
// unselect the topmost object under the mouse cursor
|
|
VObjectList newSelection;
|
|
VSelectObjects selector( newSelection, first() );
|
|
if( selector.visit( view()->part()->document() ) )
|
|
view()->part()->document().selection()->take( *newSelection.last() );
|
|
|
|
view()->part()->repaintAllViews( view()->part()->document().selection()->boundingBox() );
|
|
view()->selectionChanged();
|
|
|
|
updateStatusBar();
|
|
}
|
|
else if( view()->part()->document().selection()->objects().count() > 0 )
|
|
{
|
|
view()->showSelectionPopupMenu( QCursor::pos() );
|
|
}
|
|
}
|
|
|
|
void
|
|
VSelectTool::mouseButtonRelease()
|
|
{
|
|
m_state = normal;
|
|
m_add = true;
|
|
|
|
// selection of next underlying object
|
|
if( shiftPressed() )
|
|
{
|
|
VObjectList newSelection;
|
|
VObjectList oldSelection = view()->part()->document().selection()->objects();
|
|
|
|
// clear selection if not in multi-slection-mode
|
|
if( ! ctrlPressed() )
|
|
view()->part()->document().selection()->clear();
|
|
|
|
// get a list of all object under the mouse cursor
|
|
VSelectObjects selector( newSelection, first(), true, true );
|
|
if( selector.visit( view()->part()->document() ) )
|
|
{
|
|
// determine the last selected object of the object stack
|
|
VObject *lastMatched = 0L;
|
|
VObjectListIterator it( newSelection );
|
|
for( ; it.current(); ++it )
|
|
{
|
|
if( oldSelection.contains( it.current() ) )
|
|
lastMatched = it.current();
|
|
}
|
|
|
|
// select the next underlying object or the first if:
|
|
// - none is selected
|
|
// - the stack's bottom object was the last selected object
|
|
if( lastMatched && lastMatched != newSelection.first() )
|
|
view()->part()->document().selection()->append( newSelection.at( newSelection.find( lastMatched )-1 ) );
|
|
else
|
|
view()->part()->document().selection()->append( newSelection.last() );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// clear selection if not in multi-slection-mode
|
|
if( ! ctrlPressed() )
|
|
view()->part()->document().selection()->clear();
|
|
|
|
// append the topmost object under the mouse cursor to the selection
|
|
VObjectList newSelection;
|
|
VSelectObjects selector( newSelection, first() );
|
|
if( selector.visit( view()->part()->document() ) )
|
|
view()->part()->document().selection()->append( newSelection.last() );
|
|
}
|
|
|
|
view()->part()->repaintAllViews( view()->part()->document().selection()->boundingBox() );
|
|
view()->selectionChanged();
|
|
|
|
updateStatusBar();
|
|
}
|
|
|
|
void
|
|
VSelectTool::mouseDragRelease()
|
|
{
|
|
if( m_state == normal )
|
|
{
|
|
// Y mirroring
|
|
KoPoint fp = first();
|
|
KoPoint lp = last();
|
|
if( ! ctrlPressed() )
|
|
view()->part()->document().selection()->clear();
|
|
|
|
KoRect selRect = KoRect( fp.x(), fp.y(), lp.x() - fp.x(), lp.y() - fp.y() ).normalize();
|
|
if( m_add )
|
|
view()->part()->document().selection()->append( selRect );
|
|
else
|
|
view()->part()->document().selection()->take( selRect );
|
|
view()->part()->repaintAllViews( selRect );
|
|
}
|
|
else if( m_state == moving )
|
|
{
|
|
m_state = normal;
|
|
recalc();
|
|
if( m_lock )
|
|
view()->part()->addCommand(
|
|
new VTranslateCmd(
|
|
&view()->part()->document(),
|
|
abs( int( m_distx ) ) >= abs( int( m_disty ) ) ? qRound( m_distx ) : 0,
|
|
abs( int( m_distx ) ) <= abs( int( m_disty ) ) ? qRound( m_disty ) : 0, altPressed() ),
|
|
true );
|
|
else
|
|
view()->part()->addCommand(
|
|
new VTranslateCmd( &view()->part()->document(), qRound( m_distx ), qRound( m_disty ), altPressed() ),
|
|
true );
|
|
}
|
|
else if( m_state == scaling )
|
|
{
|
|
m_state = normal;
|
|
view()->part()->addCommand(
|
|
new VScaleCmd( &view()->part()->document(), m_sp, m_s1, m_s2, altPressed() ),
|
|
true );
|
|
m_s1 = m_s2 = 1;
|
|
}
|
|
|
|
view()->selectionChanged();
|
|
m_lock = false;
|
|
updateStatusBar();
|
|
}
|
|
|
|
void
|
|
VSelectTool::arrowKeyReleased( Qt::Key key )
|
|
{
|
|
int dx = 0;
|
|
int dy = 0;
|
|
switch( key )
|
|
{
|
|
case Qt::Key_Up: dy = 10; break;
|
|
case Qt::Key_Down: dy = -10; break;
|
|
case Qt::Key_Right: dx = 10; break;
|
|
case Qt::Key_Left: dx = -10; break;
|
|
default: return;
|
|
}
|
|
m_state = normal;
|
|
view()->part()->addCommand(
|
|
new VTranslateCmd(
|
|
&view()->part()->document(),
|
|
dx, dy ),
|
|
true );
|
|
view()->selectionChanged();
|
|
updateStatusBar();
|
|
}
|
|
|
|
bool
|
|
VSelectTool::keyReleased( Qt::Key key )
|
|
{
|
|
|
|
VSelection* selection = view()->part()->document().selection();
|
|
|
|
switch( key )
|
|
{
|
|
// increase/decrease the handle size
|
|
case Qt::Key_I:
|
|
{
|
|
uint handleSize = selection->handleSize();
|
|
if( shiftPressed() )
|
|
selection->setHandleSize( ++handleSize );
|
|
else if( handleSize > 1 )
|
|
selection->setHandleSize( --handleSize );
|
|
}
|
|
break;
|
|
default: return false;
|
|
}
|
|
|
|
if( view() )
|
|
view()->repaintAll( selection->boundingBox() );
|
|
|
|
return true;
|
|
}
|
|
|
|
void
|
|
VSelectTool::updateStatusBar() const
|
|
{
|
|
if( ! view() )
|
|
return;
|
|
|
|
if( ! view()->part() )
|
|
return;
|
|
|
|
int objcount = view()->part()->document().selection()->objects().count();
|
|
if( objcount > 0 )
|
|
{
|
|
KoRect rect = view()->part()->document().selection()->boundingBox();
|
|
|
|
double x = KoUnit::toUserValue( rect.x(), view()->part()->unit() );
|
|
double y = KoUnit::toUserValue( rect.y(), view()->part()->unit() );
|
|
double r = KoUnit::toUserValue( rect.right(), view()->part()->unit() );
|
|
double b = KoUnit::toUserValue( rect.bottom(), view()->part()->unit() );
|
|
|
|
// print bottom-left (%1,%2), top-right (%3,%4) corner of selection bounding box and document unit (%5)
|
|
QString selectMessage = i18n( "[(left,bottom), (right,top)] (actual unit)", "Selection [(%1, %2), (%3, %4)] (%5)").arg( x, 0, 'f', 1 ).arg( y, 0, 'f', 1 ).arg( r, 0, 'f', 1 ).arg( b, 0, 'f', 1 ).arg( view()->part()->unitName() );
|
|
|
|
VSelectionDescription selectionDesc;
|
|
selectionDesc.visit( *view()->part()->document().selection() );
|
|
selectMessage += QString( "(%1)" ).arg( selectionDesc.description() );
|
|
|
|
view()->statusMessage()->setText( selectMessage );
|
|
}
|
|
else
|
|
view()->statusMessage()->setText( i18n( "No selection" ) );
|
|
}
|
|
|
|
void
|
|
VSelectTool::mouseDragCtrlPressed()
|
|
{
|
|
m_lock = true;
|
|
}
|
|
|
|
void
|
|
VSelectTool::mouseDragCtrlReleased()
|
|
{
|
|
m_lock = false;
|
|
}
|
|
|
|
void
|
|
VSelectTool::mouseDragShiftPressed()
|
|
{
|
|
draw();
|
|
|
|
recalc();
|
|
|
|
draw();
|
|
}
|
|
|
|
void
|
|
VSelectTool::mouseDragShiftReleased()
|
|
{
|
|
draw();
|
|
|
|
recalc();
|
|
|
|
draw();
|
|
}
|
|
|
|
void
|
|
VSelectTool::cancel()
|
|
{
|
|
// Erase old object:
|
|
if ( isDragging() )
|
|
{
|
|
draw();
|
|
m_state = normal;
|
|
view()->repaintAll( view()->part()->document().selection()->boundingBox() );
|
|
}
|
|
}
|
|
|
|
void
|
|
VSelectTool::recalc()
|
|
{
|
|
if( m_state == normal )
|
|
{
|
|
m_current = last();
|
|
}
|
|
else
|
|
{
|
|
VTransformCmd* cmd;
|
|
KoPoint _first = view()->canvasWidget()->snapToGrid( first() );
|
|
KoPoint _last = view()->canvasWidget()->snapToGrid( last() );
|
|
KoRect rect = view()->part()->document().selection()->boundingBox();
|
|
|
|
if( m_state == moving )
|
|
{
|
|
KoPoint p( rect.x() + last().x() - first().x(), rect.bottom() + last().y() - first().y() );
|
|
p = view()->canvasWidget()->snapToGrid( p );
|
|
m_distx = p.x() - rect.x();
|
|
m_disty = p.y() - rect.bottom();
|
|
if( m_lock )
|
|
cmd = new VTranslateCmd( 0L, abs( int( m_distx ) ) >= abs( int( m_disty ) ) ? m_distx : 0,
|
|
abs( int( m_distx ) ) <= abs( int( m_disty ) ) ? m_disty : 0 );
|
|
else
|
|
cmd = new VTranslateCmd( 0L, m_distx, m_disty );
|
|
}
|
|
else
|
|
{
|
|
if( m_activeNode == node_lb )
|
|
{
|
|
m_sp = KoPoint( rect.right(), rect.bottom() );
|
|
m_s1 = ( rect.right() - _last.x() ) / double( rect.width() );
|
|
m_s2 = ( rect.bottom() - _last.y() ) / double( rect.height() );
|
|
}
|
|
else if( m_activeNode == node_mb )
|
|
{
|
|
m_sp = KoPoint( ( ( rect.right() + rect.left() ) / 2 ), rect.bottom() );
|
|
m_s1 = 1;
|
|
m_s2 = ( rect.bottom() - _last.y() ) / double( rect.height() );
|
|
}
|
|
else if( m_activeNode == node_rb )
|
|
{
|
|
m_sp = KoPoint( rect.x(), rect.bottom() );
|
|
m_s1 = ( _last.x() - rect.x() ) / double( rect.width() );
|
|
m_s2 = ( rect.bottom() - _last.y() ) / double( rect.height() );
|
|
}
|
|
else if( m_activeNode == node_rm)
|
|
{
|
|
m_sp = KoPoint( rect.x(), ( rect.bottom() + rect.top() ) / 2 );
|
|
m_s1 = ( _last.x() - rect.x() ) / double( rect.width() );
|
|
m_s2 = 1;
|
|
}
|
|
else if( m_activeNode == node_rt )
|
|
{
|
|
m_sp = KoPoint( rect.x(), rect.y() );
|
|
m_s1 = ( _last.x() - rect.x() ) / double( rect.width() );
|
|
m_s2 = ( _last.y() - rect.y() ) / double( rect.height() );
|
|
}
|
|
else if( m_activeNode == node_mt )
|
|
{
|
|
m_sp = KoPoint( ( ( rect.right() + rect.left() ) / 2 ), rect.y() );
|
|
m_s1 = 1;
|
|
m_s2 = ( _last.y() - rect.y() ) / double( rect.height() );
|
|
}
|
|
else if( m_activeNode == node_lt )
|
|
{
|
|
m_sp = KoPoint( rect.right(), rect.y() );
|
|
m_s1 = ( rect.right() - _last.x() ) / double( rect.width() );
|
|
m_s2 = ( _last.y() - rect.y() ) / double( rect.height() );
|
|
}
|
|
else if( m_activeNode == node_lm )
|
|
{
|
|
m_sp = KoPoint( rect.right(), ( rect.bottom() + rect.top() ) / 2 );
|
|
m_s1 = ( rect.right() - _last.x() ) / double( rect.width() );
|
|
m_s2 = 1;
|
|
}
|
|
|
|
if( shiftPressed() )
|
|
m_s1 = m_s2 = kMax( m_s1, m_s2 );
|
|
cmd = new VScaleCmd( 0L, m_sp, m_s1, m_s2 );
|
|
}
|
|
|
|
// Copy selected objects and transform:
|
|
m_objects.clear();
|
|
VObject* copy;
|
|
|
|
VObjectListIterator itr = view()->part()->document().selection()->objects();
|
|
for( ; itr.current() ; ++itr )
|
|
{
|
|
if( itr.current()->state() != VObject::deleted )
|
|
{
|
|
copy = itr.current()->clone();
|
|
copy->setState( VObject::edit );
|
|
|
|
cmd->visit( *copy );
|
|
|
|
m_objects.append( copy );
|
|
}
|
|
}
|
|
|
|
delete( cmd );
|
|
}
|
|
}
|
|
|
|
bool
|
|
VSelectTool::showDialog() const
|
|
{
|
|
return m_optionsWidget->exec() == QDialog::Accepted;
|
|
}
|
|
|
|
void
|
|
VSelectTool::refreshUnit()
|
|
{
|
|
updateStatusBar();
|
|
}
|
|
|
|
void
|
|
VSelectTool::setup( KActionCollection *collection )
|
|
{
|
|
m_action = static_cast<KRadioAction *>(collection -> action( name() ) );
|
|
|
|
if( m_action == 0 )
|
|
{
|
|
m_action = new KRadioAction( i18n( "Select Tool" ), "14_select", Qt::SHIFT+Qt::Key_H, this, SLOT( activate() ), collection, name() );
|
|
m_action->setToolTip( i18n( "Select" ) );
|
|
m_action->setExclusiveGroup( "select" );
|
|
//m_ownAction = true;
|
|
}
|
|
}
|
|
|
|
#include "vselecttool.moc"
|