diff --git a/misc/object_hierarchy.cc b/misc/object_hierarchy.cc index 1f86322c..fbe58d62 100644 --- a/misc/object_hierarchy.cc +++ b/misc/object_hierarchy.cc @@ -1,788 +1,789 @@ // 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 + #include "object_hierarchy.h" #include "../objects/object_holder.h" #include "../objects/other_type.h" #include "../objects/object_imp.h" #include "../objects/object_imp_factory.h" #include "../objects/object_type_factory.h" #include "../objects/bogus_imp.h" #include "../objects/transform_types.h" #include "../objects/object_type.h" #include class ObjectHierarchy::Node { public: enum { ID_PushStack, ID_ApplyType, ID_FetchProp }; virtual int id() const = 0; virtual ~Node(); virtual Node* copy() const = 0; virtual void apply( std::vector& stack, int loc, const KigDocument& ) const = 0; virtual void apply( std::vector& stack, int loc ) const = 0; // this function is used to check whether the final objects depend // on the given objects. The dependsstack contains a set of // booleans telling which parts of the hierarchy certainly depend on // the given objects. In this function, the node should check // whether any of its parents have true set, and if so, set its own // value to true. virtual void checkDependsOnGiven( std::vector& dependsstack, int loc ) const = 0; // this function is used to check whether the given objects are all // used by one or more of the final objects. The usedstack contains // a set of booleans telling which parts of the hierarchy are // certainly ancestors of the final objects. In this function, the // node should set all of its parents' booleans to true. virtual void checkArgumentsUsed( std::vector& usedstack ) const = 0; }; ObjectHierarchy::Node::~Node() { } class PushStackNode : public ObjectHierarchy::Node { ObjectImp* mimp; public: PushStackNode( ObjectImp* imp ) : mimp( imp ) {} ~PushStackNode(); const ObjectImp* imp() const { return mimp; } int id() const Q_DECL_OVERRIDE; Node* copy() const Q_DECL_OVERRIDE; void apply( std::vector& stack, int loc, const KigDocument& ) const Q_DECL_OVERRIDE; void apply( std::vector& stack, int loc ) const Q_DECL_OVERRIDE; void checkDependsOnGiven( std::vector& dependsstack, int loc ) const Q_DECL_OVERRIDE; void checkArgumentsUsed( std::vector& usedstack ) const Q_DECL_OVERRIDE; }; void PushStackNode::checkArgumentsUsed( std::vector& ) const { } void PushStackNode::apply( std::vector& stack, int loc ) const { stack[loc] = new ObjectConstCalcer( mimp->copy() ); } void PushStackNode::checkDependsOnGiven( std::vector&, int ) const { // pushstacknode depends on nothing.. return; } int PushStackNode::id() const { return ID_PushStack; } PushStackNode::~PushStackNode() { delete mimp; } ObjectHierarchy::Node* PushStackNode::copy() const { return new PushStackNode( mimp->copy() ); } void PushStackNode::apply( std::vector& stack, int loc, const KigDocument& ) const { stack[loc] = mimp->copy(); } class ApplyTypeNode : public ObjectHierarchy::Node { const ObjectType* mtype; std::vector mparents; public: ApplyTypeNode( const ObjectType* type, const std::vector& parents ) : mtype( type ), mparents( parents ) {} ~ApplyTypeNode(); Node* copy() const Q_DECL_OVERRIDE; const ObjectType* type() const { return mtype; } const std::vector& parents() const { return mparents; } int id() const Q_DECL_OVERRIDE; void apply( std::vector& stack, int loc, const KigDocument& ) const Q_DECL_OVERRIDE; void apply( std::vector& stack, int loc ) const Q_DECL_OVERRIDE; void checkDependsOnGiven( std::vector& dependsstack, int loc ) const Q_DECL_OVERRIDE; void checkArgumentsUsed( std::vector& usedstack ) const Q_DECL_OVERRIDE; }; int ApplyTypeNode::id() const { return ID_ApplyType; } void ApplyTypeNode::checkArgumentsUsed( std::vector& usedstack ) const { for ( uint i = 0; i < mparents.size(); ++i ) { usedstack[mparents[i]] = true; } } void ApplyTypeNode::checkDependsOnGiven( std::vector& dependsstack, int loc ) const { bool result = false; for ( uint i = 0; i < mparents.size(); ++i ) if ( dependsstack[mparents[i]] == true ) result = true; dependsstack[loc] = result; } ApplyTypeNode::~ApplyTypeNode() { } ObjectHierarchy::Node* ApplyTypeNode::copy() const { return new ApplyTypeNode( mtype, mparents ); } void ApplyTypeNode::apply( std::vector& stack, int loc ) const { std::vector parents; for ( uint i = 0; i < mparents.size(); ++i ) parents.push_back( stack[ mparents[i] ] ); stack[loc] = new ObjectTypeCalcer( mtype, parents ); } void ApplyTypeNode::apply( std::vector& stack, int loc, const KigDocument& doc ) const { Args args; for ( uint i = 0; i < mparents.size(); ++i ) args.push_back( stack[mparents[i]] ); args = mtype->sortArgs( args ); stack[loc] = mtype->calc( args, doc ); } class FetchPropertyNode : public ObjectHierarchy::Node { mutable int mpropgid; int mparent; const QByteArray mname; public: // propgid is a cache of the global id of property "name", // just as it is in PropertyObject. We // don't want to ever save this value, since we cannot guarantee it // remains consistent if we add properties some place.. FetchPropertyNode( const int parent, const QByteArray& name, const int propgid = -1 ) : mpropgid( propgid ), mparent( parent ), mname( name ) {} ~FetchPropertyNode(); Node* copy() const Q_DECL_OVERRIDE; void checkDependsOnGiven( std::vector& dependsstack, int loc ) const Q_DECL_OVERRIDE; void checkArgumentsUsed( std::vector& usedstack ) const Q_DECL_OVERRIDE; int parent() const { return mparent; } const QByteArray& propinternalname() const { return mname; } int id() const Q_DECL_OVERRIDE; void apply( std::vector& stack, int loc, const KigDocument& ) const Q_DECL_OVERRIDE; void apply( std::vector& stack, int loc ) const Q_DECL_OVERRIDE; }; FetchPropertyNode::~FetchPropertyNode() { } void FetchPropertyNode::checkArgumentsUsed( std::vector& usedstack ) const { usedstack[mparent] = true; } void FetchPropertyNode::checkDependsOnGiven( std::vector& dependsstack, int loc ) const { dependsstack[loc] = dependsstack[mparent]; } ObjectHierarchy::Node* FetchPropertyNode::copy() const { return new FetchPropertyNode( mparent, mname, mpropgid ); } int FetchPropertyNode::id() const { return ID_FetchProp; } void FetchPropertyNode::apply( std::vector& stack, int loc, const KigDocument& d ) const { assert( stack[mparent] ); if ( mpropgid == -1 ) mpropgid = stack[mparent]->getPropGid( mname ); if ( mpropgid != -1 ) stack[loc] = stack[mparent]->property( stack[mparent]->getPropLid( mpropgid ), d ); else stack[loc] = new InvalidImp(); } void FetchPropertyNode::apply( std::vector& stack, int loc ) const { if ( mpropgid == -1 ) mpropgid = stack[mparent]->imp()->getPropGid( mname ); assert( mpropgid != -1 ); stack[loc] = new ObjectPropertyCalcer( stack[mparent], mpropgid, false ); } std::vector ObjectHierarchy::calc( const Args& a, const KigDocument& doc ) const { assert( a.size() == mnumberofargs ); for ( uint i = 0; i < a.size(); ++i ) assert( a[i]->inherits( margrequirements[i] ) ); std::vector stack; stack.resize( mnodes.size() + mnumberofargs, 0 ); std::copy( a.begin(), a.end(), stack.begin() ); for( uint i = 0; i < mnodes.size(); ++i ) { mnodes[i]->apply( stack, mnumberofargs + i, doc ); }; for ( uint i = mnumberofargs; i < stack.size() - mnumberofresults; ++i ) delete stack[i]; if ( stack.size() < mnumberofargs + mnumberofresults ) { std::vector ret; ret.push_back( new InvalidImp ); return ret; } else { std::vector ret; for ( uint i = stack.size() - mnumberofresults; i < stack.size(); ++i ) ret.push_back( const_cast( stack[i] ) ); return ret; }; } int ObjectHierarchy::visit( const ObjectCalcer* o, std::map& seenmap, bool needed, bool neededatend ) { using namespace std; std::map::iterator smi = seenmap.find( o ); if ( smi != seenmap.end() ) { if ( neededatend ) { // neededatend means that this object is one of the resultant // objects. Therefore, its node has to appear at the end, // because that's where we expect it.. We therefore copy it // there using CopyObjectType.. int ret = mnumberofargs + mnodes.size(); std::vector parents; parents.push_back( smi->second ); mnodes.push_back( new ApplyTypeNode( CopyObjectType::instance(), parents ) ); return ret; } else return smi->second; } std::vector p( o->parents() ); // we check if o descends from the given objects.. bool descendsfromgiven = false; std::vector parents; parents.resize( p.size(), -1 ); for ( uint i = 0; i < p.size(); ++i ) { int v = visit( p[i], seenmap, false ); parents[i] = v; descendsfromgiven |= (v != -1); }; if ( ! descendsfromgiven && ! ( needed && o->imp()->isCache() ) ) { if ( needed ) { assert( ! o->imp()->isCache() ); // o is an object that does not depend on the given objects, but // is needed by other objects, so we just have to just save its // current value here. Node* node = new PushStackNode( o->imp()->copy() ); mnodes.push_back( node ); int ret = mnodes.size() + mnumberofargs - 1; seenmap[o] = ret; return ret; } else return -1; }; return storeObject( o, p, parents, seenmap ); } ObjectHierarchy::~ObjectHierarchy() { for ( uint i = 0; i < mnodes.size(); ++i ) delete mnodes[i]; } ObjectHierarchy::ObjectHierarchy( const ObjectHierarchy& h ) : mnumberofargs( h.mnumberofargs ), mnumberofresults( h.mnumberofresults ), msaveinputtags( h.msaveinputtags ), margrequirements( h.margrequirements ), musetexts( h.musetexts ), mselectstatements( h.mselectstatements ) { mnodes.reserve( h.mnodes.size() ); for ( uint i = 0; i < h.mnodes.size(); ++i ) mnodes.push_back( h.mnodes[i]->copy() ); } ObjectHierarchy ObjectHierarchy::withFixedArgs( const Args& a ) const { assert( a.size() <= mnumberofargs ); ObjectHierarchy ret( *this ); ret.mnumberofargs -= a.size(); ret.margrequirements.resize( ret.mnumberofargs ); std::vector newnodes( mnodes.size() + a.size() ); std::vector::iterator newnodesiter = newnodes.begin(); for ( uint i = 0; i < a.size(); ++i ) { assert( ! a[i]->isCache() ); *newnodesiter++ = new PushStackNode( a[i]->copy() ); }; std::copy( ret.mnodes.begin(), ret.mnodes.end(), newnodesiter ); ret.mnodes = newnodes; return ret; } void ObjectHierarchy::init( const std::vector& from, const std::vector& to ) { msaveinputtags = false; mnumberofargs = from.size(); mnumberofresults = to.size(); margrequirements.resize( from.size(), ObjectImp::stype() ); musetexts.resize( margrequirements.size(), "" ); std::map seenmap; for ( uint i = 0; i < from.size(); ++i ) seenmap[from[i]] = i; for ( std::vector::const_iterator i = to.begin(); i != to.end(); ++i ) { std::vector parents = (*i)->parents(); for ( std::vector::const_iterator j = parents.begin(); j != parents.end(); ++j ) visit( *j, seenmap, true ); } for ( std::vector::const_iterator i = to.begin(); i != to.end(); ++i ) visit( *i, seenmap, true, true ); mselectstatements.resize( margrequirements.size(), "" ); } ObjectHierarchy::ObjectHierarchy( const std::vector& from, const ObjectCalcer* to ) { std::vector tov; tov.push_back( const_cast( to ) ); init( from, tov ); } ObjectHierarchy::ObjectHierarchy( const std::vector& from, const std::vector& to ) { init( from, to ); } void ObjectHierarchy::serialize( QDomElement& parent, QDomDocument& doc ) const { int id = 1; for ( uint i = 0; i < mnumberofargs; ++i ) { QDomElement e = doc.createElement( "input" ); e.setAttribute( "id", id++ ); e.setAttribute( "requirement", margrequirements[i]->internalName() ); // we don't save these atm, since the user can't define them. // we only load them from builtin macro's. if ( msaveinputtags ) { QDomElement ut = doc.createElement( "UseText" ); ut.appendChild( doc.createTextNode( QString::fromLatin1(musetexts[i].c_str() ) ) ); e.appendChild( ut ); QDomElement ss = doc.createElement( "SelectStatement" ); ss.appendChild( doc.createTextNode( QString::fromLatin1(mselectstatements[i].c_str() ) ) ); e.appendChild( ss ); } parent.appendChild( e ); } for ( uint i = 0; i < mnodes.size(); ++i ) { bool result = mnodes.size() - ( id - mnumberofargs - 1 ) <= mnumberofresults; QDomElement e = doc.createElement( result ? "result" : "intermediate" ); e.setAttribute( "id", id++ ); if ( mnodes[i]->id() == Node::ID_ApplyType ) { const ApplyTypeNode* node = static_cast( mnodes[i] ); e.setAttribute( "action", "calc" ); e.setAttribute( "type", QString::fromLatin1( node->type()->fullName() ) ); for ( uint i = 0; i < node->parents().size(); ++i ) { int parent = node->parents()[i] + 1; QDomElement arge = doc.createElement( "arg" ); arge.appendChild( doc.createTextNode( QString::number( parent ) ) ); e.appendChild( arge ); }; } else if ( mnodes[i]->id() == Node::ID_FetchProp ) { const FetchPropertyNode* node = static_cast( mnodes[i] ); e.setAttribute( "action", "fetch-property" ); e.setAttribute( "property", QString( node->propinternalname() ) ); QDomElement arge = doc.createElement( "arg" ); arge.appendChild( doc.createTextNode( QString::number( node->parent() + 1 ) ) ); e.appendChild( arge ); } else { assert( mnodes[i]->id() == ObjectHierarchy::Node::ID_PushStack ); const PushStackNode* node = static_cast( mnodes[i] ); e.setAttribute( "action", "push" ); QString type = ObjectImpFactory::instance()->serialize( *node->imp(), e, doc ); e.setAttribute( "type", type ); }; parent.appendChild( e ); }; } ObjectHierarchy::ObjectHierarchy() : mnumberofargs( 0 ), mnumberofresults( 0 ), msaveinputtags( false ) { } ObjectHierarchy* ObjectHierarchy::buildSafeObjectHierarchy( const QDomElement& parent, QString& error ) { #define KIG_GENERIC_PARSE_ERROR \ { \ error = i18n( "An error was encountered at line %1 in file %2.", \ __LINE__, __FILE__ ); \ return 0; \ } ObjectHierarchy* obhi = new ObjectHierarchy(); bool ok = true; QString tmp; QDomElement e = parent.firstChild().toElement(); for (; !e.isNull(); e = e.nextSibling().toElement() ) { if ( e.tagName() != "input" ) break; tmp = e.attribute( "id" ); uint id = tmp.toInt( &ok ); if ( !ok ) KIG_GENERIC_PARSE_ERROR; obhi->mnumberofargs = qMax( id, obhi->mnumberofargs ); tmp = e.attribute( "requirement" ); const ObjectImpType* req = ObjectImpType::typeFromInternalName( tmp.toLatin1() ); if ( req == 0 ) req = ObjectImp::stype(); // sucks, i know.. obhi->margrequirements.resize( obhi->mnumberofargs, ObjectImp::stype() ); obhi->musetexts.resize( obhi->mnumberofargs, "" ); obhi->mselectstatements.resize( obhi->mnumberofargs, "" ); obhi->margrequirements[id - 1] = req; obhi->musetexts[id - 1] = req->selectStatement(); QDomElement esub = e.firstChild().toElement(); for ( ; !esub.isNull(); esub = esub.nextSibling().toElement() ) { if ( esub.tagName() == "UseText" ) { obhi->msaveinputtags = true; obhi->musetexts[id - 1] = esub.text().toLatin1().data(); } else if ( esub.tagName() == "SelectStatement" ) { obhi->msaveinputtags = true; obhi->mselectstatements[id - 1] = esub.text().toLatin1().data(); } else { // broken file ? ignore... } } } for (; !e.isNull(); e = e.nextSibling().toElement() ) { bool result = e.tagName() == "result"; if ( result ) ++obhi->mnumberofresults; tmp = e.attribute( "id" ); int id = tmp.toInt( &ok ); if ( !ok ) KIG_GENERIC_PARSE_ERROR; tmp = e.attribute( "action" ); Node* newnode = 0; if ( tmp == "calc" ) { // ApplyTypeNode QByteArray typen = e.attribute( "type" ).toLatin1(); const ObjectType* type = ObjectTypeFactory::instance()->find( typen ); if ( ! type ) { error = i18n( "This Kig file uses an object of type \"%1\", " "which this Kig version does not support. " "Perhaps you have compiled Kig without support " "for this object type, " "or perhaps you are using an older Kig version.", QString( typen ) ); return 0; } std::vector parents; for ( QDomNode p = e.firstChild(); !p.isNull(); p = p.nextSibling() ) { QDomElement q = p.toElement(); if ( q.isNull() ) KIG_GENERIC_PARSE_ERROR; // see above if ( q.tagName() != "arg" ) KIG_GENERIC_PARSE_ERROR; int pid = q.text().toInt(&ok ); if ( !ok ) KIG_GENERIC_PARSE_ERROR; parents.push_back( pid - 1 ); }; newnode = new ApplyTypeNode( type, parents ); } else if ( tmp == "fetch-property" ) { // FetchPropertyNode QByteArray propname = e.attribute( "property" ).toLatin1(); QDomElement arge = e.firstChild().toElement(); int parent = arge.text().toInt( &ok ); if ( !ok ) KIG_GENERIC_PARSE_ERROR; newnode = new FetchPropertyNode( parent - 1, propname ); } else { // PushStackNode if ( e.attribute( "action" ) != "push" ) KIG_GENERIC_PARSE_ERROR; QString typen = e.attribute( "type" ); if ( typen.isNull() ) KIG_GENERIC_PARSE_ERROR; ObjectImp* imp = ObjectImpFactory::instance()->deserialize( typen, e, error ); if ( ( ! imp ) && !error.isEmpty() ) return 0; newnode = new PushStackNode( imp ); }; obhi->mnodes.resize( qMax( size_t(id - obhi->mnumberofargs), obhi->mnodes.size() ) ); obhi->mnodes[id - obhi->mnumberofargs - 1] = newnode; }; // if we are here, all went fine return obhi; } ArgsParser ObjectHierarchy::argParser() const { std::vector specs; for ( uint i = 0; i < margrequirements.size(); ++i ) { const ObjectImpType* req = margrequirements[i]; ArgsParser::spec spec; spec.type = req; spec.usetext = musetexts[i]; spec.selectstat = mselectstatements[i]; specs.push_back( spec ); }; return ArgsParser( specs ); } std::vector ObjectHierarchy::buildObjects( const std::vector& os, const KigDocument& doc ) const { assert( os.size() == mnumberofargs ); for ( uint i = 0; i < os.size(); ++i ) assert( os[i]->imp()->inherits( margrequirements[i] ) ); std::vector stack; stack.resize( mnodes.size() + mnumberofargs, 0 ); std::copy( os.begin(), os.end(), stack.begin() ); for( uint i = 0; i < mnodes.size(); ++i ) { mnodes[i]->apply( stack, mnumberofargs + i ); stack[mnumberofargs + i]->calc( doc ); }; std::vector ret( stack.end() - mnumberofresults, stack.end() ); return ret; } const ObjectImpType* ObjectHierarchy::idOfLastResult() const { const Node* n = mnodes.back(); if ( n->id() == Node::ID_PushStack ) return static_cast( n )->imp()->type(); else if ( n->id() == Node::ID_FetchProp ) return ObjectImp::stype(); else return static_cast( n )->type()->resultId(); } ObjectHierarchy ObjectHierarchy::transformFinalObject( const Transformation& t ) const { assert( mnumberofresults == 1 ); ObjectHierarchy ret( *this ); ret.mnodes.push_back( new PushStackNode( new TransformationImp( t ) ) ); std::vector parents; parents.push_back( ret.mnodes.size() - 1); parents.push_back( ret.mnodes.size() ); const ObjectType* type = ApplyTransformationObjectType::instance(); ret.mnodes.push_back( new ApplyTypeNode( type, parents ) ); return ret; } bool operator==( const ObjectHierarchy& lhs, const ObjectHierarchy& rhs ) { if ( ! ( lhs.mnumberofargs == rhs.mnumberofargs && lhs.mnumberofresults == rhs.mnumberofresults && lhs.margrequirements == rhs.margrequirements && lhs.mnodes.size() == rhs.mnodes.size() ) ) return false; // this isn't entirely correct, but it will do, because we don't // really want to know whether the hierarchies are different, but // whether rhs has changed with regard to lhs.. for ( uint i = 0; i < lhs.mnodes.size(); ++i ) if ( lhs.mnodes[i] != lhs.mnodes[i] ) return false; return true; } -bool ObjectHierarchy::resultDoesNotDependOnGiven() const +bool ObjectHierarchy::resultDependsOnGiven() const { std::vector dependsstack( mnodes.size() + mnumberofargs, false ); - for ( uint i = 0; i < mnumberofargs; ++i ) - dependsstack[i] = true; + std::fill( dependsstack.begin(), dependsstack.begin() + mnumberofargs, true ); + for ( uint i = 0; i < mnodes.size(); ++i ) mnodes[i]->checkDependsOnGiven( dependsstack, i + mnumberofargs ); - for ( uint i = dependsstack.size() - mnumberofresults; i < dependsstack.size(); ++i ) - if ( !dependsstack[i] ) - return true; - return false; + + // This could be a call to std::any_of if there was an identity function + return std::find( dependsstack.rbegin(), dependsstack.rbegin() + mnumberofresults, false ) == dependsstack.rbegin() + mnumberofresults; } // returns the "minimum" of a and b ( in the partially ordered set of // ObjectImpType's, using the inherits member function as comparison, // if you for some reason like this sort of non-sense ;) ). This // basically means: return the type that inherits the other type, // because if another type inherits the lowermost type, then it will // also inherit the other.. // mp: if a and b are not directly comparable, as a last resort return c, // which is the "actual" ImpType of the object (see bug #157736 on // bugs.kde.org) const ObjectImpType* lowermost( const ObjectImpType* a, const ObjectImpType* b, const ObjectImpType* c ) { if ( a->inherits( b ) ) return a; if ( b->inherits( a ) ) return b; assert( c-> inherits( a ) ); assert( c-> inherits( b ) ); return c; // this is a last resort! } // this function is part of the visit procedure really. It is // factored out, because it recurses for cache ObjectImp's. What this // does is, it makes sure that object o is calcable, by putting // appropriate Node's in mnodes.. po is o->parents() and pl contains // the location of objects that are already in mnodes and -1 // otherwise.. -1 means we have to store their ObjectImp, unless // they're cache ObjectImp's etc. int ObjectHierarchy::storeObject( const ObjectCalcer* o, const std::vector& po, std::vector& pl, std::map& seenmap ) { for ( uint i = 0; i < po.size(); ++i ) { if ( pl[i] == -1 ) { // we can't store cache ObjectImp's.. if ( po[i]->imp()->isCache() ) { pl[i] = visit( po[i], seenmap, true, false ); } else { Node* argnode = new PushStackNode( po[i]->imp()->copy() ); mnodes.push_back( argnode ); int argloc = mnumberofargs + mnodes.size() - 1; seenmap[po[i]] = argloc; pl[i] = argloc; }; } else if ( (uint) pl[i] < mnumberofargs ) { ObjectCalcer* parent = o->parents()[i]; std::vector opl = o->parents(); margrequirements[pl[i]] = lowermost( margrequirements[pl[i]], o->impRequirement( parent, opl ), parent->imp()->type() ); musetexts[pl[i]] = margrequirements[pl[i]]->selectStatement(); }; }; if ( dynamic_cast( o ) ) mnodes.push_back( new ApplyTypeNode( static_cast( o )->type(), pl ) ); else if ( dynamic_cast( o ) ) { assert( pl.size() == 1 ); int parent = pl.front(); ObjectCalcer* op = po.front(); assert( op ); int propgid = static_cast( o )->propGid(); // assert( propid < op->imp()->propertiesInternalNames().size() ); // mnodes.push_back( new FetchPropertyNode( parent, op->imp()->propertiesInternalNames()[propid], propgid ) ); mnodes.push_back( new FetchPropertyNode( parent, op->imp()->getPropName( propgid ), propgid ) ); } else assert( false ); seenmap[o] = mnumberofargs + mnodes.size() - 1; return mnumberofargs + mnodes.size() - 1; } ObjectHierarchy::ObjectHierarchy( const ObjectCalcer* from, const ObjectCalcer* to ) { std::vector fromv; fromv.push_back( const_cast( from ) ); std::vector tov; tov.push_back( const_cast( to ) ); init( fromv, tov ); } bool ObjectHierarchy::allGivenObjectsUsed() const { std::vector usedstack( mnodes.size() + mnumberofargs, false ); for ( uint i = mnodes.size() - mnumberofresults; i < mnodes.size(); ++i ) usedstack[i + mnumberofargs] = true; for ( int i = mnodes.size() - 1; i >= 0; --i ) if ( usedstack[i + mnumberofargs] ) mnodes[i]->checkArgumentsUsed( usedstack ); for ( uint i = 0; i < mnumberofargs; ++i ) if ( ! usedstack[i] ) return false; return true; } diff --git a/misc/object_hierarchy.h b/misc/object_hierarchy.h index 7dcd51a7..45bdf6cf 100644 --- a/misc/object_hierarchy.h +++ b/misc/object_hierarchy.h @@ -1,112 +1,112 @@ // 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. #ifndef KIG_MISC_OBJECT_HIERARCHY_H #define KIG_MISC_OBJECT_HIERARCHY_H #include "../objects/common.h" #include #include #include class ObjectImpType; class ArgsParser; class ObjectHierarchy { public: class Node; private: std::vector mnodes; uint mnumberofargs; uint mnumberofresults; bool msaveinputtags; // if true the UseText and SelectStatement are serialized for saving std::vector margrequirements; std::vector musetexts; std::vector mselectstatements; // these two are really part of the constructor... int visit( const ObjectCalcer* o, std::map&, bool needed, bool neededatend = false); int storeObject( const ObjectCalcer*, const std::vector& po, std::vector& pl, std::map& seenmap ); friend bool operator==( const ObjectHierarchy& lhs, const ObjectHierarchy& rhs ); void init( const std::vector& from, const std::vector& to ); /** * this constructor is private since it should be used only by the static * constructor buildSafeObjectHierarchy * * \see ObjectHierarchy::buildSafeObjectHierarchy */ ObjectHierarchy(); public: ObjectHierarchy( const ObjectCalcer* from, const ObjectCalcer* to ); ObjectHierarchy( const std::vector& from, const ObjectCalcer* to ); ObjectHierarchy( const std::vector& from, const std::vector& to ); ObjectHierarchy( const ObjectHierarchy& h ); ~ObjectHierarchy(); /** * this creates a new ObjectHierarchy, that takes a.size() less * arguments, but uses copies of the ObjectImp's in \p a instead.. */ ObjectHierarchy withFixedArgs( const Args& a ) const; std::vector calc( const Args& a, const KigDocument& doc ) const; /** * saves the ObjectHierarchy data in children xml tags of \p parent .. */ void serialize( QDomElement& parent, QDomDocument& doc ) const; /** * Deserialize the ObjectHierarchy data from the xml element \p parent .. * Since this operation can fail for some reasons, we provide it as a * static to return 0 in case of error. */ static ObjectHierarchy* buildSafeObjectHierarchy( const QDomElement& parent, QString& error ); // ObjectHierarchy( const QDomElement& parent ); /** * build a set of objects that interdepend according to this * ObjectHierarchy.. Only the result objects are returned. Helper * objects that connect the given objects with the returned objects, * can only be found by following the returned objects' parents() * methods.. */ std::vector buildObjects( const std::vector& os, const KigDocument& ) const; ArgsParser argParser() const; uint numberOfArgs() const { return mnumberofargs; } uint numberOfResults() const { return mnumberofresults; } const ObjectImpType* idOfLastResult() const; - bool resultDoesNotDependOnGiven() const; + bool resultDependsOnGiven() const; bool allGivenObjectsUsed() const; ObjectHierarchy transformFinalObject( const Transformation& t ) const; }; bool operator==( const ObjectHierarchy& lhs, const ObjectHierarchy& rhs ); #endif diff --git a/modes/macro.cc b/modes/macro.cc index 0b17d4f7..a09d5eb6 100644 --- a/modes/macro.cc +++ b/modes/macro.cc @@ -1,238 +1,238 @@ // Copyright (C) 2002 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 "macro.h" #include "macrowizard.h" #include "dragrectmode.h" #include "../kig/kig_part.h" #include "../kig/kig_view.h" #include "../misc/kigpainter.h" #include "../misc/object_constructor.h" #include "../misc/lists.h" #include "../misc/guiaction.h" #include "../objects/object_imp.h" #include #include #include #include #include #include using namespace std; DefineMacroMode::DefineMacroMode( KigPart& d ) : BaseMode( d ) { mwizard = new MacroWizard( d.widget(), this ); mwizard->show(); } DefineMacroMode::~DefineMacroMode() { delete mwizard; } void DefineMacroMode::abandonMacro() { mdoc.doneMode( this ); } void DefineMacroMode::enableActions() { KigMode::enableActions(); // we don't enable any actions... } void DefineMacroMode::givenPageEntered() { std::vector given( mgiven.begin(), mgiven.end() ); static_cast( mdoc.widget() )->realWidget()->redrawScreen( given ); } void DefineMacroMode::finalPageEntered() { std::vector final( mfinal.begin(), mfinal.end() ); static_cast( mdoc.widget() )->realWidget()->redrawScreen( final ); } bool DefineMacroMode::validateObjects() { bool res = true; ObjectCalcer* (ObjectHolder::*memfun)() = &ObjectHolder::calcer; std::vector given; std::transform( mgiven.begin(), mgiven.end(), std::back_inserter( given ), std::mem_fun( memfun ) ); std::vector final; std::transform( mfinal.begin(), mfinal.end(), std::back_inserter( final ), std::mem_fun( memfun ) ); ObjectHierarchy hier( given, final ); - if ( hier.resultDoesNotDependOnGiven() ) + if ( !hier.resultDependsOnGiven() ) { KMessageBox::sorry( mwizard, i18n( "One of the result objects you selected " "cannot be calculated from the given objects. " "Kig cannot calculate this macro because of this. " "Please press Back, and construct the objects " "in the correct order..." ) ); res = false; } else if( !hier.allGivenObjectsUsed() ) { KMessageBox::sorry( mwizard, i18n( "One of the given objects is not used in the " "calculation of the resultant objects. This " "probably means you are expecting Kig to do " "something impossible. Please check the " "macro and try again." ) ); res = false; } static_cast( mdoc.widget() )->realWidget()->redrawScreen( std::vector() ); return res; } void DefineMacroMode::finishPressed() { ObjectCalcer* (ObjectHolder::*memfun)() = &ObjectHolder::calcer; std::vector given; std::transform( mgiven.begin(), mgiven.end(), std::back_inserter( given ), std::mem_fun( memfun ) ); std::vector final; std::transform( mfinal.begin(), mfinal.end(), std::back_inserter( final ), std::mem_fun( memfun ) ); ObjectHierarchy hier( given, final ); MacroConstructor* ctor = new MacroConstructor( hier, mwizard->field( "name" ).toString(), mwizard->field( "description" ).toString(), mwizard->field( "icon" ).toByteArray() ); ConstructibleAction* act = new ConstructibleAction( ctor, 0 ); MacroList::instance()->add( new Macro( act, ctor ) ); abandonMacro(); } void DefineMacroMode::cancelPressed() { abandonMacro(); } void DefineMacroMode::dragRect( const QPoint& p, KigWidget& w ) { if ( mwizard->currentId() == MacroWizard::MacroInfoPageId ) return; std::vector* objs = mwizard->currentId() == MacroWizard::GivenArgsPageId ? &mgiven : &mfinal; DragRectMode dm( p, mdoc, w ); mdoc.runMode( &dm ); KigPainter pter( w.screenInfo(), &w.stillPix, mdoc.document() ); if ( ! dm.cancelled() ) { std::vector ret = dm.ret(); if ( dm.needClear() ) { pter.drawObjects( objs->begin(), objs->end(), false ); objs->clear(); } std::copy( ret.begin(), ret.end(), std::back_inserter( *objs ) ); pter.drawObjects( objs->begin(), objs->end(), true ); }; w.updateCurPix( pter.overlay() ); w.updateWidget(); if ( mwizard->currentId() == MacroWizard::GivenArgsPageId ) mwizard->givenArgsChanged(); else mwizard->finalArgsChanged(); } void DefineMacroMode::leftClickedObject( ObjectHolder* o, const QPoint&, KigWidget& w, bool ) { if ( mwizard->currentId() == MacroWizard::MacroInfoPageId ) return; std::vector* objs = mwizard->currentId() == MacroWizard::GivenArgsPageId ? &mgiven : &mfinal; std::vector::iterator iter = std::find( objs->begin(), objs->end(), o ); bool isselected = ( iter != objs->end() ); if ( isselected ) objs->erase( iter ); else objs->push_back( o ); KigPainter p( w.screenInfo(), &w.stillPix, mdoc.document() ); p.drawObject( o, !isselected ); w.updateCurPix( p.overlay() ); w.updateWidget(); if ( mwizard->currentId() == MacroWizard::GivenArgsPageId ) mwizard->givenArgsChanged(); else mwizard->finalArgsChanged(); } void DefineMacroMode::mouseMoved( const std::vector& os, const QPoint& pt, KigWidget& w, bool ) { w.updateCurPix(); if ( os.empty() ) { w.setCursor( Qt::ArrowCursor ); mdoc.emitStatusBarText( 0 ); w.updateWidget(); } else { // the cursor is over an object, show object type next to cursor // and set statusbar text w.setCursor( Qt::PointingHandCursor ); QString selectstat = os.front()->selectStatement(); // statusbar text mdoc.emitStatusBarText( selectstat ); KigPainter p( w.screenInfo(), &w.curPix, mdoc.document() ); // set the text next to the arrow cursor QPoint point = pt; point.setX(point.x()+15); p.drawTextStd( point, selectstat ); w.updateWidget( p.overlay() ); } } void DefineMacroMode::rightClicked( const std::vector&, const QPoint&, KigWidget& ) { } void DefineMacroMode::midClicked( const QPoint&, KigWidget& ) { } bool DefineMacroMode::hasGivenArgs() const { return !mgiven.empty(); } bool DefineMacroMode::hasFinalArgs() const { return !mfinal.empty(); }