// Copyright (C) 2003 Dominique Devriese // 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. // This program 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 General Public License for more details. // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA // 02110-1301, USA. #include "construct_mode.h" #include "../objects/object_factory.h" #include "../objects/object_drawer.h" #include "../objects/text_type.h" #include "../objects/text_imp.h" #include "../objects/bogus_imp.h" #include "../objects/point_imp.h" #include "../misc/argsparser.h" #include "../kig/kig_document.h" #include "../kig/kig_part.h" #include "../kig/kig_view.h" #include "../misc/object_constructor.h" #include "../misc/coordinate_system.h" #include "../misc/kigpainter.h" #include "../misc/calcpaths.h" #include "popup.h" #include #include #include #include #include static void redefinePoint( ObjectTypeCalcer* mpt, const Coordinate& c, KigDocument& doc, const KigWidget& w ) { ObjectFactory::instance()->redefinePoint( mpt, c, doc, w ); mpt->calc( doc ); } BaseConstructMode::BaseConstructMode( KigPart& d ) : BaseMode( d ) { mpt = ObjectFactory::instance()->fixedPointCalcer( Coordinate( 0, 0 ) ); mpt->calc( d.document() ); mcursor = ObjectFactory::instance()->cursorPointCalcer( Coordinate( 0, 0 ) ); mcursor->calc( d.document() ); // mcursorholder = new ObjectHolder( mcursor ); } BaseConstructMode::~BaseConstructMode() { delete mcursor; // delete mcursorholder; } void BaseConstructMode::leftReleased( QMouseEvent* e, KigWidget* v ) { if( (pointLocation() - e->pos()).manhattanLength() > 4 ) return; ObjectHolder* o = 0; bool keyCtrlOrShift = ( e->modifiers() & ( Qt::ControlModifier | Qt::ShiftModifier) ) != 0; std::vector moco = oco(); if ( ! moco.empty() ) { std::vector goodargs; if ( !moco.empty() ) { std::vector::const_iterator it; std::vector testargs = getCalcers( mparents ); for ( std::vector::const_iterator i = moco.begin(); i != moco.end(); ++i ) { it = std::find( mparents.begin(), mparents.end(), *i ); bool newdup = ( it == mparents.end() ) || isAlreadySelectedOK( testargs, it - mparents.begin() ); if ( newdup ) { testargs.push_back( ( *i )->calcer() ); if ( wantArgs( testargs, mdoc.document(), *v ) ) goodargs.push_back( *i ); testargs.pop_back(); } } int id = ObjectChooserPopup::getObjectFromList( e->pos(), v, goodargs ); if ( id >= 0 ) o = goodargs[id]; } } leftClickedObject( o, e->pos(), *v, keyCtrlOrShift ); KigMode::leftReleased( e, v ); } void BaseConstructMode::leftClickedObject( ObjectHolder* o, const QPoint& p, KigWidget& w, bool ) { std::vector::iterator it = std::find( mparents.begin(), mparents.end(), o ); std::vector nargs = getCalcers( mparents ); // // mp: duplicationchecked controls whether the arguments list is // free of duplications or if a duplication is safe (asking this to // the Constructor class through the "isAlreadySelectedOK" method). // bool duplicationchecked = ( it == mparents.end() ) || isAlreadySelectedOK( nargs, it - mparents.begin() ); if ( o && duplicationchecked ) { nargs.push_back( o->calcer() ); if ( wantArgs( nargs, mdoc.document(), w ) ) { selectObject( o, w ); return; } } nargs = getCalcers( mparents ); nargs.push_back( mpt.get() ); if ( wantArgs( nargs, mdoc.document(), w ) ) { // add mpt to the document.. ObjectHolder* n = new ObjectHolder( mpt.get() ); mdoc.addObject( n ); selectObject( n, w ); // get a new mpt for our further use.. mpt = ObjectFactory::instance()->sensiblePointCalcer( w.fromScreen( p ), mdoc.document(), w ); mpt->calc( mdoc.document() ); return; } nargs = getCalcers( mparents ); nargs.push_back( mcursor ); if ( wantArgs( nargs, mdoc.document(), w ) ) { // DON'T add mpt to the document.. // the objectholder has been constructed once and for all // when entering construction mode, and delete in the // destructor. ObjectHolder* n = new ObjectHolder( mcursor ); selectObject( n, w ); mcursor = ObjectFactory::instance()->cursorPointCalcer( w.fromScreen( p ) ); // mcursor = ObjectFactory::instance()->sensiblePointCalcer( w.fromScreen( p ), mdoc.document(), w ); mcursor->calc( mdoc.document() ); delete n; } } void BaseConstructMode::midClicked( const QPoint& p, KigWidget& w ) { std::vector args = getCalcers( mparents ); args.push_back( mpt.get() ); if ( wantArgs( args, mdoc.document(), w ) ) { ObjectHolder* n = new ObjectHolder( mpt.get() ); mdoc.addObject( n ); selectObject( n, w ); mpt = ObjectFactory::instance()->sensiblePointCalcer( w.fromScreen( p ), mdoc.document(), w ); mpt->calc( mdoc.document() ); } } void BaseConstructMode::rightClicked( const std::vector&, const QPoint&, KigWidget& ) { // TODO ? } void BaseConstructMode::mouseMoved( const std::vector& os, const QPoint& p, KigWidget& w, bool shiftpressed ) { mdoc.emitStatusBarText( selectStatement( getCalcers( mparents ), w ) ); w.updateCurPix(); KigPainter pter( w.screenInfo(), &w.curPix, mdoc.document() ); Coordinate ncoord = w.fromScreen( p ); if ( shiftpressed ) ncoord = mdoc.document().coordinateSystem().snapToGrid( ncoord, w ); redefinePoint( mpt.get(), ncoord, mdoc.document(), w ); mcursor->move( ncoord, mdoc.document() ); mcursor->calc( mdoc.document() ); std::vector args = getCalcers( mparents ); bool duplicationchecked = false; std::vector goodargs; if ( ! os.empty() ) { std::vector::const_iterator it; std::vector testargs = getCalcers( mparents ); for ( std::vector::const_iterator i = os.begin(); i != os.end(); ++i ) { it = std::find( mparents.begin(), mparents.end(), *i ); bool newdup = ( it == mparents.end() ) || isAlreadySelectedOK( args, it - mparents.begin() ); if ( newdup ) { testargs.push_back( ( *i )->calcer() ); if ( wantArgs( testargs, mdoc.document(), w ) ) goodargs.push_back( *i ); testargs.pop_back(); } duplicationchecked |= newdup; } } bool calcnow = ( goodargs.size() == 1 ) || ( ( goodargs.size() > 0 ) && ( goodargs.front()->imp()->inherits( PointImp::stype() ) ) ); if ( calcnow ) { args.push_back( goodargs.front()->calcer() ); } if ( !os.empty() && duplicationchecked && calcnow ) { handlePrelim( args, p, pter, w ); w.setCursor( Qt::PointingHandCursor ); } else { std::vector args = getCalcers( mparents ); args.push_back( mpt.get() ); std::vector argscursor = getCalcers( mparents ); argscursor.push_back( mcursor ); bool text = true; if ( wantArgs( args, mdoc.document(), w ) ) { ObjectDrawer d; d.draw( *mpt->imp(), pter, true ); handlePrelim( args, p, pter, w ); w.setCursor( Qt::PointingHandCursor ); } else if ( wantArgs( argscursor, mdoc.document(), w ) ) { ObjectDrawer d; // d.draw( *mcursor->imp(), pter, true ); handlePrelim( argscursor, p, pter, w ); w.setCursor( Qt::CrossCursor ); } else { w.setCursor( Qt::ArrowCursor ); text = false; } if ( !text && ( goodargs.size() > 1 ) ) { QString strwhich = i18n( "Which object?" ); mdoc.emitStatusBarText( strwhich ); QPoint textloc = p; textloc.setX( textloc.x() + 15 ); pter.drawTextStd( textloc, strwhich ); w.setCursor( Qt::PointingHandCursor ); } } w.updateWidget( pter.overlay() ); } void BaseConstructMode::selectObject( ObjectHolder* o, KigWidget& w ) { mparents.push_back( o ); std::vector args = getCalcers( mparents ); if ( wantArgs( args, mdoc.document(), w ) == ArgsParser::Complete ) { handleArgs( args, w ); }; w.redrawScreen( mparents ); } PointConstructMode::PointConstructMode( KigPart& d ) : BaseMode( d ) { // we add the data objects to the document cause // ObjectFactory::redefinePoint does that too, and this way, we can // depend on them already being known by the doc when we add the // mpt.. mpt = ObjectFactory::instance()->fixedPointCalcer( Coordinate() ); mpt->calc( d.document() ); mdoc.emitStatusBarText( i18n( "Click the location where you want to place the new point, or the curve that you want to attach it to..." ) ); } PointConstructMode::~PointConstructMode() { } void PointConstructMode::leftClickedObject( ObjectHolder*, const QPoint&, KigWidget& w, bool ) { mdoc.addObject( new ObjectHolder( mpt.get() ) ); w.redrawScreen( std::vector() ); mdoc.emitStatusBarText( QString() ); mdoc.doneMode( this ); } void PointConstructMode::midClicked( const QPoint& p, KigWidget& w ) { leftClickedObject( 0, p, w, true ); } void PointConstructMode::rightClicked( const std::vector&, const QPoint&, KigWidget& ) { // TODO ? } void PointConstructMode::mouseMoved( const std::vector&, const QPoint& p, KigWidget& w, bool shiftpressed ) { w.updateCurPix(); KigPainter pter( w.screenInfo(), &w.curPix, mdoc.document() ); Coordinate ncoord = w.fromScreen( p ); if ( shiftpressed ) ncoord = mdoc.document().coordinateSystem().snapToGrid( ncoord, w ); redefinePoint( mpt.get(), ncoord, mdoc.document(), w ); ObjectDrawer d; d.draw( *mpt->imp(), pter, true ); w.setCursor( Qt::BlankCursor ); w.updateWidget( pter.overlay() ); } void BaseConstructMode::enableActions() { BaseMode::enableActions(); mdoc.aCancelConstruction->setEnabled( true ); } void BaseConstructMode::cancelConstruction() { finish(); } void PointConstructMode::enableActions() { BaseMode::enableActions(); mdoc.aCancelConstruction->setEnabled( true ); } void PointConstructMode::cancelConstruction() { mdoc.doneMode( this ); } void BaseConstructMode::selectObjects( const std::vector& os, KigWidget& w ) { for ( std::vector::const_iterator i = os.begin(); i != os.end(); ++i ) { std::vector args = getCalcers( mparents ); assert( wantArgs( args, mdoc.document(), w ) != ArgsParser::Complete ); selectObject( *i, w ); }; } void ConstructMode::handlePrelim( const std::vector& args, const QPoint& p, KigPainter& pter, KigWidget& w ) { // set the text next to the arrow cursor like in modes/normal.cc QPoint textloc = p; textloc.setX( textloc.x() + 15 ); mctor->handlePrelim( pter, args, mdoc.document(), w ); QString o = mctor->useText( *args.back(), args, mdoc.document(), w ); pter.drawTextStd( textloc, o ); } int ConstructMode::isAlreadySelectedOK( const std::vector& os, const int& pos ) { return mctor->isAlreadySelectedOK( os, pos ); } int ConstructMode::wantArgs( const std::vector& os, KigDocument& d, KigWidget& w ) { return mctor->wantArgs( os, d, w ); } void BaseConstructMode::finish() { mdoc.doneMode( this ); } ConstructMode::ConstructMode( KigPart& d, const ObjectConstructor* ctor ) : BaseConstructMode( d ), mctor( ctor ) { } ConstructMode::~ConstructMode() { } // does a test result have a frame by default ? static const bool test_has_frame_dflt = true; void TestConstructMode::handlePrelim( const std::vector& os, const QPoint& p, KigPainter& pter, KigWidget& w ) { Args args; std::transform( os.begin(), os.end(), std::back_inserter( args ), std::mem_fun( &ObjectCalcer::imp ) ); // usetext QString usetext = i18n( mtype->argsParser().usetext( args.back(), args ).c_str() ); QPoint textloc = p; textloc.setX( textloc.x() + 15 ); pter.drawTextStd( textloc, usetext ); // test result ObjectImp* data = mtype->calc( args, mdoc.document() ); if ( ! data->valid() ) return; assert( data->inherits( TestResultImp::stype() ) ); QString outputtext = static_cast( data )->data(); TextImp ti( outputtext, w.fromScreen( p + QPoint( - 40, 30 ) ), test_has_frame_dflt ); ti.draw( pter ); delete data; } TestConstructMode::TestConstructMode( KigPart& d, const ArgsParserObjectType* type ) : BaseConstructMode( d ), mtype( type ) { } TestConstructMode::~TestConstructMode() { } void ConstructMode::handleArgs( const std::vector& args, KigWidget& w ) { mctor->handleArgs( args, mdoc, w ); finish(); } int TestConstructMode::isAlreadySelectedOK( const std::vector&, const int& ) { return false; } int TestConstructMode::wantArgs( const std::vector& os, KigDocument&, KigWidget& ) { return mtype->argsParser().check( os ); } void TestConstructMode::handleArgs( const std::vector& args, KigWidget& ) { mresult = new ObjectTypeCalcer( mtype, args ); mresult->calc( mdoc.document() ); mdoc.emitStatusBarText( i18n( "Now select the location for the result label." ) ); } void TestConstructMode::leftClickedObject( ObjectHolder* o, const QPoint& p, KigWidget& w, bool ctrlOrShiftDown ) { if ( mresult ) { QPoint qloc = p + QPoint( -40, 0 ); Coordinate loc = w.fromScreen( qloc ); std::vector parents; parents.push_back( new ObjectConstCalcer( new IntImp( test_has_frame_dflt ) ) ); parents.push_back( new ObjectConstCalcer( new PointImp( loc ) ) ); parents.push_back( new ObjectConstCalcer( new StringImp( QString::fromLatin1( "%1" ) ) ) ); assert( mresult->imp()->inherits( TestResultImp::stype() ) ); // parents.push_back( // new ObjectPropertyCalcer( // mresult.get(), "test-result" ) ); // parents.back()->calc( mdoc.document() ); /* (mp) * now we can refer directly to the TestResultImp, since it is also a StringImp * this creates a backward compatibility issue with kig save files: new versions of * kig can still read old saved files, but files created with the new kig version * cannot be read by old versions of kig. This is necessary in order to allow a test * result to also carry its intrinsic boolean value and thus be used in further * constructions, e.g. as an argument to a python script. */ parents.push_back( mresult.get() ); ObjectCalcer* ret = new ObjectTypeCalcer( TextType::instance(), parents ); ret->calc( mdoc.document() ); mdoc.addObject( new ObjectHolder( ret ) ); w.unsetCursor(); mdoc.emitStatusBarText( QString() ); finish(); } else BaseConstructMode::leftClickedObject( o, p, w, ctrlOrShiftDown ); } void TestConstructMode::midClicked( const QPoint& p, KigWidget& w ) { if ( mresult ) { // nothing to be done here, really } else BaseConstructMode::midClicked( p, w ); } void TestConstructMode::rightClicked( const std::vector& oco, const QPoint& p, KigWidget& w ) { if ( mresult ) { // nothing to be done here, really } else BaseConstructMode::rightClicked( oco, p, w ); } void TestConstructMode::mouseMoved( const std::vector& os, const QPoint& p, KigWidget& w, bool shiftPressed ) { if ( mresult ) { w.setCursor( Qt::BlankCursor ); w.updateCurPix(); KigPainter pter( w.screenInfo(), &w.curPix, mdoc.document() ); QPoint qloc = p + QPoint( -40, 0 ); Coordinate loc = w.fromScreen( qloc ); assert( dynamic_cast( mresult->imp() ) ); TextImp ti( static_cast( mresult->imp() )->data(), loc, test_has_frame_dflt ); ObjectDrawer d; d.draw( ti, pter, false ); w.updateWidget( pter.overlay() ); } else BaseConstructMode::mouseMoved( os, p, w, shiftPressed ); } QString ConstructMode::selectStatement( const std::vector& args, const KigWidget& w ) { return mctor->selectStatement( args, mdoc.document(), w ); } QString TestConstructMode::selectStatement( const std::vector& sel, const KigWidget& ) { using namespace std; Args args; transform( sel.begin(), sel.end(), back_inserter( args ), mem_fun( &ObjectCalcer::imp ) ); std::string ret = mtype->argsParser().selectStatement( args ); if ( ret.empty() ) return QString(); return i18n( ret.c_str() ); } void PointConstructMode::redrawScreen( KigWidget* w ) { w->redrawScreen( std::vector() ); } void BaseConstructMode::redrawScreen( KigWidget* w ) { w->redrawScreen( std::vector() ); }