karbon

vselecttool.cc

00001 /* This file is part of the KDE project
00002    Copyright (C) 2001, 2002, 2003 The Karbon Developers
00003 
00004    This library is free software; you can redistribute it and/or
00005    modify it under the terms of the GNU Library General Public
00006    License as published by the Free Software Foundation; either
00007    version 2 of the License, or (at your option) any later version.
00008 
00009    This library is distributed in the hope that it will be useful,
00010    but WITHOUT ANY WARRANTY; without even the implied warranty of
00011    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00012    Library General Public License for more details.
00013 
00014    You should have received a copy of the GNU Library General Public License
00015    along with this library; see the file COPYING.LIB.  If not, write to
00016    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
00017  * Boston, MA 02110-1301, USA.
00018 */
00019 
00020 #include <math.h>
00021 #include <stdlib.h>
00022 
00023 #include <qcursor.h>
00024 #include <qlabel.h>
00025 #include <qradiobutton.h>
00026 #include <qbuttongroup.h>
00027 
00028 #include <KoPoint.h>
00029 #include <KoRect.h>
00030 #include <kdebug.h>
00031 
00032 #include <karbon_part.h>
00033 #include <karbon_view.h>
00034 #include <render/vpainter.h>
00035 #include <render/vpainterfactory.h>
00036 #include <core/vselection.h>
00037 #include "vselecttool.h"
00038 #include <commands/vtransformcmd.h>
00039 #include <visitors/vselectiondesc.h>
00040 #include <visitors/vselectobjects.h>
00041 #include <widgets/vcanvas.h>
00042 
00043 VSelectOptionsWidget::VSelectOptionsWidget( KarbonPart *part )
00044     : KDialogBase( 0L, "", true, i18n( "Selection" ), Ok | Cancel ), m_part( part )
00045 {
00046     QButtonGroup *group = new QButtonGroup( 1, Qt::Horizontal, i18n( "Selection Mode" ), this );
00047 
00048     new QRadioButton( i18n( "Select in current layer" ), group );
00049     new QRadioButton( i18n( "Select in visible layers" ), group );
00050     new QRadioButton( i18n( "Select in selected layers" ), group );
00051 
00052     group->setRadioButtonExclusive( true );
00053     group->setButton( part->document().selectionMode() );
00054 
00055     connect( group, SIGNAL( clicked( int ) ), this, SLOT( modeChange( int ) ) );
00056 
00057     group->setInsideMargin( 4 );
00058     group->setInsideSpacing( 2 );
00059 
00060     setMainWidget( group );
00061     setFixedSize( baseSize() );
00062 } // VSelectOptionsWidget::VSelectOptionsWidget
00063 
00064 void VSelectOptionsWidget::modeChange( int mode )
00065 {
00066     m_part->document().setSelectionMode( (VDocument::VSelectionMode)mode );
00067 } // VSelectOptionsWidget::modeChanged
00068 
00069 VSelectTool::VSelectTool( KarbonView *view )
00070     : VTool( view, "tool_select" ), m_state( normal )
00071 {
00072     m_lock = false;
00073     m_add = true;
00074     m_objects.setAutoDelete( true );
00075     m_optionsWidget = new VSelectOptionsWidget( view->part() );
00076     registerTool( this );
00077     connect( view, SIGNAL( selectionChange() ), this, SLOT( updateStatusBar() ) );
00078 }
00079 
00080 VSelectTool::~VSelectTool()
00081 {
00082     delete m_optionsWidget;
00083 }
00084 
00085 void
00086 VSelectTool::activate()
00087 {
00088     VTool::activate();
00089     view()->setCursor( QCursor( Qt::arrowCursor ) );
00090     view()->part()->document().selection()->showHandle();
00091     view()->part()->document().selection()->setSelectObjects();
00092     view()->part()->document().selection()->setState( VObject::selected );
00093     view()->part()->document().selection()->selectNodes();
00094     view()->repaintAll( view()->part()->document().selection()->boundingBox() );
00095     updateStatusBar();
00096 }
00097 
00098 QString
00099 VSelectTool::statusText()
00100 {
00101     return i18n( "Select" );
00102 }
00103 
00104 QString VSelectTool::contextHelp()
00105 {
00106     QString s = i18n( "<qt><b>Selection tool:</b><br>" );
00107     s += i18n( "<i>Select in current layer:</i><br>The selection is made in the layer selected in the layers docker.<br><br>" );
00108     s += i18n( "<i>Select in visible layers:</i><br>The selection is made in the visible layers (eye in the layers docker).<br><br>" );
00109     s += i18n( "<i>Select in selected layers:</i><br>The selection is made in the checked layers in the layers docker.<br><br>" );
00110     s += i18n( "<i>Position using arrow keys</i><br>The selection can be positioned up, down, left and right using the corresponding arrow keys." );
00111     return s;
00112 } // VSelectTool::contextHelp
00113 
00114 void
00115 VSelectTool::draw()
00116 {
00117     VPainter *painter = view()->painterFactory()->editpainter();
00118     //painter->setZoomFactor( view()->zoom() );
00119     painter->setRasterOp( Qt::NotROP );
00120 
00121     KoRect rect = view()->part()->document().selection()->boundingBox();
00122 
00123     if( m_state != normal )
00124     {
00125         VObjectListIterator itr = m_objects;
00126         for( ; itr.current(); ++itr )
00127         {
00128             itr.current()->draw( painter, &itr.current()->boundingBox() );
00129         }
00130     }
00131     else if( m_state == normal )
00132     {
00133         painter->setPen( Qt::DotLine );
00134         painter->newPath();
00135         painter->moveTo( KoPoint( first().x(), first().y() ) );
00136         painter->lineTo( KoPoint( m_current.x(), first().y() ) );
00137         painter->lineTo( KoPoint( m_current.x(), m_current.y() ) );
00138         painter->lineTo( KoPoint( first().x(), m_current.y() ) );
00139         painter->lineTo( KoPoint( first().x(), first().y() ) );
00140         painter->strokePath();
00141 
00142         m_state = normal;
00143     }
00144 }
00145 
00146 void
00147 VSelectTool::setCursor() const
00148 {
00149     if( m_state != normal || !view() ) return;
00150     switch( view()->part()->document().selection()->handleNode( last() ) )
00151     {
00152         case node_lt:
00153         case node_rb:
00154             view()->setCursor( QCursor( Qt::SizeFDiagCursor ) );
00155             break;
00156         case node_rt:
00157         case node_lb:
00158             view()->setCursor( QCursor( Qt::SizeBDiagCursor ) );
00159             break;
00160         case node_lm:
00161         case node_rm:
00162             view()->setCursor( QCursor( Qt::SizeHorCursor ) );
00163             break;
00164         case node_mt:
00165         case node_mb:
00166             view()->setCursor( QCursor( Qt::SizeVerCursor ) );
00167             break;
00168         default:
00169             view()->setCursor( QCursor( Qt::arrowCursor ) );
00170     }
00171 }
00172 
00173 void
00174 VSelectTool::mouseButtonPress()
00175 {
00176     // we are adding to the selection
00177     m_add = true;
00178 
00179     m_current = first();
00180 
00181     m_activeNode = view()->part()->document().selection()->handleNode( first() );
00182     KoRect rect = view()->part()->document().selection()->boundingBox();
00183 
00184     if( m_activeNode != node_none )
00185         m_state = scaling;
00186     else if( rect.contains( m_current ) && m_state == normal )
00187         m_state = moving;
00188 
00189     recalc();
00190 
00191     // undraw selection bounding box
00192     view()->part()->document().selection()->setState( VObject::edit );
00193     view()->repaintAll( rect );
00194     view()->part()->document().selection()->setState( VObject::selected );
00195 
00196     draw();
00197 }
00198 
00199 void
00200 VSelectTool::rightMouseButtonPress()
00201 {
00202     // we are removing from the selection
00203     m_add = false;
00204 
00205     m_current = first();
00206 
00207     recalc();
00208 
00209     // undraw selection bounding box
00210     view()->part()->document().selection()->setState( VObject::edit );
00211     view()->repaintAll( view()->part()->document().selection()->boundingBox() );
00212     view()->part()->document().selection()->setState( VObject::selected );
00213 
00214     draw();
00215 }
00216 
00217 void
00218 VSelectTool::mouseDrag()
00219 {
00220     draw();
00221 
00222     recalc();
00223 
00224     draw();
00225 }
00226 
00227 void
00228 VSelectTool::rightMouseButtonRelease()
00229 {
00230     m_state = normal;
00231     m_add = true;
00232 
00233     if( ctrlPressed() )
00234     {
00235         // unselect the topmost object under the mouse cursor
00236         VObjectList newSelection;
00237         VSelectObjects selector( newSelection, first() );
00238         if( selector.visit( view()->part()->document() ) )
00239             view()->part()->document().selection()->take( *newSelection.last() );
00240 
00241         view()->part()->repaintAllViews( view()->part()->document().selection()->boundingBox() );
00242         view()->selectionChanged();
00243 
00244         updateStatusBar();
00245     }
00246     else if( view()->part()->document().selection()->objects().count() > 0 )
00247     {
00248         view()->showSelectionPopupMenu( QCursor::pos() );
00249     }
00250 }
00251 
00252 void
00253 VSelectTool::mouseButtonRelease()
00254 {
00255     m_state = normal;
00256     m_add = true;
00257 
00258     // selection of next underlying object
00259     if( shiftPressed() ) 
00260     {
00261         VObjectList newSelection;
00262         VObjectList oldSelection = view()->part()->document().selection()->objects();
00263 
00264         // clear selection if not in multi-slection-mode
00265         if( ! ctrlPressed() )
00266             view()->part()->document().selection()->clear();
00267 
00268         // get a list of all object under the mouse cursor
00269         VSelectObjects selector( newSelection, first(), true, true );
00270         if( selector.visit( view()->part()->document() ) )
00271         {
00272             // determine the last selected object of the object stack
00273             VObject *lastMatched = 0L;
00274             VObjectListIterator it( newSelection );
00275             for( ; it.current(); ++it )
00276             {
00277                 if( oldSelection.contains( it.current() ) )
00278                     lastMatched = it.current();
00279             }
00280             
00281             // select the next underlying object or the first if:
00282             // - none is selected
00283             // - the stack's bottom object was the last selected object
00284             if( lastMatched && lastMatched != newSelection.first() )
00285                 view()->part()->document().selection()->append( newSelection.at( newSelection.find( lastMatched )-1 ) );
00286             else
00287                 view()->part()->document().selection()->append( newSelection.last() );
00288         }
00289     }
00290     else    
00291     {
00292         // clear selection if not in multi-slection-mode
00293         if( ! ctrlPressed() )
00294             view()->part()->document().selection()->clear();
00295 
00296         // append the topmost object under the mouse cursor to the selection
00297         VObjectList newSelection;
00298         VSelectObjects selector( newSelection, first() );
00299         if( selector.visit( view()->part()->document() ) )
00300             view()->part()->document().selection()->append( newSelection.last() );
00301     }
00302 
00303     view()->part()->repaintAllViews( view()->part()->document().selection()->boundingBox() );
00304     view()->selectionChanged();
00305 
00306     updateStatusBar();
00307 }
00308 
00309 void
00310 VSelectTool::mouseDragRelease()
00311 {
00312     if( m_state == normal )
00313     {
00314         // Y mirroring
00315         KoPoint fp = first();
00316         KoPoint lp = last();
00317         if( ! ctrlPressed() )
00318             view()->part()->document().selection()->clear();
00319         
00320         KoRect selRect = KoRect( fp.x(), fp.y(), lp.x() - fp.x(), lp.y() - fp.y() ).normalize();
00321         if( m_add )
00322             view()->part()->document().selection()->append( selRect );
00323         else
00324             view()->part()->document().selection()->take( selRect );
00325         view()->part()->repaintAllViews( selRect );
00326     }
00327     else if( m_state == moving )
00328     {
00329         m_state = normal;
00330         recalc();
00331         if( m_lock )
00332             view()->part()->addCommand(
00333                  new VTranslateCmd(
00334                     &view()->part()->document(),
00335                     abs( int( m_distx ) ) >= abs( int( m_disty ) ) ? qRound( m_distx ) : 0,
00336                     abs( int( m_distx ) ) <= abs( int( m_disty ) ) ? qRound( m_disty ) : 0, altPressed() ),
00337                 true );
00338         else
00339             view()->part()->addCommand(
00340                 new VTranslateCmd( &view()->part()->document(), qRound( m_distx ), qRound( m_disty ), altPressed() ),
00341                 true );
00342     }
00343     else if( m_state == scaling )
00344     {
00345         m_state = normal;
00346         view()->part()->addCommand(
00347             new VScaleCmd( &view()->part()->document(), m_sp, m_s1, m_s2, altPressed() ),
00348             true );
00349         m_s1 = m_s2 = 1;
00350     }
00351 
00352     view()->selectionChanged();
00353     m_lock = false;
00354     updateStatusBar();
00355 }
00356 
00357 void
00358 VSelectTool::arrowKeyReleased( Qt::Key key )
00359 {
00360     int dx = 0;
00361     int dy = 0;
00362     switch( key )
00363     {
00364         case Qt::Key_Up: dy = 10; break;
00365         case Qt::Key_Down: dy = -10; break;
00366         case Qt::Key_Right: dx = 10; break;
00367         case Qt::Key_Left: dx = -10; break;
00368         default: return;
00369     }
00370     m_state = normal;
00371     view()->part()->addCommand(
00372         new VTranslateCmd(
00373             &view()->part()->document(),
00374             dx, dy ),
00375         true );
00376     view()->selectionChanged();
00377     updateStatusBar();
00378 }
00379 
00380 bool
00381 VSelectTool::keyReleased( Qt::Key key )
00382 {
00383     
00384     VSelection* selection = view()->part()->document().selection();
00385 
00386     switch( key )
00387     {
00388         // increase/decrease the handle size
00389         case Qt::Key_I:
00390         {
00391             uint handleSize = selection->handleSize();
00392             if( shiftPressed() ) 
00393                 selection->setHandleSize( ++handleSize );
00394             else if( handleSize > 1 )
00395                 selection->setHandleSize( --handleSize );
00396         }
00397         break;
00398         default: return false;
00399     }
00400 
00401     if( view() )
00402         view()->repaintAll( selection->boundingBox() );
00403 
00404     return true;
00405 }
00406 
00407 void
00408 VSelectTool::updateStatusBar() const
00409 {
00410     if( ! view() ) 
00411         return;
00412 
00413     if( ! view()->part() )
00414         return;
00415 
00416     int objcount = view()->part()->document().selection()->objects().count();
00417     if( objcount > 0 )
00418     {
00419         KoRect rect = view()->part()->document().selection()->boundingBox();
00420 
00421         double x = KoUnit::toUserValue( rect.x(), view()->part()->unit() );
00422         double y = KoUnit::toUserValue( rect.y(), view()->part()->unit() );
00423         double r = KoUnit::toUserValue( rect.right(), view()->part()->unit() );
00424         double b = KoUnit::toUserValue( rect.bottom(), view()->part()->unit() );
00425 
00426         // print bottom-left (%1,%2), top-right (%3,%4) corner of selection bounding box and document unit (%5)
00427         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() );
00428 
00429         VSelectionDescription selectionDesc;
00430         selectionDesc.visit( *view()->part()->document().selection() );
00431         selectMessage += QString( "(%1)" ).arg( selectionDesc.description() );
00432 
00433         view()->statusMessage()->setText( selectMessage );
00434     }
00435     else
00436         view()->statusMessage()->setText( i18n( "No selection" ) );
00437 }
00438 
00439 void
00440 VSelectTool::mouseDragCtrlPressed()
00441 {
00442     m_lock = true;
00443 }
00444 
00445 void
00446 VSelectTool::mouseDragCtrlReleased()
00447 {
00448     m_lock = false;
00449 }
00450 
00451 void
00452 VSelectTool::mouseDragShiftPressed()
00453 {
00454     draw();
00455 
00456     recalc();
00457 
00458     draw();
00459 }
00460 
00461 void
00462 VSelectTool::mouseDragShiftReleased()
00463 {
00464     draw();
00465 
00466     recalc();
00467 
00468     draw();
00469 }
00470 
00471 void
00472 VSelectTool::cancel()
00473 {
00474     // Erase old object:
00475     if ( isDragging() )
00476     {
00477         draw();
00478         m_state = normal;
00479         view()->repaintAll( view()->part()->document().selection()->boundingBox() );
00480     }
00481 }
00482 
00483 void
00484 VSelectTool::recalc()
00485 {
00486     if( m_state == normal )
00487     {
00488         m_current = last();
00489     }
00490     else
00491     {
00492         VTransformCmd* cmd;
00493         KoPoint _first = view()->canvasWidget()->snapToGrid( first() );
00494         KoPoint _last = view()->canvasWidget()->snapToGrid( last() );
00495         KoRect rect = view()->part()->document().selection()->boundingBox();
00496 
00497         if( m_state == moving )
00498         {
00499             KoPoint p( rect.x() + last().x() - first().x(), rect.bottom() + last().y() - first().y() );
00500             p = view()->canvasWidget()->snapToGrid( p );
00501             m_distx = p.x() - rect.x();
00502             m_disty = p.y() - rect.bottom();
00503             if( m_lock )
00504                 cmd = new VTranslateCmd( 0L, abs( int( m_distx ) ) >= abs( int( m_disty ) ) ? m_distx : 0,
00505                                              abs( int( m_distx ) ) <= abs( int( m_disty ) ) ? m_disty : 0 );
00506             else
00507                 cmd = new VTranslateCmd( 0L, m_distx, m_disty );
00508         }
00509         else
00510         {
00511             if( m_activeNode == node_lb )
00512             {
00513                 m_sp = KoPoint( rect.right(), rect.bottom() );
00514                 m_s1 = ( rect.right() - _last.x() ) / double( rect.width() );
00515                 m_s2 = ( rect.bottom() - _last.y() ) / double( rect.height() );
00516             }
00517             else if( m_activeNode == node_mb )
00518             {
00519                 m_sp = KoPoint( ( ( rect.right() + rect.left() ) / 2 ), rect.bottom() );
00520                 m_s1 = 1;
00521                 m_s2 = ( rect.bottom() - _last.y() ) / double( rect.height() );
00522             }
00523             else if( m_activeNode == node_rb )
00524             {
00525                 m_sp = KoPoint( rect.x(), rect.bottom() );
00526                 m_s1 = ( _last.x() - rect.x() ) / double( rect.width() );
00527                 m_s2 = ( rect.bottom() - _last.y() ) / double( rect.height() );
00528             }
00529             else if( m_activeNode == node_rm)
00530             {
00531                 m_sp = KoPoint( rect.x(), ( rect.bottom() + rect.top() )  / 2 );
00532                 m_s1 = ( _last.x() - rect.x() ) / double( rect.width() );
00533                 m_s2 = 1;
00534             }
00535             else if( m_activeNode == node_rt )
00536             {
00537                 m_sp = KoPoint( rect.x(), rect.y() );
00538                 m_s1 = ( _last.x() - rect.x() ) / double( rect.width() );
00539                 m_s2 = ( _last.y() - rect.y() ) / double( rect.height() );
00540             }
00541             else if( m_activeNode == node_mt )
00542             {
00543                 m_sp = KoPoint( ( ( rect.right() + rect.left() ) / 2 ), rect.y() );
00544                 m_s1 = 1;
00545                 m_s2 = ( _last.y() - rect.y() ) / double( rect.height() );
00546             }
00547             else if( m_activeNode == node_lt )
00548             {
00549                 m_sp = KoPoint( rect.right(), rect.y() );
00550                 m_s1 = ( rect.right() - _last.x() ) / double( rect.width() );
00551                 m_s2 = ( _last.y() - rect.y() ) / double( rect.height() );
00552             }
00553             else if( m_activeNode == node_lm )
00554             {
00555                 m_sp = KoPoint( rect.right(), ( rect.bottom() + rect.top() )  / 2 );
00556                 m_s1 = ( rect.right() - _last.x() ) / double( rect.width() );
00557                 m_s2 = 1;
00558             }
00559 
00560             if( shiftPressed() )
00561                 m_s1 = m_s2 = kMax( m_s1, m_s2 );
00562             cmd = new VScaleCmd( 0L, m_sp, m_s1, m_s2 );
00563         }
00564 
00565         // Copy selected objects and transform:
00566         m_objects.clear();
00567         VObject* copy;
00568 
00569         VObjectListIterator itr = view()->part()->document().selection()->objects();
00570         for( ; itr.current() ; ++itr )
00571         {
00572             if( itr.current()->state() != VObject::deleted )
00573             {
00574                 copy = itr.current()->clone();
00575                 copy->setState( VObject::edit );
00576 
00577                 cmd->visit( *copy );
00578 
00579                 m_objects.append( copy );
00580             }
00581         }
00582 
00583         delete( cmd );
00584     }
00585 }
00586 
00587 bool
00588 VSelectTool::showDialog() const
00589 {
00590     return m_optionsWidget->exec() == QDialog::Accepted;
00591 }
00592 
00593 void 
00594 VSelectTool::refreshUnit()
00595 {
00596     updateStatusBar();
00597 }
00598 
00599 void
00600 VSelectTool::setup( KActionCollection *collection )
00601 {
00602        m_action = static_cast<KRadioAction *>(collection -> action( name() ) );
00603 
00604     if( m_action == 0 )
00605     {
00606         m_action = new KRadioAction( i18n( "Select Tool" ), "14_select", Qt::SHIFT+Qt::Key_H, this, SLOT( activate() ), collection, name() );
00607         m_action->setToolTip( i18n( "Select" ) );
00608         m_action->setExclusiveGroup( "select" );
00609         //m_ownAction = true;
00610     }
00611 }
00612 
00613 #include "vselecttool.moc"
KDE Home | KDE Accessibility Home | Description of Access Keys