diff --git a/microbe/btreenode.h b/microbe/btreenode.h index d5c3862f..cc455711 100644 --- a/microbe/btreenode.h +++ b/microbe/btreenode.h @@ -1,107 +1,107 @@ /*************************************************************************** * Copyright (C) 2004-2005 by Daniel Clarke * * daniel.jc@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. * * * * 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., * * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * ***************************************************************************/ #ifndef BTREENODE_H #define BTREENODE_H #include "btreebase.h" #include "expression.h" -#include -#include +#include +#include /** A node points to the two child nodes (left and right), and contains the binary operation used to combine them. @author Daniel Clarke @author David Saxton */ class BTreeNode { public: BTreeNode(); BTreeNode(BTreeNode *p, BTreeNode *l, BTreeNode *r); ~BTreeNode(); /** * Used for debugging purposes; prints the tree structure to stdout. */ // void printTree(); /** * Recursively delete all children of a node. */ void deleteChildren(); /** * @return the parent node. */ BTreeNode *parent() const { return m_parent; } /** * @return the left child node. */ BTreeNode *left() const { return m_left; } /** * @return the right child node. */ BTreeNode *right() const { return m_right; } void setParent(BTreeNode *parent) { m_parent = parent; } /** * Set the child node on the left to the one give, and reparents it to * this node. */ void setLeft(BTreeNode *left) { m_left = left; m_left->setParent( this ); } /** * Set the child node on the right to the one give, and reparents it to * this node. */ void setRight(BTreeNode *right) { m_right = right; m_right->setParent( this ); } /** * @return true if have a left or a right child node. */ bool hasChildren() const { return m_left || m_right; } ExprType type() const {return m_type;} void setType(ExprType type) { m_type = type; } QString value() const {return m_value;} void setValue( const QString & value ) { m_value = value; } Expression::Operation childOp() const {return m_childOp;} void setChildOp(Expression::Operation op){ m_childOp = op;} void setReg( const QString & r ){ m_reg = r; } QString reg() const {return m_reg;} bool needsEvaluating() const { return hasChildren(); } protected: BTreeNode *m_parent; BTreeNode *m_left; BTreeNode *m_right; /** This is used to remember what working register contains the value of the node during assembly.*/ QString m_reg; ExprType m_type; QString m_value; Expression::Operation m_childOp; }; #endif diff --git a/microbe/expression.cpp b/microbe/expression.cpp index 6a63fb3b..dc371ed3 100644 --- a/microbe/expression.cpp +++ b/microbe/expression.cpp @@ -1,850 +1,850 @@ /*************************************************************************** * Copyright (C) 2004-2005 by Daniel Clarke * * daniel.jc@gmail.com * * * * 24-04-2007 * * Modified to add pic 16f877,16f627 and 16f628 * * by george john george@space-kerala.org,az.j.george@gmail.com * * supported by SPACE www.space-kerala.org * * * * 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., * * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * ***************************************************************************/ #include "btreebase.h" #include "btreenode.h" #include "expression.h" #include "traverser.h" #include "parser.h" #include "pic14.h" -#include -#include +#include +#include Expression::Expression( PIC14 *pic, Microbe *master, SourceLine sourceLine, bool suppressNumberTooBig ) : m_sourceLine(sourceLine) { m_pic = pic; mb = master; m_bSupressNumberTooBig = suppressNumberTooBig; } Expression::~Expression() { } void Expression::traverseTree( BTreeNode *root, bool conditionalRoot ) { Traverser t(root); t.start(); // special case: if we are starting at the root node then // we are dealing with something of the form variable = 6 // or variable = portb ///TODO reimplement assignments as two branched trees? if ( t.current() == root && !root->hasChildren() && t.current()->childOp() != pin && t.current()->childOp() != notpin && t.current()->childOp() != function && t.current()->childOp() != read_keypad ) { switch(root->type()) { case number: m_pic->assignNum(root->value()); break; case variable: m_pic->assignVar(root->value()); break; default: break; // Should never get here } // no need to traverse the tree as there is none. return; } t.setCurrent(root); if(t.current()->hasChildren()) { // Here we work out what needs evaulating, and in which order. // To minimize register usage, if only one branch needs traversing, // then that branch should be done first. bool evaluateLeft = t.current()->left()->needsEvaluating(); BTreeNode *evaluateFirst; BTreeNode *evaluateSecond; // If both need doing, then it really doesn't matter which we do // first (unless we are looking to do really complex optimizations... // Cases: // - Both need evaluating, // - or left needs doing first, // in both cases we evaluate left, then right. if( evaluateLeft ) { evaluateFirst = t.current()->left(); evaluateSecond = t.current()->right(); } // Otherwise it is best to evaluate right first for reasons given above. else { evaluateFirst = t.current()->right(); evaluateSecond = t.current()->left(); } QString dest1 = mb->dest(); mb->incDest(); QString dest2 = mb->dest(); mb->decDest(); bool evaluated = false; if( evaluateFirst->hasChildren() ) { traverseTree(evaluateFirst); evaluated = true; } else if( isUnaryOp(evaluateFirst->childOp()) ) { doUnaryOp( evaluateFirst->childOp(), evaluateFirst ); evaluated = true; } if ( evaluated ) { // We need to save the result if we are going tro traverse the other // branch, or if we are performing a subtraction in which case the // value wanted in working is not the current value. // But as the optimizer will deal with unnecessary variables anyway, // always save to a register evaluateFirst->setReg( dest1 ); evaluateFirst->setType( variable ); m_pic->saveToReg( dest1 ); } evaluated = false; if( evaluateSecond->hasChildren() ) { mb->incDest(); mb->incDest(); traverseTree(evaluateSecond); evaluated = true; mb->decDest(); mb->decDest(); } else if( isUnaryOp(evaluateSecond->childOp()) ) { doUnaryOp( evaluateSecond->childOp(), evaluateSecond ); evaluated = true; } if ( evaluated ) { evaluateSecond->setReg( dest2 ); evaluateSecond->setType( variable ); m_pic->saveToReg( dest2 ); } } if(t.current()->childOp()==divbyzero) { mistake( Microbe::DivisionByZero ); } // If we are at the top level of something like 'if a == 3 then', then we are ready to put // in the if code, else the expression just evaluates to 0 or 1 if(conditionalRoot && t.current() == root) m_pic->setConditionalCode(m_ifCode, m_elseCode); // Handle operations // (functions are not actually supported) if(isUnaryOp(t.current()->childOp())) doUnaryOp( t.current()->childOp(), t.current() ); else doOp( t.current()->childOp(), t.current()->left(), t.current()->right() ); } void Expression::doOp( Operation op, BTreeNode *left, BTreeNode *right ) { QString lvalue; if(left->reg().isEmpty()) lvalue = left->value(); else lvalue = left->reg(); QString rvalue; if(right->reg().isEmpty()) rvalue = right->value(); else rvalue = right->reg(); // Handle if stuff PIC14::LocationType leftType = PIC14::num; switch ( left->type() ) { case number: leftType = PIC14::num; break; case variable: leftType = PIC14::var; break; case working: leftType = PIC14::work; break; case unset: case extpin: case keypad: qCritical() << Q_FUNC_INFO << "Bad left->type(): " << left->type() << endl; }; PIC14::LocationType rightType = PIC14::work; switch ( right->type() ) { case number: rightType = PIC14::num; break; case variable: rightType = PIC14::var; break; case working: rightType = PIC14::work; break; case unset: case extpin: case keypad: qCritical() << Q_FUNC_INFO << "Bad right->type(): " << right->type() << endl; }; switch(op) { case equals: m_pic->equal( lvalue, rvalue, leftType, rightType ); break; case notequals: m_pic->notEqual( lvalue, rvalue, leftType, rightType ); break; case lt: m_pic->lessThan( lvalue, rvalue, leftType, rightType ); break; case gt: m_pic->greaterThan( lvalue, rvalue, leftType, rightType ); break; case le: m_pic->lessOrEqual( lvalue, rvalue, leftType, rightType ); break; case ge: m_pic->greaterOrEqual( lvalue, rvalue, leftType, rightType ); break; case addition: m_pic->add( lvalue, rvalue, leftType, rightType ); break; case subtraction: m_pic->subtract( lvalue, rvalue, leftType, rightType ); break; case multiplication: m_pic->mul( lvalue, rvalue, leftType, rightType ); break; case division: m_pic->div( lvalue, rvalue, leftType, rightType ); break; case bwand: m_pic->bitwise( bwand, lvalue, rvalue, leftType, rightType ); break; case bwor: m_pic->bitwise( bwor, lvalue, rvalue, leftType, rightType ); break; case bwxor: m_pic->bitwise( bwxor, lvalue, rvalue, leftType, rightType ); break; case bwnot: m_pic->bitwise( bwnot, lvalue, rvalue, leftType, rightType ); break; default: break; } } void Expression::buildTree( const QString & unstrippedExpression, BTreeBase *tree, BTreeNode *node, int level ) { int firstEnd = 0; int secondStart = 0; bool unary = false; Operation op = noop; QString expression = stripBrackets( unstrippedExpression ); switch(level) { // ==, != case 0: { int equpos = findSkipBrackets(expression, "=="); int neqpos = findSkipBrackets(expression, "!="); if( equpos != -1 ) { op = equals; firstEnd = equpos; secondStart = equpos + 2; } else if( neqpos != -1 ) { op = notequals; firstEnd = neqpos; secondStart = neqpos + 2; } else op = noop; break; } // <, <=, >=, > case 1: { int ltpos = findSkipBrackets(expression, "<"); int lepos = findSkipBrackets(expression, "<="); int gepos = findSkipBrackets(expression, ">="); int gtpos = findSkipBrackets(expression, ">"); // Note: if (for example) "<=" is present, "<" will also be present. This // means that we have to check for "<=" before "<", etc. if( lepos != -1 ) { op = le; firstEnd = lepos; secondStart = lepos + 2; } else if( gepos != -1 ) { op = ge; firstEnd = gepos; secondStart = gepos + 2; } else if( ltpos != -1 ) { op = lt; firstEnd = ltpos; secondStart = ltpos + 1; } else if( gtpos != -1 ) { op = gt; firstEnd = gtpos; secondStart = gtpos + 1; } else op = noop; break; } // +,- case 2: { int addpos = findSkipBrackets(expression, '+'); int subpos = findSkipBrackets(expression, '-'); if( subpos != -1 ) { op = subtraction; firstEnd = subpos; secondStart = subpos + 1; } else if( addpos != -1 ) { op = addition; firstEnd = addpos; secondStart = addpos + 1; } else op = noop; break; } // *,/ case 3: { int mulpos = findSkipBrackets(expression, '*'); int divpos = findSkipBrackets(expression, '/'); if( divpos != -1 ) { op = division; firstEnd = divpos; secondStart = divpos + 1; } else if( mulpos != -1 ) { op = multiplication; firstEnd = mulpos; secondStart = mulpos + 1; } else op = noop; break; } // ^ case 4: { int exppos = findSkipBrackets(expression, '^'); if( exppos != -1 ) { op = exponent; firstEnd = exppos; secondStart = exppos + 1; } else op = noop; break; } // AND, OR, XOR case 5: { int bwAndPos = findSkipBrackets(expression, " AND "); int bwOrPos = findSkipBrackets(expression, " OR "); int bwXorPos = findSkipBrackets(expression, " XOR "); if( bwAndPos != -1 ) { op = bwand; firstEnd = bwAndPos; secondStart = bwAndPos + 5; } else if( bwOrPos != -1 ) { op = bwor; firstEnd = bwOrPos; secondStart = bwOrPos + 4; } else if( bwXorPos != -1 ) { op = bwxor; firstEnd = bwXorPos; secondStart = bwXorPos + 5; } else op = noop; break; } // NOT case 6: { int bwNotPos = findSkipBrackets(expression, " NOT "); if( bwNotPos != -1 ) { op = bwnot; unary = true; firstEnd = bwNotPos; // this line is not needed for unary things/ secondStart = bwNotPos + 5; } else op = noop; break; } } node->setChildOp(op); QString tokens[2]; tokens[0] = expression.left(firstEnd).trimmed(); tokens[1] = expression.mid(secondStart).trimmed(); if( op != noop ) { for( int j = 0; j < 2; j++ ) { BTreeNode *newNode = new BTreeNode(); tree->addNode( node, newNode, (j == 0) ); // we need to strip any brackets from the sub-expression // try each token again at the same level, if they // don't have any of this level's operators, then the function // will go to the next level as below. // For unary opertaions, e.g NOT, we have no special // code for nodes with only one child, so we leave the left // hand child blank and put the rest in the right hand node. if( unary && j == 0 ) { newNode->setValue(""); newNode->setType(number); } else buildTree(tokens[j], tree, newNode, 0 ); } } else { // if there was no relevant operation i.e. " 3*4 / 6" as opposed to " 3*4 + 6" // then just pass the node onto the next parsing level. // unless we are at the lowest level, in which case we have reached a final value. if( level == 6 ) expressionValue(expression,tree,node); else { buildTree(expression,tree,node,level + 1); } } } void Expression::doUnaryOp(Operation op, BTreeNode *node) { /* Note that this isn't for unary operations as such, rather for things that are operations that have no direct children, e.g. portx.n is high, and functionname(args)*/ if ( op == pin || op == notpin ) m_pic->Spin( m_pic->toPortPin( node->value() ), (op==notpin) ); else if ( op == read_keypad ) m_pic->Skeypad( mb->variable( node->value() ) ); } void Expression::compileExpression( const QString & expression ) { // Make a tree to put the expression in. BTreeBase *tree = new BTreeBase(); BTreeNode *root = new BTreeNode(); // parse the expression into the tree buildTree(expression,tree,root,0); // compile the tree into assembly code tree->setRoot(root); tree->pruneTree(tree->root()); traverseTree(tree->root()); // Note deleting the tree deletes all nodes, so the root // doesn't need deleting separately. delete tree; return; } void Expression::compileConditional( const QString & expression, Code * ifCode, Code * elseCode ) { if( expression.contains(QRegExp("=>|=<|=!")) ) { mistake( Microbe::InvalidComparison, expression ); return; } if( expression.contains(QRegExp("[^=>childOp() != equals && root->childOp() != notequals && root->childOp() != gt && root->childOp() != lt && root->childOp() != ge && root->childOp() != le && root->childOp() != pin && root->childOp() != notpin && root->childOp() != read_keypad ) { BTreeNode *newRoot = new BTreeNode(); BTreeNode *oneNode = new BTreeNode(); oneNode->setChildOp(noop); oneNode->setType(number); oneNode->setValue("1"); newRoot->setLeft(root); newRoot->setRight(oneNode); newRoot->setType(unset); newRoot->setChildOp(ge); tree->setRoot(newRoot); root = newRoot; } // compile the tree into assembly code tree->setRoot(root); tree->pruneTree(tree->root(),true); // We might have just a constant expression, in which case we can just always do if or else depending // on whether it is true or false. if( root->childOp() == noop ) { if( root->value().toInt() == 0 ) m_pic->mergeCode( elseCode ); else m_pic->mergeCode( ifCode ); return; } // traverse tree with argument conditionalRoot true // so that 3 == x gets integrated with code for if, repeat until etc... m_ifCode = ifCode; m_elseCode = elseCode; traverseTree(tree->root(),true); // Note deleting the tree deletes all nodes, so the root // doesn't need deleting separately. delete tree; } bool Expression::isUnaryOp(Operation op) { return op == pin || op == notpin || op == function || op == read_keypad; } void Expression::mistake( Microbe::MistakeType type, const QString & context ) { mb->compileError( type, context, m_sourceLine ); } int Expression::findSkipBrackets( const QString & expr, char ch, int startPos) { bool found = false; int i = startPos; int bracketLevel = 0; while(!found) { if(expr[i].toLatin1() == '\'') { if( i + 2 < int(expr.length()) ) { if( expr[i+2].toLatin1() == '\'' ) { i = i + 2; found = true; } } } if(expr[i].toLatin1() == '(') bracketLevel++; else if(expr[i].toLatin1() == ')') bracketLevel--; if( bracketLevel == 0 ) { if(expr[i].toLatin1() == ch) found = true; else i++; } else i++; if( i >= int(expr.length()) ) { found = true; i = -1; } } return i; } int Expression::findSkipBrackets( const QString & expr, QString phrase, int startPos) { bool found = false; int i = startPos; int bracketLevel = 0; while(!found) { if(expr[i].toLatin1() == '\'') { if( i + 2 < int(expr.length()) ) { if( expr[i+2].toLatin1() == '\'' ) { i = i + 2; found = true; } } } if(expr[i].toLatin1() == '(') bracketLevel++; else if(expr[i].toLatin1() == ')') bracketLevel--; if( bracketLevel == 0 ) { if(expr.mid(i,phrase.length()) == phrase) found = true; else i++; } else i++; if( i >= int(expr.length()) ) { found = true; i = -1; } } return i; } QString Expression::stripBrackets( QString expression ) { bool stripping = true; int bracketLevel = 0; int i = 0; expression = expression.trimmed(); while(stripping) { if( expression.at(i) == '(' ) bracketLevel++; else if( expression.at(i) == ')' ) { if( i == int(expression.length() - 1) && bracketLevel == 1) { expression = expression.mid(1,expression.length() - 2).trimmed(); } bracketLevel--; } if( i == int(expression.length() - 1) && bracketLevel > 0 ) { mistake( Microbe::MismatchedBrackets, expression ); // Stray brackets might cause the expressionession parser some problems, // so we just avoid parsing anything altogether expression = ""; stripping = false; } i++; if( bracketLevel == 0 ) stripping = false; } return expression; } void Expression::expressionValue( QString expr, BTreeBase */*tree*/, BTreeNode *node) { /* The "end of the line" for the expression parsing, the expression has been broken down into the fundamental elements of expr.value()=="to"|| variable, number, special etc... so we now just set value and type */ /* Alternatively we might have a function call e.g. somefunction(3,potatoes,hairstyle + 6) In which case we need to call back to parseExpr to process the arguments, saving them on the basic stack then making the function call. Of course we also need to mark the terminal node type as a function. */ expr = expr.trimmed(); // My intention is so that these error checks are ordered // so that e.g. for x = 3 it picks up the = rather than the spaces first. expr = mb->alias(expr); ExprType t = expressionType(expr); // See if it is a single qouted character, e.g. 'A' if( expr.left(1) == "\'" && expr.right(1) == "\'" ) { if( expr.length() == 3 ) // fall through to report as unknown variable if not of form 'x' { // If so, turn it into a number, and use the ASCII code as the value t = number; expr = QString::number(expr[1].toLatin1()); } } // Check for the most common mistake ever! if(expr.contains("=")) mistake( Microbe::InvalidEquals ); // Check for reserved keywords if(expr=="to"||expr=="step"||expr=="then") mistake( Microbe::ReservedKeyword, expr ); // Check for empty expressions, or expressions contating spaces // both indicating a Mistake. if(expr.isEmpty()) mistake( Microbe::ConsecutiveOperators ); else if(expr.contains(QRegExp("\\s")) && t!= extpin) mistake( Microbe::MissingOperator ); //***************modified isValidRegister is included ***********************// if( t == variable && !mb->isVariableKnown(expr) && !m_pic->isValidPort( expr ) && !m_pic->isValidTris( expr )&&!m_pic->isValidRegister( expr ) ) mistake( Microbe::UnknownVariable, expr ); //modification ends if ( mb->isVariableKnown(expr) && !mb->variable(expr).isReadable() ) mistake( Microbe::WriteOnlyVariable, expr ); node->setType(t); // Since we currently only implement 8 bit unsigned integers, we should disallow // anything outside the range [0-255]. if( t == number && !m_bSupressNumberTooBig && (expr.toInt() > 255) ) { mistake( Microbe::NumberTooBig ); } // if there was a pin, we need to decocde it. // For now and sacrificing syntax error checking // we just look for the word "is" then "high" or "low". if( t == extpin ) { bool NOT; int i = expr.indexOf("is"); if(i > 0) { NOT = expr.contains("low"); if(!expr.contains("high") && !expr.contains("low")) mistake( Microbe::HighLowExpected, expr ); expr = expr.left(i-1); } else NOT = false; node->setChildOp(NOT?notpin:pin); } else if ( t == keypad ) node->setChildOp( read_keypad ); node->setValue(expr); } ExprType Expression::expressionType( const QString & expression ) { // So we can't handle complex expressions yet anyway, // let's just decide whether it is a variable or number. // Thanks to the convention that variable names must not // begin with a number this is extremely simple to do! /* But now there is a catch, because there can also be things that have a first character alpha, but are of the form "portb.3 is high", general syntax: portx.n is additionally, there can be things that are just porta.6, which just return the truth of that port. In reality it is just: portx.n is high === portx.n portx.n is low === !(portx.n) These types of expression can be identified by the fact that they should be the only things that contain a '.' */ /* Note that at the moment, literalToInt returns -1 if it is not literal so isLiteral is redundant, but this may change if say negative numbers are implemented */ int value = Parser::literalToInt(expression); if ( value != -1 ) return number; if( expression.contains('.') ) return extpin; if ( mb->variable( expression ).type() == Variable::keypadType ) return keypad; return variable; } QString Expression::processConstant( const QString & expr, bool * isConstant ) { bool temp; if (!isConstant) isConstant = &temp; QString code; // Make a tree to put the expression in. BTreeBase *tree = new BTreeBase(); BTreeNode *root = new BTreeNode(); // parse the expression into the tree buildTree(expr,tree,root,0); // compile the tree into assembly code tree->setRoot(root); tree->pruneTree(tree->root()); //code = traverseTree(tree->root()); // Look to see if it is a number if( root->type() == number ) { code = root->value(); *isConstant = true; } else { code = ""; *isConstant = false; } // Note deleting the tree deletes all nodes, so the root // doesn't need deleting separately. delete tree; return code; } diff --git a/microbe/expression.h b/microbe/expression.h index a2ea2982..58d0fdc0 100644 --- a/microbe/expression.h +++ b/microbe/expression.h @@ -1,131 +1,131 @@ /*************************************************************************** * Copyright (C) 2004-2005 by Daniel Clarke * * daniel.jc@gmail.com * * * * 24-04-2007 * * Modified to add pic 16f877,16f627 and 16f628 * * by george john george@space-kerala.org,az.j.george@gmail.com * * supported by SPACE www.space-kerala.org * * * * 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., * * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * ***************************************************************************/ #ifndef EXPRESSION_H #define EXPRESSION_H #include "microbe.h" -#include +#include class PIC14; class BTreeNode; class Microbe; /** @author Daniel Clarke @author David Saxton */ class Expression { public: enum Operation { noop, addition, subtraction, multiplication, division, exponent, equals, notequals, pin,//(returns the truth value obtatined by testing the pin) notpin, //(the result of doing the pin op NOTted).] read_keypad, //read value from keypad function, bwand, bwor, bwxor, bwnot, divbyzero, // used to make handling this situation easier gt, lt, ge, le }; Expression(PIC14 *pic, Microbe *master, SourceLine sourceLine, bool supressNumberTooBig ); ~Expression(); /** * Generates the code needed to evaluate an expression. Firstly, a tree * is generated from the expression string; then that tree is traversed * to generate the assembly. */ void compileExpression( const QString & expression); void compileConditional( const QString & expression, Code * ifCode, Code * elseCode ); /** * Returns a *number* rather than evaluating code, and sets isConstant to true * if it the expression evaluated to a constant. */ QString processConstant( const QString & expr, bool * isConsant ); private: PIC14 *m_pic; Microbe *mb; /** Turns the operations encoded in the given tree into assembly code */ void traverseTree( BTreeNode *root, bool conditionalRoot = false ); bool isUnaryOp(Operation op); void expressionValue( QString expression, BTreeBase *tree, BTreeNode *node ); void doOp( Operation op, BTreeNode *left, BTreeNode *right ); void doUnaryOp( Operation op, BTreeNode *node ); /** * Parses an expression, and generates a tree structure from it. */ void buildTree( const QString & expression, BTreeBase *tree, BTreeNode *node, int level ); static int findSkipBrackets( const QString & expr, char ch, int startPos = 0); static int findSkipBrackets( const QString & expr, QString phrase, int startPos = 0); QString stripBrackets( QString expression ); void mistake( Microbe::MistakeType type, const QString & context = nullptr ); SourceLine m_sourceLine; Code * m_ifCode; Code * m_elseCode; /** *Returns expression type * 0 = directly usable number (literal) * 1 = variable * 2 = expression that needs evaluating * (maybe not, see enum). */ ExprType expressionType( const QString & expression ); static bool isLiteral( const QString &text ); /** * Normally, only allow numbers upto 255; but for some uses where the * number is not going to be placed in a PIC register (such as when * delaying), we can ignore numbers being too big. */ bool m_bSupressNumberTooBig; }; #endif diff --git a/microbe/instruction.cpp b/microbe/instruction.cpp index 1984e81e..0ee3f7c9 100644 --- a/microbe/instruction.cpp +++ b/microbe/instruction.cpp @@ -1,3616 +1,3616 @@ /*************************************************************************** * Copyright (C) 2004-2005 by Daniel Clarke * * 2005 by David Saxton * * * * 24-04-2007 * * Modified to add pic 16f877,16f627 and 16f628 * * by george john george@space-kerala.org * * supported by SPACE www.space-kerala.org * * * * 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., * * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * ***************************************************************************/ #include "instruction.h" #include "optimizer.h" #include "pic14.h" -#include -#include +#include +#include #include #include using namespace std; //modified new varable pic_type is added extern QString pic_type; //BEGIN class Register Register::Register( Type type ) { m_type = type; //***********modified almost all the register names are included**************// switch ( m_type ) { //----------------------------------------------Bank0---------------------------// case TMR0: m_name = "TMR0"; break; case PCL: m_name = "PCL"; break; case STATUS: m_name = "STATUS"; break; case FSR: m_name = "FSR"; break; case PORTA: m_name = "PORTA"; break; case PORTB: m_name = "PORTB"; break; case PORTC: m_name = "PORTC"; break; case PORTD: m_name = "PORTD"; break; case PORTE: m_name = "PORTE"; break; case PCLATH: m_name = "PCLATH"; break; case INTCON: m_name = "INTCON"; break; case PIR1: m_name = "PIR1"; break; case PIR2: m_name = "PIR2"; break; case TMR1L: m_name = "TMR1L"; break; case TMR1H: m_name = "TMR1H"; break; case T1CON: m_name = "T1CON"; break; case TMR2: m_name = "TMR2"; break; case T2CON: m_name = "T2CON"; break; case SSPBUF: m_name = "SSPBUF"; break; case SSPCON: m_name = "SSPCON"; break; case CCPR1L: m_name = "CCPR1L"; break; case CCPR1H: m_name = "CCPR1H"; break; case CCP1CON: m_name = "CCP1CON"; break; case RCSTA: m_name = "RCSTA"; break; case TXREG: m_name = "TXREG"; break; case RCREG: m_name = "RCREG"; break; case CCPR2L: m_name = "CCPR2L"; break; case CCPR2H: m_name = "CCPR2H"; break; case CCP2CON: m_name = "CCP2CON"; break; case ADRESH: m_name = "ADRESH"; break; case ADCON0: m_name = "ADCON0"; break; case CMCON: m_name = "CMCON"; break; //----------------------------------------------Bank1---------------------------// case OPTION_REG: m_name = "OPTION_REG"; break; case TRISA: m_name = "TRISA"; break; case TRISB: m_name = "TRISB"; break; case TRISC: m_name = "TRISC"; break; case TRISD: m_name = "TRISD"; break; case TRISE: m_name = "TRISE"; break; case PIE1: m_name = "PIE1"; break; case PIE2: m_name = "PIE2"; break; case PCON: m_name = "PCON"; break; case SSPCON2: m_name = "SSPCON2"; break; case PR2: m_name = "PR2"; break; case SSPADD: m_name = "SSPADD"; break; case SSPSTAT: m_name = "SSPSTAT"; break; case TXSTA: m_name = "TXSTA"; break; case SPBRG: m_name = "SPBRG"; break; case ADRESL: m_name = "ADRESL"; break; case ADCON1: m_name = "ADCON1"; break; case VRCON: m_name = "VRCON"; break; //----------------------------------------------Bank2---------------------------// case EEDATA: m_name = "EEDATA"; break; case EEADR: m_name = "EEADR"; break; case EEDATH: m_name = "EEDATH"; break; case EEADRH: m_name = "EEADRH"; break; //----------------------------------------------Bank3---------------------------// case EECON1: m_name = "EECON1"; break; case EECON2: m_name = "EECON2"; break; //---------------------------------------------NoBank---------------------------// case WORKING: m_name = ""; break; case GPR: case none: break; } } Register::Register( const QString & name )//--to find a name varable or register(ex trise) { m_name = name.trimmed(); QString upper = m_name.toUpper(); //--------------------------------------------Bank0-------------------// if ( upper == "TMR0" ) m_type = TMR0; else if ( upper == "PCL" ) m_type = PCL; else if ( upper == "STATUS") m_type = STATUS; else if ( upper == "FSR") m_type = FSR; else if ( upper == "PORTA") m_type = PORTA; else if ( upper == "PORTB") m_type = PORTB; else if ( upper == "PORTC") m_type = PORTC; else if ( upper == "PORTD") m_type = PORTD; else if ( upper == "PORTE") m_type = PORTE; else if ( upper == "PCLATH") m_type = PCLATH; else if ( upper == "INTCON") m_type = INTCON; else if ( upper == "PIR1") m_type = PIR1; else if ( upper == "PIR2") m_type = PIR2; else if ( upper == "TMR1L") m_type = TMR1L; else if ( upper == "TMR1H") m_type = TMR1H; else if ( upper == "T1CON") m_type = T1CON; else if ( upper == "TMR2") m_type = TMR2; else if ( upper == "T2CON") m_type = T2CON; else if ( upper == "SSPBUF") m_type = SSPBUF; else if ( upper == "SSPCON") m_type = SSPCON; else if ( upper == "CCPR1L") m_type = CCPR1L; else if ( upper == "CCPR1H") m_type = CCPR1H; else if ( upper == "CCP1CON") m_type = CCP1CON; else if ( upper == "RCSTA") m_type = RCSTA; else if ( upper == "TXREG") m_type = TXREG; else if ( upper == "RCREG") m_type = RCREG; else if ( upper == "CCPR2L") m_type = CCPR2L; else if ( upper == "CCPR2H") m_type = CCPR2H; else if ( upper == "CCP2CON") m_type = CCP2CON; else if ( upper == "ADRESH") m_type = ADRESH; else if ( upper == "ADCON0") m_type = ADCON0; else if ( upper == "CMCON") m_type = CMCON; //--------------------------------------------Bank1-------------------// else if ( upper == "OPTION_REG" ) m_type = OPTION_REG; else if ( upper == "TRISA") m_type = TRISA; else if ( upper == "TRISB") m_type = TRISB; else if ( upper == "TRISC") m_type = TRISC; else if ( upper == "TRISD") m_type = TRISD; else if ( upper == "TRISE") m_type = TRISE; else if ( upper == "PIE1") m_type = PIE1; else if ( upper == "PIE2") m_type = PIE2; else if ( upper == "PCON") m_type = PCON; else if ( upper == "SSPCON2") m_type = SSPCON2; else if ( upper == "PR2") m_type = PR2; else if ( upper == "SSPADD") m_type = SSPADD; else if ( upper == "SSPSTAT") m_type = SSPSTAT; else if ( upper == "TXSTA") m_type = TXSTA; else if ( upper == "SPBRG") m_type = SPBRG; else if ( upper == "ADRESL") m_type = ADRESL; else if ( upper == "ADCON1") m_type = ADCON1; else if ( upper == "VRCON") m_type = VRCON; //--------------------------------------------Bank2-------------------// else if ( upper == "EEDATA") m_type = EEDATA; else if ( upper == "EEADR") m_type = EEADR; else if ( upper == "EEDATH") m_type = EEDATH; else if ( upper == "EEADRH") m_type = EEADRH; //--------------------------------------------Bank3-------------------// else if ( upper == "EECON1") m_type = EECON1; else if ( upper == "EECON2") m_type = EECON2; //---------------------------------------------NoBank----------------// else m_type = GPR; } Register::Register( const char * name ) { *this = Register( QString(name) ); } bool Register::operator < ( const Register & reg ) const { if ( (type() != GPR) || (reg.type() != GPR) ) return type() < reg.type(); return name() < reg.name(); } bool Register::operator == ( const Register & reg ) const { if ( type() != reg.type() ) return false; return name() == reg.name(); } uchar Register::banks() const { switch ( m_type ) { //---------------------bank 0 registers return zero---------------------------// case TMR0: return Bank0& Bank1;//Bank0 case PCL: return Bank0 & Bank1;//Bank0 | Bank1 case STATUS: return Bank0 & Bank1;//Bank0 | Bank1 case FSR: return Bank0 & Bank1;//Bank0 | Bank1 case PORTA: return Bank0 & Bank1;//Bank0 case PORTB: return Bank0 & Bank1;//Bank0 case PORTC: return Bank0 & Bank1;//Bank0 case PORTD: return Bank0 & Bank1;//Bank0 case PORTE: return Bank0 & Bank1;//Bank0 case PCLATH: return Bank0 & Bank1;//Bank0 | Bank1 case INTCON: return Bank0 & Bank1;//Bank0 | Bank1 case PIR1: return Bank0 & Bank1; case PIR2: return Bank0 & Bank1; case TMR1L: return Bank0 & Bank1; case TMR1H: return Bank0 & Bank1; case T1CON: return Bank0 & Bank1; case TMR2: return Bank0 & Bank1; case T2CON: return Bank0 & Bank1; case SSPBUF: return Bank0 & Bank1; case SSPCON: return Bank0 & Bank1; case CCPR1L: return Bank0 & Bank1; case CCPR1H: return Bank0 & Bank1; case CCP1CON: return Bank0 & Bank1; case RCSTA: return Bank0 & Bank1; case TXREG: return Bank0 & Bank1; case RCREG: return Bank0 & Bank1; case CCPR2L: return Bank0 & Bank1; case CCPR2H: return Bank0 & Bank1; case CCP2CON: return Bank0 & Bank1; case ADRESH: return Bank0 & Bank1;//Bank0 case ADCON0: return Bank0 & Bank1;//Bank0 case CMCON: return Bank0 & Bank1;//Bank0 //-----------------------------NO Bank-------------------------------------// case GPR: return Bank0 & Bank1;//Bank0 | Bank1 case WORKING: return Bank0 & Bank1;//Bank0 | Bank1 case none: return Bank0 & Bank1;//Bank0 | Bank1 //-------------------bank 1 registers return one---------------------------// case OPTION_REG: return Bank0;//Bank1 ///------tris registers-------// case TRISA: return Bank0;//Bank1 case TRISB: return Bank0;//Bank1 case TRISC: return Bank0;//Bank1 case TRISD: return Bank0;//Bank1 case TRISE: return Bank0;//Bank1 //--------------------------------------------------------------------------// case PIE1: return Bank0; case PIE2: return Bank0; case PCON: return Bank0; case SSPCON2: return Bank0; case PR2: return Bank0; case SSPADD: return Bank0; case SSPSTAT: return Bank0; case TXSTA: return Bank0; case SPBRG: return Bank0; //--------adc register-------// case ADRESL: return Bank0;//Bank1 case ADCON1: return Bank0;//Bank1 case VRCON: return Bank0;//Bank1 //------------------bank 2 registers return two----------completed------------// case EEDATA: return Bank1;//Bank1 case EEADR: return Bank1;//Bank0 case EEDATH: return Bank1;//Bank0 case EEADRH: return Bank1;//Bank0 //------------------bank 3 registers return three--------completed----------------// case EECON1: return Bank0|Bank1;//Bank1 case EECON2: return Bank0|Bank1;//Bank1 } return Bank0 & Bank1; // Vacously true (and useful too) - a non-existent bank can be accessed anywhere } bool Register::bankDependent() const { return ( banks() != (Bank0 | Bank1) ); } bool Register::affectsExternal() const { switch ( m_type ) { case PORTA: case TRISA: case PORTB: case TRISB: case PORTC: case TRISC: case PORTD: case TRISD: case PORTE: case TRISE: case INTCON: case ADCON0: case ADCON1: //************************modification*************************** case TMR0: case OPTION_REG: case PCL: case STATUS: case FSR: case EEDATA: case EECON1: case EEADR: case EECON2: case PCLATH: case GPR: //--------------------------------FINAL LAST-------------// case PIR1: case PIR2: case TMR1L: case TMR2: case TMR1H: case T1CON: case T2CON: case SSPBUF: case SSPCON: case CCPR1L: case CCPR1H: case CCP1CON: case CCPR2L: case CCPR2H: case CCP2CON: case ADRESH: case PIE1: case PIE2: case PCON: case SSPCON2: case PR2: case SSPADD: case SSPSTAT: case TXSTA: case SPBRG: case ADRESL: case EEDATH: case EEADRH: case RCSTA: case TXREG: case RCREG: case CMCON: case VRCON: return true; case WORKING: case none: return false; } return false; } //BEGIN class RegisterBit RegisterBit::RegisterBit( uchar bitPos, Register::Type reg ) { m_bitPos = bitPos; m_registerType = reg; switch ( m_registerType ) { case Register::TMR0: case Register::PCL: break; case Register::STATUS: { switch ( m_bitPos ) { case 0: m_name = "C"; break; case 1: m_name = "DC"; break; case 2: m_name = "Z"; break; case 3: m_name = "NOT_PD"; break; case 4: m_name = "NOT_TO"; break; case 5: m_name = "RP0"; break; case 6: m_name = "RP1"; break; case 7: m_name = "IRP"; break; } break; } case Register::FSR: case Register::PORTA: case Register::PORTB: case Register::PORTC: case Register::PORTD: case Register::PORTE: case Register::PCLATH: break; case Register::INTCON: { switch ( m_bitPos ) { case 0: m_name = "RBIF"; break; case 1: m_name = "INTF"; break; case 2: m_name = "T0IF"; break; case 3: m_name = "RBIE"; break; case 4: m_name = "INTE"; break; case 5: m_name = "T0IE"; break; case 6: { if(pic_type=="P16F84"||pic_type=="P16C84") { m_name = "EEIE"; break; } if(pic_type=="P16F877"||pic_type=="P16F627" ||pic_type =="P16F628") { m_name = "PEIE"; break; } break; } case 7: m_name = "GIE"; break; } break; } case Register::PIR1: { switch ( m_bitPos ) { case 0: m_name = "TMR1F"; break; case 1: m_name = "TMR2F"; break; case 2: m_name = "CCP1IF"; break; case 3: m_name = "SSPIF"; break; case 4: m_name = "TXIF"; break; case 5: m_name = "RCIF"; break; case 6: if(pic_type=="P16F877") { m_name = "ADIF"; break; } if(pic_type=="P16F627"||pic_type=="P16F628") { m_name = "CMIF";break; } break; case 7: if(pic_type=="P16F877") { m_name = "PSPIF"; break; } if(pic_type=="P16F627"||pic_type=="P16F628") { m_name = "EEIF";break; } break; } break; } case Register::PIR2: { switch ( m_bitPos ) { case 0: m_name = "CCP2IF"; break; case 3: m_name = "BCLIF"; break; case 4: m_name = "EEIF"; break; } break; } case Register::TMR1L: case Register::TMR1H: break; case Register::T1CON: { switch ( m_bitPos ) { case 0: m_name = "TMR1ON"; break; case 1: m_name = "TMRCS"; break; case 2: if(pic_type=="P16F877") { m_name = "T1SYNC"; break; } if(pic_type=="P16F627"||pic_type=="P16F628") { m_name = "NOT_T1SYNC"; break; } break; case 3: m_name = "T1OSCEN"; break; case 4: m_name = "T1CKPS0"; break; case 5: m_name = "T1CKPS1"; break; } break; } case Register::TMR2: break; case Register::T2CON: { switch ( m_bitPos ) { case 0: m_name = "T2CKPS0"; break; case 1: m_name = "T2CKPS1"; break; case 2: m_name = "TMR2ON"; break; case 3: m_name = "TOUTPS0"; break; case 4: m_name = "TOUTPS1"; break; case 5: m_name = "TOUTPS2"; break; case 6: m_name = "TOUTPS3"; break; } break; } case Register::SSPBUF: break; case Register::SSPCON: switch ( m_bitPos ) { case 0: m_name = "SSPM0"; break; case 1: m_name = "SSPM1"; break; case 2: m_name = "SSPM2"; break; case 3: m_name = "SSPM3"; break; case 4: m_name = "CKP"; break; case 5: m_name = "SSPEN"; break; case 6: m_name = "SSPOV"; break;//!!!!!!START&STOPEEIE!!! case 7: m_name = "WCOL"; break; } break; case Register::CCPR1L: case Register::CCPR1H: break; case Register::CCP1CON: switch ( m_bitPos ) { case 0: m_name = "CCP1M0"; break; case 1: m_name = "CCP1M1"; break; case 2: m_name = "CCP1M2"; break; case 3: m_name = "CCP1M3"; break; case 4: m_name = "CCP1Y"; break; case 5: m_name = "CCP1X"; break; } break; case Register::RCSTA: switch ( m_bitPos ) { case 0: m_name = "RX9D"; break; case 1: m_name = "OERR"; break; case 2: m_name = "FERR"; break; case 3: if(pic_type=="P16F877") { m_name = "ADDEN"; break; } if(pic_type=="P16F627"||pic_type=="P16F628") { m_name = "ADEN"; break; } break; case 4: m_name = "CREN"; break; case 5: m_name = "SREN"; break; case 6: m_name = "RX9"; break; case 7: m_name = "SPEN"; break; } break; case Register::TXREG: case Register::RCREG: case Register::CCPR2L: case Register::CCPR2H: break; case Register::CCP2CON: switch ( m_bitPos ) { case 0: m_name = "CCP2M0"; break; case 1: m_name = "CCP2M1"; break; case 2: m_name = "CCP2M2"; break; case 3: m_name = "CCP2M3"; break; case 4: m_name = "CCP2Y"; break; case 5: m_name = "CCP2X"; break; } break; case Register::ADRESH: break; case Register::ADCON0: { switch ( m_bitPos ) { case 0: m_name = "ADON"; break; case 2: m_name = "GO"; break; case 3: m_name = "CHS0"; break; case 4: m_name = "CHS1"; break; case 5: m_name = "CHS2"; break; case 6: m_name = "ADCS0"; break; case 7: m_name = "ADCS1"; break; } break; } case Register::CMCON: { switch ( m_bitPos ) { case 0: m_name = "CM0"; break; case 1: m_name = "CM1"; break; case 2: m_name = "CM2"; break; case 3: m_name = "CIS"; break; case 4: m_name = "C1INV"; break; case 5: m_name = "C2INV"; break; case 6: m_name = "C1OUT"; break; case 7: m_name = "C2OUT"; break; } break; } //-----------------------------------------------------Bank1----------------// case Register::OPTION_REG: { switch ( m_bitPos ) { case 0: m_name = "PS0"; break; case 1: m_name = "PS1"; break; case 2: m_name = "PS2"; break; case 3: m_name = "PSA"; break; case 4: m_name = "T0SE"; break; case 5: m_name = "T0CS"; break; case 6: m_name = "INTEDG"; break; case 7: { if(pic_type=="P16F84") m_name = "RBPU"; if(pic_type=="P16F877"||pic_type=="P16C84"||pic_type=="P16F627"||pic_type=="P16F628") m_name = "NOT_RBPU"; break; } } break; } case Register::TRISA: case Register::TRISB: case Register::TRISC: case Register::TRISD: case Register::TRISE: break; case Register::PIE1: switch ( m_bitPos ) { case 0: m_name = "TMR1IE"; break; case 1: m_name = "TMR2IE"; break; case 2: m_name = "CCP1IE"; break; case 3: m_name = "SSPIE"; break; case 4: m_name = "TXIE"; break; case 5: m_name = "RCIE"; break; case 6: { if (pic_type=="P16F877") { m_name = "ADIE"; break; } if (pic_type=="P16F627"||pic_type=="P16F628") { m_name = "CMIE"; break; } break; } case 7: { if (pic_type=="P16F877") { m_name = "PSPIE"; break; } if (pic_type=="P16F627"||pic_type=="P16F628") { m_name = "EEIE"; break; } break; } } break; case Register::PIE2: switch ( m_bitPos ) { case 0: m_name = "CCP2IE"; break; case 3: m_name = "BCLIE"; break; case 4: m_name = "EEIE"; break; } break; case Register::PCON: switch ( m_bitPos ) { case 0: m_name = "NOT_BOR"; break; case 1: m_name = "NOT_POR"; break; case 3: m_name = "OSCF"; break; } break; case Register::SSPCON2: switch ( m_bitPos ) { case 0: m_name = "SEN"; break; case 1: m_name = "RSEN"; break; case 2: m_name = "PEN"; break; case 3: m_name = "RCEN"; break; case 4: m_name = "ACKEN"; break; case 5: m_name = "ACKDT"; break; case 6: m_name = "ACKSTAT"; break; case 7: m_name = "GCEN"; break; } break; case Register::PR2: case Register::SSPADD: break; case Register::SSPSTAT: switch ( m_bitPos ) { case 0: m_name = "BF"; break; case 1: m_name = "UA"; break; case 2: m_name = "R"; break; case 3: m_name = "S"; break; case 4: m_name = "P"; break; case 5: m_name = "D"; break; case 6: m_name = "CKE"; break; case 7: m_name = "SMP"; break; } break; case Register::TXSTA: switch ( m_bitPos ) { case 0: m_name = "TX9D"; break; case 1: m_name = "TRMT"; break; case 2: m_name = "BRGH"; break; case 4: m_name = "SYNC"; break; case 5: m_name = "TXEN"; break; case 6: m_name = "TX9"; break; case 7: m_name = "CSRC"; break; } break; case Register::SPBRG: case Register::ADRESL: break; case Register::ADCON1: { switch ( m_bitPos ) { case 0: m_name = "PCFG0"; break; case 1: m_name = "PCFG1"; break; case 2: m_name = "PCFG2"; break; case 3: m_name = "PCFG3"; break; case 7: m_name = "ADFM"; break; } break; } //-----------------------------------------------------Bank2----------------// case Register::EEDATA: case Register::EEADR: case Register::EEDATH: case Register::EEADRH: break; //-----------------------------------------------------Bank3----------------// case Register::EECON1: { switch ( m_bitPos ) { case 0: m_name = "RD"; break; case 1: m_name = "WR"; break; case 2: m_name = "WREN"; break; case 3: m_name = "WRERR"; break; case 4: m_name = "EEIF"; break; case 7: m_name = "EEPGD"; break;//imp ***** } break; } case Register::EECON2: break; case Register::VRCON: { switch ( m_bitPos ) { case 0: m_name = "VR0"; break; case 1: m_name = "VR1"; break; case 2: m_name = "VR2"; break; case 3: m_name = "VR3"; break; case 5: m_name = "VRR"; break; case 6: m_name = "VROE"; break; case 7: m_name = "VREN"; break; } break; } case Register::GPR: case Register::WORKING: case Register::none: { // qCritical() << Q_FUNC_INFO << "Bad register: " << reg << endl; } } } RegisterBit::RegisterBit( const QString & name ) { m_name = name.toUpper().trimmed(); initFromName(); } RegisterBit::RegisterBit( const char * name ) { m_name = QString(name).toUpper().trimmed(); initFromName(); } void RegisterBit::initFromName() { bool ok; m_bitPos = m_name.toInt( & ok, 0 ); if ( ok ) m_registerType = Register::none; // hmm it should be unknown - not none. //----------------------------------------Bank0----------------------------// //--------STATUS REGISTER--------// else if ( m_name == "C" ) { m_registerType = Register::STATUS; m_bitPos = 0; } else if ( m_name == "DC" ) { m_registerType = Register::STATUS; m_bitPos = 1; } else if ( m_name == "Z" ) { m_registerType = Register::STATUS; m_bitPos = 2; } else if ( m_name == "NOT_PD" ) { m_registerType = Register::STATUS; m_bitPos = 3; } else if ( m_name == "NOT_TO" ) { m_registerType = Register::STATUS; m_bitPos = 4; } else if ( m_name == "RP0" ) { m_registerType = Register::STATUS; m_bitPos = 5; } else if ( m_name == "RP1" ) { m_registerType = Register::STATUS; m_bitPos = 6; } else if ( m_name == "IRP" ) { m_registerType = Register::STATUS; m_bitPos = 7; } //-----------INTCON REGISTER---------// else if ( m_name == "RBIF" ) { m_registerType = Register::INTCON; m_bitPos = 0; } else if ( m_name == "INTF" ) { m_registerType = Register::INTCON; m_bitPos = 1; } else if ( m_name == "T0IF" ) { m_registerType = Register::INTCON; m_bitPos = 2; } else if ( m_name == "RBIE" ) { m_registerType = Register::INTCON; m_bitPos = 3; } else if ( m_name == "INTE" ) { m_registerType = Register::INTCON; m_bitPos = 4; } else if ( m_name == "T0IE" ) { m_registerType = Register::INTCON; m_bitPos = 5; } else if ( m_name =="PEIE"&&(pic_type=="P16F877"||pic_type=="P16F627")) { m_registerType = Register::INTCON; m_bitPos = 6; } else if (m_name == "EEIE"&& (pic_type=="P16F84"||pic_type=="P16C84")) { m_registerType = Register::INTCON; m_bitPos = 6; } else if ( m_name == "GIE" ) { m_registerType = Register::INTCON; m_bitPos = 7; } //-------PIR1---------// else if ( m_name == "TMR1F" ) { m_registerType = Register::PIR1; m_bitPos = 0; } else if ( m_name == "TMR2F" ) { m_registerType = Register::PIR1; m_bitPos = 1; } else if ( m_name == "CCP1IF" ) { m_registerType = Register::PIR1; m_bitPos = 2; } else if ( m_name == "SSPIF"&& pic_type=="P16F877" ) { m_registerType = Register::PIR1; m_bitPos = 3; } else if ( m_name == "TXIF" ) { m_registerType = Register::PIR1; m_bitPos = 4; } else if ( m_name == "RCIF" ) { m_registerType = Register::PIR1; m_bitPos = 5; } else if ( m_name == "ADIF" && pic_type=="P16F877") { m_registerType = Register::PIR1; m_bitPos = 6; } else if ( m_name == "CMIF" && pic_type=="P16F627") { m_registerType = Register::PIR1; m_bitPos = 6; } else if ( m_name == "PSPIF"&& pic_type=="P16F877") { m_registerType = Register::PIR1; m_bitPos = 7; } else if ( m_name == "EEIF"&& pic_type=="P16F627") { m_registerType = Register::PIR1; m_bitPos = 7; } //-------PIR2---------// else if ( m_name == "CCP2IF" ) { m_registerType = Register::PIR2; m_bitPos = 0; } else if ( m_name == "BCLIF" ) { m_registerType = Register::PIR2; m_bitPos = 3; } else if ( m_name == "EEIF" && pic_type=="P16F877" ) { m_registerType = Register::PIR2; m_bitPos = 4; } //-------T1CON--------// else if ( m_name == "TMR1ON" ) { m_registerType = Register::T1CON; m_bitPos = 0; } else if ( m_name == "TMR1CS" ) { m_registerType = Register::T1CON; m_bitPos = 1; } else if ( m_name == "T1SYNC"&& pic_type=="P16F877" ) { m_registerType = Register::T1CON; m_bitPos = 2; } else if ( m_name == "NOT_T1SYNC"&& pic_type=="P16F627" ) { m_registerType = Register::T1CON; m_bitPos = 2; } else if ( m_name == "T1OSCEN" ) { m_registerType = Register::T1CON; m_bitPos = 3; } else if ( m_name == "T1CKPS0" ) { m_registerType = Register::T1CON; m_bitPos = 4; } else if ( m_name == "T1CKPS1" ) { m_registerType = Register::T1CON; m_bitPos = 5; } //-------T2CON--------// else if ( m_name == "T2CKPS0" ) { m_registerType = Register::T2CON; m_bitPos = 0; } else if ( m_name == "T2CKPS1" ) { m_registerType = Register::T2CON; m_bitPos = 1; } else if ( m_name == "TMR2ON" ) { m_registerType = Register::T2CON; m_bitPos = 2; } else if ( m_name == "TOUTPS0" ) { m_registerType = Register::T2CON; m_bitPos = 3; } else if ( m_name == "TOUTPS1" ) { m_registerType = Register::T2CON; m_bitPos = 4; } else if ( m_name == "TOUTPS2" ) { m_registerType = Register::T2CON; m_bitPos = 5; } else if ( m_name == "TOUTPS3" ) { m_registerType = Register::T2CON; m_bitPos = 6; } //---SSPCON------// else if ( m_name == "SSPM0" ) { m_registerType = Register::SSPCON; m_bitPos = 0; } else if ( m_name == "SSPM1" ) { m_registerType = Register::SSPCON; m_bitPos = 1; } else if ( m_name == "SSPM2" ) { m_registerType = Register::SSPCON; m_bitPos = 2; } else if ( m_name == "SSPM3" ) { m_registerType = Register::SSPCON; m_bitPos = 3; } else if ( m_name == "CKP" ) { m_registerType = Register::SSPCON; m_bitPos = 4; } else if ( m_name == "SSPEN" ) { m_registerType = Register::SSPCON; m_bitPos = 5; } else if ( m_name == "SSPOV" ) { m_registerType = Register::SSPCON; m_bitPos = 6; } else if ( m_name == "WCOL" ) { m_registerType = Register::SSPCON; m_bitPos = 7; } //-------CCP1CON----// else if ( m_name == "CCP1M0" ) { m_registerType = Register::CCP1CON; m_bitPos = 0; } else if ( m_name == "CCP1M1" ) { m_registerType = Register::CCP1CON; m_bitPos = 1; } else if ( m_name == "CCP1M2" ) { m_registerType = Register::CCP1CON; m_bitPos = 2; } else if ( m_name == "CCP1M3" ) { m_registerType = Register::CCP1CON; m_bitPos = 3; } else if ( m_name == "CCP1Y" ) { m_registerType = Register::CCP1CON; m_bitPos = 4; } else if ( m_name == "CCP1X" ) { m_registerType = Register::CCP1CON; m_bitPos = 5; } //-------RCSTA----// else if ( m_name == "RX9D" ) { m_registerType = Register::RCSTA; m_bitPos = 0; } else if ( m_name == "OERR" ) { m_registerType = Register::RCSTA; m_bitPos = 1; } else if ( m_name == "FERR" ) { m_registerType = Register::RCSTA; m_bitPos = 2; } else if ( m_name == "ADDEN"&& pic_type=="P16F877" ) { m_registerType = Register::RCSTA; m_bitPos = 3; } else if ( m_name == "ADEN"&& pic_type=="P16F627" ) { m_registerType = Register::RCSTA; m_bitPos = 3; } else if ( m_name == "CREN" ) { m_registerType = Register::RCSTA; m_bitPos = 4; } else if ( m_name == "SREN" ) { m_registerType = Register::RCSTA; m_bitPos = 5; } else if ( m_name == "RX9" ) { m_registerType = Register::RCSTA; m_bitPos = 6; } else if ( m_name == "SPEN" ) { m_registerType = Register::RCSTA; m_bitPos = 7; } //-----CCP2CON-------// else if ( m_name == "CCP2M0" ) { m_registerType = Register::CCP2CON; m_bitPos = 0; } else if ( m_name == "CCP2M1" ) { m_registerType = Register::CCP2CON; m_bitPos = 1; } else if ( m_name == "CCP2M2" ) { m_registerType = Register::CCP2CON; m_bitPos = 2; } else if ( m_name == "CCP2M3" ) { m_registerType = Register::CCP2CON; m_bitPos = 3; } else if ( m_name == "CCP2Y" ) { m_registerType = Register::CCP2CON; m_bitPos = 4; } else if ( m_name == "CCP2X" ) { m_registerType = Register::CCP2CON; m_bitPos = 5; } //--------ADCON0------// else if ( m_name == "ADON" ) { m_registerType = Register::ADCON0; m_bitPos = 0; } else if ( m_name == "GO" ) { m_registerType = Register::ADCON0; m_bitPos = 2; } else if ( m_name == "CHS0" ) { m_registerType = Register::ADCON0; m_bitPos = 3; } else if ( m_name == "CHS1" ) { m_registerType = Register::ADCON0; m_bitPos = 4; } else if ( m_name == "CHS2" ) { m_registerType = Register::ADCON0; m_bitPos = 5; } else if ( m_name == "ADCS0" ) { m_registerType = Register::ADCON0; m_bitPos = 6; } else if ( m_name == "ADCS1" ) { m_registerType = Register::ADCON0; m_bitPos = 7; } //-------CMCON---------------//pic16f627 else if ( m_name == "CM0"&& pic_type=="P16F627") { m_registerType = Register::CMCON; m_bitPos = 0; } else if ( m_name == "CM1"&& pic_type=="P16F627") { m_registerType = Register::CMCON; m_bitPos = 1; } else if ( m_name == "CM2"&& pic_type=="P16F627") { m_registerType = Register::CMCON; m_bitPos = 2; } else if ( m_name == "CM3"&& pic_type=="P16F627") { m_registerType = Register::CMCON; m_bitPos = 3; } else if ( m_name == "CIS"&& pic_type=="P16F627") { m_registerType = Register::CMCON; m_bitPos = 4; } else if ( m_name == "C2INV"&& pic_type=="P16F627") { m_registerType = Register::CMCON; m_bitPos = 5; } else if ( m_name == "C1OUT"&& pic_type=="P16F627") { m_registerType = Register::CMCON; m_bitPos = 6; } else if ( m_name == "C2OUT"&& pic_type=="P16F627") { m_registerType = Register::CMCON; m_bitPos = 7; } //---------------------------------------------Bank1-------------------------------// //-------OPTION_REGSITER---------------// else if ( m_name == "PS0" ) { m_registerType = Register::OPTION_REG; m_bitPos = 0; } else if ( m_name == "PS1" ) { m_registerType = Register::OPTION_REG; m_bitPos = 1; } else if ( m_name == "PS2" ) { m_registerType = Register::OPTION_REG; m_bitPos = 2; } else if ( m_name == "PSA" ) { m_registerType = Register::OPTION_REG; m_bitPos = 3; } else if ( m_name == "T0SE" ) { m_registerType = Register::OPTION_REG; m_bitPos = 4; } else if ( m_name == "T0CS" ) { m_registerType = Register::OPTION_REG; m_bitPos = 5; } else if ( m_name == "INTEDG" ) { m_registerType = Register::OPTION_REG; m_bitPos = 6; } else if(m_name =="NOT_RBPU"&&(pic_type=="P16C84"||pic_type=="P16F84"||pic_type=="P16F627")) { m_registerType = Register::OPTION_REG; m_bitPos = 7; } else if (m_name == "RBPU" && pic_type=="P16C84") { m_registerType = Register::OPTION_REG; m_bitPos = 7; } //--------PIE1---------// else if ( m_name == "TMR1IE" ) { m_registerType = Register::PIE1; m_bitPos = 0; } else if ( m_name == "TMR2IE" ) { m_registerType = Register::PIE1; m_bitPos = 1; } else if ( m_name == "CCP1IE" ) { m_registerType = Register::PIE1; m_bitPos = 2; } else if ( m_name == "SSPIE" && pic_type=="P16F877") { m_registerType = Register::PIE1; m_bitPos = 3; } else if ( m_name == "TXIE" ) { m_registerType = Register::PIE1; m_bitPos = 4; } else if ( m_name == "RCIE" ) { m_registerType = Register::PIE1; m_bitPos = 5; } else if ( m_name == "ADIE" && pic_type=="P16F877" ) { m_registerType = Register::PIE1; m_bitPos = 6; } else if ( m_name == "CMIE" && pic_type=="P16F627" ) { m_registerType = Register::PIE1; m_bitPos = 6; } else if ( m_name == "PSPIE" && pic_type=="P16F877" ) { m_registerType = Register::PIE1; m_bitPos = 7; } else if ( m_name == "EEIE" && pic_type=="P16F627" ) { m_registerType = Register::PIE1; m_bitPos = 7; } //--------PIE2---------// else if ( m_name == "CCP2IE" ) { m_registerType = Register::PIE2; m_bitPos = 0; } else if ( m_name == "BCLIE" ) { m_registerType = Register::PIE2; m_bitPos = 3; } else if ( m_name == "EEIE"&& pic_type=="P16F877" ) { m_registerType = Register::PIE2; m_bitPos = 4; } //--------PCON---------// else if ( m_name == "NOT_BOR" ) { m_registerType = Register::PCON; m_bitPos = 0; } else if ( m_name == "NOT_POR" ) { m_registerType = Register::PCON; m_bitPos = 1; } else if ( m_name == "OSCF"&& pic_type=="P16F627" ) { m_registerType = Register::PCON; m_bitPos = 3; } //--------SSPCON2------// else if ( m_name =="SEN") { m_registerType = Register::SSPCON2; m_bitPos = 0; } else if ( m_name =="RSEN") { m_registerType = Register::SSPCON2; m_bitPos = 1; } else if ( m_name =="PEN") { m_registerType = Register::SSPCON2; m_bitPos = 2; } else if ( m_name =="RCEN") { m_registerType = Register::SSPCON2; m_bitPos = 3; } else if ( m_name =="ACKEN") { m_registerType = Register::SSPCON2; m_bitPos = 4; } else if ( m_name =="ACKDT") { m_registerType = Register::SSPCON2; m_bitPos = 5; } else if ( m_name =="ACKSTAT" ) { m_registerType = Register::SSPCON2; m_bitPos = 6; } else if ( m_name == "GCEN" ) { m_registerType = Register::SSPCON2; m_bitPos = 7; } //--------SSPSTAT------// else if ( m_name =="BF") { m_registerType = Register::SSPSTAT; m_bitPos = 0; } else if ( m_name == "UA") { m_registerType = Register::SSPSTAT; m_bitPos = 1; } else if ( m_name =="R") { m_registerType = Register::SSPSTAT; m_bitPos = 2; } else if ( m_name =="S") { m_registerType = Register::SSPSTAT; m_bitPos = 3; } else if ( m_name == "P") { m_registerType = Register::SSPSTAT; m_bitPos = 4; } else if ( m_name == "D") { m_registerType = Register::SSPSTAT; m_bitPos = 5; } else if ( m_name == "CKE" ) { m_registerType = Register::SSPSTAT; m_bitPos = 6; } else if ( m_name == "SMP" ) { m_registerType = Register::SSPSTAT; m_bitPos = 7; } //--------TXSTA--------// else if ( m_name == "TX9D" ) { m_registerType = Register::TXSTA; m_bitPos = 0; } else if ( m_name == "TRMT" ) { m_registerType = Register::TXSTA; m_bitPos = 1; } else if ( m_name == "BRGH" ) { m_registerType = Register::TXSTA; m_bitPos = 2; } else if ( m_name == "SYNC" ) { m_registerType = Register::TXSTA; m_bitPos = 4; } else if ( m_name == "TXEN" ) { m_registerType = Register::TXSTA; m_bitPos = 5; } else if ( m_name == "TX9" ) { m_registerType = Register::TXSTA; m_bitPos = 6; } else if ( m_name == "CSRC" ) { m_registerType = Register::TXSTA; m_bitPos = 7; } //---------ADCON1-----// else if ( m_name == "PCFG0" ) { m_registerType = Register::ADCON1; m_bitPos = 0; } else if ( m_name == "PCFG1" ) { m_registerType = Register::ADCON1; m_bitPos = 1; } else if ( m_name == "PCFG2" ) { m_registerType = Register::ADCON1; m_bitPos = 2; } else if ( m_name == "PCFG3" ) { m_registerType = Register::ADCON1; m_bitPos = 3; } else if ( m_name == "ADFM" ) { m_registerType = Register::ADCON1; m_bitPos = 7; } //--------------------------------------------Bank2------------------// //-----NOTHING TODO---// // //--------------------------------------------Bank3------------------// else if ( m_name == "RD" ) { m_registerType = Register::EECON1; m_bitPos = 0; } else if ( m_name == "WR" ) { m_registerType = Register::EECON1; m_bitPos = 1; } else if ( m_name == "WREN" ) { m_registerType = Register::EECON1; m_bitPos = 2; } else if ( m_name == "WRERR" ) { m_registerType = Register::EECON1; m_bitPos = 3; } else if ( m_name == "EEIF"&&(pic_type=="P16F84"||pic_type=="P16C84"))//imp **** { m_registerType = Register::EECON1; m_bitPos = 4; } else if ( m_name == "EEPGD" && pic_type=="P16F877" ) { m_registerType = Register::EECON1; m_bitPos = 7; } //---------VRCON------// else if ( m_name == "VR0" && pic_type=="P16F627" ) { m_registerType = Register::VRCON; m_bitPos = 0; } else if ( m_name == "VR1" && pic_type=="P16F627" ) { m_registerType = Register::VRCON; m_bitPos = 1; } else if ( m_name == "VR2" && pic_type=="P16F627" ) { m_registerType = Register::VRCON; m_bitPos = 2; } else if ( m_name == "VR3" && pic_type=="P16F627" ) { m_registerType = Register::VRCON; m_bitPos = 3; } else if ( m_name == "VRR" && pic_type=="P16F627" ) { m_registerType = Register::VRCON; m_bitPos = 5; } else if ( m_name == "VROE" && pic_type=="P16F627" ) { m_registerType = Register::VRCON; m_bitPos = 6; } else if ( m_name == "VREN" && pic_type=="P16F627" ) { m_registerType = Register::VRCON; m_bitPos = 7; } else { m_registerType = Register::none; m_bitPos = 0; qCritical() << Q_FUNC_INFO << "Unknown bit: " << m_name << endl; } } //END class RegisterBit //BEGIN class RegisterState RegisterState::RegisterState() { reset(); } void RegisterState::reset() { known = 0x0; value = 0x0; } void RegisterState::merge( const RegisterState & state ) { known &= state.known; known &= ~( value ^ state.value ); } bool RegisterState::operator == ( const RegisterState & state ) const { return (known == state.known) && (value == state.value); } void RegisterState::print() { cout << " known="<< binary(known).toStdString() <instructionList( (InstructionPosition)i ); InstructionList::const_iterator end = list->end(); for ( InstructionList::const_iterator it = list->begin(); it != end; ++it ) append( *it, ( (i == Middle) ? middleInsertionPosition : (InstructionPosition)i ) ); // Queue any labels that the other code has queued m_queuedLabels[i] += code->queuedLabels( (InstructionPosition)i ); } } void Code::queueLabel( const QString & label, InstructionPosition position ) { // cout << Q_FUNC_INFO << "label="<addLabels( m_queuedLabels[position] ); m_queuedLabels[position].clear(); } } Instruction * Code::instruction( const QString & label ) const { for ( unsigned i = 0; i < PositionCount; ++i ) { InstructionList::const_iterator end = m_instructionLists[i].end(); for ( InstructionList::const_iterator it = m_instructionLists[i].begin(); it != end; ++it ) { if ( (*it)->labels().contains( label ) ) return *it; } } return nullptr; } Code::iterator Code::find( Instruction * instruction ) { iterator e = end(); iterator i = begin(); for ( ; i != e; ++i ) { if ( *i == instruction ) break; } return i; } void Code::postCompileConstruct() { // Give any queued labels to the instructions in the subsequent code block for ( unsigned i = 0; i < PositionCount; ++i ) { if ( m_queuedLabels[i].isEmpty() ) continue; QStringList labels = m_queuedLabels[i]; m_queuedLabels[i].clear(); // Find an instruction to dump them onto for ( unsigned block = i+1; block < PositionCount; ++block ) { bool added = false; InstructionList::iterator end = m_instructionLists[block].end(); for ( InstructionList::iterator it = m_instructionLists[block].begin(); it != end; ++it ) { if ( (*it)->type() == Instruction::Assembly ) { (*it)->addLabels( labels ); added = true; break; } } if ( added ) break; } } } QString Code::generateCode( PIC14 * pic ) const { QString code; const QStringList variables = findVariables(); if ( !variables.isEmpty() ) { code += "; Variables\n"; uchar reg = pic->gprStart(); QStringList::const_iterator end = variables.end(); for ( QStringList::const_iterator it = variables.begin(); it != end; ++it ) code += QString("%1\tequ\t0x%2\n").arg( *it ).arg( QString::number( reg++, 16 ) ); code += "\n"; } QString picString = pic->minimalTypeString(); code += QString("list p=%1\n").arg( picString ); code += QString("include \"p%2.inc\"\n\n").arg( picString.toLower() ); code += "; Config options\n"; code += " __config _WDT_OFF\n\n"; code += "START\n\n"; for ( unsigned i = 0; i < PositionCount; ++i ) { InstructionList::const_iterator end = m_instructionLists[i].end(); for ( InstructionList::const_iterator it = m_instructionLists[i].begin(); it != end; ++it ) { const QStringList labels = (*it)->labels(); if ( !labels.isEmpty() ) { code += '\n'; QStringList::const_iterator labelsEnd = labels.end(); for ( QStringList::const_iterator labelsIt = labels.begin(); labelsIt != labelsEnd; ++labelsIt ) code += *labelsIt + '\n'; } if ( (*it)->type() == Instruction::Assembly ) code += '\t'; code += (*it)->code() + '\n'; } } return code; } QStringList Code::findVariables() const { QStringList variables; const_iterator e = end(); for ( const_iterator i = begin(); i != e; ++i ) { if ( (*i)->file().type() != Register::GPR ) continue; QString alias = (*i)->file().name(); if ( !variables.contains( alias ) ) variables << alias; } return variables; } void Code::generateLinksAndStates() { CodeIterator e = end(); for ( CodeIterator it = begin(); it != e; ++it ) (*it)->clearLinks(); for ( CodeIterator it = begin(); it != e; ++it ) (*it)->generateLinksAndStates( it ); // Generate return links for call instructions // This cannot be done from the call instructions as we need to have // generated the links first. for ( CodeIterator it = begin(); it != e; ++it ) { Instr_call * ins = dynamic_cast(*it); if ( !ins ) continue; Instruction * next = *(++Code::iterator(it)); ins->makeReturnLinks( next ); } } void Code::setAllUnused() { CodeIterator e = end(); for ( CodeIterator it = begin(); it != e; ++it ) { (*it)->setUsed( false ); (*it)->resetRegisterDepends(); } } CodeIterator Code::begin() { // Following code is very similar to the version of this function. // Make sure any changes are applied to both (when applicable). for ( unsigned i = 0; i < PositionCount; ++i ) { if ( m_instructionLists[i].isEmpty() ) continue; CodeIterator codeIterator; codeIterator.code = this; codeIterator.it = m_instructionLists[i].begin(); codeIterator.pos = (Code::InstructionPosition)i; codeIterator.list = & m_instructionLists[i]; codeIterator.listEnd = m_instructionLists[i].end(); return codeIterator; } return end(); } CodeIterator Code::end() { // Following code is very similar to the version of this function. // Make sure any changes are applied to both (when applicable). CodeIterator codeIterator; codeIterator.code = this; codeIterator.it = m_instructionLists[ PositionCount - 1 ].end(); codeIterator.pos = (Code::InstructionPosition)(Code::PositionCount - 1); codeIterator.list = & m_instructionLists[ PositionCount - 1 ]; codeIterator.listEnd = m_instructionLists[ PositionCount - 1 ].end(); return codeIterator; } CodeConstIterator Code::begin() const { // Following code is very similar to the non-const version of this function. // Make sure any changes are applied to both (when applicable). for ( unsigned i = 0; i < PositionCount; ++i ) { if ( m_instructionLists[i].isEmpty() ) continue; CodeConstIterator codeIterator; codeIterator.code = this; codeIterator.it = m_instructionLists[i].begin(); codeIterator.pos = (Code::InstructionPosition)i; codeIterator.list = & m_instructionLists[i]; codeIterator.listEnd = m_instructionLists[i].end(); return codeIterator; } return end(); } CodeConstIterator Code::end() const { // Following code is very similar to the non-const version of this function. // Make sure any changes are applied to both (when applicable). CodeConstIterator codeIterator; codeIterator.code = this; codeIterator.it = m_instructionLists[ PositionCount - 1 ].end(); codeIterator.pos = (Code::InstructionPosition)(Code::PositionCount - 1); codeIterator.list = & m_instructionLists[ PositionCount - 1 ]; codeIterator.listEnd = m_instructionLists[ PositionCount - 1 ].end(); return codeIterator; } //END class Code //BEGIN class CodeIterator CodeIterator & CodeIterator::operator ++ () { // NOTE: This code is very similar to the const version. // Any changes to thsi code should be applied there as well (when applicable). do { if ( ++it == listEnd && pos < (Code::PositionCount - 1) ) { bool found = false; for ( pos = (Code::InstructionPosition)(pos+1); pos < Code::PositionCount; pos = (Code::InstructionPosition)(pos+1) ) { list = code->instructionList( pos ); listEnd = list->end(); if ( list->isEmpty() ) continue; it = list->begin(); found = true; break; } if ( !found ) it = listEnd; } } while ( (it != listEnd) && ((*it)->type() != Instruction::Assembly) ); return *this; } CodeIterator & CodeIterator::removeAndIncrement() { Instruction * i = *it; ++(*this); code->removeInstruction( i ); return *this; } void CodeIterator::insertBefore( Instruction * ins ) { list->insert( it, ins ); } //END class CodeIterator //BEGIN class CodeConstIterator CodeConstIterator & CodeConstIterator::operator ++ () { // NOTE: This code is very similar to the non-const version. // Any changes to thsi code should be applied there as well (when applicable). do { if ( ++it == listEnd && pos < (Code::PositionCount - 1) ) { bool found = false; for ( pos = (Code::InstructionPosition)(pos+1); pos < Code::PositionCount; pos = (Code::InstructionPosition)(pos+1) ) { list = code->instructionList( pos ); listEnd = list->end(); if ( list->isEmpty() ) continue; it = list->begin(); found = true; break; } if ( !found ) it = listEnd; } } while ( (it != listEnd) && ((*it)->type() != Instruction::Assembly) ); return *this; } //END class CodeConstIterator //BEGIN class Instruction Instruction::Instruction() { m_bInputStateChanged = true; m_bPositionAffectsBranching = false; m_bUsed = false; m_literal = 0; m_dest = 0; } Instruction::~ Instruction() { } void Instruction::addLabels( const QStringList & labels ) { m_labels += labels; } void Instruction::setLabels( const QStringList & labels ) { m_labels = labels; } void Instruction::generateLinksAndStates( Code::iterator current ) { makeOutputLinks( current ); m_outputState.reset(); } ProcessorBehaviour Instruction::behaviour() const { return ProcessorBehaviour(); } void Instruction::makeOutputLinks( Code::iterator current, bool firstOutput, bool secondOutput ) { if ( !firstOutput && !secondOutput ) return; ++current; if ( !*current ) { qWarning() << Q_FUNC_INFO << "current+1 is null"<addInputLink( this ); if ( !secondOutput ) return; ++current; (*current)->addInputLink( this ); } void Instruction::makeLabelOutputLink( const QString & label ) { Instruction * output = m_pCode->instruction( label ); if ( output ) output->addInputLink( this ); } void Instruction::addInputLink( Instruction * instruction ) { // Don't forget that a link to ourself is valid! if ( !instruction || m_inputLinks.contains( instruction ) ) return; m_inputLinks << instruction; instruction->addOutputLink( this ); } void Instruction::addOutputLink( Instruction * instruction ) { // Don't forget that a link to ourself is valid! if ( !instruction || m_outputLinks.contains( instruction ) ) return; m_outputLinks << instruction; instruction->addInputLink( this ); } void Instruction::removeInputLink( Instruction * instruction ) { m_inputLinks.removeAll( instruction ); } void Instruction::removeOutputLink( Instruction * instruction ) { m_outputLinks.removeAll( instruction ); } void Instruction::clearLinks() { m_inputLinks.clear(); m_outputLinks.clear(); } //END class Instruction //BEGIN Byte-Oriented File Register Operations QString Instr_addwf::code() const { return QString("addwf\t%1,%2").arg( m_file.name() ).arg( m_dest ); } void Instr_addwf::generateLinksAndStates( Code::iterator current ) { m_outputState = m_inputState; m_outputState.reg( outputReg() ).value = (m_inputState.working.value + m_inputState.reg( m_file ).value) & 0xff; m_outputState.reg( outputReg() ).known = ((m_inputState.working.known == 0xff) && (m_inputState.reg( m_file ).known == 0xff)) ? 0xff : 0x0; m_outputState.status.known &= ~( (1 << RegisterBit::C) | (1 << RegisterBit::DC) | (1 << RegisterBit::Z) ); if ( m_file.type() != Register::PCL || m_dest == 0 ) { makeOutputLinks( current ); return; } ++current; // Don't have a link to ourself // maxInc is the greatest possibly value that we might have incremented the program counter by. // It is generated by ORing the known bits of the working register with the greatest value // of the unknown bits; uchar maxInc = m_inputState.working.maxValue(); if ( maxInc < 0xff ) maxInc++; // cout << "m_inputState.working.known="< m_inputState.reg( m_file ).maxValue() ) { m_outputState.status.value &= ~(1 << RegisterBit::C); m_outputState.status.known |= (1 << RegisterBit::C); } else if ( m_inputState.working.maxValue() <= m_inputState.reg( m_file ).minValue() ) { m_outputState.status.value |= (1 << RegisterBit::C); m_outputState.status.known |= (1 << RegisterBit::C); } if ( (m_inputState.working.known == 0xff) && (m_inputState.reg( m_file ).known == 0xff) ) { bool isZero = (m_inputState.working.value == m_inputState.reg( m_file ).value); if ( isZero ) m_outputState.status.value |= (1 << RegisterBit::Z); else m_outputState.status.value &= ~(1 << RegisterBit::Z); m_outputState.status.known |= (1 << RegisterBit::Z); } } ProcessorBehaviour Instr_subwf::behaviour() const { ProcessorBehaviour behaviour; // Depend on W and f behaviour.working.depends = 0xff; behaviour.reg( m_file ).depends = 0xff; behaviour.status.depends = m_file.bankDependent() ? (1 << RegisterBit::RP0) : 0x0; behaviour.status.indep = (1 << RegisterBit::C) | (1 << RegisterBit::DC) | (1 << RegisterBit::Z); return behaviour; } QString Instr_swapf::code() const { return QString("swapf\t%1,%2").arg( m_file.name() ).arg( m_dest ); } void Instr_swapf::generateLinksAndStates( Code::iterator current ) { makeOutputLinks( current ); m_outputState = m_inputState; if ( m_dest == 0 ) { // Writing to the working register m_outputState.working.known = 0x0; } } ProcessorBehaviour Instr_swapf::behaviour() const { ProcessorBehaviour behaviour; behaviour.reg( m_file ).depends = 0xff; behaviour.working.indep = ( m_dest == 0 ) ? 0xff : 0x0; behaviour.status.depends = m_file.bankDependent() ? (1 << RegisterBit::RP0) : 0x0; return behaviour; } QString Instr_xorwf::code() const { return QString("xorwf\t%1,%2").arg( m_file.name() ).arg( m_dest ); } void Instr_xorwf::generateLinksAndStates( Code::iterator current ) { makeOutputLinks( current ); m_outputState = m_inputState; m_outputState.status.known &= ~(1 << RegisterBit::Z); m_outputState.reg( outputReg() ).known = 0x0; } ProcessorBehaviour Instr_xorwf::behaviour() const { ProcessorBehaviour behaviour; // Depend on W and f behaviour.working.depends = 0xff; behaviour.reg( m_file ).depends = 0xff; behaviour.status.depends = m_file.bankDependent() ? (1 << RegisterBit::RP0) : 0x0; behaviour.status.indep = (1 << RegisterBit::Z); return behaviour; } //END Byte-Oriented File Register Operations //BEGIN Bit-Oriented File Register Operations QString Instr_bcf::code() const { return QString("bcf\t\t%1,%2").arg( m_file.name() ).arg( m_bit.name() ); } void Instr_bcf::generateLinksAndStates( Code::iterator current ) { makeOutputLinks( current ); m_outputState = m_inputState; m_outputState.reg( m_file ).value &= ~uchar(1 << m_bit.bitPos()); m_outputState.reg( m_file ).known |= uchar(1 << m_bit.bitPos()); } ProcessorBehaviour Instr_bcf::behaviour() const { ProcessorBehaviour behaviour; behaviour.status.depends = m_file.bankDependent() ? (1 << RegisterBit::RP0) : 0x0; behaviour.reg( m_file ).indep = 1 << m_bit.bitPos(); return behaviour; } QString Instr_bsf::code() const { return QString("bsf\t\t%1,%2").arg( m_file.name() ).arg( m_bit.name() ); } void Instr_bsf::generateLinksAndStates( Code::iterator current ) { makeOutputLinks( current ); m_outputState = m_inputState; m_outputState.reg( m_file ).value |= uchar(1 << m_bit.bitPos()); m_outputState.reg( m_file ).known |= uchar(1 << m_bit.bitPos()); } ProcessorBehaviour Instr_bsf::behaviour() const { ProcessorBehaviour behaviour; behaviour.status.depends = m_file.bankDependent() ? (1 << RegisterBit::RP0) : 0x0; behaviour.reg( m_file ).indep = 1 << m_bit.bitPos(); return behaviour; } QString Instr_btfsc::code() const { return QString("btfsc\t%1,%2").arg( m_file.name() ).arg( m_bit.name() ); } void Instr_btfsc::generateLinksAndStates( Code::iterator current ) { m_outputState = m_inputState; if ( m_inputState.reg( m_file ).known & (1 << m_bit.bitPos()) ) { bool bit = m_inputState.reg( m_file ).value & (1 << m_bit.bitPos()); makeOutputLinks( current, bit, !bit ); } else makeOutputLinks( current, true, true ); } ProcessorBehaviour Instr_btfsc::behaviour() const { ProcessorBehaviour behaviour; behaviour.reg( m_file ).depends = 1 << m_bit.bitPos(); behaviour.status.depends = (m_file.type() == Register::STATUS) ? m_bit.bit() : 0x0; return behaviour; } QString Instr_btfss::code() const { return QString("btfss\t%1,%2").arg( m_file.name() ).arg( m_bit.name() ); } void Instr_btfss::generateLinksAndStates( Code::iterator current ) { m_outputState = m_inputState; if ( m_inputState.reg( m_file ).known & (1 << m_bit.bitPos()) ) { bool bit = m_inputState.reg( m_file ).value & (1 << m_bit.bitPos()); makeOutputLinks( current, !bit, bit ); } else makeOutputLinks( current, true, true ); } ProcessorBehaviour Instr_btfss::behaviour() const { ProcessorBehaviour behaviour; behaviour.reg( m_file ).depends = 1 << m_bit.bitPos(); behaviour.status.depends = (m_file.type() == Register::STATUS) ? m_bit.bit() : 0x0; return behaviour; } //END Bit-Oriented File Register Operations //BEGIN Literal and Control Operations QString Instr_addlw::code() const { return QString("addlw\t%1").arg( m_literal ); } void Instr_addlw::generateLinksAndStates( Code::iterator current ) { makeOutputLinks( current ); m_outputState = m_inputState; m_outputState.working.value = (m_inputState.working.value + m_literal) & 0xff; m_outputState.working.known = (m_inputState.working.known == 0xff) ? 0xff : 0x0; m_outputState.status.known &= ~( (1 << RegisterBit::C) | (1 << RegisterBit::DC) | (1 << RegisterBit::Z) ); } ProcessorBehaviour Instr_addlw::behaviour() const { ProcessorBehaviour behaviour; behaviour.working.depends = 0xff; behaviour.status.indep = (1 << RegisterBit::C) | (1 << RegisterBit::DC) | (1 << RegisterBit::Z); return behaviour; } QString Instr_andlw::code() const { return QString("andlw\t%1").arg( m_literal ); } void Instr_andlw::generateLinksAndStates( Code::iterator current ) { makeOutputLinks( current ); m_outputState = m_inputState; m_outputState.working.value = (m_inputState.working.value & m_literal) & 0xff; m_outputState.working.known |= ~m_literal; // Now know any bits that are zero in value m_outputState.status.known &= ~(1 << RegisterBit::Z); } ProcessorBehaviour Instr_andlw::behaviour() const { ProcessorBehaviour behaviour; behaviour.working.indep = ~m_literal; behaviour.working.depends = m_literal; behaviour.status.indep = (1 << RegisterBit::Z); return behaviour; } QString Instr_call::code() const { return QString("call\t%1").arg( m_label ); } void Instr_call::generateLinksAndStates( Code::iterator current ) { (void)current; makeLabelOutputLink( m_label ); m_outputState = m_inputState; } ProcessorBehaviour Instr_call::behaviour() const { ProcessorBehaviour behaviour; return behaviour; } void Instr_call::makeReturnLinks( Instruction * next ) { m_pCode->setAllUnused(); linkReturns( m_pCode->instruction( m_label ), next ); } void Instr_call::linkReturns( Instruction * current, Instruction * returnPoint ) { while (true) { if ( !current || current->isUsed() ) return; current->setUsed( true ); if ( dynamic_cast(current) || dynamic_cast(current) ) { // cout << "Added return link" << endl; // cout << " FROM: " << current->code() << endl; // cout << " TO: " << returnPoint->code() << endl; returnPoint->addInputLink( current ); return; } if ( dynamic_cast(current) ) { // Jump over the call instruction to its return point, // which will be the instruction after current. current = *(++m_pCode->find( current )); continue; } const InstructionList outputs = current->outputLinks(); if ( outputs.isEmpty() ) return; if ( outputs.size() == 1 ) current = outputs.first(); else { // Can't avoid function recursion now. InstructionList::const_iterator end = outputs.end(); for ( InstructionList::const_iterator it = outputs.begin(); it != end; ++it ) linkReturns( *it, returnPoint ); return; } }; } //TODO CLRWDT QString Instr_goto::code() const { return QString("goto\t%1").arg( m_label ); } void Instr_goto::generateLinksAndStates( Code::iterator current ) { (void)current; makeLabelOutputLink( m_label ); m_outputState = m_inputState; } ProcessorBehaviour Instr_goto::behaviour() const { ProcessorBehaviour behaviour; return behaviour; } QString Instr_iorlw::code() const { return QString("iorlw\t%1").arg( m_literal ); } void Instr_iorlw::generateLinksAndStates( Code::iterator current ) { makeOutputLinks( current ); m_outputState = m_inputState; m_outputState.working.value = (m_inputState.working.value | m_literal) & 0xff; m_outputState.working.known |= m_literal; // Now know any bits that are one in value m_outputState.status.known &= ~(1 << RegisterBit::Z); } ProcessorBehaviour Instr_iorlw::behaviour() const { ProcessorBehaviour behaviour; behaviour.working.indep = m_literal; behaviour.working.depends = ~m_literal; behaviour.status.indep = (1 << RegisterBit::Z);; return behaviour; } QString Instr_movlw::code() const { return QString("movlw\t%1").arg( m_literal ); } void Instr_movlw::generateLinksAndStates( Code::iterator current ) { makeOutputLinks( current ); m_outputState = m_inputState; m_outputState.working.known = 0xff; m_outputState.working.value = m_literal; } ProcessorBehaviour Instr_movlw::behaviour() const { ProcessorBehaviour behaviour; behaviour.working.indep = 0xff; return behaviour; } QString Instr_retfie::code() const { return "retfie"; } void Instr_retfie::generateLinksAndStates( Code::iterator current ) { // Don't generate any output links (void)current; m_inputState = m_outputState; } ProcessorBehaviour Instr_retfie::behaviour() const { ProcessorBehaviour behaviour; return behaviour; } QString Instr_retlw::code() const { return QString("retlw\t%1").arg( m_literal ); } void Instr_retlw::generateLinksAndStates( Code::iterator current ) { (void)current; m_outputState = m_inputState; m_outputState.working.known = 0xff; m_outputState.working.value = m_literal; } ProcessorBehaviour Instr_retlw::behaviour() const { ProcessorBehaviour behaviour; behaviour.working.indep = 0xff; return behaviour; } QString Instr_return::code() const { return "return"; } void Instr_return::generateLinksAndStates( Code::iterator current ) { (void)current; m_outputState = m_inputState; } ProcessorBehaviour Instr_return::behaviour() const { ProcessorBehaviour behaviour; return behaviour; } QString Instr_sleep::code() const { return "sleep"; } void Instr_sleep::generateLinksAndStates( Code::iterator current ) { // Don't generate any output links (void)current; m_outputState = m_inputState; m_outputState.status.value &= ~(1 << RegisterBit::NOT_PD); m_outputState.status.value |= (1 << RegisterBit::NOT_TO); m_outputState.status.known |= (1 << RegisterBit::NOT_TO) | (1 << RegisterBit::NOT_PD); } ProcessorBehaviour Instr_sleep::behaviour() const { ProcessorBehaviour behaviour; behaviour.status.indep = (1 << RegisterBit::NOT_TO) | (1 << RegisterBit::NOT_PD); return behaviour; } QString Instr_sublw::code() const { return QString("sublw\t%1").arg( m_literal ); } void Instr_sublw::generateLinksAndStates( Code::iterator current ) { makeOutputLinks( current ); m_outputState = m_inputState; m_outputState.working.value = (m_literal - m_inputState.working.value) & 0xff; m_outputState.working.known = (m_inputState.working.known == 0xff) ? 0xff : 0x00; m_outputState.status.known &= ~( (1 << RegisterBit::C) | (1 << RegisterBit::DC) | (1 << RegisterBit::Z) ); if ( m_inputState.working.minValue() > m_literal ) { m_outputState.status.value &= ~(1 << RegisterBit::C); m_outputState.status.known |= (1 << RegisterBit::C); } else if ( m_inputState.working.maxValue() <= m_literal ) { m_outputState.status.value |= (1 << RegisterBit::C); m_outputState.status.known |= (1 << RegisterBit::C); } if ( m_inputState.working.known == 0xff ) { bool isZero = (m_inputState.working.value == m_literal); if ( isZero ) m_outputState.status.value |= (1 << RegisterBit::Z); else m_outputState.status.value &= ~(1 << RegisterBit::Z); m_outputState.status.known |= (1 << RegisterBit::Z); } } ProcessorBehaviour Instr_sublw::behaviour() const { ProcessorBehaviour behaviour; behaviour.working.depends = 0xff; behaviour.status.indep = (1 << RegisterBit::C) | (1 << RegisterBit::DC) | (1 << RegisterBit::Z); return behaviour; } QString Instr_xorlw::code() const { return QString("xorlw\t%1").arg( m_literal ); } void Instr_xorlw::generateLinksAndStates( Code::iterator current ) { makeOutputLinks( current ); m_outputState = m_inputState; m_outputState.working.value = (m_inputState.working.value ^ m_literal) & 0xff; m_outputState.working.known = m_inputState.working.known; m_outputState.status.known &= ~(1 << RegisterBit::Z); } ProcessorBehaviour Instr_xorlw::behaviour() const { ProcessorBehaviour behaviour; behaviour.working.depends = 0xff; behaviour.status.indep = (1 << RegisterBit::Z); return behaviour; } //END Literal and Control Operations //BEGIN Microbe (non-assembly) Operations QString Instr_sourceCode::code() const { QStringList sourceLines = m_raw.split("\n", QString::SkipEmptyParts); // QString::split("\n",m_raw); return ";" + sourceLines.join("\n;"); } QString Instr_asm::code() const { return "; asm {\n" + m_raw + "\n; }"; } QString Instr_raw::code() const { return m_raw; } //END Microbe (non-assembly) Operations diff --git a/microbe/instruction.h b/microbe/instruction.h index b570d537..28466fd0 100644 --- a/microbe/instruction.h +++ b/microbe/instruction.h @@ -1,1327 +1,1327 @@ /*************************************************************************** * Copyright (C) 2004-2005 by Daniel Clarke * * 2005 by David Saxton * * * * 24-04-2007 * * Modified to add pic 16f877,16f627 and 16f628 * * by george john george@space-kerala.org * * supported by SPACE www.space-kerala.org * * * * 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., * * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * ***************************************************************************/ #ifndef INSTRUCTION_H #define INSTRUCTION_H -#include -#include -#include -#include +#include +#include +#include +#include class Code; class CodeIterator; class CodeConstIterator; class Instruction; class PIC14; typedef QList InstructionList; /** Abstraction for a Register - should be used instead of a register name. Contains info like whether or not the adressing of the register depends on the bank selection. @author David Saxton */ class Register { public: enum Type { TMR0, OPTION_REG, PCL, STATUS, FSR, PORTA, TRISA, PORTB, TRISB, EEDATA, EECON1, EEADR, EECON2, PCLATH, INTCON, //modification start PORTC, PORTD, PORTE, TRISC, TRISD, TRISE, ADCON0, ADCON1, //modification end // The following three are "special" WORKING, // Not a register that is addressable by an address GPR, // Refers to the collection of General Purpose Registers //modification start PIR1, PIR2, TMR1L, TMR1H, T1CON, TMR2, T2CON, RCSTA, TXREG, RCREG, ADRESH, PIE1, TXSTA, ADRESL, EEDATH, EEADRH, SSPBUF, SSPCON, CCPR1L, CCPR1H, CCP1CON, CCPR2L, CCPR2H, CCP2CON, PIE2, PCON, SSPCON2, PR2, SSPADD, SSPSTAT, SPBRG, VRCON, CMCON, //modification end none // used in default constructor //TODO //SSPBUF:SSPCON:CCPR1L:CCPR1H:CCP1CON:CCPR2L:CCPR2H:CCP2CON:--FOR BANK0 //PIE2:PCON:SSPCON2:PR2:SSPADD:SSPSTAT:SPBRG:--------FOR BANK1 }; // These banks are used for ORing together in the banks() function enum Banks { Bank0 = 1 << 0, Bank1 = 1 << 1 }; /** * Creates a register of the given type, giving it the appropriate name. * Note that this constructor should not be used for GPR. */ Register( Type type = none ); /** * Construct a Register with the given name. If the name is not * recognized, then it is assumed to be a GPR register. */ Register( const QString & name ); /** * Construct a Register with the given name. If the name is not * recognized, then it is assumed to be a GPR register. */ Register( const char * name ); /** * @return less-than-equality between registers; name is only compared * if both registers have type GPR. */ bool operator < ( const Register & reg ) const; /** * @return equality between registers; name is only compared if both * registers have type GPR. */ bool operator == ( const Register & reg ) const; /** * @return 0x1 and 0x2 for being addressable from banks 0 and 1 * respectively, OR'ed together. */ uchar banks() const; /** * Convenience function. * @see banks */ bool bankDependent() const; /** * Returns the name of the register, or the alias for the GPR. */ QString name() const { return m_name; } /** * @return the type of register. */ Type type() const { return m_type; } /** * From the Optimizer's perspective, it is OK to remove, change or add * any instruction so long as there are no visible external changes that * go against the original intention of the microbe source (a general * guiding principle). Therefore, this function returns true for PORT * and TRIS registers, false for everything else. */ bool affectsExternal() const; protected: QString m_name; Type m_type; }; class RegisterBit { public: enum STATUS_bits { C = 0, // Carry DC = 1, // Digit carry Z = 2, // Zero NOT_PD = 3, // Power-down NOT_TO = 4, // Time-out RP0 = 5, // Bank Select RP1 = 6, IRP = 7 }; enum INTCON_bits { RBIF = 0, INTF = 1, T0IF = 2, RBIE = 3, INTE = 4, T0IE = 5, EEIE = 6, GIE = 7 }; enum OPTION_bits { PS0 = 0, PS1 = 1, PS2 = 2, PSA = 3, T0SE = 4, T0CS = 5, INTEDG = 6, NOT_RBPU = 7 }; enum EECON1_bits { RD = 0, WR = 1, WREN = 2, WRERR = 3, EEIF = 4, EEPGD = 7 }; /** * Constructs a bit of the given register type at the given position. */ RegisterBit( uchar bitPos = 0, Register::Type reg = Register::none ); /** * Construct a register bit with the given name. */ RegisterBit( const QString & name ); /** * Construct a register bit with the given name. */ RegisterBit( const char * name ); /** * @warning do not trust this value! actually, this function should be * removed, or the constructors fixed so that this value can be trusted. * @return the register type that the bit belongs to. */ Register::Type registerType() const { return m_registerType; } /** * @return the position of the bit, e.g. "5" for RP0. */ uchar bitPos() const { return m_bitPos; } /** * @return the bit, e.g. "0x20" for Z. */ uchar bit() const { return (1 << m_bitPos); } /** * @return the name of the bit, e.g. "Z" for Z. */ QString name() const { return m_name; } protected: /** * Determines the register type and bit pos from the bit name (m_name). */ void initFromName(); Register::Type m_registerType; uchar m_bitPos:3; QString m_name; }; /** Contains information on the state of a register before an instruction is executed. Note that all the "uchar" values in this class should be considered as the 8 bits of a register. So for example, if known=0x2, then only the second bit of the register is known, and its value is given in the second bit of value. @author David Saxton */ class RegisterState { public: RegisterState(); /** * Merges the known and values together, (possibly) reducing what is * known. */ void merge( const RegisterState & state ); /** * Sets known to unknown and value to zero. */ void reset(); /** * Returns the bits that are definitely zero. */ uchar definiteZeros() const { return (~value) & known; } /** * Returns the bits that are definitely one. */ uchar definiteOnes() const { return value & known; } /** * Returns the bits that are unknown. */ uchar unknown() const { return ~known; } /** * @return the largest possible value that this register might be * storing, based on which bits are known and the value of those bits. */ uchar maxValue() const { return (value & known) | (~known); } /** * @return the smallest possible value that this register might be * storing, based on which bits are known and the value of those bits. */ uchar minValue() const { return (value & known); } /** * @return whether the known and value uchars are equal */ bool operator == ( const RegisterState & state ) const; /** * @return whether either of the known and value uchars are not equal. */ bool operator != ( const RegisterState & state ) const { return !( *this == state ); } /** * Prints known and value. */ void print(); /// Whether or not the value is known (for each bit). uchar known; /// The value of the register. uchar value; }; /** Setting and dependency information for register bits. See the respective member descriptions for more information. @author David Saxton */ class RegisterBehaviour { public: RegisterBehaviour(); /** * Sets "depends", "indep" and "changes" to 0x0. */ void reset(); /** * The bits whose value before the instruction is executed will affect * the processor state after execution. So for example, * in MOVLW this will be 0x0; * in ANDLW this will be the bits that are non-zero in the literal; * in BTFSC this will be the bit being tested (if this is the register * being tested). */ uchar depends; /** * The bits whose value after the instruction is executed is independent * of the value before execution. So for example, * in MOVLW, this will be 0xff; * in ANDLW this will be the bits that are zero in the literal; * in BTFSC this will be 0x0. */ uchar indep; }; /** Contains information on the state of a processor; e.g. register values @author David Saxton */ class ProcessorState { public: ProcessorState(); /** * Calls merge for each RegisterState. */ void merge( const ProcessorState & state ); /** * Calls reset() for each RegisterState. */ void reset(); /** * @return state for the given register. */ RegisterState & reg( const Register & reg ); /** * @return state for the given register. */ RegisterState reg( const Register & reg ) const; /** * @return whether all the RegisterStates are identical */ bool operator == ( const ProcessorState & state ) const; /** * @return whether any of the RegisterStates are not equal. */ bool operator != ( const ProcessorState & state ) const { return !( *this == state ); } /** * Displays each register's name and calls RegisterState::print in turn. */ void print(); /// The working register RegisterState working; /// The status register RegisterState status; protected: typedef QMap< Register, RegisterState > RegisterMap; /** * All registers other than working and status. Entries are created on * calls to reg with a new Register. */ RegisterMap m_registers; }; /** Contains behavioural information for each register. @author David Saxton */ class ProcessorBehaviour { public: ProcessorBehaviour(); /** * Calls reset() for each RegisterBehaviour. */ void reset(); /** * @return behaviour for the given register. */ RegisterBehaviour & reg( const Register & reg ); /// The working register RegisterBehaviour working; /// The status register RegisterBehaviour status; protected: typedef QMap< Register, RegisterBehaviour > RegisterMap; /** * All registers other than working and status. Entries are created on * calls to reg with a new Register. */ RegisterMap m_registers; }; /** Contains information on whether a register is overwritten before its value is used. Each uchar respresents the 8 bits of the register; if the bit is 1, then the corresponding bit of the register is used by the Instruction or one of its outputs before it is overwritten. @author David Saxton */ class RegisterDepends { public: RegisterDepends(); /** * Sets all the depends values to 0x0. */ void reset(); /** * @return behaviour for the given register. */ uchar & reg( const Register & reg ); /// The working register uchar working; /// The status register uchar status; protected: typedef QMap< Register, uchar > RegisterMap; /** * All registers other than working and status. Entries are created on * calls to reg with a new Register. */ RegisterMap m_registers; }; /** Holds a program structure; an (ordered) list of blocks of code, each of which contains a list of instructions. The structure is such as to provide easy manipulation of the program, as well as aiding the optimizer. @author David Saxton */ class Code { public: Code(); typedef CodeIterator iterator; typedef CodeConstIterator const_iterator; enum InstructionPosition { InterruptHandler = 0, LookupTable = 1, Middle = 2, ///< Used for main code Subroutine = 3, ///< Used for subroutines PositionCount = 4 ///< This must remain the last item and be the number of valid positions }; CodeIterator begin(); CodeIterator end(); CodeConstIterator begin() const; CodeConstIterator end() const; /** * Queues a label to be given to the next instruction to be added in the * given position */ void queueLabel( const QString & label, InstructionPosition position = Middle ); /** * Returns the list of queued labels for the given position. This is * used in merging code, as we also need to merge any queued labels. */ QStringList queuedLabels( InstructionPosition position ) const { return m_queuedLabels[position]; } /** * Adds the Instruction at the given position. */ void append( Instruction * instruction, InstructionPosition position = Middle ); /** * @returns the Instruction with the given label (or null if no such * Instruction). */ Instruction * instruction( const QString & label ) const; /** * Look for an Assembly instruction (other types are ignored). * @return an iterator to the current instruction, or end if it wasn't * found. */ iterator find( Instruction * instruction ); /** * Removes the Instruction (regardless of position). * @warning You should always use only this function to remove an * instruction as this function handles stuff such as pushing labels * from this instruction onto the next before deletion. */ void removeInstruction( Instruction * instruction ); /** * Merges all the blocks output together with other magic such as adding * variables, gpasm directives, etc. */ QString generateCode( PIC14 * pic ) const; /** * Appends the InstructionLists to the end of the ones in this instance. * @param middleInsertionPosition is the position where the middle code * blocks of the given code will be merged at. */ void merge( Code * code, InstructionPosition middleInsertionPosition = Middle ); /** * @returns the InstructionList for the given insertion position. */ InstructionList * instructionList( InstructionPosition position ) { return & m_instructionLists[position]; } /** * @returns the InstructionList for the given insertion position. */ const InstructionList * instructionList( InstructionPosition position ) const { return & m_instructionLists[position]; } /** * Calls generateOutputLinks for each Instruction */ void generateLinksAndStates(); /** * Calls setUsed(false) for all instructions. */ void setAllUnused(); /** * Does any work that is needed to the code before it can be passed to * the optimizer (such as flushing out queued labels). This is called * after all the instructions have been added to the code. */ void postCompileConstruct(); protected: /** * Used when generating the code. Finds the list of general purpose * registers that are referenced and returns their aliases. */ QStringList findVariables() const; InstructionList m_instructionLists[ PositionCount ]; ///< @see InstructionPosition QStringList m_queuedLabels[ PositionCount ]; ///< @see InstructionPosition private: // Disable copy constructor and operator= Code( const Code & ); Code &operator=( const Code & ); }; /** Iterates over all the instructions, going seamlessly between the different lists and avoiding the non-assembly instructions. @author David Saxton */ class CodeIterator { public: bool operator != ( const CodeIterator & i ) const { return it != i.it; } bool operator == ( const CodeIterator & i ) const { return it == i.it; } CodeIterator & operator ++ (); Instruction * & operator * () { return *it; } /** * Deletes the instruction that this iterator is currently pointing at * (removing it from any lists), and increments the iterator to the next * instruction. */ CodeIterator & removeAndIncrement(); /** * Inserts the given instruction before the instruction pointed at by * this iterator. */ void insertBefore( Instruction * ins ); InstructionList::iterator it; InstructionList::iterator listEnd; Code::InstructionPosition pos; Code * code; InstructionList * list; }; /** A const version of CodeIterator (cannot change instructions). @author David Saxton */ class CodeConstIterator { public: bool operator != ( const CodeConstIterator & i ) const { return it != i.it; } bool operator == ( const CodeConstIterator & i ) const { return it == i.it; } CodeConstIterator & operator ++ (); const Instruction * operator * () const { return *it; } InstructionList::const_iterator it; InstructionList::const_iterator listEnd; Code::InstructionPosition pos; const Code * code; const InstructionList * list; }; /** @author Daniel Clarke @author David Saxton */ class Instruction { public: enum InstructionType { Assembly, Raw, // User-inserted assembly Comment }; /** * Used in optimization. Note that this follows roughly, but not * exactly, the Microchip classifications of similar categories. */ enum AssemblyType { /** * Writes to a file (which can be obtained by calling outputReg(). */ FileOriented, /** * Writes to a file bit (so BCF or BSF). */ BitOriented, /** * Affects the working register via a literal operation, with no * branching (so excludes retlw). */ WorkingOriented, /** * Assembly instructions that don't come under the above categories * (so control and branching instructions). */ Other, /** * The Instruction is not of Assembly InstructionType. */ None }; Instruction(); virtual ~Instruction(); void setCode( Code * code ) { m_pCode = code; } /** * This is used to decide how to output the instruction, and which * instructions to avoid while optimizing. */ virtual InstructionType type() const { return Assembly; } /** * @return the AssemblyType (None for non-Assembly instructions). */ virtual AssemblyType assemblyType() const = 0; /** * The text to output to the generated assembly. */ virtual QString code() const = 0; /** * The input processor state is used to generate the outputlinks and the * output processor state. */ void setInputState( const ProcessorState & processorState ) { m_inputState = processorState; } /** * By using the ProcessorState, the Instruction should: * * Find all instructions that could be executed after this instruction. * * Generate the output ProcessorState. * The default behaviour of this function is to link to the next * sequential instruction, and to generate an unknown ProcessorState. * @warning if your instruction depends on any bits, then it must * reinherit this function and say so. * @param instruction points at this instruction */ virtual void generateLinksAndStates( Code::iterator instruction ); /** * @return the processor behaviour for this instruction. */ virtual ProcessorBehaviour behaviour() const; /** * An input link is an instruction that might be executed immediately * before this Instruction. */ void addInputLink( Instruction * inputLink ); /** * An output link is an instruction that might be executed immediately * after this Instruction. */ void addOutputLink( Instruction * inputLink ); /** * The list of instructions that might be executed immediately before * this instruction. * @see addInputLink */ InstructionList inputLinks() const { return m_inputLinks; } /** * The list of instructions that might be executed immediately after * this instruction. Instruction does not generate these links; instead * the list is generated Code::generateLinksAndStates function. */ InstructionList outputLinks() const { return m_outputLinks; } /** * Remove the given input link from the instruction. */ void removeInputLink( Instruction * ins ); /** * Remove the given output link from the instruction. */ void removeOutputLink( Instruction * ins ); /** * Clears all input and output links from this instruction. This does * not remove references to this instruction from other instructions. */ void clearLinks(); /** * An instruction may have zero, or more than zero labels associated * with it - these will be printed before the instruction in the * assembly output. */ QStringList labels() const { return m_labels; } /** * @see labels */ void addLabels( const QStringList & labels ); /** * @see labels */ void setLabels( const QStringList & labels ); /** * @see used */ void setUsed( bool used ) { m_bUsed = used; } /** * Used for optimization purposes in determining whether the instruction * has been examined yet (to avoid infinite loops). */ bool isUsed() const { return m_bUsed; } /** * Set by the optimizer to indicate whether this instruction or any of * its outputs overwrite any of the bits of the given register. */ void setRegisterDepends( uchar depends, const Register & reg ) { m_registerDepends.reg(reg) = depends; } /** * @see setOutputsOverwriteWorking */ uchar registerDepends( const Register & reg ) { return m_registerDepends.reg(reg); } /** * Resets the overwrites. */ void resetRegisterDepends() { m_registerDepends.reset(); } /** * @return the input processor state to this instruction. * @see setInputState */ ProcessorState inputState() const { return m_inputState; } /** * @return the output processor state from this instruction. * @see generateLinksAndStates. */ ProcessorState outputState() const { return m_outputState; } /** * Only applicable to Instructions that refer to a file. */ Register file() const { return m_file; } /** * Only applicable to Instructions that refer to a bit (such as BCF). */ RegisterBit bit() const { return m_bit; } /** * Only applicable to instructions that refer to a literal (such as * XORLW). */ uchar literal() const { return m_literal; } /** * Applicable only to instructions that save a result to working or file * depending on the destination bit. */ Register outputReg() const { return (m_dest == 0) ? Register::WORKING : m_file; } /** * Applicable only to instructions that use the destination flag. */ unsigned dest() const { return m_dest; } protected: /** * This function is provided for convenience; it creates links to the * first or second instructions after this one, depending on the value * of firstOutput and secondOutput. * @see generateOutputLinks */ void makeOutputLinks( Code::iterator current, bool firstOutput = true, bool secondOutput = false ); /** * This function is provided for instructions that jump to a label (i.e. * call and goto). */ void makeLabelOutputLink( const QString & label ); RegisterDepends m_registerDepends; bool m_bInputStateChanged; bool m_bUsed; bool m_bPositionAffectsBranching; InstructionList m_inputLinks; InstructionList m_outputLinks; QStringList m_labels; Code * m_pCode; // Commonly needed member variables for assembly instructions Register m_file; RegisterBit m_bit; QString m_raw; // Used by source code, raw asm, etc uchar m_literal; unsigned m_dest:1; // is 0 (W) or 1 (file). ProcessorState m_inputState; ProcessorState m_outputState; private: // Disable copy constructor and operator= Instruction( const Instruction & ); Instruction &operator=( const Instruction & ); }; //BEGIN Byte-Oriented File Register Operations class Instr_addwf : public Instruction { public: Instr_addwf( const Register & file, int dest ) { m_file = file; m_dest = dest; } QString code() const override; void generateLinksAndStates( Code::iterator current ) override; ProcessorBehaviour behaviour() const override; AssemblyType assemblyType() const override { return FileOriented; } }; class Instr_andwf : public Instruction { public: Instr_andwf( const Register & file, int dest ) { m_file = file; m_dest = dest; } QString code() const override; void generateLinksAndStates( Code::iterator current ) override; ProcessorBehaviour behaviour() const override; AssemblyType assemblyType() const override { return FileOriented; } }; class Instr_clrf : public Instruction { public: Instr_clrf( const Register & file ) { m_file = file; m_dest = 1; } QString code() const override; void generateLinksAndStates( Code::iterator current ) override; ProcessorBehaviour behaviour() const override; AssemblyType assemblyType() const override { return FileOriented; } }; //TODO CLRW //TODO COMF class Instr_decf : public Instruction { public: Instr_decf( const Register & file, int dest ) { m_file = file; m_dest = dest; } QString code() const override; void generateLinksAndStates( Code::iterator current ) override; ProcessorBehaviour behaviour() const override; AssemblyType assemblyType() const override { return FileOriented; } }; class Instr_decfsz : public Instruction { public: Instr_decfsz( const Register & file, int dest ) { m_file = file; m_dest = dest; } QString code() const override; void generateLinksAndStates( Code::iterator current ) override; ProcessorBehaviour behaviour() const override; AssemblyType assemblyType() const override { return FileOriented; } }; class Instr_incf : public Instruction { public: Instr_incf( const Register & file, int dest ) { m_file = file; m_dest = dest; } QString code() const override; void generateLinksAndStates( Code::iterator current ) override; ProcessorBehaviour behaviour() const override; AssemblyType assemblyType() const override { return FileOriented; } }; //TODO INCFSZ class Instr_iorwf : public Instruction { public: Instr_iorwf( const Register & file, int dest ) { m_file = file; m_dest = dest; } QString code() const override; void generateLinksAndStates( Code::iterator current ) override; ProcessorBehaviour behaviour() const override; AssemblyType assemblyType() const override { return FileOriented; } }; class Instr_movf : public Instruction { public: Instr_movf( const Register & file, int dest ) { m_file = file; m_dest = dest; } QString code() const override; void generateLinksAndStates( Code::iterator current ) override; ProcessorBehaviour behaviour() const override; AssemblyType assemblyType() const override { return FileOriented; } }; class Instr_movwf : public Instruction { public: Instr_movwf( const Register & file ) { m_file = file; m_dest = 1; } QString code() const override; void generateLinksAndStates( Code::iterator current ) override; ProcessorBehaviour behaviour() const override; AssemblyType assemblyType() const override { return FileOriented; } }; //TODO NOP class Instr_rlf : public Instruction { public: Instr_rlf( const Register & file, int dest ) { m_file = file; m_dest = dest; } QString code() const override; void generateLinksAndStates( Code::iterator current ) override; ProcessorBehaviour behaviour() const override; AssemblyType assemblyType() const override { return FileOriented; } }; class Instr_rrf : public Instruction { public: Instr_rrf( const Register & file, int dest ) { m_file = file; m_dest = dest; } QString code() const override; void generateLinksAndStates( Code::iterator current ) override; ProcessorBehaviour behaviour() const override; AssemblyType assemblyType() const override { return FileOriented; } }; class Instr_subwf : public Instruction { public: Instr_subwf( const Register & file, int dest ) { m_file = file; m_dest = dest; } QString code() const override; void generateLinksAndStates( Code::iterator current ) override; ProcessorBehaviour behaviour() const override; AssemblyType assemblyType() const override { return FileOriented; } }; class Instr_swapf : public Instruction { public: Instr_swapf( const Register & file, int dest ) { m_file = file; m_dest = dest; } QString code() const override; void generateLinksAndStates( Code::iterator current ) override; ProcessorBehaviour behaviour() const override; AssemblyType assemblyType() const override { return FileOriented; } }; class Instr_xorwf : public Instruction { public: Instr_xorwf( const Register & file, int dest ) { m_file = file; m_dest = dest; } QString code() const override; void generateLinksAndStates( Code::iterator current ) override; ProcessorBehaviour behaviour() const override; AssemblyType assemblyType() const override { return FileOriented; } }; //END Byte-Oriented File Register Operations //BEGIN Bit-Oriented File Register Operations class Instr_bcf : public Instruction { public: Instr_bcf( const Register & file, const RegisterBit & bit ) { m_file = file; m_bit = bit; } QString code() const override; void generateLinksAndStates( Code::iterator current ) override; ProcessorBehaviour behaviour() const override; AssemblyType assemblyType() const override { return BitOriented; } }; class Instr_bsf : public Instruction { public: Instr_bsf( const Register & file, const RegisterBit & bit ) { m_file = file; m_bit = bit; } QString code() const override; void generateLinksAndStates( Code::iterator current ) override; ProcessorBehaviour behaviour() const override; AssemblyType assemblyType() const override { return BitOriented; } }; class Instr_btfsc : public Instruction { public: Instr_btfsc( const Register & file, const RegisterBit & bit ) { m_file = file; m_bit = bit; } QString code() const override; void generateLinksAndStates( Code::iterator current ) override; ProcessorBehaviour behaviour() const override; AssemblyType assemblyType() const override { return Other; } }; class Instr_btfss : public Instruction { public: Instr_btfss( const Register & file, const RegisterBit & bit ) { m_file = file; m_bit = bit; } QString code() const override; void generateLinksAndStates( Code::iterator current ) override; ProcessorBehaviour behaviour() const override; AssemblyType assemblyType() const override { return Other; } }; //END Bit-Oriented File Register Operations //BEGIN Literal and Control Operations class Instr_addlw : public Instruction { public: Instr_addlw( int literal ) { m_literal = literal; } QString code() const override; void generateLinksAndStates( Code::iterator current ) override; ProcessorBehaviour behaviour() const override; AssemblyType assemblyType() const override { return WorkingOriented; } }; class Instr_andlw : public Instruction { public: Instr_andlw( int literal ) { m_literal = literal; } QString code() const override; void generateLinksAndStates( Code::iterator current ) override; ProcessorBehaviour behaviour() const override; AssemblyType assemblyType() const override { return WorkingOriented; } }; class Instr_call : public Instruction { public: Instr_call( const QString & label ) { m_label = label; } QString code() const override; void generateLinksAndStates( Code::iterator current ) override; ProcessorBehaviour behaviour() const override; AssemblyType assemblyType() const override { return Other; } /** * Called from Code after all the output links have been generated. The * instruction that is called has its output links followed, and any * returns encountered are linked back to the instruction after this * one. * @param next the instruction after this one which the return points * will be linked to. */ void makeReturnLinks( Instruction * next ); QString label() const { return m_label; } void setLabel( const QString & label ) { m_label = label; } protected: /** * Used by makeReturnLinks. Recursively follows the instruction's output * links, until a return is found - then, link the return point back to * the instruction after this one. Call instructions found while * following the output are ignored. * @param returnPoint the instruction to link back to on finding a * return. */ void linkReturns( Instruction * current, Instruction * returnPoint ); QString m_label; }; //TODO CLRWDT class Instr_goto : public Instruction { public: Instr_goto( const QString & label ) { m_label = label; } QString code() const override; void generateLinksAndStates( Code::iterator current ) override; ProcessorBehaviour behaviour() const override; AssemblyType assemblyType() const override { return Other; } QString label() const { return m_label; } void setLabel( const QString & label ) { m_label = label; } protected: QString m_label; }; class Instr_iorlw : public Instruction { public: Instr_iorlw( int literal ) { m_literal = literal; } QString code() const override; void generateLinksAndStates( Code::iterator current ) override; ProcessorBehaviour behaviour() const override; AssemblyType assemblyType() const override { return WorkingOriented; } }; class Instr_movlw : public Instruction { public: Instr_movlw( int literal ) { m_literal = literal; } QString code() const override; void generateLinksAndStates( Code::iterator current ) override; ProcessorBehaviour behaviour() const override; AssemblyType assemblyType() const override { return WorkingOriented; } }; class Instr_retfie : public Instruction { public: Instr_retfie() {}; QString code() const override; void generateLinksAndStates( Code::iterator current ) override; ProcessorBehaviour behaviour() const override; AssemblyType assemblyType() const override { return Other; } }; class Instr_retlw : public Instruction { public: Instr_retlw( int literal ) { m_literal = literal; } QString code() const override; void generateLinksAndStates( Code::iterator current ) override; ProcessorBehaviour behaviour() const override; AssemblyType assemblyType() const override { return Other; } }; class Instr_return : public Instruction { public: Instr_return() {}; QString code() const override; void generateLinksAndStates( Code::iterator current ) override; ProcessorBehaviour behaviour() const override; AssemblyType assemblyType() const override { return Other; } }; class Instr_sleep : public Instruction { public: Instr_sleep() {}; QString code() const override; void generateLinksAndStates( Code::iterator current ) override; ProcessorBehaviour behaviour() const override; AssemblyType assemblyType() const override { return Other; } }; class Instr_sublw : public Instruction { public: Instr_sublw( int literal ) { m_literal = literal; } QString code() const override; void generateLinksAndStates( Code::iterator current ) override; ProcessorBehaviour behaviour() const override; AssemblyType assemblyType() const override { return WorkingOriented; } }; class Instr_xorlw : public Instruction { public: Instr_xorlw( int literal ) { m_literal = literal; } QString code() const override; void generateLinksAndStates( Code::iterator current ) override; ProcessorBehaviour behaviour() const override; AssemblyType assemblyType() const override { return WorkingOriented; } }; //END Literal and Control Operations //BEGIN Microbe (non-assembly) Operations class Instr_sourceCode : public Instruction { public: Instr_sourceCode( const QString & source ) { m_raw = source; } QString code() const override; InstructionType type() const override { return Comment; } AssemblyType assemblyType() const override { return None; } }; class Instr_asm : public Instruction { public: Instr_asm( const QString & raw ) { m_raw = raw; } QString code() const override; InstructionType type() const override { return Raw; } AssemblyType assemblyType() const override { return None; } }; // Like Instr_asm, but does not put ;asm {} in, used // for internal things like gpasm directives etc... class Instr_raw : public Instruction { public: Instr_raw( const QString & raw ) { m_raw = raw; } QString code() const override; InstructionType type() const override { return Raw; } AssemblyType assemblyType() const override { return None; } }; //END Microbe (non-assembly) Operations #endif diff --git a/microbe/microbe.cpp b/microbe/microbe.cpp index 0ee8bc72..86ef6c95 100644 --- a/microbe/microbe.cpp +++ b/microbe/microbe.cpp @@ -1,541 +1,541 @@ /*************************************************************************** * Copyright (C) 2004-2005 by Daniel Clarke daniel.jc@gmail.com * * Copyright (C) 2005 by David Saxton * * * * 24-04-2007 * * Modified to add pic 16f877,16f627 and 16f628 * * by george john george@space-kerala.org * * supported by SPACE www.space-kerala.org * * * * 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., * * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * ***************************************************************************/ #include "instruction.h" #include "microbe.h" #include "parser.h" #include "optimizer.h" #include "pic14.h" #include -#include -#include +#include +#include #include using namespace std; //BEGIN class Microbe Microbe::Microbe() { m_maxDelaySubroutine = PIC14::Delay_None; m_dest = 0; m_uniqueLabel = 0; // Hardwired constants m_aliasList["true"] = "1"; m_aliasList["false"] = "0"; // Things starting with b are reserved by gpasm (for binary numbers) m_aliasList["b"] = "_b"; //BEGIN Keypad values int bv[4][6] = { { 1, 2, 3, 10, 14, 18 }, { 4, 5, 6, 11, 15, 19 }, { 7, 8, 9, 12, 16, 20 }, { 253, 0, 254, 13, 17, 21 } }; for ( unsigned row = 0; row < 4; ++row ) { for ( unsigned col = 0; col < 6; ++col ) { m_aliasList[ QString("Keypad_%1_%2").arg(row+1).arg(col+1) ] = QString::number( bv[row][col] ); } } m_aliasList[ "Keypad_None" ] = "0xff"; //END Keypad values } Microbe::~Microbe() { } QString Microbe::compile( const QString & url, bool optimize ) { QFile file( url ); if( file.open( QIODevice::ReadOnly ) ) { QTextStream stream(&file); unsigned line = 0; while( !stream.atEnd() ) m_program += SourceLine( stream.readLine(), url, line++ ); file.close(); simplifyProgram(); } else { m_errorReport += i18n("Could not open file '%1'\n", url); return nullptr; } Parser parser(this); // Extract the PIC ID if ( !m_program.isEmpty() ) { m_picType = PIC14::toType( m_program[0].text() ); m_program.erase( m_program.begin() ); } PIC14 * pic = makePic(); if ( !pic ) return nullptr; Code * code = parser.parse( m_program ); pic->setCode( code ); pic->addCommonFunctions( (PIC14::DelaySubroutine)m_maxDelaySubroutine ); pic->postCompileConstruct( m_usedInterrupts ); code->postCompileConstruct(); if ( optimize ) { Optimizer opt; opt.optimize( code ); } return code->generateCode( pic ); } PIC14 * Microbe::makePic() { return new PIC14( this, (PIC14::Type)m_picType ); } void Microbe::simplifyProgram() { SourceLineList simplified; enum CommentType { None, SingleLine, MultiLine }; CommentType commentType = None; SourceLineList::const_iterator end = m_program.end(); for ( SourceLineList::const_iterator it = m_program.begin(); it != end; ++it ) { QString code = (*it).text(); QString simplifiedLine; if ( commentType == SingleLine ) commentType = None; unsigned l = code.length(); for ( unsigned i = 0; i < l; ++i ) { QChar c = code[i]; const char cc = c.toLatin1(); switch ( cc ) { case '/': // Look for start of comments in form "//" and "/*" if ( commentType == None && (i+1 < l) ) { if ( code[i+1] == '/' ) { commentType = SingleLine; i++; } else if ( code[i+1] == '*' ) { commentType = MultiLine; i++; } } break; case '*': // Look for end of comments in form "*/" if ( commentType == MultiLine && (i+1 < l) && code[i+1] == '/' ) { i++; commentType = None; continue; } break; case '{': case '}': // Put braces on separate lines if ( commentType != None ) break; simplified << SourceLine( simplifiedLine.simplified(), (*it).url(), (*it).line() ); simplified << SourceLine( c, (*it).url(), (*it).line() ); simplifiedLine = ""; continue; } if ( commentType == None ) simplifiedLine += c; } simplified << SourceLine( simplifiedLine.simplified(), (*it).url(), (*it).line() ); } m_program.clear(); end = simplified.end(); for ( SourceLineList::const_iterator it = simplified.begin(); it != end; ++it ) { if ( !(*it).text().isEmpty() ) m_program << *it; } } void Microbe::compileError( MistakeType type, const QString & context, const SourceLine & sourceLine ) { QString message; switch (type) { case UnknownStatement: message = i18n("Unknown statement"); break; case InvalidPort: message = i18n("Port '%1' is not supported by target PIC", context); break; case UnassignedPin: message = i18n("Pin identifier was not followed by '='"); break; case NonHighLowPinState: message = i18n("Pin state can only be 'high' or 'low'"); break; case UnassignedPort: message = i18n("Invalid token '%1'. Port identifier should be followed by '='", context); break; case UnexpectedStatementBeforeBracket: message = i18n("Unexpected statement before '{'"); break; case MismatchedBrackets: message = i18n("Mismatched brackets in expression '%1'", context); break; case InvalidEquals: message = i18n("Invalid '=' found in expression"); break; case ReservedKeyword: message = i18n("Reserved keyword '%1' cannot be a variable name.", context); break; case ConsecutiveOperators: message = i18n("Nothing between operators"); break; case MissingOperator: message = i18n("Missing operator or space in operand"); break; case UnknownVariable: if ( context.isEmpty() ) message = i18n("Unknown variable"); else message = i18n("Unknown variable '%1'", context); break; case UnopenableInclude: message = i18n("Could not open include file '%1'", context); break; case DivisionByZero: message = i18n("Division by zero"); break; case NumberTooBig: message = i18n("Number too big"); break; case NonConstantStep: message = i18n("Step can only be a constant expression"); break; case NonConstantDelay: message = i18n("Delay must be a positive constant value"); break; case HighLowExpected: message = i18n("'high' or 'low' expected after pin expression '%1'", context); break; case InvalidComparison: message = i18n("Comparison operator in '%1' is not recognized"); break; case SubBeforeEnd: message = i18n("Subroutine definition before end of program"); break; case InterruptBeforeEnd: message = i18n("Interrupt routine definition before end of program"); break; case LabelExpected: message = i18n("Label expected"); break; case TooManyTokens: message = i18n("Extra tokens at end of line"); break; case FixedStringExpected: message = i18n("Expected '%1'", context); break; case PinListExpected: message = i18n("Pin list expected"); break; case AliasRedefined: message = i18n("Alias already defined"); break; case InvalidInterrupt: message = i18n("Interrupt type not supported by target PIC"); break; case InterruptRedefined: message = i18n("Interrupt already defined"); break; case ReadOnlyVariable: message = i18n("Variable '%1' is read only", context); break; case WriteOnlyVariable: message = i18n("Variable '%1' is write only", context); break; case InvalidPinMapSize: message = i18n("Invalid pin list size"); break; case VariableRedefined: message = i18n("Variable '%1' is already defined", context); break; case InvalidVariableName: message = i18n("'%1' is not a valid variable name", context); break; case VariableExpected: message = i18n("Variable expected"); break; case NameExpected: message = i18n("Name expected"); break; } m_errorReport += QString("%1:%2:Error [%3] %4\n") .arg( sourceLine.url() ) .arg( sourceLine.line()+1 ) .arg( type ) .arg( message ); } bool Microbe::isValidVariableName( const QString & variableName) { //*****modified checking is included for preventing the uses of registername as varable name***** //Prevent usage of register/port name as varable if (/*PORT-NAME*/ variableName == "PORTA" || variableName == "PORTB" || variableName == "PORTC" || variableName == "PORTD" || variableName == "PORTE" /*TRIS-NAME*/ || variableName == "TRISA" || variableName == "TRISB" || variableName == "TRISC" || variableName == "TRISD" || variableName == "TRISE" /**REGISTER-NAME**/ || variableName == "TMR0" || variableName == "PCL" || variableName == "STATUS" || variableName == "FSR" || variableName == "PCLATH" || variableName == "INTCON" || variableName == "PIR1" || variableName == "PIR2" || variableName == "TMR1L" || variableName == "TMR1H" || variableName == "T1CON" || variableName == "TMR2" || variableName == "T2CON" || variableName == "SSPBUF" || variableName == "SSPCON" || variableName == "CCPR1L" || variableName == "CCPR1H" || variableName == "CCP1CON" || variableName == "RCSTA" || variableName == "TXREG" || variableName == "RCREG" || variableName == "CCPR2L" || variableName == "CCPR2H" || variableName == "CCP2CON" || variableName == "ADRESH" || variableName == "ADCON0" /*bank0ends*/ || variableName == "OPTION_REG" || variableName == "PIE1" || variableName == "PIE2" || variableName == "PCON" || variableName == "SSPCON2" || variableName == "PR2" || variableName == "SSPADD" || variableName == "SSPSTAT" || variableName == "TXSTA" || variableName == "SPBRG" || variableName == "ADRESL" || variableName == "ADCON1" /*bank1ends*/ || variableName == "EEDATA " || variableName == "EEADR" || variableName == "EEDATH" || variableName == "EEADRH" /*bank2ends*/ || variableName == "EECON1" || variableName == "EECON2" /*bank3ends*/ ) return false; //****************************modification ends******************************* if ( variableName.isEmpty() ) return false; if ( !variableName[0].isLetter() && variableName[0] != '_' ) return false; for ( int i = 1; i < variableName.length(); ++i ) { if ( !variableName[i].isLetterOrNumber() && variableName[i] != '_' ) return false; } return true; } void Microbe::addVariable( const Variable & variable ) { if ( variable.type() == Variable::invalidType ) return; if ( !isVariableKnown( variable.name() ) ) m_variables << variable; } Variable Microbe::variable( const QString & name ) const { VariableList::const_iterator end = m_variables.end(); for ( VariableList::const_iterator it = m_variables.begin(); it != end; ++it ) { if ( (*it).name() == name ) return *it; } return Variable(); } bool Microbe::isVariableKnown( const QString & name ) const { return variable(name).type() != Variable::invalidType; } void Microbe::addDelayRoutineWanted( unsigned routine ) { if ( m_maxDelaySubroutine < routine ) m_maxDelaySubroutine = routine; } void Microbe::addAlias( const QString & name, const QString & dest ) { m_aliasList[name] = dest; } QString Microbe::alias( const QString & alias ) const { // If the string is an alias, return the real string, // otherwise just return the alias as that is the real string. AliasMap::const_iterator it = m_aliasList.find(alias); if ( it != m_aliasList.constEnd() ) return it.value(); return alias; } void Microbe::setInterruptUsed(const QString &interruptName) { // Don't add it again if it is already in the list if ( m_usedInterrupts.contains( interruptName ) ) return; m_usedInterrupts.append(interruptName); } bool Microbe::isInterruptUsed( const QString & interruptName ) { return m_usedInterrupts.contains( interruptName ); } QString Microbe::dest() const { return QString("__op%1").arg(m_dest); } void Microbe::incDest() { m_dest++; // if ( ++m_dest > m_highestDest ) // m_highestDest = m_dest; } void Microbe::decDest() { m_dest--; } void Microbe::resetDest() { m_dest = 0; } //END class Microbe //BEGIN class SourceLine SourceLine::SourceLine( const QString & text, const QString & url, int line ) { m_text = text; m_url = url; m_line = line; } SourceLine::SourceLine() { m_line = -1; } QStringList SourceLine::toStringList( const SourceLineList & lines ) { QStringList joined; SourceLineList::const_iterator end = lines.end(); for ( SourceLineList::const_iterator it = lines.begin(); it != end; ++it ) joined << (*it).text(); return joined; } //END class SourceLine diff --git a/microbe/microbe.h b/microbe/microbe.h index 3748bbaf..0b067f6a 100644 --- a/microbe/microbe.h +++ b/microbe/microbe.h @@ -1,254 +1,254 @@ /*************************************************************************** * Copyright (C) 2004-2005 by Daniel Clarke * * daniel.jc@gmail.com * * * * 24-04-2007 * * Modified to add pic 16f877,16f627 and 16f628 * * by george john george@space-kerala.org * * supported by SPACE www.space-kerala.org * * * * 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., * * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * ***************************************************************************/ #ifndef MICROBE_H #define MICROBE_H #include #include // #include -#include -#include -#include +#include +#include +#include class QString; class BTreeBase; class BTreeNode; class Code; class PIC14; class PortPin; typedef QList PortPinList; typedef QList VariableList; typedef QMap AliasMap; enum ExprType { unset = 1, working = 2, number = 3, variable = 4, extpin = 5, keypad = 6 }; class SourceLine; typedef QList SourceLineList; /** Represents a source line, with the convention of line number starting at zero. @author David Saxton */ class SourceLine { public: /** * The QList template requires a default constructor - calling this * though creates an invalid SourceLine with line() returning -1. So * this constructor should never be used. */ SourceLine(); SourceLine( const QString & text, const QString & url, int line ); QString text() const { return m_text; } QString url() const { return m_url; } int line() const { return m_line; } /** * Extracts the text from each SourceLine and adds it to the * returned QStringList. */ static QStringList toStringList( const SourceLineList & lines ); protected: QString m_text; QString m_url; int m_line; }; /** @author Daniel Clarke @author David Saxton */ class Microbe { public: Microbe(); ~Microbe(); enum MistakeType { UnknownStatement = 1, InvalidPort = 2, UnassignedPin = 3, // pin identifier without an "= something" NonHighLowPinState = 4, UnassignedPort = 5, // port identifier without an "= something" UnexpectedStatementBeforeBracket = 6, MismatchedBrackets = 7, InvalidEquals = 8, ReservedKeyword = 9, ConsecutiveOperators = 10, MissingOperator = 11, UnknownVariable = 12, UnopenableInclude = 16, DivisionByZero = 17, NumberTooBig = 18, NonConstantStep = 19, NonConstantDelay = 20, HighLowExpected = 21, InvalidComparison = 22, SubBeforeEnd = 23, LabelExpected = 24, TooManyTokens = 25, FixedStringExpected = 26, PinListExpected = 27, AliasRedefined = 28, InvalidInterrupt = 29, InterruptRedefined = 30, InterruptBeforeEnd = 31, ReadOnlyVariable = 32, WriteOnlyVariable = 33, InvalidPinMapSize = 34, VariableRedefined = 35, InvalidVariableName = 36, VariableExpected = 40, NameExpected = 41 }; /** * Returns a list of errors occurred during compilation, intended for * outputting to stderr. */ QString errorReport() const { return m_errorReport; } /** * Call this to compile the given code. This serves as the top level of * recursion as it performs initialisation of things, to recurse at * levels use parseUsingChild(), or create your own Parser. * @param url is used for reporting errors */ QString compile( const QString & url, bool optimize ); /** * Adds the given compiler error at the file line number to the * compilation report. */ void compileError( MistakeType type, const QString & context, const SourceLine & sourceLine ); /** * This is for generating unique numbers for computer generated labels. */ QString uniqueLabel() { return QString("__%1").arg(m_uniqueLabel++); } /** * If alias is an alias for something then it returns that something, * otherwise it just returns alias (which in that case is not an alias!) */ QString alias( const QString & alias ) const; /** * Aliases the name to the dest. */ void addAlias( const QString & name, const QString & dest ); /** * Tell Microbe that a minimum of the given delay routine needs to be * created. * @see PIC14::DelaySubroutine * @param routine - DelaySubroutine enum, higher is more priority */ void addDelayRoutineWanted( unsigned routine ); /** * Makes a new PIC assembly object, based on the PIC string that the * user has given in the source. */ PIC14 * makePic(); /** * Add the interrupt as being used, i.e. make sure there is one and only * one occurance of its name in m_usedInterrupts. */ void setInterruptUsed( const QString & interruptName ); /** * @returns whether the given interrupt has already been used. */ bool isInterruptUsed( const QString & interruptName ); /** * @returns whether the variable name is valid. */ static bool isValidVariableName( const QString & variableName ); /** * Appends the given variable name to the variable list. */ void addVariable( const Variable & variable ); /** * @returns the variable with the given name, or one of invalidType if * no such variable exists. */ Variable variable( const QString & variableName ) const; /** * @returns whether the variable has been declared yet. */ bool isVariableKnown( const QString & variableName ) const; /** * This is used as a temporary variable while evaluating an expression. */ QString dest() const; void incDest(); void decDest(); void resetDest(); protected: /** * Strips comments from m_program, simplifies the white space in each line, * puts braces on separate lines, and then removes any blank lines. */ void simplifyProgram(); QStringList m_usedInterrupts; SourceLineList m_program; QString m_errorReport; int m_uniqueLabel; VariableList m_variables; int m_dest; unsigned m_maxDelaySubroutine; /** * Keeps a list of aliases that have been created which maps the key as * the alias text to the data which is the thing being aliased, so that * something can be aliased to two different things. e.g. * alias ken bob * alias mary bob */ QMap m_aliasList; /** * Once the child parser has found it, this is set to the pic type * string found in the source file. The pic type directive must be * the first thing in the microbe program, before even includes. * @see PIC14::Type */ int m_picType; }; #endif diff --git a/microbe/optimizer.cpp b/microbe/optimizer.cpp index 7ccfce4b..4de8407e 100644 --- a/microbe/optimizer.cpp +++ b/microbe/optimizer.cpp @@ -1,524 +1,525 @@ /*************************************************************************** * Copyright (C) 2005 by David Saxton * * david@bluehaze.org * * * * 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 "instruction.h" #include "optimizer.h" -#include #include +#include + #include #include using namespace std; QString binary( uchar val ) { QString bin = QString::number( val, 2 ); QString pad; pad.fill( '0', 8-bin.length() ); return pad + bin; } Optimizer::Optimizer() { m_pCode = nullptr; } Optimizer::~Optimizer() { } void Optimizer::optimize( Code * code ) { // return; m_pCode = code; const int maxIterations = 10000; // selected randomly bool changed; int iterationNumber = 0; do { ++iterationNumber; changed = false; // Repeatedly generate links and states until // we know as much as possible about the system. propagateLinksAndStates(); // Remove instructions without input links changed |= pruneInstructions(); // Perform optimizations based on processor states changed |= optimizeInstructions(); } while ( changed && (iterationNumber < maxIterations) ); if (iterationNumber >= maxIterations) { QString warnMessage( i18n( "Internal issue: Optimization has not finished in %1 iterations.", iterationNumber) ); //qDebug() << warnMessage; // qDebug or qWarning generates "compilation failed" message in ktechlab std::cout << warnMessage.toStdString(); } } void Optimizer::propagateLinksAndStates() { int count = 0; do { count++; m_pCode->generateLinksAndStates(); } while ( giveInputStates() ); // cout << "count="<end(); for ( Code::iterator it = m_pCode->begin(); it != end; ++it ) { // Now, build up the most specific known processor state from the instructins // that could be executed immediately before this instruction. // This is done by taking the output state of the first input link, and // then reducing it to the greatest common denominator of all the input states. const InstructionList list = (*it)->inputLinks(); if ( list.isEmpty() ) continue; InstructionList::const_iterator inputIt = list.begin(); InstructionList::const_iterator inputsEnd = list.end(); ProcessorState input = (*(inputIt++))->outputState(); while ( inputIt != inputsEnd ) input.merge( (*inputIt++)->outputState() ); if ( !changed ) { ProcessorState before = (*it)->inputState(); bool stateChanged = ( before != input ); changed |= stateChanged; } (*it)->setInputState( input ); } return changed; } bool Optimizer::pruneInstructions() { bool removed = false; //BEGIN remove instructions without any input links Code::iterator it = m_pCode->begin(); Code::iterator end = m_pCode->end(); // Jump past the first instruction, as nothing (necessarily) points to that if ( it != end ) ++it; while ( it != end ) { if ( (*it)->inputLinks().isEmpty() ) { // cout << "Removing: " << (*it)->code() << endl; it.removeAndIncrement(); removed = true; } else ++it; } end = m_pCode->end(); // Reset end as instructions may have been removed //END remove instructions without any input links //BEGIN remove labels without any reference to them // First: build up a list of labels which are referenced QStringList referencedLabels; for ( it = m_pCode->begin(); it != end; ++it ) { if ( Instr_goto * ins = dynamic_cast(*it) ) referencedLabels << ins->label(); else if ( Instr_call * ins = dynamic_cast(*it) ) referencedLabels << ins->label(); } // Now remove labels from instructions that aren't in the referencedLabels list for ( it = m_pCode->begin(); it != end; ++it ) { QStringList labels = (*it)->labels(); for ( QStringList::iterator labelsIt = labels.begin(); labelsIt != labels.end(); ) { if ( !referencedLabels.contains( *labelsIt ) ) { labelsIt = labels.erase( labelsIt ); removed = true; } else ++labelsIt; } (*it)->setLabels( labels); } //END remove labels without any reference to them return removed; } bool Optimizer::optimizeInstructions() { //BEGIN Optimization 1: Concatenate chained GOTOs // We go through the instructions looking for GOTO statements. If we find any, then // we trace back through their input links to any other GOTO statements - any that // are found are then redirected to point to the label that the original GOTO statement // was pointing at. Code::iterator end = m_pCode->end(); for ( Code::iterator it = m_pCode->begin(); it != end; ++it ) { Instr_goto * gotoIns = dynamic_cast(*it); if ( !gotoIns ) continue; if ( redirectGotos( gotoIns, gotoIns->label() ) ) return true; m_pCode->setAllUnused(); } //END Optimization 1: Concatenate chained GOTOs //BEGIN Optimization 2: Remove GOTOs when jumping to the subsequent instruction // Any GOTO instructions that just jump to the next instruction can be removed. for ( Code::iterator it = m_pCode->begin(); it != end; ++it ) { Instruction * next = *(++Code::iterator(it)); Instruction * gotoIns = dynamic_cast(*it); if ( !gotoIns || !next || (gotoIns->outputLinks().first() != next) ) continue; // cout << "Removing: " << gotoIns->code() << endl; it.removeAndIncrement(); return true; } end = m_pCode->end(); //END Optimization 2: Remove GOTOs when jumping to the subsequent instruction //BEGIN Optimization 3: Replace MOVWF with CLRF with W is 0 // We look for MOVWF instructions where the working register holds zero. // We then replace the MOVWf instruction with a CLRF instruction. for ( Code::iterator it = m_pCode->begin(); it != end; ++it ) { Instr_movwf * ins = dynamic_cast(*it); if ( !ins ) continue; ProcessorState inputState = ins->inputState(); RegisterState working = inputState.working; if ( (working.value != 0x0) || (working.known != 0xff) ) continue; // CLRF sets the Z flag of STATUS to 1, but MOVWF does not set any flags. // So we need to check for dependence of the Z flag if we are possibly // changing the flag by replacing the instruction. if ( !(inputState.status.definiteOnes() & (1 << RegisterBit::Z)) ) { // Input state of Z flag is either unknown or low. uchar depends = generateRegisterDepends( *it, Register::STATUS ); if ( depends & (1 << RegisterBit::Z) ) { // Looks like there's some instruction that depends on the zero bit, // and we about potentially about to change it. continue; } } Instr_clrf * instr_clrf = new Instr_clrf( ins->file() ); // cout << "Replacing \""<<(*it)->code()<<"\" with \""<code()<<"\"\n"; it.insertBefore( instr_clrf ); it.removeAndIncrement(); return true; } //END Optimization 3: Replace MOVWF with CLRF with W is 0 //BEGIN Optimization 4: Replace writes to W with MOVLW when value is known // We look for instructions with AssemblyType either WorkingOriented, or FileOriented // and writing to W. Then, if the value is known and there are no instructions that // depend on the STATUS bits set by the instruction, then we replace it with a MOVLW for ( Code::iterator it = m_pCode->begin(); it != end; ++it ) { if ( dynamic_cast(*it) ) { // If we don't catch this condition, we'll end up in an infinite loop, // repeatedly replacing the first MOVLW that we come across. continue; } bool workingOriented = (*it)->assemblyType() == Instruction::WorkingOriented; bool fileOriented = (*it)->assemblyType() == Instruction::FileOriented; if ( !workingOriented && (!fileOriented || ((*it)->dest() != 0)) ) continue; // So can now assume that workingOriented and fileOriented are logical opposites RegisterState outputState = (*it)->outputState().working; if ( outputState.known != 0xff ) continue; ProcessorBehaviour behaviour = (*it)->behaviour(); // MOVLW does not set any STATUS flags, but the instruction that we are replacing // might. So we must check if any of these STATUS flags are depended upon, and if so // only allow replacement if the STATUS flags are not being changed. if ( !canRemove( *it, Register::STATUS, behaviour.reg( Register::STATUS ).indep ) ) continue; Instr_movlw * movlw = new Instr_movlw( outputState.value ); // cout << "Replacing \""<<(*it)->code()<<"\" with \""<code()<<"\"\n"; it.insertBefore( movlw ); it.removeAndIncrement(); return true; } //END Optimization 4: Replace writes to W with MOVLW when value is known //BEGIN Optimization 5: Remove writes to a bit when the value is ignored and overwritten again // We go through the instructions looking for statements that write to a bit (bcf, bsf). // If we find any, then we trace through their output links to see if their value is // overwritten before it is used - and if so, the instruction can be removed. for ( Code::iterator it = m_pCode->begin(); it != end; ++it ) { if ( (*it)->assemblyType() != Instruction::BitOriented ) continue; const Register regSet = (*it)->file(); if ( regSet.affectsExternal() ) continue; uchar bitPos = (*it)->bit().bitPos(); ProcessorState inputState = (*it)->inputState(); ProcessorState outputState = (*it)->outputState(); ProcessorBehaviour behaviour = (*it)->behaviour(); // Are we rewriting over a bit that already has the same value? // (Note this check is just for the bit changing instructions, as there is a similar // check for register changing actions later on when we know which bits care about // being overwritten). if ( inputState.reg( regSet ).known & (1 << bitPos) ) { bool beforeVal = (inputState.reg( regSet ).value & (1 << bitPos)); bool afterVal = (outputState.reg( regSet ).value & (1 << bitPos)); if ( beforeVal == afterVal ) { // cout << "Removing: " << (*it)->code() << endl; it.removeAndIncrement(); return true; } } uchar depends = generateRegisterDepends( *it, regSet ); if ( !(depends & (1 << bitPos)) ) { // Bit is overwritten before being used - so lets remove this instruction :) // cout << "Removing: " << (*it)->code() << endl; it.removeAndIncrement(); return true; } } m_pCode->setAllUnused(); //END Optimization 5: Remove writes to a bit when the value is ignored and overwritten again //BEGIN Optimization 6: Remove writes to a register when the value is ignored and overwritten again // We go through the instructions looking for statements that write to a register (such as MOVLW). // If we find any, then we trace through their output links to see if their value is // overwritten before it is used - and if so, the instruction can be removed. for ( Code::iterator it = m_pCode->begin(); it != end; ++it ) { bool noFile = false; switch ( (*it)->assemblyType() ) { case Instruction::WorkingOriented: noFile = true; // (no break) case Instruction::FileOriented: break; case Instruction::BitOriented: case Instruction::Other: case Instruction::None: continue; } const Register regSet = noFile ? Register( Register::WORKING ) : (*it)->outputReg(); if ( regSet.affectsExternal() ) continue; ProcessorState inputState = (*it)->inputState(); ProcessorState outputState = (*it)->outputState(); ProcessorBehaviour behaviour = (*it)->behaviour(); // All ins_file instructions will affect at most two registers; the // register it is writing to (regSet) and the status register. // In i==0, test regSet // In i==1, test STATUS bool ok = true; for ( unsigned i = 0; i < 2; ++ i) { // If we are testing STATUS, then we assume that the bits changed // are only those that are marked as independent. uchar bitmask = ( i == 1 ) ? behaviour.reg( Register::STATUS ).indep : 0xff; if ( !canRemove( *it, (i == 0) ? regSet : Register::STATUS, bitmask ) ) { ok = false; break; } } if ( !ok ) continue; // Looks like we're free to remove the instruction :); // cout << "Removing: " << (*it)->code() << endl; it.removeAndIncrement(); return true; } m_pCode->setAllUnused(); //END Optimization 6: Remove writes to a register when the value is ignored and overwritten again return false; } bool Optimizer::redirectGotos( Instruction * current, const QString & label ) { if ( current->isUsed() ) return false; current->setUsed( true ); bool changed = false; const InstructionList list = current->inputLinks(); InstructionList::const_iterator end = list.end(); for ( InstructionList::const_iterator it = list.begin(); it != end; ++it ) { Instr_goto * gotoIns = dynamic_cast(*it); if ( !gotoIns || (gotoIns->label() == label) ) continue; // cout << "Redirecting goto to label \"" << label << "\" : " << gotoIns->code() << endl; gotoIns->setLabel( label ); changed = true; } return changed; } uchar Optimizer::generateRegisterDepends( Instruction * current, const Register & reg ) { m_pCode->setAllUnused(); const InstructionList list = current->outputLinks(); InstructionList::const_iterator listEnd = list.end(); uchar depends = 0x0; for ( InstructionList::const_iterator listIt = list.begin(); listIt != listEnd; ++listIt ) depends |= registerDepends( *listIt, reg ); return depends; } uchar Optimizer::registerDepends( Instruction * current, const Register & reg ) { if ( current->isUsed() ) return current->registerDepends( reg ); current->setUsed( true ); uchar depends = 0x0; const InstructionList list = current->outputLinks(); InstructionList::const_iterator end = list.end(); for ( InstructionList::const_iterator it = list.begin(); it != end; ++it ) depends |= registerDepends( *it, reg ); RegisterBehaviour behaviour = current->behaviour().reg( reg ); depends &= ~(behaviour.indep); // Get rid of depend bits that are set in this instruction depends |= behaviour.depends; // And add the ones that are dependent in this instruction current->setRegisterDepends( depends, reg ); return depends; } bool Optimizer::canRemove( Instruction * ins, const Register & reg, uchar bitMask ) { // The bits that are depended upon in the future for this register uchar depends = generateRegisterDepends( ins, reg ); // Only interested in those bits allowed by the bit mask depends &= bitMask; RegisterState inputState = ins->inputState().reg( reg ); RegisterState outputState = ins->outputState().reg( reg ); if ( inputState.unknown() & depends ) { // There's at least one bit whose value is depended on, but is not known before this // instruction is executed. Therefore, it is not safe to remove this instruction. return false; } if ( outputState.unknown() & depends ) { // There's at least one bit whose value is depended on, but is not known after this // instruction is executed. Therefore, it is not safe to remove this instruction. return false; } uchar dependsInput = inputState.value & depends; uchar dependsOutput = outputState.value & depends; if ( dependsInput != dependsOutput ) { // At least one bit whose value is depended upon was changed. return false; } return true; } diff --git a/microbe/parser.cpp b/microbe/parser.cpp index 2fc0efdb..f3eae355 100644 --- a/microbe/parser.cpp +++ b/microbe/parser.cpp @@ -1,1054 +1,1054 @@ /*************************************************************************** * Copyright (C) 2004-2005 by Daniel Clarke * * daniel.jc@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. * * * * 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., * * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * ***************************************************************************/ #include "btreebase.h" #include "expression.h" #include "instruction.h" #include "parser.h" #include "pic14.h" #include "traverser.h" #include -#include -#include -#include -#include +#include +#include +#include +#include #include using namespace std; //BEGIN class Parser Parser::Parser( Microbe * _mb ) { m_code = nullptr; m_pPic = nullptr; mb = _mb; // Set up statement definitions. StatementDefinition definition; definition.append( Field(Field::Label, "label") ); m_definitionMap["goto"] = definition; definition.clear(); definition.append( Field(Field::Label, "label") ); m_definitionMap["call"] = definition; definition.clear(); definition.append( Field(Field::Expression, "expression") ); definition.append( Field(Field::Code, "code") ); m_definitionMap["while"] = definition; definition.clear(); m_definitionMap["end"] = definition; definition.clear(); definition.append( Field(Field::Label, "label") ); definition.append( Field(Field::Code, "code") ); // For backwards compataibility m_definitionMap["sub"] = definition; m_definitionMap["subroutine"] = definition; definition.clear(); definition.append( Field(Field::Label, "label") ); definition.append( Field(Field::Code, "code") ); m_definitionMap["interrupt"] = definition; definition.clear(); definition.append( Field(Field::Label, "alias") ); definition.append( Field(Field::Label, "dest") ); m_definitionMap["alias"] = definition; definition.clear(); definition.append( Field(Field::Expression, "expression") ); definition.append( Field(Field::FixedString, nullptr, "then", true) ); definition.append( Field(Field::Code, "ifCode") ); definition.append( Field(Field::Newline) ); definition.append( Field(Field::FixedString, nullptr, "else", false) ); definition.append( Field(Field::Code, "elseCode") ); m_definitionMap["if"] = definition; definition.clear(); definition.append( Field(Field::Expression, "initExpression") ); definition.append( Field(Field::FixedString, nullptr, "to", true) ); definition.append( Field(Field::Expression, "toExpression") ); definition.append( Field(Field::FixedString, nullptr, "step", false) ); definition.append( Field(Field::Expression, "stepExpression") ); definition.append( Field(Field::Code, "code") ); m_definitionMap["for"] = definition; definition.clear(); definition.append( Field(Field::Variable, "variable") ); m_definitionMap["decrement"] = definition; definition.clear(); definition.append( Field(Field::Variable, "variable") ); m_definitionMap["increment"] = definition; definition.clear(); definition.append( Field(Field::Variable, "variable") ); m_definitionMap["rotateleft"] = definition; definition.clear(); definition.append( Field(Field::Variable, "variable") ); m_definitionMap["rotateright"] = definition; definition.clear(); definition.append( Field(Field::Code, "code") ); m_definitionMap["asm"] = definition; definition.clear(); definition.append( Field(Field::Expression, "expression") ); m_definitionMap["delay"] = definition; definition.clear(); definition.append( Field(Field::Code, "code") ); definition.append( Field(Field::Newline) ); definition.append( Field(Field::FixedString, nullptr, "until", true) ); definition.append( Field(Field::Expression, "expression") ); m_definitionMap["repeat"] = definition; definition.clear(); definition.append( Field(Field::Name, "name") ); definition.append( Field(Field::PinList, "pinlist") ); m_definitionMap["sevenseg"] = definition; definition.clear(); definition.append( Field(Field::Name, "name") ); definition.append( Field(Field::PinList, "pinlist") ); m_definitionMap["keypad"] = definition; definition.clear(); } Parser::~Parser() { } Parser* Parser::createChildParser() { Parser * parser = new Parser( mb ); return parser; } Code * Parser::parseWithChild( const SourceLineList & lines ) { Parser * p = createChildParser(); Code * code = p->parse(lines); delete p; return code; } Code * Parser::parse( const SourceLineList & lines ) { StatementList sList; m_pPic = mb->makePic(); m_code = new Code(); m_pPic->setCode( m_code ); m_pPic->setParser(this); m_bPassedEnd = false; /* First pass ========== Here we go through the code making each line into a statement object, looking out for braced code as we go, if we find it then we put then we make attach the braced code to the statment. */ SourceLineList::const_iterator end = lines.end(); for ( SourceLineList::const_iterator slit = lines.begin(); slit != end; ++slit ) { Statement s; s.content = *slit; // Check to see if the line after next is a brace SourceLineList::const_iterator previous = slit; if ( (++slit != end) && (*slit).text() == "{" ) s.bracedCode = getBracedCode( & slit, end ); else slit = previous; if ( !s.text().isEmpty() ) sList.append(s); } mb->resetDest(); for( StatementList::Iterator sit = sList.begin(); sit != sList.end(); ++sit ) { m_currentSourceLine = (*sit).content; QString line = (*sit).text(); QString command; // e.g. "delay", "for", "subroutine", "increment", etc { int spacepos = line.indexOf(' '); if ( spacepos >= 0 ) command = line.left( spacepos ); else command = line; } OutputFieldMap fieldMap; if ( (*sit).content.line() >= 0 ) m_code->append( new Instr_sourceCode( QString("#MSRC\t%1; %2\t%3").arg( (*sit).content.line() + 1 ).arg( (*sit).content.url() ).arg( (*sit).content.text() ) )); bool showBracesInSource = (*sit).hasBracedCode(); if ( showBracesInSource ) m_code->append(new Instr_sourceCode("{")); // Use the first token in the line to look up the statement type DefinitionMap::Iterator dmit = m_definitionMap.find(command); if(dmit == m_definitionMap.end()) { if( !processAssignment( (*sit).text() ) ) { // Not an assignement, maybe a label if( (*sit).isLabel() ) { QString label = (*sit).text().left( (*sit).text().length() - 1 ); ///TODO sanity check label name and then do error like "Bad label" m_pPic->Slabel( label ); } else mistake( Microbe::Microbe::UnknownStatement ); } continue; // Give up on the current statement } StatementDefinition definition = dmit.value(); // Start at the first white space character following the statement name int newPosition = 0; int position = command.length() + 1; // Temporaries for use inside the switch Field nextField; Statement nextStatement; bool errorInLine = false; bool finishLine = false; for( StatementDefinition::Iterator sdit = definition.begin(); sdit != definition.end(); ++sdit ) { // If there is an error, or we have finished the statement, // the stop. If we are at the end of a line in a multiline, then // break to fall through to the next line if( errorInLine || finishLine) break; Field field = (*sdit); QString token; bool saveToken = false; bool saveBraced = false; bool saveSingleLine = false; switch(field.type()) { case (Field::Label): case (Field::Variable): case (Field::Name): { newPosition = line.indexOf( ' ', position ); if(position == newPosition) { newPosition = -1; token = line.mid(position); } else token = line.mid(position, newPosition - position); if( token.isEmpty() ) { if(field.type() == Field::Label) mistake( Microbe::Microbe::LabelExpected ); else if (field.type() == Field::Variable) mistake( Microbe::VariableExpected ); else // field.type() == Field::Name mistake( Microbe::NameExpected ); errorInLine = true; continue; } position = newPosition; saveToken = true; break; } case (Field::Expression): { // This is slightly different, as there is nothing // in particular that delimits an expression, we just have to // look at what comes next and hope we can use that. StatementDefinition::Iterator it(sdit); ++it; if( it != definition.end() ) { nextField = (*it); if(nextField.type() == Field::FixedString) newPosition = line.indexOf(QRegExp("\\b" + nextField.string() + "\\b")); // Although code is not neccessarily braced, after an expression it is the only // sensilbe way to have it. else if(nextField.type() == Field::Code) { newPosition = line.indexOf("{"); if(newPosition == -1) newPosition = line.length() + 1; } else if(nextField.type() == Field::Newline) newPosition = line.length()+1; else qDebug() << "Bad statement definition - awkward field type after expression"; } else newPosition = line.length() + 1; if(newPosition == -1) { // Something was missing, we'll just play along for now, // the next iteration will catch whatever was supposed to be there } token = line.mid(position, newPosition - position); position = newPosition; saveToken = true; } break; case (Field::PinList): { // For now, just assume that the list of pins will continue to the end of the tokens. // (we could check until we come across a non-pin, but no command has that format at // the moment). token = line.mid( position + 1 ); position = line.length() + 1; if ( token.isEmpty() ) mistake( Microbe::PinListExpected ); else saveToken = true; break; } case (Field::Code): if ( !(*sit).hasBracedCode() ) { saveSingleLine = true; token = line.mid(position); position = line.length() + 1; } else if( position != -1 && position <= int(line.length()) ) { mistake( Microbe::UnexpectedStatementBeforeBracket ); errorInLine = true; continue; } else { // Because of the way the superstructure parsing works there is no // 'next line' as it were, the braced code is attached to the current line. saveBraced = true; } break; case (Field::FixedString): { // Is the string found, and is it starting in the right place? int stringPosition = line.indexOf(QRegExp("\\b"+field.string()+"\\b")); if( stringPosition != position || stringPosition == -1 ) { if( !field.compulsory() ) { position = -1; // Skip the next field ++sdit; continue; } else { // Otherwise raise an appropriate error mistake( Microbe::FixedStringExpected, field.string() ); errorInLine = true; continue; } } else { position += field.string().length() + 1; } } break; case (Field::Newline): // It looks like the best way to handle this is to just actually // look at the next line, and see if it begins with an expected fixed // string. // Assume there is a next field, it would be silly if there weren't. nextField = *(++StatementDefinition::Iterator(sdit)); if( nextField.type() == Field::FixedString ) { nextStatement = *(++StatementList::Iterator(sit)); newPosition = nextStatement.text().indexOf(QRegExp("\\b" + nextField.string() + "\\b")); if(newPosition != 0) { // If the next field is optional just carry on as nothing happened, // the next line will be processed as a new statement if(!nextField.compulsory()) continue; } position = 0; line = (*(++sit)).text(); m_currentSourceLine = (*sit).content; } break; case (Field::None): // Do nothing break; } if ( saveToken ) fieldMap[field.key()] = OutputField( token ); if ( saveSingleLine ) { SourceLineList list; list << SourceLine( token, nullptr, -1 ); fieldMap[field.key()] = OutputField( list ); } if ( saveBraced ) fieldMap[field.key()] = OutputField( (*sit).bracedCode ); // If position = -1, we have reached the end of the line. } // See if we got to the end of the line, but not all fields had been // processed. if( position != -1 && position <= int(line.length()) ) { mistake( Microbe::TooManyTokens ); errorInLine = true; } if( errorInLine ) continue; // Everything has been parsed up, so send it off for processing. processStatement( command, fieldMap ); if( showBracesInSource ) m_code->append(new Instr_sourceCode("}")); } delete m_pPic; return m_code; } bool Parser::processAssignment(const QString &line) { QStringList tokens = Statement::tokenise(line); // Have to have at least 3 tokens for an assignment; if ( tokens.size() < 3 ) return false; QString firstToken = tokens[0]; firstToken = mb->alias(firstToken); // Well firstly we look to see if it is a known variable. // These can include 'special' variables such as ports // For now, the processor subclass generates ports it self // and puts them in a list for us. // Look for port variables first. if ( firstToken.contains(".") ) { PortPin portPin = m_pPic->toPortPin( firstToken ); // check port is valid if ( portPin.pin() == -1 ) mistake( Microbe::InvalidPort, firstToken ); // more error checking if ( tokens[1] != "=" ) mistake( Microbe::UnassignedPin ); QString state = tokens[2]; if( state == "high" ) m_pPic->Ssetlh( portPin, true ); else if( state == "low" ) m_pPic->Ssetlh( portPin, false ); else mistake( Microbe::NonHighLowPinState ); } // no dots, lets try for just a port name else if( m_pPic->isValidPort( firstToken ) ) { // error checking if ( tokens[1] != "=" ) mistake( Microbe::UnassignedPort, tokens[1] ); Expression( m_pPic, mb, m_currentSourceLine, false ).compileExpression(line.mid(line.indexOf("=")+1)); m_pPic->saveResultToVar( firstToken ); } else if ( m_pPic->isValidTris( firstToken ) ) { if( tokens[1] == "=" ) { Expression( m_pPic, mb, m_currentSourceLine, false ).compileExpression(line.mid(line.indexOf("=")+1)); m_pPic->Stristate(firstToken); } } else { // Is there an assignment? if ( tokens[1] != "=" ) return false; if ( !mb->isValidVariableName( firstToken ) ) { mistake( Microbe::InvalidVariableName, firstToken ); return true; } // Don't care whether or not the variable is new; Microbe will only add it if it // hasn't been defined yet. mb->addVariable( Variable( Variable::charType, firstToken ) ); Expression( m_pPic, mb, m_currentSourceLine, false ).compileExpression(line.mid(line.indexOf("=")+1)); Variable v = mb->variable( firstToken ); switch ( v.type() ) { case Variable::charType: m_pPic->saveResultToVar( v.name() ); break; case Variable::keypadType: mistake( Microbe::ReadOnlyVariable, v.name() ); break; case Variable::sevenSegmentType: m_pPic->SsevenSegment( v ); break; case Variable::invalidType: // Doesn't happen, but include this case to avoid compiler warnings break; } } return true; } SourceLineList Parser::getBracedCode( SourceLineList::const_iterator * it, SourceLineList::const_iterator end ) { // Note: The sourceline list has the braces on separate lines. // This function should only be called when the parser comes across a line that is a brace. assert( (**it).text() == "{" ); SourceLineList braced; // Jump past the first brace unsigned level = 1; ++(*it); for ( ; *it != end; ++(*it) ) { if ( (**it).text() == "{" ) level++; else if ( (**it).text() == "}" ) level--; if ( level == 0 ) return braced; braced << **it; } // TODO Error: mismatched bracing return braced; } void Parser::processStatement( const QString & name, const OutputFieldMap & fieldMap ) { // Name is guaranteed to be something known, the calling // code has taken care of that. Also fieldMap is guaranteed to contain // all required fields. if ( name == "goto" ) m_pPic->Sgoto(fieldMap["label"].string()); else if ( name == "call" ) m_pPic->Scall(fieldMap["label"].string()); else if ( name == "while" ) m_pPic->Swhile( parseWithChild(fieldMap["code"].bracedCode() ), fieldMap["expression"].string() ); else if ( name == "repeat" ) m_pPic->Srepeat( parseWithChild(fieldMap["code"].bracedCode() ), fieldMap["expression"].string() ); else if ( name == "if" ) m_pPic->Sif( parseWithChild(fieldMap["ifCode"].bracedCode() ), parseWithChild(fieldMap["elseCode"].bracedCode() ), fieldMap["expression"].string() ); else if ( name == "sub" || name == "subroutine" ) { if(!m_bPassedEnd) { mistake( Microbe::InterruptBeforeEnd ); } else { m_pPic->Ssubroutine( fieldMap["label"].string(), parseWithChild( fieldMap["code"].bracedCode() ) ); } } else if( name == "interrupt" ) { QString interrupt = fieldMap["label"].string(); if(!m_bPassedEnd) { mistake( Microbe::InterruptBeforeEnd ); } else if( !m_pPic->isValidInterrupt( interrupt ) ) { mistake( Microbe::InvalidInterrupt ); } else if ( mb->isInterruptUsed( interrupt ) ) { mistake( Microbe::InterruptRedefined ); } else { mb->setInterruptUsed( interrupt ); m_pPic->Sinterrupt( interrupt, parseWithChild( fieldMap["code"].bracedCode() ) ); } } else if( name == "end" ) { ///TODO handle end if we are not in the top level m_bPassedEnd = true; m_pPic->Send(); } else if( name == "for" ) { QString step = fieldMap["stepExpression"].string(); bool stepPositive; if( fieldMap["stepExpression"].found() ) { if(step.left(1) == "+") { stepPositive = true; step = step.mid(1).trimmed(); } else if(step.left(1) == "-") { stepPositive = false; step = step.mid(1).trimmed(); } else stepPositive = true; } else { step = "1"; stepPositive = true; } QString variable = fieldMap["initExpression"].string().mid(0,fieldMap["initExpression"].string().indexOf("=")).trimmed(); QString endExpr = variable+ " <= " + fieldMap["toExpression"].string().trimmed(); if( fieldMap["stepExpression"].found() ) { bool isConstant; step = processConstant(step,&isConstant); if( !isConstant ) mistake( Microbe::NonConstantStep ); } SourceLineList tempList; tempList << SourceLine( fieldMap["initExpression"].string(), nullptr, -1 ); m_pPic->Sfor( parseWithChild( fieldMap["code"].bracedCode() ), parseWithChild( tempList ), endExpr, variable, step, stepPositive ); } else if( name == "alias" ) { // It is important to get this the right way round! // The alias should be the key since two aliases could // point to the same name. QString alias = fieldMap["alias"].string().trimmed(); QString dest = fieldMap["dest"].string().trimmed(); // Check to see whether or not we've already aliased it... // if ( mb->alias(alias) != alias ) // mistake( Microbe::AliasRedefined ); // else mb->addAlias( alias, dest ); } else if( name == "increment" ) { QString variableName = fieldMap["variable"].string(); if ( !mb->isVariableKnown( variableName ) ) mistake( Microbe::UnknownVariable ); else if ( !mb->variable( variableName ).isWritable() ) mistake( Microbe::ReadOnlyVariable, variableName ); else m_pPic->SincVar( variableName ); } else if( name == "decrement" ) { QString variableName = fieldMap["variable"].string(); if ( !mb->isVariableKnown( variableName ) ) mistake( Microbe::UnknownVariable ); else if ( !mb->variable( variableName ).isWritable() ) mistake( Microbe::ReadOnlyVariable, variableName ); else m_pPic->SdecVar( variableName ); } else if( name == "rotateleft" ) { QString variableName = fieldMap["variable"].string(); if ( !mb->isVariableKnown( variableName ) ) mistake( Microbe::UnknownVariable ); else if ( !mb->variable( variableName ).isWritable() ) mistake( Microbe::ReadOnlyVariable, variableName ); else m_pPic->SrotlVar( variableName ); } else if( name == "rotateright" ) { QString variableName = fieldMap["variable"].string(); if ( !mb->isVariableKnown( variableName ) ) mistake( Microbe::UnknownVariable ); else if ( !mb->variable( variableName ).isWritable() ) mistake( Microbe::ReadOnlyVariable, variableName ); else m_pPic->SrotrVar( variableName ); } else if( name == "asm" ) { m_pPic->Sasm( SourceLine::toStringList( fieldMap["code"].bracedCode() ).join("\n") ); } else if( name == "delay" ) { // This is one of the rare occasions that the number will be bigger than a byte, // so suppressNumberTooBig must be used bool isConstant; QString delay = processConstant(fieldMap["expression"].string(),&isConstant,true); if (!isConstant) mistake( Microbe::NonConstantDelay ); // else m_pPic->Sdelay( fieldMap["expression"].string(), ""); else { // TODO We should use the "delay" string returned by processConstant - not the expression (as, e.g. 2*3 won't be ok) int length_ms = literalToInt( fieldMap["expression"].string() ); if ( length_ms >= 0 ) m_pPic->Sdelay( length_ms * 1000 ); // Pause the delay length in microseconds else mistake( Microbe::NonConstantDelay ); } } else if ( name == "keypad" || name == "sevenseg" ) { //QStringList pins = QStringList::split( ' ', fieldMap["pinlist"].string() ); QStringList pins = fieldMap["pinlist"].string().split(' ', QString::SkipEmptyParts); QString variableName = fieldMap["name"].string(); if ( mb->isVariableKnown( variableName ) ) { mistake( Microbe::VariableRedefined, variableName ); return; } PortPinList pinList; QStringList::iterator end = pins.end(); for ( QStringList::iterator it = pins.begin(); it != end; ++it ) { PortPin portPin = m_pPic->toPortPin(*it); if ( portPin.pin() == -1 ) { // Invalid port/pin //TODO mistake return; } pinList << portPin; } if ( name == "keypad" ) { Variable v( Variable::keypadType, variableName ); v.setPortPinList( pinList ); mb->addVariable( v ); } else // name == "sevenseg" { if ( pinList.size() != 7 ) mistake( Microbe::InvalidPinMapSize ); else { Variable v( Variable::sevenSegmentType, variableName ); v.setPortPinList( pinList ); mb->addVariable( v ); } } } } void Parser::mistake( Microbe::MistakeType type, const QString & context ) { mb->compileError( type, context, m_currentSourceLine ); } // static function QStringList Statement::tokenise(const QString &line) { QStringList result; QString current; int count = 0; for(int i = 0; i < int(line.length()); i++) { QChar nextChar = line[i]; if( nextChar.isSpace() ) { if( count > 0 ) { result.append(current); current = ""; count = 0; } } else if( nextChar == '=' ) { if( count > 0 ) result.append(current); current = ""; count = 0; result.append("="); } else if( nextChar == '{' ) { if( count > 0 ) result.append(current); current = ""; count = 0; result.append("{"); } else { count++; current.append(nextChar); } } if( count > 0 ) result.append(current); return result; } int Parser::doArithmetic(int lvalue, int rvalue, Expression::Operation op) { switch(op) { case Expression::noop: return 0; case Expression::addition: return lvalue + rvalue; case Expression::subtraction: return lvalue - rvalue; case Expression::multiplication: return lvalue * rvalue; case Expression::division: return lvalue / rvalue; case Expression::exponent: return lvalue ^ rvalue; case Expression::equals: return lvalue == rvalue; case Expression::notequals: return !(lvalue == rvalue); case Expression::bwand: return lvalue & rvalue; case Expression::bwor: return lvalue | rvalue; case Expression::bwxor: return lvalue ^ rvalue; case Expression::bwnot: return !rvalue; case Expression::le: return lvalue <= rvalue; case Expression::ge: return lvalue >= rvalue; case Expression::lt: return lvalue < rvalue; case Expression::gt: return lvalue > rvalue; case Expression::pin: case Expression::notpin: case Expression::function: case Expression::divbyzero: case Expression::read_keypad: // Not applicable actions. break; } return -1; } bool Parser::isLiteral( const QString &text ) { bool ok; literalToInt( text, & ok ); return ok; } /* Literal's in form: -> 321890 -> 021348 -> 0x3C -> b'0100110' -> 0101001b -> h'43A' -> 2Ah Everything else is non-literal... */ int Parser::literalToInt( const QString &literal, bool * ok ) { bool temp; if ( !ok ) ok = & temp; *ok = true; int value = -1; // Note when we use toInt, we don't have to worry about checking // that literal.mid() is convertible, as toInt returns this in ok anyway. // Try binary first, of form b'n...n' if( literal.left(2) == "b'" && literal.right(1) == "'" ) { value = literal.mid(2,literal.length() - 3).toInt(ok,2); return *ok ? value : -1; } // Then try hex of form h'n...n' if( literal.left(2) == "h'" && literal.right(1) == "'" ) { value = literal.mid(2,literal.length() - 3).toInt(ok,16); return *ok ? value : -1; } // otherwise, let QString try and convert it // base 0 == automatic base guessing value = literal.toInt( ok, 0 ); return *ok ? value : -1; } void Parser::compileConditionalExpression( const QString & expression, Code * ifCode, Code * elseCode ) const { ///HACK ///TODO this is a little improper, I don't think we should be using the pic that called us... Expression( m_pPic, mb, m_currentSourceLine, false ).compileConditional(expression,ifCode,elseCode); } QString Parser::processConstant(const QString &expression, bool * isConstant, bool suppressNumberTooBig) const { return Expression( m_pPic, mb, m_currentSourceLine, suppressNumberTooBig ).processConstant(expression, isConstant); } //END class Parser //BEGIN class Field Field::Field() { m_type = None; m_compulsory = false; } Field::Field( Type type, const QString & key ) { m_type = type; m_compulsory = false; m_key = key; } Field::Field( Type type, const QString & key, const QString & string, bool compulsory ) { m_type = type; m_compulsory = compulsory; m_key = key; m_string = string; } //END class Field //BEGIN class OutputField OutputField::OutputField() { m_found = false; } OutputField::OutputField( const SourceLineList & bracedCode ) { m_bracedCode = bracedCode; m_found = true; } OutputField::OutputField( const QString & string/*, int lineNumber*/ ) { m_string = string; m_found = true; } //END class OutputField #if 0 // Second pass else if( firstToken == "include" ) { // only cope with 'sane' strings a.t.m. // e.g. include "filename.extenstion" QString filename = (*sit).content.mid( (*sit).content.find("\"") ).trimmed(); // don't strip whitespace from within quotes as you // can have filenames composed entirely of spaces (kind of weird)... // remove quotes. filename = filename.mid(1); filename = filename.mid(0,filename.length()-1); QFile includeFile(filename); if( includeFile.open(QIODevice::ReadOnly) ) { QTextStream stream( &includeFile ); QStringList includeCode; while( !stream.atEnd() ) { includeCode += stream.readLine(); } ///TODO make includes work //output += parse(includeCode); includeFile.close(); } else mistake( Microbe::UnopenableInclude, filename ); } #endif diff --git a/microbe/parser.h b/microbe/parser.h index 979eb48c..04d1d0b2 100644 --- a/microbe/parser.h +++ b/microbe/parser.h @@ -1,293 +1,293 @@ /*************************************************************************** * Copyright (C) 2004-2005 by Daniel Clarke * * daniel.jc@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. * * * * 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., * * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * ***************************************************************************/ #ifndef PARSER_H #define PARSER_H #include "expression.h" #include "instruction.h" #include "microbe.h" -#include -#include +#include +#include class PIC14; /** @author Daniel Clarke @author David Saxton */ class Statement { public: /** * Is the assembly output generated for this statement. */ InstructionList * code; /** * The original microbe source line. */ SourceLine content; /** * Returns the microbe code from content. */ QString text() const { return content.text(); } /** * If this Statement is for a for loop, then content will contain * something like "for x = 1 to 10", and bracedCode will contain the * source code within (but not including) the braces. */ SourceLineList bracedCode; /** * Just returns whether or not the braced code is empty. */ bool hasBracedCode() const { return !bracedCode.isEmpty(); } /** * This breaks up the line separated by spaces,{,and =/ */ static QStringList tokenise(const QString &line); /** * @see tokenise(const QString &line) */ QStringList tokenise() const { return tokenise( content.text() ); } /** * @returns whether or not the content looks like a label (ends with a * colon). */ bool isLabel() const { return content.text().right(1) == ":"; } }; typedef QList StatementList; /** @author Daniel Clarke @author David Saxton */ class Field { public: enum Type { // String that doesn't change the program logic, but might or might // not need to be there depending on the statement (e.g. "then" in // the if-statement). FixedString, // Label, Variable, Name are all treated similarly (only different // error messages are given). Label, // e.g. in "goto [Label]" Variable, // e.g. in "increment [Variable]" Name, // e.g. in "sevenseg [Name]" // List of strings which should be pin names. PinList, // Braced code. Code, Expression, Newline, None }; /** * Create a Field of type None. */ Field(); /** * Create a Field. */ Field( Type type, const QString & key = nullptr ); /** * Create a Field (this constructor should only be used with * FixedStrings. */ Field( Type type, const QString & key, const QString & string, bool compulsory = true); /** * The type of field expected. */ Type type() const { return m_type; } /** * String data relevant to the field dependent on m_type. */ QString string() const { return m_string; } /** * The key in which the found token will be attached to * in the output map. If it is an empty string, then the field will be * processed but not put in the output, effectively ignoring it. */ QString key() const { return m_key; } /** * Only FixedStrings may be compulsory, that is the only type that can * actually have its presence checked. * This flag is set to indicate that no error should be rasied if the * field is not present. Note that if a field is found missing, then * the rest of the statement is ignored (regardless of whether the rest * is marked compulsory or not.) */ bool compulsory() const { return m_compulsory; } private: Type m_type; QString m_string; QString m_key; bool m_compulsory; }; class OutputField { public: /** * Constructs an empty output field. */ OutputField(); /** * Constructs an output field consisting of braced code. */ OutputField( const SourceLineList & bracedCode ); /** * Constructs an output field consisting of a single string. */ OutputField( const QString &string ); QString string() const { return m_string; } SourceLineList bracedCode() const { return m_bracedCode; } bool found() const { return m_found; } private: QString m_string; SourceLineList m_bracedCode; /** * This specifies if a non compulsory field was found or not. */ bool m_found; }; typedef QList StatementDefinition; typedef QMap DefinitionMap; typedef QMap OutputFieldMap; /** @author Daniel Clarke @author David Saxton */ class Parser { public: Parser( Microbe * mb ); ~Parser(); /** * Report a compile error to Microbe; the current source line will be * sent. Context is extra information to be inserted into the error * message, only applicable to some errors (such as a use of a reserved * keyword). */ void mistake( Microbe::MistakeType type, const QString & context = nullptr ); /** * Creates a new instance of the parser class with all state information * (class members) copied from this instance of the class. Don't forget to * delete it when you are done! */ Parser * createChildParser(); /** * Creates a child class and uses it to parse recursively. */ Code * parseWithChild( const SourceLineList & lines ); /** * This is the actual parsing function, make sure to use parseUsingChild * instead (???) */ Code * parse( const SourceLineList & lines ); /** * Returns the lines between the braces, excluding the braces, e.g. * defproc name * { * more code * some more code * } * returns ("more code","some more code"). * Note that Microbe has already put the braces on separate lines for us. * @param it is the iterator at the position of the first brace, this * function will return with it pointing at the matching closing brace. * @param end is the iterator pointing to the end of the source line * list, so that we don't search past it. * @returns The braced code (excluding the braces). */ SourceLineList getBracedCode( SourceLineList::const_iterator * it, SourceLineList::const_iterator end ); /** * Returns expression type. * 0 = directly usable number (literal). * 1 = variable. * 2 = expression that needs evaluating. */ ExprType getExpressionType( const QString & expression ); /** * Examines the text to see if it looks like a literal, i.e. of the form * "321890","021348","0x3C","b'0100110'","0101001b","h'43A'", or "2Ah". * Everything else is considered non-literal. * @see literalToInt. */ static bool isLiteral( const QString &text ); /** * Tries to convert the given literal string into a integer. If it fails, * i.e. it is not any recognised literal, then it returns -1 and sets *ok to * false. Else, *ok is set to true and the literal value is returned. * @see isLiteral */ static int literalToInt( const QString & literal, bool * ok = nullptr ); /** * Does the specified operation on the given numbers and returns the result. */ static int doArithmetic( int lvalue, int rvalue, Expression::Operation op ); /** * @return whether it was an assignment (which might not have been in * the proper form). */ bool processAssignment(const QString &line); void compileConditionalExpression( const QString & expression, Code * ifCode, Code * elseCode ) const; QString processConstant(const QString &expression, bool * isConstant, bool suppressNumberTooBig = false) const; private: /** * This is called when the bulk of the actual parsing has been carried * out and is ready to be turned into assembly code. * @param name Name of the statement to be processed * @param fieldMap A map of named fields as appropriate to the statement */ void processStatement( const QString & name, const OutputFieldMap & fieldMap ); DefinitionMap m_definitionMap; PIC14 * m_pPic; bool m_bPassedEnd; Microbe * mb; Code * m_code; SourceLine m_currentSourceLine; private: // Disable copy constructor and operator= Parser( const Parser & ); Parser &operator=( const Parser & ); }; #endif diff --git a/microbe/pic14.cpp b/microbe/pic14.cpp index a6aabf94..7fd89427 100644 --- a/microbe/pic14.cpp +++ b/microbe/pic14.cpp @@ -1,1446 +1,1447 @@ /*************************************************************************** * Copyright (C) 2004-2005 by Daniel Clarke * * daniel.jc@gmail.com * * * * 24-04-2007 * * Modified to add pic 16f877,16f627 and 16f628 * * by george john george@space-kerala.org * * supported by SPACE www.space-kerala.org * * * * 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., * * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * ***************************************************************************/ #include "instruction.h" #include "parser.h" #include "pic14.h" +#include + #include -#include #include using namespace std; QString pic_type; bool LEDSegTable[][7] = { { 1, 1, 1, 1, 1, 1, 0 }, { 0, 1, 1, 0, 0, 0, 0 }, // 1 { 1, 1, 0, 1, 1, 0, 1 }, // 2 { 1, 1, 1, 1, 0, 0, 1 }, // 3 { 0, 1, 1, 0 ,0, 1, 1 }, // 4 { 1, 0, 1, 1, 0, 1, 1 }, // 5 { 1, 0, 1, 1, 1, 1, 1 }, // 6 { 1, 1, 1, 0, 0, 0, 0 }, // 7 { 1, 1, 1, 1, 1, 1, 1 }, // 8 { 1, 1, 1, 0, 0, 1, 1 }, // 9 { 1, 1, 1, 0, 1, 1, 1 }, // A { 0, 0, 1, 1, 1, 1, 1 }, // b { 1, 0, 0, 1, 1, 1, 0 }, // C { 0, 1, 1, 1, 1, 0, 1 }, // d { 1, 0, 0, 1, 1, 1, 1 }, // E { 1, 0, 0, 0, 1, 1, 1 } // F }; PIC14::PIC14( Microbe * master, Type type ) { mb = master; m_pCode = nullptr; m_type = type; } PIC14::~PIC14() { } PortPin PIC14::toPortPin( const QString & portPinString ) { QString port,holdport; int pin = -1; /*****************************modified *********************************************/ //inorder to support RB.3=high/1 if ( portPinString.length() == 3 ) { port = QString("PORT%1").arg( portPinString[1].toUpper() ); pin = QString( portPinString[2] ).toInt(); } // In form e.g. RB.3 else if ( portPinString.length() == 4 )//modification change ==3 to ==4 { port = QString("PORT%1").arg( portPinString[1].toUpper() ); pin = QString( portPinString[3] ).toInt();//modification change 2 to 3 } else { int dotpos = portPinString.indexOf("."); if ( dotpos == -1 ) return PortPin(); port = portPinString.left(dotpos); //modified checking is added in the case of Register also //now INTCON.GIE is high ,and INTCON.GIE=1/high works if(isValidRegister( port)) { RegisterBit REG(portPinString.mid(dotpos+1)); pin=REG.bitPos(); Register Reg(REG.registerType()); holdport=Reg.name(); if(holdport!=port) cerr << QString(" ERROR: %1 is not a Register bit\n").arg(portPinString ).toStdString(); } else pin = portPinString.mid(dotpos+1).toInt(); //***************************Modification ends******************************** } PortPin portPin( port, pin ); if ( isValidPortPin( portPin ) ) return portPin; //**************************Modification start ******************************** else if(isValidRegister(port)) return portPin; //**************************Modification ends ******************************** else { cerr << QString("ERROR: %1 is not a Port/Register bit\n").arg(portPinString ).toStdString(); return PortPin(); } } void PIC14::mergeCode( Code * code ) { m_pCode->merge( code ); } uchar PIC14::gprStart() const { switch ( m_type ) { case P16C84: case P16F84: return 0xc; case P16F627: case P16F628: case P16F877: return 0x20; case unknown: break; } qCritical() << Q_FUNC_INFO << "Unknown PIC type = " << m_type << endl; return 0xc; } PIC14::Type PIC14::toType( const QString & _text ) { QString text = _text.toUpper().simplified().remove('P'); if ( text == "16C84" ) { pic_type="P16C84"; return P16C84; } if ( text == "16F84" ) { pic_type="P16F84"; return P16F84; } if ( text == "16F627" ) { pic_type="P16F627"; return P16F627; } if ( text == "16F628" ) { pic_type="P16F627"; return P16F628; } //modified checking of 16F877 is included if ( text == "16F877" ) { pic_type="P16F877"; return P16F877; } cerr << QString("%1 is not a known PIC identifier\n").arg(_text).toStdString(); return unknown; } QString PIC14::minimalTypeString() const { switch ( m_type ) { case P16C84: return "16C84"; case P16F84: return "16F84"; case P16F627: return "16F627"; case P16F628: return "16F628"; //modified checking of 16F877 is included case P16F877: return "16F877"; case unknown: break; } qCritical() << Q_FUNC_INFO << "Unknown PIC type = " << m_type << endl; return nullptr;; } void PIC14::postCompileConstruct( const QStringList &interrupts ) { m_pCode->append( new Instr_raw("\n\tEND\n"), Code::Subroutine ); if ( interrupts.isEmpty() ) { // If there are no ISRs then we don't need to put in any handler code. // Instead, just insert the goto start instruction in case we need to // jump past any lookup tabes (and if there are none, then the optimizer // will remove the goto instruction). m_pCode->append(new Instr_goto("_start"), Code::InterruptHandler); m_pCode->queueLabel( "_start", Code::LookupTable ); return; } /* INTCON register: 7 --- GIE EEIE T0IE INTE RBIE T0IF INTF RBIF --- 0 E: enable F: flag Flag bits must be cleared manually before reactivating GIE, but we do this in each individual interrupt handler */ // The bizarre dance with swap is to ensure the status bits // are preserved properly m_pCode->append(new Instr_goto("_start"), Code::InterruptHandler); m_pCode->append(new Instr_raw("ORG 0x4"), Code::InterruptHandler); // When we arrive here: // Return address on stack, // GIE flag cleared (globally interrupts disabled) // W or STATUS not preserved by processor. m_pCode->append(new Instr_movwf("W_TEMP"), Code::InterruptHandler); m_pCode->append(new Instr_swapf("STATUS",0), Code::InterruptHandler); m_pCode->append(new Instr_movwf("STATUS_TEMP"), Code::InterruptHandler); QStringList::ConstIterator interruptsEnd = interrupts.end(); for( QStringList::ConstIterator it = interrupts.begin(); it != interruptsEnd; ++it ) { // Is the interrupt's flag bit set? m_pCode->append(new Instr_btfsc("INTCON",QString::number(interruptNameToBit((*it), true))), Code::InterruptHandler); m_pCode->append(new Instr_goto("_interrupt_" + (*it)), Code::InterruptHandler); // Yes, do its handler routine // Otherwise fall through to the next. } // If there was "somehow" a suprious interrupt there isn't really // much we can do about that (??) so just fall through and hope for the worst. m_pCode->queueLabel( "_interrupt_end", Code::InterruptHandler ); m_pCode->append(new Instr_swapf("STATUS_TEMP",0), Code::InterruptHandler ); m_pCode->append(new Instr_movwf("STATUS"), Code::InterruptHandler ); m_pCode->append(new Instr_swapf("W_TEMP",1), Code::InterruptHandler ); m_pCode->append(new Instr_swapf("W_TEMP",0), Code::InterruptHandler ); m_pCode->append(new Instr_retfie()); // Returns and renables globally interrupts. m_pCode->queueLabel( "_start", Code::LookupTable ); } int PIC14::interruptNameToBit(const QString &name, bool flag) { // 7 --- GIE EEIE T0IE INTE RBIE T0IF INTF RBIF --- 0 if( name == "change" ) // RB { if(flag) return 0; else return 3; } else if( name == "timer" ) { if(flag) return 2; else return 5; } else if( name == "external" ) { if(flag) return 1; else return 4; } return -1; } bool PIC14::isValidPort( const QString & portName ) const { if(pic_type =="P16F84"||pic_type =="P16C84"||pic_type =="P16F627"||pic_type =="P16F628") return ( portName == "PORTA" || portName == "PORTB"); if(pic_type=="P16F877") return ( portName == "PORTA" ||portName == "PORTB"||portName == "PORTC" ||portName == "PORTD"||portName == "PORTE"); return false; } bool PIC14::isValidPortPin( const PortPin & portPin ) const { if(pic_type == "P16F84" ||pic_type =="P16C84") { if ( portPin.port() == "PORTA" ) return (portPin.pin() >= 0) && (portPin.pin() <= 4); if ( portPin.port() == "PORTB" ) return (portPin.pin() >= 0) && (portPin.pin() <= 7); } if(pic_type == "P16F627" ||pic_type =="P16F628") { if ( portPin.port() == "PORTA" ) return (portPin.pin() >= 0) && (portPin.pin() <= 7); if ( portPin.port() == "PORTB" ) return (portPin.pin() >= 0) && (portPin.pin() <= 7); } if(pic_type=="P16F877") { if ( portPin.port() == "PORTA" ) return (portPin.pin() >= 0) && (portPin.pin() <= 5); if ( portPin.port() == "PORTB" ) return (portPin.pin() >= 0) && (portPin.pin() <= 7); if ( portPin.port() == "PORTC" ) return (portPin.pin() >= 0) && (portPin.pin() <= 7); if ( portPin.port() == "PORTD" ) return (portPin.pin() >= 0) && (portPin.pin() <= 7); if ( portPin.port() == "PORTE" ) return (portPin.pin() >= 0) && (portPin.pin() <= 2); } return false; } bool PIC14::isValidTris( const QString & trisName ) const { if(pic_type =="P16F84"||pic_type =="P16C84"||pic_type =="P16F627"||pic_type =="P16F628") return ( trisName == "TRISA" || trisName == "TRISB"); if(pic_type=="P16F877") return ( trisName =="TRISA"|| trisName =="TRISB"||trisName =="TRISC"||trisName == "TRISD"||trisName == "TRISE" ); return false; } //*****************Modified ****************************// //New function isValiedRegister is added to check whether a register is valied or not bool PIC14::isValidRegister( const QString & registerName)const { if(pic_type=="P16F84"||pic_type=="P16C84") return ( registerName == "TMR0" || registerName == "PCL" || registerName == "STATUS" || registerName == "FSR" || registerName == "EEDATH" || registerName == "EEADR" || registerName == "PCLATH" || registerName == "INTCON" || registerName == "EECON1" || registerName == "EECON2" || registerName == "OPTION_REG"); if(pic_type=="P16F877") return ( registerName == "TMR0" || registerName == "PCL" || registerName == "STATUS" || registerName == "FSR" || registerName == "PCLATH" || registerName == "INTCON" || registerName == "PIR1" || registerName == "PIR2" || registerName == "TMR1L" || registerName == "TMR1H" || registerName == "T1CON" || registerName == "TMR2" || registerName == "T2CON" || registerName == "SSPBUF" || registerName == "SSPCON" || registerName == "CCPR1L" || registerName == "CCPR1H" || registerName == "CCP1CON" || registerName == "RCSTA" || registerName == "TXREG" || registerName == "RCREG" || registerName == "CCPR2L" || registerName == "CCPR2H" || registerName == "CCP2CON" || registerName == "ADRESH" || registerName == "ADCON0" /*bank0ends*/ || registerName == "OPTION_REG" || registerName == "PIE1" || registerName == "PIE2" || registerName == "PCON" || registerName == "SSPCON2" || registerName == "PR2" || registerName == "SSPADD" || registerName == "SSPSTAT" || registerName == "TXSTA" || registerName == "SPBRG" || registerName == "ADRESL" || registerName == "ADCON1" /*bank1ends*/ || registerName == "EEDATA" || registerName == "EEADR" || registerName == "EEDATH" || registerName == "EEADRH" /*bank2ends*/ || registerName == "EECON1" || registerName == "EECON2" /*bank3ends*/ ); if(pic_type=="P16F627"||pic_type=="P16F628") return ( registerName == "TMR0" || registerName == "PCL" || registerName == "STATUS" || registerName == "FSR" || registerName == "PCLATH" || registerName == "INTCON" || registerName == "PIR1" || registerName == "TMR1L" || registerName == "TMR1H" || registerName == "T1CON" || registerName == "TMR2" || registerName == "T2CON" || registerName == "CCPR1L" || registerName == "CCPR1H" || registerName == "CCP1CON" || registerName == "RCSTA" || registerName == "TXREG" || registerName == "RCREG" || registerName == "CMCON"/*bank0ends*/ || registerName == "OPTION_REG" || registerName == "PIE1" || registerName == "PCON" || registerName == "PR2" || registerName == "TXSTA" || registerName == "SPBRG" || registerName == "EEDATA" || registerName == "EEADR" || registerName == "EECON1" || registerName == "EECON2" || registerName == "VRCON"/*bank1ends*/ ); return false; } //****************************modifications ends******************************************** bool PIC14::isValidInterrupt( const QString & interruptName ) const { if(pic_type == "P16F84" ||pic_type =="P16C84"||pic_type =="P16F877"||pic_type=="P16F627"||pic_type=="P16F628") return ( interruptName == "change" || interruptName == "timer" || interruptName == "external" ); return false; } void PIC14::setConditionalCode( Code * ifCode, Code * elseCode ) { m_ifCode = ifCode; m_elseCode = elseCode; } void PIC14::Sgoto(const QString &label) { m_pCode->append( new Instr_goto(label) ); } void PIC14::Slabel(const QString &label) { // std::cout << Q_FUNC_INFO << "label="<queueLabel( label, Code::Middle ); } void PIC14::Send() { m_pCode->append( new Instr_sleep() ); } void PIC14::Ssubroutine( const QString &procName, Code * subCode ) { m_pCode->queueLabel( procName, Code::Subroutine ); m_pCode->merge( subCode, Code::Subroutine ); m_pCode->append( new Instr_return(), Code::Subroutine ); } void PIC14::Sinterrupt( const QString &procName, Code * subCode ) { m_pCode->queueLabel( "_interrupt_" + procName, Code::Subroutine ); // Clear the interrupt flag for this particular interrupt source m_pCode->append( new Instr_bcf("INTCON",QString::number(interruptNameToBit(procName,true))) ); m_pCode->merge( subCode, Code::Subroutine ); m_pCode->append( new Instr_goto("_interrupt_end"), Code::Subroutine ); } void PIC14::Scall(const QString &name) { m_pCode->append( new Instr_call(name) ); } void PIC14::Ssetlh( const PortPin & portPin, bool high) { if(high) m_pCode->append( new Instr_bsf( portPin.port(),QString::number(portPin.pin()) ) ); else m_pCode->append( new Instr_bcf( portPin.port(), QString::number(portPin.pin()) ) ); } void PIC14::rearrangeOpArguments( QString * val1, QString * val2, LocationType * val1Type, LocationType * val2Type) { if( *val2Type == work && *val1Type != work ) { LocationType tempType = *val2Type; QString tempVal = *val2; *val2Type = *val1Type; *val2 = *val1; *val1Type = tempType; *val1 = tempVal; } } void PIC14::add( QString val1, QString val2, LocationType val1Type, LocationType val2Type ) { rearrangeOpArguments( &val1, &val2, &val1Type, &val2Type ); switch(val1Type) { case num: m_pCode->append(new Instr_movlw( val1.toInt( nullptr, 0 ) )); break; case work: break; case var: m_pCode->append(new Instr_movf(val1,0)); break; } switch(val2Type) { case num: m_pCode->append(new Instr_addlw(val2.toInt( nullptr, 0 ))); break; case work: break; case var: m_pCode->append(new Instr_addwf(val2,0)); break; } } void PIC14::subtract( const QString & val1, const QString & val2, LocationType val1Type, LocationType val2Type ) { switch(val2Type) { case num: m_pCode->append(new Instr_movlw( val2.toInt( nullptr, 0 ) )); break; case work: break; case var: m_pCode->append(new Instr_movf(val2,0)); break; } switch(val1Type) { case num: m_pCode->append(new Instr_sublw(val1.toInt( nullptr, 0 ))); break; case work: break; case var: m_pCode->append(new Instr_subwf(val1,0)); break; } } void PIC14::assignNum(const QString & val) { m_pCode->append(new Instr_movlw(val.toInt( nullptr, 0 ))); } void PIC14::assignVar(const QString &val) { m_pCode->append(new Instr_movf(val,0)); } void PIC14::saveToReg(const QString &dest) { m_pCode->append(new Instr_movwf(dest)); } void PIC14::saveResultToVar( const QString & var ) { m_pCode->append( new Instr_movwf( var ) ); } void PIC14::mul(QString val1, QString val2, LocationType val1Type, LocationType val2Type) { multiply(); rearrangeOpArguments( &val1, &val2, &val1Type, &val2Type ); // First, set _i argument switch(val1Type) { case num: m_pCode->append(new Instr_movlw(val1.toInt( nullptr, 0 ))); break; case work: break; case var: m_pCode->append(new Instr_movf(val1,0)); break; } m_pCode->append(new Instr_movwf("__i")); // Then set _j argument switch(val2Type) { case num: m_pCode->append(new Instr_movlw(val2.toInt( nullptr, 0 ))); break; case work: break; case var: m_pCode->append(new Instr_movf(val2,0)); break; } m_pCode->append(new Instr_movwf("__j")); m_pCode->append(new Instr_call("__picfunc_multiply")); m_pCode->append(new Instr_movf("__result",0)); } void PIC14::multiply() { if ( m_pCode->instruction("__picfunc_multiply") ) return; m_pCode->queueLabel( "__picfunc_multiply", Code::Subroutine ); m_pCode->append(new Instr_clrf("__result"), Code::Subroutine ); //result+=m_pCode->appenduction("clrf __result"); m_pCode->queueLabel( "__picfunc_multiply_loop", Code::Subroutine ); m_pCode->append(new Instr_movf("__i",0), Code::Subroutine ); //result+=m_pCode->appenduction("movf __i,0"); m_pCode->append(new Instr_btfsc("__j","0"), Code::Subroutine ); //result+=m_pCode->appenduction("btfsc __j,0"); m_pCode->append(new Instr_addwf("__result",1), Code::Subroutine ); //result+=m_pCode->appenduction("addwf __result,1"); m_pCode->append(new Instr_bcf("STATUS","C"), Code::Subroutine ); //result+=m_pCode->appenduction("bcf STATUS,C"); m_pCode->append(new Instr_rrf("__j",1), Code::Subroutine ); //result+=m_pCode->appenduction("rrf __j,1"); m_pCode->append(new Instr_bcf("STATUS","C"), Code::Subroutine ); //result+=m_pCode->appenduction("bcf STATUS,C"); m_pCode->append(new Instr_rlf("__i",1), Code::Subroutine ); //result+=m_pCode->appenduction("rlf __i,1"); m_pCode->append(new Instr_movf("__j",1), Code::Subroutine ); //result+=m_pCode->appenduction("movf __j,1"); m_pCode->append(new Instr_btfss("STATUS","Z"), Code::Subroutine ); //result+=m_pCode->appenduction("btfss STATUS,Z"); m_pCode->append(new Instr_goto("__picfunc_multiply_loop"), Code::Subroutine ); //result+=m_pCode->appenduction("goto __picfunc_multiply_loop"); m_pCode->append(new Instr_return(), Code::Subroutine ); //result+=m_pCode->appenduction("return"); } void PIC14::div( const QString & val1, const QString & val2, LocationType val1Type, LocationType val2Type) { divide(); // NOO - "x / 2" is NOT the same as "2 / x" // rearrangeOpArguments( val1, val2, val1Type, val2Type ); // First, set _i argument switch(val1Type) { case num: m_pCode->append(new Instr_movlw(val1.toInt( nullptr, 0 ))); break; case work: break; case var: m_pCode->append(new Instr_movf(val1,0)); break; } m_pCode->append(new Instr_movwf("__i")); // Then set _j argument switch(val2Type) { case num: m_pCode->append(new Instr_movlw(val2.toInt( nullptr, 0 ))); break; case work: break; case var: m_pCode->append(new Instr_movf(val2,0)); break; } m_pCode->append(new Instr_movwf("__j")); m_pCode->append(new Instr_call("__picfunc_divide"));//result+=instruction("call __picfunc_divide"); m_pCode->append(new Instr_movf("__result",0));//result+=instruction("movf __result,0"); } void PIC14::divide() { m_pCode->queueLabel( "__picfunc_divide", Code::Subroutine ); m_pCode->append(new Instr_movf("__j",1), Code::Subroutine ); m_pCode->append(new Instr_btfsc("STATUS","2"), Code::Subroutine ); m_pCode->append(new Instr_return(), Code::Subroutine ); m_pCode->append(new Instr_clrf("__result"), Code::Subroutine ); m_pCode->append(new Instr_movlw(1), Code::Subroutine ); m_pCode->append(new Instr_movwf("__k"), Code::Subroutine ); m_pCode->queueLabel( "__divide_shift", Code::Subroutine ); m_pCode->append(new Instr_bcf("STATUS","C"), Code::Subroutine ); m_pCode->append(new Instr_rlf("__k",1), Code::Subroutine ); m_pCode->append(new Instr_bcf("STATUS","C"), Code::Subroutine ); m_pCode->append(new Instr_rlf("__j",1), Code::Subroutine ); m_pCode->append(new Instr_btfss("__j","7"), Code::Subroutine ); m_pCode->append(new Instr_goto("__divide_shift"), Code::Subroutine ); m_pCode->queueLabel( "__divide_loop", Code::Subroutine ); m_pCode->append(new Instr_movf("__j",0), Code::Subroutine ); m_pCode->append(new Instr_subwf("__i",1), Code::Subroutine ); m_pCode->append(new Instr_btfsc("STATUS","C"), Code::Subroutine ); m_pCode->append(new Instr_goto("__divide_count"), Code::Subroutine ); m_pCode->append(new Instr_addwf("__i",1), Code::Subroutine ); m_pCode->append(new Instr_goto("__divide_final"), Code::Subroutine ); m_pCode->queueLabel( "__divide_count", Code::Subroutine ); m_pCode->append(new Instr_movf("__k",0), Code::Subroutine ); m_pCode->append(new Instr_addwf("__result",1), Code::Subroutine ); m_pCode->queueLabel( "__divide_final", Code::Subroutine ); m_pCode->append(new Instr_bcf("STATUS","C"), Code::Subroutine ); m_pCode->append(new Instr_rrf("__j",1), Code::Subroutine ); m_pCode->append(new Instr_bcf("STATUS","C"), Code::Subroutine ); m_pCode->append(new Instr_rrf("__k",1), Code::Subroutine ); m_pCode->append(new Instr_btfss("STATUS","C"), Code::Subroutine ); m_pCode->append(new Instr_goto("__divide_loop"), Code::Subroutine ); m_pCode->append(new Instr_return(), Code::Subroutine ); } Code * PIC14::ifCode() { return m_ifCode; } Code * PIC14::elseCode() { return m_elseCode; } void PIC14::ifInitCode( const QString &val1, const QString &val2, LocationType val1Type, LocationType val2Type ) { // NOO - "x < 2" is NOT the same as "2 < x" // rearrangeOpArguments( val1, val2, val1Type, val2Type ); switch(val1Type) { case num: m_pCode->append(new Instr_movlw(val1.toInt( nullptr, 0 ))); break; case work: break; // Nothing to do case var: m_pCode->append(new Instr_movf(val1,0)); break; } switch(val2Type) { case num: m_pCode->append(new Instr_sublw(val2.toInt( nullptr, 0 ))); break; case work: qCritical() << Q_FUNC_INFO << "Cannot subtract working from working!" << endl; break; case var: m_pCode->append(new Instr_subwf(val2,0)); break; } } void PIC14::equal( const QString &val1, const QString &val2, LocationType val1Type, LocationType val2Type ) { ifInitCode( val1, val2, val1Type, val2Type ); const QString labelEnd = mb->uniqueLabel()+"_endif"; const QString labelFalse = mb->uniqueLabel()+"_case_false"; m_pCode->append(new Instr_btfss("STATUS","2")); m_pCode->append(new Instr_goto(labelFalse)); mergeCode( ifCode() ); m_pCode->append(new Instr_goto(labelEnd)); m_pCode->queueLabel( labelFalse ); mergeCode( elseCode() ); m_pCode->queueLabel( labelEnd ); } void PIC14::notEqual( const QString &val1, const QString &val2, LocationType val1Type, LocationType val2Type ) { ifInitCode( val1, val2, val1Type, val2Type ); const QString labelEnd = mb->uniqueLabel()+"_endif"; const QString labelFalse = mb->uniqueLabel()+"_case_false"; m_pCode->append(new Instr_btfsc("STATUS","2")); m_pCode->append(new Instr_goto(labelFalse)); mergeCode( ifCode() ); m_pCode->append(new Instr_goto(labelEnd)); m_pCode->queueLabel( labelFalse ); mergeCode( elseCode() ); m_pCode->queueLabel( labelEnd ); } void PIC14::greaterThan( const QString &val1, const QString &val2, LocationType val1Type, LocationType val2Type ) { ifInitCode( val1, val2, val1Type, val2Type ); const QString labelEnd = mb->uniqueLabel()+"_endif"; const QString labelFalse = mb->uniqueLabel()+"_case_false"; m_pCode->append(new Instr_btfsc("STATUS","0")); m_pCode->append(new Instr_goto(labelFalse)); mergeCode( ifCode() ); m_pCode->append(new Instr_goto(labelEnd)); m_pCode->queueLabel( labelFalse ); mergeCode( elseCode() ); m_pCode->queueLabel( labelEnd ); } void PIC14::lessThan( const QString &val1, const QString &val2, LocationType val1Type, LocationType val2Type ) { cout << Q_FUNC_INFO << endl; ifInitCode( val1, val2, val1Type, val2Type ); const QString labelEnd = mb->uniqueLabel()+"_endif"; const QString labelFalse = mb->uniqueLabel()+"_case_false"; m_pCode->append(new Instr_btfss("STATUS","0")); m_pCode->append(new Instr_goto(labelFalse)); m_pCode->append(new Instr_btfsc("STATUS","2")); m_pCode->append(new Instr_goto(labelFalse)); mergeCode( ifCode() ); m_pCode->append(new Instr_goto(labelEnd)); m_pCode->queueLabel( labelFalse ); mergeCode( elseCode() ); m_pCode->queueLabel( labelEnd ); } void PIC14::greaterOrEqual( const QString &val1, const QString &val2, LocationType val1Type, LocationType val2Type ) { ifInitCode( val1, val2, val1Type, val2Type ); const QString labelEnd = mb->uniqueLabel()+"_endif"; const QString labelTrue = mb->uniqueLabel()+"_case_true"; // Note that unlike the others, this is labelTrue, not labelFalse m_pCode->append(new Instr_btfsc("STATUS","2")); m_pCode->append(new Instr_goto(labelTrue)); m_pCode->append(new Instr_btfss("STATUS","0")); m_pCode->append(new Instr_goto(labelTrue)); mergeCode( elseCode() ); m_pCode->append(new Instr_goto(labelEnd)); m_pCode->queueLabel( labelTrue ); mergeCode( ifCode() ); m_pCode->queueLabel( labelEnd ); } void PIC14::lessOrEqual( const QString &val1, const QString &val2, LocationType val1Type, LocationType val2Type ) { ifInitCode( val1, val2, val1Type, val2Type ); const QString labelEnd = mb->uniqueLabel()+"_endif"; const QString labelFalse = mb->uniqueLabel()+"_case_false"; m_pCode->append(new Instr_btfss("STATUS","0")); m_pCode->append(new Instr_goto(labelFalse)); mergeCode( ifCode() ); m_pCode->append(new Instr_goto(labelEnd)); m_pCode->queueLabel( labelFalse ); mergeCode( elseCode() ); m_pCode->queueLabel( labelEnd ); } void PIC14::Swhile( Code * whileCode, const QString &expression) { QString result; QString ul = mb->uniqueLabel(); whileCode->append( new Instr_goto(ul) ); m_pCode->queueLabel( ul, Code::Middle ); // If the condition is not true, just fall through m_parser->compileConditionalExpression( expression, whileCode, nullptr ); } void PIC14::Srepeat( Code * repeatCode, const QString &expression) { QString result; QString ul = mb->uniqueLabel(); Code * elseCode = new Code; elseCode->append( new Instr_goto(ul) ); m_pCode->queueLabel( ul ); m_pCode->merge( repeatCode ); // If the condition is true, just fall through m_parser->compileConditionalExpression( expression, nullptr, elseCode ); } void PIC14::Sif( Code * ifCode, Code * elseCode, const QString &expression) { m_parser->compileConditionalExpression( expression, ifCode, elseCode ); } void PIC14::Sfor( Code * forCode, Code * initCode, const QString &expression, const QString &variable, const QString &step, bool stepPositive) { QString ul = mb->uniqueLabel(); if ( step == "1" ) { if (stepPositive) forCode->append(new Instr_incf(variable,1)); else forCode->append(new Instr_decf(variable,1)); } else { forCode->append(new Instr_movlw(step.toInt( nullptr, 0 ))); if (stepPositive) forCode->append(new Instr_addwf(variable,1)); else forCode->append(new Instr_subwf(variable,1)); } forCode->append(new Instr_goto(ul)); m_pCode->merge( initCode ); m_pCode->queueLabel( ul ); m_parser->compileConditionalExpression( expression, forCode, nullptr ); } void PIC14::Spin( const PortPin & portPin, bool NOT) { QString lowLabel, highLabel, postLabel; lowLabel = mb->uniqueLabel(); highLabel = mb->uniqueLabel(); postLabel = mb->uniqueLabel(); /*result += indent + "goto\t" + lowLabel; result += indent + "movlw\t1" + "goto\t"+postLabel+; result += lowLabel + + indent + "movlw\t0" + indent; result += postLabel + ;*/ if(NOT) m_pCode->append(new Instr_btfsc( portPin.port(), QString::number( portPin.pin() ) )); //result +=instruction((QString)(NOT?"btfsc":"btfss")+"\t"+port+","+pin); else m_pCode->append(new Instr_btfss( portPin.port(), QString::number( portPin.pin() ) )); m_pCode->append(new Instr_goto(lowLabel));//result += instruction("goto\t" + lowLabel); mergeCode( ifCode() ); m_pCode->append(new Instr_goto(postLabel));//result += instruction("goto\t"+postLabel); m_pCode->queueLabel( lowLabel ); mergeCode( elseCode() ); m_pCode->queueLabel( postLabel ); } void PIC14::Sdelay( unsigned length_us, Code::InstructionPosition pos ) { if ( length_us == 0 ) return; if ( length_us > 50070524 ) { length_us += 50267642; int l = length_us/50070530; length_us -= l * 50070530; int k = length_us/196355; m_pCode->append( new Instr_movlw( l ), pos ); m_pCode->append( new Instr_movwf( "__l" ), pos ); m_pCode->append( new Instr_movlw( k ), pos ); m_pCode->append( new Instr_movwf( "__k" ), pos ); mb->addDelayRoutineWanted( Delay_50S ); } else if ( length_us > 196350 ) { length_us += 197116; int k = length_us/196355; length_us -= k * 196355; int j = length_us/770; m_pCode->append( new Instr_incf( "__l", 1 ), pos ); m_pCode->append( new Instr_movlw( k ), pos ); m_pCode->append( new Instr_movwf( "__k" ), pos ); m_pCode->append( new Instr_movlw( j ), pos ); m_pCode->append( new Instr_movwf( "__j" ), pos ); mb->addDelayRoutineWanted( Delay_200mS ); } else if ( length_us > 766 ) { length_us += 765; int j = length_us/770; length_us -= j * 770; int i = length_us/3; m_pCode->append( new Instr_incf( "__l", 1 ), pos ); m_pCode->append( new Instr_incf( "__k", 1 ), pos ); m_pCode->append( new Instr_movlw( j ), pos ); m_pCode->append( new Instr_movwf( "__j" ), pos ); m_pCode->append( new Instr_movlw( i ), pos ); m_pCode->append( new Instr_movwf( "__i" ), pos ); mb->addDelayRoutineWanted( Delay_768uS ); } else { length_us += -1; int i = length_us/3; m_pCode->append( new Instr_incf( "__l", 1 ), pos ); m_pCode->append( new Instr_incf( "__k", 1 ), pos ); m_pCode->append( new Instr_incf( "__j", 1 ), pos ); m_pCode->append( new Instr_movlw( i ), pos ); m_pCode->append( new Instr_movwf( "__i" ), pos ); mb->addDelayRoutineWanted( Delay_3uS ); } m_pCode->append( new Instr_call( "__delay_subroutine"), pos ); } void PIC14::addCommonFunctions( DelaySubroutine delay ) { if ( delay != Delay_None ) { QString subName = "__delay_subroutine"; m_pCode->queueLabel( subName, Code::Subroutine ); m_pCode->append( new Instr_decfsz( "__i", 1 ), Code::Subroutine ); m_pCode->append( new Instr_goto( subName ), Code::Subroutine ); if ( delay > Delay_3uS ) { m_pCode->append( new Instr_decfsz( "__j", 1 ), Code::Subroutine ); m_pCode->append( new Instr_goto( subName ), Code::Subroutine ); } if ( delay > Delay_768uS ) { m_pCode->append( new Instr_decfsz( "__k", 1 ), Code::Subroutine ); m_pCode->append( new Instr_goto( subName ), Code::Subroutine ); } if ( delay > Delay_200mS ) { m_pCode->append( new Instr_decfsz( "__l", 1 ), Code::Subroutine ); m_pCode->append( new Instr_goto( subName ), Code::Subroutine ); } m_pCode->append( new Instr_return(), Code::Subroutine ); } } void PIC14::SsevenSegment( const Variable & pinMap ) { assert( pinMap.type() == Variable::sevenSegmentType ); assert( pinMap.portPinList().size() == 7 ); QString subName = QString("__output_seven_segment_%1").arg( pinMap.name() ); m_pCode->append( new Instr_call( subName ) ); if ( m_pCode->instruction(subName) ) return; // Build up what are going to write to each port from the pin map struct SSPortOutput { bool used; // Wheter we use this port at all bool use[8]; // Whether or not we use each pin. bool out[16][8]; // The bit to write to each pin for each value. uchar useMask; // The bits of use[8] - this is generated later from use[8] }; unsigned numPorts = 2; SSPortOutput portOutput[ 2 ]; // numPorts memset( portOutput, 0, numPorts * sizeof(SSPortOutput) ); for ( unsigned i = 0; i < 7; ++i ) { PortPin portPin = pinMap.portPinList()[i]; unsigned port = unsigned( portPin.portPosition() ); unsigned pin = unsigned( portPin.pin() ); portOutput[ port ].used = true; portOutput[ port ].use[ pin ] = true; for ( unsigned num = 0; num < 16; ++num ) { portOutput[ port ].out[ num ][ pin ] = LEDSegTable[num][ i ]; } } // See if we've used more than one port unsigned portsUsed = 0; for ( unsigned port = 0; port < numPorts; ++port ) { if ( portOutput[port].used ) portsUsed++; } // Generate the useMasks for ( unsigned port = 0; port < numPorts; ++port ) { portOutput[port].useMask = 0; for ( unsigned pin = 0; pin < 8; ++pin ) portOutput[port].useMask |= portOutput[port].use[pin] ? (1 << pin) : 0; } //BEGIN Generate [subName] Subroutine m_pCode->queueLabel( subName, Code::Subroutine ); // if ( portsUsed > 1 ) { m_pCode->append( new Instr_movwf("__i"), Code::Subroutine ); } // bool overwrittenW = false; bool overwrittenW = true; for ( unsigned port = 0; port < numPorts; ++port ) { if ( !portOutput[port].used ) continue; QString portName = QString("PORT%1").arg( char('A'+port) ); // Save the current value of the port pins that we should not be writing to m_pCode->append( new Instr_movf( portName, 0 ), Code::Subroutine ); m_pCode->append( new Instr_andlw( ~portOutput[port].useMask ), Code::Subroutine ); m_pCode->append( new Instr_movwf( "__j" ), Code::Subroutine ); if ( overwrittenW ) m_pCode->append( new Instr_movf("__i",0), Code::Subroutine ); m_pCode->append( new Instr_call( subName + QString("_lookup_%1").arg(port) ), Code::Subroutine ); overwrittenW = true; // Restore the state of the pins which aren't used m_pCode->append( new Instr_iorwf( "__j", 0 ), Code::Subroutine ); // And write the result to the port m_pCode->append( new Instr_movwf( portName ), Code::Subroutine ); } m_pCode->append( new Instr_return(), Code::Subroutine ); //END Generate [subName] Subroutine // For each port, generate code for looking up the value for writing to it for ( unsigned port = 0; port < numPorts; ++port ) { if ( !portOutput[port].used ) continue; m_pCode->queueLabel( subName + QString("_lookup_%1").arg(port), Code::LookupTable ); m_pCode->append( new Instr_andlw(15), Code::LookupTable ); // Generate the lookup table m_pCode->append( new Instr_addwf( "pcl", 1 ), Code::LookupTable ); for ( unsigned num = 0; num < 16; ++num ) { unsigned literal = 0; for ( unsigned bit = 0; bit < 8; ++bit ) literal += ( portOutput[port].out[num][bit] ? 1 : 0 ) << bit; m_pCode->append( new Instr_retlw( literal ), Code::LookupTable ); } } } void PIC14::Skeypad( const Variable & pinMap ) { // pinMap = 4 rows, n columns assert( pinMap.type() == Variable::keypadType ); assert( pinMap.portPinList().size() >= 7 ); // 4 rows, at least 3 columns QString subName = QString("__wait_read_keypad_%1").arg( pinMap.name() ); QString waitName = QString("__wait_keypad_%1").arg( pinMap.name() ); QString readName = QString("__read_keypad_%1").arg( pinMap.name() ); m_pCode->append( new Instr_call( subName ) ); if ( m_pCode->instruction( subName ) ) return; //BEGIN Wait until read subroutine m_pCode->queueLabel( subName, Code::Subroutine ); // Read current key (if any) from keypad and save to temporary variable m_pCode->append( new Instr_call( readName ), Code::Subroutine ); m_pCode->append( new Instr_movwf( "__m" ), Code::Subroutine ); // Test if any key was pressed; if not, then start again // std::cout << "mb->alias(\"Keypad_None\")="<alias("Keypad_None") << std::endl; m_pCode->append( new Instr_sublw( mb->alias("Keypad_None").toInt( nullptr, 0 ) ), Code::Subroutine ); m_pCode->append( new Instr_btfsc( "STATUS","Z" ), Code::Subroutine ); m_pCode->append( new Instr_goto( subName ), Code::Subroutine ); m_pCode->append( new Instr_goto( waitName ), Code::Subroutine ); //END Wait until read subroutine //BEGIN Wait until released subroutine m_pCode->queueLabel( waitName, Code::Subroutine ); Sdelay( 10000, Code::Subroutine ); // 10 milliseconds for debouncing // Key was pressed; now we wait until the key is released again m_pCode->append( new Instr_call( readName ), Code::Subroutine ); m_pCode->append( new Instr_sublw( mb->alias("Keypad_None").toInt( nullptr, 0 ) ), Code::Subroutine ); m_pCode->append( new Instr_btfss( "STATUS","Z" ), Code::Subroutine ); m_pCode->append( new Instr_goto( waitName ), Code::Subroutine ); m_pCode->append( new Instr_movf( "__m", 0 ), Code::Subroutine ); m_pCode->append( new Instr_return(), Code::Subroutine ); //END Wait until released subroutine if ( m_pCode->instruction( readName ) ) return; //BEGIN Read current value of keypad subroutine m_pCode->queueLabel( readName, Code::Subroutine ); // Make the four row lines low for ( unsigned row = 0; row < 4; ++ row ) { PortPin rowPin = pinMap.portPinList()[row]; m_pCode->append( new Instr_bcf( rowPin.port(), QString::number( rowPin.pin() ) ), Code::Subroutine ); } // Test each row in turn for ( unsigned row = 0; row < 4; ++ row ) { // Make the high low PortPin rowPin = pinMap.portPinList()[row]; m_pCode->append( new Instr_bsf( rowPin.port(), QString::number( rowPin.pin() ) ), Code::Subroutine ); for ( unsigned col = 0; col < 3; ++ col ) { PortPin colPin = pinMap.portPinList()[4+col]; m_pCode->append( new Instr_btfsc( colPin.port(), QString::number( colPin.pin() ) ), Code::Subroutine ); m_pCode->append( new Instr_retlw( mb->alias( QString("Keypad_%1_%2").arg(row+1).arg(col+1) ).toInt( nullptr, 0 ) ), Code::Subroutine ); } // Make the low again m_pCode->append( new Instr_bcf( rowPin.port(), QString::number( rowPin.pin() ) ), Code::Subroutine ); } // No key was pressed m_pCode->append( new Instr_retlw( mb->alias("Keypad_None").toInt( nullptr, 0 ) ), Code::Subroutine ); //END Read current value of keypad subroutine } /*****************************commented for modification ******************************* void PIC14::bitwise( Expression::Operation op, const QString &r_val1, const QString &val2, bool val1IsNum, bool val2IsNum ) { QString val1 = r_val1; // There is no instruction for notting a literal, // so instead I am going to XOR with 0xFF if( op == Expression::bwnot ) val1 = "0xFF"; if( val1IsNum ) m_pCode->append(new Instr_movlw(val1.toInt( 0, 0 )));// result += instruction("movlw\t"+val1); else m_pCode->append(new Instr_movf(val1,0));//result += instruction("movf\t"+val1+",0"); QString opString; if( val2IsNum ) { switch(op) { case Expression::bwand: m_pCode->append(new Instr_andlw(val2.toInt( 0, 0 ))); break; case Expression::bwor: m_pCode->append(new Instr_iorlw(val2.toInt( 0, 0 ))); break; case Expression::bwxor: m_pCode->append(new Instr_xorlw(val2.toInt( 0, 0 ))); break; case Expression::bwnot: m_pCode->append(new Instr_xorlw(val2.toInt( 0, 0 ))); break; default: break; } } else { switch(op) { case Expression::bwand: m_pCode->append(new Instr_andwf(val2,0)); break; case Expression::bwor: m_pCode->append(new Instr_iorwf(val2,0)); break; case Expression::bwxor: m_pCode->append(new Instr_xorwf(val2,0)); break; case Expression::bwnot: m_pCode->append(new Instr_xorwf(val2,0)); break; default: break; } } }*/ ///comment end and the new function is given bellow -- new code is working well // TODO - One error with OR operation if A OR 255 result in segebentation fault //*****************modified to make the bit operation works***************************/ void PIC14::bitwise( Expression::Operation op,const QString & r_val1, const QString & val2, LocationType val1Type, LocationType val2Type) { QString val1 = r_val1; if( op == Expression::bwnot ) val1 = "0xFF"; switch(val1Type) { case num: m_pCode->append(new Instr_movlw(val1.toInt( nullptr, 0 ))); break; case work: break; case var: m_pCode->append(new Instr_movf(val1,0)); break; } switch(val2Type) { case num: { switch(op) { case Expression::bwand: m_pCode->append(new Instr_andlw(val2.toInt( nullptr, 0 ))); break; case Expression::bwor: m_pCode->append(new Instr_iorlw(val2.toInt( nullptr, 0 ))); break; case Expression::bwxor: m_pCode->append(new Instr_xorlw(val2.toInt( nullptr, 0 ))); break; case Expression::bwnot: m_pCode->append(new Instr_xorlw(val2.toInt( nullptr, 0 ))); break; default: break; } } case work: break; case var: { switch(op) { case Expression::bwand: m_pCode->append(new Instr_andwf(val2,0)); break; case Expression::bwor: m_pCode->append(new Instr_iorwf(val2,0)); break; case Expression::bwxor: m_pCode->append(new Instr_xorwf(val2,0)); break; case Expression::bwnot: m_pCode->append(new Instr_xorwf(val2,0)); break; default: break; } } } }//***************************************modification ends***************************** void PIC14::SincVar( const QString &var ) { m_pCode->append(new Instr_incf(var,1) ); } void PIC14::SdecVar( const QString &var ) { m_pCode->append(new Instr_decf(var,1) ); } void PIC14::SrotlVar( const QString &var ) { m_pCode->append(new Instr_rlf(var,1)); } void PIC14::SrotrVar( const QString &var ) { m_pCode->append(new Instr_rrf(var,1)); } void PIC14::Stristate(const QString &port) { //modification pic type is checked here m_pCode->append( new Instr_bsf("STATUS","5") );//commented if(pic_type== "P16C84" || pic_type =="P16F84"||pic_type =="P16F627") { if( port == "trisa" || port == "TRISA" ) saveResultToVar( "TRISA" ); else saveResultToVar( "TRISB" ); } if(pic_type =="P16F877") { if( port == "trisa" || port == "TRISA" ) saveResultToVar( "TRISA" ); else if( port == "trisb" || port == "TRISB" ) saveResultToVar( "TRISB" ); else if( port == "trisc" || port == "TRISC" ) saveResultToVar( "TRISC" ); else if( port == "trisd" || port == "TRISD" ) saveResultToVar( "TRISD" ); else saveResultToVar( "TRISE" ); } m_pCode->append( new Instr_bcf(Register("STATUS"),"5") );//commented } void PIC14::Sasm(const QString &raw) { m_pCode->append(new Instr_asm(raw)); } //BEGIN class PortPin PortPin::PortPin( const QString & port, int pin ) { m_port = port.toUpper(); m_pin = pin; } PortPin::PortPin() { m_port = ' '; m_pin = -1; } int PortPin::portPosition() const { if ( m_port.isEmpty() ) return 0; return uchar( m_port[ m_port.length() - 1 ].toLatin1() ) - 'A'; } //END class PortPin diff --git a/microbe/pic14.h b/microbe/pic14.h index 3274bf37..aa3a2097 100644 --- a/microbe/pic14.h +++ b/microbe/pic14.h @@ -1,273 +1,273 @@ /*************************************************************************** * Copyright (C) 2004-2005 by Daniel Clarke * * daniel.jc@gmail.com * * * * 24-04-2007 * * Modified to add pic 16f877,16f627 and 16f628 * * by george john george@space-kerala.org * * supported by SPACE www.space-kerala.org * * * * 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., * * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * ***************************************************************************/ #ifndef PIC14_H #define PIC14_H #include "expression.h" #include "microbe.h" -#include -#include -#include +#include +#include +#include class Code; class Microbe; class Parser; /** @author David Saxton */ class PortPin { public: PortPin( const QString & port, int pin ); /** * Creates an invalid PortPin ( pin() will return -1). */ PortPin(); /** * Returns port (uppercase). */ QString port() const { return m_port; } /** * Returns the port position (e.g. "PORTA" is 0, "PORTB" is 1, etc). */ int portPosition() const; /** * Returns the pin (-1 == invalid PortPin). */ int pin() const { return m_pin; } protected: QString m_port; int m_pin; }; typedef QList PortPinList; /** @author Daniel Clarke @author David Saxton */ class PIC14 { public: enum Type { P16C84, P16F84, P16F627, P16F628, P16F877, unknown }; enum LocationType { num = 1, work = 2, var = 3 }; /** * Used in determining which delay subroutine should be created. */ enum DelaySubroutine { Delay_None = 0, Delay_3uS = 1, Delay_768uS = 2, Delay_200mS = 3, Delay_50S = 4 }; /*PIC14::*/PIC14( Microbe * master, Type type ); ~PIC14(); /** * Tries to convert the string to a PIC type, returning unknown if * unsuccessful. */ static Type toType( const QString & text ); /** * @return the PIC type. */ Type type() const { return m_type; } /** * @return the Type as a string without the P at the front. */ QString minimalTypeString() const; /** * Translates the portPinString (e.g. "PORTA.2") into a PortPin if the port * and pin combination is valid (otherwise returns an invalid PortPin with * a pin of -1. */ PortPin toPortPin( const QString & portPinString ); /** * Returns the address that the General Purpose Registers starts at. */ uchar gprStart() const; void setParser(Parser *parser) { m_parser = parser; } void setCode( Code * code ) { m_pCode = code; } void mergeCode( Code * code ); void setConditionalCode( Code * ifCode, Code * elseCode ); Code * ifCode(); Code * elseCode(); Code * m_ifCode; Code * m_elseCode; void postCompileConstruct( const QStringList &interrupts ); /** * @returns whether or not the port is valid. * @see isValidPortPin */ bool isValidPort( const QString & portName ) const; /** * @returns whether or not the port and pin is a valid combination. * @see isValidPort */ bool isValidPortPin( const PortPin & portPin ) const; bool isValidTris( const QString & trisName ) const; bool isValidInterrupt( const QString & interruptName ) const; ///modified new function isValidRegister is added ****************** bool isValidRegister( const QString & interruptName ) const; // bool isValidRegisterBit( const QString & interruptName ) const; //TODO GIE=high //******************************modification ends*********************************** void Sgoto(const QString &label); void Slabel(const QString &label); void Send(); void Ssubroutine(const QString &procName, Code * compiledProcCode); void Sinterrupt(const QString & procName, Code * compiledProcCode); void Scall(const QString &name); void Ssetlh( const PortPin & portPin, bool high); void add( QString val1, QString val2, LocationType val1Type, LocationType val2Type ); void subtract( const QString & val1, const QString & val2, LocationType val1Type, LocationType val2Type ); void mul( QString val1, QString val2, LocationType val1Type, LocationType val2Type); void div( const QString & val1, const QString & val2, LocationType val1Type, LocationType val2Type); void assignNum(const QString & val); void assignVar(const QString & val); void saveToReg(const QString &dest); /** * Move the contents of the working register to the register with the given * name. */ void saveResultToVar( const QString & var ); /** Code for "==" */ void equal( const QString &val1, const QString &val2, LocationType val1Type, LocationType val2Type ); /** Code for "!=" */ void notEqual( const QString &val1, const QString &val2, LocationType val1Type, LocationType val2Type ); /** Code for ">" */ void greaterThan( const QString &val1, const QString &val2, LocationType val1Type, LocationType val2Type ); /** Code for "<" */ void lessThan( const QString &val1, const QString &val2, LocationType val1Type, LocationType val2Type ); /** Code for ">=" */ void greaterOrEqual( const QString &val1, const QString &val2, LocationType val1Type, LocationType val2Type ); /** Code for "<=" */ void lessOrEqual( const QString &val1, const QString &val2, LocationType val1Type, LocationType val2Type ); ///*****modified the function ************** //commented for new function since it is not working // void bitwise( Expression::Operation op, const QString &val1, const QString &val2, bool val1IsNum, bool val2IsNum ); //code for AND OR XOR opertions void bitwise( Expression::Operation op,const QString & val1, const QString & val2, LocationType val1Type, LocationType val2Type); //*******************modification end ---Result --- new code is working well************** void Swhile( Code * whileCode, const QString &expression); void Srepeat( Code * repeatCode, const QString &expression); void Sif( Code * ifCode, Code * elseCode, const QString &expression); void Sfor( Code * forCode, Code * initCode, const QString &expression, const QString &variable, const QString &step, bool stepPositive); void Spin( const PortPin & portPin, bool NOT); void addCommonFunctions( DelaySubroutine delay ); //BEGIN "Special Functionality" functions /** * Delay the program execution, for the given period of length_us (unit: * microseconds). * @param pos the position to add the code for calling the delay subroutine. */ void Sdelay( unsigned length_us, Code::InstructionPosition pos = Code::Middle ); /** * Output the working register to the given seven segment. * @param pinMap The variable giving the pin configuration of the seven * segment. */ void SsevenSegment( const Variable & pinMap ); /** * Read the value of the keypad to the working register. * @param pinMap The variable giving the pin configuration of the keypad. */ void Skeypad( const Variable & pinMap ); //END "Special Functionality" functions void SincVar( const QString &var ); void SdecVar( const QString &var ); void SrotlVar( const QString &var ); void SrotrVar( const QString &var ); void Stristate( const QString &port ); void Sasm(const QString &raw); protected: void multiply(); void divide(); /** @see Microbe::m_picType */ Type m_type; Parser * m_parser; Microbe * mb; Code * m_pCode; void ifInitCode( const QString &val1, const QString &val2, LocationType val1Type, LocationType val2Type ); /** * The function makes sure that val1 always contains a working register * variable, if one has been passed, this is done by swapping val1 and val2 when * neccessary */ void rearrangeOpArguments( QString * val1, QString * val2, LocationType * val1Type, LocationType * val2Type); /** * @param flag True means give flag bit, false means give enable bit instead */ int interruptNameToBit(const QString &name, bool flag); }; #endif diff --git a/microbe/variable.h b/microbe/variable.h index 7af8bf82..033ef3c9 100644 --- a/microbe/variable.h +++ b/microbe/variable.h @@ -1,79 +1,79 @@ /*************************************************************************** * Copyright (C) 2004-2005 by Daniel Clarke daniel.jc@gmail.com * * Copyright (C) 2005 by David Saxton * * * * 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., * * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * ***************************************************************************/ #ifndef VARIABLE_H #define VARIABLE_H -#include -#include +#include +#include class PortPin; typedef QList PortPinList; /** @author Daniel Clarke @author David Saxton */ class Variable { public: enum VariableType { charType, // 8 bit file register sevenSegmentType, // A pin configuration for a seven segment is represented by a write-only variable. keypadType, // A pin configuration for a keypad has 4 rows and n columns (typically n = 3 or 4) - a read-only variable invalidType }; Variable( VariableType type, const QString & name ); Variable(); ~Variable(); VariableType type() const { return m_type; } QString name() const { return m_name; } /** * @returns whether the variable can be read from (e.g. the seven * segment variable cannot). */ bool isReadable() const; /** * @returns whether the variable can be written to (e.g. the keypad * variable cannot). */ bool isWritable() const; /** * @see portPinList */ void setPortPinList( const PortPinList & portPinList ); /** * Used in seven-segments and keypads, */ PortPinList portPinList() const { return m_portPinList; } protected: VariableType m_type; QString m_name; PortPinList m_portPinList; }; typedef QList VariableList; #endif diff --git a/src/asmformatter.h b/src/asmformatter.h index 891c4e75..e79c5acc 100644 --- a/src/asmformatter.h +++ b/src/asmformatter.h @@ -1,76 +1,76 @@ /*************************************************************************** * Copyright (C) 2003-2005 by David Saxton * * david@bluehaze.org * * * * 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. * ***************************************************************************/ #ifndef ASMFORMATTER_H #define ASMFORMATTER_H -#include +#include /** @author David Saxton */ class InstructionParts { public: /** * Breaks up the line into parts. */ InstructionParts( QString line ); QString label() const { return m_label; } QString operand() const { return m_operand; } QString operandData() const { return m_operandData; } QString comment() const { return m_comment; } protected: QString m_label; QString m_operand; QString m_operandData; QString m_comment; ///< includes the ";" part }; /** @author David Saxton */ class AsmFormatter { public: AsmFormatter(); ~AsmFormatter(); enum LineType { Equ, Instruction, // could include label Other // eg comments, __config }; QString tidyAsm( QStringList lines ); static LineType lineType( QString line ); protected: QString tidyInstruction( const QString & line ); QString tidyEqu( const QString & line ); /** * Appends spaces to the end of text until it is greater or equakl to * length. */ static void pad( QString & text, int length ); int m_indentAsmName; int m_indentAsmData; int m_indentEqu; int m_indentEquValue; int m_indentEquComment; int m_indentComment; }; #endif diff --git a/src/canvas.cpp b/src/canvas.cpp index f6b0b096..e50e104d 100644 --- a/src/canvas.cpp +++ b/src/canvas.cpp @@ -1,1442 +1,1440 @@ /*************************************************************************** * Copyright (C) 1999-2005 Trolltech AS * * Copyright (C) 2006 David Saxton * * * * This file may be distributed and/or modified under the terms of the * * GNU General Public License version 2 as published by the Free * * Software Foundation * ***************************************************************************/ #include "utils.h" #include "canvas.h" #include "canvas_private.h" +#include "ktlq3polygonscanner.h" -#include +#include -#include "qapplication.h" -#include "qbitmap.h" +#include +#include +#include //#include "q3ptrdict.h" -#include "qpainter.h" -#include "ktlq3polygonscanner.h" -#include "qtimer.h" +#include +#include // #include "q3tl.h" // #include // needed for q3polygonscanner -#include -#include - #include using namespace std; static bool isCanvasDebugEnabled() { return false; } //BEGIN class KtlQCanvasClusterizer static void include(QRect& r, const QRect& rect) { if (rect.left()r.right()) { r.setRight(rect.right()); } if (rect.top()r.bottom()) { r.setBottom(rect.bottom()); } } /* A KtlQCanvasClusterizer groups rectangles (QRects) into non-overlapping rectangles by a merging heuristic. */ KtlQCanvasClusterizer::KtlQCanvasClusterizer(int maxclusters) : cluster(new QRect[maxclusters]), count(0), maxcl(maxclusters) { } KtlQCanvasClusterizer::~KtlQCanvasClusterizer() { delete [] cluster; } void KtlQCanvasClusterizer::clear() { count=0; } void KtlQCanvasClusterizer::add(int x, int y) { add(QRect(x,y,1,1)); } void KtlQCanvasClusterizer::add(int x, int y, int w, int h) { add(QRect(x,y,w,h)); } void KtlQCanvasClusterizer::add(const QRect& rect) { QRect biggerrect(rect.x()-1,rect.y()-1,rect.width()+2,rect.height()+2); //assert(rect.width()>0 && rect.height()>0); int cursor; for (cursor=0; cursor=0) { include(cluster[cheapest],rect); return; } if (count < maxcl) { cluster[count++]=rect; return; } // Do cheapest of: // add to closest cluster // do cheapest cluster merge, add to new cluster lowestcost=9999999; cheapest=-1; cursor=0; while( cursor=0) { include(cluster[cheapestmerge1],cluster[cheapestmerge2]); cluster[cheapestmerge2]=cluster[count--]; } else { // if (!cheapest) debugRectangles(rect); include(cluster[cheapest],rect); } // NB: clusters do not intersect (or intersection will // overwrite). This is a result of the above algorithm, // given the assumption that (x,y) are ordered topleft // to bottomright. // ### // // add explicit x/y ordering to that comment, move it to the top // and rephrase it as pre-/post-conditions. } const QRect& KtlQCanvasClusterizer::operator[](int i) { return cluster[i]; } //END class KtlQCanvasClusterizer static int gcd(int a, int b) { int r; while ( (r = a%b) ) { a=b; b=r; } return b; } static int scm(int a, int b) { int g = gcd(a,b); return a/g*b; } int KtlQCanvas::toChunkScaling( int x ) const { return roundDown( x, chunksize ); } void KtlQCanvas::initChunkSize( const QRect & s ) { m_chunkSize = QRect( toChunkScaling(s.left()), toChunkScaling(s.top()), ((s.width()-1)/chunksize)+3, ((s.height()-1)/chunksize)+3 ); } void KtlQCanvas::init(int w, int h, int chunksze, int mxclusters) { init( QRect( 0, 0, w, h ), chunksze, mxclusters ); } void KtlQCanvas::init( const QRect & r, int chunksze, int mxclusters ) { m_size = r ; chunksize=chunksze; maxclusters=mxclusters; initChunkSize( r ); chunks=new KtlQCanvasChunk[m_chunkSize.width()*m_chunkSize.height()]; update_timer = nullptr; bgcolor = Qt::white; grid = nullptr; htiles = 0; vtiles = 0; debug_redraw_areas = false; } KtlQCanvas::KtlQCanvas( QObject* parent, const char* name ) : QObject( parent /*, name*/ ) { setObjectName( name ); init(0,0); } KtlQCanvas::KtlQCanvas(const int w, const int h) { init(w,h); } KtlQCanvas::KtlQCanvas( QPixmap p, int h, int v, int tilewidth, int tileheight ) { init(h*tilewidth, v*tileheight, scm(tilewidth,tileheight) ); setTiles( p, h, v, tilewidth, tileheight ); } void qt_unview( KtlQCanvas * c ) { for (QList::iterator itView =c->m_viewList.begin(); itView != c->m_viewList.end(); ++itView) { KtlQCanvasView* view = *itView; view->viewing = nullptr; } } KtlQCanvas::~KtlQCanvas() { qt_unview(this); KtlQCanvasItemList all = allItems(); for (KtlQCanvasItemList::Iterator it=all.begin(); it!=all.end(); ++it) delete *it; delete [] chunks; delete [] grid; } /*! \internal Returns the chunk at a chunk position \a i, \a j. */ KtlQCanvasChunk& KtlQCanvas::chunk(int i, int j) const { i -= m_chunkSize.left(); j -= m_chunkSize.top(); //return chunks[i+m_chunkSize.width()*j]; const int chunkOffset = i + m_chunkSize.width() * j; if ((chunkOffset < 0) || (chunkOffset >= (m_chunkSize.width()*m_chunkSize.height())) ) { qWarning() << Q_FUNC_INFO << " invalid chunk coordinates: " << i << " " << j; return chunks[0]; // at least it should not crash } return chunks[chunkOffset]; } /*! \internal Returns the chunk at a pixel position \a x, \a y. */ KtlQCanvasChunk& KtlQCanvas::chunkContaining(int x, int y) const { return chunk( toChunkScaling(x), toChunkScaling(y) ); } KtlQCanvasItemList KtlQCanvas::allItems() { KtlQCanvasItemList list; SortedCanvasItems::iterator end = m_canvasItems.end(); for ( SortedCanvasItems::iterator it = m_canvasItems.begin(); it != end; ++it ) list << it->second; return list; } void KtlQCanvas::resize( const QRect & newSize ) { if ( newSize == m_size ) return; KtlQCanvasItem* item; QList hidden; SortedCanvasItems::iterator end = m_canvasItems.end(); for ( SortedCanvasItems::iterator it = m_canvasItems.begin(); it != end; ++it ) { KtlQCanvasItem * i = it->second; if ( i->isVisible() ) { i->hide(); hidden.append( i ); } } initChunkSize( newSize ); KtlQCanvasChunk* newchunks = new KtlQCanvasChunk[m_chunkSize.width()*m_chunkSize.height()]; m_size = newSize; delete [] chunks; chunks=newchunks; for (QList::iterator itItem = hidden.begin(); itItem != hidden.end(); ++itItem) { KtlQCanvasItem* item = *itItem; item->show(); } // for (item=hidden.first(); item != 0; item=hidden.next()) { // 2018.08.14 - use QList // item->show(); // } setAllChanged(); emit resized(); } void KtlQCanvas::retune(int chunksze, int mxclusters) { maxclusters=mxclusters; if ( chunksize!=chunksze ) { QList hidden; SortedCanvasItems::iterator end = m_canvasItems.end(); for ( SortedCanvasItems::iterator it = m_canvasItems.begin(); it != end; ++it ) { KtlQCanvasItem * i = it->second; if ( i->isVisible() ) { i->hide(); hidden.append( i ); } } chunksize=chunksze; initChunkSize( m_size ); KtlQCanvasChunk* newchunks = new KtlQCanvasChunk[m_chunkSize.width()*m_chunkSize.height()]; delete [] chunks; chunks=newchunks; for (QList::iterator itItem = hidden.begin(); itItem != hidden.end(); ++itItem) { KtlQCanvasItem* item = *itItem; item->show(); } } } void KtlQCanvas::addItem(KtlQCanvasItem* item) { m_canvasItems.insert( make_pair( item->z(), item ) ); } void KtlQCanvas::removeItem(const KtlQCanvasItem* item) { SortedCanvasItems::iterator end = m_canvasItems.end(); for ( SortedCanvasItems::iterator it = m_canvasItems.begin(); it != end; ++it ) { if ( it->second == item ) { m_canvasItems.erase( it ); return; } } } void KtlQCanvas::addView(KtlQCanvasView* view) { m_viewList.append(view); if ( htiles>1 || vtiles>1 || pm.isNull() ) { //view->viewport()->setBackgroundColor(backgroundColor()); // 2018.11.21 QPalette palette; palette.setColor(view->viewport()->backgroundRole(), backgroundColor()); view->viewport()->setPalette(palette); } } void KtlQCanvas::removeView(KtlQCanvasView* view) { m_viewList.removeAll(view); } void KtlQCanvas::setUpdatePeriod(int ms) { if ( ms<0 ) { if ( update_timer ) update_timer->stop(); } else { if ( update_timer ) delete update_timer; update_timer = new QTimer(this); connect(update_timer,SIGNAL(timeout()),this,SLOT(update())); update_timer->start(ms); } } // Don't call this unless you know what you're doing. // p is in the content's co-ordinate example. void KtlQCanvas::drawViewArea( KtlQCanvasView* view, QPainter* p, const QRect& vr, bool dbuf /* always false */) { QPoint tl = view->contentsToViewport(QPoint(0,0)); QMatrix wm = view->worldMatrix(); QMatrix iwm = wm.inverted(); // ivr = covers all chunks in vr QRect ivr = iwm.mapRect(vr); QMatrix twm; twm.translate(tl.x(),tl.y()); // QRect all(0,0,width(),height()); QRect all(m_size); if (!p->isActive()) { qWarning() << Q_FUNC_INFO << " painter is not active"; } if ( !all.contains(ivr) ) { // Need to clip with edge of canvas. // For translation-only transformation, it is safe to include the right // and bottom edges, but otherwise, these must be excluded since they // are not precisely defined (different bresenham paths). QPolygon a; if ( wm.m12()==0.0 && wm.m21()==0.0 && wm.m11() == 1.0 && wm.m22() == 1.0 ) a = QPolygon( QRect(all.x(),all.y(),all.width()+1,all.height()+1) ); else a = QPolygon( all ); a = (wm*twm).map(a); //if ( view->viewport()->backgroundMode() == Qt::NoBackground ) // 2018.12.02 QWidget *vp = view->viewport(); if ( vp->palette().color( vp->backgroundRole() ) == QColor(Qt::transparent) ) { QRect cvr = vr; cvr.translate(tl.x(),tl.y()); p->setClipRegion(QRegion(cvr)-QRegion(a)); p->fillRect(vr,view->viewport()->palette() .brush(QPalette::Active, QPalette::Window)); } p->setClipRegion(a); } #if 0 // 2018.03.11 - dbuf is always false if ( dbuf ) { offscr = QPixmap(vr.width(), vr.height()); offscr.x11SetScreen(p->device()->x11Screen()); //QPainter dbp(&offscr); QPainter dbp; const bool isSuccess = dbp.begin(&offscr); if (!isSuccess) { qWarning() << Q_FUNC_INFO << " painter not active"; } twm.translate(-vr.x(),-vr.y()); twm.translate(-tl.x(),-tl.y()); dbp.setWorldMatrix( wm*twm, true ); // 2015.11.27 - do not clip, in order to fix drawing of garbage on the screen. //dbp.setClipRect(0,0,vr.width(), vr.height()); // dbp.setClipRect(v); drawCanvasArea(ivr,&dbp,false); dbp.end(); p->drawPixmap(vr.x(), vr.y(), offscr, 0, 0, vr.width(), vr.height()); } else #endif { QRect r = vr; r.translate(tl.x(),tl.y()); // move to untransformed co-ords if ( !all.contains(ivr) ) { QRegion inside = p->clipRegion() & r; //QRegion outside = p->clipRegion() - r; //p->setClipRegion(outside); //p->fillRect(outside.boundingRect(),red); // 2015.11.27 - do not clip, in order to fix drawing of garbage on the screen. //p->setClipRegion(inside); } else { // 2015.11.27 - do not clip, in order to fix drawing of garbage on the screen. //p->setClipRect(r); } p->setWorldMatrix( wm*twm ); p->setBrushOrigin(tl.x(), tl.y()); drawCanvasArea(ivr,p,false); } } void KtlQCanvas::advance() { qWarning() << "KtlQCanvas::advance: TODO"; // TODO } /*! Repaints changed areas in all views of the canvas. */ void KtlQCanvas::update() { KtlQCanvasClusterizer clusterizer(m_viewList.count()); QList doneareas; //Q3PtrListIterator it(m_viewList); // 2018.08.14 - see below //KtlQCanvasView* view; //while( (view=it.current()) != 0 ) { // ++it; // for (QList::iterator itView = m_viewList.begin(); itView != m_viewList.end(); ++itView) { KtlQCanvasView* view = *itView; QMatrix wm = view->worldMatrix(); QRect area(view->contentsX(),view->contentsY(), view->visibleWidth(),view->visibleHeight()); if (area.width()>0 && area.height()>0) { if ( !wm.isIdentity() ) { // r = Visible area of the canvas where there are changes QRect r = changeBounds(view->inverseWorldMatrix().mapRect(area)); if ( !r.isEmpty() ) { // as of my testing, drawing below always fails, so just post for an update event to the widget view->viewport()->update(); #if 0 //view->viewport()->setAttribute(Qt::WA_PaintOutsidePaintEvent, true); // note: remove this when possible //QPainter p(view->viewport()); QPainter p; const bool startSucces = p.begin( view->viewport() ); if (!startSucces) { qWarning() << Q_FUNC_INFO << " painter is not active "; } // Translate to the coordinate system of drawViewArea(). QPoint tl = view->contentsToViewport(QPoint(0,0)); p.translate(tl.x(),tl.y()); // drawViewArea( view, &p, wm.map(r), true ); #endif doneareas.append(r); } } else clusterizer.add(area); } } for (int i=0; i::iterator itDone = doneareas.begin(); itDone != doneareas.end(); ++itDone) { setUnchanged( *itDone ); } } /*! Marks the whole canvas as changed. All views of the canvas will be entirely redrawn when update() is called next. */ void KtlQCanvas::setAllChanged() { setChanged(m_size); } /*! Marks \a area as changed. This \a area will be redrawn in all views that are showing it when update() is called next. */ void KtlQCanvas::setChanged(const QRect& area) { QRect thearea = area.intersect( m_size ); int mx = toChunkScaling(thearea.x()+thearea.width()+chunksize); int my = toChunkScaling(thearea.y()+thearea.height()+chunksize); if (mx > m_chunkSize.right()) mx=m_chunkSize.right(); if (my > m_chunkSize.bottom()) my=m_chunkSize.bottom(); int x= toChunkScaling(thearea.x()); while( x m_chunkSize.right()) mx=m_chunkSize.right(); if (my > m_chunkSize.bottom()) my=m_chunkSize.bottom(); int x = toChunkScaling(thearea.x()); while( x m_chunkSize.right()) mx=m_chunkSize.right(); if (my > m_chunkSize.bottom()) my=m_chunkSize.bottom(); QRect result; int x = toChunkScaling(area.x()); while( x m_chunkSize.right()) mx=m_chunkSize.right(); if (my > m_chunkSize.bottom()) my=m_chunkSize.bottom(); int x = toChunkScaling(area.x()); while( x=m_chunkSize.right()) mx=m_chunkSize.right()-1; if (my>=m_chunkSize.bottom()) my=m_chunkSize.bottom()-1; // Stores the region within area that need to be drawn. It is relative // to area.topLeft() (so as to keep within bounds of 16-bit XRegions) QRegion rgn; for (int x=lx; x<=mx; x++) { for (int y=ly; y<=my; y++) { // Only reset change if all views updating, and // wholy within area. (conservative: ignore entire boundary) // // Disable this to help debugging. // if (!p) { if ( chunk(x,y).takeChange() ) { // ### should at least make bands rgn |= QRegion( x*chunksize-area.x(), y*chunksize-area.y(), chunksize,chunksize ); // allvisible += *chunk(x,y).listPtr(); setNeedRedraw( chunk(x,y).listPtr() ); // chunk(x,y).listPtr()->first()->m_bNeedRedraw = true; } } else { // allvisible += *chunk(x,y).listPtr(); setNeedRedraw( chunk(x,y).listPtr() ); } } } // allvisible.sort(); #if 0 // 2018.03.11 - double buffer is always false if ( double_buffer ) { offscr = QPixmap(area.width(), area.height()); if (p) offscr.x11SetScreen(p->device()->x11Screen()); } if ( double_buffer && !offscr.isNull() ) { QPainter painter; const bool isSucces = painter.begin(&offscr); if (!isSucces) { qWarning() << Q_FUNC_INFO << " " << __LINE__ << " painter not active "; } painter.translate(-area.x(),-area.y()); painter.setBrushOrigin(-area.x(),-area.y()); if ( p ) { painter.setClipRect(QRect(0,0,area.width(),area.height())); } else { painter.setClipRegion(rgn); } if (!painter.isActive()) { qWarning() << Q_FUNC_INFO << " " << __LINE__ << " painter is not active"; } drawBackground(painter,area); // allvisible.drawUnique(painter); drawChangedItems( painter ); drawForeground(painter,area); painter.end(); if ( p ) { p->drawPixmap( area.x(), area.y(), offscr, 0, 0, area.width(), area.height() ); return; } } else #endif if ( p ) { drawBackground(*p,area); // allvisible.drawUnique(*p); drawChangedItems( *p ); drawForeground(*p,area); return; } QPoint trtr; // keeps track of total translation of rgn trtr -= area.topLeft(); for ( QList::iterator itView = m_viewList.begin(); itView != m_viewList.end(); ++itView) { KtlQCanvasView* view = *itView; if ( !view->worldMatrix().isIdentity() ) continue; // Cannot paint those here (see callers). // as of my testing, drawing below always fails, so just post for an update event to the widget view->viewport()->update(); #if 0 //view->viewport()->setAttribute(Qt::WA_PaintOutsidePaintEvent, true); // note: remove this when possible //QPainter painter(view->viewport()); QPainter painter; const bool isSuccess = painter.begin(view->viewport()); static int paintSuccessCount = 0; static int paintFailCount = 0; if (!isSuccess) { //qWarning() << Q_FUNC_INFO << " on view " << view << " viewport " << view->viewport(); qWarning() << Q_FUNC_INFO << " " << __LINE__ << " painter not active, applying workaround"; // TODO fix this workaround for repainting: the painter would try to draw to the widget outside of a paint event, // which is not expected to work. Thus this code just sends an update() to the widget, ensuring correct painting ++paintFailCount; qWarning() << Q_FUNC_INFO << " paint success: " << paintSuccessCount << ", fail: " << paintFailCount; view->viewport()->update(); continue; } else { ++paintSuccessCount; } QPoint tr = view->contentsToViewport(area.topLeft()); QPoint nrtr = view->contentsToViewport(QPoint(0,0)); // new translation QPoint rtr = nrtr - trtr; // extra translation of rgn trtr += rtr; // add to total if (double_buffer) { rgn.translate(rtr.x(),rtr.y()); painter.setClipRegion(rgn); painter.drawPixmap(tr,offscr, QRect(QPoint(0,0),area.size())); } else { painter.translate(nrtr.x(),nrtr.y()); rgn.translate(rtr.x(),rtr.y()); painter.setClipRegion(rgn); drawBackground(painter,area); // allvisible.drawUnique(painter); drawChangedItems( painter ); drawForeground(painter,area); painter.translate(-nrtr.x(),-nrtr.y()); } #endif } } void KtlQCanvas::setNeedRedraw( const KtlQCanvasItemList * list ) { KtlQCanvasItemList::const_iterator end = list->end(); for ( KtlQCanvasItemList::const_iterator it = list->begin(); it != end; ++it ) (*it)->setNeedRedraw( true ); } void KtlQCanvas::drawChangedItems( QPainter & painter ) { SortedCanvasItems::iterator end = m_canvasItems.end(); for ( SortedCanvasItems::iterator it = m_canvasItems.begin(); it != end; ++it ) { KtlQCanvasItem * i = it->second; if ( i->needRedraw() ) { i->draw( painter ); i->setNeedRedraw( false ); } } } /*! \internal This method to informs the KtlQCanvas that a given chunk is `dirty' and needs to be redrawn in the next Update. (\a x,\a y) is a chunk location. The sprite classes call this. Any new derived class of KtlQCanvasItem must do so too. SetChangedChunkContaining can be used instead. */ void KtlQCanvas::setChangedChunk(int x, int y) { if (validChunk(x,y)) { KtlQCanvasChunk& ch=chunk(x,y); ch.change(); } } /*! \internal This method to informs the KtlQCanvas that the chunk containing a given pixel is `dirty' and needs to be redrawn in the next Update. (\a x,\a y) is a pixel location. The item classes call this. Any new derived class of KtlQCanvasItem must do so too. SetChangedChunk can be used instead. */ void KtlQCanvas::setChangedChunkContaining(int x, int y) { if ( onCanvas(x, y) ) { KtlQCanvasChunk& chunk=chunkContaining(x,y); chunk.change(); } } /*! \internal This method adds the KtlQCanvasItem \a g to the list of those which need to be drawn if the given chunk at location ( \a x, \a y ) is redrawn. Like SetChangedChunk and SetChangedChunkContaining, this method marks the chunk as `dirty'. */ void KtlQCanvas::addItemToChunk(KtlQCanvasItem* g, int x, int y) { if (validChunk(x, y) ) { chunk(x,y).add(g); } } /*! \internal This method removes the KtlQCanvasItem \a g from the list of those which need to be drawn if the given chunk at location ( \a x, \a y ) is redrawn. Like SetChangedChunk and SetChangedChunkContaining, this method marks the chunk as `dirty'. */ void KtlQCanvas::removeItemFromChunk(KtlQCanvasItem* g, int x, int y) { if (validChunk(x,y)) { chunk(x,y).remove(g); } } /*! \internal This method adds the KtlQCanvasItem \a g to the list of those which need to be drawn if the chunk containing the given pixel ( \a x, \a y ) is redrawn. Like SetChangedChunk and SetChangedChunkContaining, this method marks the chunk as `dirty'. */ void KtlQCanvas::addItemToChunkContaining(KtlQCanvasItem* g, int x, int y) { if ( onCanvas( x, y ) ) { chunkContaining(x,y).add(g); } } /*! \internal This method removes the KtlQCanvasItem \a g from the list of those which need to be drawn if the chunk containing the given pixel ( \a x, \a y ) is redrawn. Like SetChangedChunk and SetChangedChunkContaining, this method marks the chunk as `dirty'. */ void KtlQCanvas::removeItemFromChunkContaining(KtlQCanvasItem* g, int x, int y) { if ( onCanvas( x, y ) ) { chunkContaining(x,y).remove(g); } } /*! Returns the color set by setBackgroundColor(). By default, this is white. This function is not a reimplementation of QWidget::backgroundColor() (KtlQCanvas is not a subclass of QWidget), but all QCanvasViews that are viewing the canvas will set their backgrounds to this color. \sa setBackgroundColor(), backgroundPixmap() */ QColor KtlQCanvas::backgroundColor() const { return bgcolor; } /*! Sets the solid background to be the color \a c. \sa backgroundColor(), setBackgroundPixmap(), setTiles() */ void KtlQCanvas::setBackgroundColor( const QColor& c ) { if ( bgcolor != c ) { bgcolor = c; for (QList::iterator itView = m_viewList.begin(); itView != m_viewList.end(); ++itView) { KtlQCanvasView* view = *itView; /* XXX this doesn't look right. Shouldn't this be more like setBackgroundPixmap? : Ian */ //view->viewport()->setEraseColor( bgcolor ); // 2018.11.21 QWidget *viewportWidg = view->viewport(); QPalette palette; palette.setColor(viewportWidg->backgroundRole(), bgcolor); viewportWidg->setPalette(palette); } setAllChanged(); } } /*! Returns the pixmap set by setBackgroundPixmap(). By default, this is a null pixmap. \sa setBackgroundPixmap(), backgroundColor() */ QPixmap KtlQCanvas::backgroundPixmap() const { return pm; } /*! Sets the solid background to be the pixmap \a p repeated as necessary to cover the entire canvas. \sa backgroundPixmap(), setBackgroundColor(), setTiles() */ void KtlQCanvas::setBackgroundPixmap( const QPixmap& p ) { setTiles(p, 1, 1, p.width(), p.height()); for (QList::iterator itView = m_viewList.begin(); itView != m_viewList.end(); ++itView) { (*itView)->updateContents(); } //KtlQCanvasView* view = m_viewList.first(); // 2018.08.14 - see above //while ( view != 0 ) { // view->updateContents(); // view = m_viewList.next(); //} } /*! This virtual function is called for all updates of the canvas. It renders any background graphics using the painter \a painter, in the area \a clip. If the canvas has a background pixmap or a tiled background, that graphic is used, otherwise the canvas is cleared using the background color. If the graphics for an area change, you must explicitly call setChanged(const QRect&) for the result to be visible when update() is next called. \sa setBackgroundColor(), setBackgroundPixmap(), setTiles() */ void KtlQCanvas::drawBackground(QPainter& painter, const QRect& clip) { painter.fillRect( clip, Qt::white ); if ( pm.isNull() ) painter.fillRect(clip,bgcolor); else if ( !grid ) { for (int x=clip.x()/pm.width(); x<(clip.x()+clip.width()+pm.width()-1)/pm.width(); x++) { for (int y=clip.y()/pm.height(); y<(clip.y()+clip.height()+pm.height()-1)/pm.height(); y++) { painter.drawPixmap(x*pm.width(), y*pm.height(),pm); } } } else { const int x1 = roundDown( clip.left(), tilew ); int x2 = roundDown( clip.right(), tilew ); const int y1 = roundDown( clip.top(), tileh ); int y2 = roundDown( clip.bottom(), tileh ); const int roww = pm.width()/tilew; for (int j=y1; j<=y2; j++) { int tv = tilesVertically(); int jj = ((j%tv)+tv)%tv; for (int i=x1; i<=x2; i++) { int th = tilesHorizontally(); int ii = ((i%th)+th)%th; int t = tile(ii,jj); int tx = t % roww; int ty = t / roww; painter.drawPixmap( i*tilew, j*tileh, pm, tx*tilew, ty*tileh, tilew, tileh ); } } } } void KtlQCanvas::drawForeground(QPainter& painter, const QRect& clip) { if ( debug_redraw_areas ) { painter.setPen( Qt::red ); painter.setBrush(Qt::NoBrush); painter.drawRect(clip); } } void KtlQCanvas::setTiles( QPixmap p, int h, int v, int tilewidth, int tileheight ) { if ( !p.isNull() && (!tilewidth || !tileheight || p.width() % tilewidth != 0 || p.height() % tileheight != 0 ) ) return; htiles = h; vtiles = v; delete[] grid; pm = p; if ( h && v && !p.isNull() ) { grid = new ushort[h*v]; memset( grid, 0, h*v*sizeof(ushort) ); tilew = tilewidth; tileh = tileheight; } else { grid = nullptr; } if ( h + v > 10 ) { int s = scm(tilewidth,tileheight); retune( s < 128 ? s : qMax(tilewidth,tileheight) ); } setAllChanged(); } void KtlQCanvas::setTile( int x, int y, int tilenum ) { ushort& t = grid[x+y*htiles]; if ( t != tilenum ) { t = tilenum; if ( tilew == tileh && tilew == chunksize ) setChangedChunk( x, y ); // common case else setChanged( QRect(x*tilew,y*tileh,tilew,tileh) ); } } KtlQCanvasItemList KtlQCanvas::collisions(const QPoint& p) /* const */ { return collisions(QRect(p,QSize(1,1))); } KtlQCanvasItemList KtlQCanvas::collisions(const QRect& r) /* const */ { KtlQCanvasRectangle *i = new KtlQCanvasRectangle(r, /*(KtlQCanvas*) */ this); // TODO verify here, why is crashing ?! i->setPen( QPen( Qt::NoPen) ); i->show(); // doesn't actually show, since we destroy it KtlQCanvasItemList l = i->collisions(true); delete i; l.sort(); return l; } KtlQCanvasItemList KtlQCanvas::collisions(const QPolygon& chunklist, const KtlQCanvasItem* item, bool exact) const { if (isCanvasDebugEnabled()) { qDebug() << Q_FUNC_INFO << " test item: " << item; for (SortedCanvasItems::const_iterator itIt = m_canvasItems.begin(); itIt != m_canvasItems.end(); ++itIt) { const KtlQCanvasItem *i = itIt->second; qDebug() << " in canvas item: " << i; } qDebug() << "end canvas item list"; } //Q3PtrDict seen; QHash seen; KtlQCanvasItemList result; for (int i=0; i<(int)chunklist.count(); i++) { int x = chunklist[i].x(); int y = chunklist[i].y(); if ( validChunk(x,y) ) { const KtlQCanvasItemList* l = chunk(x,y).listPtr(); for (KtlQCanvasItemList::ConstIterator it=l->begin(); it!=l->end(); ++it) { KtlQCanvasItem *g=*it; if ( g != item ) { //if ( !seen.find(g) ) { if ( seen.find(g) == seen.end() ) { //seen.replace(g,(void*)1); seen.take(g); seen.insert(g, true); //if ( !exact || item->collidesWith(g) ) // result.append(g); if (!exact) { result.append(g); } if (isCanvasDebugEnabled()) { qDebug() <<"test collides " << item << " with " << g; } if (item->collidesWith(g)) { result.append(g); } } } } } } return result; } KtlQCanvasView::KtlQCanvasView(QWidget* parent, const char* name, Qt::WindowFlags f) : KtlQ3ScrollView(parent,name,f /* |Qt::WResizeNoErase |Qt::WStaticContents */) { setAttribute( Qt::WA_StaticContents ); d = new KtlQCanvasViewData; viewing = nullptr; setCanvas(nullptr); connect(this,SIGNAL(contentsMoving(int,int)),this,SLOT(cMoving(int,int))); } KtlQCanvasView::KtlQCanvasView(KtlQCanvas* canvas, QWidget* parent, const char* name, Qt::WindowFlags f) : KtlQ3ScrollView(parent,name,f /* |Qt::WResizeNoErase |Qt::WA_StaticContents */) { setAttribute( Qt::WA_StaticContents ); d = new KtlQCanvasViewData; viewing = nullptr; setCanvas(canvas); connect(this,SIGNAL(contentsMoving(int,int)),this,SLOT(cMoving(int,int))); } KtlQCanvasView::~KtlQCanvasView() { delete d; d = nullptr; setCanvas(nullptr); } void KtlQCanvasView::setCanvas(KtlQCanvas* canvas) { if (viewing) { disconnect(viewing); viewing->removeView(this); } viewing=canvas; if (viewing) { connect(viewing,SIGNAL(resized()), this, SLOT(updateContentsSize())); viewing->addView(this); } if ( d ) // called by d'tor updateContentsSize(); } const QMatrix &KtlQCanvasView::worldMatrix() const { return d->xform; } const QMatrix &KtlQCanvasView::inverseWorldMatrix() const { return d->ixform; } bool KtlQCanvasView::setWorldMatrix( const QMatrix & wm ) { bool ok = wm.isInvertible(); if ( ok ) { d->xform = wm; d->ixform = wm.inverted(); updateContentsSize(); viewport()->update(); } return ok; } void KtlQCanvasView::updateContentsSize() { if ( viewing ) { QRect br; // br = d->xform.map(QRect(0,0,viewing->width(),viewing->height())); br = d->xform.mapRect(viewing->rect()); if ( br.width() < contentsWidth() ) { QRect r(contentsToViewport(QPoint(br.width(),0)), QSize(contentsWidth()-br.width(),contentsHeight())); //viewport()->erase(r); // 2015.11.25 - not recommended to directly repaint viewport()->update(r); } if ( br.height() < contentsHeight() ) { QRect r(contentsToViewport(QPoint(0,br.height())), QSize(contentsWidth(),contentsHeight()-br.height())); //viewport()->erase(r); // 2015.11.25 - not recommended to directly repaint viewport()->update(r); } resizeContents(br.width(),br.height()); } else { //viewport()->erase(); // 2015.11.25 - not recommended to directly repaint viewport()->update(); resizeContents(1,1); } } void KtlQCanvasView::cMoving(int x, int y) { // A little kludge to smooth up repaints when scrolling int dx = x - contentsX(); int dy = y - contentsY(); d->repaint_from_moving = abs(dx) < width() / 8 && abs(dy) < height() / 8; } /*! Repaints part of the KtlQCanvas that the canvas view is showing starting at \a cx by \a cy, with a width of \a cw and a height of \a ch using the painter \a p. \warning When double buffering is enabled, drawContents() will not respect the current settings of the painter when setting up the painter for the double buffer (e.g., viewport() and window()). Also, be aware that KtlQCanvas::update() bypasses drawContents(), which means any reimplementation of drawContents() is not called. \sa setDoubleBuffering() */ void KtlQCanvasView::drawContents(QPainter *p, int cx, int cy, int cw, int ch) { QRect r(cx,cy,cw,ch); r = r.normalized(); if (viewing) { //viewing->drawViewArea(this,p,r,true); viewing->drawViewArea(this,p,r, /*!d->repaint_from_moving*/ false); /* 2018.03.11 - fix build for osx */ d->repaint_from_moving = false; } else { p->eraseRect(r); } } /*! \reimp \internal (Implemented to get rid of a compiler warning.) */ void KtlQCanvasView::drawContents( QPainter *p ) { qDebug() << Q_FUNC_INFO << " called, altough not expected"; drawContents(p, 0, 0, width(), height()); } /*! Suggests a size sufficient to view the entire canvas. */ QSize KtlQCanvasView::sizeHint() const { if ( !canvas() ) return KtlQ3ScrollView::sizeHint(); // TODO QT3 // should maybe take transformations into account return ( canvas()->size() + 2 * QSize(frameWidth(), frameWidth()) ) .boundedTo( 3 * QApplication::desktop()->size() / 4 ); } diff --git a/src/canvas.h b/src/canvas.h index 0b0aeb22..43bc858b 100644 --- a/src/canvas.h +++ b/src/canvas.h @@ -1,192 +1,192 @@ /*************************************************************************** * Copyright (C) 1999-2005 Trolltech AS * * Copyright (C) 2006 David Saxton * * * * This file may be distributed and/or modified under the terms of the * * GNU General Public License version 2 as published by the Free * * Software Foundation * ***************************************************************************/ #ifndef QCANVAS_H #define QCANVAS_H #include #include "ktlqt3support/ktlq3scrollview.h" -#include "qpixmap.h" +#include // #include "q3ptrlist.h" -#include "qbrush.h" -#include "qpen.h" -#include "qlist.h" +#include +#include +#include // #include "q3pointarray.h" // 2018.08.14 #include "canvasitemlist.h" class KtlQCanvasView; class KtlQCanvasChunk; class KtlQCanvas : public QObject { Q_OBJECT public: KtlQCanvas( QObject* parent = nullptr, const char* name = nullptr ); KtlQCanvas( const int w, const int h); KtlQCanvas( QPixmap p, int h, int v, int tilewidth, int tileheight ); ~KtlQCanvas() override; virtual void setTiles( QPixmap tiles, int h, int v, int tilewidth, int tileheight ); virtual void setBackgroundPixmap( const QPixmap& p ); QPixmap backgroundPixmap() const; virtual void setBackgroundColor( const QColor& c ); QColor backgroundColor() const; virtual void setTile( int x, int y, int tilenum ); int tile( int x, int y ) const { return grid[x+y*htiles]; } int tilesHorizontally() const { return htiles; } int tilesVertically() const { return vtiles; } int tileWidth() const { return tilew; } int tileHeight() const { return tileh; } virtual void resize( const QRect & newSize ); int width() const { return size().width(); } int height() const { return size().height(); } QSize size() const { return m_size.size(); } QRect rect() const { return m_size; } bool onCanvas( const int x, const int y ) const { return onCanvas( QPoint( x, y ) ); } bool onCanvas( const QPoint& p ) const { return m_size.contains( p ); } bool validChunk( const int x, const int y ) const { return validChunk( QPoint( x, y ) ); } bool validChunk( const QPoint& p ) const { return m_chunkSize.contains( p ); } int chunkSize() const { return chunksize; } virtual void retune(int chunksize, int maxclusters=100); virtual void setChangedChunk(int i, int j); virtual void setChangedChunkContaining(int x, int y); virtual void setAllChanged(); virtual void setChanged(const QRect& area); virtual void setUnchanged(const QRect& area); // These call setChangedChunk. void addItemToChunk(KtlQCanvasItem*, int i, int j); void removeItemFromChunk(KtlQCanvasItem*, int i, int j); void addItemToChunkContaining(KtlQCanvasItem*, int x, int y); void removeItemFromChunkContaining(KtlQCanvasItem*, int x, int y); KtlQCanvasItemList allItems(); KtlQCanvasItemList collisions( const QPoint&) /* const */ ; KtlQCanvasItemList collisions( const QRect&) /* const */; KtlQCanvasItemList collisions( const QPolygon& pa, const KtlQCanvasItem* item, bool exact) const; void drawArea(const QRect&, QPainter* p); // These are for KtlQCanvasView to call virtual void addView(KtlQCanvasView*); virtual void removeView(KtlQCanvasView*); void drawCanvasArea(const QRect&, QPainter* p, bool double_buffer); void drawViewArea( KtlQCanvasView* view, QPainter* p, const QRect& r, bool dbuf ); // These are for KtlQCanvasItem to call virtual void addItem(KtlQCanvasItem*); virtual void removeItem(const KtlQCanvasItem*); virtual void setUpdatePeriod(int ms); int toChunkScaling( int x ) const; signals: void resized(); public slots: virtual void advance(); virtual void update(); protected: virtual void drawBackground(QPainter&, const QRect& area); virtual void drawForeground(QPainter&, const QRect& area); private: void init(int w, int h, int chunksze=16, int maxclust=100); void init(const QRect & r, int chunksze=16, int maxclust=100); void initChunkSize( const QRect & s ); KtlQCanvasChunk& chunk(int i, int j) const; KtlQCanvasChunk& chunkContaining(int x, int y) const; QRect changeBounds(const QRect& inarea); void drawChanges(const QRect& inarea); void drawChangedItems( QPainter & painter ); void setNeedRedraw( const KtlQCanvasItemList * list ); QPixmap offscr; int chunksize; int maxclusters; QRect m_size; QRect m_chunkSize; KtlQCanvasChunk* chunks; SortedCanvasItems m_canvasItems; QList m_viewList; void initTiles(QPixmap p, int h, int v, int tilewidth, int tileheight); ushort *grid; ushort htiles; ushort vtiles; ushort tilew; ushort tileh; bool oneone; QPixmap pm; QTimer* update_timer; QColor bgcolor; bool debug_redraw_areas; friend void qt_unview(KtlQCanvas* c); KtlQCanvas( const KtlQCanvas & ); KtlQCanvas &operator=( const KtlQCanvas & ); }; class KtlQCanvasViewData; class KtlQCanvasView : public KtlQ3ScrollView { Q_OBJECT public: KtlQCanvasView(QWidget* parent = nullptr, const char* name = nullptr, Qt::WindowFlags f = {}); // 2018.08.15 - unused? KtlQCanvasView(KtlQCanvas* viewing, QWidget* parent = nullptr, const char* name = nullptr, Qt::WindowFlags f = {}); ~KtlQCanvasView() override; KtlQCanvas* canvas() const { return viewing; } void setCanvas(KtlQCanvas* v); const QMatrix &worldMatrix() const; const QMatrix &inverseWorldMatrix() const; bool setWorldMatrix( const QMatrix & ); protected: /** overrides KtlQ3ScrollView::drawContents() */ // override paintEvent? void drawContents( QPainter*, int cx, int cy, int cw, int ch ) override; QSize sizeHint() const override; private: void drawContents( QPainter* ) override; KtlQCanvas* viewing; KtlQCanvasViewData* d; friend void qt_unview(KtlQCanvas* c); KtlQCanvasView( const KtlQCanvasView & ); KtlQCanvasView &operator=( const KtlQCanvasView & ); private slots: void cMoving(int,int); void updateContentsSize(); }; #endif // QCANVAS_H diff --git a/src/canvas_private.h b/src/canvas_private.h index fa96580d..56c66b42 100644 --- a/src/canvas_private.h +++ b/src/canvas_private.h @@ -1,248 +1,248 @@ // // C++ Interface: canvas_private // // Description: // // // Author: Alan Grimes , (C) 2008 // // Copyright: See COPYING file that comes with this distribution // // #ifndef CANVAS_PRIVATE_H #define CANVAS_PRIVATE_H -#include "qbitmap.h" -#include "qimage.h" +#include +#include #include "ktlq3polygonscanner.h" #include "canvasitems.h" class KtlQPolygonalProcessor { public: KtlQPolygonalProcessor(KtlQCanvas* c, const QPolygon& pa) : canvas(c) { QRect pixelbounds = pa.boundingRect(); bounds.setLeft( canvas->toChunkScaling( pixelbounds.left() ) ); bounds.setRight( canvas->toChunkScaling( pixelbounds.right() ) ); bounds.setTop( canvas->toChunkScaling( pixelbounds.top() ) ); bounds.setBottom( canvas->toChunkScaling( pixelbounds.bottom() ) ); //bitmap = QImage(bounds.width(),bounds.height(),1,2,QImage::LittleEndian); // 2018.09.07 - convert to non-deprecated bitmap = QImage(bounds.width(),bounds.height(), QImage::Format_MonoLSB); pnt = 0; bitmap.fill(0); } inline void add(int x, int y) { if ( pnt >= result.size() ) { result.resize(pnt*2+10); } result[pnt++] = QPoint(x+bounds.x(),y+bounds.y()); } inline void addBits(int x1, int x2, uchar newbits, int xo, int yo) { for (int i=x1; i<=x2; i++) if ( newbits & (1<toChunkScaling( pt[j].y() )-bounds.y(); uchar* l = bitmap.scanLine(y); int x = pt[j].x(); int x1 = canvas->toChunkScaling( x )-bounds.x(); int x2 = canvas->toChunkScaling( x+w[j] ) - bounds.x(); int x1q = x1/8; int x1r = x1%8; int x2q = x2/8; int x2r = x2%8; if ( x1q == x2q ) { uchar newbits = (~l[x1q]) & (((2<<(x2r-x1r))-1)<>(7-x2r)); if ( newbits2 ) { addBits(0,x2r,newbits2,x2q*8,y); l[x2q] |= newbits2; } } } result.resize(pnt); } QPolygon result; private: int pnt; KtlQCanvas* canvas; QRect bounds; QImage bitmap; }; class KtlQCanvasViewData { public: KtlQCanvasViewData() : repaint_from_moving( false ) {} QMatrix xform; QMatrix ixform; bool repaint_from_moving; }; class KtlQCanvasClusterizer { public: KtlQCanvasClusterizer(int maxclusters); ~KtlQCanvasClusterizer(); void add(int x, int y); // 1x1 rectangle (point) void add(int x, int y, int w, int h); void add(const QRect& rect); void clear(); int clusters() { return count; } const QRect& operator[](int i); private: QRect* cluster; int count; const int maxcl; }; class KtlQCanvasItemPtr { public: KtlQCanvasItemPtr() : ptr(nullptr) { } KtlQCanvasItemPtr( KtlQCanvasItem* p ) : ptr(p) { } bool operator<=(const KtlQCanvasItemPtr& that) const { // Order same-z objects by identity. if (that.ptr->z()==ptr->z()) return that.ptr <= ptr; return that.ptr->z() <= ptr->z(); } bool operator<(const KtlQCanvasItemPtr& that) const { // Order same-z objects by identity. if (that.ptr->z()==ptr->z()) return that.ptr < ptr; return that.ptr->z() < ptr->z(); } bool operator>(const KtlQCanvasItemPtr& that) const { // Order same-z objects by identity. if (that.ptr->z()==ptr->z()) return that.ptr > ptr; return that.ptr->z() > ptr->z(); } bool operator==(const KtlQCanvasItemPtr& that) const { return that.ptr == ptr; } operator KtlQCanvasItem*() const { return ptr; } private: KtlQCanvasItem* ptr; }; class KtlQCanvasChunk { public: KtlQCanvasChunk() : changed(true) { } // Other code assumes lists are not deleted. Assignment is also // done on ChunkRecs. So don't add that sort of thing here. void sort() { list.sort(); } const KtlQCanvasItemList* listPtr() const { return &list; } void add(KtlQCanvasItem* item) { list.prepend(item); changed = true; } void remove(KtlQCanvasItem* item) { list.removeAll(item); changed = true; } void change() { changed = true; } bool hasChanged() const { return changed; } bool takeChange() { bool y = changed; changed = false; return y; } private: KtlQCanvasItemList list; bool changed; }; class KtlQCanvasPolygonScanner : public KtlQ3PolygonScanner { KtlQPolygonalProcessor& processor; public: KtlQCanvasPolygonScanner(KtlQPolygonalProcessor& p) : processor(p) { } void processSpans( int n, QPoint* point, int* width ) override { processor.doSpans(n,point,width); } }; // lesser-used data in canvas item, plus room for extension. // Be careful adding to this - check all usages. class KtlQCanvasItemExtra { KtlQCanvasItemExtra() /* : vx(0.0), vy(0.0) */ { } //double vx,vy; // 2017.10.01 - commented unused members friend class KtlQCanvasItem; }; #endif diff --git a/src/canvasitemlist.cpp b/src/canvasitemlist.cpp index 7375581c..f9820e77 100644 --- a/src/canvasitemlist.cpp +++ b/src/canvasitemlist.cpp @@ -1,29 +1,29 @@ /*************************************************************************** * Copyright (C) 1999-2005 Trolltech AS * * Copyright (C) 2006 David Saxton * * * * This file may be distributed and/or modified under the terms of the * * GNU General Public License version 2 as published by the Free * * Software Foundation * ***************************************************************************/ #include "canvasitemlist.h" //#include -#include +#include void KtlQCanvasItemList::sort() { //qHeapSort(* /* ((QList*)) */ this); // 2018.12.07 qSort( *this ); } KtlQCanvasItemList KtlQCanvasItemList::operator+(const KtlQCanvasItemList &l) const { KtlQCanvasItemList l2(*this); for(const_iterator it = l.begin(); it != l.end(); ++it) l2.append(*it); return l2; } diff --git a/src/canvasitemlist.h b/src/canvasitemlist.h index 9d197ab7..2b42f421 100644 --- a/src/canvasitemlist.h +++ b/src/canvasitemlist.h @@ -1,30 +1,30 @@ /*************************************************************************** * Copyright (C) 1999-2005 Trolltech AS * * Copyright (C) 2006 David Saxton * * * * This file may be distributed and/or modified under the terms of the * * GNU General Public License version 2 as published by the Free * * Software Foundation * ***************************************************************************/ #ifndef KTL_CANVASITEMLIST_H_ #define KTL_CANVASITEMLIST_H_ -#include +#include #include class KtlQCanvasItem; typedef std::multimap< double, KtlQCanvasItem* > SortedCanvasItems; class KtlQCanvasItemList : public QList { public: void sort(); KtlQCanvasItemList operator+(const KtlQCanvasItemList &l) const; }; #endif // KTL_CANVASITEMLIST_H_ diff --git a/src/canvasitemparts.cpp b/src/canvasitemparts.cpp index c791a936..e3a9fc21 100644 --- a/src/canvasitemparts.cpp +++ b/src/canvasitemparts.cpp @@ -1,621 +1,621 @@ /*************************************************************************** * Copyright (C) 2003-2005 by David Saxton * * david@bluehaze.org * * * * 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 "canvasitemparts.h" #include "cells.h" #include "cnitem.h" #include "icndocument.h" -#include -#include -#include +#include +#include +#include //BEGIN Class GuiPart GuiPart::GuiPart( CNItem *parent, const QRect & r, KtlQCanvas * canvas ) : //QObject(parent), KtlQCanvasRectangle( r, canvas ), m_angleDegrees(0), p_parent(parent), b_pointsAdded(false), m_originalRect(r) { connect( parent, SIGNAL(movedBy(double, double )), this, SLOT(slotMoveBy(double, double )) ); setZ( parent->z() + 0.5 ); } GuiPart::~GuiPart() { hide(); } void GuiPart::setAngleDegrees( int angleDegrees ) { m_angleDegrees = angleDegrees; posChanged(); if (canvas()) canvas()->setChanged( boundingRect() ); } void GuiPart::setGuiPartSize( int width, int height ) { updateConnectorPoints(false); setSize( width, height ); posChanged(); } void GuiPart::initPainter( QPainter &p ) { if ( (m_angleDegrees%180) == 0 ) return; p.translate( int(x()+(width()/2)), int(y()+(height()/2)) ); p.rotate(m_angleDegrees); p.translate( -int(x()+(width()/2)), -int(y()+(height()/2)) ); } void GuiPart::deinitPainter( QPainter &p ) { if ( (m_angleDegrees%180) == 0 ) return; p.translate( int(x()+(width()/2)), int(y()+(height()/2)) ); p.rotate(-m_angleDegrees); p.translate( -int(x()+(width()/2)), -int(y()+(height()/2)) ); } void GuiPart::slotMoveBy( double dx, double dy ) { if ( dx==0 && dy==0 ) return; moveBy( dx, dy ); posChanged(); } void GuiPart::updateConnectorPoints( bool add ) { ICNDocument *icnd = dynamic_cast(p_parent->itemDocument()); if ( !icnd) return; Cells * cells = icnd->cells(); if (!cells) return; if ( !isVisible() ) add = false; if ( add == b_pointsAdded ) return; b_pointsAdded = add; int mult = add ? 1 : -1; int sx = roundDown( x(), 8 ); int sy = roundDown( y(), 8 ); int ex = roundDown( x()+width(), 8 ); int ey = roundDown( y()+height(), 8 ); for ( int x=sx; x<=ex; ++x ) { for ( int y=sy; y<=ey; ++y ) { if ( cells->haveCell( x, y ) ) cells->cell( x, y ).CIpenalty += mult*ICNDocument::hs_item/2; } } } QRect GuiPart::drawRect() { QRect dr = rect(); if ( m_angleDegrees%180 != 0 ) { QMatrix m; m.translate( int(x()+(width()/2)), int(y()+(height()/2)) ); if ( (m_angleDegrees%180) != 0 ) m.rotate(-m_angleDegrees); m.translate( -int(x()+(width()/2)), -int(y()+(height()/2)) ); dr = m.mapRect(dr); } return dr; } //END Class GuiPart //BEGIN Class Text Text::Text( const QString &text, CNItem *parent, const QRect & r, KtlQCanvas * canvas, int flags ) : GuiPart( parent, r, canvas ) { m_flags = flags; setText(text); } Text::~Text() { } bool Text::setText( const QString & text ) { if ( m_text == text ) return false; updateConnectorPoints(false); m_text = text; return true; } void Text::setFlags( int flags ) { updateConnectorPoints( false ); m_flags = flags; } void Text::drawShape( QPainter & p ) { initPainter(p); p.setFont( p_parent->font() ); p.drawText( drawRect(), m_flags, m_text ); deinitPainter(p); } QRect Text::recommendedRect() const { return QFontMetrics( p_parent->font() ).boundingRect( m_originalRect.x(), m_originalRect.y(), m_originalRect.width(), m_originalRect.height(), m_flags, m_text ); } //END Class Text //BEGIN Class Widget Widget::Widget( const QString & id, CNItem * parent, const QRect & r, KtlQCanvas * canvas ) : GuiPart( parent, r, canvas ) { m_id = id; show(); } Widget::~Widget() { } void Widget::setEnabled( bool enabled ) { widget()->setEnabled(enabled); } void Widget::posChanged() { // Swap around the width / height if we are rotated at a non-half way around if ( m_angleDegrees%90 != 0 ) widget()->setFixedSize( QSize( height(), width() ) ); else widget()->setFixedSize( size() ); widget()->move( int(x()), int(y()) ); } void Widget::drawShape( QPainter &p ) { // initPainter(p); //p.drawPixmap( int(x()), int(y()), QPixmap::grabWidget( widget() ) ); // 2019.05.06 p.drawPixmap( int(x()), int(y()), widget()->grab() ); // deinitPainter(p); } //END Class Widget //BEGIN Class ToolButton ToolButton::ToolButton( QWidget *parent ) : QToolButton(parent) { m_angleDegrees = 0; if ( QFontInfo(m_font).pixelSize() > 11 ) // It has to be > 11, not > 12, as (I think) pixelSize() rounds off the actual size m_font.setPixelSize(12); } void ToolButton::drawButtonLabel( QPainter * p ) { if ( m_angleDegrees % 180 == 0 || text().isEmpty() ) { //QToolButton::drawButtonLabel(p); QToolButton::render(p); return; } double dx = size().width()/2; double dy = size().height()/2; p->translate( dx, dy ); p->rotate( m_angleDegrees ); p->translate( -dx, -dy ); p->translate( -dy+dx, 0 ); int m = width() > height() ? width() : height(); p->setPen( Qt::black ); p->drawText( isDown()?1:0, isDown()?1:0, m, m, Qt::AlignVCenter | Qt::AlignHCenter, text() ); p->translate( dy-dx, 0 ); p->translate( dx, dy ); p->rotate( -m_angleDegrees ); p->translate( -dx, -dy ); } //END Class ToolButton //BEGIN Class Button Button::Button( const QString & id, CNItem * parent, bool isToggle, const QRect & r, KtlQCanvas * canvas ) : Widget( id, parent, r, canvas ) { b_isToggle = isToggle; m_button = new ToolButton(nullptr); m_button->setToolButtonStyle(Qt::ToolButtonIconOnly); m_button->setCheckable(b_isToggle); connect( m_button, SIGNAL(pressed()), this, SLOT(slotStateChanged()) ); connect( m_button, SIGNAL(released()), this, SLOT(slotStateChanged()) ); posChanged(); } Button::~Button() { delete m_button; } void Button::setToggle( bool toggle ) { if ( b_isToggle == toggle ) return; if (b_isToggle) { // We must first untoggle it, else it'll be forever stuck... setState(false); } b_isToggle = toggle; m_button->setCheckable(b_isToggle); } void Button::posChanged() { Widget::posChanged(); m_button->setAngleDegrees(m_angleDegrees); } void Button::slotStateChanged() { parent()->buttonStateChanged( id(), m_button->isDown() || m_button->isChecked() ); } QWidget* Button::widget() const { return m_button; } void Button::setIcon( const QIcon &icon ) { m_button->setIcon(icon); } void Button::setState( bool state ) { if ( this->state() == state ) return; if ( isToggle() ) m_button->setChecked(state); else m_button->setDown(state); slotStateChanged(); } bool Button::state() const { if ( isToggle() ) return m_button->isChecked(); //was: state() else return m_button->isDown(); } QRect Button::recommendedRect() const { QSize sizeHint = m_button->sizeHint(); if ( sizeHint.width() < m_originalRect.width() ) sizeHint.setWidth( m_originalRect.width() ); // Hmm...for now, lets just keep the recomended rect the same height as the original rect sizeHint.setHeight( m_originalRect.height() ); int hdw = (sizeHint.width() - m_originalRect.width())/2; int hdh = (sizeHint.height() - m_originalRect.height())/2; return QRect( m_originalRect.x()-hdw, m_originalRect.y()-hdh, sizeHint.width(), sizeHint.height() ); } void Button::setText( const QString &text ) { if ( m_button->text() == text ) return; updateConnectorPoints(false); m_button->setToolButtonStyle(Qt::ToolButtonTextUnderIcon); m_button->setText(text); m_button->setToolTip(text); canvas()->setChanged( rect() ); p_parent->updateAttachedPositioning(); } void Button::mousePressEvent( QMouseEvent *e ) { if ( !m_button->isEnabled() ) return; QMouseEvent event( QEvent::MouseButtonPress, e->pos()-QPoint(int(x()),int(y())), e->button(), // e->state() // 2018.12.02 e->buttons(), e->modifiers() ); m_button->mousePressEvent(&event); if (event.isAccepted()) e->accept(); canvas()->setChanged( rect() ); } void Button::mouseReleaseEvent( QMouseEvent *e ) { QMouseEvent event( QEvent::MouseButtonRelease, e->pos()-QPoint(int(x()),int(y())), e->button(), //e->state() e->buttons(), e->modifiers() ); m_button->mouseReleaseEvent(&event); if (event.isAccepted()) e->accept(); canvas()->setChanged( rect() ); } void Button::enterEvent(QEvent *) { m_button->enterEvent(nullptr); // m_button->setFocus(); // bool hasFocus = m_button->hasFocus(); // m_button->setAutoRaise(true); // m_button->setChecked(true); } void Button::leaveEvent(QEvent *) { m_button->leaveEvent(nullptr); // m_button->clearFocus(); // bool hasFocus = m_button->hasFocus(); // m_button->setAutoRaise(false); // m_button->setChecked(false); } //END Class Button //BEGIN Class SliderWidget SliderWidget::SliderWidget( QWidget *parent ) : QSlider(parent) { //setWFlags(Qt::WNoAutoErase|Qt::WRepaintNoErase); //setWindowFlags(/*Qt::WNoAutoErase | */ Qt::WRepaintNoErase); } //END Class SliderWidget //BEGIN Class Slider Slider::Slider( const QString & id, CNItem * parent, const QRect & r, KtlQCanvas * canvas ) : Widget( id, parent, r, canvas ) { m_orientation = Qt::Vertical; m_bSliderInverted = false; m_slider = new SliderWidget(nullptr); QPalette p; p.setColor(m_slider->foregroundRole(), Qt::white); p.setColor(m_slider->backgroundRole(), Qt::transparent); //m_slider->setPaletteBackgroundColor(Qt::white); // 2018.12.02 //m_slider->setPaletteForegroundColor(Qt::white); //m_slider->setEraseColor(Qt::white); //m_slider->setBackgroundMode( Qt::NoBackground ); m_slider->setPalette(p); connect( m_slider, SIGNAL(valueChanged(int)), this, SLOT(slotValueChanged(int)) ); posChanged(); } Slider::~Slider() { delete m_slider; } QWidget* Slider::widget() const { return m_slider; } int Slider::value() const { if ( m_bSliderInverted ) { // Return the value as if the slider handle was reflected along through // the center of the slide. return m_slider->maximum() + m_slider->minimum() - m_slider->value(); } else return m_slider->value(); } void Slider::setValue( int value ) { if ( m_bSliderInverted ) { value = m_slider->maximum() + m_slider->minimum() - value; } m_slider->setValue( value ); if ( canvas() ) canvas()->setChanged( rect() ); } void Slider::mousePressEvent( QMouseEvent *e ) { qDebug() << Q_FUNC_INFO << "pos " << e->pos() << " x " << int(x()) << " y " << int(y()) << " b " << e->button() << " bs " << e->buttons() << " m " << e->modifiers() ; QMouseEvent event( QEvent::MouseButtonPress, e->pos()-QPoint(int(x()),int(y())), e->button(), e->buttons(), e->modifiers() //e->state() // 2018.12.02 ); m_slider->mousePressEvent(&event); if (event.isAccepted()) { qDebug() << Q_FUNC_INFO << "accepted " << e; e->accept(); } canvas()->setChanged( rect() ); } void Slider::mouseReleaseEvent( QMouseEvent *e ) { qDebug() << Q_FUNC_INFO << "pos " << e->pos() << " x " << int(x()) << " y " << int(y()) << " b " << e->button() << " bs " << e->buttons() << " m " << e->modifiers() ; QMouseEvent event( QEvent::MouseButtonRelease, e->pos()-QPoint(int(x()),int(y())), e->button(), e->buttons(), e->modifiers() //e->state() // 2018.12.02 ); m_slider->mouseReleaseEvent(&event); if (event.isAccepted()) { qDebug() << Q_FUNC_INFO << "accepted " << e; e->accept(); } canvas()->setChanged( rect() ); } void Slider::mouseDoubleClickEvent ( QMouseEvent *e ) { QMouseEvent event( QEvent::MouseButtonDblClick, e->pos()-QPoint(int(x()),int(y())), e->button(), e->buttons(), e->modifiers() //e->state() // 2018.12.02 ); m_slider->mouseDoubleClickEvent(&event); if (event.isAccepted()) e->accept(); canvas()->setChanged( rect() ); } void Slider::mouseMoveEvent( QMouseEvent *e ) { QMouseEvent event( QEvent::MouseMove, e->pos()-QPoint(int(x()),int(y())), e->button(), e->buttons(), e->modifiers() //e->state() //2018.12.02 ); m_slider->mouseMoveEvent(&event); if (event.isAccepted()) e->accept(); } void Slider::wheelEvent( QWheelEvent *e ) { QWheelEvent event( e->pos()-QPoint(int(x()),int(y())), e->delta(), e->buttons(), e->modifiers(), // e->state(), e->orientation() ); m_slider->wheelEvent(&event); if (event.isAccepted()) e->accept(); canvas()->setChanged( rect() ); } void Slider::enterEvent(QEvent *e) { qDebug() << Q_FUNC_INFO; m_slider->enterEvent(e); } void Slider::leaveEvent(QEvent *e) { qDebug() << Q_FUNC_INFO; m_slider->leaveEvent(e); } void Slider::slotValueChanged( int value ) { if ( parent()->itemDocument() ) parent()->itemDocument()->setModified(true); // Note that we do not use value as we want to take into account rotation (void)value; parent()->sliderValueChanged( id(), this->value() ); if ( canvas() ) canvas()->setChanged( rect() ); } void Slider::setOrientation( Qt::Orientation o ) { m_orientation = o; posChanged(); } void Slider::posChanged() { Widget::posChanged(); bool nowInverted; if ( m_orientation == Qt::Vertical ) { nowInverted = angleDegrees() == 90 || angleDegrees() == 180; m_slider->setOrientation( (m_angleDegrees%180 == 0) ? Qt::Vertical : Qt::Horizontal ); } else { nowInverted = angleDegrees() == 0 || angleDegrees() == 90; m_slider->setOrientation( (m_angleDegrees%180 == 0) ? Qt::Horizontal : Qt::Vertical ); } if ( nowInverted != m_bSliderInverted ) { int prevValue = value(); m_bSliderInverted = nowInverted; setValue( prevValue ); } } //END Class Slider diff --git a/src/canvasitemparts.h b/src/canvasitemparts.h index b138fe75..e73c841b 100644 --- a/src/canvasitemparts.h +++ b/src/canvasitemparts.h @@ -1,293 +1,293 @@ /*************************************************************************** * Copyright (C) 2003-2005 by David Saxton * * david@bluehaze.org * * * * 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. * ***************************************************************************/ #ifndef CANVASITEMPARTS_H #define CANVASITEMPARTS_H //#include // 2018.10.16 - not needed #include "canvasitems.h" -#include -#include -#include +#include +#include +#include #include class Cells; class CIWidgetMgr; class CNItem; class SliderWidget; class ToolButton; class QString; class GuiPart : /* public QObject, */ public KtlQCanvasRectangle { Q_OBJECT public: /** * Create a GuiPart. Control the position using setGuiPartSize, instead * of calling KtlQCanvasRectangle::setSize. This allows GuiPart to know * when its size has been changed */ GuiPart( CNItem *parent, const QRect & r, KtlQCanvas * canvas ); ~GuiPart() override; virtual QRect recommendedRect() const { return m_originalRect; } void setOriginalRect( const QRect & r ) { m_originalRect = r; } virtual void updateConnectorPoints( bool add ); /** * Set the angle that the GuiPart draws itself (if the GuiPart chooses * to use it by calling initPainter and deinitPainter from drawShape). * Note that this doesn't affect the rectangle position that the * GuiPart is in. The rotation is taken to be about the center of the * rectangle. */ void setAngleDegrees( int angleDegrees ); /** * Control the size. Call this instead of KtlQCanvasRectangle::setSize. In * turn, this function will notify subclasses via posChanged(); */ void setGuiPartSize( int width, int height ); /** * Returns the rectangle to draw in to compensate for rotation of * the QPainter */ QRect drawRect(); int angleDegrees() const { return m_angleDegrees; } CNItem *parent() const { return p_parent; } protected: /** * Called when the size or angle changes */ virtual void posChanged() {;} /** * Rotate / etc the painter. You must call deinitPainter after * calling this function. */ void initPainter( QPainter & p ); /** * Complement function to initPainter - restores painter to normal * transform */ void deinitPainter( QPainter & p ); int m_angleDegrees; CNItem *p_parent; bool b_pointsAdded; QRect m_originalRect; private slots: void slotMoveBy( double dx, double dy ); }; /** @short Stores internal information about text associated with CNItem @author David Saxton */ class Text : public GuiPart { Q_OBJECT public: Text( const QString &text, CNItem *parent, const QRect & r, KtlQCanvas * canvas, int flags = Qt::AlignHCenter | Qt::AlignVCenter ); ~Text() override; /** * Set the text, returning true if the size of this Text on the canvas * has changed. */ bool setText( const QString & text ); QRect recommendedRect() const override; void drawShape ( QPainter & p ) override; /** * The text flags (see QPainter::drawText) - Qt::AlignmentFlags and * Qt::TextFlags OR'd together. */ int flags() const { return m_flags; } /** * @see flags */ void setFlags( int flags ); protected: QString m_text; int m_flags; }; typedef QMap > TextMap; /** @short Base class for embedding Qt Widgets into the canvas @author David Saxton */ class Widget : public GuiPart { public: Widget( const QString & id, CNItem *parent, const QRect & r, KtlQCanvas * canvas ); ~Widget() override; virtual QWidget *widget() const = 0; QString id() const { return m_id; } /** * Set the widget enabled/disabled */ void setEnabled( bool enabled ); virtual void enterEvent(QEvent *) {}; virtual void leaveEvent(QEvent *) {}; /** * Mouse was pressed. pos is given relative to CNItem position. */ virtual void mousePressEvent( QMouseEvent *e ) { Q_UNUSED(e); } /** * Mouse was released. pos is given relative to CNItem position. */ virtual void mouseReleaseEvent( QMouseEvent *e ) { Q_UNUSED(e); } /** * Mouse was double clicked. pos is given relative to CNItem position. */ virtual void mouseDoubleClickEvent( QMouseEvent *e ) { Q_UNUSED(e); } /** * Mouse was moved. pos is given relative to CNItem position. */ virtual void mouseMoveEvent( QMouseEvent *e ) { Q_UNUSED(e); } /** * Mouse was scrolled. pos is given relative to CNItem position. */ virtual void wheelEvent( QWheelEvent *e ) { Q_UNUSED(e); } void drawShape( QPainter &p ) override; protected: void posChanged() override; QString m_id; }; class ToolButton : public QToolButton { public: ToolButton( QWidget* parent ); void mousePressEvent( QMouseEvent *e ) override { QToolButton::mousePressEvent(e); } void mouseReleaseEvent( QMouseEvent *e ) override { QToolButton::mouseReleaseEvent(e); } void mouseDoubleClickEvent ( QMouseEvent *e ) override { QToolButton::mouseDoubleClickEvent(e); } void mouseMoveEvent( QMouseEvent *e ) override { QToolButton::mouseMoveEvent(e); } void wheelEvent( QWheelEvent *e ) override { QToolButton::wheelEvent(e); } void enterEvent(QEvent *) override { QToolButton::enterEvent(nullptr); } void leaveEvent(QEvent *) override { QToolButton::leaveEvent(nullptr); } void setAngleDegrees( int angleDegrees ) { m_angleDegrees = angleDegrees; } protected: virtual void drawButtonLabel( QPainter * p ); int m_angleDegrees; QFont m_font; }; /** @short Stores internal information about button associated with CNItem @author David Saxton */ class Button : public Widget { Q_OBJECT public: Button( const QString & id, CNItem *parent, bool isToggle, const QRect &r, KtlQCanvas *canvas ); ~Button() override; void mousePressEvent( QMouseEvent *e ) override; void mouseReleaseEvent( QMouseEvent *e ) override; void enterEvent(QEvent *) override; void leaveEvent(QEvent *) override; /** * Set the text displayed inside the button */ void setText( const QString &text ); void setToggle( bool toggle ); bool isToggle() const { return b_isToggle; } QWidget *widget() const override; bool state() const; void setIcon( const QIcon & ); void setState( bool state ); QRect recommendedRect() const override; protected: void posChanged() override; private slots: void slotStateChanged(); private: bool b_isToggle; // i.e. whether it should be depressed when the mouse is released ToolButton *m_button; }; class SliderWidget : public QSlider { public: SliderWidget( QWidget* parent ); void mousePressEvent( QMouseEvent *e ) override { QSlider::mousePressEvent(e); } void mouseReleaseEvent( QMouseEvent *e ) override { QSlider::mouseReleaseEvent(e); } void mouseDoubleClickEvent ( QMouseEvent *e ) override { QSlider::mouseDoubleClickEvent(e); } void mouseMoveEvent( QMouseEvent *e ) override { QSlider::mouseMoveEvent(e); } void wheelEvent( QWheelEvent *e ) override { QSlider::wheelEvent(e); } void enterEvent(QEvent *) override { QSlider::enterEvent(nullptr); } void leaveEvent(QEvent *) override { QSlider::leaveEvent(nullptr); } }; /** @short Stores internal information about a QSlider associated with CNItem @author David Saxton */ class Slider : public Widget { Q_OBJECT public: Slider( const QString & id, CNItem *parent, const QRect & r, KtlQCanvas * canvas ); ~Slider() override; void mousePressEvent( QMouseEvent *e ) override; void mouseReleaseEvent( QMouseEvent *e ) override; void mouseDoubleClickEvent ( QMouseEvent *e ) override; void mouseMoveEvent( QMouseEvent *e ) override; void wheelEvent( QWheelEvent *e ) override; void enterEvent(QEvent *) override; void leaveEvent(QEvent *) override; QWidget *widget() const override; int value() const; void setValue( int value ); void setOrientation( Qt::Orientation o ); protected: void posChanged() override; private slots: void slotValueChanged( int value ); private: bool m_bSliderInverted; ///< In some orientations, the slider is reflected SliderWidget *m_slider; Qt::Orientation m_orientation; }; #endif diff --git a/src/canvasitems.cpp b/src/canvasitems.cpp index 397655e8..29a27fa0 100644 --- a/src/canvasitems.cpp +++ b/src/canvasitems.cpp @@ -1,758 +1,758 @@ /*************************************************************************** * Copyright (C) 1999-2005 Trolltech AS * * Copyright (C) 2006 David Saxton * * * * This file may be distributed and/or modified under the terms of the * * GNU General Public License version 2 as published by the Free * * Software Foundation * ***************************************************************************/ #include "canvasitems.h" #include "canvas.h" #include "canvas_private.h" -#include -#include -#include -#include -#include -#include +#include +#include +#include +#include +#include +#include static bool isCanvasDebugEnabled() { return false; } KtlQCanvasItem::KtlQCanvasItem(KtlQCanvas* canvas) : val(false), myx(0), myy(0), myz(0), cnv(canvas), ext(nullptr), m_bNeedRedraw(true), vis(false), sel(false) { if (isCanvasDebugEnabled()) { qDebug() << Q_FUNC_INFO << " this=" << this; } if (cnv) cnv->addItem(this); } KtlQCanvasItem::~KtlQCanvasItem() { if (cnv) cnv->removeItem(this); delete ext; } KtlQCanvasItemExtra& KtlQCanvasItem::extra() { if ( !ext ) ext = new KtlQCanvasItemExtra; return *ext; } void KtlQCanvasItem::setZ(double a) { if ( myz == a ) return; // remove and then add the item so that z-ordered list in canvas is updated if (cnv) cnv->removeItem(this); myz=a; changeChunks(); if (cnv) cnv->addItem(this); } void KtlQCanvasItem::moveBy(const double dx, const double dy ) { if ( dx || dy ) { removeFromChunks(); myx += dx; myy += dy; addToChunks(); } } void KtlQCanvasItem::move(const double x, const double y ) { moveBy( x-myx, y-myy ); } void KtlQCanvasItem::setCanvas(KtlQCanvas* c) { bool v=isVisible(); setVisible(false); if (cnv) { cnv->removeItem(this); } cnv=c; if (cnv) { cnv->addItem(this); } setVisible(v); } void KtlQCanvasItem::show() { setVisible(true); } void KtlQCanvasItem::hide() { setVisible(false); } void KtlQCanvasItem::setVisible(bool yes) { if (vis != yes) { if (yes) { vis=(uint)yes; addToChunks(); } else { removeFromChunks(); vis=(uint)yes; } } } void KtlQCanvasItem::setSelected(const bool yes) { if ((bool)sel!=yes) { sel=(uint)yes; changeChunks(); } } static bool collision_double_dispatch( const KtlQCanvasPolygonalItem* p1, const KtlQCanvasRectangle* r1, const KtlQCanvasEllipse* e1, const KtlQCanvasPolygonalItem* p2, const KtlQCanvasRectangle* r2, const KtlQCanvasEllipse* e2 ) { const KtlQCanvasItem *i1 = nullptr; if (p1) { i1 = p1; } else { if (r1) { i1 = r1; } else { i1 = e1; } } const KtlQCanvasItem *i2 = nullptr; if (i2) { i2 = p2; } else { if (r2) { i2 = r2; } else { i2 = e2; } } // const KtlQCanvasItem* i1 = p1 ? // (const KtlQCanvasItem*)p1 : r1 ? // (const KtlQCanvasItem*)r1 : (const KtlQCanvasItem*)e1; // const KtlQCanvasItem* i2 = p2 ? // (const KtlQCanvasItem*)p2 : r2 ? // (const KtlQCanvasItem*)r2 : (const KtlQCanvasItem*)e2; if ( r1 && r2 ) { // b QRect rc1 = i1->boundingRect(); QRect rc2 = i2->boundingRect(); return rc1.intersects(rc2); } else if ( e1 && e2 && e1->angleLength()>=360*16 && e2->angleLength()>=360*16 && e1->width()==e1->height() && e2->width()==e2->height() ) { // c double xd = (e1->x())-(e2->x()); double yd = (e1->y())-(e2->y()); double rd = (e1->width()+e2->width())/2; return xd*xd+yd*yd <= rd*rd; } else if ( p1 && p2 ) { // d QPolygon pa1 = p1->areaPoints(); QPolygon pa2 = p2 ? p2->areaPoints() : QPolygon(i2->boundingRect()); bool col= !(QRegion(pa1) & QRegion(pa2, /* true */ Qt::WindingFill )).isEmpty(); return col; } else { return collision_double_dispatch(p2,r2,e2,p1,r1,e1); } } bool KtlQCanvasPolygonalItem::collidesWith( const KtlQCanvasItem* i ) const { return i->collidesWith(this,nullptr,nullptr); } bool KtlQCanvasPolygonalItem::collidesWith( const KtlQCanvasPolygonalItem* p, const KtlQCanvasRectangle* r, const KtlQCanvasEllipse* e ) const { return collision_double_dispatch(p,r,e,this,nullptr,nullptr); } bool KtlQCanvasRectangle::collidesWith( const KtlQCanvasItem* i ) const { return i->collidesWith(this,this,nullptr); } bool KtlQCanvasRectangle::collidesWith( const KtlQCanvasPolygonalItem* p, const KtlQCanvasRectangle* r, const KtlQCanvasEllipse* e ) const { return collision_double_dispatch(p,r,e,this,this,nullptr); } bool KtlQCanvasEllipse::collidesWith( const KtlQCanvasItem* i ) const { return i->collidesWith(this,nullptr,this); } bool KtlQCanvasEllipse::collidesWith( const KtlQCanvasPolygonalItem* p, const KtlQCanvasRectangle* r, const KtlQCanvasEllipse* e ) const { return collision_double_dispatch(p,r,e,this,nullptr,this); } KtlQCanvasItemList KtlQCanvasItem::collisions(const bool exact) const { return canvas()->collisions(chunks(),this,exact); } void KtlQCanvasItem::addToChunks() { if (isVisible() && canvas()) { QPolygon pa = chunks(); for (int i=0; i<(int)pa.count(); i++) canvas()->addItemToChunk(this,pa[i].x(),pa[i].y()); val = true; } } void KtlQCanvasItem::removeFromChunks() { if (isVisible() && canvas()) { QPolygon pa = chunks(); for (int i=0; i<(int)pa.count(); i++) canvas()->removeItemFromChunk(this,pa[i].x(),pa[i].y()); } } void KtlQCanvasItem::changeChunks() { if (isVisible() && canvas()) { if (!val) addToChunks(); QPolygon pa = chunks(); for (int i=0; i<(int)pa.count(); i++) canvas()->setChangedChunk(pa[i].x(),pa[i].y()); } } QPolygon KtlQCanvasItem::chunks() const { QPolygon r; int n=0; QRect br = boundingRect(); if (isVisible() && canvas()) { br &= canvas()->rect(); if ( br.isValid() ) { r.resize((canvas()->toChunkScaling( br.width() )+2)*(canvas()->toChunkScaling( br.height() )+2)); for (int j=canvas()->toChunkScaling(br.top()); j<=canvas()->toChunkScaling(br.bottom()); j++) { for (int i=canvas()->toChunkScaling(br.left()); i<=canvas()->toChunkScaling(br.right()); i++) { r[n++] = QPoint(i,j); } } } } r.resize(n); return r; } /* Since most polygonal items don't have a pen, the default is NoPen and a black brush. */ static const QPen& defaultPolygonPen() { static QPen* dp = nullptr; if ( !dp ) dp = new QPen; return *dp; } static const QBrush& defaultPolygonBrush() { static QBrush* db = nullptr; if ( !db ) db = new QBrush; return *db; } KtlQCanvasPolygonalItem::KtlQCanvasPolygonalItem(KtlQCanvas* canvas) : KtlQCanvasItem(canvas), br(defaultPolygonBrush()), pn(defaultPolygonPen()), wind(false) { if (isCanvasDebugEnabled()) { qDebug() << "created KtlQCanvasPolygonalItem at " << this; } } KtlQCanvasPolygonalItem::~KtlQCanvasPolygonalItem() { if (isCanvasDebugEnabled()) { qDebug() << "destroying KtlQCanvasPolygonalItem at " << this; } } bool KtlQCanvasPolygonalItem::winding() const { return wind; } void KtlQCanvasPolygonalItem::setWinding(bool enable) { wind = enable; } void KtlQCanvasPolygonalItem::invalidate() { val = false; removeFromChunks(); } QPolygon KtlQCanvasPolygonalItem::chunks() const { QPolygon pa = areaPoints(); if ( !pa.size() ) { pa.detach(); // Explicit sharing is stupid. return pa; } KtlQPolygonalProcessor processor(canvas(),pa); scanPolygon(pa, wind, processor); return processor.result; } QPolygon KtlQCanvasRectangle::chunks() const { // No need to do a polygon scan! return KtlQCanvasItem::chunks(); } QRect KtlQCanvasPolygonalItem::boundingRect() const { return areaPoints().boundingRect(); } void KtlQCanvasPolygonalItem::draw(QPainter & p) { p.setPen(pn); p.setBrush(br); drawShape(p); } void KtlQCanvasPolygonalItem::setPen( const QPen & p ) { if ( pn == p ) return; pn.setColor( p.color() ); // if only the color was different, then don't need to re-add to chunks if ( pn == p ) { changeChunks(); } else { // Have to re-add to chunks as e.g. pen width might have changed removeFromChunks(); pn = p; addToChunks(); } } void KtlQCanvasPolygonalItem::setBrush( const QBrush & b ) { if ( br != b) { br = b; changeChunks(); } } KtlQCanvasPolygon::KtlQCanvasPolygon(KtlQCanvas* canvas) : KtlQCanvasPolygonalItem(canvas) , guardBef() , poly(new QPolygon) , guardAft() { if (isCanvasDebugEnabled()) { qDebug() << Q_FUNC_INFO << " this=" << this; } } KtlQCanvasPolygon::~KtlQCanvasPolygon() { hide(); delete poly; } void KtlQCanvasPolygon::drawShape(QPainter & p) { // ### why can't we draw outlines? We could use drawPolyline for it. Lars // ### see other message. Warwick p.setPen(Qt::NoPen); // since QRegion(QPolygon) excludes outline :-( )-: p.drawPolygon(*poly); } void KtlQCanvasPolygon::setPoints(QPolygon pa) { removeFromChunks(); *poly = pa; poly->detach(); // Explicit sharing is stupid. poly->translate((int)x(),(int)y()); addToChunks(); } void KtlQCanvasPolygon::moveBy(double dx, double dy) { // Note: does NOT call KtlQCanvasPolygonalItem::moveBy(), since that // only does half this work. // int idx = int(x()+dx)-int(x()); int idy = int(y()+dy)-int(y()); if ( idx || idy ) { removeFromChunks(); poly->translate(idx,idy); } myx+=dx; myy+=dy; if ( idx || idy ) { addToChunks(); } } QPolygon KtlQCanvasPolygon::points() const { QPolygon pa = areaPoints(); pa.translate(int(-x()),int(-y())); return pa; } QPolygon KtlQCanvasPolygon::areaPoints() const { return QPolygon( *poly ); // ->copy(); // 2018.06.02 - copy is only in QPolygon } // ### mark: Why don't we offer a constructor that lets the user set the // points -- that way for some uses just the constructor call would be // required? KtlQCanvasLine::KtlQCanvasLine(KtlQCanvas* canvas) : KtlQCanvasPolygonalItem(canvas) { if (isCanvasDebugEnabled()) { qDebug() << Q_FUNC_INFO << " this=" << this; } x1 = y1 = x2 = y2 = 0; } KtlQCanvasLine::~KtlQCanvasLine() { hide(); } void KtlQCanvasLine::setPen( const QPen & p ) { KtlQCanvasPolygonalItem::setPen(p); } void KtlQCanvasLine::setPoints(int xa, int ya, int xb, int yb) { if ( x1 != xa || x2 != xb || y1 != ya || y2 != yb ) { removeFromChunks(); x1 = xa; y1 = ya; x2 = xb; y2 = yb; addToChunks(); } } void KtlQCanvasLine::drawShape(QPainter &p) { p.drawLine((int)(x()+x1), (int)(y()+y1), (int)(x()+x2), (int)(y()+y2)); } QPolygon KtlQCanvasLine::areaPoints() const { QPolygon p(4); int xi = int(x()); int yi = int(y()); int pw = pen().width(); int dx = abs(x1-x2); int dy = abs(y1-y2); pw = pw*4/3+2; // approx pw*sqrt(2) int px = x1 dy ? (dx*2/dy <= 2) : (dy*2/dx <= 2)) ) { // steep if ( px == py ) { p[0] = QPoint(x1+xi ,y1+yi+py); p[1] = QPoint(x2+xi-px,y2+yi ); p[2] = QPoint(x2+xi ,y2+yi-py); p[3] = QPoint(x1+xi+px,y1+yi ); } else { p[0] = QPoint(x1+xi+px,y1+yi ); p[1] = QPoint(x2+xi ,y2+yi-py); p[2] = QPoint(x2+xi-px,y2+yi ); p[3] = QPoint(x1+xi ,y1+yi+py); } } else if ( dx > dy ) { // horizontal p[0] = QPoint(x1+xi+px,y1+yi+py); p[1] = QPoint(x2+xi-px,y2+yi+py); p[2] = QPoint(x2+xi-px,y2+yi-py); p[3] = QPoint(x1+xi+px,y1+yi-py); } else { // vertical p[0] = QPoint(x1+xi+px,y1+yi+py); p[1] = QPoint(x2+xi+px,y2+yi-py); p[2] = QPoint(x2+xi-px,y2+yi-py); p[3] = QPoint(x1+xi-px,y1+yi+py); } return p; } void KtlQCanvasLine::moveBy(double dx, double dy) { KtlQCanvasPolygonalItem::moveBy(dx, dy); } KtlQCanvasRectangle::KtlQCanvasRectangle(KtlQCanvas* canvas) : KtlQCanvasPolygonalItem(canvas), w(32), h(32) { setObjectName("KtlQCanvasRectangle"); if (isCanvasDebugEnabled()) { qDebug() << Q_FUNC_INFO << " this=" << this; } } KtlQCanvasRectangle::KtlQCanvasRectangle(const QRect& r, KtlQCanvas* canvas) : KtlQCanvasPolygonalItem(canvas), w(r.width()), h(r.height()) { setObjectName("KtlQCanvasRectangle"); move(r.x(),r.y()); if (isCanvasDebugEnabled()) { qDebug() << Q_FUNC_INFO << " this=" << this; } } KtlQCanvasRectangle::KtlQCanvasRectangle(int x, int y, int width, int height, KtlQCanvas* canvas) : KtlQCanvasPolygonalItem(canvas), w(width), h(height) { setObjectName("KtlQCanvasRectangle"); move(x,y); if (isCanvasDebugEnabled()) { qDebug() << Q_FUNC_INFO << " this=" << this; } } KtlQCanvasRectangle::~KtlQCanvasRectangle() { hide(); } int KtlQCanvasRectangle::width() const { return w; } int KtlQCanvasRectangle::height() const { return h; } void KtlQCanvasRectangle::setSize(const int width, const int height) { if ( w != width || h != height ) { removeFromChunks(); w = width; h = height; addToChunks(); } } QPolygon KtlQCanvasRectangle::areaPoints() const { QPolygon pa(4); int pw = (pen().width()+1)/2; if ( pw < 1 ) pw = 1; if ( pen() == Qt::NoPen ) pw = 0; pa[0] = QPoint((int)x()-pw,(int)y()-pw); pa[1] = pa[0] + QPoint(w+pw*2,0); pa[2] = pa[1] + QPoint(0,h+pw*2); pa[3] = pa[0] + QPoint(0,h+pw*2); return pa; } void KtlQCanvasRectangle::drawShape(QPainter &p) { p.drawRect((int)x(), (int)y(), w, h); } KtlQCanvasEllipse::KtlQCanvasEllipse(KtlQCanvas* canvas) : KtlQCanvasPolygonalItem(canvas), w(32), h(32), a1(0), a2(360*16) { if (isCanvasDebugEnabled()) { qDebug() << Q_FUNC_INFO << " this=" << this; } } /*! Constructs a \a width by \a height pixel ellipse, centered at (0, 0) on \a canvas. */ KtlQCanvasEllipse::KtlQCanvasEllipse(int width, int height, KtlQCanvas* canvas) : KtlQCanvasPolygonalItem(canvas), w(width),h(height), a1(0),a2(360*16) { if (isCanvasDebugEnabled()) { qDebug() << Q_FUNC_INFO << " this=" << this; } } KtlQCanvasEllipse::KtlQCanvasEllipse(int width, int height, int startangle, int angle, KtlQCanvas* canvas) : KtlQCanvasPolygonalItem(canvas), w(width),h(height), a1(startangle),a2(angle) { if (isCanvasDebugEnabled()) { qDebug() << Q_FUNC_INFO << " this=" << this; } } KtlQCanvasEllipse::~KtlQCanvasEllipse() { hide(); } int KtlQCanvasEllipse::width() const { return w; } int KtlQCanvasEllipse::height() const { return h; } void KtlQCanvasEllipse::setSize(int width, int height) { if ( w != width || h != height ) { removeFromChunks(); w = width; h = height; addToChunks(); } } void KtlQCanvasEllipse::setAngles(int start, int length) { if ( a1 != start || a2 != length ) { removeFromChunks(); a1 = start; a2 = length; addToChunks(); } } QPolygon KtlQCanvasEllipse::areaPoints() const { //Q3PointArray r; // 2018.08.14 - see below // // makeArc at 0,0, then translate so that fixed point math doesn't overflow //r.makeArc(int(x()-w/2.0+0.5)-1, int(y()-h/2.0+0.5)-1, w+3, h+3, a1, a2); QPainterPath path; path.arcTo(int(x()-w/2.0+0.5)-1, int(y()-h/2.0+0.5)-1, w+3, h+3, a1, a2-a1); QPolygon r = path.toFillPolygon().toPolygon(); r.resize(r.size()+1); r.setPoint(r.size()-1,int(x()),int(y())); return QPolygon(r); } void KtlQCanvasEllipse::drawShape(QPainter & p) { p.setPen(Qt::NoPen); // since QRegion(QPolygon) excludes outline :-( )-: if ( !a1 && a2 == 360*16 ) { p.drawEllipse(int(x()-w/2.0+0.5), int(y()-h/2.0+0.5), w, h); } else { p.drawPie(int(x()-w/2.0+0.5), int(y()-h/2.0+0.5), w, h, a1, a2); } } void KtlQCanvasPolygonalItem::scanPolygon(const QPolygon& pa, int winding, KtlQPolygonalProcessor& process) const { KtlQCanvasPolygonScanner scanner(process); scanner.scan(pa,winding); } diff --git a/src/canvasitems.h b/src/canvasitems.h index e769a87f..95c0b8a8 100644 --- a/src/canvasitems.h +++ b/src/canvasitems.h @@ -1,260 +1,260 @@ /*************************************************************************** * Copyright (C) 1999-2005 Trolltech AS * * Copyright (C) 2006 David Saxton * * * * This file may be distributed and/or modified under the terms of the * * GNU General Public License version 2 as published by the Free * * Software Foundation * ***************************************************************************/ #ifndef KTL_CANVASITEMS_H_ #define KTL_CANVASITEMS_H_ #include "canvasitemlist.h" -#include -#include -#include -#include -#include +#include +#include +#include +#include +#include class QPainter; class KtlQCanvasPolygonalItem; class KtlQCanvasRectangle; class KtlQCanvasPolygon; class KtlQCanvasEllipse; class KtlQCanvasLine; class KtlQCanvasChunk; class KtlQCanvas; class KtlQCanvasItem; class KtlQCanvasView; class KtlQCanvasItemExtra; class KtlQCanvasItem : public QObject { Q_OBJECT public: KtlQCanvasItem(KtlQCanvas* canvas); ~KtlQCanvasItem() override; double x() const { return myx; } double y() const { return myy; } double z() const { return myz; } // (depth) virtual void moveBy(double const dx, double const dy); void move(double const x, double const y); void setX(double a) { move(a,y()); } void setY(double a) { move(x(),a); } void setZ(double a); virtual bool collidesWith( const KtlQCanvasItem* ) const=0; KtlQCanvasItemList collisions(const bool exact /* NO DEFAULT */ ) const; virtual void setCanvas(KtlQCanvas*); virtual void draw(QPainter&)=0; void show(); void hide(); virtual void setVisible(bool yes); bool isVisible() const { return vis; } virtual void setSelected(const bool yes); bool isSelected() const { return sel; } virtual QRect boundingRect() const=0; KtlQCanvas* canvas() const { return cnv; } virtual bool collidesWith( const KtlQCanvasPolygonalItem*, const KtlQCanvasRectangle*, const KtlQCanvasEllipse* ) const = 0; bool needRedraw() const { return m_bNeedRedraw; } void setNeedRedraw( const bool needRedraw ) { m_bNeedRedraw = needRedraw; } protected: void update() { changeChunks(); } virtual QPolygon chunks() const; virtual void addToChunks(); virtual void removeFromChunks(); virtual void changeChunks(); bool val; double myx,myy,myz; private: KtlQCanvas* cnv; static KtlQCanvas* current_canvas; KtlQCanvasItemExtra *ext; KtlQCanvasItemExtra& extra(); bool m_bNeedRedraw; bool vis; bool sel; }; class KtlQPolygonalProcessor; class KtlQCanvasPolygonalItem : public KtlQCanvasItem { public: KtlQCanvasPolygonalItem(KtlQCanvas* canvas); ~KtlQCanvasPolygonalItem() override; bool collidesWith( const KtlQCanvasItem* ) const override; virtual void setPen( const QPen & p ); virtual void setBrush( const QBrush & b ); QPen pen() const { return pn; } QBrush brush() const { return br; } virtual QPolygon areaPoints() const=0; QRect boundingRect() const override; protected: void draw(QPainter &) override; virtual void drawShape(QPainter &) = 0; bool winding() const; void setWinding(bool); void invalidate(); bool isValid() const { return val; } private: void scanPolygon( const QPolygon& pa, int winding, KtlQPolygonalProcessor& process ) const; QPolygon chunks() const override; bool collidesWith( const KtlQCanvasPolygonalItem*, const KtlQCanvasRectangle*, const KtlQCanvasEllipse* ) const override; QBrush br; QPen pn; bool wind; }; class KtlQCanvasRectangle : public KtlQCanvasPolygonalItem { public: KtlQCanvasRectangle(KtlQCanvas* canvas); KtlQCanvasRectangle(const QRect&, KtlQCanvas* canvas); KtlQCanvasRectangle(int x, int y, int width, int height, KtlQCanvas* canvas); ~KtlQCanvasRectangle() override; int width() const; int height() const; void setSize(const int w, const int h); QSize size() const { return QSize(w,h); } QPolygon areaPoints() const override; QRect rect() const { return QRect(int(x()),int(y()),w,h); } bool collidesWith( const KtlQCanvasItem* ) const override; protected: void drawShape(QPainter &) override; QPolygon chunks() const override; private: bool collidesWith( const KtlQCanvasPolygonalItem*, const KtlQCanvasRectangle*, const KtlQCanvasEllipse* ) const override; int w, h; }; class KtlQCanvasPolygon : public KtlQCanvasPolygonalItem { public: KtlQCanvasPolygon(KtlQCanvas* canvas); ~KtlQCanvasPolygon() override; void setPoints(QPolygon); QPolygon points() const; void moveBy(double dx, double dy) override; QPolygon areaPoints() const override; protected: void drawShape(QPainter &) override; // TODO FIXME guarts are added for debugging memory corruption (poly takes non-pointer values) int guardBef[10]; QPolygon *poly; int guardAft[10]; }; class KtlQCanvasLine : public KtlQCanvasPolygonalItem { public: KtlQCanvasLine(KtlQCanvas* canvas); ~KtlQCanvasLine() override; void setPoints(int x1, int y1, int x2, int y2); QPoint startPoint() const { return QPoint(x1,y1); } QPoint endPoint() const { return QPoint(x2,y2); } void setPen( const QPen & p ) override; void moveBy(double dx, double dy) override; protected: void drawShape(QPainter &) override; QPolygon areaPoints() const override; private: int x1,y1,x2,y2; }; class KtlQCanvasEllipse : public KtlQCanvasPolygonalItem { public: KtlQCanvasEllipse( KtlQCanvas* canvas ); KtlQCanvasEllipse( int width, int height, KtlQCanvas* canvas ); KtlQCanvasEllipse( int width, int height, int startangle, int angle, KtlQCanvas* canvas ); ~KtlQCanvasEllipse() override; int width() const; int height() const; void setSize(int w, int h); void setAngles(int start, int length); int angleStart() const { return a1; } int angleLength() const { return a2; } QPolygon areaPoints() const override; bool collidesWith( const KtlQCanvasItem* ) const override; protected: void drawShape(QPainter &) override; private: bool collidesWith( const KtlQCanvasPolygonalItem*, const KtlQCanvasRectangle*, const KtlQCanvasEllipse* ) const override; int w, h; int a1, a2; }; #endif // KTL_CANVASITEMS_H_ diff --git a/src/canvasmanipulator.cpp b/src/canvasmanipulator.cpp index e5005bd8..d2b5c024 100644 --- a/src/canvasmanipulator.cpp +++ b/src/canvasmanipulator.cpp @@ -1,1928 +1,1928 @@ /*************************************************************************** * Copyright (C) 2004-2006 by David Saxton * * david@bluehaze.org * * * * 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 "cnitemgroup.h" #include "canvasitemparts.h" #include "dptext.h" #include "canvasmanipulator.h" #include "connector.h" #include "flowcontainer.h" #include "icndocument.h" #include "itemview.h" #include "mechanicsdocument.h" #include "mechanicsgroup.h" #include "mechanicsitem.h" #include "node.h" #include "nodegroup.h" #include "picitem.h" #include "resizeoverlay.h" #include "utils.h" #include #include -#include #include #include -#include -#include -#include +#include +#include +#include +#include // FIXME: This source file is HUUUGE!!!, contains numerous clases, should be broken down. //BEGIN class CMManager CMManager::CMManager( ItemDocument *itemDocument ) : QObject() { b_allowItemScroll = true; p_lastMouseOverResizeHandle = nullptr; m_canvasManipulator = nullptr; p_itemDocument = itemDocument; m_cmState = 0; p_lastMouseOverItem = nullptr; p_lastItemClicked = nullptr; m_drawAction = -1; m_allowItemScrollTmr = new QTimer(this); connect( m_allowItemScrollTmr, SIGNAL(timeout()), this, SLOT(slotAllowItemScroll()) ); KConfigGroup grGen = KSharedConfig::openConfig()->group("General"); slotSetManualRoute( grGen.readEntry( "ManualRouting", false ) ); } CMManager::~CMManager() { delete m_allowItemScrollTmr; delete m_canvasManipulator; const ManipulatorInfoList::iterator end = m_manipulatorInfoList.end(); for ( ManipulatorInfoList::iterator it = m_manipulatorInfoList.begin(); it != end; ++it ) { delete *it; } m_manipulatorInfoList.clear(); } void CMManager::addManipulatorInfo( ManipulatorInfo *eventInfo ) { if ( eventInfo && !m_manipulatorInfoList.contains(eventInfo) ) { m_manipulatorInfoList.prepend(eventInfo); } } void CMManager::cancelCurrentManipulation() { delete m_canvasManipulator; m_canvasManipulator = nullptr; setRepeatedAddId(); } void CMManager::mousePressEvent( EventInfo eventInfo ) { if (m_canvasManipulator) { if (m_canvasManipulator->mousePressedRepeat(eventInfo)) { delete m_canvasManipulator; m_canvasManipulator = nullptr; } return; } uint eventState=0; if (eventInfo.isRightClick) eventState |= CMManager::es_right_click; if (eventInfo.ctrlPressed) eventState |= CMManager::es_ctrl_pressed; uint itemType=0; uint cnItemType=0; KtlQCanvasItem * qcanvasItem = eventInfo.qcanvasItemClickedOn; if ( ! qcanvasItem ) itemType = it_none; else if ( dynamic_cast(qcanvasItem) ) itemType = it_node; else if ( dynamic_cast(qcanvasItem) || dynamic_cast(qcanvasItem) ) itemType = it_connector; else if ( dynamic_cast(qcanvasItem) ) itemType = it_pin; else if ( dynamic_cast(qcanvasItem) ) itemType = it_resize_handle; else if ( DrawPart *drawPartClickedOn = dynamic_cast(qcanvasItem) ) { itemType = it_drawpart; if ( drawPartClickedOn->mousePressEvent(eventInfo) ) { p_lastItemClicked = drawPartClickedOn; return; } if ( drawPartClickedOn->isMovable() ) cnItemType |= CMManager::isi_isMovable; } else if ( MechanicsItem *p_mechanicsItemClickedOn = dynamic_cast(qcanvasItem) ) { itemType = it_mechanics_item; if ( p_mechanicsItemClickedOn->mousePressEvent(eventInfo) ) { p_lastItemClicked = p_mechanicsItemClickedOn; return; } } else { if ( Widget *widget = dynamic_cast(qcanvasItem) ) qcanvasItem = widget->parent(); if ( CNItem *cnItemClickedOn = dynamic_cast(qcanvasItem) ) { itemType = it_canvas_item; if ( cnItemClickedOn->mousePressEvent(eventInfo) ) { p_lastItemClicked = cnItemClickedOn; return; } if ( cnItemClickedOn->isMovable() ) cnItemType |= CMManager::isi_isMovable; } } // uint highestScore=0; // ManipulatorInfo *best = nullptr; const ManipulatorInfoList::iterator end = m_manipulatorInfoList.end(); for ( ManipulatorInfoList::iterator it = m_manipulatorInfoList.begin(); it != end && !m_canvasManipulator; ++it ) { if ( (*it)->m_acceptManipulationPtr( eventState, m_cmState, itemType, cnItemType ) ) { m_canvasManipulator = (*it)->m_createManipulatorPtr( p_itemDocument, this ); } } if (m_canvasManipulator) { if (m_canvasManipulator->mousePressedInitial(eventInfo)) { delete m_canvasManipulator; m_canvasManipulator = nullptr; } } } void CMManager::mouseDoubleClickEvent( const EventInfo &eventInfo ) { if (m_canvasManipulator) { // Translate this into a repeat-click event if (m_canvasManipulator->mousePressedRepeat(eventInfo)) { delete m_canvasManipulator; m_canvasManipulator = nullptr; } return; } Item *item = dynamic_cast(eventInfo.qcanvasItemClickedOn); if (item) { item->mouseDoubleClickEvent(eventInfo); return; } Widget *widget = dynamic_cast(eventInfo.qcanvasItemClickedOn); if (widget) { widget->parent()->mouseDoubleClickEvent(eventInfo); return; } } void CMManager::mouseMoveEvent( const EventInfo &eventInfo ) { if (m_canvasManipulator) { if (m_canvasManipulator->mouseMoved(eventInfo)) { delete m_canvasManipulator; m_canvasManipulator = nullptr; } ItemView *itemView = dynamic_cast(p_itemDocument->activeView()); if (itemView) itemView->scrollToMouse(eventInfo.pos); return; } //BEGIN KtlQCanvasItem *qcnItem = p_itemDocument->itemAtTop(eventInfo.pos); Item *item; Widget *widget = dynamic_cast(qcnItem); if (widget) item = widget->parent(); else item = dynamic_cast(qcnItem); if ( p_lastMouseOverItem != (QPointer)item ) { QEvent event(QEvent::Leave); if (p_lastMouseOverItem) p_lastMouseOverItem->leaveEvent(nullptr); if (item) item->enterEvent(nullptr); p_lastMouseOverItem = item; } // If we clicked on an item, then continue to pass mouse events to that item until we release the mouse... if (p_lastItemClicked) { p_lastItemClicked->mouseMoveEvent(eventInfo); } else if (item) { item->mouseMoveEvent(eventInfo); } //END updateCurrentResizeHandle( dynamic_cast(qcnItem) ); } void CMManager::updateCurrentResizeHandle( ResizeHandle * resizeHandle ) { if ( p_lastMouseOverResizeHandle != (QPointer)resizeHandle ) { if (p_lastMouseOverResizeHandle) p_lastMouseOverResizeHandle->setHover(false); p_lastMouseOverResizeHandle = resizeHandle; if (resizeHandle) resizeHandle->setHover(true); } } void CMManager::mouseReleaseEvent( const EventInfo &eventInfo ) { // If it returns true, then it has finished its editing operation if ( m_canvasManipulator && m_canvasManipulator->mouseReleased(eventInfo) ) { delete m_canvasManipulator; m_canvasManipulator = nullptr; } if (p_lastItemClicked) { p_lastItemClicked->mouseReleaseEvent(eventInfo); p_lastItemClicked = nullptr; } updateCurrentResizeHandle( dynamic_cast( p_itemDocument->itemAtTop(eventInfo.pos) ) ); } void CMManager::wheelEvent( const EventInfo &eventInfo ) { bool accepted = false; if (b_allowItemScroll) { KtlQCanvasItem *qcnItem = p_itemDocument->itemAtTop(eventInfo.pos); Item *item; Widget *widget = dynamic_cast(qcnItem); if (widget) item = widget->parent(); else item = dynamic_cast(qcnItem); if (item) accepted = item->wheelEvent(eventInfo); } if (!accepted) { // Only allow scrolling of items if we have not just been scrolling the canvas b_allowItemScroll = false; m_allowItemScrollTmr->stop(); m_allowItemScrollTmr->setSingleShot(true); m_allowItemScrollTmr->start(500 /*,true */ ); ItemView *itemView = dynamic_cast(p_itemDocument->activeView()); if (itemView) { itemView->cvbEditor()->setPassEventsToView(false); itemView->cvbEditor()->contentsWheelEvent( eventInfo.wheelEvent( 0, 0 ) ); itemView->cvbEditor()->setPassEventsToView(true); } } } void CMManager::setDrawAction( int drawAction ) { if ( m_drawAction == drawAction ) return; m_drawAction = drawAction; setCMState( cms_draw, (m_drawAction != -1) ); } void CMManager::slotSetManualRoute( bool manualRoute ) { KConfigGroup grGen = KSharedConfig::openConfig()->group("General"); grGen.writeEntry( "ManualRouting", manualRoute ); setCMState( cms_manual_route, manualRoute ); } void CMManager::setCMState( CMState type, bool state ) { // Set or clear the correct bit state ? (m_cmState|=type) : (m_cmState&=(~type)); if ( type == CMManager::cms_manual_route ) emit manualRoutingChanged(state); } void CMManager::setRepeatedAddId( const QString & repeatedId ) { m_repeatedItemId = repeatedId; } //END class CMManager //BEGIN class CanvasManipulator CanvasManipulator::CanvasManipulator( ItemDocument *itemDocument, CMManager *cmManager ) { p_itemDocument = itemDocument; p_icnDocument = dynamic_cast(itemDocument); p_mechanicsDocument = dynamic_cast(itemDocument); p_canvas = p_itemDocument->canvas(); // b_connectorsAllowedRouting = true; p_selectList = p_itemDocument->selectList(); p_cnItemSelectList = dynamic_cast(p_selectList); p_mechItemSelectList = dynamic_cast(p_selectList); p_cnItemClickedOn = nullptr; p_cmManager = cmManager; connect( itemDocument->canvas(), SIGNAL(resized( const QRect&, const QRect& )), this, SLOT(canvasResized( const QRect&, const QRect& )) ); } CanvasManipulator::~CanvasManipulator() { } QPoint CanvasManipulator::snapPoint( QPoint point ) { point /= 8; point *= 8; point += QPoint( 4, 4 ); return point; } //END class CanvasManipulator CMRepeatedItemAdd::CMRepeatedItemAdd( ItemDocument *itemDocument, CMManager *cmManager ) : CanvasManipulator( itemDocument, cmManager ) { } CMRepeatedItemAdd::~CMRepeatedItemAdd() { } CanvasManipulator* CMRepeatedItemAdd::construct( ItemDocument *itemDocument, CMManager *cmManager ) { return new CMRepeatedItemAdd(itemDocument,cmManager); } ManipulatorInfo *CMRepeatedItemAdd::manipulatorInfo() { ManipulatorInfo *eventInfo = new ManipulatorInfo(); eventInfo->m_acceptManipulationPtr = CMRepeatedItemAdd::acceptManipulation; eventInfo->m_createManipulatorPtr = CMRepeatedItemAdd::construct; return eventInfo; } bool CMRepeatedItemAdd::acceptManipulation( uint /*eventState*/, uint cmState, uint /*itemType*/, uint /*cnItemType*/ ) { return (cmState & CMManager::cms_repeated_add); } bool CMRepeatedItemAdd::mousePressedRepeat( const EventInfo &eventInfo ) { return mousePressedInitial(eventInfo); } bool CMRepeatedItemAdd::mousePressedInitial( const EventInfo &eventInfo ) { m_eventInfo = eventInfo; if (eventInfo.isRightClick) { p_cmManager->setCMState( CMManager::cms_repeated_add, false ); return true; } p_icnDocument->addItem( p_cmManager->repeatedItemId(), eventInfo.pos, true ); p_itemDocument->requestStateSave(); return false; } bool CMRepeatedItemAdd::mouseMoved( const EventInfo &/*eventInfo*/ ) { return false; } bool CMRepeatedItemAdd::mouseReleased( const EventInfo &/*eventInfo*/ ) { return false; } CMRightClick::CMRightClick( ItemDocument *itemDocument, CMManager *cmManager ) : CanvasManipulator( itemDocument, cmManager ) { } CMRightClick::~CMRightClick() { } CanvasManipulator* CMRightClick::construct( ItemDocument *itemDocument, CMManager *cmManager ) { return new CMRightClick(itemDocument,cmManager); } ManipulatorInfo *CMRightClick::manipulatorInfo() { ManipulatorInfo *eventInfo = new ManipulatorInfo(); // eventInfo->m_eventState.m_activate = CMManager::es_right_click; eventInfo->m_acceptManipulationPtr = CMRightClick::acceptManipulation; eventInfo->m_createManipulatorPtr = CMRightClick::construct; return eventInfo; } bool CMRightClick::acceptManipulation( uint eventState, uint /*cmState*/, uint /*itemType*/, uint /*cnItemType*/ ) { return eventState & CMManager::es_right_click; } bool CMRightClick::mousePressedInitial( const EventInfo &eventInfo ) { m_eventInfo = eventInfo; p_itemDocument->canvasRightClick( eventInfo.globalPos, eventInfo.qcanvasItemClickedOn ); return true; } bool CMRightClick::mouseMoved( const EventInfo &/*eventInfo*/ ) { return true; } bool CMRightClick::mouseReleased( const EventInfo &/*eventInfo*/ ) { return true; } //BEGIN class ConnectorDraw ConnectorDraw::ConnectorDraw( ItemDocument *itemDocument, CMManager *cmManager ) : CanvasManipulator( itemDocument, cmManager ) { p_startNode = nullptr; p_startConnector = nullptr; p_endNode = nullptr; p_endConnector = nullptr; } ConnectorDraw::~ConnectorDraw() { } QColor ConnectorDraw::validConnectionColor() { return QColor( 255, 166, 0 ); } QPoint ConnectorDraw::toValidPos( const QPoint & clickPos, Connector * clickedConnector ) const { if ( !clickedConnector ) return clickPos; const QPointList pointList = clickedConnector->connectorPoints(); QPointList::const_iterator end = pointList.end(); double dl[] = { 0.5, 8.5, 11.5, 18.0, 23.0 }; // various distances rounded up of (0,0) cells, (0,1), etc for ( unsigned i = 0; i < 5; ++i ) { for ( QPointList::const_iterator it = pointList.begin(); it != end; ++it ) { if ( qpoint_distance( *it, clickPos ) <= dl[i] ) return *it; } } return clickPos; } Connector * ConnectorDraw::toConnector( Node * node ) { if ( !node || node->numCon( true, false ) < 3 ) return nullptr; return node->getAConnector(); } void ConnectorDraw::grabEndStuff( KtlQCanvasItem * endItem, const QPoint & pos, bool posIsExact ) { if (!endItem) return; CNItem * cnItem = dynamic_cast(endItem); if ( cnItem && !posIsExact ) p_endNode = cnItem->getClosestNode(pos); else p_endNode = dynamic_cast(endItem); if ( p_endNode && p_endNode->numCon( true, false ) > 2 ) { p_endConnector = toConnector(p_endNode); p_endNode = nullptr; } // If the endItem is a node, we have to finish exactly on the end when posIsExact is true if ( posIsExact && p_endNode && (p_endNode->x() != pos.x() || p_endNode->y() != pos.y()) ) p_endNode = nullptr; if (!p_endConnector) p_endConnector = dynamic_cast(endItem); } //END class ConnectorDraw //BEGIN class CMAutoConnector CMAutoConnector::CMAutoConnector( ItemDocument *itemDocument, CMManager *cmManager ) : ConnectorDraw( itemDocument, cmManager ) { m_connectorLine = nullptr; p_startNode = nullptr; p_startConnector = nullptr; } CMAutoConnector::~CMAutoConnector() { delete m_connectorLine; } CanvasManipulator* CMAutoConnector::construct( ItemDocument *itemDocument, CMManager *cmManager ) { return new CMAutoConnector(itemDocument,cmManager); } ManipulatorInfo *CMAutoConnector::manipulatorInfo() { ManipulatorInfo *eventInfo = new ManipulatorInfo(); eventInfo->m_acceptManipulationPtr = CMAutoConnector::acceptManipulation; eventInfo->m_createManipulatorPtr = CMAutoConnector::construct; return eventInfo; } bool CMAutoConnector::acceptManipulation( uint /*eventState*/, uint cmState, uint itemType, uint /*cnItemType*/ ) { return (itemType & (CMManager::it_node | CMManager::it_connector)) && !(cmState & CMManager::cms_manual_route); } bool CMAutoConnector::mousePressedInitial( const EventInfo &eventInfo ) { m_eventInfo = eventInfo; p_startNode = dynamic_cast(eventInfo.qcanvasItemClickedOn); if (p_startNode) { m_eventInfo.pos = m_prevPos = p_icnDocument->gridSnap( QPoint( (int)p_startNode->x(), (int)p_startNode->y() ) ); if (p_startNode->numCon( true, false ) > 2) { p_startConnector = toConnector(p_startNode); p_startNode = nullptr; } } else if ((p_startConnector = dynamic_cast(eventInfo.qcanvasItemClickedOn) )) { // startConnectorPoint = m_eventInfo.pos = m_prevPos = p_icnDocument->gridSnap(m_eventInfo.pos); startConnectorPoint = m_eventInfo.pos = m_prevPos = toValidPos( m_eventInfo.pos, p_startConnector ); } else return true; p_icnDocument->unselectAll(); delete m_connectorLine; m_connectorLine = new KtlQCanvasLine(p_canvas); m_connectorLine->setPen( QColor(0,0,0) ); m_connectorLine->setZ( ItemDocument::Z::ConnectorCreateLine ); m_connectorLine->show(); return false; } bool CMAutoConnector::mouseMoved( const EventInfo &eventInfo ) { const QPoint pos = eventInfo.pos; int newX = p_icnDocument->gridSnap( pos.x() ); int newY = p_icnDocument->gridSnap( pos.y() ); bool movedFlag = false; if ( newX != m_prevPos.x() ) { m_prevPos.setX(newX); movedFlag = true; } if ( newY != m_prevPos.y() ) { m_prevPos.setY(newY); movedFlag = true; } m_connectorLine->setPoints( m_eventInfo.pos.x(), m_eventInfo.pos.y(), newX, newY ); if (movedFlag) { KtlQCanvasItem *startItem = nullptr; if (p_startNode) startItem = p_startNode; else if (p_startConnector) startItem = p_startConnector; KtlQCanvasItem *endItem = p_icnDocument->itemAtTop( QPoint( newX, newY ) ); if ( CNItem * cni = dynamic_cast(endItem) ) endItem = cni->getClosestNode( QPoint( newX, newY ) ); bool validLine = p_icnDocument->canConnect( startItem, endItem ); m_connectorLine->setPen( validLine ? validConnectionColor() : Qt::black ); } return false; } bool CMAutoConnector::mouseReleased( const EventInfo &eventInfo ) { const QPoint pos = eventInfo.pos; QPoint end = m_connectorLine->endPoint(); delete m_connectorLine; m_connectorLine = nullptr; KtlQCanvasItem *qcanvasItem = p_icnDocument->itemAtTop(end); if ( !qcanvasItem ) return true; grabEndStuff( qcanvasItem, pos, false ); if (p_startConnector) { if (p_endConnector) { if ( !p_icnDocument->createConnector( p_endConnector, p_startConnector, p_icnDocument->gridSnap(pos), startConnectorPoint ) ) return true; } else if (p_endNode) { if ( !p_icnDocument->createConnector( p_endNode, p_startConnector, startConnectorPoint ) ) return true; } else return true; } else if (p_startNode) { if (p_endConnector) { if ( !p_icnDocument->createConnector( p_startNode, p_endConnector, p_icnDocument->gridSnap(pos) ) ) return true; } else if (p_endNode) { if ( !p_icnDocument->createConnector( p_startNode, p_endNode ) ) return true; } else return true; } else return true; p_itemDocument->requestStateSave(); return true; } //END class CMAutoConnector //BEGIN class CMManualConnector CMManualConnector::CMManualConnector( ItemDocument *itemDocument, CMManager *cmManager ) : ConnectorDraw( itemDocument, cmManager ) { m_manualConnectorDraw = nullptr; } CMManualConnector::~CMManualConnector() { delete m_manualConnectorDraw; } CanvasManipulator* CMManualConnector::construct( ItemDocument *itemDocument, CMManager *cmManager ) { return new CMManualConnector(itemDocument,cmManager); } ManipulatorInfo *CMManualConnector::manipulatorInfo() { ManipulatorInfo *eventInfo = new ManipulatorInfo(); eventInfo->m_acceptManipulationPtr = CMManualConnector::acceptManipulation; eventInfo->m_createManipulatorPtr = CMManualConnector::construct; return eventInfo; } bool CMManualConnector::acceptManipulation( uint /*eventState*/, uint cmState, uint itemType, uint /*cnItemType*/ ) { return (itemType & (CMManager::it_node | CMManager::it_connector)) && (cmState & CMManager::cms_manual_route); } bool CMManualConnector::mousePressedInitial( const EventInfo &eventInfo ) { if ( eventInfo.isRightClick ) return true; m_eventInfo = eventInfo; p_icnDocument->unselectAll(); QPoint sp; if ( (p_startNode = dynamic_cast(eventInfo.qcanvasItemClickedOn)) ) { sp.setX( (int)p_startNode->x() ); sp.setY( (int)p_startNode->y() ); if ( p_startNode->numCon( true, false ) > 2 ) { p_startConnector = toConnector(p_startNode); p_startNode = nullptr; } } else { p_startConnector = dynamic_cast(eventInfo.qcanvasItemClickedOn); sp = toValidPos( eventInfo.pos, p_startConnector ); } startConnectorPoint = sp; if (m_manualConnectorDraw) delete m_manualConnectorDraw; m_manualConnectorDraw = new ManualConnectorDraw( p_icnDocument, sp ); return false; } bool CMManualConnector::mousePressedRepeat( const EventInfo &eventInfo ) { m_eventInfo = eventInfo; if ( eventInfo.isRightClick ) { return true; } m_manualConnectorDraw->mouseClicked( p_icnDocument->gridSnap(m_eventInfo.pos) ); return false; } bool CMManualConnector::mouseMoved( const EventInfo &eventInfo ) { if ( !m_manualConnectorDraw ) return true; const QPoint pos = eventInfo.pos; int newX = p_icnDocument->gridSnap( pos.x() ); int newY = p_icnDocument->gridSnap( pos.y() ); bool movedFlag = false; if ( newX != m_prevPos.x() ) { m_prevPos.setX(newX); movedFlag = true; } if ( newY != m_prevPos.y() ) { m_prevPos.setY(newY); movedFlag = true; } if ( movedFlag ) { KtlQCanvasItem *startItem = nullptr; if (p_startNode) startItem = p_startNode; else if (p_startConnector) startItem = p_startConnector; KtlQCanvasItem * endItem = p_icnDocument->itemAtTop( QPoint( newX, newY ) ); // If the endItem is a node, we have to finish exactly on the end. if ( Node * node = dynamic_cast(endItem) ) { if ( node->x() != newX || node->y() != newY ) endItem = nullptr; } bool validLine = p_icnDocument->canConnect( startItem, endItem ); m_manualConnectorDraw->setColor( validLine ? validConnectionColor() : Qt::black ); m_manualConnectorDraw->mouseMoved( QPoint( newX, newY ) ); } return false; } bool CMManualConnector::mouseReleased( const EventInfo &eventInfo ) { if (!m_manualConnectorDraw) return true; QPoint pos = p_icnDocument->gridSnap(eventInfo.pos); grabEndStuff( m_manualConnectorDraw->mouseClicked(pos), pos, true ); if ( !p_endNode && !p_endConnector ) return false; // Create the points that define the manual route QPointList list = m_manualConnectorDraw->pointList(); delete m_manualConnectorDraw; m_manualConnectorDraw = nullptr; if (p_startConnector) { if (p_endConnector) { if ( !p_icnDocument->createConnector( p_endConnector, p_startConnector, p_icnDocument->gridSnap(pos), startConnectorPoint, &list ) ) return true; } else // if (p_endNode) { if ( !p_icnDocument->createConnector( p_endNode, p_startConnector, startConnectorPoint, &list ) ) return true; } } else if (p_startNode) { if (p_endConnector) { if ( !p_icnDocument->createConnector( p_startNode, p_endConnector, p_icnDocument->gridSnap(pos), &list ) ) return true; } else // if (p_endNode) { if ( !p_icnDocument->createConnector( p_startNode, p_endNode, &list ) ) return true; } } else return true; p_itemDocument->requestStateSave(); return true; } //END class CMManualConnector //BEGIN class CMItemMove CMItemMove::CMItemMove( ItemDocument *itemDocument, CMManager *cmManager ) : CanvasManipulator( itemDocument, cmManager ) { p_flowContainerCandidate = nullptr; m_bItemsSnapToGrid = false; } CMItemMove::~CMItemMove() { } CanvasManipulator* CMItemMove::construct( ItemDocument *itemDocument, CMManager *cmManager ) { return new CMItemMove(itemDocument,cmManager); } ManipulatorInfo *CMItemMove::manipulatorInfo() { ManipulatorInfo *eventInfo = new ManipulatorInfo(); eventInfo->m_acceptManipulationPtr = CMItemMove::acceptManipulation; eventInfo->m_createManipulatorPtr = CMItemMove::construct; return eventInfo; } bool CMItemMove::acceptManipulation( uint eventState, uint /*cmState*/, uint itemType, uint cnItemType ) { return ((itemType & CMManager::it_canvas_item) || (itemType & CMManager::it_drawpart)) && (cnItemType & CMManager::isi_isMovable) && !(eventState & CMManager::es_right_click); } bool CMItemMove::mousePressedInitial( const EventInfo &eventInfo ) { m_eventInfo = eventInfo; m_prevPos = eventInfo.pos; Item *item = dynamic_cast(eventInfo.qcanvasItemClickedOn); if (!item) return true; if ( !p_selectList->contains(item) ) { if (!eventInfo.ctrlPressed) p_itemDocument->unselectAll(); p_itemDocument->select(item); } else if (m_eventInfo.ctrlPressed) p_itemDocument->unselect(item); if ( p_selectList->isEmpty() ) return true; // We want to allow dragging into FlowContainers if this is a FlowView p_flowContainerCandidate = nullptr; { const ItemList &itemList = p_icnDocument->itemList(); const ItemList::const_iterator ciEnd = itemList.end(); for ( ItemList::const_iterator it = itemList.begin(); it != ciEnd; ++it ) { if ( FlowContainer *flowContainer = dynamic_cast((Item*)*it) ) flowContainer->setFullBounds(true); } } ItemList itemList = p_cnItemSelectList->items(false); itemList.removeAll((Item*)nullptr); m_bItemsSnapToGrid = false; const ItemList::iterator itemListEnd = itemList.end(); for ( ItemList::iterator it = itemList.begin(); it != itemListEnd; ++it ) { CNItem *cnItem = dynamic_cast((Item*)*it); if ( !cnItem || !cnItem->canvas() ) continue; m_bItemsSnapToGrid = true; } if ( m_bItemsSnapToGrid ) m_prevSnapPoint = this->snapPoint( m_prevPos ); else m_prevSnapPoint = m_prevPos; ConnectorList fixedConnectors; p_icnDocument->getTranslatable( itemList, &fixedConnectors, &m_translatableConnectors, &m_translatableNodeGroups ); const ConnectorList::iterator fixedConnectorsEnd = fixedConnectors.end(); for ( ConnectorList::iterator it = fixedConnectors.begin(); it != fixedConnectorsEnd; ++it ) (*it)->setSemiHidden(true); p_flowContainerCandidate = p_icnDocument->flowContainer(eventInfo.pos); return false; } void CMItemMove::canvasResized( const QRect & /*oldSize*/, const QRect & /*newSize*/ ) { //QPoint delta = oldSize.topLeft() - newSize.topLeft(); // 2017.10.01 - comment out unused variable // scrollCanvasToSelection(); // QCursor::setPos( QCursor::pos() + delta ); // m_prevPos += delta; // m_prevSnapPoint += delta; } void CMItemMove::scrollCanvasToSelection() { QRect bound; ItemList itemList = p_cnItemSelectList->items(false); itemList.removeAll((Item*)nullptr); const ItemList::iterator itemListEnd = itemList.end(); for ( ItemList::iterator it = itemList.begin(); it != itemListEnd; ++it ) bound |= (*it)->boundingRect(); QPoint scrollToPos = m_prevPos; if ( m_dx < 0 ) { // Scrolling left scrollToPos -= QPoint( bound.left(), 0 ); } else { // Scrolling right scrollToPos += QPoint( bound.right(), 0 ); } if ( m_dy < 0 ) { // Scrolling up scrollToPos -= QPoint( 0, bound.top() ); } else { // Scrolling right scrollToPos += QPoint( 0, bound.bottom() ); } ItemView *itemView = dynamic_cast(p_itemDocument->activeView()); if (itemView) itemView->scrollToMouse( scrollToPos ); } bool CMItemMove::mouseMoved( const EventInfo &eventInfo ) { QPoint pos = eventInfo.pos; QPoint snapPoint = pos; if ( m_bItemsSnapToGrid ) snapPoint = this->snapPoint( snapPoint ); int dx = snapPoint.x() - m_prevSnapPoint.x(); int dy = snapPoint.y() - m_prevSnapPoint.y(); m_dx = dx; m_dy = dy; const ItemList itemList = p_cnItemSelectList->items(); const ItemList::const_iterator end = itemList.end(); for ( ItemList::const_iterator it = itemList.begin(); it != end; ++it ) { if ( !*it || !(*it)->isMovable() ) continue; //QRect oldRect = (*it)->boundingRect(); // 2017.10.01 - comment out unused variable (*it)->moveBy( dx, dy ); //QRect newRect = (*it)->boundingRect(); //QRect merged = oldRect | newRect; // 2017.10.01 - comment out unused variable } if ( (dx != 0) || (dy != 0) ) { const ConnectorList::iterator frEnd = m_translatableConnectors.end(); for ( ConnectorList::iterator it = m_translatableConnectors.begin(); it != frEnd; ++it ) (*it)->translateRoute( dx, dy ); const NodeGroupList::iterator end = m_translatableNodeGroups.end(); for ( NodeGroupList::iterator it = m_translatableNodeGroups.begin(); it != end; ++it ) (*it)->translate( dx, dy ); } FlowContainer *fc = p_icnDocument->flowContainer(pos); if ( fc != p_flowContainerCandidate ) { if ( p_flowContainerCandidate ) { p_flowContainerCandidate->setSelected(false); p_flowContainerCandidate = nullptr; } } if (fc) { p_flowContainerCandidate = fc; p_flowContainerCandidate->setSelected(true); } p_itemDocument->requestEvent( ItemDocument::ItemDocumentEvent::ResizeCanvasToItems ); p_canvas->update(); m_prevPos = pos; m_prevSnapPoint = snapPoint; // scrollCanvasToSelection(); return false; } bool CMItemMove::mouseReleased( const EventInfo &eventInfo ) { // Is the release event from a right click (which rotates items)? if ( eventInfo.isRightClick || eventInfo.isMiddleClick ) return false; QStringList itemIDs; const ItemList itemList = p_cnItemSelectList->items(); const ItemList::const_iterator ilEnd = itemList.end(); for ( ItemList::const_iterator it = itemList.begin(); it != ilEnd; ++it ) { if (*it) itemIDs.append( (*it)->id() ); } //const QPoint pos = eventInfo.pos; // 2017.10.10 - comment out unused variable // And make sure all connectors are properly shown const ConnectorList &connectorList = p_icnDocument->connectorList(); const ConnectorList::const_iterator conEnd = connectorList.end(); for ( ConnectorList::const_iterator it = connectorList.begin(); it != conEnd; ++it ) { (*it)->setSemiHidden(false); } if (p_flowContainerCandidate) { for ( ItemList::const_iterator it = itemList.begin(); it != ilEnd; ++it ) p_flowContainerCandidate->addChild(*it); p_flowContainerCandidate->setSelected(false); p_flowContainerCandidate = nullptr; } else { for ( ItemList::const_iterator it = itemList.begin(); it != ilEnd; ++it ) (*it)->setParentItem(nullptr); } // And disable the FlowContainers again... const ItemList &cnItemList = p_icnDocument->itemList(); const ItemList::const_iterator end = cnItemList.end(); for ( ItemList::const_iterator it = cnItemList.begin(); it != end; ++it ) { if ( FlowContainer *flowContainer = dynamic_cast((Item*)*it) ) flowContainer->setFullBounds(false); } if (p_icnDocument) p_icnDocument->requestRerouteInvalidatedConnectors(); if ( m_eventInfo.pos != eventInfo.pos ) p_itemDocument->requestStateSave(); p_itemDocument->requestEvent( ItemDocument::ItemDocumentEvent::ResizeCanvasToItems ); return true; } bool CMItemMove::mousePressedRepeat( const EventInfo & info ) { if ( info.isRightClick ) p_cnItemSelectList->slotRotateCW(); else if ( info.isMiddleClick ) p_cnItemSelectList->flipHorizontally(); return false; } //END class CMItemMove CMItemResize::CMItemResize( ItemDocument *itemDocument, CMManager *cmManager ) : CanvasManipulator( itemDocument, cmManager ) { } CMItemResize::~CMItemResize() { } CanvasManipulator* CMItemResize::construct( ItemDocument *itemDocument, CMManager *cmManager ) { return new CMItemResize(itemDocument,cmManager); } ManipulatorInfo *CMItemResize::manipulatorInfo() { ManipulatorInfo *eventInfo = new ManipulatorInfo(); // eventInfo->m_itemType.m_activate = CMManager::it_canvas_item; eventInfo->m_acceptManipulationPtr = CMItemResize::acceptManipulation; eventInfo->m_createManipulatorPtr = CMItemResize::construct; return eventInfo; } bool CMItemResize::acceptManipulation( uint eventState, uint /*cmState*/, uint itemType, uint /*cnItemType*/ ) { return (itemType & CMManager::it_resize_handle) && !(eventState & CMManager::es_right_click); } bool CMItemResize::mousePressedInitial( const EventInfo &eventInfo ) { m_eventInfo = eventInfo; p_resizeHandle = dynamic_cast(eventInfo.qcanvasItemClickedOn); m_rh_dx = p_resizeHandle->x()-eventInfo.pos.x(); m_rh_dy = p_resizeHandle->y()-eventInfo.pos.y(); return false; } bool CMItemResize::mouseMoved( const EventInfo &eventInfo ) { int _x = int(m_rh_dx + eventInfo.pos.x()); int _y = int(m_rh_dy + eventInfo.pos.y()); // Shift pressed == snap to grid if ( eventInfo.shiftPressed ) { _x = snapToCanvas(_x); _y = snapToCanvas(_y); } p_resizeHandle->moveRH( _x, _y ); return false; } bool CMItemResize::mouseReleased( const EventInfo &/*eventInfo*/ ) { if (p_icnDocument) p_icnDocument->requestRerouteInvalidatedConnectors(); p_itemDocument->requestStateSave(); return true; } CMMechItemMove::CMMechItemMove( ItemDocument *itemDocument, CMManager *cmManager ) : CanvasManipulator( itemDocument, cmManager ) { } CMMechItemMove::~CMMechItemMove() { } CanvasManipulator* CMMechItemMove::construct( ItemDocument *itemDocument, CMManager *cmManager ) { return new CMMechItemMove(itemDocument,cmManager); } ManipulatorInfo *CMMechItemMove::manipulatorInfo() { ManipulatorInfo *eventInfo = new ManipulatorInfo(); // eventInfo->m_itemType.m_activate = CMManager::it_canvas_item; eventInfo->m_acceptManipulationPtr = CMMechItemMove::acceptManipulation; eventInfo->m_createManipulatorPtr = CMMechItemMove::construct; return eventInfo; } bool CMMechItemMove::acceptManipulation( uint eventState, uint /*cmState*/, uint itemType, uint /*cnItemType*/ ) { return ((itemType & CMManager::it_mechanics_item) || (itemType & CMManager::it_drawpart)) && !(eventState & CMManager::es_right_click); } bool CMMechItemMove::mousePressedInitial( const EventInfo &eventInfo ) { m_eventInfo = eventInfo; m_prevPos = eventInfo.pos; Item *item = dynamic_cast(eventInfo.qcanvasItemClickedOn); if (!item) return true; MechanicsItem *mechItem = dynamic_cast(eventInfo.qcanvasItemClickedOn); if (mechItem) m_prevClickedOnSM = mechItem->selectionMode(); if (eventInfo.shiftPressed) { p_mechanicsDocument->unselectAll(); p_mechanicsDocument->select(item); if (mechItem) { mechItem->setSelectionMode(MechanicsItem::sm_move); mechItem->setParentItem(nullptr); } } else if ( !p_selectList->contains(mechItem) ) { if (!eventInfo.ctrlPressed) p_mechanicsDocument->unselectAll(); p_mechanicsDocument->select(item); if (mechItem) mechItem->setSelectionMode(MechanicsItem::sm_move); } else { if (mechItem) mechItem->setSelectionMode(MechanicsItem::sm_move); if (m_eventInfo.ctrlPressed) p_mechanicsDocument->unselect(item); } if ( p_selectList->isEmpty() ) return true; p_mechItemSelectList->setSelectionMode( MechanicsItem::sm_move ); p_mechItemSelectList->setRaised(true); return false; } bool CMMechItemMove::mouseMoved( const EventInfo &eventInfo ) { const QPoint pos = eventInfo.pos; int x = pos.x(); int y = pos.y(); const MechItemList itemList = p_mechItemSelectList->toplevelMechItemList(); const MechItemList::const_iterator ilEnd = itemList.end(); for ( MechItemList::const_iterator it = itemList.begin(); it != ilEnd; ++it ) { if (*it) (*it)->moveBy( x - m_prevPos.x(), y - m_prevPos.y() ); } m_prevPos = QPoint( x, y ); p_canvas->update(); p_itemDocument->requestEvent( ItemDocument::ItemDocumentEvent::ResizeCanvasToItems ); return false; } bool CMMechItemMove::mouseReleased( const EventInfo &eventInfo ) { const QPoint pos = eventInfo.pos; int dx = pos.x() - m_eventInfo.pos.x(); int dy = pos.y() - m_eventInfo.pos.y(); p_mechItemSelectList->setRaised(false); MechanicsItem *mechItem = dynamic_cast(m_eventInfo.qcanvasItemClickedOn); if ( dx == 0 && dy == 0 ) { if ( mechItem && mechItem->isSelected() ) { if ( m_prevClickedOnSM == MechanicsItem::sm_resize ) mechItem->setSelectionMode( MechanicsItem::sm_rotate ); else mechItem->setSelectionMode( MechanicsItem::sm_resize ); } p_itemDocument->requestStateSave(); return true; } if ( mechItem && mechItem->isSelected() ) { if ( m_prevClickedOnSM == MechanicsItem::sm_rotate ) mechItem->setSelectionMode(MechanicsItem::sm_rotate); else mechItem->setSelectionMode(MechanicsItem::sm_resize); } QStringList itemIDs; ItemList itemList = p_mechItemSelectList->items(); const ItemList::iterator ilEnd = itemList.end(); for ( ItemList::iterator it = itemList.begin(); it != ilEnd; ++it ) { if (*it) { itemIDs.append( (*it)->id() ); } } p_mechItemSelectList->setSelectionMode( MechanicsItem::sm_resize ); p_itemDocument->requestStateSave(); p_itemDocument->requestEvent( ItemDocument::ItemDocumentEvent::ResizeCanvasToItems ); return true; } //BEGIN class SelectRectangle SelectRectangle::SelectRectangle( int x, int y, int w, int h, KtlQCanvas *qcanvas ) : m_x(x), m_y(y) { m_topLine = new KtlQCanvasLine(qcanvas); m_rightLine = new KtlQCanvasLine(qcanvas); m_bottomLine = new KtlQCanvasLine(qcanvas); m_leftLine = new KtlQCanvasLine(qcanvas); setSize( w, h ); KtlQCanvasLine* lines[] = { m_topLine, m_rightLine, m_bottomLine, m_leftLine }; for ( int i=0; i<4; ++ i) { lines[i]->setPen( QPen( QColor(190,190,190), 1, Qt::DotLine ) ); lines[i]->setZ( ICNDocument::Z::Select ); lines[i]->show(); } } SelectRectangle::~SelectRectangle() { delete m_topLine; delete m_rightLine; delete m_bottomLine; delete m_leftLine; } void SelectRectangle::setSize( int w, int h ) { m_topLine->setPoints( m_x, m_y, m_x+w, m_y ); m_rightLine->setPoints( m_x+w, m_y, m_x+w, m_y+h ); m_bottomLine->setPoints( m_x+w, m_y+h, m_x, m_y+h ); m_leftLine->setPoints( m_x, m_y+h, m_x, m_y ); m_w = w; m_h = h; } KtlQCanvasItemList SelectRectangle::collisions() { KtlQCanvas *canvas = m_topLine->canvas(); return canvas->collisions( QRect( m_x, m_y, m_w, m_h ) ); } //END class SelectRectangle //BEGIN class CMSelect CMSelect::CMSelect( ItemDocument *itemDocument, CMManager *cmManager ) : CanvasManipulator( itemDocument, cmManager ) { m_selectRectangle = nullptr; } CMSelect::~CMSelect() { delete m_selectRectangle; } CanvasManipulator* CMSelect::construct( ItemDocument *itemDocument, CMManager *cmManager ) { return new CMSelect(itemDocument,cmManager); } ManipulatorInfo *CMSelect::manipulatorInfo() { ManipulatorInfo *eventInfo = new ManipulatorInfo(); // eventInfo->m_itemType.m_activate = CMManager::it_none; eventInfo->m_acceptManipulationPtr = CMSelect::acceptManipulation; eventInfo->m_createManipulatorPtr = CMSelect::construct; return eventInfo; } bool CMSelect::acceptManipulation( uint /*eventState*/, uint /*cmState*/, uint itemType, uint /*cnItemType*/ ) { return (itemType & CMManager::it_none); } bool CMSelect::mousePressedInitial( const EventInfo &eventInfo ) { m_eventInfo = eventInfo; if (!eventInfo.ctrlPressed) { p_itemDocument->unselectAll(); } m_selectRectangle = new SelectRectangle( eventInfo.pos.x(), eventInfo.pos.y(), 0, 0, p_canvas ); return false; } bool CMSelect::mouseMoved( const EventInfo &eventInfo ) { QPoint pos = eventInfo.pos; m_selectRectangle->setSize( pos.x()-m_eventInfo.pos.x(), pos.y()-m_eventInfo.pos.y() ); if (m_eventInfo.ctrlPressed) { p_itemDocument->select( m_selectRectangle->collisions() ); } else if (p_selectList) { p_selectList->setItems( m_selectRectangle->collisions() ); } if (p_selectList && !p_mechanicsDocument) { p_selectList->setSelected(true); } return false; } bool CMSelect::mouseReleased( const EventInfo &/*eventInfo*/ ) { delete m_selectRectangle; m_selectRectangle = nullptr; return true; } //END class CMSelect CMItemDrag::CMItemDrag( ItemDocument *itemDocument, CMManager *cmManager ) : CanvasManipulator( itemDocument, cmManager ) { b_dragged = false; } CMItemDrag::~CMItemDrag() { } CanvasManipulator* CMItemDrag::construct( ItemDocument *itemDocument, CMManager *cmManager ) { return new CMItemDrag(itemDocument,cmManager); } ManipulatorInfo *CMItemDrag::manipulatorInfo() { ManipulatorInfo *eventInfo = new ManipulatorInfo(); // eventInfo->m_itemType.m_activate = CMManager::it_canvas_item; eventInfo->m_acceptManipulationPtr = CMItemDrag::acceptManipulation; eventInfo->m_createManipulatorPtr = CMItemDrag::construct; return eventInfo; } bool CMItemDrag::acceptManipulation( uint /*eventState*/, uint /*cmState*/, uint itemType, uint cnItemType ) { return (itemType & (CMManager::it_canvas_item|CMManager::it_pin)) && !(cnItemType & CMManager::isi_isMovable); } bool CMItemDrag::mousePressedInitial( const EventInfo &eventInfo ) { m_eventInfo = eventInfo; b_dragged = false; return false; } bool CMItemDrag::mouseMoved( const EventInfo &eventInfo ) { const QPoint pos = eventInfo.pos; if ( b_dragged || pos.x() > (m_eventInfo.pos.x()+4 ) || pos.x() < (m_eventInfo.pos.x()-4) || pos.y() > (m_eventInfo.pos.y()+4) || pos.y() < (m_eventInfo.pos.y()-4) ) { b_dragged = true; if ( PinItem * pi = dynamic_cast(m_eventInfo.qcanvasItemClickedOn) ) pi->dragged( pos.x() - m_eventInfo.pos.x() ); } return false; } bool CMItemDrag::mouseReleased( const EventInfo &/*eventInfo*/ ) { if ( !b_dragged ) { if ( PinItem * pi = dynamic_cast(m_eventInfo.qcanvasItemClickedOn) ) pi->switchState(); } p_itemDocument->requestStateSave(); return true; } //BEGIN class CanvasEllipseDraw CanvasEllipseDraw::CanvasEllipseDraw( int x, int y, KtlQCanvas * canvas ) : KtlQCanvasEllipse( 0, 0, canvas ) { move( x, y ); } void CanvasEllipseDraw::drawShape( QPainter & p ) { p.drawEllipse( int(x()-width()/2), int(y()-height()/2), width(), height() ); } //END class CanvasEllipseDraw //BEGIN class CMDraw CMDraw::CMDraw( ItemDocument *itemDocument, CMManager *cmManager ) : CanvasManipulator( itemDocument, cmManager ) { m_pDrawLine = nullptr; m_pDrawRectangle = nullptr; m_pDrawEllipse = nullptr; } CMDraw::~CMDraw() { p_cmManager->setDrawAction(-1); } CanvasManipulator* CMDraw::construct( ItemDocument *itemDocument, CMManager *cmManager ) { return new CMDraw(itemDocument,cmManager); } ManipulatorInfo *CMDraw::manipulatorInfo() { ManipulatorInfo *eventInfo = new ManipulatorInfo(); eventInfo->m_acceptManipulationPtr = CMDraw::acceptManipulation; eventInfo->m_createManipulatorPtr = CMDraw::construct; return eventInfo; } bool CMDraw::acceptManipulation( uint /*eventState*/, uint cmState, uint /*itemType*/, uint /*cnItemType*/ ) { return (cmState & CMManager::cms_draw); } bool CMDraw::mousePressedInitial( const EventInfo &eventInfo ) { m_eventInfo = eventInfo; switch ( (DrawPart::DrawAction) p_cmManager->drawAction() ) { case DrawPart::da_text: case DrawPart::da_rectangle: case DrawPart::da_image: { m_pDrawRectangle = new KtlQCanvasRectangle( eventInfo.pos.x(), eventInfo.pos.y(), 0, 0, p_canvas ); m_pDrawRectangle->setPen( QPen( QColor(0,0,0), 1 ) ); m_pDrawRectangle->setZ( ICNDocument::Z::ConnectorCreateLine ); m_pDrawRectangle->show(); break; } case DrawPart::da_ellipse: { m_pDrawEllipse = new CanvasEllipseDraw( eventInfo.pos.x(), eventInfo.pos.y(), p_canvas ); m_pDrawEllipse->setPen( QPen( QColor(0,0,0), 1 ) ); m_pDrawEllipse->setZ( ICNDocument::Z::ConnectorCreateLine ); m_pDrawEllipse->show(); break; } case DrawPart::da_line: case DrawPart::da_arrow: { m_pDrawLine = new KtlQCanvasLine(p_canvas); m_pDrawLine->setPoints( eventInfo.pos.x(), eventInfo.pos.y(), eventInfo.pos.x(), eventInfo.pos.y() ); m_pDrawLine->setPen( QPen( QColor(0,0,0), 1 ) ); m_pDrawLine->setZ( ICNDocument::Z::ConnectorCreateLine ); m_pDrawLine->show(); break; } default: return true; } return false; } bool CMDraw::mouseMoved( const EventInfo &eventInfo ) { const QPoint pos = eventInfo.pos; if (m_pDrawRectangle) m_pDrawRectangle->setSize( pos.x()-m_eventInfo.pos.x(), pos.y()-m_eventInfo.pos.y() ); else if (m_pDrawEllipse) { // QRect r( m_eventInfo.pos.x(), m_eventInfo.pos.y(), pos.x()-m_eventInfo.pos.x(), pos.y()-m_eventInfo.pos.y() ); // r = r.normalized(); // // m_pDrawEllipse->setSize( r.width(), r.height() ); // m_pDrawEllipse->move( r.left()+(r.width()/2), r.top()+(r.height()/2) ); m_pDrawEllipse->setSize( 2 * abs(pos.x() - m_eventInfo.pos.x()), 2 * abs(pos.y() - m_eventInfo.pos.y()) ); } else if (m_pDrawLine) m_pDrawLine->setPoints( eventInfo.pos.x(), eventInfo.pos.y(), m_pDrawLine->endPoint().x(), m_pDrawLine->endPoint().y() ); else return true; return false; } bool CMDraw::mouseReleased( const EventInfo & /*eventInfo*/ ) { //const QPoint pos = eventInfo.pos; // 2017.10.01 - comment out unused variable QRect sizeRect; if ( m_pDrawRectangle || m_pDrawEllipse ) { if (m_pDrawRectangle) { sizeRect = m_pDrawRectangle->rect(); // We have to manually adjust the size rect so that it matches up with what the user has drawn sizeRect.setWidth( sizeRect.width()+1 ); sizeRect.setHeight( sizeRect.height()+1 ); sizeRect = sizeRect.normalized(); if ( m_pDrawRectangle->rect().width() < 0 ) sizeRect.moveLeft( sizeRect.left() + 1); if ( m_pDrawRectangle->rect().height() < 0 ) sizeRect.moveTop( sizeRect.top() + 1); } else { int w = m_pDrawEllipse->width()+1; int h = m_pDrawEllipse->height()+1; int x = int(m_pDrawEllipse->x()-w/2); int y = int(m_pDrawEllipse->y()-h/2); sizeRect = QRect( x, y, w, h ).normalized(); } delete m_pDrawRectangle; delete m_pDrawEllipse; m_pDrawRectangle = nullptr; m_pDrawEllipse = nullptr; } else if (m_pDrawLine) { int sx = m_pDrawLine->startPoint().x(); int sy = m_pDrawLine->startPoint().y(); int ex = m_pDrawLine->endPoint().x(); int ey = m_pDrawLine->endPoint().y(); sizeRect = QRect( ex, ey, sx-ex, sy-ey ); delete m_pDrawLine; m_pDrawLine = nullptr; } else return true; QString id; switch ( (DrawPart::DrawAction) p_cmManager->drawAction() ) { case DrawPart::da_rectangle: id = "dp/rectangle"; break; case DrawPart::da_image: id = "dp/image"; break; case DrawPart::da_ellipse: id = "dp/ellipse"; break; case DrawPart::da_text: id = "dp/canvas_text"; if ( sizeRect.width() < 56 ) sizeRect.setWidth( 56 ); if ( sizeRect.height() < 24 ) sizeRect.setHeight( 24 ); break; case DrawPart::da_line: id = "dp/line"; break; case DrawPart::da_arrow: id = "dp/arrow"; break; } if ( id.isEmpty() ) return true; Item *item = p_itemDocument->addItem( id, sizeRect.topLeft(), true ); if (!item) return true; item->move( sizeRect.x(), sizeRect.y() ); // We call this again as p_itemDocument->addItem will move the item if it is slightly off the canvas. item->setSize( 0, 0, sizeRect.width(), sizeRect.height() ); p_itemDocument->requestStateSave(); return true; } //END class CMDraw //BEGIN class ManualConnectorDraw ManualConnectorDraw::ManualConnectorDraw( ICNDocument *_icnDocument, const QPoint &initialPos ) { m_color = Qt::black; icnDocument = _icnDocument; m_currentPos = m_previousPos = m_initialPos = initialPos; p_initialItem = icnDocument->itemAtTop(initialPos); b_currentVertical = false; b_orientationDefined = false; m_connectorLines.append( m_previousCon = new KtlQCanvasLine( icnDocument->canvas() ) ); m_connectorLines.append( m_currentCon = new KtlQCanvasLine( icnDocument->canvas() ) ); m_currentCon->setPoints( initialPos.x(), initialPos.y(), initialPos.x(), initialPos.y() ); m_previousCon->setPoints( initialPos.x(), initialPos.y(), initialPos.x(), initialPos.y() ); m_currentCon->setPen( m_color ); m_previousCon->setPen( m_color ); updateConnectorEnds(); m_currentCon->show(); m_previousCon->show(); } ManualConnectorDraw::~ManualConnectorDraw() { const QList::iterator end = m_connectorLines.end(); for ( QList::iterator it = m_connectorLines.begin(); it != end; ++it ) delete *it; m_connectorLines.clear(); } void ManualConnectorDraw::setColor( const QColor & color ) { m_color = color; const QList::iterator end = m_connectorLines.end(); for ( QList::iterator it = m_connectorLines.begin(); it != end; ++it ) (*it)->setPen( m_color ); } void ManualConnectorDraw::mouseMoved( const QPoint &pos ) { if ( m_currentPos == pos ) return; if (!b_orientationDefined) { QPoint previousStart = m_previousCon->startPoint(); double distance = std::sqrt( std::pow( (double)(m_currentPos.x()-previousStart.x()), 2. ) + std::pow( (double)(m_currentPos.y()-previousStart.y()), 2. ) ); if ( distance < 24 ) { b_currentVertical = ( std::abs( double(m_currentPos.x()-previousStart.x()) ) >= std::abs( double(m_currentPos.y()-previousStart.y()) ) ); } } m_previousPos = m_currentPos; m_currentPos = pos; updateConnectorEnds(); } KtlQCanvasItem* ManualConnectorDraw::mouseClicked( const QPoint &pos ) { if (b_orientationDefined) b_currentVertical = !b_currentVertical; else mouseMoved(pos); b_orientationDefined = true; m_currentPos = pos; KtlQCanvasItem * qcanvasItem = icnDocument->itemAtTop(pos); if ( qcanvasItem && pos != m_initialPos && qcanvasItem != p_initialItem ) return qcanvasItem; m_previousCon = m_currentCon; m_connectorLines.append( m_currentCon = new KtlQCanvasLine( icnDocument->canvas() ) ); m_currentCon->setPoints( pos.x(), pos.y(), pos.x(), pos.y() ); m_currentCon->setPen( m_color ); updateConnectorEnds(); m_currentCon->show(); return nullptr; } void ManualConnectorDraw::updateConnectorEnds() { QPoint pivot = m_currentPos; QPoint previousStart = m_previousCon->startPoint(); if (b_currentVertical) { pivot.setY( previousStart.y() ); m_currentCon->setPoints( pivot.x(), pivot.y(), pivot.x(), m_currentPos.y() ); } else { pivot.setX( previousStart.x() ); m_currentCon->setPoints( pivot.x(), pivot.y(), m_currentPos.x(), pivot.y() ); } m_previousCon->setPoints( previousStart.x(), previousStart.y(), pivot.x(), pivot.y() ); } QPointList ManualConnectorDraw::pointList() { QPointList list; list.append(m_initialPos); const QList::iterator end = m_connectorLines.end(); for ( QList::iterator it = m_connectorLines.begin(); it != end; ++it ) { list.append( (*it)->endPoint() ); } return list; } //END class ManualConnectorDraw //BEGIN class ManipulatorInfo ManipulatorInfo::ManipulatorInfo() { } //END class ManipulatorInfo diff --git a/src/canvasmanipulator.h b/src/canvasmanipulator.h index 0cf69b7e..ee913790 100644 --- a/src/canvasmanipulator.h +++ b/src/canvasmanipulator.h @@ -1,627 +1,627 @@ /*************************************************************************** * Copyright (C) 2004-2005 by David Saxton * * david@bluehaze.org * * * * 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. * ***************************************************************************/ #ifndef CANVAsmANIPULATOR_H #define CANVAsmANIPULATOR_H #include "eventinfo.h" //#include // 2018.10.16 - not needed #include "canvasitems.h" -#include +#include class CanvasManipulator; class Connector; class CMManager; class CNItem; class CNItemGroup; class EventInfo; class FlowContainer; class ICNDocument; class Item; class ItemDocument; class ItemGroup; class ItemView; class ManipulatorInfo; class ManualConnectorDraw; class MechanicsItem; class MechanicsGroup; class MechanicsDocument; class Node; class NodeGroup; class ResizeHandle; class KtlQCanvas; class KtlQCanvasItem; class KtlQCanvasLine; class KtlQCanvasRectangle; class QMouseEvent; class QTimer; class QWheelEvent; typedef CanvasManipulator*(*CreateManipulatorPtr)( ItemDocument *, CMManager * ); typedef bool(*AcceptManipulationPtr)( uint eventState, uint cmState, uint itemType, uint cnItemType ); typedef QList NodeGroupList; typedef QList > ConnectorList; typedef QList QPointList; class ManipulatorInfo { public: ManipulatorInfo(); AcceptManipulationPtr m_acceptManipulationPtr; CreateManipulatorPtr m_createManipulatorPtr; }; typedef QList ManipulatorInfoList; /** Handles canvas manipulation, such as moving an item or resizing the canvas @author David Saxton */ class CMManager : public QObject { Q_OBJECT public: enum EventState { es_right_click = 1 << 0, es_ctrl_pressed = 1 << 1 }; enum CMState { cms_repeated_add = 1 << 0, cms_manual_route = 1 << 1, cms_draw = 1 << 2 }; enum ItemType { it_none = 1 << 0, it_node = 1 << 1, it_connector = 1 << 2, it_pin = 1 << 3, it_canvas_item = 1 << 4, it_mechanics_item = 1 << 5, it_resize_handle = 1 << 6, it_drawpart = 1 << 7 }; enum ItemStateInfo { isi_isMovable = 0x2 }; CMManager( ItemDocument *itemDocument ); ~CMManager() override; /** * Called when the user single-clicks the mouse */ void mousePressEvent( EventInfo eventInfo ); /** * Called when the user releases the mouse */ void mouseReleaseEvent( const EventInfo &eventInfo ); /** * Called when the user double clicks the mouse */ void mouseDoubleClickEvent( const EventInfo &eventInfo ); /** * Called when the user moves the mouse */ void mouseMoveEvent( const EventInfo &eventInfo ); /** * Called when the user scrolls the mouse */ void wheelEvent( const EventInfo &eventInfo ); /** * Set a current CMState to true or false */ void setCMState( CMState type, bool state ); /** * Cancels the current manipulation (if there is one) */ void cancelCurrentManipulation(); CanvasManipulator * currentManipulator() const { return m_canvasManipulator; } void setRepeatedAddId( const QString & repeatedId = QString::null ); uint cmState() const { return m_cmState; } void addManipulatorInfo( ManipulatorInfo *info ); QString repeatedItemId() const { return m_repeatedItemId; } void setDrawAction( int drawAction ); int drawAction() const { return m_drawAction; } public slots: void slotSetManualRoute( bool manualRoute ); signals: void manualRoutingChanged( bool manualRouting ); protected: /** * Called when the mouse is moved or released, with the ResizeHandle that * the mouse is currently over (which can be null). Updates which handle is * selected, etc. */ void updateCurrentResizeHandle( ResizeHandle * mouseOver ); CanvasManipulator *m_canvasManipulator; uint m_cmState; QString m_repeatedItemId; ItemDocument *p_itemDocument; ManipulatorInfoList m_manipulatorInfoList; QPointer p_lastMouseOverItem; // Pointer to the item where the mouse was last over - this is used to determine when mouse QPointer p_lastMouseOverResizeHandle; QPointer p_lastItemClicked; QTimer *m_allowItemScrollTmr; // When a user scrolls on the canvas, we don't want to stop scrolling when the user gets to (e.g.) a scrollable widget. So this timer prevents scrolling a widget for a few hundred milliseconds after a scroll event if it was initiated over the canvas bool b_allowItemScroll; // See above. int m_drawAction; private slots: void slotAllowItemScroll() { b_allowItemScroll = true; } }; /** Abstract class for a "editing operation" on the ICNDocument, such as moving an item or resizing the canvas @author David Saxton */ class CanvasManipulator : public QObject { Q_OBJECT public: CanvasManipulator( ItemDocument *itemDocument, CMManager *cmManager ); ~CanvasManipulator() override; enum Type { RepeatedItemAdd, RightClick, AutoConnector, ManualConnector, ItemMove, ItemResize, MechItemMove, Select, CanvasResize, ItemDrag, Draw }; virtual Type type() const = 0; /** * Called when the user single-clicks the mouse * @returns true if the manipulation operation has finished */ virtual bool mousePressedInitial( const EventInfo &/*info*/ ) { return false; } /** * Called when the user single-clicks the mouse after the first time (only * applicable for those operations who are not oneClick * @returns true if the manipulation operation has finished */ virtual bool mousePressedRepeat( const EventInfo &/*info*/ ) { return false; }; /** * Called when the user moves the mouse * @returns true if the manipulation operation has finished */ virtual bool mouseMoved( const EventInfo &/*info*/ ) { return false; }; /** * Called when the user releases the mouse * @returns true if the manipulation operation has finished */ virtual bool mouseReleased( const EventInfo &/*info*/ ) { return true; } /** * Snaps the point to the 8-sized canvas grid. */ static QPoint snapPoint( QPoint point ); protected slots: /** * Called when the working canvas emits a resized signal. */ virtual void canvasResized( const QRect & oldSize, const QRect & newSize ) { (void)oldSize; (void)newSize; } protected: Type m_type; EventInfo m_eventInfo; QPoint m_prevPos; ItemDocument *p_itemDocument; ICNDocument *p_icnDocument; MechanicsDocument *p_mechanicsDocument; KtlQCanvas *p_canvas; ItemGroup *p_selectList; CNItemGroup *p_cnItemSelectList; MechanicsGroup *p_mechItemSelectList; CNItem *p_cnItemClickedOn; MechanicsItem *p_mechanicsItemClickedOn; CMManager *p_cmManager; }; /** @author David Saxton */ class CMRepeatedItemAdd : public CanvasManipulator { public: CMRepeatedItemAdd( ItemDocument *itemDocument, CMManager *cmManager ); ~CMRepeatedItemAdd() override; Type type() const override { return RepeatedItemAdd; } static CanvasManipulator* construct( ItemDocument *itemDocument, CMManager *cmManager ); static ManipulatorInfo *manipulatorInfo(); static bool acceptManipulation( uint eventState, uint cmState, uint itemType, uint cnItemType ); bool mousePressedInitial( const EventInfo &info ) override; bool mousePressedRepeat( const EventInfo &info ) override; bool mouseMoved( const EventInfo &info ) override; bool mouseReleased( const EventInfo &info ) override; protected: }; /** @author David Saxton */ class CMRightClick : public CanvasManipulator { public: CMRightClick( ItemDocument *itemDocument, CMManager *cmManager ); ~CMRightClick() override; Type type() const override { return RightClick; } static CanvasManipulator* construct( ItemDocument *itemDocument, CMManager *cmManager ); static ManipulatorInfo *manipulatorInfo(); static bool acceptManipulation( uint eventState, uint cmState, uint itemType, uint cnItemType ); bool mousePressedInitial( const EventInfo &info ) override; bool mouseMoved( const EventInfo &info ) override; bool mouseReleased( const EventInfo &info ) override; protected: }; /** @author David Saxton */ class ConnectorDraw : public CanvasManipulator { public: ConnectorDraw( ItemDocument *itemDocument, CMManager *cmManager ); ~ConnectorDraw() override; /** * Returns the colour used to indicate that the current connection * being drawn is valid. Invalid colour is black. */ static QColor validConnectionColor(); protected: /** * If the node has more than 2 connections, return one of the * connectors */ Connector * toConnector( Node * node ); /** * Converts the given qcanvasitem to an appropriate node or connector. * @param posIsExact if true, then only gets an appropriate node or * connector when the to-be end-point of the new connector will coincide * with pos (i.e. auto-connector will call this with posIsExact = false, * and manual-connector will call this with posIsExact = true). */ void grabEndStuff( KtlQCanvasItem * endItem, const QPoint & pos, bool posIsExact ); /** * Returns the closest point to the clickPos that is on the given * connector. */ QPoint toValidPos( const QPoint & clickPos, Connector * clickedConnector ) const; QPointer p_startNode; QPointer p_startConnector; Node * p_endNode; Connector * p_endConnector; QPoint startConnectorPoint; }; /** @author David Saxton */ class CMAutoConnector : public ConnectorDraw { public: CMAutoConnector( ItemDocument *itemDocument, CMManager *cmManager ); ~CMAutoConnector() override; Type type() const override { return AutoConnector; } static CanvasManipulator* construct( ItemDocument *itemDocument, CMManager *cmManager ); static ManipulatorInfo *manipulatorInfo(); static bool acceptManipulation( uint eventState, uint cmState, uint itemType, uint cnItemType ); bool mousePressedInitial( const EventInfo &info ) override; bool mouseMoved( const EventInfo &info ) override; bool mouseReleased( const EventInfo &info ) override; protected: KtlQCanvasLine *m_connectorLine; }; /** @author David Saxton */ class CMManualConnector : public ConnectorDraw { public: CMManualConnector( ItemDocument *itemDocument, CMManager *cmManager ); ~CMManualConnector() override; Type type() const override { return ManualConnector; } static CanvasManipulator* construct( ItemDocument *itemDocument, CMManager *cmManager ); static ManipulatorInfo *manipulatorInfo(); static bool acceptManipulation( uint eventState, uint cmState, uint itemType, uint cnItemType ); bool mousePressedInitial( const EventInfo &info ) override; bool mousePressedRepeat( const EventInfo &info ) override; bool mouseMoved( const EventInfo &info ) override; bool mouseReleased( const EventInfo &info ) override; protected: ConnectorList m_fixedRouteConnectors; ManualConnectorDraw *m_manualConnectorDraw; }; /** @author David Saxton */ class CMItemMove : public CanvasManipulator { public: CMItemMove( ItemDocument *itemDocument, CMManager *cmManager ); ~CMItemMove() override; Type type() const override { return ItemMove; } static CanvasManipulator* construct( ItemDocument *itemDocument, CMManager *cmManager ); static ManipulatorInfo *manipulatorInfo(); static bool acceptManipulation( uint eventState, uint cmState, uint itemType, uint cnItemType ); bool mousePressedInitial( const EventInfo &info ) override; bool mouseMoved( const EventInfo &info ) override; bool mouseReleased( const EventInfo &info ) override; bool mousePressedRepeat( const EventInfo & info ) override; protected: void canvasResized( const QRect & oldSize, const QRect & newSize ) override; void scrollCanvasToSelection(); QPoint m_prevSnapPoint; bool m_bItemsSnapToGrid; ///< true iff selection contains CNItems int m_dx; int m_dy; ConnectorList m_translatableConnectors; NodeGroupList m_translatableNodeGroups; FlowContainer *p_flowContainerCandidate; }; /** @author David Saxton */ class CMItemResize : public CanvasManipulator { public: CMItemResize( ItemDocument *itemDocument, CMManager *cmManager ); ~CMItemResize() override; Type type() const override { return ItemResize; } static CanvasManipulator* construct( ItemDocument *itemDocument, CMManager *cmManager ); static ManipulatorInfo *manipulatorInfo(); static bool acceptManipulation( uint eventState, uint cmState, uint itemType, uint cnItemType ); bool mousePressedInitial( const EventInfo &info ) override; bool mouseMoved( const EventInfo &info ) override; bool mouseReleased( const EventInfo &info ) override; protected: ResizeHandle *p_resizeHandle; double m_rh_dx; double m_rh_dy; }; /** @author David Saxton */ class CMMechItemMove : public CanvasManipulator { public: CMMechItemMove( ItemDocument *itemDocument, CMManager *cmManager ); ~CMMechItemMove() override; Type type() const override { return MechItemMove; } static CanvasManipulator* construct( ItemDocument *itemDocument, CMManager *cmManager ); static ManipulatorInfo *manipulatorInfo(); static bool acceptManipulation( uint eventState, uint cmState, uint itemType, uint cnItemType ); bool mousePressedInitial( const EventInfo &info ) override; bool mouseMoved( const EventInfo &info ) override; bool mouseReleased( const EventInfo &info ) override; protected: uint m_prevClickedOnSM; // Previous select mode of the item that was clicked on }; /** @author David Saxton */ class SelectRectangle { public: SelectRectangle( int x, int y, int w, int h, KtlQCanvas *qcanvas ); ~SelectRectangle(); void setSize( int w, int h ); KtlQCanvasItemList collisions(); protected: KtlQCanvasLine *m_topLine; KtlQCanvasLine *m_rightLine; KtlQCanvasLine *m_bottomLine; KtlQCanvasLine *m_leftLine; const int m_x; const int m_y; int m_w; int m_h; int m_prevCollisions_w; int m_prevCollisions_h; KtlQCanvasItemList m_prevCollisions; }; /** @author David Saxton */ class CMSelect : public CanvasManipulator { public: CMSelect( ItemDocument *itemDocument, CMManager *cmManager ); ~CMSelect() override; Type type() const override { return Select; } static CanvasManipulator* construct( ItemDocument *itemDocument, CMManager *cmManager ); static ManipulatorInfo *manipulatorInfo(); static bool acceptManipulation( uint eventState, uint cmState, uint itemType, uint cnItemType ); bool mousePressedInitial( const EventInfo &info ) override; bool mouseMoved( const EventInfo &info ) override; bool mouseReleased( const EventInfo &info ) override; protected: SelectRectangle *m_selectRectangle; }; /** @author David Saxton */ class CMItemDrag : public CanvasManipulator { public: CMItemDrag( ItemDocument *itemDocument, CMManager *cmManager ); ~CMItemDrag() override; Type type() const override { return ItemDrag; } static CanvasManipulator* construct( ItemDocument *itemDocument, CMManager *cmManager ); static ManipulatorInfo *manipulatorInfo(); static bool acceptManipulation( uint eventState, uint cmState, uint itemType, uint cnItemType ); bool mousePressedInitial( const EventInfo &info ) override; bool mouseMoved( const EventInfo &info ) override; bool mouseReleased( const EventInfo &info ) override; protected: bool b_dragged; }; /** @author David Saxton A KtlQCanvasEllipse that uses a pen (not a brush) to paint */ class CanvasEllipseDraw : public KtlQCanvasEllipse { public: CanvasEllipseDraw( int x, int y, KtlQCanvas * canvas ); protected: void drawShape( QPainter & p ) override; }; /** @author David Saxton */ class CMDraw : public CanvasManipulator { public: CMDraw( ItemDocument *itemDocument, CMManager *cmManager ); ~CMDraw() override; Type type() const override { return Draw; } static CanvasManipulator* construct( ItemDocument *itemDocument, CMManager *cmManager ); static ManipulatorInfo *manipulatorInfo(); static bool acceptManipulation( uint eventState, uint cmState, uint itemType, uint cnItemType ); bool mousePressedInitial( const EventInfo &info ) override; bool mouseMoved( const EventInfo &info ) override; bool mouseReleased( const EventInfo &info ) override; protected: KtlQCanvasRectangle * m_pDrawRectangle; CanvasEllipseDraw * m_pDrawEllipse; KtlQCanvasLine * m_pDrawLine; }; /** @author David Saxton */ class ManualConnectorDraw { public: ManualConnectorDraw( ICNDocument *_icnDocument, const QPoint &initialPos ); virtual ~ManualConnectorDraw(); /** * Called when the mouse is moved. * Normally will do something like updating the connector route */ void mouseMoved( const QPoint &pos ); /** * Called when the user clicks the mouse. If the connector finishes on a * valid KtlQCanvasItem (Node or Connetor), then this is returned. Otherwise, * null is returned. */ KtlQCanvasItem * mouseClicked( const QPoint &pos ); /** * Returns the list of points that define the manual connection route */ QPointList pointList(); /** * Sets the colour used to draw the connection lines. */ void setColor( const QColor & color ); protected: void updateConnectorEnds(); QList m_connectorLines; ICNDocument *icnDocument; bool b_currentVertical; bool b_orientationDefined; QPoint m_initialPos; QPoint m_previousPos; QPoint m_currentPos; KtlQCanvasLine *m_currentCon; KtlQCanvasLine *m_previousCon; // The first item that we clicked on KtlQCanvasItem *p_initialItem; QColor m_color; }; #endif diff --git a/src/cells.h b/src/cells.h index 95a5fc12..29e259e6 100644 --- a/src/cells.h +++ b/src/cells.h @@ -1,149 +1,149 @@ /*************************************************************************** * Copyright (C) 2003-2006 by David Saxton * * david@bluehaze.org * * * * 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. * ***************************************************************************/ #ifndef CELLS_H #define CELLS_H #include #include -#include +#include #include "utils.h" class Point { public: Point(); short x; short y; short prevX; short prevY; }; // Key = cell, data = previous cell, compare = score typedef std::multimap< unsigned short, Point > TempLabelMap; /** @short Used for mapping out connections */ const short startCellPos = -(1 << 14); class Cell { public: Cell(); /** * Resets bestScore, prevX, prevY, addedToLabels, it, permanent for * each cell. */ void reset(); /** * 'Penalty' of using the cell from CNItem. */ unsigned short CIpenalty; /** * 'Penalty' of using the cell from Connector. */ unsigned short Cpenalty; /** * Best (lowest) score so far, _the_ best if it is permanent. */ unsigned short bestScore; /** * Which cell this came from, (startCellPos,startCellPos) if originating * cell. */ short prevX, prevY; /** * Whether the score can be improved on. */ bool permanent; /** * Whether the cell has already been added to the list of cells to * check. */ bool addedToLabels; /** * Pointer to the point in the TempLabelMap. */ Point * point; /** * Number of connectors through that point. */ unsigned short numCon; }; /** @author David Saxton */ class Cells { public: Cells( const QRect & canvasRect ); ~Cells(); /** * Resets bestScore, prevX, prevY, addedToLabels, it, permanent for each cell */ void reset(); QRect cellsRect() const { return m_cellsRect; } /** * Returns the cell containing the given position on the canvas. */ Cell & cellContaining( int x, int y ) const { return cell( roundDown( x, 8 ), roundDown( y, 8 ) ); } /** * @return if the given cell exists. */ bool haveCell( int i, int j ) const { if ( (i < m_cellsRect.left()) || (i >= m_cellsRect.right()) ) return false; if ( (j < m_cellsRect.top()) || (j >= m_cellsRect.bottom()) ) return false; return true; } /** * @return if there is a cell containg the given canvas point. */ bool haveCellContaing( int x, int y ) const { return haveCell( roundDown( x, 8 ), roundDown( y, 8 ) ); } Cell & cell( int i, int j ) const { assert( i < m_cellsRect.right() ); assert( j < m_cellsRect.bottom() ); i -= m_cellsRect.left(); j -= m_cellsRect.top(); return m_cells[i][j]; } protected: void init( const QRect & canvasRect ); QRect m_cellsRect; Cell **m_cells; private: Cells( const Cells & ); Cells & operator= ( const Cells & ); }; #endif diff --git a/src/circuitview.cpp b/src/circuitview.cpp index 8aec7e83..a63416c9 100644 --- a/src/circuitview.cpp +++ b/src/circuitview.cpp @@ -1,169 +1,170 @@ /*************************************************************************** * Copyright (C) 2005 by David Saxton * * david@bluehaze.org * * * * 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 "circuitdocument.h" #include "circuitview.h" #include "config.h" #include "ktechlab.h" #include "simulator.h" #include "viewiface.h" #include #include #include -#include -#include -#include -#include +#include +#include +#include +#include +#include CircuitView::CircuitView( CircuitDocument * circuitDocument, ViewContainer *viewContainer, uint viewAreaId, const char *name ) : ICNView( circuitDocument, viewContainer, viewAreaId, name ), p_circuitDocument(circuitDocument) { KActionCollection * ac = actionCollection(); { //new QAction( "Dump linear equations", Qt::CTRL|Qt::Key_D, circuitDocument, SLOT(displayEquations()), ac, "dump_les" ); QAction * a = new QAction( i18n("Dump linear equations"), ac); a->setObjectName("dump_les"); ac->setDefaultShortcut(a, Qt::CTRL | Qt::Key_D); connect(a, SIGNAL(triggered(bool)), circuitDocument, SLOT(displayEquations())); ac->addAction(a->objectName(), a); } //BEGIN Item Control Actions //QAction * ra; // 2017.10.01 - commented unused variable { //ra = new QAction( i18n("0 Degrees"), "", 0, circuitDocument, SLOT(setOrientation0()), ac, "edit_orientation_0" ); // ra->setExclusiveGroup("orientation"); // TODO test QAction *ra = new QAction( i18n("0 Degrees"), ac); ra->setObjectName("edit_orientation_0"); connect(ra, SIGNAL(triggered(bool)), circuitDocument, SLOT(setOrientation0())); ac->addAction(ra->objectName(), ra); ra->setChecked(true); } { //ra = new QAction( i18n("90 Degrees"), "", 0, circuitDocument, SLOT(setOrientation90()), ac, "edit_orientation_90" ); // ra->setExclusiveGroup("orientation"); // TODO test QAction *ra = new QAction( i18n("90 Degrees"), ac); ra->setObjectName("edit_orientation_90"); connect(ra, SIGNAL(triggered(bool)), circuitDocument, SLOT(setOrientation90())); ac->addAction(ra->objectName(), ra); } { //ra = new QAction( i18n("180 Degrees"), "", 0, circuitDocument, SLOT(setOrientation180()), ac, "edit_orientation_180" ); //ra->setExclusiveGroup("orientation"); // TODO test QAction *ra = new QAction( i18n("180 Degrees"), ac); ra->setObjectName("edit_orientation_180"); connect(ra, SIGNAL(triggered(bool)), circuitDocument, SLOT(setOrientation180())); ac->addAction(ra->objectName(), ra); } { //ra =new QAction( i18n("270 Degrees"), "", 0, circuitDocument, SLOT(setOrientation270()), ac, "edit_orientation_270" ); //ra->setExclusiveGroup("orientation"); // TODO test QAction *ra = new QAction( i18n("270 Degrees"), ac); ra->setObjectName("edit_orientation_270"); connect(ra, SIGNAL(triggered(bool)), circuitDocument, SLOT(setOrientation270())); ac->addAction(ra->objectName(), ra); } { //new QAction( i18n("Create Subcircuit"), "", 0, circuitDocument, SLOT(createSubcircuit()), ac, "circuit_create_subcircuit" ); QAction *ra = new QAction( i18n("Create Subcircuit"), ac); ra->setObjectName("circuit_create_subcircuit"); connect(ra, SIGNAL(triggered(bool)), circuitDocument, SLOT(createSubcircuit())); ac->addAction(ra->objectName(), ra); } { //new QAction( i18n("Rotate Clockwise"), "object-rotate-right", "]", circuitDocument, SLOT(rotateClockwise()), ac, "edit_rotate_cw" ); QAction *ra = new QAction( QIcon::fromTheme("object-rotate-right"), i18n("Rotate Clockwise"), ac); ra->setObjectName("edit_rotate_cw"); connect(ra, SIGNAL(triggered(bool)), circuitDocument, SLOT(rotateClockwise())); ac->addAction(ra->objectName(), ra); } { //new QAction( i18n("Rotate Counter-Clockwise"), "object-rotate-left", "[", circuitDocument, SLOT(rotateCounterClockwise()), ac, "edit_rotate_ccw" ); QAction *ra = new QAction( QIcon::fromTheme("object-rotate-left"), i18n("Rotate Counter-Clockwise"), ac); ra->setObjectName("edit_rotate_ccw"); connect(ra, SIGNAL(triggered(bool)), circuitDocument, SLOT(rotateCounterClockwise())); ac->addAction(ra->objectName(), ra); } { //new QAction( i18n("Flip Horizontally"), "", 0, circuitDocument, SLOT(flipHorizontally()), ac, "edit_flip_horizontally" ); QAction *ra = new QAction( QIcon::fromTheme("object-flip-horizontal"), i18n("Flip Horizontally"), ac); ra->setObjectName("edit_flip_horizontally"); connect(ra, SIGNAL(triggered(bool)), circuitDocument, SLOT(flipHorizontally())); ac->addAction(ra->objectName(), ra); } { //new QAction( i18n("Flip Vertically"), "", 0, circuitDocument, SLOT(flipVertically()), ac, "edit_flip_vertically" ); QAction *ra = new QAction( QIcon::fromTheme("object-flip-vertical"), i18n("Flip Vertically"), ac); ra->setObjectName("edit_flip_vertically"); connect(ra, SIGNAL(triggered(bool)), circuitDocument, SLOT(flipVertically())); ac->addAction(ra->objectName(), ra); } //END Item Control Actions setXMLFile( "ktechlabcircuitui.rc", true ); setWhatsThis( i18n( "Construct a circuit by dragging components from the Component selector from the left. Create the connections by dragging a wire from the component connectors.

" "The simulation is running by default, but can be paused and resumed from the Tools menu.

" "To delete a wire, select it with a select box, and hit delete.

" "To edit the attributes of a component, select it (making sure that no components of another type are also selected), and edit in the toolbar. More advanced properties can be edited using the item editor on the right.

" "Subcircuits can be created by connecting the components with an External Connection, selecting the desired components and clicking on \"Create Subcircuit\" in the right-click menu.") ); m_pViewIface = new CircuitViewIface(this); // note: the text below normally should not be visible; it is needed to get the "real" status displayed; // see slotUpdateRunningStatus m_statusBar->setStatusText(i18n("Simulation Initializing")); connect( Simulator::self(), SIGNAL(simulatingStateChanged(bool )), this, SLOT(slotUpdateRunningStatus(bool )) ); slotUpdateRunningStatus( Simulator::self()->isSimulating() ); } CircuitView::~CircuitView() { delete m_pViewIface; } void CircuitView::slotUpdateRunningStatus( bool isRunning ) { m_statusBar->setStatusText(isRunning ? i18n("Simulation Running") : i18n("Simulation Paused")); } void CircuitView::dragEnterEvent( QDragEnterEvent * e ) { const QMimeData* mimeData = e->mimeData(); qDebug() << Q_FUNC_INFO << mimeData->formats(); ICNView::dragEnterEvent(e); if ( e->isAccepted() ) return; //bool acceptable = e->provides("ktechlab/component") || e->provides("ktechlab/subcircuit"); bool acceptable = e->mimeData()->hasFormat("ktechlab/component") || e->mimeData()->hasFormat("ktechlab/subcircuit"); if ( !acceptable ) return; e->setAccepted( true ); createDragItem( e ); } diff --git a/src/ciwidgetmgr.cpp b/src/ciwidgetmgr.cpp index 9947bcf0..c2aee9b2 100644 --- a/src/ciwidgetmgr.cpp +++ b/src/ciwidgetmgr.cpp @@ -1,314 +1,315 @@ /*************************************************************************** * Copyright (C) 2005 by David Saxton * * david@bluehaze.org * * * * 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 "cnitem.h" #include "canvasitemparts.h" #include "eventinfo.h" -#include +#include +#include // #include -#include +#include CIWidgetMgr::CIWidgetMgr( KtlQCanvas *canvas, CNItem *item ) { p_cnItem = item; p_canvas = canvas; } CIWidgetMgr::~CIWidgetMgr() { // KtlQCanvas deletes our items for us. Actually, it pretty much insists on deleting them, // despite me telling it not to, so if I delete them then it gets confused and crashes. // Naughty KtlQCanvas! const WidgetMap::iterator widgetMapEnd = m_widgetMap.end(); for ( WidgetMap::iterator it = m_widgetMap.begin(); it != widgetMapEnd; ++it ) { delete it.value(); } m_widgetMap.clear(); } void CIWidgetMgr::setWidgetsPos( const QPoint &pos ) { m_pos = pos; } void CIWidgetMgr::setDrawWidgets( bool draw ) { const WidgetMap::iterator widgetMapEnd = m_widgetMap.end(); for ( WidgetMap::iterator it = m_widgetMap.begin(); it != widgetMapEnd; ++it ) { draw ? it.value()->show() : it.value()->hide(); } } Widget *CIWidgetMgr::widgetWithID( const QString &id ) const { WidgetMap::const_iterator it = m_widgetMap.find(id); if ( it == m_widgetMap.end() ) return nullptr; else return it.value(); } Button *CIWidgetMgr::button( const QString &id ) const { return dynamic_cast(widgetWithID(id)); } Slider *CIWidgetMgr::slider( const QString &id ) const { return dynamic_cast(widgetWithID(id)); } void CIWidgetMgr::setButtonState( const QString &id, int state ) { Button *b = button(id); if (!b) return; // Actually, we don't want to check to see if we are already down; this way, // we get toggle events when loading from file // bool oldState = b->isDown(); // if ( oldState == state ) // return; b->setState(state); } void CIWidgetMgr::drawWidgets( QPainter &p ) { const WidgetMap::iterator widgetMapEnd = m_widgetMap.end(); for ( WidgetMap::iterator it = m_widgetMap.begin(); it != widgetMapEnd; ++it ) { it.value()->drawShape(p); } } void CIWidgetMgr::removeWidget( const QString & id ) { if ( !m_widgetMap.contains(id) ) return; delete m_widgetMap[id]; m_widgetMap.remove(id); } Button* CIWidgetMgr::addButton( const QString &id, const QRect & pos, const QString &display, bool toggle ) { WidgetMap::iterator it; Button *button = new Button( id, p_cnItem, toggle, pos, p_canvas ); (dynamic_cast(button->widget()))->setText(display); it = m_widgetMap.find(id); if ( it == m_widgetMap.end() ) { m_widgetMap[id] = button; } else { qWarning() << "CIWidgetMgr::addButton: Attempting to re-add button with same id as previous"<updateAttachedPositioning(); return button; } Button* CIWidgetMgr::addButton( const QString &id, const QRect & pos, const QIcon &icon, bool toggle ) { WidgetMap::iterator it; Button *button = new Button( id, p_cnItem, toggle, pos, p_canvas ); button->setIcon( icon ); it = m_widgetMap.find(id); if ( it == m_widgetMap.end() ) { m_widgetMap[id] = button; } else { qWarning() << "CIWidgetMgr::addButton: Attempting to re-add button with same id as previous"<updateAttachedPositioning(); return button; } Slider* CIWidgetMgr::addSlider( const QString &id, int minValue, int maxValue, int pageStep, int value, Qt::Orientation orientation, const QRect & pos ) { Slider *slider = new Slider( id, p_cnItem, pos, p_canvas ); QSlider *qslider = dynamic_cast(slider->widget()); qslider->setMinimum(minValue); qslider->setMaximum(maxValue); qslider->setPageStep(pageStep); qslider->setValue(value); slider->setOrientation(orientation); WidgetMap::iterator it = m_widgetMap.find(id); if ( it == m_widgetMap.end() ) { m_widgetMap[id] = slider; } else { qWarning() << "CIWidgetMgr::addSlider: Attempting to re-add slider with same id as previous"<updateAttachedPositioning(); return slider; } bool CIWidgetMgr::mousePressEvent( const EventInfo &info ) { QMouseEvent *e = info.mousePressEvent( 0, 0 ); const WidgetMap::iterator widgetMapEnd = m_widgetMap.end(); for ( WidgetMap::iterator it = m_widgetMap.begin(); it != widgetMapEnd; ++it ) { if ( it.value()->rect().contains(info.pos) ) { it.value()->mousePressEvent(e); if (e->isAccepted()) { delete e; return true; } } } delete e; return false; } bool CIWidgetMgr::mouseReleaseEvent( const EventInfo &info ) { QMouseEvent *e = info.mouseReleaseEvent( 0, 0 ); qDebug() << Q_FUNC_INFO << " button=" << e->button() << " buttons=" << e->buttons(); const WidgetMap::iterator widgetMapEnd = m_widgetMap.end(); for ( WidgetMap::iterator it = m_widgetMap.begin(); it != widgetMapEnd; ++it ) { it.value()->mouseReleaseEvent(e); } bool accepted = e->isAccepted(); delete e; return accepted; } bool CIWidgetMgr::mouseDoubleClickEvent( const EventInfo &info ) { QMouseEvent *e = info.mouseDoubleClickEvent( 0, 0 ); const WidgetMap::iterator widgetMapEnd = m_widgetMap.end(); for ( WidgetMap::iterator it = m_widgetMap.begin(); it != widgetMapEnd; ++it ) { if ( it.value()->rect().contains(info.pos) ) { it.value()->mouseDoubleClickEvent(e); if (e->isAccepted()) { delete e; return true; } } } delete e; return false; } bool CIWidgetMgr::mouseMoveEvent( const EventInfo &info ) { QMouseEvent *e = info.mouseMoveEvent( 0, 0 ); const WidgetMap::iterator widgetMapEnd = m_widgetMap.end(); for ( WidgetMap::iterator it = m_widgetMap.begin(); it != widgetMapEnd; ++it ) { it.value()->mouseMoveEvent(e); if (e->isAccepted()) { delete e; return true; } } delete e; return false; } bool CIWidgetMgr::wheelEvent( const EventInfo &info ) { QWheelEvent *e = info.wheelEvent( 0, 0 ); const WidgetMap::iterator widgetMapEnd = m_widgetMap.end(); for ( WidgetMap::iterator it = m_widgetMap.begin(); it != widgetMapEnd; ++it ) { if ( it.value()->rect().contains(info.pos) ) { it.value()->wheelEvent(e); if (e->isAccepted()) { delete e; return true; } } } delete e; return false; } void CIWidgetMgr::enterEvent(QEvent *) { const WidgetMap::iterator widgetMapEnd = m_widgetMap.end(); for ( WidgetMap::iterator it = m_widgetMap.begin(); it != widgetMapEnd; ++it ) { it.value()->enterEvent(nullptr); } } void CIWidgetMgr::leaveEvent(QEvent *) { const WidgetMap::iterator widgetMapEnd = m_widgetMap.end(); for ( WidgetMap::iterator it = m_widgetMap.begin(); it != widgetMapEnd; ++it ) { it.value()->leaveEvent(nullptr); } } diff --git a/src/ciwidgetmgr.h b/src/ciwidgetmgr.h index 135b9009..51e1cc59 100644 --- a/src/ciwidgetmgr.h +++ b/src/ciwidgetmgr.h @@ -1,104 +1,104 @@ /*************************************************************************** * Copyright (C) 2005 by David Saxton * * david@bluehaze.org * * * * 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. * ***************************************************************************/ #ifndef CIWIDGETMGR_H #define CIWIDGETMGR_H -#include -#include +#include +#include #include class Button; class CNItem; class Slider; class KtlQCanvas; class Widget; typedef QMap WidgetMap; /** This class handles the widgets (those things associated with CNItems that use QWidgets. This class is pretty much to maintain a tidy interface: the functions could just as well be all shoved in CNItem, but that gets messy. @author David Saxton */ class CIWidgetMgr { public: CIWidgetMgr( KtlQCanvas *canvas, CNItem *item ); virtual ~CIWidgetMgr(); /** * Set the top-left position from which mouse events are interpreted and the * widgets are drawn from. */ void setWidgetsPos( const QPoint &pos ); /** * Returns a pointer to the widget with the given id, or nullptr if no such * widgets are found. */ Widget *widgetWithID( const QString &id ) const; Button *button( const QString &id ) const; Slider *slider( const QString &id ) const; void setButtonState( const QString &id, int state ); /** * Adds a slider with the given id and values to the position */ Slider* addSlider( const QString &id, int minValue, int maxValue, int pageStep, int value, Qt::Orientation orientation, const QRect & pos ); /** * Essentially the same as addDisplayText, but displays a button with * text on it. The virtual functions buttonPressed( const QString &id ) and * buttonReleased( const QString &id ) are called as appropriate with button id */ Button* addButton( const QString &id, const QRect & pos, const QString &display, bool toggle = false ); /** * Adds a button with a QIcon icon on it instead of text * @see void addButton( const QString &id, QRect pos, const QString &display ) */ Button* addButton( const QString &id, const QRect & pos, const QIcon &icon, bool toggle = false ); /** * Removes the widget with the given id. */ void removeWidget( const QString & id ); /** * Sets whether or not to draw the widgets (drawing widgets mucks up SVG * export). This function just calls either hide() or show() in each widget. */ void setDrawWidgets( bool draw ); bool mousePressEvent( const EventInfo &info ); bool mouseReleaseEvent( const EventInfo &info ); bool mouseDoubleClickEvent ( const EventInfo &info ); bool mouseMoveEvent( const EventInfo &info ); bool wheelEvent( const EventInfo &info ); void enterEvent(QEvent *); void leaveEvent(QEvent *); virtual void buttonStateChanged( const QString &/*id*/, bool /*on*/ ) {}; virtual void sliderValueChanged( const QString &/*id*/, int /*value*/ ) {}; int mgrX() const { return m_pos.x(); } int mgrY() const { return m_pos.y(); } /** * Draw the widgets using the given painter. This function isn't actually * used to draw the widgets on the canvas, as they are QCanvasItems * themselves, but allows other classes (e.g. ItemLibrary) to draw them * using a special painter. */ void drawWidgets( QPainter &p ); protected: WidgetMap m_widgetMap; QPoint m_pos; KtlQCanvas *p_canvas; CNItem *p_cnItem; }; #endif diff --git a/src/cnitem.cpp b/src/cnitem.cpp index 8fe8a8d9..4c410733 100644 --- a/src/cnitem.cpp +++ b/src/cnitem.cpp @@ -1,611 +1,611 @@ /*************************************************************************** * Copyright (C) 2003-2005 by David Saxton * * david@bluehaze.org * * * * 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 "canvasitemparts.h" #include "icndocument.h" #include "cells.h" #include "component.h" #include "pinnode.h" #include "junctionnode.h" #include "inputflownode.h" #include "outputflownode.h" #include "junctionflownode.h" #include "itemdocumentdata.h" -#include -#include -#include +#include +#include +#include #include #include CNItem::CNItem( ICNDocument *icnDocument, bool newItem, const QString &id ) : Item( icnDocument, newItem, id ), CIWidgetMgr( icnDocument ? icnDocument->canvas() : nullptr, this ), p_icnDocument(icnDocument), b_pointsAdded(false) { qDebug() << Q_FUNC_INFO << " this=" << this; setZ( ICNDocument::Z::Item ); setSelected(false); m_brushCol = QColor( 0xf7, 0xf7, 0xff ); m_selectedCol = QColor( 101, 134, 192 ); setBrush(m_brushCol); setPen( QPen( Qt::black ) ); } CNItem::~CNItem() { const TextMap::iterator textMapEnd = m_textMap.end(); for ( TextMap::iterator it = m_textMap.begin(); it != textMapEnd; ++it ) { if (it.value()) it.value()->setCanvas(nullptr); delete (Text*)it.value(); } m_textMap.clear(); updateConnectorPoints(false); } bool CNItem::preResize( QRect sizeRect ) { if ( (std::abs(sizeRect.width()) < minimumSize().width()) || (std::abs(sizeRect.height()) < minimumSize().height()) ) return false; updateConnectorPoints(false); return true; } void CNItem::postResize() { updateAttachedPositioning(); } void CNItem::setVisible( bool yes ) { if (b_deleted) { Item::setVisible(false); return; } Item::setVisible(yes); const TextMap::iterator textMapEnd = m_textMap.end(); for ( TextMap::iterator it = m_textMap.begin(); it != textMapEnd; ++it ) { it.value()->setVisible(yes); } const NodeInfoMap::iterator nodeMapEnd = m_nodeMap.end(); for ( NodeInfoMap::iterator it = m_nodeMap.begin(); it != nodeMapEnd; ++it ) { it.value().node->setVisible(yes); } CNItem::setDrawWidgets(yes); if (!yes) updateConnectorPoints(false); } void CNItem::reparented( Item *oldParent, Item *newParent ) { Item::reparented( oldParent, newParent ); updateNodeLevels(); } void CNItem::updateNodeLevels() { int l = level(); // Tell our nodes about our level const NodeInfoMap::iterator nodeMapEnd = m_nodeMap.end(); for ( NodeInfoMap::iterator it = m_nodeMap.begin(); it != nodeMapEnd; ++it ) { it.value().node->setLevel(l); } const ItemList::iterator end = m_children.end(); for ( ItemList::iterator it = m_children.begin(); it != end; ++it ) { if ( CNItem *cnItem = dynamic_cast((Item*)*it) ) cnItem->updateNodeLevels(); } } ConnectorList CNItem::connectorList() { ConnectorList list; const NodeInfoMap::iterator nodeMapEnd = m_nodeMap.end(); for ( NodeInfoMap::iterator it = m_nodeMap.begin(); it != nodeMapEnd; ++it ) { Node *node = p_icnDocument->nodeWithID(it.value().id); if (node) { ConnectorList nodeList = node->getAllConnectors(); ConnectorList::iterator end = nodeList.end(); for ( ConnectorList::iterator it = nodeList.begin(); it != end; ++it ) { if ( *it && !list.contains(*it) ) { list.append(*it); } } } } return list; } void CNItem::removeItem() { if (b_deleted) return; const TextMap::iterator textMapEnd = m_textMap.end(); for ( TextMap::iterator it = m_textMap.begin(); it != textMapEnd; ++it ) it.value()->setCanvas(nullptr); Item::removeItem(); updateConnectorPoints(false); } void CNItem::restoreFromItemData( const ItemData &itemData ) { Item::restoreFromItemData(itemData); updateConnectorPoints(false); { const BoolMap::const_iterator end = itemData.buttonMap.end(); for ( BoolMap::const_iterator it = itemData.buttonMap.begin(); it != end; ++it ) { Button *b = button(it.key()); if (b) b->setState(it.value()); } } { const IntMap::const_iterator end = itemData.sliderMap.end(); for ( IntMap::const_iterator it = itemData.sliderMap.begin(); it != end; ++it ) { Slider *s = slider(it.key()); if (s) s->setValue(it.value()); } } } ItemData CNItem::itemData() const { ItemData itemData = Item::itemData(); const WidgetMap::const_iterator end = m_widgetMap.end(); for ( WidgetMap::const_iterator it = m_widgetMap.begin(); it != end; ++it ) { if ( Slider *slider = dynamic_cast(*it) ) itemData.sliderMap[slider->id()] = slider->value(); else if ( Button *button = dynamic_cast(*it) ) itemData.buttonMap[button->id()] = button->state(); } return itemData; } Node* CNItem::createNode( double _x, double _y, int orientation, const QString &name, uint type ) { orientation %= 360; if ( orientation < 0 ) orientation += 360; Node *node = nullptr; // TODO get rid of this switch statement... switch(type) { case Node::ec_pin: node = new PinNode(p_icnDocument, orientation, QPoint( 0, 0) ); break; case Node::ec_junction: node = new JunctionNode( p_icnDocument, orientation, QPoint( 0, 0) ); break; case Node::fp_junction: node = new JunctionFlowNode(p_icnDocument, orientation, QPoint( 0, 0) ); break; case Node::fp_in: node = new InputFlowNode( p_icnDocument, orientation, QPoint( 0, 0) ); break; case Node::fp_out: node = new OutputFlowNode( p_icnDocument, orientation, QPoint( 0, 0) ); break; } node->setLevel( level() ); node->setParentItem(this); node->setChildId(name); NodeInfo info; info.id = node->id(); info.node = node; info.x = _x; info.y = _y; info.orientation = orientation; m_nodeMap[name] = info; updateAttachedPositioning(); return node; } bool CNItem::removeNode( const QString &name ) { NodeInfoMap::iterator it = m_nodeMap.find(name); if ( it == m_nodeMap.end() ) { return false; } it.value().node->removeNode(); p_icnDocument->flushDeleteList(); m_nodeMap.erase(it); return true; } Node *CNItem::getClosestNode( const QPoint &pos ) { // Work through the nodes, finding the one closest to the (x, y) position Node *shortestNode = nullptr; double shortestDistance = 1e10; // Nice large distance :-) const NodeInfoMap::iterator end = m_nodeMap.end(); for ( NodeInfoMap::iterator it = m_nodeMap.begin(); it != end; ++it ) { Node *node = p_icnDocument->nodeWithID(it.value().id); if (node) { // Calculate the distance // Yeah, it's actually the distance squared; but it's all relative, so doesn't matter double distance = std::pow(node->x()-pos.x(),2) + std::pow(node->y()-pos.y(),2); if ( distance < shortestDistance ) { shortestDistance = distance; shortestNode = node; } } } return shortestNode; } void CNItem::updateAttachedPositioning() { if (b_deleted) return; // Actually, we don't do anything anymore... } void CNItem::updateZ( int baseZ ) { Item::updateZ(baseZ); double _z = z(); const NodeInfoMap::iterator nodeMapEnd = m_nodeMap.end(); for ( NodeInfoMap::iterator it = m_nodeMap.begin(); it != nodeMapEnd; ++it ) it.value().node->setZ( _z + 0.5 ); const WidgetMap::iterator widgetMapEnd = m_widgetMap.end(); for ( WidgetMap::iterator it = m_widgetMap.begin(); it != widgetMapEnd; ++it ) it.value()->setZ( _z + 0.5 ); const TextMap::iterator textMapEnd = m_textMap.end(); for ( TextMap::iterator it = m_textMap.begin(); it != textMapEnd; ++it ) it.value()->setZ( _z + 0.5 ); } void CNItem::moveBy( double dx, double dy ) { if ( dx == 0 && dy == 0 ) return; updateConnectorPoints(false); Item::moveBy( dx, dy ); setWidgetsPos( QPoint( int(x()), int(y()) ) ); } bool CNItem::mousePressEvent( const EventInfo &info ) { bool accepted = Item::mousePressEvent(info); if (!accepted) accepted = CIWidgetMgr::mousePressEvent(info); if (accepted) setChanged(); return accepted; } bool CNItem::mouseReleaseEvent( const EventInfo &info ) { bool accepted = Item::mouseReleaseEvent(info); if (!accepted) accepted = CIWidgetMgr::mouseReleaseEvent(info); if (accepted) setChanged(); return accepted; } bool CNItem::mouseDoubleClickEvent( const EventInfo &info ) { bool accepted = Item::mouseDoubleClickEvent(info); if (!accepted) accepted = CIWidgetMgr::mouseDoubleClickEvent(info); if (accepted) setChanged(); return accepted; } bool CNItem::mouseMoveEvent( const EventInfo &info ) { bool accepted = Item::mouseMoveEvent(info); if (!accepted) accepted = CIWidgetMgr::mouseMoveEvent(info); if (accepted) setChanged(); return accepted; } bool CNItem::wheelEvent( const EventInfo &info ) { bool accepted = Item::wheelEvent(info); if (!accepted) accepted = CIWidgetMgr::wheelEvent(info); if (accepted) setChanged(); return accepted; } void CNItem::enterEvent(QEvent *) { Item::enterEvent(nullptr); CIWidgetMgr::enterEvent(nullptr); setChanged(); } void CNItem::leaveEvent(QEvent *) { Item::leaveEvent(nullptr); CIWidgetMgr::leaveEvent(nullptr); setChanged(); } void CNItem::drawShape( QPainter &p ) { if (!isVisible()) return; // initPainter(p); if ( isSelected() ) p.setPen(m_selectedCol); p.drawPolygon(areaPoints()); p.drawPolyline(areaPoints()); // deinitPainter(p); } void CNItem::initPainter( QPainter &p ) { if ( isSelected() ) p.setPen(m_selectedCol); } void CNItem::updateConnectorPoints( bool add ) { if ( b_deleted || !isVisible() ) add = false; if ( b_pointsAdded == add ) return; b_pointsAdded = add; Cells *cells = p_icnDocument->cells(); if (!cells) return; // Get translation matrix // Hackish... QMatrix m; if ( Component *c = dynamic_cast(this) ) m = c->transMatrix( c->angleDegrees(), c->flipped(), int(x()), int(y()), false ); // Convention used here: _UM = unmapped by both matrix and cell reference, _M = mapped const QPoint start_UM = QPoint( int(x()+offsetX())-8, int(y()+offsetY())-8 ); const QPoint end_UM = start_UM + QPoint( width()+2*8, height()+2*8 ); const QPoint start_M = roundDown( m.map(start_UM), 8 ); const QPoint end_M = roundDown( m.map(end_UM), 8 ); int sx_M = start_M.x(); int ex_M = end_M.x(); int sy_M = start_M.y(); int ey_M = end_M.y(); // Normalise start and end points if ( sx_M > ex_M ) { const int temp = sx_M; sx_M = ex_M; ex_M = temp; } if ( sy_M > ey_M ) { const int temp = sy_M; sy_M = ey_M; ey_M = temp; } ex_M++; ey_M++; const int mult = add ? 1 : -1; for ( int x = sx_M; x < ex_M; x++ ) { for ( int y = sy_M; y < ey_M; y++ ) { if ( cells->haveCell( x, y ) ) { if ( x != sx_M && y != sy_M && x != (ex_M-1) && y != (ey_M-1) ) { cells->cell( x, y ).CIpenalty += mult*ICNDocument::hs_item; } else { // (*cells)[x][y].CIpenalty += mult*ICNDocument::hs_item/2; cells->cell( x, y ).CIpenalty += mult*ICNDocument::hs_connector*5; } } } } #if 0 // And subtract the positions of the node on the border NodeInfoMap::iterator end = m_nodeMap.end(); for ( NodeInfoMap::iterator it = m_nodeMap.begin(); it != end; ++it ) { const int x = (int)((it->second.node->x()-4)/cellSize); const int y = (int)((it->second.node->y()-4)/cellSize); if ( p_icnDocument->isValidCellReference(x,y) ) { (*cells)[x][y].CIpenalty -= mult*ICNDocument::hs_connector*5; } } #endif const TextMap::iterator textMapEnd = m_textMap.end(); for ( TextMap::iterator it = m_textMap.begin(); it != textMapEnd; ++it ) { it.value()->updateConnectorPoints(add); } const WidgetMap::iterator widgetMapEnd = m_widgetMap.end(); for ( WidgetMap::iterator it = m_widgetMap.begin(); it != widgetMapEnd; ++it ) { it.value()->updateConnectorPoints(add); } } Text* CNItem::addDisplayText( const QString &id, const QRect & pos, const QString &display, bool internal, int flags ) { Text *text = nullptr; TextMap::iterator it = m_textMap.find(id); if ( it != m_textMap.end() ) { // qWarning() << "CNItem::addDisplayText: removing old text"<setZ( z()+(internal?0.1:-0.1) ); m_textMap[id] = text; // Calculate the correct size setDisplayText( id, display ); text->show(); return text; } void CNItem::setDisplayText( const QString &id, const QString &display ) { TextMap::iterator it = m_textMap.find(id); if ( it == m_textMap.end() ) { qCritical() << "CNItem::setDisplayText: Could not find text with id \""<setText(display); updateAttachedPositioning(); } void CNItem::removeDisplayText( const QString &id ) { TextMap::iterator it = m_textMap.find(id); if ( it == m_textMap.end() ) { // qCritical() << "CNItem::removeDisplayText: Could not find text with id \""<updateConnectorPoints(false); delete it.value(); m_textMap.erase(it); } QString CNItem::nodeId( const QString &internalNodeId ) { NodeInfoMap::iterator it = m_nodeMap.find(internalNodeId); if ( it == m_nodeMap.end() ) return ""; else return it.value().id; } Node *CNItem::childNode( const QString &childId ) { return p_icnDocument->nodeWithID( nodeId(childId) ); } NodeInfo::NodeInfo() { node = nullptr; x = 0.; y = 0.; orientation = 0; } diff --git a/src/connector.cpp b/src/connector.cpp index 156cb892..9ee47ee5 100644 --- a/src/connector.cpp +++ b/src/connector.cpp @@ -1,630 +1,630 @@ /*************************************************************************** * Copyright (C) 2003-2005 by David Saxton * * david@bluehaze.org * * * * 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 "circuitdocument.h" #include "component.h" #include "connector.h" #include "conrouter.h" #include "cnitem.h" #include "ecnode.h" #include "junctionnode.h" #include "itemdocumentdata.h" #include "wire.h" #include "utils.h" -#include -#include +#include +#include #include #include #include //BEGIN class Connector Connector::Connector(Node * /*startNode*/, Node * /*endNode*/, ICNDocument *icnDocument, QString *id) : //QObject(icnDocument), KtlQCanvasPolygon(icnDocument->canvas()) { QString name("Connector"); if (id) { name.append(QString( "-%1").arg(*id)); } else { name.append("-Unknown"); } setObjectName(name.toLatin1().data()); qDebug() << Q_FUNC_INFO << " this=" << this; m_currentAnimationOffset = 0.0; p_parentContainer = nullptr; p_nodeGroup = nullptr; b_semiHidden = false; b_deleted = false; b_pointsAdded = false; b_manualPoints = false; p_icnDocument = icnDocument; m_conRouter = new ConRouter(p_icnDocument); if (id) { m_id = *id; if ( !p_icnDocument->registerUID(*id) ) { qDebug() << Q_FUNC_INFO << "Connector attempted to register given ID, but ID already in use: " << *id << endl; m_id = p_icnDocument->generateUID( *id ); qDebug() << "Creating a new one: " << m_id << endl; } } else m_id = p_icnDocument->generateUID("connector"); p_icnDocument->registerItem(this); p_icnDocument->requestRerouteInvalidatedConnectors(); setVisible(true); } Connector::~Connector() { p_icnDocument->unregisterUID(id()); delete m_conRouter; for (int i = 0; i < m_wires.size(); i++) delete m_wires[i]; // m_wires.resize(0); } void Connector::setParentContainer(const QString &cnItemId) { // // We only allow the node to be parented once // if ( p_parentContainer || !ICNDocument->itemWithID(cnItemId) ) return; p_parentContainer = p_icnDocument->cnItemWithID(cnItemId); } void Connector::removeConnector(Node*) { if (b_deleted) return; b_deleted = true; // Remove 'penalty' points for this connector from the ICNDocument updateConnectorPoints(false); emit selected(false); emit removed(this); if (startNode()) startNode()->removeConnector(this); if (endNode()) endNode()->removeConnector(this); p_icnDocument->appendDeleteList(this); } int getSlope(float x1, float y1, float x2, float y2) { enum slope { s_n = 0,// . s_v, // | s_h, // - s_s, // / s_d // \ (backwards slash) }; if (x1 == x2) { if (y1 == y2) return s_n; return s_v; } else if (y1 == y2) { return s_h; } else if ((y2 - y1) / (x2 - x1) > 0) { return s_s; } else return s_d; } void Connector::updateDrawList() { if (!startNode() || !endNode() || !canvas()) return; QPointList drawLineList; int prevX = (*m_conRouter->cellPointList()->begin()).x(); int prevY = (*m_conRouter->cellPointList()->begin()).y(); int prevX_canvas = toCanvas(prevX); int prevY_canvas = toCanvas(prevY); Cells *cells = p_icnDocument->cells(); bool bumpNow = false; for (QPoint p: *m_conRouter->cellPointList()) { const int x = p.x(); const int y = p.y(); const int numCon = cells->haveCell(x, y) ? cells->cell(x, y).numCon : 0; const int y_canvas = toCanvas(y); const int x_canvas = toCanvas(x); const bool bumpNext = (prevX == x && numCon > 1 && std::abs(y_canvas - startNode()->y()) > 8 && std::abs(y_canvas - endNode()->y()) > 8); int x0 = prevX_canvas; int x2 = x_canvas; int x1 = (x0 + x2) / 2; int y0 = prevY_canvas; int y3 = y_canvas; int y1 = (y0 == y3) ? y0 : ((y0 < y3) ? y0 + 3 : y0 - 3); int y2 = (y0 == y3) ? y3 : ((y0 < y3) ? y3 - 3 : y3 + 3); if (bumpNow) x0 += 3; if (bumpNext) x2 += 3; if (!bumpNow && !bumpNext) { drawLineList += QPoint(x0, y0); drawLineList += QPoint(x2, y3); } else if (bumpNow) { drawLineList += QPoint(x0, y0); drawLineList += QPoint(x1, y1); drawLineList += QPoint(x2, y3); } else if (bumpNext) { drawLineList += QPoint(x0, y0); drawLineList += QPoint(x1, y2); drawLineList += QPoint(x2, y3); } else { drawLineList += QPoint(x0, y0); drawLineList += QPoint(x1, y1); drawLineList += QPoint(x1, y2); drawLineList += QPoint(x2, y3); } prevX = x; prevY = y; prevY_canvas = y_canvas; prevX_canvas = x_canvas; bumpNow = bumpNext; } // Now, remove redundant points (i.e. those that are either repeated or are // in the same direction as the previous points) if (drawLineList.size() < 3) return; const QPointList::iterator dllEnd = drawLineList.end(); QPointList::iterator previous = drawLineList.begin(); QPointList::iterator current = previous; current++; QPointList::const_iterator next = current; next++; int invalid = -(1 << 30); while (previous != dllEnd && current != dllEnd && next != dllEnd) { const int slope1 = getSlope((*previous).x(), (*previous).y(), (*current).x(), (*current).y()); const int slope2 = getSlope((*current).x(), (*current).y(), (*next).x(), (*next).y()); if (slope1 == slope2 || slope1 == 0 || slope2 == 0) { *current = QPoint(invalid, invalid); } else previous = current; current++; next++; } drawLineList.removeAll(QPoint(invalid, invalid)); // Find the bounding rect { int x1 = invalid, y1 = invalid, x2 = invalid, y2 = invalid; for (const QPoint p: drawLineList) { if (p.x() < x1 || x1 == invalid) x1 = p.x(); if (p.x() > x2 || x2 == invalid) x2 = p.x(); if (p.y() < y1 || y1 == invalid) y1 = p.y(); if (p.y() > y2 || y2 == invalid) y2 = p.y(); } QRect boundRect(x1, y1, x2 - x1, y2 - y1); if (boundRect != m_oldBoundRect) { canvas()->setChanged(boundRect | m_oldBoundRect); m_oldBoundRect = boundRect; } } //BEGIN build up ConnectorLine list for (ConnectorLine* line: m_connectorLineList) delete line; m_connectorLineList.clear(); if (drawLineList.size() > 1) { QPoint prev = drawLineList.first(); int pixelOffset = 0; for (QPoint next: drawLineList) { ConnectorLine *line = new ConnectorLine(this, pixelOffset); m_connectorLineList.append(line); line->setPoints(prev.x(), prev.y(), next.x(), next.y()); // (note that only one of the following QABS will be non-zero) pixelOffset += abs(prev.x() - next.x()) + abs(prev.y() - next.y()); prev = next; } } updateConnectorLines(); //END build up ConnectorPoint list } void Connector::setSemiHidden(bool semiHidden) { if (!canvas() || semiHidden == b_semiHidden) return; b_semiHidden = semiHidden; updateConnectorLines(); } void Connector::updateConnectorPoints(bool add) { if (!canvas()) return; if (b_deleted || !isVisible()) add = false; // Check we haven't already added/removed the points... if (b_pointsAdded == add) return; b_pointsAdded = add; // We don't include the end points in the mapping if (m_conRouter->cellPointList()->size() < 3) return; Cells * cells = p_icnDocument->cells(); const int mult = (add) ? 1 : -1; for (QPoint p: *m_conRouter->cellPointList()) { int x = p.x(); int y = p.y(); // Add the points of this connector to the cell array in the ICNDocument, // so that other connectors still to calculate their points know to try // and avoid this connector p_icnDocument->addCPenalty(x , y - 1, mult*ICNDocument::hs_connector / 2); p_icnDocument->addCPenalty(x - 1, y , mult*ICNDocument::hs_connector / 2); p_icnDocument->addCPenalty(x , y , mult*ICNDocument::hs_connector ); p_icnDocument->addCPenalty(x + 1, y , mult*ICNDocument::hs_connector / 2); p_icnDocument->addCPenalty(x , y + 1, mult*ICNDocument::hs_connector / 2); if (cells->haveCell(x , y)) cells->cell(x, y).numCon += mult; } // updateDrawList(); } void Connector::setRoutePoints(QPointList pointList, bool setManual, bool checkEndPoints) { if (!canvas()) return; updateConnectorPoints(false); bool reversed = pointsAreReverse(pointList); // a little performance boost: don't call (start|end)Node 4 times Node* l_endNode = endNode(); Node* l_startNode = startNode(); if (checkEndPoints) { if (reversed) { pointList.prepend(QPoint(int(l_endNode->x()), int(l_endNode->y()))); pointList.append(QPoint(int(l_startNode->x()), int(l_startNode->y()))); } else { pointList.prepend(QPoint(int(l_startNode->x()), int(l_startNode->y()))); pointList.append(QPoint(int(l_endNode->x()), int(l_endNode->y()))); } } m_conRouter->setPoints(pointList, reversed); b_manualPoints = setManual; updateConnectorPoints(true); } bool Connector::pointsAreReverse(const QPointList &pointList) const { if (!startNode() || !endNode()) { qWarning() << Q_FUNC_INFO << "Cannot determine orientation as no start and end nodes" << endl; return false; } if (pointList.isEmpty()) return false; int plsx = pointList.first().x(); int plsy = pointList.first().y(); int plex = pointList.last().x(); int pley = pointList.last().y(); double nsx = startNode()->x(); double nsy = startNode()->y(); double nex = endNode()->x(); double ney = endNode()->y(); double dist_normal = (nsx - plsx) * (nsx - plsx) + (nsy - plsy) * (nsy - plsy) + (nex - plex) * (nex - plex) + (ney - pley) * (ney - pley); double dist_reverse = (nsx - plex) * (nsx - plex) + (nsy - pley) * (nsy - pley) + (nex - plsx) * (nex - plsx) + (ney - plsy) * (ney - plsy); return dist_reverse < dist_normal; } void Connector::rerouteConnector() { if (!isVisible()) return; if (nodeGroup()) { qWarning() << Q_FUNC_INFO << "Connector is controlled by a NodeGroup! Use that to reroute the connector" << endl; return; } if (!startNode() || !endNode()) return; updateConnectorPoints(false); m_conRouter->mapRoute(int(startNode()->x()), int(startNode()->y()), int(endNode()->x()), int(endNode()->y())); b_manualPoints = false; updateConnectorPoints(true); } void Connector::translateRoute(int dx, int dy) { updateConnectorPoints(false); m_conRouter->translateRoute(dx, dy); updateConnectorPoints(true); updateDrawList(); } void Connector::restoreFromConnectorData(const ConnectorData &connectorData) { updateConnectorPoints(false); b_manualPoints = connectorData.manualRoute; m_conRouter->setRoutePoints(connectorData.route); updateConnectorPoints(true); updateDrawList(); } ConnectorData Connector::connectorData() const { ConnectorData connectorData; if (!startNode() || !endNode()) { qDebug() << Q_FUNC_INFO << " m_startNode=" << startNode() << " m_endNode=" << endNode() << endl; return connectorData; } connectorData.manualRoute = usesManualPoints(); connectorData.route = *m_conRouter->cellPointList(); if (startNode()->isChildNode()) { connectorData.startNodeIsChild = true; connectorData.startNodeCId = startNode()->childId(); connectorData.startNodeParent = startNode()->parentItem()->id(); } else { connectorData.startNodeIsChild = false; connectorData.startNodeId = startNode()->id(); } if (endNode()->isChildNode()) { connectorData.endNodeIsChild = true; connectorData.endNodeCId = endNode()->childId(); connectorData.endNodeParent = endNode()->parentItem()->id(); } else { connectorData.endNodeIsChild = false; connectorData.endNodeId = endNode()->id(); } return connectorData; } void Connector::setVisible(bool yes) { if (!canvas() || isVisible() == yes) return; KtlQCanvasPolygon::setVisible(yes); updateConnectorLines(); } Wire *Connector::wire(unsigned num) const { return (num < m_wires.size()) ? m_wires[num] : nullptr; } void Connector::setSelected(bool yes) { if (!canvas() || isSelected() == yes) return; KtlQCanvasPolygon::setSelected(yes); updateConnectorLines(); emit selected(yes); } void Connector::updateConnectorLines(bool forceRedraw) { QColor color; if (b_semiHidden) color = Qt::gray; else if (isSelected()) color = QColor(101, 134, 192); else if (!KTLConfig::showVoltageColor()) color = Qt::black; else color = Component::voltageColor(wire() ? wire()->voltage() : 0.0); int z = ICNDocument::Z::Connector + (isSelected() ? 5 : 0); QPen pen(color, (numWires() > 1) ? 2 : 1); bool animateWires = KTLConfig::animateWires(); for (KtlQCanvasPolygonalItem *item: m_connectorLineList) { bool changed = (item->z() != z) || (item->pen() != pen) || (item->isVisible() != isVisible()); if (!changed) { if (forceRedraw) canvas()->setChanged(item->boundingRect()); continue; } item->setZ(z); item->setPen(pen); item->setVisible(isVisible()); } } QList Connector::splitConnectorPoints(const QPoint & pos) const { return m_conRouter->splitPoints(pos); } QPointList Connector::connectorPoints(bool reverse) const { bool doReverse = (reverse != pointsAreReverse(m_conRouter->pointList(false))); return m_conRouter->pointList(doReverse); } void Connector::incrementCurrentAnimation(double deltaTime) { // The values and equations used in this function have just been developed // empircally to be able to show a nice range of currents while still giving // a good indication of the amount of current flowing double I_min = 1e-4; double sf = 3.0; // scaling factor for (int i = 0; i < m_wires.size(); ++i) { if (!m_wires[i]) continue; double I = m_wires[i]->current(); double sign = (I > 0) ? 1 : -1; double I_abs = I * sign; double prop = (I_abs > I_min) ? std::log(I_abs / I_min) : 0.0; m_currentAnimationOffset += deltaTime * sf * std::pow(prop, 1.3) * sign; } } //END class Connector //BEGIN class ConnectorLine ConnectorLine::ConnectorLine(Connector * connector, int pixelOffset) : //QObject(connector), KtlQCanvasLine(connector->canvas()) { qDebug() << Q_FUNC_INFO << " this=" << this; m_pConnector = connector; m_pixelOffset = pixelOffset; } /** * @returns x, possibly moving it to the closest bound if it is out of bounds. */ int boundify(int x, int bound1, int bound2) { if (bound2 < bound1) { // swap bounds int temp = bound2; bound2 = bound1; bound1 = temp; } // now, have bound1 <= bound2 if (x < bound1) return bound1; else if (x > bound2) return bound2; else return x; } void ConnectorLine::drawShape(QPainter & p) { if (!m_bAnimateCurrent) { KtlQCanvasLine::drawShape(p); return; } int ss = 3; // segment spacing int sl = 13; // segment length (includes segment spacing) int offset = int(m_pConnector->currentAnimationOffset() - m_pixelOffset); offset = ((offset % sl) - sl) % sl; int x1 = startPoint().x(); int y1 = startPoint().y(); int x2 = endPoint().x(); int y2 = endPoint().y(); QPen pen = p.pen(); // pen.setStyle( Qt::DashDotLine ); p.setPen(pen); if (x1 == x2) { int _x = int(x() + x1); int y_end = int(y() + y2); if (y1 > y2) { // up connector line for (int _y = int(y() + y1 - offset); _y >= y_end; _y -= sl) { int y_1 = boundify(_y, int(y() + y1), y_end); int y_2 = boundify(_y - (sl - ss), int(y() + y1), y_end); p.drawLine(_x, y_1, _x, y_2); } } else { // down connector line for (int _y = int(y() + y1 + offset); _y <= y_end; _y += sl) { int y_1 = boundify(_y, int(y() + y1), y_end); int y_2 = boundify(_y + (sl - ss), int(y() + y1), y_end); p.drawLine(_x, y_1, _x, y_2); } } } else { // y1 == y2 int _y = int(y() + y1); int x_end = int(x() + x2); if (x1 > x2) { // left connector line int x_start = int(x() + x1 - offset); for (int _x = x_start; _x >= x_end; _x -= sl) { int x_1 = boundify(_x, int(x() + x1), x_end); int x_2 = boundify(_x - (sl - ss), int(x() + x1), x_end); p.drawLine(x_1, _y, x_2, _y); } } else { // right connector line for (int _x = int(x() + x1 + offset); _x <= x_end; _x += sl) { int x_1 = boundify(_x, int(x() + x1), x_end); int x_2 = boundify(_x + (sl - ss), int(x() + x1), x_end); p.drawLine(x_1, _y, x_2, _y); } } } } //END class ConnectorLine diff --git a/src/connector.h b/src/connector.h index bb695829..5c6fae06 100644 --- a/src/connector.h +++ b/src/connector.h @@ -1,254 +1,254 @@ /*************************************************************************** * Copyright (C) 2003-2005 by David Saxton * * david@bluehaze.org * * * * 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. * ***************************************************************************/ #ifndef CONNECTOR_H #define CONNECTOR_H //#include // 2018.10.16 - not needed #include "canvasitems.h" -#include +#include // #include class Cell; class ConnectorData; class ConnectorLine; class ConRouter; class CNItem; class ICNDocument; class Node; class NodeGroup; class Wire; typedef QList ConnectorLineList; typedef QList QPointList; typedef QVector > WireVector; /** @short Represents a connection between two Nodes on a ICNDocument @author David Saxton */ class Connector : /* public QObject, */ public KtlQCanvasPolygon { Q_OBJECT public: Connector(Node *startNode, Node *endNode, ICNDocument *_ICNDocument, QString *id = nullptr); ~Connector() override; /** * Node at start of connector (which refers to this as the output connector) */ virtual Node *startNode() const = 0; /** * Node at end of connector (which refers to this as the input connector) */ virtual Node *endNode() const = 0; /** * @returns connector data describing this connector */ ConnectorData connectorData() const; /** * Restore the state of the connector (route, etc) from the saved data */ void restoreFromConnectorData(const ConnectorData &connectorData); /** * If selected, will be drawn in a different colour */ void setSelected(bool yes) override; /** * Connected id */ QString id() const { return m_id; } /** * Update the list of lines and connetion-points that the connector uses for * drawing. */ void updateDrawList(); /** * Tells the connector that it is under the control of a NodeGroup. When * the connector is under the control of a NodeGroup, all requests for * connection rerouting will be passed onto that NodeGroup */ void setNodeGroup(NodeGroup *nodeGroup) { p_nodeGroup = nodeGroup; } /** * Returns the NodeGroup that the connector is under the control of (if any) */ NodeGroup *nodeGroup() const { return p_nodeGroup; } /** * ICNDocument needs to know what 'cells' a connector is present in, * so that connection mapping can be done to avoid connectors. * This function will add the hit penalty to the cells pointed to * by ICNDocument::cells() */ void updateConnectorPoints(bool add); /** * Sets the canvas points that the connector should route itself along. * This is used for manual routing. The cells points are absolute positions * (unlike the points stored internally in this class, which are the cell poisition * @param setManual if true then the connector will change to a manual route one * @param checkEndPoints if true then will check to see if the end points are at the nodes, and adds them if not */ void setRoutePoints(QPointList pointList, bool setManual, bool checkEndPoints = false); /** * Call this function (e.g. when moving a CNItem connected to the connector) * to make the connector partially hidden - probably grayed out - if semiHidden * is true. */ void setSemiHidden(bool semiHidden); /** * Sets the container parent (i.e. the container of the parent item) */ void setParentContainer(const QString &cnItemId); /** * Returns a pointer to the parent item container */ CNItem *parentContainer() const { return p_parentContainer; } /** * @returns whether the points have been set by the user manually defining them */ bool usesManualPoints() const { return b_manualPoints; } /** * Returns two sets of points (in canvas-reference) that define the connector * from start to finish, when it is split at the given point (in canvas-reference) */ QList splitConnectorPoints(const QPoint &pos) const; /** * @returns pointer to ICNDocument that this connector is a member of */ ICNDocument *icnDocument() const { return p_icnDocument; } /** * Looks at the set of canvas points and tries to determine whether they are * in the reverse order from start to end node */ bool pointsAreReverse(const QPointList &pointList) const; /** * Returns the points, given in canvas-reference, in order of start node to * end node if reverse is false * @param reverse whether or not to reverse the points from start node to end node */ QPointList connectorPoints(bool reverse = false) const; /** * Reroute the connector. Note that if this connector is controlled by a * NodeGroup, it will do nothing (other than print out a warning) */ void rerouteConnector(); /** * Translates the route by the given amoumt. No checking is done to see if * the translation is useful, etc. */ void translateRoute(int dx, int dy); void setVisible(bool yes) override; /** Methods relating to wire lists */ WireVector wires() const { return m_wires; } unsigned numWires() const { return m_wires.size(); } Wire* wire(unsigned num=0) const; void updateConnectorLines(bool forceRedraw = false); /** * Modular offset of moving dots in connector, indicating current (in * pixels). */ double currentAnimationOffset() const { return m_currentAnimationOffset; } /** * Increases the currentAnimationOffset according to the current flowing in * the connector and deltaTime. */ void incrementCurrentAnimation(double deltaTime); signals: void removed(Connector *connector); void selected(bool yes); void numWiresChanged(unsigned newNum); public slots: void removeConnector(Node* = nullptr); //protected: // bool m_bIsSyncingWires; protected: WireVector m_wires; private: bool b_semiHidden; bool b_deleted; bool b_manualPoints; bool b_pointsAdded; double m_currentAnimationOffset; NodeGroup *p_nodeGroup; CNItem *p_parentContainer; ICNDocument *p_icnDocument; ConRouter *m_conRouter; QString m_id; QRect m_oldBoundRect; ConnectorLineList m_connectorLineList; }; typedef QList > ConnectorList; //BEGIN ConnectorLine things class ConnectorLine : /* public QObject, */ public KtlQCanvasLine { public: /** * @param pixelOffset the number of pixels between the start of the * parent connector and the start of this wire. Used in current * animation. */ ConnectorLine(Connector *connector, int pixelOffset); Connector *parent() const { return m_pConnector; } void setAnimateCurrent(bool animateCurrent) { m_bAnimateCurrent = animateCurrent; } protected: void drawShape(QPainter &p) override; Connector *m_pConnector; int m_pixelOffset; bool m_bAnimateCurrent; }; //END ConnectorLine things #endif diff --git a/src/conrouter.cpp b/src/conrouter.cpp index be557275..7c4c17d0 100644 --- a/src/conrouter.cpp +++ b/src/conrouter.cpp @@ -1,481 +1,481 @@ /*************************************************************************** * Copyright (C) 2003-2004 by David Saxton * * david@bluehaze.org * * * * 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 "conrouter.h" #include "icndocument.h" #include "utils.h" -#include +#include #include #include #include ConRouter::ConRouter( ICNDocument *cv ) { p_icnDocument = cv; m_lcx = m_lcy = 0; } ConRouter::~ConRouter() { } QPointList ConRouter::pointList( bool reverse ) const { QPointList pointList; if (reverse) { bool notDone = m_cellPointList.size() > 0; for ( QPointList::const_iterator it = (--m_cellPointList.constEnd()); notDone; --it ) { pointList.append( toCanvas(&*it) ); if ( it == m_cellPointList.begin() ) notDone = false; } } else { const QPointList::const_iterator end = m_cellPointList.end(); for ( QPointList::const_iterator it = m_cellPointList.begin(); it != end; ++it ) { pointList.append( toCanvas(&*it) ); } } return pointList; } QPointListList ConRouter::splitPoints( const QPoint &pos ) const { const QPoint split = fromCanvas(&pos); QList list; // Check that the point is in the connector points, and not at the start or end bool found = false; QPointList::const_iterator end = m_cellPointList.end(); double dl[] = { 0.0, 1.1, 1.5 }; // sqrt(2) < 1.5 < sqrt(5) for ( unsigned i = 0; (i < 3) && !found; ++i ) { for ( QPointList::const_iterator it = m_cellPointList.begin(); it != end && !found; ++it ) { QPointList::const_iterator fromLast = --m_cellPointList.constEnd(); if ( qpoint_distance( *it, split ) <= dl[i] && it != m_cellPointList.begin() && it != fromLast) // m_cellPointList.fromLast() ) found = true; } } QPointList first; QPointList second; if (!found) { qWarning() << "ConRouter::splitConnectorPoints: Could not find point ("<isValidCellReference(x,y) ) return; if ( !cellsPtr->haveCell( x, y ) ) return; Cell * c = &cellsPtr->cell( x, y ); if ( c->permanent ) return; int newScore = nextScore + c->CIpenalty + c->Cpenalty; // Check for changing direction if ( x != prevX && prev->prevX == prevX ) newScore += 5; else if ( y != prevY && prev->prevY == prevY ) newScore += 5; if ( c->bestScore < newScore ) return; // We only want to change the previous cell if the score is different, // or the score is the same but this cell allows the connector // to travel in the same direction if ( c->bestScore == newScore && x != prevX && y != prevY ) return; c->bestScore = newScore; c->prevX = prevX; c->prevY = prevY; if ( !c->addedToLabels ) { c->addedToLabels = true; Point point; point.x = x; point.y = y; point.prevX = prevX; point.prevY = prevY; TempLabelMap::iterator it = tempLabels.insert( std::make_pair(newScore,point) ); c->point = &it->second; } else { c->point->prevX = prevX; c->point->prevY = prevY; } } void ConRouter::checkCell( int x, int y ) { Cell * c = &cellsPtr->cell( x, y ); c->permanent = true; int nextScore = c->bestScore+1; // Check the surrounding cells (up, left, right, down) checkACell( x, y-1, c, x, y, nextScore ); checkACell( x-1, y, c, x, y, nextScore ); checkACell( x+1, y, c, x, y, nextScore ); checkACell( x, y+1, c, x, y, nextScore ); } bool ConRouter::needsRouting( int sx, int sy, int ex, int ey ) const { if ( m_cellPointList.size() < 2 ) { // Better be on the safe side... return true; } const int scx = fromCanvas(sx); const int scy = fromCanvas(sy); const int ecx = fromCanvas(ex); const int ecy = fromCanvas(ey); const int psx = m_cellPointList.first().x(); const int psy = m_cellPointList.first().y(); const int pex = m_cellPointList.last().x(); const int pey = m_cellPointList.last().y(); return (psx != scx || psy != scy || pex != ecx || pey != ecy ) && (pex != scx || pey != scy || psx != ecx || psy != ecy ); } void ConRouter::setRoutePoints( const QPointList &pointList ) { m_cellPointList = pointList; removeDuplicatePoints(); } void ConRouter::setPoints( const QPointList &pointList, bool reverse ) { if ( pointList.size() == 0 ) return; QPointList cellPointList; QPoint prevCellPoint = fromCanvas(*pointList.begin()); cellPointList.append(prevCellPoint); const QPointList::const_iterator end = pointList.end(); for ( QPointList::const_iterator it = pointList.begin(); it != end; ++it ) { QPoint cellPoint = fromCanvas(*it); while ( prevCellPoint != cellPoint ) { cellPointList.append(prevCellPoint); if ( prevCellPoint.x() < cellPoint.x() ) prevCellPoint.setX( prevCellPoint.x()+1 ); else if ( prevCellPoint.x() > cellPoint.x() ) prevCellPoint.setX( prevCellPoint.x()-1 ); if ( prevCellPoint.y() < cellPoint.y() ) prevCellPoint.setY( prevCellPoint.y()+1 ); else if ( prevCellPoint.y() > cellPoint.y() ) prevCellPoint.setY( prevCellPoint.y()-1 ); }; prevCellPoint = cellPoint; } cellPointList.append(prevCellPoint); if (reverse) { m_cellPointList.clear(); const QPointList::iterator begin = cellPointList.begin(); for ( QPointList::iterator it = --cellPointList.end(); it != begin; --it ) { m_cellPointList += *it; } m_cellPointList += *begin; } else { m_cellPointList = cellPointList; } removeDuplicatePoints(); } void ConRouter::translateRoute( int dx, int dy ) { if ( dx == 0 && dy == 0 ) return; m_lcx += dx; m_lcy += dy; // const QPoint ds = QPoint( fromCanvas(dx), fromCanvas(dy) ); const QPoint ds = QPoint( dx/8, dy/8 ); QPointList::iterator end = m_cellPointList.end(); for ( QPointList::iterator it = m_cellPointList.begin(); it != end; ++it ) { (*it) += ds; } removeDuplicatePoints(); } void ConRouter::mapRoute( int sx, int sy, int ex, int ey ) { const int scx = fromCanvas(sx); const int scy = fromCanvas(sy); const int ecx = fromCanvas(ex); const int ecy = fromCanvas(ey); cellsPtr = p_icnDocument->cells(); if ( !cellsPtr->haveCell( scx, scy ) || !cellsPtr->haveCell( ecx, ecy ) ) { qDebug() << Q_FUNC_INFO << "cellPtr doesn't have cells, giving up"; return; } m_cellPointList.clear(); m_lcx = ecx; m_lcy = ecy; // First, lets try some common connector routes (which will not necesssarily // be shortest, but they will be neat, and cut down on overall CPU usage) // If that fails, we will resort to a shortest-route algorithm to find an // appropriate route. // Connector configuration: Line { bool ok = checkLineRoute( scx, scy, ecx, ecy, 4*ICNDocument::hs_connector, 0 ); if (ok) { return; } else { m_cellPointList.clear(); } } // Corner 1 { bool ok = checkLineRoute( scx, scy, ecx, ecy, 2*ICNDocument::hs_connector, 0 ); if (!ok) { m_cellPointList.clear(); } else { ok = checkLineRoute( scx, scy, ecx, ecy, ICNDocument::hs_connector-1, 0 ); if (ok) { return; } else { m_cellPointList.clear(); } } } // Corner 2 { bool ok = checkLineRoute( scx, scy, ecx, ecy, 2*ICNDocument::hs_connector, 0 ); if (!ok) { m_cellPointList.clear(); } else { ok = checkLineRoute( scx, scy, ecx, ecy, ICNDocument::hs_connector-1, 0 ); if (ok) { return; } else { m_cellPointList.clear(); } } } // It seems we must resort to brute-force route-checking { cellsPtr->reset(); xcells = p_icnDocument->canvas()->width()/8; ycells = p_icnDocument->canvas()->height()/8; // Now to map out the shortest routes to the cells Cell * const startCell = &cellsPtr->cell( ecx, ecy ); startCell->permanent = true; startCell->bestScore = 0; startCell->prevX = startCellPos; startCell->prevY = startCellPos; tempLabels.clear(); checkCell( ecx, ecy ); // Daniel: I changed it from a do while to a while otherwise // in rare cases the iterator can end up as end(). while ( tempLabels.size() > 0 && !cellsPtr->cell( scx, scy ).permanent ) { TempLabelMap::iterator it = tempLabels.begin(); checkCell( it->second.x, it->second.y ); tempLabels.erase(it); } // Now, retrace the shortest route from the endcell to get out points :) int x = scx, y = scy; bool ok = true; do { m_cellPointList.append( QPoint( x, y ) ); int newx = cellsPtr->cell( x, y ).prevX; int newy = cellsPtr->cell( x, y ).prevY; if ( newx == x && newy == y ) { ok = false; } x = newx; y = newy; } while ( cellsPtr->haveCell( x, y ) && (x != startCellPos) && (y != startCellPos) && ok ); // And append the last point... m_cellPointList.append( QPoint( ecx, ecy ) ); } removeDuplicatePoints(); } bool ConRouter::checkLineRoute( int scx, int scy, int ecx, int ecy, int maxConScore, int maxCIScore ) { if ( (scx != ecx) && (scy != ecy) ) return false; const bool isHorizontal = scy == ecy; int start=0, end=0, x=0, y=0, dd=0; if (isHorizontal) { dd = (scxcells(); if (isHorizontal) { for ( int x = start; x!=end; x+=dd ) { if ( std::abs(x-start)>1 && std::abs(x-end)>1 && (cells->cell( x, y ).CIpenalty > maxCIScore || cells->cell( x, y ).Cpenalty > maxConScore) ) { return false; } else m_cellPointList.append( QPoint( x, y ) ); } } else { for ( int y = start; y!=end; y+=dd ) { if ( std::abs(y-start)>1 && std::abs(y-end)>1 && (cells->cell( x, y ).CIpenalty > maxCIScore || cells->cell( x, y ).Cpenalty > maxConScore) ) { return false; } else { m_cellPointList.append( QPoint( x, y ) ); } } } m_cellPointList.prepend( QPoint( scx, scy ) ); m_cellPointList.append( QPoint( ecx, ecy ) ); removeDuplicatePoints(); return true; } void ConRouter::removeDuplicatePoints() { QPoint invalid( -(1<<30), -(1<<30) ); QPoint prev = invalid; const QPointList::iterator end = m_cellPointList.end(); for ( QPointList::iterator it = m_cellPointList.begin(); it != end; ++it ) { if ( *it == prev ) { *it = invalid; } else { prev = *it; } } m_cellPointList.removeAll( invalid ); } diff --git a/src/conrouter.h b/src/conrouter.h index 6b11d84a..870572a0 100644 --- a/src/conrouter.h +++ b/src/conrouter.h @@ -1,102 +1,102 @@ /*************************************************************************** * Copyright (C) 2003-2004 by David Saxton * * david@bluehaze.org * * * * 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. * ***************************************************************************/ #ifndef CONROUTER_H #define CONROUTER_H #include "cells.h" -#include -#include +#include +#include class ICNDocument; class Cell; typedef QList QPointList; typedef QList QPointListList; /** Abstraction for the routing of a connector. NB: As a general rule of thumb, the point references stored as members of this class are in Cell-space (i.e. 8^2 x smaller than Canvas-space), and the interfacing functions take or give point references in Canvas-space (unless otherwise indicated). @author David Saxton */ class ConRouter { public: ConRouter( ICNDocument *cv ); ~ConRouter(); /** * What this class is all about - finding a route, from (sx,sy) to (ex,ey). */ void mapRoute( int sx, int sy, int ex, int ey ); /** * Translates the precalculated routepoints by the given amount */ void translateRoute( int dx, int dy ); /** * Sets the route to the given canvas points * @param reverse if true, the points in pointList will be reversed */ void setPoints( const QPointList &pointList, bool reverse = false ); /** * Sets the route to the given route points */ void setRoutePoints( const QPointList &pointList ); /** * @returns true if the start or end points differ from that of the current route */ bool needsRouting( int sx, int sy, int ex, int ey ) const; /** * Returns the list of canvas points */ QPointList pointList( bool reverse ) const; /** * Returns a pointer to the internall cellPointList */ QPointList *cellPointList() { return &m_cellPointList; } /** * This will return two lists of Canvas points from the splitting of the * route at the Canvas point "pos". The internall stored points are not * affected. */ QPointListList splitPoints( const QPoint &pos ) const; /** * This will return a list of Canvas pointLists from the route, divided * into n parts (at n-1 equally spaced places). */ QPointListList dividePoints( uint n ) const; protected: /** * Check a line of the ICNDocument cells for a valid route */ bool checkLineRoute( int scx, int scy, int ecx, int ecy, int maxConScore, int maxCIScore ); void checkACell( int x, int y, Cell *prev, int prevX, int prevY, int nextScore ); void checkCell( int x, int y ); // Gets the shortest route from the final cell /** * Remove duplicated points from the route */ void removeDuplicatePoints(); int xcells, ycells; int m_lcx, m_lcy; // Last x / y from mapRoute, if we need a point on the route Cells *cellsPtr; TempLabelMap tempLabels; ICNDocument *p_icnDocument; QPointList m_cellPointList; }; #endif diff --git a/src/core/diagnosticstyle.cpp b/src/core/diagnosticstyle.cpp index 54299870..526d2e3a 100644 --- a/src/core/diagnosticstyle.cpp +++ b/src/core/diagnosticstyle.cpp @@ -1,38 +1,38 @@ /*************************************************************************** * Copyright (C) 2015 Zoltan Padrah * * * * 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 "diagnosticstyle.h" -#include -#include +#include +#include void DiagnosticStyle::drawControl(ControlElement element, const QStyleOption* option, QPainter* painter, const QWidget* widget) const { BaseStyle::drawControl(element, option, painter, widget); if (widget && painter) { // draw a border around the widget painter->setPen(QColor("red")); painter->drawRect(widget->rect()); // show the classname of the widget QBrush translucentBrush(QColor(255,246,240, 100)); painter->fillRect(widget->rect(), translucentBrush); painter->setPen(QColor("darkblue")); // QFont textFont = painter->font(); // textFont.setPointSize( 8 ); // painter->setFont(textFont); QString text(widget->metaObject()->className()); text.append(":"); text.append(widget->objectName()); painter->drawText(widget->rect(), Qt::AlignLeft | Qt::AlignTop, //Qt::AlignRight | Qt::AlignBottom, text); } } diff --git a/src/core/diagnosticstyle.h b/src/core/diagnosticstyle.h index e97e839a..f3238b33 100644 --- a/src/core/diagnosticstyle.h +++ b/src/core/diagnosticstyle.h @@ -1,30 +1,30 @@ /*************************************************************************** * Copyright (C) 2015 Zoltan Padrah * * * * 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. * ***************************************************************************/ #ifndef KTECHLAB_CORE_MAIN_H__ #define KTECHLAB_CORE_MAIN_H__ // #include -#include +#include /** * see approach from here: * http://stackoverflow.com/questions/5909907/drawing-an-overlay-on-top-of-an-applications-window */ class DiagnosticStyle : public QProxyStyle { Q_OBJECT public: typedef QProxyStyle BaseStyle; void drawControl(ControlElement element, const QStyleOption* option, QPainter* painter, const QWidget* widget) const override; ~DiagnosticStyle() override { } }; #endif // KTECHLAB_CORE_MAIN_H__ diff --git a/src/core/logtofilemsghandler.cpp b/src/core/logtofilemsghandler.cpp index bf35f7fd..0d7d4369 100644 --- a/src/core/logtofilemsghandler.cpp +++ b/src/core/logtofilemsghandler.cpp @@ -1,118 +1,118 @@ /* * * Copyright 2015 Zoltan Padrah * * 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) version 3 or any later version * accepted by the membership of KDE e.V. (or its successor approved * by the membership of KDE e.V.), which shall act as a proxy * defined in Section 14 of version 3 of the license. * * 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, see . * */ #include "logtofilemsghandler.h" #include #include #include -#include +#include #include #include #include #include static FILE *logFile = nullptr; static void ktlLogToFile(QtMsgType type, const char *msg) { if (!logFile) { return; } QString nowStr = QDateTime::currentDateTime().toString("yy-MM-dd hh:mm:ss,zzz"); const QByteArray latin1Text = nowStr.toLatin1(); const char *nowCStr = latin1Text.data(); switch (type) { case QtDebugMsg: fprintf(logFile, "%s (DD) %s\n", nowCStr, msg); fflush(logFile); break; case QtWarningMsg: fprintf(logFile, "%s (WW) %s\n", nowCStr, msg); fflush(logFile); break; case QtCriticalMsg: fprintf(logFile, "%s (Critical) %s\n", nowCStr, msg); fflush(logFile); break; case QtFatalMsg: fprintf(stderr, " %s (Fatal) %s\n", nowCStr, msg); fflush(logFile); } } static void ktlLogToStderr(QtMsgType type, const char *msg) { switch (type) { case QtDebugMsg: //fprintf(stderr, "(DD) %s\n", msg); // quite noisy break; case QtWarningMsg: fprintf(stderr, "(WW) %s\n", msg); break; case QtCriticalMsg: fprintf(stderr, "(Critical) %s\n", msg); break; case QtFatalMsg: fprintf(stderr, "(Fatal) %s\n", msg); } } static void ktlMessageOutput(QtMsgType type, const char *msg) { ktlLogToFile(type, msg); ktlLogToStderr(type, msg); if (QtFatalMsg == type) { abort(); } } LogToFileMsgHandler::LogToFileMsgHandler() { logFile = nullptr; qint64 appPid = QApplication::applicationPid(); QString logFileName = QString("/tmp/ktechlab-pid-%1-log").arg(appPid); qDebug() << "Starting logging to " << logFileName; logFile = fopen(logFileName.toLatin1().data(), "w+"); if (!logFile) { const int lastErrno = errno; qWarning() << "Failed to create log file" << logFileName << ". errno=" << lastErrno << ", strerror=" << strerror(lastErrno); return; } qInstallMsgHandler(ktlMessageOutput); qDebug() << "logging started to " << logFileName << " by " << this; } LogToFileMsgHandler::~LogToFileMsgHandler() { qDebug() << "logging ending by " << this; const int closeRet = fclose(logFile); logFile = nullptr; if (closeRet) { const int lastErrno = errno; qCritical() << "failed to close log file, errno=" << lastErrno; } qInstallMsgHandler(nullptr); if (closeRet) { const int lastErrno = errno; qCritical() << "failed to close log file, errno=" << lastErrno; } } diff --git a/src/debugmanager.cpp b/src/debugmanager.cpp index 5f45314d..dbf9bd3e 100644 --- a/src/debugmanager.cpp +++ b/src/debugmanager.cpp @@ -1,87 +1,87 @@ /*************************************************************************** * Copyright (C) 2005 by David Saxton * * david@bluehaze.org * * * * 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 "config.h" #ifndef NO_GPSIM #include "debugmanager.h" #include "docmanager.h" #include "gpsimprocessor.h" #include "textdocument.h" -#include +#include // #include #include //BEGIN class DebugManager // DebugManager * DebugManager::m_pSelf = nullptr; // 2017.10.10 - use K_GLOBAL_STATIC // static K3StaticDeleter staticDebugManagerDeleter; Q_GLOBAL_STATIC( DebugManager, globalDebugManager); DebugManager * DebugManager::self() { return globalDebugManager; } DebugManager::DebugManager() : QObject() { } DebugManager::~DebugManager() { } void DebugManager::registerGpsim( GpsimProcessor * gpsim ) { if (!gpsim) return; m_processors << gpsim; const QStringList files = gpsim->sourceFileList(); QStringList::const_iterator end = files.end(); for ( QStringList::const_iterator it = files.begin(); it != end; ++it ) { const QUrl url = QUrl::fromLocalFile(*it); if (TextDocument * doc = dynamic_cast(DocManager::self()->findDocument(url))) { if ( !doc->debuggerIsRunning() ) doc->setDebugger( gpsim->currentDebugger(), false ); } } } void DebugManager::urlOpened( TextDocument * td ) { if ( td->debuggerIsRunning() ) return; m_processors.removeAll( (GpsimProcessor*)nullptr ); GpsimProcessorList::iterator end = m_processors.end(); for ( GpsimProcessorList::iterator it = m_processors.begin(); it != end; ++it ) { if ( !(*it)->sourceFileList().contains( td->url().path() ) ) continue; (*it)->setDebugMode( (td->guessedCodeType() == TextDocument::ct_asm) ? GpsimDebugger::AsmDebugger : GpsimDebugger::HLLDebugger ); td->setDebugger( (*it)->currentDebugger(), false ); return; } } //END class DebugManager #endif diff --git a/src/debugmanager.h b/src/debugmanager.h index a9dbca7f..45c8d52c 100644 --- a/src/debugmanager.h +++ b/src/debugmanager.h @@ -1,55 +1,55 @@ /*************************************************************************** * Copyright (C) 2005 by David Saxton * * david@bluehaze.org * * * * 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 "config.h" #ifndef NO_GPSIM #ifndef DEBUGMANAGER_H #define DEBUGMANAGER_H -#include -#include -#include +#include +#include +#include class GpsimProcessor; class TextDocument; typedef QList< QPointer > GpsimProcessorList; /** @author David Saxton */ class DebugManager : public QObject { Q_OBJECT public: static DebugManager * self(); ~DebugManager() override; void registerGpsim( GpsimProcessor * gpsim ); /** * Called from TextDocument when it opens a URL so that it can be * connected up to any processors that refer to its url. */ void urlOpened( TextDocument * td ); protected: GpsimProcessorList m_processors; public: DebugManager(); private: // static DebugManager * m_pSelf; }; #endif #endif diff --git a/src/docmanager.cpp b/src/docmanager.cpp index 8a08baf0..5efb7b1b 100644 --- a/src/docmanager.cpp +++ b/src/docmanager.cpp @@ -1,510 +1,510 @@ /*************************************************************************** * Copyright (C) 2005 by David Saxton * * david@bluehaze.org * * * * 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 "electronics/circuitdocument.h" #include "docmanager.h" #include "docmanageriface.h" #include "flowcodedocument.h" #include "iteminterface.h" #include "itemselector.h" #include "ktechlab.h" #include "mechanicsdocument.h" #include "textdocument.h" #include "textview.h" #include "viewcontainer.h" #include #include #include #include #include -#include +#include #include #include DocManager * DocManager::m_pSelf = nullptr; DocManager * DocManager::self() { if ( !m_pSelf ) m_pSelf = new DocManager(); return m_pSelf; } DocManager::DocManager() : QObject( KTechlab::self() ) { p_focusedView = nullptr; m_countCircuit = 0; m_countFlowCode = 0; m_countMechanics = 0; m_countOther = 0; p_connectedDocument = nullptr; m_nextDocumentID = 1; m_pIface = new DocManagerIface(this); } DocManager::~DocManager() { delete m_pIface; } bool DocManager::closeAll() { while ( !m_documentList.isEmpty() ) { Document *document = m_documentList.first(); if ( document->fileClose() ) { m_documentList.removeAll(document); removeDocumentAssociations(document); } else return false; } return true; } void DocManager::gotoTextLine( const QUrl &url, int line ) { TextDocument * doc = dynamic_cast( openURL(url) ); TextView * tv = doc ? doc->textView() : nullptr; if ( !tv ) return; tv->gotoLine(line); tv->setFocus(); } Document* DocManager::openURL( const QUrl &url, ViewArea *viewArea ) { if ( url.isEmpty() ) return nullptr; if ( url.isLocalFile() ) { QFile file(url.toLocalFile()); if ( file.open(QIODevice::ReadOnly) == false ) { KMessageBox::sorry(nullptr, i18n( "Could not open '%1'", file.fileName())); return nullptr; } file.close(); } // If the currently active view area is empty, and we were not given a view area // to open into, then use the empty view area if ( !viewArea ) { ViewContainer * currentVC = static_cast( KTechlab::self()->tabWidget()->currentWidget() ); if ( currentVC ) { ViewArea * va = currentVC->viewArea( currentVC->activeViewArea() ); if ( !va->view() ) viewArea = va; } } // If the document is already open, and a specific view area hasn't been // specified, then just return that document - otherwise, create a new // view in the viewarea Document *document = findDocument(url); if ( document ) { if ( viewArea ) createNewView( document, viewArea ); else giveDocumentFocus( document, viewArea ); return document; } QString fileName = url.fileName(); QString extension = fileName.right( fileName.length() - fileName.lastIndexOf('.') ); if ( extension == ".circuit" ) return openCircuitFile( url, viewArea ); else if ( extension == ".flowcode" ) return openFlowCodeFile( url, viewArea ); else if ( extension == ".mechanics" ) return openMechanicsFile( url, viewArea ); else return openTextFile( url, viewArea ); } Document *DocManager::getFocusedDocument() const { Document * doc = p_focusedView ? p_focusedView->document() : nullptr; return (doc && !doc->isDeleted()) ? doc : nullptr; } void DocManager::giveDocumentFocus( Document * toFocus, ViewArea * viewAreaForNew ) { if ( !toFocus ) return; if ( View * activeView = toFocus->activeView() ) { //KTechlab::self()->tabWidget()->showPage( activeView->viewContainer() ); // 2018.12.01 KTechlab::self()->tabWidget()->setCurrentIndex( KTechlab::self()->tabWidget()->indexOf(activeView->viewContainer()) ); } else if ( viewAreaForNew ) createNewView( toFocus, viewAreaForNew ); } QString DocManager::untitledName( int type ) { QString name; switch(type) { case Document::dt_circuit: { if ( m_countCircuit>1 ) name = i18n("Untitled (Circuit %1)", QString::number(m_countCircuit)); else name = i18n("Untitled (Circuit)"); m_countCircuit++; break; } case Document::dt_flowcode: { if ( m_countFlowCode>1 ) name = i18n("Untitled (FlowCode %1)", QString::number(m_countFlowCode)); else name = i18n("Untitled (FlowCode)"); m_countFlowCode++; break; } case Document::dt_mechanics: { if ( m_countMechanics>1 ) name = i18n("Untitled (Mechanics %1)", QString::number(m_countMechanics)); else name = i18n("Untitled (Mechanics)"); m_countMechanics++; break; } default: { if ( m_countOther>1 ) name = i18n("Untitled (%1)", QString::number(m_countOther)); else name = i18n("Untitled"); m_countOther++; break; } } return name; } Document *DocManager::findDocument( const QUrl &url ) const { // First, look in the associated documents if ( m_associatedDocuments.contains(url) ) return m_associatedDocuments[url]; // Not found, so look in the known documents const DocumentList::const_iterator end = m_documentList.end(); for ( DocumentList::const_iterator it = m_documentList.begin(); it != end; ++it ) { if ( (*it)->url() == url ) return *it; } return nullptr; } void DocManager::associateDocument( const QUrl &url, Document *document ) { if (!document) return; m_associatedDocuments[url] = document; } void DocManager::removeDocumentAssociations( Document *document ) { bool doneErase; do { doneErase = false; const URLDocumentMap::iterator end = m_associatedDocuments.end(); for ( URLDocumentMap::iterator it = m_associatedDocuments.begin(); it != end; ++it ) { if ( it.value() == document ) { doneErase = true; m_associatedDocuments.erase(it); break; } } } while (doneErase); } void DocManager::handleNewDocument( Document *document, ViewArea *viewArea ) { if ( !document || m_documentList.contains(document) ) return; m_documentList.append(document); document->setDCOPID(m_nextDocumentID++); connect( document, SIGNAL(modifiedStateChanged()), KTechlab::self(), SLOT(slotDocModifiedChanged()) ); connect( document, &Document::fileNameChanged, KTechlab::self(), &KTechlab::slotDocModifiedChanged); connect( document, &Document::fileNameChanged, KTechlab::self(), &KTechlab::addRecentFile); connect( document, SIGNAL(destroyed(QObject* )), this, SLOT(documentDestroyed(QObject* )) ); connect( document, SIGNAL(viewFocused(View* )), this, SLOT(slotViewFocused(View* )) ); connect( document, SIGNAL(viewUnfocused()), this, SLOT(slotViewUnfocused()) ); createNewView( document, viewArea ); } View *DocManager::createNewView( Document *document, ViewArea *viewArea ) { if (!document) return nullptr; View *view = nullptr; if (viewArea) view = document->createView( viewArea->viewContainer(), viewArea->id() ); else { ViewContainer *viewContainer = new ViewContainer( document->caption() ); view = document->createView( viewContainer, 0 ); KTechlab::self()->addWindow(viewContainer); } return view; } void DocManager::documentDestroyed( QObject *obj ) { Document *doc = static_cast(obj); m_documentList.removeAll(doc); removeDocumentAssociations(doc); disableContextActions(); } void DocManager::slotViewFocused( View *view ) { ViewContainer * vc = static_cast(KTechlab::self()->tabWidget()->currentWidget()); if (!vc) view = nullptr; if (!view) return; // This function can get called with a view that is not in the current view // container (such as when the user right clicks and then the popup is // destroyed - not too sure why, but this is the easiest way to fix it). if ( view->viewContainer() != vc ) view = vc->activeView(); if ( !view || (View*)p_focusedView == view ) return; if (p_focusedView) slotViewUnfocused(); p_focusedView = view; if ( TextView * textView = dynamic_cast((View*)p_focusedView) ) KTechlab::self()->factory()->addClient( textView->kateView() ); else KTechlab::self()->factory()->addClient( p_focusedView ); Document *document = view->document(); connect( document, SIGNAL(undoRedoStateChanged()), KTechlab::self(), SLOT(slotDocUndoRedoChanged()) ); p_connectedDocument = document; if ( document->type() == Document::dt_circuit || document->type() == Document::dt_flowcode || document->type() == Document::dt_mechanics ) { ItemDocument *cvb = static_cast(view->document()); ItemInterface::self()->slotItemDocumentChanged(cvb); } KTechlab::self()->slotDocUndoRedoChanged(); KTechlab::self()->slotDocModifiedChanged(); KTechlab::self()->requestUpdateCaptions(); } void DocManager::slotViewUnfocused() { if ( !KTechlab::self() ) return; KTechlab::self()->removeGUIClients(); disableContextActions(); if (!p_focusedView) return; if (p_connectedDocument) { disconnect( p_connectedDocument, SIGNAL(undoRedoStateChanged()), KTechlab::self(), SLOT(slotDocUndoRedoChanged()) ); p_connectedDocument = nullptr; } ItemInterface::self()->slotItemDocumentChanged(nullptr); p_focusedView = nullptr; // KTechlab::self()->setCaption( 0 ); KTechlab::self()->requestUpdateCaptions(); } void DocManager::disableContextActions() { KTechlab * ktl = KTechlab::self(); if ( !ktl ) return; ktl->actionByName("file_save")->setEnabled(false); ktl->actionByName("file_save_as")->setEnabled(false); ktl->actionByName("file_close")->setEnabled(false); ktl->actionByName("file_print")->setEnabled(false); ktl->actionByName("edit_undo")->setEnabled(false); ktl->actionByName("edit_redo")->setEnabled(false); ktl->actionByName("edit_cut")->setEnabled(false); ktl->actionByName("edit_copy")->setEnabled(false); ktl->actionByName("edit_paste")->setEnabled(false); ktl->actionByName("view_split_leftright")->setEnabled(false); ktl->actionByName("view_split_topbottom")->setEnabled(false); } TextDocument *DocManager::createTextDocument() { TextDocument *document = TextDocument::constructTextDocument( untitledName(Document::dt_text) ); handleNewDocument(document); return document; } CircuitDocument *DocManager::createCircuitDocument() { CircuitDocument *document = new CircuitDocument( untitledName(Document::dt_circuit) ); handleNewDocument(document); if ( KTLConfig::raiseItemSelectors() ) KTechlab::self()->showToolView( KTechlab::self()->toolView( ComponentSelector::toolViewIdentifier() ) ); return document; } FlowCodeDocument *DocManager::createFlowCodeDocument() { FlowCodeDocument *document = new FlowCodeDocument( untitledName(Document::dt_flowcode) ); handleNewDocument(document); if ( KTLConfig::raiseItemSelectors() ) KTechlab::self()->showToolView( KTechlab::self()->toolView( FlowPartSelector::toolViewIdentifier() ) ); return document; } MechanicsDocument *DocManager::createMechanicsDocument() { MechanicsDocument *document = new MechanicsDocument( untitledName(Document::dt_mechanics) ); handleNewDocument(document); if ( KTLConfig::raiseItemSelectors() ) KTechlab::self()->showToolView( KTechlab::self()->toolView( MechanicsSelector::toolViewIdentifier() ) ); return document; } CircuitDocument *DocManager::openCircuitFile( const QUrl &url, ViewArea *viewArea ) { CircuitDocument *document = new CircuitDocument( url.fileName() ); if ( !document->openURL(url) ) { KMessageBox::sorry(nullptr, i18n("Could not open Circuit file \"%1\"", url.toDisplayString(QUrl::PreferLocalFile))); document->deleteLater(); return nullptr; } handleNewDocument( document, viewArea ); emit fileOpened(url); return document; } FlowCodeDocument *DocManager::openFlowCodeFile( const QUrl &url, ViewArea *viewArea ) { FlowCodeDocument *document = new FlowCodeDocument( url.fileName() ); if ( !document->openURL(url) ) { KMessageBox::sorry(nullptr, i18n("Could not open FlowCode file \"%1\"", url.toDisplayString(QUrl::PreferLocalFile))); document->deleteLater(); return nullptr; } handleNewDocument( document, viewArea ); emit fileOpened(url); return document; } MechanicsDocument *DocManager::openMechanicsFile( const QUrl &url, ViewArea *viewArea ) { MechanicsDocument *document = new MechanicsDocument( url.fileName() ); if ( !document->openURL(url) ) { KMessageBox::sorry(nullptr, i18n("Could not open Mechanics file \"%1\"", url.toDisplayString(QUrl::PreferLocalFile))); document->deleteLater(); return nullptr; } handleNewDocument( document, viewArea ); emit fileOpened(url); return document; } TextDocument *DocManager::openTextFile( const QUrl &url, ViewArea *viewArea ) { TextDocument *document = TextDocument::constructTextDocument( url.fileName() ); if (!document) return nullptr; if ( !document->openURL(url) ) { KMessageBox::sorry(nullptr, i18n("Could not open text file \"%1\"", url.toDisplayString(QUrl::PreferLocalFile))); document->deleteLater(); return nullptr; } handleNewDocument( document, viewArea ); emit fileOpened(url); return document; } diff --git a/src/docmanager.h b/src/docmanager.h index 460d4447..8d0a908c 100644 --- a/src/docmanager.h +++ b/src/docmanager.h @@ -1,170 +1,170 @@ /*************************************************************************** * Copyright (C) 2005 by David Saxton * * david@bluehaze.org * * * * 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. * ***************************************************************************/ #ifndef DOCMANAGER_H #define DOCMANAGER_H #include "view.h" #include -#include +#include class CircuitDocument; class DocManager; class DocManagerIface; class Document; class FlowCodeDocument; class KTechlab; class MechanicsDocument; class TextDocument; class View; class ViewArea; typedef QList DocumentList; typedef QMap URLDocumentMap; /** @author David Saxton */ class DocManager : public QObject { Q_OBJECT friend class KtlTestsAppFixture; public: static DocManager * self(); ~DocManager() override; /** * Attempts to close all open documents, returning true if successful */ bool closeAll(); /** * Goes to the given line in the given text file (if the file exists) */ void gotoTextLine( const QUrl &url, int line ); /** * Attempts to open the document at the given url. * @param viewArea if non-null, will open the new view into the ViewArea */ Document* openURL( const QUrl &url, ViewArea *viewArea = nullptr ); /** * Returns the focused View */ View *getFocusedView() const { return p_focusedView; } /** * Returns the focused Document (the document of the focused view) */ Document *getFocusedDocument() const; /** * Get a unique name, e.g. Untitled (circuit) - n" depending on the types * of Document and whether it is the first one or not * @param type Document::DocumentType - type of Document */ QString untitledName( int type ); /** * Checks to see if a document with the given URL is already open, and * returns a pointer to that Document if so - otherwises returns null * @see associateDocument */ Document *findDocument( const QUrl &url ) const; /** * Associates a url with a pointer to a document. When findFile is called * with the given url, it will return a pointer to this document if it still * exists. * @see findDocument */ void associateDocument( const QUrl &url, Document *document ); /** * Gives the given document focus. If it has no open views, one will be * created for it if viewAreaForNew is non-null */ void giveDocumentFocus( Document * toFocus, ViewArea * viewAreaForNew = nullptr ); void removeDocumentAssociations( Document *document ); void disableContextActions(); public slots: /** * Creates an empty text document (with an open view) */ TextDocument *createTextDocument(); /** * Creates an empty circuit document (with an open view), and shows the * component selector. */ CircuitDocument *createCircuitDocument(); /** * Creates an empty flowcode document (with an open view), and shows the * flowpart selector. */ FlowCodeDocument *createFlowCodeDocument(); /** * Creates an empty mechanics document (with an open view), and shows the * mechanics selector. */ MechanicsDocument *createMechanicsDocument(); signals: /** * Emitted when a file is successfully opened */ void fileOpened( const QUrl &url ); protected slots: /** * Does the appropriate enabling / disabling of actions, connections, etc */ void slotViewFocused( View *view ); /** * Does the appropriate enabling / disabling of actions, connections, etc */ void slotViewUnfocused(); void documentDestroyed( QObject *obj ); protected: /** * This function should be called after creating a new document to add it * to the appropriate lists and connect it up as appropriate */ void handleNewDocument( Document *document, ViewArea *viewArea = nullptr ); /** * Takes the document, creates a new view and shoves it in a new * ViewContainer */ View *createNewView( Document *document, ViewArea *viewArea = nullptr ); CircuitDocument *openCircuitFile( const QUrl &url, ViewArea *viewArea = nullptr ); FlowCodeDocument *openFlowCodeFile( const QUrl &url, ViewArea *viewArea = nullptr ); MechanicsDocument *openMechanicsFile( const QUrl &url, ViewArea *viewArea = nullptr ); TextDocument *openTextFile( const QUrl &url, ViewArea *viewArea = nullptr ); DocumentList m_documentList; URLDocumentMap m_associatedDocuments; // Keeps track of how many // new files have been made // for the purpose of making // titles of the form Untitled (n) int m_countCircuit; int m_countFlowCode; int m_countMechanics; int m_countOther; QPointer p_focusedView; QPointer p_connectedDocument; DocManagerIface *m_pIface; unsigned m_nextDocumentID; private: DocManager(); static DocManager * m_pSelf; }; #endif diff --git a/src/document.h b/src/document.h index 04709f03..4c10648d 100644 --- a/src/document.h +++ b/src/document.h @@ -1,248 +1,248 @@ /*************************************************************************** * Copyright (C) 2005 by David Saxton * * david@bluehaze.org * * * * 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. * ***************************************************************************/ #ifndef DOCUMENT_H #define DOCUMENT_H #include "view.h" #include -#include +#include class DCOPObject; class Document; class DocumentIface; class KTechlab; class View; class ViewContainer; typedef QList > ViewList; /** @author David Saxton */ class Document : public QObject { Q_OBJECT public: enum DocumentType { dt_none, // Used to denote document type not known / specified / etc, when appropriate dt_flowcode, dt_circuit, dt_mechanics, dt_text, dt_pinMapEditor }; Document( const QString &caption, const char *name = nullptr ); ~Document() override; /** * If the user has created a new document from the new file dialog, and * wants to add it to the project, then this must wait until this file is * given a url. Set this to true to add the file to the active project when * it is first saved. */ void setAddToProjectOnSave( bool add ) { m_bAddToProjectOnSave = add; } /** * Caption of document, e.g. "Untitled 2" */ QString caption() const { return m_caption; } /** * Set the caption of the document, to be displayed in the tab bar when * active */ void setCaption( const QString &caption ); /** * Return the dcop object for this document */ DCOPObject * dcopObject() const; /** * Returns the dcop suffix for this document - a unique ID for the current * app session. DCOP name will be "Document#dcopID()" */ unsigned dcopID() const { return m_dcopID; } /** * Sets the dcop suffix. The DCOP object for this document will be renamed. * @see dcopID */ void setDCOPID( unsigned id ); /** * Returns the active view, which is the last view to be used to edit in */ View *activeView() const { return m_pActiveView; } ViewList viewList() const { return m_viewList; } /** * Returns the type of document. * @see Document::DocumentType */ DocumentType type() const { return m_type; } /** * Returns the number of open views. */ uint numberOfViews() const { return m_viewList.size(); } /** * Create a view that will display the document data. In all reimplemented * functions, you must call handleNewView after creating the view, so that * the appropriate slots, pointers, etc can all be initialised. */ virtual View *createView( ViewContainer *viewContainer, uint viewAreaId, const char *name = nullptr ) = 0; /** * Returns the url of the file that the Document refers to */ const QUrl& url() const { return m_url; } /** * Prompts the user for a url, with the given types for the filter. * If user accepts, returns true, and set the url to the new url. */ bool getURL( const QString &types ); /** * Attempts to open a url, and returns true if succesful. * You must reinherit this function. */ virtual bool openURL( const QUrl &url ) = 0; /** * Sets the url of the file that this Document refers to */ void setURL( const QUrl &url ); /** * Sets whether the file is modified or not. Will emit modifiedStateChanged * if state changes. You must emit this signal if you reinherit this */ virtual void setModified( bool modified ); /** * Returns the modification state since last-save. */ virtual bool isModified() const { return b_modified; } /** * Returns true if undo is avilable. */ virtual bool isUndoAvailable() const { return false; } /** * Returns true if redo is avilable. */ virtual bool isRedoAvailable() const { return false; } /** * Saves the file to a new name. */ virtual void fileSaveAs() = 0; /** * Attempts to close the file without saving, prompting the user if the * file has been modified. If succesful, calls QObject::deleteLater(), and * returns true (otherwise returns false). */ virtual bool fileClose(); /** * Saves the file. */ virtual void fileSave() = 0; /** * Prints the file. */ virtual void print() {}; /** * Cuts whatever is selected. */ virtual void cut() {}; /** * Copies whatever is selected. */ virtual void copy() {}; /** * Attempts to paste whatever is in the clipboard. */ virtual void paste() {}; /** * Undo the last operation. You should reinherit this function. */ virtual void undo() {}; /** * Redo the undone last operation. You should reinherit this function. */ virtual void redo() {}; /** * Selects everything in the view. */ virtual void selectAll() {}; virtual void convertToMicrobe() {}; virtual void convertToHex() {}; virtual void convertToPIC() {}; virtual void convertToAssembly() {}; virtual void debugRun() {}; virtual void debugInterrupt() {}; virtual void debugStop() {}; virtual void debugStep() {}; bool isDeleted() const { return m_bDeleted; } protected slots: /** * Called when the user changes the configuration. */ virtual void slotUpdateConfiguration() {}; #define protected public signals: /** * Emitted when an operation has been performed that * has caused the stack of available undo/redo operations to * have changed */ void undoRedoStateChanged(); #undef protected signals: /** * Emitted when the Document goes from modified to unmodified, * or vice-versa */ void modifiedStateChanged(); /** * Emitted when the name of the file that the Document refers to * is changed. */ void fileNameChanged( const QUrl &url ); void viewFocused( View *view ); void viewUnfocused(); private slots: void slotViewDestroyed( QObject *obj ); void slotViewFocused( View *view ); protected: /** * You must call this function after creating a new view */ virtual void handleNewView( View *view ); bool b_modified; // TODO: refactor this out. DocumentType m_type; // XXXX ViewList m_viewList; DocumentIface *m_pDocumentIface; // Set to true by the document et subclasses destructors, used to avoid // doing stuff that might lead to crash when being deleted. bool m_bDeleted; private: QUrl m_url; QPointer m_pActiveView; QString m_caption; bool m_bAddToProjectOnSave; unsigned m_dcopID; unsigned m_nextViewID; }; #endif diff --git a/src/documentiface.h b/src/documentiface.h index 2d50c429..8dc65531 100644 --- a/src/documentiface.h +++ b/src/documentiface.h @@ -1,199 +1,199 @@ /*************************************************************************** * Copyright (C) 2005 by David Saxton * * david@bluehaze.org * * * * 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. * ***************************************************************************/ #ifndef DOCUMENTIFACE_H #define DOCUMENTIFACE_H #include "config.h" //#include //#include #include "dcop_stub.h" -#include +#include class CircuitDocument; class Document; class FlowCodeDocument; class ICNDocument; class ItemDocument; class MechanicsDocument; class TextDocument; class View; /** @author David Saxton */ class DocumentIface : public DCOPObject // TODO port to dbus { K_DCOP // TODO port to dbus public: DocumentIface( Document * document ); virtual ~DocumentIface(); k_dcop: // TODO port to dbus QString caption() const; DCOPRef activeView(); uint numberOfViews(); // View *createView( ViewContainer *viewContainer, uint viewAreaId, const char *name = nullptr ); QString url(); bool openURL( const QString & url ); bool isModified(); bool isUndoAvailable(); bool isRedoAvailable(); void save(); void saveAs(); bool close(); void print(); void cut(); void copy(); void paste(); void undo(); void redo(); void selectAll(); protected: DCOPRef viewToRef( View * view ); Document * m_pDocument; }; class TextDocumentIface : public DocumentIface { // K_DCOP TODO port to dbus public: TextDocumentIface( TextDocument * document ); //k_dcop: TODO port to dbus void formatAssembly(); void convertToMicrobe(); void convertToHex(); void convertToPIC(); void convertToAssembly(); void clearBookmarks(); bool isDebugging(); void debugRun(); void debugInterrupt(); void debugStop(); void debugStep(); void debugStepOver(); void debugStepOut(); protected: TextDocument * m_pTextDocument; }; class ItemDocumentIface : public DocumentIface { // K_DCOP TODO port to dbus public: ItemDocumentIface( ItemDocument * document ); // k_dcop: TODO port to dbus QStringList validItemIDs(); /** * Create an item with the given id (e.g. "ec/resistor") at the given * position. * @return name of item (assigned to it by KTechlab) */ QString addItem( const QString & id, int x, int y ); void selectItem( const QString & id ); void unselectItem( const QString & id ); void clearHistory(); void unselectAll(); void alignHorizontally(); void alignVertically(); void distributeHorizontally(); void distributeVertically(); void deleteSelection(); protected: ItemDocument * m_pItemDocument; }; class MechanicsDocumentIface : public ItemDocumentIface { // K_DCOP TODO port to dbus public: MechanicsDocumentIface( MechanicsDocument * document ); protected: MechanicsDocument * m_pMechanicsDocument; }; class ICNDocumentIface : public ItemDocumentIface { // K_DCOP TODO port to dbus public: ICNDocumentIface( ICNDocument * document ); // k_dcop: TODO port to dbus void exportToImage(); QStringList nodeIDs( const QString & id ); /** * Makes a connection from node1 on item1 to node2 on item2 */ QString makeConnection( const QString & item1, const QString & node1, const QString & item2, const QString & node2 ); void selectConnector( const QString & id ); void unselectConnector( const QString & id ); protected: ICNDocument * m_pICNDocument; }; // FIXME: move to separate file and put in same path as circuitdocument.* class CircuitDocumentIface : public ICNDocumentIface { // K_DCOP TODO port to dbus public: CircuitDocumentIface( CircuitDocument * document ); //k_dcop: TODO port to dbus void setOrientation0(); void setOrientation90(); void setOrientation180(); void setOrientation270(); void rotateCounterClockwise(); void rotateClockwise(); void flipHorizontally(); void flipVertically(); void displayEquations(); void createSubcircuit(); protected: CircuitDocument * m_pCircuitDocument; }; class FlowCodeDocumentIface : public ICNDocumentIface { // K_DCOP TODO port to dbus public: FlowCodeDocumentIface( FlowCodeDocument * document ); void convertToMicrobe(); void convertToHex(); void convertToPIC(); void convertToAssembly(); // k_dcop: TODO port to dbus void setPicType( const QString & id ); protected: FlowCodeDocument * m_pFlowCodeDocument; }; #endif diff --git a/src/drawparts/dpimage.cpp b/src/drawparts/dpimage.cpp index 00165179..88e1bc0b 100644 --- a/src/drawparts/dpimage.cpp +++ b/src/drawparts/dpimage.cpp @@ -1,269 +1,269 @@ /*************************************************************************** * Copyright (C) 2006 by David Saxton * * david@bluehaze.org * * * * 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 "dpimage.h" #include "itemdocument.h" #include "libraryitem.h" #include "resizeoverlay.h" -#include #include #include -#include -#include +#include +#include +#include //BEGIN class ImageScaleThread ImageScaleThread::ImageScaleThread() { // Start with a blank (grey) image QPixmap pm( 1, 1 ); pm.fill( Qt::gray ); m_image = pm.toImage(); m_width = -1; m_height = -1; m_bDoneNormalScale = false; m_bDoneSmoothScale = false; m_bSettingsChanged = false; } bool ImageScaleThread::updateSettings( const QString & imageURL, int width, int height ) { if ( isRunning() ) { qWarning() << Q_FUNC_INFO << "Cannot update settings while running.\n"; return false; } bool changed = false; if ( m_width != width ) { m_width = width; changed = true; } if ( m_height != height ) { m_height = height; changed = true; } if ( m_imageURL != imageURL ) { m_imageURL = imageURL; m_image.load( m_imageURL ); if ( m_image.isNull() ) { QPixmap pm( 1, 1 ); pm.fill( Qt::gray ); m_image = pm.toImage(); } changed = true; } if ( changed ) { m_bSettingsChanged = true; m_bDoneNormalScale = false; m_bDoneSmoothScale = false; } return changed; } QImage ImageScaleThread::bestScaling( BestScaling * scaling ) const { BestScaling temp; if ( !scaling ) scaling = & temp; if ( m_bDoneSmoothScale ) { *scaling = SmoothScaled; return m_smoothScaled; } else if ( m_bDoneNormalScale ) { *scaling = NormalScaled; return m_normalScaled; } else { *scaling = Unscaled; return m_image; } } void ImageScaleThread::run() { do { m_bSettingsChanged = false; if ( !m_bDoneNormalScale ) { m_normalScaled = m_image.scaled( m_width, m_height ); m_bDoneNormalScale = true; } } while ( m_bSettingsChanged ); // If m_bSettingsChanged is true, then another thread called updateSettings // while we were doing normal scaling, so don't both doing smooth scaling // just yet. if ( !m_bDoneSmoothScale ) { //m_smoothScaled = m_image.smoothScale( m_width, m_height ); // 2018.12.01 m_smoothScaled = m_image.scaled( QSize( m_width, m_height ), Qt::IgnoreAspectRatio, Qt::SmoothTransformation ); m_bDoneSmoothScale = true; } } //END class ImageScaleThread //BEGIN class DPImage Item* DPImage::construct( ItemDocument *itemDocument, bool newItem, const char *id ) { return new DPImage( itemDocument, newItem, id ); } LibraryItem* DPImage::libraryItem() { return new LibraryItem( QStringList(QString("dp/image")), i18n("Image"), i18n("Other"), KIconLoader::global()->loadIcon( "text", KIconLoader::Small ), LibraryItem::lit_drawpart, DPImage::construct ); } DPImage::DPImage( ItemDocument *itemDocument, bool newItem, const char *id ) : DrawPart( itemDocument, newItem, id ? id : "image" ) { m_bSettingsChanged = false; m_bResizeToImage = newItem; m_imageScaling = ImageScaleThread::Unscaled; m_pRectangularOverlay = new RectangularOverlay( this ); m_pCheckImageScalingTimer = new QTimer( this ); connect( m_pCheckImageScalingTimer, SIGNAL(timeout()), SLOT(checkImageScaling()) ); m_pCheckImageScalingTimer->start( 100 ); m_name = i18n("Image"); Variant * v = createProperty( "image", Variant::Type::FileName ); v->setCaption( i18n("Image File") ); dataChanged(); } DPImage::~DPImage() { m_imageScaleThread.wait(); } void DPImage::setSelected( bool yes ) { if ( yes == isSelected() ) return; DrawPart::setSelected(yes); m_pRectangularOverlay->showResizeHandles(yes); } void DPImage::postResize() { setItemPoints( QPolygon(m_sizeRect), false ); m_bSettingsChanged = true; } void DPImage::dataChanged() { m_imageURL = dataString( "image" ); m_image.load( m_imageURL ); if ( m_image.isNull() ) { // Make a grey image //m_image.resize( width(), height() ); // 2018.12.01 m_image = m_image.copy( 0, 0, width(), height() ); m_image.fill( Qt::gray ); m_imageScaling = ImageScaleThread::SmoothScaled; } else { if ( m_bResizeToImage ) { int w = m_image.width(); int h = m_image.height(); setSize( 0, 0, w, h ); m_imageScaling = ImageScaleThread::SmoothScaled; } else { m_bResizeToImage = true; m_bSettingsChanged = true; } } } void DPImage::checkImageScaling() { if ( !m_bSettingsChanged && (m_imageScaling == ImageScaleThread::SmoothScaled) ) { // Image scaling is already at its best, so return return; } ImageScaleThread::BestScaling bs; QImage im = m_imageScaleThread.bestScaling( & bs ); if ( bs > m_imageScaling ) { m_imageScaling = bs; m_image = QPixmap::fromImage(im); setChanged(); } if ( !m_imageScaleThread.isRunning() ) { if ( m_imageScaleThread.updateSettings( m_imageURL, width(), height() ) ) { m_bSettingsChanged = false; m_imageScaling = ImageScaleThread::Unscaled; m_imageScaleThread.start(); } } } void DPImage::drawShape( QPainter & p ) { p.drawPixmap( int(x()+offsetX()), int(y()+offsetY()), m_image, 0, 0, width(), height() ); } //END class DPImage diff --git a/src/drawparts/dpimage.h b/src/drawparts/dpimage.h index 2d13e9cb..36c14a74 100644 --- a/src/drawparts/dpimage.h +++ b/src/drawparts/dpimage.h @@ -1,107 +1,107 @@ /*************************************************************************** * Copyright (C) 2006 by David Saxton * * david@bluehaze.org * * * * 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. * ***************************************************************************/ #ifndef DPIMAGE_H #define DPIMAGE_H #include "drawpart.h" -#include -#include -#include +#include +#include +#include /** @short Thread to perform quick and then good image scaling. @author David Saxton */ class ImageScaleThread : public QThread { public: enum BestScaling { Unscaled, NormalScaled, SmoothScaled }; ImageScaleThread(); /** * Use the given settings. * @return if any of the settings changed. */ bool updateSettings( const QString & imageURL, int width, int height ); /** * @param scaling is set to the type of scaling that this image has had. * @return the best image done so far. */ QImage bestScaling( BestScaling * scaling = nullptr ) const; protected: /** * Start scaling. */ void run() override; QImage m_image; QImage m_normalScaled; QImage m_smoothScaled; bool m_bDoneNormalScale; bool m_bDoneSmoothScale; int m_width; int m_height; QString m_imageURL; bool m_bSettingsChanged; }; /** @short Represents editable text on the canvas @author David Saxton */ class DPImage : public DrawPart { Q_OBJECT public: DPImage( ItemDocument *itemDocument, bool newItem, const char *id = nullptr ); ~DPImage() override; static Item* construct( ItemDocument *itemDocument, bool newItem, const char *id ); static LibraryItem *libraryItem(); void setSelected( bool yes ) override; protected: void postResize() override; protected slots: /** * Called from a timeout event after resizing to see if the image * resizing thread has done anything useful yet. */ void checkImageScaling(); private: void drawShape( QPainter &p ) override; void dataChanged() override; ImageScaleThread::BestScaling m_imageScaling; QPixmap m_image; ImageScaleThread m_imageScaleThread; RectangularOverlay * m_pRectangularOverlay; QTimer * m_pCheckImageScalingTimer; QString m_imageURL; bool m_bSettingsChanged; /** * If we have been loaded from a file, etc, then we want to keep the * previous size instead of resizing ourselves to the new image size * like we would do normally if the user loads an image. */ bool m_bResizeToImage; }; #endif diff --git a/src/drawparts/dpline.cpp b/src/drawparts/dpline.cpp index e56dd8e2..61676ffc 100644 --- a/src/drawparts/dpline.cpp +++ b/src/drawparts/dpline.cpp @@ -1,244 +1,244 @@ /*************************************************************************** * Copyright (C) 2005,2006 by David Saxton * * david@bluehaze.org * * * * 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 "dpline.h" #include "libraryitem.h" #include "resizeoverlay.h" #include "variant.h" #include #include #include #include -#include +#include //BEGIN class DPLine Item* DPLine::construct( ItemDocument *itemDocument, bool newItem, const char *id ) { return new DPLine( itemDocument, newItem, id ); } LibraryItem* DPLine::libraryItem() { return new LibraryItem( QStringList(QString("dp/line")), i18n("Line"), i18n("Other"), KIconLoader::global()->loadIcon( "text", KIconLoader::Small ), LibraryItem::lit_drawpart, DPLine::construct ); } DPLine::DPLine( ItemDocument *itemDocument, bool newItem, const char *id ) : DrawPart( itemDocument, newItem, id ? id : "line" ) { m_pLineOverlay = new LineOverlay(this); m_name = i18n("Line"); createProperty( "line-color", Variant::Type::Color ); property("line-color")->setCaption( i18n("Line Color") ); property("line-color")->setValue(QColor(Qt::black)); createProperty( "line-width", Variant::Type::Int ); property("line-width")->setCaption( i18n("Line Width") ); property("line-width")->setMinValue(1); property("line-width")->setMaxValue(1000); property("line-width")->setValue(1); createProperty( "line-style", Variant::Type::PenStyle ); property("line-style")->setCaption( i18n("Line Style") ); property("line-style")->setAdvanced(true); setDataPenStyle( "line-style", Qt::SolidLine ); createProperty( "cap-style", Variant::Type::PenCapStyle ); property("cap-style")->setCaption( i18n("Cap Style") ); property("cap-style")->setAdvanced(true); setDataPenCapStyle( "cap-style", Qt::FlatCap ); } DPLine::~DPLine() { } void DPLine::setSelected( bool yes ) { if ( yes == isSelected() ) return; DrawPart::setSelected(yes); m_pLineOverlay->showResizeHandles(yes); } void DPLine::dataChanged() { setPen( QPen( dataColor("line-color"), unsigned( dataInt("line-width") ), getDataPenStyle("line-style"), getDataPenCapStyle("cap-style"), Qt::MiterJoin ) ); postResize(); // in case the pen width has changed update(); } void DPLine::postResize() { int x1 = offsetX(); int y1 = offsetY(); int x2 = x1+width(); int y2 = y1+height(); QPolygon p(4); int pw = pen().width(); int dx = abs(x1-x2); int dy = abs(y1-y2); pw = pw * 4 / 3 + 2; // approx pw*sqrt(2) int px = x1 dy ? (dx*2/dy <= 2) : (dy*2/dx <= 2)) ) { // steep if ( px == py ) { p[0] = QPoint(x1 ,y1+py); p[1] = QPoint(x2-px,y2 ); p[2] = QPoint(x2 ,y2-py); p[3] = QPoint(x1+px,y1 ); } else { p[0] = QPoint(x1+px,y1 ); p[1] = QPoint(x2 ,y2-py); p[2] = QPoint(x2-px,y2 ); p[3] = QPoint(x1 ,y1+py); } } else if ( dx > dy ) { // horizontal p[0] = QPoint(x1+px,y1+py); p[1] = QPoint(x2-px,y2+py); p[2] = QPoint(x2-px,y2-py); p[3] = QPoint(x1+px,y1-py); } else { // vertical p[0] = QPoint(x1+px,y1+py); p[1] = QPoint(x2+px,y2-py); p[2] = QPoint(x2-px,y2-py); p[3] = QPoint(x1-px,y1+py); } setItemPoints( p, false ); } void DPLine::drawShape( QPainter & p ) { int x1 = int(x()+offsetX()); int y1 = int(y()+offsetY()); int x2 = x1+width(); int y2 = y1+height(); p.drawLine( x1, y1, x2, y2 ); } //END class DPLine //BEGIN class DPArrow Item* DPArrow::construct( ItemDocument *itemDocument, bool newItem, const char *id ) { return new DPArrow( itemDocument, newItem, id ); } LibraryItem* DPArrow::libraryItem() { return new LibraryItem( QStringList(QString("dp/arrow")), i18n("Arrow"), i18n("Other"), KIconLoader::global()->loadIcon( "text", KIconLoader::Small ), LibraryItem::lit_drawpart, DPArrow::construct ); } DPArrow::DPArrow( ItemDocument *itemDocument, bool newItem, const char *id ) : DPLine( itemDocument, newItem, id ? id : "arrow" ) { m_name = i18n("Arrow"); // We don't want to use the square cap style as it screws up drawing our arrow head QStringList allowed = property("cap-style")->allowed(); allowed.removeAll( DrawPart::penCapStyleToName( Qt::SquareCap ) ); property("cap-style")->setAllowed(allowed); m_headAngle = 20.0; Variant * v = createProperty( "HeadAngle", Variant::Type::Double ); v->setAdvanced( true ); v->setCaption( i18n("Head angle") ); v->setMinValue( 10.0 ); v->setMaxValue( 60.0 ); v->setUnit( QChar(0xb0) ); v->setValue( m_headAngle ); } DPArrow::~DPArrow() { } void DPArrow::dataChanged() { DPLine::dataChanged(); m_headAngle = dataDouble( "HeadAngle" ); setChanged(); } inline int round_x( double x ) { return int(x+((x > 0) ? 0.5 : -0.5)); } void DPArrow::drawShape( QPainter & p ) { double x1 = x()+offsetX(); double y1 = y()+offsetY(); double x2 = x1+width(); double y2 = y1+height(); p.drawLine( int(x1), int(y1), int(x2), int(y2) ); double dx = x2-x1; double dy = y2-y1; if ( dx == 0. && dy == 0. ) return; double arrow_angle = ( dx == 0 ? (dy>0?(M_PI_2):(-M_PI_2)) : std::atan(dy/dx) ); if ( dx < 0 ) arrow_angle += M_PI; double head_angle = M_PI * m_headAngle / 180.0; double head_length = 10.0; // Position of arrowhead double x3 = x2 + head_length*std::cos( M_PI + arrow_angle - head_angle ); double y3 = y2 + head_length*std::sin( M_PI + arrow_angle - head_angle ); double x4 = x2 + head_length*std::cos( M_PI + arrow_angle + head_angle ); double y4 = y2 + head_length*std::sin( M_PI + arrow_angle + head_angle ); // Draw arrowhead QPen pen = p.pen(); pen.setCapStyle( Qt::RoundCap ); p.setPen(pen); p.setBrush(pen.color()); QPolygon pa(3); pa[0] = QPoint( round_x(x2), round_x(y2) ); pa[1] = QPoint( round_x(x3), round_x(y3) ); pa[2] = QPoint( round_x(x4), round_x(y4) ); p.drawPolygon(pa); p.drawPolyline(pa); } //END class DPLine diff --git a/src/drawparts/dptext.cpp b/src/drawparts/dptext.cpp index d7186ade..fa3ef3a7 100644 --- a/src/drawparts/dptext.cpp +++ b/src/drawparts/dptext.cpp @@ -1,144 +1,144 @@ /*************************************************************************** * Copyright (C) 2003-2005 by David Saxton * * david@bluehaze.org * * * * 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 "dptext.h" #include "itemdocument.h" #include "libraryitem.h" #include "resizeoverlay.h" #include #include -#include -#include +#include +#include // #include // 2018.08.13 - not needed anymore // #include Item* DPText::construct( ItemDocument *itemDocument, bool newItem, const char *id ) { return new DPText( itemDocument, newItem, id ); } LibraryItem* DPText::libraryItem() { QStringList idList; idList << "dp/text" << "dp/canvas_text" << "canvas_text"; return new LibraryItem( idList, i18n("Canvas Text"), i18n("Other"), KIconLoader::global()->loadIcon( "text", KIconLoader::Small ), LibraryItem::lit_drawpart, DPText::construct ); } DPText::DPText( ItemDocument *itemDocument, bool newItem, const char *id ) : DrawPart( itemDocument, newItem, id ? id : "canvas_text" ) { m_rectangularOverlay = new RectangularOverlay(this); m_name = i18n("Text"); createProperty( "text", Variant::Type::RichText ); property("text")->setValue( i18n("Text") ); createProperty( "background", Variant::Type::Bool ); property("background")->setValue(false); property("background")->setCaption( i18n("Display Background") ); property("background")->setAdvanced(true); createProperty( "background-color", Variant::Type::Color ); property("background-color")->setValue(QColor(Qt::white)); property("background-color")->setCaption( i18n("Background Color") ); property("background-color")->setAdvanced(true); createProperty( "frame-color", Variant::Type::Color ); property("frame-color")->setValue(QColor(Qt::black)); property("frame-color")->setCaption( i18n("Frame Color") ); property("frame-color")->setAdvanced(true); } DPText::~DPText() { } void DPText::setSelected( bool yes ) { if ( yes == isSelected() ) return; DrawPart::setSelected(yes); m_rectangularOverlay->showResizeHandles(yes); } void DPText::dataChanged() { b_displayBackground = dataBool("background"); m_backgroundColor = dataColor("background-color"); m_frameColor = dataColor("frame-color"); m_text = dataString("text"); if ( !Qt::mightBeRichText( m_text ) ) { // Format the text to be HTML m_text.replace( '\n', "
" ); } update(); } void DPText::postResize() { setItemPoints( QPolygon(m_sizeRect), false ); } QSize DPText::minimumSize() const { return QSize( 48, 24 ); } void DPText::drawShape( QPainter &p ) { QRect bound = m_sizeRect; bound.setWidth( bound.width()-2 ); bound.setHeight( bound.height()-2 ); bound.translate( int(x()+1), int(y()+1) ); if (b_displayBackground) { p.save(); p.setPen( QPen( m_frameColor, 1, Qt::DotLine) ); p.setBrush(m_backgroundColor); p.drawRect(bound); p.restore(); } const int pad = 6; bound.setLeft( bound.left()+pad ); bound.setTop( bound.top() ); bound.setRight( bound.right()-pad ); bound.setBottom( bound.bottom()-pad ); QTextEdit * t = new QTextEdit( m_text ); t->resize( bound.width(), bound.height() ); // t->setWidth( bound.width() ); t->viewport()->setAutoFillBackground( false ); t->setFrameStyle(QFrame::NoFrame); t->render( &p, bound.topLeft(), QRegion(), QWidget::DrawChildren ); //t->draw( &p, bound.left(), bound.top(), bound, QColorGroup() ); delete t; } diff --git a/src/drawparts/drawpart.cpp b/src/drawparts/drawpart.cpp index 998e0c69..98338796 100644 --- a/src/drawparts/drawpart.cpp +++ b/src/drawparts/drawpart.cpp @@ -1,260 +1,260 @@ /*************************************************************************** * Copyright (C) 2005 by David Saxton * * david@bluehaze.org * * * * 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 "itemdocument.h" #include "itemdocumentdata.h" #include "drawpart.h" #include "variant.h" #include -#include +#include DrawPart::DrawPart( ItemDocument *itemDocument, bool newItem, const char *id ) : Item( itemDocument, newItem, id ) { if ( itemDocument ) itemDocument->registerItem(this); } DrawPart::~DrawPart() { } Variant * DrawPart::createProperty( const QString & id, Variant::Type::Value type ) { if ( type == Variant::Type::PenStyle ) { QStringList penStyles; penStyles << DrawPart::penStyleToName(Qt::SolidLine) << DrawPart::penStyleToName(Qt::DashLine) << DrawPart::penStyleToName(Qt::DotLine) << DrawPart::penStyleToName(Qt::DashDotLine) << DrawPart::penStyleToName(Qt::DashDotDotLine); Variant * v = createProperty( id, Variant::Type::String ); v->setType( Variant::Type::PenStyle ); v->setAllowed(penStyles); return v; } if ( type == Variant::Type::PenCapStyle ) { QStringList penCapStyles; penCapStyles << DrawPart::penCapStyleToName(Qt::FlatCap) << DrawPart::penCapStyleToName(Qt::SquareCap) << DrawPart::penCapStyleToName(Qt::RoundCap); Variant * v = createProperty( id, Variant::Type::String ); v->setType( Variant::Type::PenCapStyle ); v->setAllowed(penCapStyles); return v; } return Item::createProperty( id, type ); } Qt::PenStyle DrawPart::getDataPenStyle( const QString & id ) { return nameToPenStyle( dataString(id) ); } Qt::PenCapStyle DrawPart::getDataPenCapStyle( const QString & id ) { return nameToPenCapStyle( dataString(id) ); } void DrawPart::setDataPenStyle( const QString & id, Qt::PenStyle value ) { property(id)->setValue( penStyleToName(value) ); } void DrawPart::setDataPenCapStyle( const QString & id, Qt::PenCapStyle value ) { property(id)->setValue( penCapStyleToName(value) ); } ItemData DrawPart::itemData() const { ItemData itemData = Item::itemData(); const VariantDataMap::const_iterator end = m_variantData.end(); for ( VariantDataMap::const_iterator it = m_variantData.begin(); it != end; ++it ) { switch( it.value()->type() ) { case Variant::Type::PenStyle: itemData.dataString[it.key()] = penStyleToID( nameToPenStyle( it.value()->value().toString() ) ); break; case Variant::Type::PenCapStyle: itemData.dataString[it.key()] = penCapStyleToID( nameToPenCapStyle( it.value()->value().toString() ) ); break; case Variant::Type::String: case Variant::Type::FileName: case Variant::Type::Port: case Variant::Type::Pin: case Variant::Type::VarName: case Variant::Type::Combo: case Variant::Type::Select: case Variant::Type::Multiline: case Variant::Type::RichText: case Variant::Type::Int: case Variant::Type::Double: case Variant::Type::Color: case Variant::Type::Bool: case Variant::Type::Raw: case Variant::Type::SevenSegment: case Variant::Type::KeyPad: case Variant::Type::None: // All of these are handled by Item break; } } return itemData; } void DrawPart::restoreFromItemData( const ItemData &itemData ) { Item::restoreFromItemData(itemData); const QStringMap::const_iterator stringEnd = itemData.dataString.end(); for ( QStringMap::const_iterator it = itemData.dataString.begin(); it != stringEnd; ++it ) { VariantDataMap::iterator vit = m_variantData.find(it.key()); if ( vit == m_variantData.end() ) continue; if ( vit.value()->type() == Variant::Type::PenStyle ) setDataPenStyle( it.key(), idToPenStyle( it.value() ) ); else if ( vit.value()->type() == Variant::Type::PenCapStyle ) setDataPenCapStyle( it.key(), idToPenCapStyle( it.value() ) ); } } QString DrawPart::penStyleToID( Qt::PenStyle style ) { switch (style) { case Qt::SolidLine: return "SolidLine"; case Qt::NoPen: return "NoPen"; case Qt::DashLine: return "DashLine"; case Qt::DotLine: return "DotLine"; case Qt::DashDotLine: return "DashDotLine"; case Qt::DashDotDotLine: return "DashDotDotLine"; case Qt::MPenStyle: default: return ""; // ?! } } Qt::PenStyle DrawPart::idToPenStyle( const QString & id ) { if ( id == "NoPen" ) return Qt::NoPen; if ( id == "DashLine" ) return Qt::DashLine; if ( id == "DotLine" ) return Qt::DotLine; if ( id == "DashDotLine" ) return Qt::DashDotLine; if ( id == "DashDotDotLine" ) return Qt::DashDotDotLine; return Qt::SolidLine; } QString DrawPart::penCapStyleToID( Qt::PenCapStyle style ) { switch (style) { case Qt::FlatCap: return "FlatCap"; case Qt::SquareCap: return "SquareCap"; case Qt::RoundCap: return "RoundCap"; case Qt::MPenCapStyle: default: return ""; // ?! } } Qt::PenCapStyle DrawPart::idToPenCapStyle( const QString & id ) { if ( id == "SquareCap" ) return Qt::SquareCap; if ( id == "RoundCap" ) return Qt::RoundCap; return Qt::FlatCap; } QString DrawPart::penStyleToName( Qt::PenStyle style ) { switch (style) { case Qt::SolidLine: return i18n("Solid"); case Qt::NoPen: return i18n("None"); case Qt::DashLine: return i18n("Dash"); case Qt::DotLine: return i18n("Dot"); case Qt::DashDotLine: return i18n("Dash Dot"); case Qt::DashDotDotLine: return i18n("Dash Dot Dot"); case Qt::MPenStyle: default: return ""; // ?! } } Qt::PenStyle DrawPart::nameToPenStyle( const QString & name ) { if ( name == i18n("None") ) return Qt::NoPen; if ( name == i18n("Dash") ) return Qt::DashLine; if ( name == i18n("Dot") ) return Qt::DotLine; if ( name == i18n("Dash Dot") ) return Qt::DashDotLine; if ( name == i18n("Dash Dot Dot") ) return Qt::DashDotDotLine; return Qt::SolidLine; } QString DrawPart::penCapStyleToName( Qt::PenCapStyle style ) { switch (style) { case Qt::FlatCap: return i18n("Flat"); case Qt::SquareCap: return i18n("Square"); case Qt::RoundCap: return i18n("Round"); case Qt::MPenCapStyle: default: return ""; // ?! } } Qt::PenCapStyle DrawPart::nameToPenCapStyle( const QString & name ) { if ( name == i18n("Square") ) return Qt::SquareCap; if ( name == i18n("Round") ) return Qt::RoundCap; return Qt::FlatCap; } diff --git a/src/drawparts/solidshape.cpp b/src/drawparts/solidshape.cpp index 98c58e87..00e89cfa 100644 --- a/src/drawparts/solidshape.cpp +++ b/src/drawparts/solidshape.cpp @@ -1,193 +1,193 @@ /*************************************************************************** * Copyright (C) 2005 by David Saxton * * david@bluehaze.org * * * * 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 "solidshape.h" #include "libraryitem.h" #include "resizeoverlay.h" #include #include #include -#include +#include //BEGIN class DPRectangle Item * DPRectangle::construct( ItemDocument *itemDocument, bool newItem, const char *id ) { return new DPRectangle( itemDocument, newItem, id ); } LibraryItem* DPRectangle::libraryItem() { return new LibraryItem( QStringList(QString("dp/rectangle")), i18n("Rectangle"), i18n("Other"), KIconLoader::global()->loadIcon( "text", KIconLoader::Small ), LibraryItem::lit_drawpart, DPRectangle::construct ); } DPRectangle::DPRectangle( ItemDocument *itemDocument, bool newItem, const char *id ) : DrawPart( itemDocument, newItem, id ? id : "rectangle" ) { m_pRectangularOverlay = new RectangularOverlay(this); m_name = i18n("Rectangle"); createProperty( "background", Variant::Type::Bool ); property("background")->setValue(false); property("background")->setCaption( i18n("Display Background") ); property("background")->setAdvanced(true); createProperty( "background-color", Variant::Type::Color ); property("background-color")->setValue(QColor(Qt::white)); property("background-color")->setCaption( i18n("Background Color") ); property("background-color")->setAdvanced(true); createProperty( "line-color", Variant::Type::Color ); property("line-color")->setValue(QColor(Qt::black)); property("line-color")->setCaption( i18n("Line Color") ); property("line-color")->setAdvanced(true); createProperty( "line-width", Variant::Type::Int ); property("line-width")->setCaption( i18n("Line Width") ); property("line-width")->setMinValue(1); property("line-width")->setMaxValue(1000); property("line-width")->setValue(1); property("line-width")->setAdvanced(true); createProperty( "line-style", Variant::Type::PenStyle ); property("line-style")->setAdvanced(true); property("line-style")->setCaption( i18n("Line Style") ); setDataPenStyle( "line-style", Qt::SolidLine ); } DPRectangle::~DPRectangle() { } void DPRectangle::setSelected( bool yes ) { if ( yes == isSelected() ) return; DrawPart::setSelected(yes); m_pRectangularOverlay->showResizeHandles(yes); } void DPRectangle::dataChanged() { bool displayBackground = dataBool("background"); QColor line_color = dataColor("line-color"); unsigned width = unsigned( dataInt("line-width") ); Qt::PenStyle style = getDataPenStyle("line-style"); setPen( QPen( line_color, width, style ) ); if (displayBackground) setBrush( dataColor("background-color") ); else setBrush( Qt::NoBrush ); postResize(); update(); } QSize DPRectangle::minimumSize() const { int side = qMax(16, pen().width()+2); return QSize( side, side ); } void DPRectangle::postResize() { setItemPoints( m_sizeRect, false ); } QRect DPRectangle::drawRect() const { int lw = pen().width(); if ( lw > m_sizeRect.width() ) lw = m_sizeRect.width(); if ( lw > m_sizeRect.height() ) lw = m_sizeRect.height(); return QRect( int(x() + m_sizeRect.x()+lw/2), int(y() + m_sizeRect.y()+lw/2), m_sizeRect.width()-lw, m_sizeRect.height()-lw ); } void DPRectangle::drawShape( QPainter & p ) { p.drawRect(drawRect()); } //END class DPRectangle //BEGIN class DPEllipse Item * DPEllipse::construct( ItemDocument *itemDocument, bool newItem, const char *id ) { return new DPEllipse( itemDocument, newItem, id ); } LibraryItem* DPEllipse::libraryItem() { return new LibraryItem( QStringList(QString("dp/ellipse")), i18n("Ellipse"), i18n("Other"), KIconLoader::global()->loadIcon( "text", KIconLoader::Small ), LibraryItem::lit_drawpart, DPEllipse::construct ); } DPEllipse::DPEllipse( ItemDocument *itemDocument, bool newItem, const char *id ) : DPRectangle( itemDocument, newItem, id ? id : "ellipse" ) { m_name = i18n("Ellipse"); } DPEllipse::~DPEllipse() { } void DPEllipse::postResize() { QRect br = m_sizeRect; // Make octagon that roughly covers ellipse QPolygon pa(8); pa[0] = QPoint( br.x() + br.width()/4, br.y() ); pa[1] = QPoint( br.x() + 3*br.width()/4, br.y() ); pa[2] = QPoint( br.x() + br.width(), br.y() + br.height()/4 ); pa[3] = QPoint( br.x() + br.width(), br.y() + 3*br.height()/4 ); pa[4] = QPoint( br.x() + 3*br.width()/4, br.y() + br.height() ); pa[5] = QPoint( br.x() + br.width()/4, br.y() + br.height() ); pa[6] = QPoint( br.x(), br.y() + 3*br.height()/4 ); pa[7] = QPoint( br.x(), br.y() + br.height()/4 ); setItemPoints( pa, false ); } void DPEllipse::drawShape( QPainter & p ) { p.drawEllipse(drawRect()); } //END class SolidShape diff --git a/src/electronics/circuitdocument.cpp b/src/electronics/circuitdocument.cpp index eeaad48a..463a73f8 100644 --- a/src/electronics/circuitdocument.cpp +++ b/src/electronics/circuitdocument.cpp @@ -1,844 +1,844 @@ /*************************************************************************** * Copyright (C) 2003-2005 by David Saxton * * david@bluehaze.org * * * * 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 "canvasmanipulator.h" #include "circuitdocument.h" #include "circuiticndocument.h" #include "circuitview.h" #include "component.h" #include "connector.h" #include "cnitemgroup.h" #include "documentiface.h" #include "drawparts/drawpart.h" #include "ecnode.h" #include "itemdocumentdata.h" #include "ktechlab.h" #include "pin.h" #include "simulator.h" #include "subcircuits.h" #include "switch.h" -#include #include #include #include #include +#include #include -#include -#include +#include +#include #include CircuitDocument::CircuitDocument( const QString & caption, const char *name ) : CircuitICNDocument( caption, name ) { m_pOrientationAction = new KActionMenu( QIcon::fromTheme("transform-rotate"), i18n("Orientation"), this ); m_type = Document::dt_circuit; m_pDocumentIface = new CircuitDocumentIface(this); m_fileExtensionInfo = QString("*.circuit|%1(*.circuit)\n*|%2").arg( i18n("Circuit") ).arg( i18n("All Files") ); m_cmManager->addManipulatorInfo( CMSelect::manipulatorInfo() ); m_cmManager->addManipulatorInfo( CMDraw::manipulatorInfo() ); m_cmManager->addManipulatorInfo( CMRightClick::manipulatorInfo() ); m_cmManager->addManipulatorInfo( CMRepeatedItemAdd::manipulatorInfo() ); m_cmManager->addManipulatorInfo( CMItemResize::manipulatorInfo() ); m_cmManager->addManipulatorInfo( CMItemDrag::manipulatorInfo() ); connect( this, SIGNAL(connectorAdded(Connector*)), this, SLOT(requestAssignCircuits()) ); connect( this, SIGNAL(connectorAdded(Connector*)), this, SLOT(connectorAdded(Connector*)) ); m_updateCircuitsTmr = new QTimer(); connect( m_updateCircuitsTmr, SIGNAL(timeout()), this, SLOT(assignCircuits()) ); requestStateSave(); } CircuitDocument::~CircuitDocument() { m_bDeleted = true; disconnect( m_updateCircuitsTmr, SIGNAL(timeout()), this, SLOT(assignCircuits()) ); disconnect( this, SIGNAL(connectorAdded(Connector*)), this, SLOT(connectorAdded(Connector*)) ); disconnect( this, SIGNAL(connectorAdded(Connector*)), this, SLOT(requestAssignCircuits()) ); for (ConnectorList::Iterator itConn = m_connectorList.begin(); itConn != m_connectorList.end(); ++itConn) { Connector *connector = itConn->data(); disconnect( connector, SIGNAL(removed(Connector*)), this, SLOT(requestAssignCircuits()) ); } for (ItemMap::Iterator itItem = m_itemList.begin(); itItem != m_itemList.end(); ++itItem) { Item *item = itItem.value(); disconnect( item, SIGNAL(removed(Item*)), this, SLOT(componentRemoved(Item*)) ); Component *comp = dynamic_cast( item ); if ( comp ) { disconnect( comp, SIGNAL(elementDestroyed(Element*)), this, SLOT(requestAssignCircuits()) ); } } deleteCircuits(); delete m_updateCircuitsTmr; delete m_pDocumentIface; } void CircuitDocument::slotInitItemActions( ) { CircuitICNDocument::slotInitItemActions(); CircuitView *activeCircuitView = dynamic_cast(activeView()); if ( !KTechlab::self() || !activeCircuitView ) return; Component *item = dynamic_cast( m_selectList->activeItem() ); if ( (!item && m_selectList->count() > 0) || !m_selectList->itemsAreSameType() ) return; QAction * orientation_actions[] = { activeCircuitView->actionByName("edit_orientation_0"), activeCircuitView->actionByName("edit_orientation_90"), activeCircuitView->actionByName("edit_orientation_180"), activeCircuitView->actionByName("edit_orientation_270") }; if ( !item ) { for ( unsigned i = 0; i < 4; ++i ) orientation_actions[i]->setEnabled(false); return; } for ( unsigned i = 0; i < 4; ++ i) { orientation_actions[i]->setEnabled(true); m_pOrientationAction->removeAction( orientation_actions[i] ); m_pOrientationAction->addAction( orientation_actions[i] ); } if ( item->angleDegrees() == 0 ) (static_cast( orientation_actions[0] ))->setChecked(true); else if ( item->angleDegrees() == 90 ) (static_cast( orientation_actions[1] ))->setChecked(true); else if ( item->angleDegrees() == 180 ) (static_cast( orientation_actions[2] ))->setChecked(true); else if ( item->angleDegrees() == 270 ) (static_cast( orientation_actions[3] ))->setChecked(true); } void CircuitDocument::rotateCounterClockwise() { m_selectList->slotRotateCCW(); requestRerouteInvalidatedConnectors(); } void CircuitDocument::rotateClockwise() { m_selectList->slotRotateCW(); requestRerouteInvalidatedConnectors(); } void CircuitDocument::flipHorizontally() { m_selectList->flipHorizontally(); requestRerouteInvalidatedConnectors(); } void CircuitDocument::flipVertically() { m_selectList->flipVertically(); requestRerouteInvalidatedConnectors(); } void CircuitDocument::setOrientation0() { m_selectList->slotSetOrientation0(); requestRerouteInvalidatedConnectors(); } void CircuitDocument::setOrientation90() { m_selectList->slotSetOrientation90(); requestRerouteInvalidatedConnectors(); } void CircuitDocument::setOrientation180() { m_selectList->slotSetOrientation180(); requestRerouteInvalidatedConnectors(); } void CircuitDocument::setOrientation270() { m_selectList->slotSetOrientation270(); requestRerouteInvalidatedConnectors(); } View *CircuitDocument::createView( ViewContainer *viewContainer, uint viewAreaId, const char *name ) { View *view = new CircuitView( this, viewContainer, viewAreaId, name ); handleNewView(view); return view; } void CircuitDocument::slotUpdateConfiguration() { CircuitICNDocument::slotUpdateConfiguration(); ECNodeMap::iterator nodeEnd = m_ecNodeList.end(); for ( ECNodeMap::iterator it = m_ecNodeList.begin(); it != nodeEnd; ++it ) { ECNode * n = *it; // static_cast(*it); n->setShowVoltageBars( KTLConfig::showVoltageBars() ); n->setShowVoltageColor( KTLConfig::showVoltageColor() ); } ConnectorList::iterator connectorsEnd = m_connectorList.end(); for ( ConnectorList::iterator it = m_connectorList.begin(); it != connectorsEnd; ++it ) (*it)->updateConnectorLines(); ComponentList::iterator componentsEnd = m_componentList.end(); for ( ComponentList::iterator it = m_componentList.begin(); it != componentsEnd; ++it ) (*it)->slotUpdateConfiguration(); } void CircuitDocument::update() { CircuitICNDocument::update(); bool animWires = KTLConfig::animateWires(); if ( KTLConfig::showVoltageColor() || animWires ) { if ( animWires ) { // Wire animation is for showing currents, so we need to recalculate the currents // in the wires. calculateConnectorCurrents(); } ConnectorList::iterator end = m_connectorList.end(); for ( ConnectorList::iterator it = m_connectorList.begin(); it != end; ++it ) { (*it)->incrementCurrentAnimation( 1.0 / double(KTLConfig::refreshRate()) ); (*it)->updateConnectorLines( animWires ); } } if ( KTLConfig::showVoltageColor() || KTLConfig::showVoltageBars() ) { ECNodeMap::iterator end = m_ecNodeList.end(); for ( ECNodeMap::iterator it = m_ecNodeList.begin(); it != end; ++it ) { // static_cast(*it)->setNodeChanged(); (*it)->setNodeChanged(); } } } void CircuitDocument::fillContextMenu( const QPoint &pos ) { CircuitICNDocument::fillContextMenu(pos); CircuitView *activeCircuitView = dynamic_cast(activeView()); if ( !activeCircuitView ) return; bool canCreateSubcircuit = (m_selectList->count() > 1 && countExtCon(m_selectList->items()) > 0); QAction * subcircuitAction = activeCircuitView->actionByName("circuit_create_subcircuit"); subcircuitAction->setEnabled( canCreateSubcircuit ); if ( m_selectList->count() < 1 ) return; Component *item = dynamic_cast( selectList()->activeItem() ); // NOTE: I negated this whole condition because I couldn't make out quite what the //logic was --electronerd if (!( (!item && m_selectList->count() > 0) || !m_selectList->itemsAreSameType() )) { QAction * orientation_actions[] = { activeCircuitView->actionByName("edit_orientation_0"), activeCircuitView->actionByName("edit_orientation_90"), activeCircuitView->actionByName("edit_orientation_180"), activeCircuitView->actionByName("edit_orientation_270") }; if ( !item ) return; for ( unsigned i = 0; i < 4; ++ i) { m_pOrientationAction->removeAction( orientation_actions[i] ); m_pOrientationAction->addAction( orientation_actions[i] ); } QList orientation_actionlist; // orientation_actionlist.prepend( new KActionSeparator() ); orientation_actionlist.append( m_pOrientationAction ); KTechlab::self()->plugActionList( "orientation_actionlist", orientation_actionlist ); } } void CircuitDocument::deleteCircuits() { const CircuitList::iterator end = m_circuitList.end(); for ( CircuitList::iterator it = m_circuitList.begin(); it != end; ++it ) { if (!Simulator::isDestroyedSim()) { Simulator::self()->detachCircuit(*it); } delete *it; } m_circuitList.clear(); m_pinList.clear(); m_wireList.clear(); } void CircuitDocument::requestAssignCircuits() { // qDebug() << Q_FUNC_INFO << endl; if (m_bDeleted) { return; } deleteCircuits(); m_updateCircuitsTmr->stop(); m_updateCircuitsTmr->setSingleShot( true ); m_updateCircuitsTmr->start( 0 /*, true */ ); } void CircuitDocument::connectorAdded( Connector * connector ) { if (connector) { connect( connector, SIGNAL(numWiresChanged(unsigned )), this, SLOT(requestAssignCircuits()) ); connect( connector, SIGNAL(removed(Connector*)), this, SLOT(requestAssignCircuits()) ); } } void CircuitDocument::itemAdded( Item * item) { CircuitICNDocument::itemAdded( item ); componentAdded( item ); } void CircuitDocument::componentAdded( Item * item ) { Component *component = dynamic_cast(item); if (!component) return; requestAssignCircuits(); connect( component, SIGNAL(elementCreated(Element*)), this, SLOT(requestAssignCircuits()) ); connect( component, SIGNAL(elementDestroyed(Element*)), this, SLOT(requestAssignCircuits()) ); connect( component, SIGNAL(removed(Item*)), this, SLOT(componentRemoved(Item*)) ); // We don't attach the component to the Simulator just yet, as the // Component's vtable is not yet fully constructed, and so Simulator can't // tell whether or not it is a logic component if ( !m_toSimulateList.contains(component) ) m_toSimulateList << component; } void CircuitDocument::componentRemoved( Item * item ) { Component *component = dynamic_cast(item); if (!component) return; m_componentList.removeAll( component ); m_toSimulateList.removeAll( component ); requestAssignCircuits(); if (!Simulator::isDestroyedSim()) { Simulator::self()->detachComponent(component); } } // I think this is where the inf0z from cnodes/branches is moved into the midle-layer // pins/wires. void CircuitDocument::calculateConnectorCurrents() { const CircuitList::iterator circuitEnd = m_circuitList.end(); for ( CircuitList::iterator it = m_circuitList.begin(); it != circuitEnd; ++it ) (*it)->updateCurrents(); PinList groundPins; // Tell the Pins to reset their calculated currents to zero m_pinList.removeAll((Pin*)nullptr); const PinList::iterator pinEnd = m_pinList.end(); for ( PinList::iterator it = m_pinList.begin(); it != pinEnd; ++it ) { if ( Pin *n = dynamic_cast((Pin*)*it) ) { n->resetCurrent(); n->setSwitchCurrentsUnknown(); if ( !n->parentECNode()->isChildNode() ) { n->setCurrentKnown( true ); // (and it has a current of 0 amps) } else if ( n->groundType() == Pin::gt_always ) { groundPins << n; n->setCurrentKnown( false ); } else { // Child node that is non ground n->setCurrentKnown( n->parentECNode()->numPins() < 2 ); } } } // Tell the components to update their ECNode's currents' from the elements // currents are merged into PINS. const ComponentList::iterator componentEnd = m_componentList.end(); for ( ComponentList::iterator it = m_componentList.begin(); it != componentEnd; ++it ) (*it)->setNodalCurrents(); // And now for the wires and switches... m_wireList.removeAll((Wire*)nullptr); const WireList::iterator clEnd = m_wireList.end(); for ( WireList::iterator it = m_wireList.begin(); it != clEnd; ++it ) (*it)->setCurrentKnown(false); SwitchList switches = m_switchList; WireList wires = m_wireList; bool found = true; while ( (!wires.isEmpty() || !switches.isEmpty() || !groundPins.isEmpty()) && found ) { found = false; for ( WireList::iterator itW = wires.begin(); itW != wires.end(); ) { if ( (*itW)->calculateCurrent() ) { found = true; itW = wires.erase(itW); // note: assigning a temporary interator, incrementing and erasing, seems to crash } else { ++itW; } } SwitchList::iterator switchesEnd = switches.end(); for ( SwitchList::iterator it = switches.begin(); it != switchesEnd; ) { if ( (*it)->calculateCurrent() ) { found = true; // note: assigning a temporary interator, incrementing and erasing, seems to crash // it = container.erase( it ) seems to crash other times SwitchList::iterator oldIt = it; ++it; switches.erase(oldIt); } else ++it; } /* make the ground pins work. Current engine doesn't treat ground explicitly. */ PinList::iterator groundPinsEnd = groundPins.end(); for ( PinList::iterator it = groundPins.begin(); it != groundPinsEnd; ) { if ( (*it)->calculateCurrentFromWires() ) { found = true; // note: assigning a temporary interator, incrementing and erasing, seems to crash sometimes; // it = container.erase( it ) seems to crash other times PinList::iterator oldIt = it; ++it; groundPins.erase(oldIt); } else ++it; } } } void CircuitDocument::assignCircuits() { // Now we can finally add the unadded components to the Simulator const ComponentList::iterator toSimulateEnd = m_toSimulateList.end(); for ( ComponentList::iterator it = m_toSimulateList.begin(); it != toSimulateEnd; ++it ) Simulator::self()->attachComponent(*it); m_toSimulateList.clear(); // Stage 0: Build up pin and wire lists m_pinList.clear(); const ECNodeMap::const_iterator nodeListEnd = m_ecNodeList.end(); for ( ECNodeMap::const_iterator it = m_ecNodeList.begin(); it != nodeListEnd; ++it ) { // if ( ECNode * ecnode = dynamic_cast(*it) ) ECNode* ecnode = *it; for ( unsigned i = 0; i < ecnode->numPins(); i++ ) m_pinList << ecnode->pin(i); } m_wireList.clear(); const ConnectorList::const_iterator connectorListEnd = m_connectorList.end(); for ( ConnectorList::const_iterator it = m_connectorList.begin(); it != connectorListEnd; ++it ) { for ( unsigned i = 0; i < (*it)->numWires(); i++ ) m_wireList << (*it)->wire(i); } typedef QList PinListList; // Stage 1: Partition the circuit up into dependent areas (bar splitting // at ground pins) PinList unassignedPins = m_pinList; PinListList pinListList; while( !unassignedPins.isEmpty() ) { PinList pinList; getPartition( *unassignedPins.begin(), & pinList, & unassignedPins ); pinListList.append(pinList); } // qDebug () << "pinListList.size()="<init(); m_switchList.clear(); m_componentList.clear(); const ItemMap::const_iterator cilEnd = m_itemList.end(); for ( ItemMap::const_iterator it = m_itemList.begin(); it != cilEnd; ++it ) { Component *component = dynamic_cast(*it); if ( !component ) continue; m_componentList << component; component->initElements(0); m_switchList += component->switchList(); } circuitListEnd = m_circuitList.end(); for ( CircuitList::iterator it = m_circuitList.begin(); it != circuitListEnd; ++it ) (*it)->createMatrixMap(); for ( ItemMap::const_iterator it = m_itemList.begin(); it != cilEnd; ++it ) { Component *component = dynamic_cast(*it); if (component) component->initElements(1); } circuitListEnd = m_circuitList.end(); for ( CircuitList::iterator it = m_circuitList.begin(); it != circuitListEnd; ++it ) { (*it)->initCache(); Simulator::self()->attachCircuit(*it); } } void CircuitDocument::getPartition( Pin *pin, PinList *pinList, PinList *unassignedPins, bool onlyGroundDependent ) { if (!pin) return; unassignedPins->removeAll(pin); if ( pinList->contains(pin) ) return; pinList->append(pin); const PinList localConnectedPins = pin->localConnectedPins(); const PinList::const_iterator end = localConnectedPins.end(); for ( PinList::const_iterator it = localConnectedPins.begin(); it != end; ++it ) getPartition( *it, pinList, unassignedPins, onlyGroundDependent ); const PinList groundDependentPins = pin->groundDependentPins(); const PinList::const_iterator dEnd = groundDependentPins.end(); for ( PinList::const_iterator it = groundDependentPins.begin(); it != dEnd; ++it ) getPartition( *it, pinList, unassignedPins, onlyGroundDependent ); if (!onlyGroundDependent) { PinList circuitDependentPins = pin->circuitDependentPins(); const PinList::const_iterator dEnd = circuitDependentPins.end(); for ( PinList::const_iterator it = circuitDependentPins.begin(); it != dEnd; ++it ) getPartition( *it, pinList, unassignedPins, onlyGroundDependent ); } } void CircuitDocument::splitIntoCircuits( PinList *pinList ) { // First: identify ground PinList unassignedPins = *pinList; typedef QList PinListList; PinListList pinListList; while ( !unassignedPins.isEmpty() ) { PinList tempPinList; getPartition( *unassignedPins.begin(), & tempPinList, & unassignedPins, true ); pinListList.append(tempPinList); } const PinListList::iterator nllEnd = pinListList.end(); for ( PinListList::iterator it = pinListList.begin(); it != nllEnd; ++it ) Circuit::identifyGround(*it); while ( !pinList->isEmpty() ) { PinList::iterator end = pinList->end(); PinList::iterator it = pinList->begin(); while ( it != end && (*it)->eqId() == -1 ) ++it; if ( it == end ) break; else { Circuitoid *circuitoid = new Circuitoid; recursivePinAdd( *it, circuitoid, pinList ); if ( !tryAsLogicCircuit(circuitoid) ) m_circuitList += createCircuit(circuitoid); delete circuitoid; } } // Remaining pins are ground; tell them about it // TODO This is a bit hacky.... const PinList::iterator end = pinList->end(); for ( PinList::iterator it = pinList->begin(); it != end; ++it ) { (*it)->setVoltage(0.0); ElementList elements = (*it)->elements(); const ElementList::iterator eEnd = elements.end(); for ( ElementList::iterator it = elements.begin(); it != eEnd; ++it ) { if ( LogicIn * logicIn = dynamic_cast(*it) ) { logicIn->setLastState(false); logicIn->callCallback(); } } } } void CircuitDocument::recursivePinAdd( Pin *pin, Circuitoid *circuitoid, PinList *unassignedPins ) { if(!pin) return; if(pin->eqId() != -1 ) unassignedPins->removeAll(pin); if(circuitoid->contains(pin) ) return; circuitoid->addPin(pin); if(pin->eqId() == -1 ) return; const PinList localConnectedPins = pin->localConnectedPins(); const PinList::const_iterator end = localConnectedPins.end(); for ( PinList::const_iterator it = localConnectedPins.begin(); it != end; ++it ) recursivePinAdd( *it, circuitoid, unassignedPins ); const PinList groundDependentPins = pin->groundDependentPins(); const PinList::const_iterator gdEnd = groundDependentPins.end(); for ( PinList::const_iterator it = groundDependentPins.begin(); it != gdEnd; ++it ) recursivePinAdd( *it, circuitoid, unassignedPins ); const PinList circuitDependentPins = pin->circuitDependentPins(); const PinList::const_iterator cdEnd = circuitDependentPins.end(); for ( PinList::const_iterator it = circuitDependentPins.begin(); it != cdEnd; ++it ) recursivePinAdd( *it, circuitoid, unassignedPins ); const ElementList elements = pin->elements(); const ElementList::const_iterator eEnd = elements.end(); for ( ElementList::const_iterator it = elements.begin(); it != eEnd; ++it ) circuitoid->addElement(*it); } bool CircuitDocument::tryAsLogicCircuit( Circuitoid *circuitoid ) { if (!circuitoid) return false; if ( circuitoid->elementList.size() == 0 ) { // This doesn't quite belong here...but whatever. Initialize all // pins to voltage zero as they won't get set to zero otherwise const PinList::const_iterator pinListEnd = circuitoid->pinList.constEnd(); for ( PinList::const_iterator it = circuitoid->pinList.constBegin(); it != pinListEnd; ++it ) (*it)->setVoltage(0.0); // A logic circuit only requires there to be no non-logic components, // and at most one LogicOut - so this qualifies return true; } LogicInList logicInList; LogicOut *out = nullptr; uint logicInCount = 0; uint logicOutCount = 0; ElementList::const_iterator end = circuitoid->elementList.end(); for ( ElementList::const_iterator it = circuitoid->elementList.begin(); it != end; ++it ) { if ( (*it)->type() == Element::Element_LogicOut ) { logicOutCount++; out = static_cast(*it); } else if ( (*it)->type() == Element::Element_LogicIn ) { logicInCount++; logicInList += static_cast(*it); } else return false; } if ( logicOutCount > 1 ) return false; else if ( logicOutCount == 1 ) Simulator::self()->createLogicChain( out, logicInList, circuitoid->pinList ); else { // We have ourselves stranded LogicIns...so lets set them all to low const PinList::const_iterator pinListEnd = circuitoid->pinList.constEnd(); for ( PinList::const_iterator it = circuitoid->pinList.constBegin(); it != pinListEnd; ++it ) (*it)->setVoltage(0.0); for ( ElementList::const_iterator it = circuitoid->elementList.begin(); it != end; ++it ) { LogicIn * logicIn = static_cast(*it); logicIn->setNextLogic(nullptr); logicIn->setElementSet(nullptr); if ( logicIn->isHigh() ) { logicIn->setLastState(false); logicIn->callCallback(); } } } return true; } Circuit *CircuitDocument::createCircuit( Circuitoid *circuitoid ) { if (!circuitoid) return nullptr; Circuit *circuit = new Circuit(); const PinList::const_iterator nEnd = circuitoid->pinList.end(); for ( PinList::const_iterator it = circuitoid->pinList.begin(); it != nEnd; ++it ) circuit->addPin(*it); const ElementList::const_iterator eEnd = circuitoid->elementList.end(); for ( ElementList::const_iterator it = circuitoid->elementList.begin(); it != eEnd; ++it ) circuit->addElement(*it); return circuit; } void CircuitDocument::createSubcircuit() { ItemList itemList = m_selectList->items(); const ItemList::iterator itemListEnd = itemList.end(); for ( ItemList::iterator it = itemList.begin(); it != itemListEnd; ++it ) { if ( !dynamic_cast((Item*)*it) ) *it = nullptr; } itemList.removeAll((Item*)nullptr); if ( itemList.isEmpty() ) { KMessageBox::sorry( activeView(), i18n("No components were found in the selection.") ); return; } // Number of external connections const int extConCount = countExtCon(itemList); if ( extConCount == 0 ) { KMessageBox::sorry( activeView(), i18n("No External Connection components were found in the selection.") ); return; } bool ok; const QString name = QInputDialog::getText(activeView(), "Subcircuit", "Name", QLineEdit::Normal, QString(), &ok); if (!ok) return; SubcircuitData subcircuit; subcircuit.addItems(itemList); subcircuit.addNodes( getCommonNodes(itemList) ); subcircuit.addConnectors( getCommonConnectors(itemList) ); Subcircuits::addSubcircuit( name, subcircuit.toXML() ); } int CircuitDocument::countExtCon( const ItemList &itemList ) const { int count = 0; const ItemList::const_iterator end = itemList.end(); for ( ItemList::const_iterator it = itemList.begin(); it != end; ++it ) { Item * item = *it; if ( item && item->type() == "ec/external_connection" ) count++; } return count; } bool CircuitDocument::isValidItem( const QString &itemId ) { return itemId.startsWith("ec/") || itemId.startsWith("dp/") || itemId.startsWith("sc/"); } bool CircuitDocument::isValidItem( Item *item ) { return (dynamic_cast(item) || dynamic_cast(item)); } void CircuitDocument::displayEquations() { qDebug() << "######################################################" << endl; const CircuitList::iterator end = m_circuitList.end(); int i = 1; for ( CircuitList::iterator it = m_circuitList.begin(); it != end; ++it ) { qDebug() << "Equation set "<displayEquations(); i++; } qDebug() << "######################################################" << endl; } diff --git a/src/electronics/circuiticndocument.cpp b/src/electronics/circuiticndocument.cpp index ea0c048f..109c12ce 100644 --- a/src/electronics/circuiticndocument.cpp +++ b/src/electronics/circuiticndocument.cpp @@ -1,483 +1,483 @@ // // C++ Implementation: circuiticndocument // // Description: // // // Author: Zoltan P , (C) 2008 // // Copyright: See COPYING file that comes with this distribution // // #include "circuiticndocument.h" #include "connector.h" #include "conrouter.h" #include "cnitemgroup.h" #include "ecnode.h" #include "flowcontainer.h" #include "item.h" #include "junctionnode.h" #include "nodegroup.h" -#include +#include CircuitICNDocument::CircuitICNDocument( const QString &caption, const char *name) : ICNDocument(caption, name ) { } CircuitICNDocument::~CircuitICNDocument() { // Go to hell, KtlQCanvas. I'm in charge of what gets deleted. KtlQCanvasItemList all = m_canvas->allItems(); const KtlQCanvasItemList::Iterator end = all.end(); for ( KtlQCanvasItemList::Iterator it= all.begin(); it != end; ++it ) (*it)->setCanvas(nullptr); // Remove all items from the canvas selectAll(); deleteSelection(); // Delete anything that got through the above couple of lines ConnectorList connectorsToDelete = m_connectorList; connectorsToDelete.clear(); const ConnectorList::iterator connectorListEnd = connectorsToDelete.end(); for ( ConnectorList::iterator it = connectorsToDelete.begin(); it != connectorListEnd; ++it ) delete *it; deleteAllNodes(); } void CircuitICNDocument::deleteAllNodes() { const ECNodeMap::iterator nodeListEnd = m_ecNodeList.end(); for ( ECNodeMap::iterator it = m_ecNodeList.begin(); it != nodeListEnd; ++it ) { qDebug() << "CircuitICNDocument::deleteAllNodes removing [" << it.key() << "] " << it.value(); //delete *it; // 2015.07.31 - this will not work // prevent crash on node destructor { ECNode *ecNode = it.value(); //unregisterUID( ecNode->id() ); // do not use, will modify m_ecNodeList ICNDocument::unregisterUID( ecNode->id() ); ecNode->setICNDocument( nullptr ); } it.value()->deleteLater(); } m_ecNodeList.clear(); } bool CircuitICNDocument::canConnect( KtlQCanvasItem *qcanvasItem1, KtlQCanvasItem *qcanvasItem2 ) const { // Rough outline of what can and can't connect: // * At most three connectors to a node // * Can't have connectors going between different levels (e.g. can't have // a connector coming outside a FlowContainer from inside). // * Can't have more than one route between any two nodes // * In all connections between nodes, must have at least one input and one // output node at the ends. // nothing special in a circuit; we can connect almost anything return ICNDocument::canConnect(qcanvasItem1, qcanvasItem2); } Connector * CircuitICNDocument::createConnector( Node *node, Connector *con, const QPoint &pos2, QPointList *pointList ) { if(!canConnect( node, con ) ) return nullptr; // FIXME dynamic_cast used, fix it in Connector class ECNode *conStartNode = dynamic_cast (con->startNode() ); ECNode *conEndNode = dynamic_cast (con->endNode() ); ECNode *ecNode = dynamic_cast (node ); const bool usedManual = con->usesManualPoints(); ECNode *newNode = new JunctionNode(this, 0, pos2); QPointList autoPoints; if (!pointList) { addAllItemConnectorPoints(); ConRouter cr(this); cr.mapRoute( int(node->x()), int(node->y()), pos2.x(), pos2.y() ); autoPoints = cr.pointList(false); pointList = &autoPoints; } QList oldConPoints = con->splitConnectorPoints(pos2); con->hide(); // The actual new connector Connector *new1 = newNode->createConnector(node); ecNode->addConnector(new1); new1->setRoutePoints(*pointList,usedManual); // The two connectors formed from the original one when split Connector *new2 = newNode->createConnector(conStartNode); conStartNode->addConnector(new2); new2->setRoutePoints( oldConPoints.at(0), usedManual ); Connector *new3 = conEndNode->createConnector(newNode); newNode->addConnector(new3); new3->setRoutePoints( oldConPoints.at(1), usedManual ); // Avoid flicker: tell them to update their draw lists now con->updateConnectorPoints(false); new1->updateDrawList(); new2->updateDrawList(); new3->updateDrawList(); // Now it's safe to remove the connector... con->removeConnector(); flushDeleteList(); deleteNodeGroup(conStartNode); deleteNodeGroup(conEndNode); createNodeGroup(newNode)->init(); return new1; } Connector *CircuitICNDocument::createConnector(Connector *con1, Connector *con2, const QPoint &pos1, const QPoint &pos2, QPointList *pointList ) { if ( !canConnect( con1, con2 ) ) return nullptr; const bool con1UsedManual = con1->usesManualPoints(); const bool con2UsedManual = con2->usesManualPoints(); QList oldCon1Points = con1->splitConnectorPoints(pos1); QList oldCon2Points = con2->splitConnectorPoints(pos2); ECNode *node1a = dynamic_cast ( con1->startNode() ); ECNode *node1b = dynamic_cast ( con1->endNode( ) ); ECNode *node2a = dynamic_cast ( con2->startNode() ); ECNode *node2b = dynamic_cast ( con2->endNode( ) ); if ( !node1a || !node1b || !node2a || !node2b ) return nullptr; con1->hide(); con2->hide(); // from this point forward, we are dealing with a circuit document -> all nodes are electronic ECNode *newNode1 = new JunctionNode( this, 0, pos1 ); ECNode *newNode2 = new JunctionNode( this, 0, pos2 ); Connector *con1a = newNode1->createConnector(node1a); node1a->addConnector(con1a); Connector *con1b = newNode1->createConnector(node1b); node1b->addConnector(con1b); Connector *newCon = newNode1->createConnector(newNode2); newNode2->addConnector(newCon); Connector *con2a = node2a->createConnector(newNode2); newNode2->addConnector(con2a); Connector *con2b = node2b->createConnector(newNode2); newNode2->addConnector(con2b); if(!con1a || !con1b || !con2a || !con2b ) { // This should never happen, as the canConnect function should strictly // determine whether the connectors could be created before hand. qWarning() << Q_FUNC_INFO << "Not all the connectors were created, this should never happen" << endl; if(con1a) con1a->removeConnector(); if(con1b) con1b->removeConnector(); if(con2a) con2a->removeConnector(); if(con2b) con2b->removeConnector(); newNode1->removeNode(); newNode2->removeNode(); flushDeleteList(); return nullptr; } con1a->setRoutePoints(oldCon1Points.at(0), con1UsedManual ); con1b->setRoutePoints(oldCon1Points.at(1), con1UsedManual ); con2a->setRoutePoints(oldCon2Points.at(0), con2UsedManual ); con2b->setRoutePoints(oldCon2Points.at(1), con2UsedManual ); QPointList autoPoints; if (!pointList) { addAllItemConnectorPoints(); ConRouter cr(this); cr.mapRoute( pos1.x(), pos1.y(), pos2.x(), pos2.y() ); autoPoints = cr.pointList(false); pointList = &autoPoints; } newCon->setRoutePoints(*pointList,true); // Avoid flicker: tell them to update their draw lists now con1->updateConnectorPoints(false); con2->updateConnectorPoints(false); newCon->updateDrawList(); con1a->updateDrawList(); con1b->updateDrawList(); con2a->updateDrawList(); con2b->updateDrawList(); // Now it's safe to remove the connectors con1->removeConnector(); con2->removeConnector(); flushDeleteList(); deleteNodeGroup(node1a); deleteNodeGroup(node1b); deleteNodeGroup(node2a); deleteNodeGroup(node2b); NodeGroup *ng = createNodeGroup(newNode1); ng->addNode( newNode2, true ); ng->init(); return newCon; } Connector *CircuitICNDocument::createConnector( const QString &startNodeId, const QString &endNodeId, QPointList *pointList ) { ECNode *startNode = getEcNodeWithID(startNodeId); ECNode *endNode = getEcNodeWithID(endNodeId); if ( !startNode || !endNode ) { qDebug() << "Either/both the connector start node and end node could not be found" << endl; return nullptr; } if ( !canConnect( startNode, endNode ) ) return nullptr; Connector *connector = endNode->createConnector(startNode); if (!connector) { qCritical() << Q_FUNC_INFO << "End node did not create the connector" << endl; return nullptr; } startNode->addConnector(connector); flushDeleteList(); // Delete any connectors that might have been removed by the nodes // Set the route to the manual created one if the user created such a route if(pointList) connector->setRoutePoints(*pointList,true); // FIXME WTF is going on here? Redundant/meaningless code? // ConnectorList connectorList; // connectorList.append(connector); setModified(true); requestRerouteInvalidatedConnectors(); return connector; } Node *CircuitICNDocument::nodeWithID( const QString &id ) { if ( m_ecNodeList.contains( id ) ) return m_ecNodeList[id]; else return nullptr; } ECNode *CircuitICNDocument::getEcNodeWithID( const QString &id ) { if ( m_ecNodeList.contains( id ) ) return m_ecNodeList[id]; else return nullptr; } void CircuitICNDocument::slotAssignNodeGroups() { ICNDocument::slotAssignNodeGroups(); const ECNodeMap::iterator end = m_ecNodeList.end(); for ( ECNodeMap::iterator it = m_ecNodeList.begin(); it != end; ++it ) { NodeGroup *ng = createNodeGroup ( *it ); if ( ng ) ng->init(); } // We've destroyed the old node groups, so any collapsed flowcontainers // containing new node groups need to update them to make them invisible. const ItemMap::const_iterator itemListEnd = m_itemList.end(); for ( ItemMap::const_iterator it = m_itemList.begin(); it != itemListEnd; ++it ) { if ( FlowContainer * fc = dynamic_cast ( *it ) ) fc->updateContainedVisibility(); } } void CircuitICNDocument::flushDeleteList() { // Remove duplicate items in the delete list KtlQCanvasItemList::iterator end = m_itemDeleteList.end(); for ( KtlQCanvasItemList::iterator it = m_itemDeleteList.begin(); it != end; ++it ) { if ( *it && (m_itemDeleteList.count( *it ) > 1) ) { *it = nullptr; } } m_itemDeleteList.removeAll(nullptr); /* again we're spending time to figure out what special method to call instead of a generic call..*/ end = m_itemDeleteList.end(); for ( KtlQCanvasItemList::iterator it = m_itemDeleteList.begin(); it != end; ++it ) { KtlQCanvasItem *qcanvasItem = *it; m_selectList->removeQCanvasItem ( *it ); if ( Item *item = dynamic_cast ( qcanvasItem ) ) m_itemList.remove ( item->id() ); else if ( ECNode * node = dynamic_cast ( qcanvasItem ) ) m_ecNodeList.remove ( node->id() ); else if ( Connector * con = dynamic_cast ( qcanvasItem ) ) m_connectorList.removeAll ( con ); else qCritical() << Q_FUNC_INFO << "Unknown qcanvasItem! "<setCanvas(nullptr); delete qcanvasItem; *it = nullptr; } // // Check connectors for merging bool doneJoin = false; const ECNodeMap::iterator nlEnd = m_ecNodeList.end(); for ( ECNodeMap::iterator it = m_ecNodeList.begin(); it != nlEnd; ++it ) { ( *it )->removeNullConnectors(); int conCount = ( *it )->connectorList().count(); if ( conCount == 2 && ! ( *it )->parentItem() ) { if ( joinConnectors ( *it ) ) doneJoin = true; } } if(doneJoin) flushDeleteList(); requestRerouteInvalidatedConnectors(); } bool CircuitICNDocument::registerItem( KtlQCanvasItem *qcanvasItem ) { if(!qcanvasItem) return false; if ( !ItemDocument::registerItem(qcanvasItem) ) { if ( ECNode * node = dynamic_cast(qcanvasItem) ) { m_ecNodeList[ node->id() ] = node; emit nodeAdded( (Node*)node ); } else if ( Connector * connector = dynamic_cast(qcanvasItem) ) { m_connectorList.append(connector); emit connectorAdded(connector); } else { qCritical() << Q_FUNC_INFO << "Unrecognised item"<parentItem() ) return false; node->removeNullConnectors(); // an electronic node can be removed if it has exactly 2 connectors connected to it int conCount = node->getAllConnectors().count(); if ( conCount != 2 ) return false; Connector *con1, *con2; ECNode *startNode = nullptr; ECNode *endNode = nullptr; QPointList conPoints; con1 = node->connectorList().at(0).data(); con2 = node->connectorList().at(1).data(); if ( con1 == con2 ) return false; // we don't know on which end of the connectors is our node, so we must check both ends // HACK // TODO // dynamic_cast used, because Connector doesn't know about ECNode, only Node if( con1->startNode() == node ) startNode = dynamic_cast ( con1->endNode() ); else startNode = dynamic_cast ( con1->startNode() ); if( con2->startNode() == node ) endNode = dynamic_cast ( con2->endNode() ); else endNode = dynamic_cast ( con2->startNode() ); conPoints = con1->connectorPoints(false) + con2->connectorPoints(false); if( !startNode || !endNode ) return false; Connector *newCon = endNode->createConnector(startNode); if(!newCon) return false; startNode->addConnector(newCon); newCon->setRoutePoints( conPoints, con1->usesManualPoints() || con2->usesManualPoints() ); // Avoid flicker: update draw lists now con1->updateConnectorPoints(false); con2->updateConnectorPoints(false); newCon->updateDrawList(); node->removeNode(); con1->removeConnector(); con2->removeConnector(); return true; } diff --git a/src/electronics/component.cpp b/src/electronics/component.cpp index dcad4402..9d62c628 100644 --- a/src/electronics/component.cpp +++ b/src/electronics/component.cpp @@ -1,1165 +1,1166 @@ /*************************************************************************** * Copyright (C) 2004-2005 by David Saxton * * david@bluehaze.org * * * * 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 "canvasitemparts.h" #include "circuitdocument.h" #include "component.h" #include "ecnode.h" #include "itemdocumentdata.h" #include "node.h" #include "pin.h" #include "simulator.h" #include "bjt.h" #include "capacitance.h" #include "cccs.h" #include "ccvs.h" #include "currentsignal.h" #include "currentsource.h" #include "diode.h" #include "jfet.h" #include "inductance.h" #include "logic.h" #include "mosfet.h" #include "opamp.h" #include "resistance.h" #include "switch.h" #include "vccs.h" #include "vcvs.h" #include "voltagepoint.h" #include "voltagesignal.h" #include "voltagesource.h" + #include -#include -#include -#include -#include -#include +#include +#include +#include +#include +#include #include // const int dipWidth = 112; // 2017.10.01 - comment out unused constants // const int pairSep = 32; // Degrees per radian Component::Component( ICNDocument *icnDocument, bool newItem, const QString &id ) : CNItem( icnDocument, newItem, id ), m_angleDegrees(0), b_flipped(false) { m_pCircuitDocument = dynamic_cast(icnDocument); for ( int i=0; i<4; ++i ) { m_pPNode[i] = nullptr; m_pNNode[i] = nullptr; } // Get configuration options slotUpdateConfiguration(); // And finally register this :-) if ( icnDocument ) icnDocument->registerItem(this); } Component::~Component() { removeElements(); if (!Simulator::isDestroyedSim()) { Simulator::self()->detachComponent(this); } } void Component::removeItem( ) { if (b_deleted) return; if (!Simulator::isDestroyedSim()) { Simulator::self()->detachComponent(this); } CNItem::removeItem(); } void Component::removeElements( bool setPinsInterIndependent ) { const ElementMapList::iterator end = m_elementMapList.end(); for ( ElementMapList::iterator it = m_elementMapList.begin(); it != end; ++it ) { Element * e = (*it).e; if (e) { emit elementDestroyed(e); e->componentDeleted(); } } m_elementMapList.clear(); const SwitchList::iterator swEnd = m_switchList.end(); for ( SwitchList::iterator it = m_switchList.begin(); it != swEnd; ++it ) { Switch *sw = *it; if ( !sw ) continue; emit switchDestroyed( sw ); delete sw; } m_switchList.clear(); if ( setPinsInterIndependent ) setAllPinsInterIndependent(); } void Component::removeElement( Element * element, bool setPinsInterIndependent ) { if (!element) return; emit elementDestroyed(element); element->componentDeleted(); const ElementMapList::iterator end = m_elementMapList.end(); for ( ElementMapList::iterator it = m_elementMapList.begin(); it != end; ) { ElementMapList::iterator next = it; ++next; if ( (*it).e == element ) m_elementMapList.erase(it); it = next; } if ( setPinsInterIndependent ) rebuildPinInterDepedence(); } void Component::removeSwitch( Switch *sw ) { if ( !sw ) return; emit switchDestroyed( sw ); delete sw; m_switchList.removeAll(sw); m_pCircuitDocument->requestAssignCircuits(); } void Component::setNodalCurrents() { const ElementMapList::iterator end = m_elementMapList.end(); for ( ElementMapList::iterator it = m_elementMapList.begin(); it != end; ++it ) { ElementMap m = (*it); for ( int i=0; i<4; i++ ) { if ( m.n[i] ) { m.n[i]->mergeCurrent( m.e->m_cnodeI[i] ); } } } } void Component::initPainter( QPainter &p ) { CNItem::initPainter(p); if ( !b_flipped && (m_angleDegrees%360 == 0) ) return; p.save(); p.translate( int(x()), int(y()) ); if (b_flipped) p.scale( -1, 1 ); p.rotate(m_angleDegrees); p.translate( -int(x()), -int(y()) ); } void Component::deinitPainter( QPainter &p ) { if ( !b_flipped && (m_angleDegrees%360 == 0) ) return; p.restore(); } void Component::setAngleDegrees( int degrees ) { if ( !p_icnDocument ) return; degrees = ((degrees%360)+360)%360; if ( m_angleDegrees == degrees ) return; updateConnectorPoints(false); m_angleDegrees = degrees; itemPointsChanged(); updateAttachedPositioning(); p_icnDocument->requestRerouteInvalidatedConnectors(); emit orientationChanged(); } void Component::setFlipped( bool flipped ) { if ( !p_icnDocument ) return; if ( flipped == b_flipped ) return; updateConnectorPoints(false); b_flipped = flipped; itemPointsChanged(); updateAttachedPositioning(); p_icnDocument->requestRerouteInvalidatedConnectors(); emit orientationChanged(); } void Component::itemPointsChanged() { QPolygon transformedPoints = transMatrix( m_angleDegrees, b_flipped, 0, 0, false ).map(m_itemPoints); // transformedPoints.translate( int(x()), int(y()) ); setPoints(transformedPoints); } void Component::restoreFromItemData( const ItemData &itemData ) { CNItem::restoreFromItemData(itemData); setAngleDegrees( int(itemData.angleDegrees) ); setFlipped(itemData.flipped); } ItemData Component::itemData() const { ItemData itemData = CNItem::itemData(); itemData.angleDegrees = m_angleDegrees; itemData.flipped = b_flipped; return itemData; } QMatrix Component::transMatrix( int angleDegrees, bool flipped, int x, int y, bool inverse ) { QMatrix m; m.translate( x, y ); if (inverse) { m.rotate(-angleDegrees); if (flipped) m.scale( -1, 1 ); } else { if (flipped) m.scale( -1, 1 ); m.rotate(angleDegrees); } m.translate( -x, -y ); // m.setTransformationMode( QMatrix::Areas ); // TODO find a replacement return m; } void Component::finishedCreation() { CNItem::finishedCreation(); updateAttachedPositioning(); } void Component::updateAttachedPositioning() { const double RPD = M_PI / 180.0; if (b_deleted || !m_bDoneCreation) return; //BEGIN Transform the nodes const NodeInfoMap::iterator end = m_nodeMap.end(); for ( NodeInfoMap::iterator it = m_nodeMap.begin(); it != end; ++it ) { if ( !it.value().node ) qCritical() << Q_FUNC_INFO << "Node in nodemap is null" << endl; else { int nx = int((std::cos(m_angleDegrees * RPD) * it.value().x) - (std::sin(m_angleDegrees * RPD) * it.value().y)); int ny = int((std::sin(m_angleDegrees * RPD) * it.value().x) + (std::cos(m_angleDegrees * RPD) * it.value().y)); if (b_flipped) nx = -nx; #define round_8(x) (((x) > 0) ? int(((x)+4)/8)*8 : int(((x)-4)/8)*8) nx = round_8(nx); ny = round_8(ny); #undef round_8 int newDir = (((m_angleDegrees + it.value().orientation)%360)+360)%360; if (b_flipped) newDir = (((180-newDir)%360)+360)%360; it.value().node->move( nx+x(), ny+y() ); it.value().node->setOrientation( newDir ); } } //END Transform the nodes //BEGIN Transform the GuiParts QMatrix m; if (b_flipped) m.scale( -1, 1 ); m.rotate(m_angleDegrees); //m.setTransformationMode( QMatrix::Areas ); // TODO find a replacement const TextMap::iterator textMapEnd = m_textMap.end(); for ( TextMap::iterator it = m_textMap.begin(); it != textMapEnd; ++it ) { QRect newPos = m.mapRect( it.value()->recommendedRect() ); it.value()->move( newPos.x() + x(), newPos.y() + y() ); it.value()->setGuiPartSize( newPos.width(), newPos.height() ); it.value()->setAngleDegrees(m_angleDegrees); } const WidgetMap::iterator widgetMapEnd = m_widgetMap.end(); for ( WidgetMap::iterator it = m_widgetMap.begin(); it != widgetMapEnd; ++it ) { QRect newPos = m.mapRect( it.value()->recommendedRect() ); it.value()->move( newPos.x() + x(), newPos.y() + y() ); it.value()->setGuiPartSize( newPos.width(), newPos.height() ); it.value()->setAngleDegrees(m_angleDegrees); } //END Transform the GuiParts } void Component::drawPortShape( QPainter & p ) { int h = height(); int w = width() - 1; int _x = int( x() + offsetX() ); int _y = int( y() + offsetY() ); double roundSize = 8; double slantIndent = 8; const double DPR = 180.0 / M_PI; double inner = std::atan(h/slantIndent); // Angle for slight corner double outer = M_PI - inner; // Angle for sharp corner int inner16 = int(16*inner*DPR); int outer16 = int(16*outer*DPR); p.save(); p.setPen( Qt::NoPen ); p.drawPolygon( areaPoints() ); p.restore(); initPainter( p ); // Left line p.drawLine( int(_x), int(_y+roundSize/2), int(_x), int(_y+h-roundSize/2) ); // Right line p.drawLine( int(_x+w), int(_y-slantIndent+h-roundSize/2), int(_x+w), int(_y+slantIndent+roundSize/2) ); // Bottom line p.drawLine( int(_x+(1-std::cos(outer))*(roundSize/2)), int(_y+h+(std::sin(outer)-1)*(roundSize/2)), int(_x+w+(std::cos(inner)-1)*(roundSize/2)), int(_y+h-slantIndent+(std::sin(inner)-1)*(roundSize/2)) ); // Top line p.drawLine( int(_x+w+(std::cos(outer)-1)*(roundSize/2)), int(_y+slantIndent+(1-std::sin(inner))*(roundSize/2)), int(_x+(1-std::cos(inner))*(roundSize/2)), int(_y+(1-std::sin(outer))*(roundSize/2)) ); // Top left p.drawArc( int(_x), int(_y), int(roundSize), int(roundSize), 90*16, outer16 ); // Bottom left p.drawArc( int(_x), int(_y+h-roundSize), int(roundSize), int(roundSize), 180*16, outer16 ); // Top right p.drawArc( int(_x+w-roundSize), int(_y+slantIndent), int(roundSize), int(roundSize), 0, inner16 ); // Bottom right p.drawArc( int(_x+w-roundSize), int(_y-slantIndent+h-roundSize), int(roundSize), int(roundSize), 270*16, inner16 ); deinitPainter( p ); } void Component::initDIP( const QStringList & pins ) { const int numPins = pins.size(); const int numSide = numPins/2 + numPins%2; // Pins along left for ( int i=0; i( p_icnDocument->nodeWithID( nodeId(ecNodeId) ) ); } void Component::slotUpdateConfiguration() { const LogicConfig logicConfig = LogicIn::getConfig(); const ElementMapList::iterator end = m_elementMapList.end(); for ( ElementMapList::iterator it = m_elementMapList.begin(); it != end; ++it ) { if ( LogicIn * logicIn = dynamic_cast((*it).e) ) logicIn->setLogic(logicConfig); } } BJT *Component::createBJT( ECNode *c, ECNode *b, ECNode *e, bool isNPN ) { return createBJT( c->pin(), b->pin(), e->pin(), isNPN ); } Capacitance *Component::createCapacitance( ECNode *n0, ECNode *n1, double capacitance ) { return createCapacitance( n0->pin(), n1->pin(), capacitance ); } CCCS *Component::createCCCS( ECNode *n0, ECNode *n1, ECNode *n2, ECNode *n3, double gain ) { return createCCCS( n0->pin(), n1->pin(), n2->pin(), n3->pin(), gain ); } CCVS *Component::createCCVS( ECNode *n0, ECNode *n1, ECNode *n2, ECNode *n3, double gain ) { return createCCVS( n0->pin(), n1->pin(), n2->pin(), n3->pin(), gain ); } CurrentSignal *Component::createCurrentSignal( ECNode *n0, ECNode *n1, double current ) { return createCurrentSignal( n0->pin(), n1->pin(), current ); } CurrentSource *Component::createCurrentSource( ECNode *n0, ECNode *n1, double current ) { return createCurrentSource( n0->pin(), n1->pin(), current ); } Diode *Component::createDiode( ECNode *n0, ECNode *n1 ) { return createDiode( n0->pin(), n1->pin() ); } JFET *Component::createJFET( ECNode * D, ECNode * G, ECNode * S, int JFET_type ) { return createJFET( D->pin(), G->pin(), S->pin(), JFET_type ); } Inductance *Component::createInductance( ECNode *n0, ECNode *n1, double inductance ) { return createInductance( n0->pin(), n1->pin(), inductance ); } LogicIn *Component::createLogicIn( ECNode *node ) { return createLogicIn( node->pin() ); } LogicOut *Component::createLogicOut( ECNode *node, bool isHigh ) { return createLogicOut( node->pin(), isHigh ); } MOSFET *Component::createMOSFET( ECNode * D, ECNode * G, ECNode * S, ECNode * B, int MOSFET_type ) { return createMOSFET( D->pin(), G->pin(), S->pin(), B ? B->pin() : nullptr, MOSFET_type ); } OpAmp *Component::createOpAmp( ECNode * nonInverting, ECNode * out, ECNode * inverting ) { return createOpAmp( nonInverting->pin(), out->pin(), inverting->pin() ); } Resistance *Component::createResistance( ECNode *n0, ECNode *n1, double resistance ) { return createResistance( n0->pin(), n1->pin(), resistance ); } Switch *Component::createSwitch( ECNode *n0, ECNode *n1, bool open ) { return createSwitch( n0->pin(), n1->pin(), open ); } VCCS *Component::createVCCS( ECNode *n0, ECNode *n1, ECNode *n2, ECNode *n3, double gain ) { return createVCCS( n0->pin(), n1->pin(), n2->pin(), n3->pin(), gain ); } VCVS *Component::createVCVS( ECNode *n0, ECNode *n1, ECNode *n2, ECNode *n3, double gain ) { return createVCVS( n0->pin(), n1->pin(), n2->pin(), n3->pin(), gain ); } VoltagePoint *Component::createVoltagePoint( ECNode *n0, double voltage ) { return createVoltagePoint( n0->pin(), voltage ); } VoltageSignal *Component::createVoltageSignal( ECNode *n0, ECNode *n1, double voltage ) { return createVoltageSignal( n0->pin(), n1->pin(), voltage ); } VoltageSource *Component::createVoltageSource( ECNode *n0, ECNode *n1, double voltage ) { return createVoltageSource( n0->pin(), n1->pin(), voltage ); } BJT* Component::createBJT( Pin *cN, Pin *bN, Pin *eN, bool isNPN ) { BJT *e = new BJT(isNPN); QList pins; pins << bN << cN << eN; ElementMapList::iterator it = handleElement( e, pins ); setInterDependent( it, pins ); return e; } Capacitance* Component::createCapacitance( Pin *n0, Pin *n1, double capacitance ) { Capacitance *e = new Capacitance( capacitance, LINEAR_UPDATE_PERIOD ); QList pins; pins << n0 << n1; ElementMapList::iterator it = handleElement( e, pins ); setInterDependent( it, pins ); return e; } CCCS* Component::createCCCS( Pin *n0, Pin *n1, Pin *n2, Pin *n3, double gain ) { CCCS *e = new CCCS(gain); QList pins; pins << n0 << n1 << n2 << n3; ElementMapList::iterator it = handleElement( e, pins ); setInterDependent( it, pins ); return e; } CCVS* Component::createCCVS( Pin *n0, Pin *n1, Pin *n2, Pin *n3, double gain ) { CCVS *e = new CCVS(gain); QList pins; pins << n0 << n1 << n2 << n3; ElementMapList::iterator it = handleElement( e, pins ); setInterCircuitDependent( it, pins ); pins.clear(); pins << n0 << n1; setInterGroundDependent( it, pins ); pins.clear(); pins << n2 << n3; setInterGroundDependent( it, pins ); return e; } CurrentSignal* Component::createCurrentSignal( Pin *n0, Pin *n1, double current ) { CurrentSignal *e = new CurrentSignal( LINEAR_UPDATE_PERIOD, current ); QList pins; pins << n0 << n1; ElementMapList::iterator it = handleElement( e, pins ); setInterDependent( it, pins ); return e; } CurrentSource* Component::createCurrentSource( Pin *n0, Pin *n1, double current ) { CurrentSource *e = new CurrentSource(current); QList pins; pins << n0 << n1; ElementMapList::iterator it = handleElement( e, pins ); setInterDependent( it, pins ); return e; } Diode* Component::createDiode( Pin *n0, Pin *n1 ) { Diode *e = new Diode(); QList pins; pins << n0 << n1; ElementMapList::iterator it = handleElement( e, pins ); setInterDependent( it, pins ); return e; } JFET * Component::createJFET( Pin * D, Pin * G, Pin * S, int JFET_type ) { JFET * e = new JFET( (JFET::JFET_type) JFET_type ); QList pins; pins << D << G << S; ElementMapList::iterator it = handleElement( e, pins ); setInterDependent( it, pins ); return e; } Inductance* Component::createInductance( Pin *n0, Pin *n1, double inductance ) { Inductance *e = new Inductance( inductance, LINEAR_UPDATE_PERIOD ); QList pins; pins << n0 << n1; ElementMapList::iterator it = handleElement( e, pins ); setInterDependent( it, pins ); return e; } LogicIn *Component::createLogicIn( Pin *node ) { LogicIn *e = new LogicIn(LogicIn::getConfig()); QList pins; pins << node; ElementMapList::iterator it = handleElement( e, pins ); return e; } LogicOut *Component::createLogicOut( Pin *node, bool isHigh ) { LogicOut *e = new LogicOut( LogicIn::getConfig(), isHigh); QList pins; pins << node; ElementMapList::iterator it = handleElement( e, pins ); setInterDependent( it, pins ); return e; } MOSFET * Component::createMOSFET( Pin * D, Pin * G, Pin * S, Pin * B, int MOSFET_type ) { MOSFET * e = new MOSFET( (MOSFET::MOSFET_type) MOSFET_type ); QList pins; pins << D << G << S << B; /// \todo remove the following line removing body if null pins.removeAll(nullptr); ElementMapList::iterator it = handleElement( e, pins ); setInterDependent( it, pins ); return e; } OpAmp * Component::createOpAmp( Pin * nonInverting, Pin * inverting, Pin * out ) { OpAmp * e = new OpAmp(); QList pins; pins << nonInverting << inverting << out; ElementMapList::iterator it = handleElement( e, pins ); setInterDependent( it, pins ); return e; } Resistance* Component::createResistance( Pin *n0, Pin *n1, double resistance ) { Resistance *e = new Resistance(resistance); QList pins; pins << n0 << n1; ElementMapList::iterator it = handleElement( e, pins ); setInterDependent( it, pins ); return e; } Switch* Component::createSwitch( Pin *n0, Pin *n1, bool open ) { // Note that a Switch is not really an element (although in many cases it // behaves very much like one). Switch * e = new Switch( this, n0, n1, open ? Switch::Open : Switch::Closed ); m_switchList.append(e); n0->addSwitch( e ); n1->addSwitch( e ); emit switchCreated( e ); return e; } VCCS* Component::createVCCS( Pin *n0, Pin *n1, Pin *n2, Pin *n3, double gain ) { VCCS *e = new VCCS(gain); QList pins; pins << n0 << n1 << n2 << n3; ElementMapList::iterator it = handleElement( e, pins ); setInterDependent( it, pins ); return e; } VCVS* Component::createVCVS( Pin *n0, Pin *n1, Pin *n2, Pin *n3, double gain ) { VCVS *e = new VCVS(gain); QList pins; pins << n0 << n1 << n2 << n3; ElementMapList::iterator it = handleElement( e, pins ); setInterCircuitDependent( it, pins ); pins.clear(); pins << n0 << n1; setInterGroundDependent( it, pins ); pins.clear(); pins << n2 << n3; setInterGroundDependent( it, pins ); return e; } VoltagePoint* Component::createVoltagePoint( Pin *n0, double voltage ) { VoltagePoint *e = new VoltagePoint(voltage); QList pins; pins << n0; ElementMapList::iterator it = handleElement( e, pins ); setInterDependent( it, pins ); return e; } VoltageSignal* Component::createVoltageSignal( Pin *n0, Pin *n1, double voltage ) { VoltageSignal *e = new VoltageSignal(LINEAR_UPDATE_PERIOD, voltage ); QList pins; pins << n0 << n1; ElementMapList::iterator it = handleElement( e, pins ); setInterDependent( it, pins ); return e; } VoltageSource* Component::createVoltageSource( Pin *n0, Pin *n1, double voltage ) { VoltageSource *e = new VoltageSource(voltage); QList pins; pins << n0 << n1; ElementMapList::iterator it = handleElement( e, pins ); setInterDependent( it, pins ); return e; } ElementMapList::iterator Component::handleElement( Element *e, const QList & pins ) { if (!e) return m_elementMapList.end(); ElementMap em; em.e = e; int at = 0; QList::ConstIterator end = pins.end(); for ( QList::ConstIterator it = pins.begin(); it != end; ++it ) { (*it)->addElement(e); em.n[at++] = *it; } //ElementMapList::iterator it = m_elementMapList.append(em); ElementMapList::iterator it = m_elementMapList.insert(m_elementMapList.end(), em); emit elementCreated(e); return it; } void Component::setInterDependent( ElementMapList::iterator it, const QList & pins ) { setInterCircuitDependent( it, pins ); setInterGroundDependent( it, pins ); } void Component::setInterCircuitDependent( ElementMapList::iterator it, const QList & pins ) { QList::ConstIterator end = pins.end(); for ( QList::ConstIterator it1 = pins.begin(); it1 != end; ++it1 ) { for ( QList::ConstIterator it2 = pins.begin(); it2 != end; ++it2 ) { (*it1)->addCircuitDependentPin( *it2 ); } } (*it).interCircuitDependent.append( pins ); } void Component::setInterGroundDependent( ElementMapList::iterator it, const QList & pins ) { QList::ConstIterator end = pins.end(); for ( QList::ConstIterator it1 = pins.begin(); it1 != end; ++it1 ) { for ( QList::ConstIterator it2 = pins.begin(); it2 != end; ++it2 ) { (*it1)->addGroundDependentPin( *it2 ); } } (*it).interGroundDependent.append( pins ); } void Component::rebuildPinInterDepedence() { setAllPinsInterIndependent(); // Rebuild dependencies ElementMapList::iterator emlEnd = m_elementMapList.end(); for ( ElementMapList::iterator it = m_elementMapList.begin(); it != emlEnd; ++it ) { // Many copies of the pin lists as these will be affected when we call setInter*Dependent PinListList list = (*it).interCircuitDependent; PinListList::iterator depEnd = list.end(); for ( PinListList::iterator depIt = list.begin(); depIt != depEnd; ++depIt ) setInterCircuitDependent( it, *depIt ); list = (*it).interGroundDependent; depEnd = list.end(); for ( PinListList::iterator depIt = list.begin(); depIt != depEnd; ++depIt ) setInterGroundDependent( it, *depIt ); } } void Component::setAllPinsInterIndependent() { NodeInfoMap::iterator nmEnd = m_nodeMap.end(); for ( NodeInfoMap::iterator it = m_nodeMap.begin(); it != nmEnd; ++it ) { //PinVector pins = (static_cast(it.value().node))->pins(); ECNode *node = dynamic_cast(it.value().node); if (!node) { qWarning() << Q_FUNC_INFO << "skipping not-ECNode node: " << it.value().node; continue; } PinVector pins = node->pins(); PinVector::iterator pinsEnd = pins.end(); for ( PinVector::iterator pinsIt = pins.begin(); pinsIt != pinsEnd; ++pinsIt ) { if ( *pinsIt ) (*pinsIt)->removeDependentPins(); } } } void Component::initElements( const uint stage ) { /// @todo this function is ugly and messy and needs tidying up const ElementMapList::iterator end = m_elementMapList.end(); if ( stage == 1 ) { for ( ElementMapList::iterator it = m_elementMapList.begin(); it != end; ++it ) { (*it).e->add_initial_dc(); } return; } for ( ElementMapList::iterator it = m_elementMapList.begin(); it != end; ++it ) { ElementMap m = (*it); if ( m.n[3] ) { m.e->setCNodes( m.n[0]->eqId(), m.n[1]->eqId(), m.n[2]->eqId(), m.n[3]->eqId() ); } else if ( m.n[2] ) { m.e->setCNodes( m.n[0]->eqId(), m.n[1]->eqId(), m.n[2]->eqId() ); } else if ( m.n[1] ) { m.e->setCNodes( m.n[0]->eqId(), m.n[1]->eqId() ); } else if ( m.n[0] ) { m.e->setCNodes( m.n[0]->eqId() ); } } } ECNode *Component::createPin( double x, double y, int orientation, const QString & name ) { return dynamic_cast( createNode( x, y, orientation, name, Node::ec_pin ) ); } // static double Component::voltageLength( double v ) { double v_max = 1e+1; double v_min = 1e-1; v = std::abs( v ); if ( v >= v_max ) return 1.0; else if ( v <= v_min ) return 0.0; else return std::log( v / v_min ) / std::log( v_max / v_min ); } // static QColor Component::voltageColor( double v ) { double prop = voltageLength( v ); if ( v >= 0 ) return QColor( int(255*prop), int(166*prop), 0 ); else return QColor( 0, int(136*prop), int(255*prop) ); } //BEGIN class ElementMap ElementMap::ElementMap() { e = nullptr; for ( int i = 0; i < 4; ++i ) n[i] = nullptr; } //END class ElementMap diff --git a/src/electronics/component.h b/src/electronics/component.h index 9fc4ee8a..cbd52782 100644 --- a/src/electronics/component.h +++ b/src/electronics/component.h @@ -1,382 +1,382 @@ /*************************************************************************** * Copyright (C) 2003 by David Saxton * * david@bluehaze.org * * * * 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. * ***************************************************************************/ #ifndef COMPONENT_H #define COMPONENT_H #include "cnitem.h" #include "circuitdocument.h" -#include +#include class ICNDocument; class CircuitDocument; class ECNode; class ECSubcircuit; class Element; class Node; class Pin; class BJT; class Capacitance; class CCCS; class CCVS; class CurrentSignal; class CurrentSource; class Diode; class JFET; class Inductance; class LogicIn; class LogicOut; class MOSFET; class OpAmp; class Resistance; class Switch; class Transformer; class VCCS; class VCVS; class VoltagePoint; class VoltageSignal; class VoltageSource; typedef QList ECNodeList; typedef QList ElementList; typedef QList SwitchList; typedef QList< QList > PinListList; /** Contains vital information about the elements in the component. */ class ElementMap { public: ElementMap(); Element * e; // The element Pin * n[4]; // The Pins associated with the CNodes in the element /// @see Component::setInterCircuitDependent PinListList interCircuitDependent; /// @see Component::setInterGroundDependent PinListList interGroundDependent; }; typedef QList ElementMapList; /** @short Base class for all electrical components @author David Saxton */ class Component : public CNItem { Q_OBJECT public: Component( ICNDocument *icnDocument, bool newItem, const QString &id ); ~Component() override; ECNode* createPin( double _x, double _y, int orientation, const QString &name ); /** * Converts the voltage level to a colour - this is used in drawing * wires and pins. */ static QColor voltageColor( double v ); /** * @return a value between 0.0 and 1.0, representing a scaled version of * the absolute value of the voltage. * @see voltageColor */ static double voltageLength( double v ); /** * Angle of orientation */ int angleDegrees() const { return m_angleDegrees; } /** * Sets the angle (in degrees) */ void setAngleDegrees( int degrees ); /** * Whether or not the item is flipped */ bool flipped() const { return b_flipped; } /** * Sets whether or not the item is flipped */ void setFlipped( bool flipped ); /** * After calculating nodal voltages, each component will be * called to tell its nodes what the current flowing *into* * the component is. */ void setNodalCurrents(); /** * @return pointer to the CircuitDocument that we're in. */ CircuitDocument *circuitDocument() const { return m_pCircuitDocument; } void initElements( const uint stage ); void finishedCreation() override; /** * If reinherit (and use) the stepNonLogic function, then you must also * reinherit this function so that it returns true. Else your component * will not get called. */ virtual bool doesStepNonLogic() const { return false; } virtual void stepNonLogic() {}; /** * Returns the translation matrix used for painting et al * @param angleDegrees The orientation to use * @param x x co-ordinate of the center of the object to be mapped * @param y y co-ordinate of the center of the object to be mapped * @param inverse If false, maps the unrotated item to a rotated one, else mapped->unmapped */ static QMatrix transMatrix( int angleDegrees, bool flipped, int x, int y, bool inverse = false ); /** * @return Information about the component in an ItemData struct. */ ItemData itemData() const override; /** * Restores the state of the component from the ItemData struct. */ void restoreFromItemData( const ItemData &itemData ) override; BJT * createBJT( Pin *c, Pin *b, Pin *e, bool isNPN = true ); BJT * createBJT( ECNode *c, ECNode *b, ECNode *e, bool isNPN = true ); Capacitance *createCapacitance( Pin *n0, Pin *n1, double capacitance ); Capacitance *createCapacitance( ECNode *n0, ECNode *n1, double capacitance ); CCCS * createCCCS( Pin *n0, Pin *n1, Pin *n2, Pin *n3, double gain ); CCCS * createCCCS( ECNode *n0, ECNode *n1, ECNode *n2, ECNode *n3, double gain ); CCVS * createCCVS( Pin *n0, Pin *n1, Pin *n2, Pin *n3, double gain ); CCVS * createCCVS( ECNode *n0, ECNode *n1, ECNode *n2, ECNode *n3, double gain ); CurrentSignal *createCurrentSignal( Pin *n0, Pin *n1, double current ); CurrentSignal *createCurrentSignal( ECNode *n0, ECNode *n1, double current ); CurrentSource *createCurrentSource( Pin *n0, Pin *n1, double current ); CurrentSource *createCurrentSource( ECNode *n0, ECNode *n1, double current ); Diode * createDiode( Pin *n0, Pin *n1 ); Diode * createDiode( ECNode *n0, ECNode *n1 ); JFET * createJFET( Pin * D, Pin * G, Pin * S, int JFET_type ); JFET * createJFET( ECNode * D, ECNode * G, ECNode * S, int JFET_type ); Inductance * createInductance( Pin *n0, Pin *n1, double inductance ); Inductance * createInductance( ECNode *n0, ECNode *n1, double inductance ); LogicIn * createLogicIn( Pin *node ); LogicIn * createLogicIn( ECNode *node ); LogicOut * createLogicOut( Pin *node, bool isHigh ); LogicOut * createLogicOut( ECNode *node, bool isHigh ); MOSFET * createMOSFET( Pin * D, Pin * G, Pin * S, Pin * B, int MOSFET_type ); MOSFET * createMOSFET( ECNode * D, ECNode * G, ECNode * S, ECNode * B, int MOSFET_type ); OpAmp * createOpAmp( Pin * nonInverting, Pin * out, Pin * inverting ); OpAmp * createOpAmp( ECNode * nonInverting, ECNode * out, ECNode * inverting ); Resistance * createResistance( Pin *n0, Pin *n1, double resistance ); Resistance * createResistance( ECNode *n0, ECNode *n1, double resistance ); Switch * createSwitch( Pin *n0, Pin *n1, bool open ); Switch * createSwitch( ECNode *n0, ECNode *n1, bool open ); VCCS * createVCCS( Pin *n0, Pin *n1, Pin *n2, Pin *n3, double gain ); VCCS * createVCCS( ECNode *n0, ECNode *n1, ECNode *n2, ECNode *n3, double gain ); VCVS * createVCVS( Pin *n0, Pin *n1, Pin *n2, Pin *n3, double gain ); VCVS * createVCVS( ECNode *n0, ECNode *n1, ECNode *n2, ECNode *n3, double gain ); VoltagePoint * createVoltagePoint( Pin *n0, double voltage ); VoltagePoint * createVoltagePoint( ECNode *n0, double voltage ); VoltageSignal * createVoltageSignal( Pin *n0, Pin *n1, double voltage ); VoltageSignal * createVoltageSignal( ECNode *n0, ECNode *n1, double voltage ); VoltageSource * createVoltageSource( Pin *n0, Pin *n1, double voltage ); VoltageSource * createVoltageSource( ECNode *n0, ECNode *n1, double voltage ); ECNode* ecNodeWithID( const QString &ecNodeId ); /** * Safely delete an element - in this case, calls element->componentDeleted, * and removes it from the element list. * @param setPinsInterIndependent whether to call * setPinsInterIndependent. The call is time-consuming, and unnecessary * if the pins from which the element was originally attached will be/ * were removed, or they will become interdependent again. */ void removeElement( Element * element, bool setPinsInterIndependent ); /** * Safely remove a switch. */ void removeSwitch( Switch * sw ); /** * Removes all elements and switches. * @param setPinsInterIndependent whether to bother calling * setPinsInterIndependent. This is false when calling from the * destructor, or when the dependency information is the same. */ void removeElements( bool setPinsInterIndependent = false ); /** * @return the list of switches that this component uses. */ SwitchList switchList() const { return m_switchList; } signals: /** * Emitted when an element is created. */ void elementCreated( Element * element ); /** * Emitted when an element is destroyed. */ void elementDestroyed( Element * element ); /** * Emitted when a switch. is created */ void switchCreated( Switch * sw ); /** * Emitted when a switch is destroyed. */ void switchDestroyed( Switch * sw ); public slots: virtual void slotUpdateConfiguration(); void removeItem() override; protected: /** * Convenience functionality provided for components in a port shape * (such as ParallelPortComponent and SerialPortComponent). */ void drawPortShape( QPainter & p ); void itemPointsChanged() override; void updateAttachedPositioning() override; void initPainter( QPainter &p ) override; /** * Untranforms the painter from the matrix. This *must* be called after doing * initPainter( QPainter &p ); */ virtual void deinitPainter( QPainter &p ); /** * This creates a set of nodes with their internal IDs set to those in QStringList pins. * The pins are in a DIP arrangement, and are spaced width() apart. */ void initDIP( const QStringList & pins ); /** * Creates the DIP symbol: * @li constructs rectangular shape * @li puts on text labels in appropriate positions from QStringList pins */ void initDIPSymbol( const QStringList & pins, int width ); /** * Create 1 pin on the left of the component, placed half way down if h1 is * -1 - else at the position of h1. */ void init1PinLeft( int h1 = -1 ); /** * Create 2 pins on the left of the component, either spread out, or at the * given heights. */ void init2PinLeft( int h1 = -1, int h2 = -1 ); /** * Create 3 pins on the left of the component, either spread out, or at the * given heights. */ void init3PinLeft( int h1 = -1, int h2 = -1, int h3 = -1 ); /** * Create 4 pins on the left of the component, either spread out, or at the * given heights. */ void init4PinLeft( int h1 = -1, int h2 = -1, int h3 = -1, int h4 = -1 ); /** * Create 1 pin on the right of the component, placed half way down if h1 is * -1 - else at the position of h1. */ void init1PinRight( int h1 = -1 ); /** * Create 2 pins on the right of the component, either spread out, or at the * given heights. */ void init2PinRight( int h1 = -1, int h2 = -1 ); /** * Create 3 pins on the right of the component, either spread out, or at the * given heights. */ void init3PinRight( int h1 = -1, int h2 = -1, int h3 = -1 ); /** * Create 4 pins on the right of the component, either spread out, or at the * given heights. */ void init4PinRight( int h1 = -1, int h2 = -1, int h3 = -1, int h4 = -1 ); /** * When we remove an element, we have to rebuild the list of inter-dependent * nodes. (when adding elements, we just call setInterDependent). */ void rebuildPinInterDepedence(); // Pointers to commonly used nodes // TODO: why do we have two sets of these? ECNode *m_pPNode[4]; ECNode *m_pNNode[4]; // TODO: only Switch cares about this, so either demote it to a member of class switch or // refactor it out alltogether. QPointer m_pCircuitDocument; int m_angleDegrees; bool b_flipped; private: /** * Convenience function for calling both setInterCircuitDependent and * setInterGroundDependent. * @param it Which pins are inter-dependent needs to be recorded in case * this information is later needed in rebuildPinInterDependence. */ void setInterDependent( ElementMapList::iterator it, const QList & pins ); /** * Sets all pins independent of each other. */ void setAllPinsInterIndependent(); /** * The given pins will affect the simulation of each other. Therefore, they * will need to be simulated in the same circuit. */ void setInterCircuitDependent( ElementMapList::iterator it, const QList & pins ); /** * If any of the given pins are ground, then that will affect whether * any of the other pins can be ground. */ void setInterGroundDependent( ElementMapList::iterator it, const QList & pins ); /** * List of ElementMaps; which contain information on the pins associated * with the element as well as the dependence between the pins for that * element. * @see ElementMap */ ElementMapList m_elementMapList; /** * The switches used by the component. TODO: ammend this comment with a more complete justification for the design decision to put this here. */ SwitchList m_switchList; /** * @return an iterator to the element in m_elementMapList */ ElementMapList::iterator handleElement( Element *e, const QList & pins ); }; #endif diff --git a/src/electronics/componentmodellibrary.cpp b/src/electronics/componentmodellibrary.cpp index d5a16f6e..bc9be021 100644 --- a/src/electronics/componentmodellibrary.cpp +++ b/src/electronics/componentmodellibrary.cpp @@ -1,179 +1,179 @@ /*************************************************************************** * Copyright (C) 2006 David Saxton * * * * 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 "componentmodellibrary.h" -#include -#include -#include +#include +#include +#include #include #include // A prime number slightly larger than the number of models for any particular type // const int maxComponentModels = 101; //BEGIN class ComponentModel ComponentModel::ComponentModel() { } ComponentModel::~ComponentModel() { } double ComponentModel::property( const QString & name ) const { return m_property[ name ]; } void ComponentModel::setProperty( const QString & name, double value ) { m_property[ name ] = value; } //END class ComponentModel //BEGIN class ComponentModelLibrary ComponentModelLibrary * ComponentModelLibrary::m_pSelf = nullptr; // static ComponentModelLibrary * ComponentModelLibrary::self() { if ( !m_pSelf ) m_pSelf = new ComponentModelLibrary; return m_pSelf; } ComponentModelLibrary::ComponentModelLibrary() { loadModels(); } ComponentModelLibrary::~ComponentModelLibrary() { } void ComponentModelLibrary::loadModels() { QTime ct; ct.start(); QStringList files; files << "transistors_lib.txt"; // Used to check that maxComponentModels isn't too small typedef QMap< ModelType, int > IntMap; IntMap modelCount; QStringList::iterator end = files.end(); for ( QStringList::iterator it = files.begin(); it != end; ++it ) { QString fileName = QStandardPaths::locate( QStandardPaths::AppDataLocation, "models/" + *it ); if ( fileName.isEmpty() ) { qWarning() << Q_FUNC_INFO << "Could not find library file \""<<*it<<"\".\n"; continue; } QFile file( fileName ); if ( !file.open( QIODevice::ReadOnly ) ) { qWarning() << Q_FUNC_INFO << "Could not open library file \""< maxComponentModels ) // 2018.08.14 - not needed { qWarning() << Q_FUNC_INFO << "There are "< -#include +#include Item* BusSplitter::construct( ItemDocument *itemDocument, bool newItem, const char *id ) { return new BusSplitter( (ICNDocument*)itemDocument, newItem, id ); } LibraryItem* BusSplitter::libraryItem() { return new LibraryItem( QStringList(QString("ec/bus")), i18n("Bus"), i18n("Connections"), "bus.png", LibraryItem::lit_component, BusSplitter::construct ); } const unsigned MAX_BUS_SIZE = 10000; BusSplitter::BusSplitter( ICNDocument *icnDocument, bool newItem, const char *id ) : Component( icnDocument, newItem, id ? id : "Bus" ) { m_name = i18n("Bus Splitter"); m_busSize = 0; init1PinLeft(); m_pInNode = m_pNNode[0]; createProperty( "size", Variant::Type::Int ); property("size")->setCaption( i18n("Size") ); property("size")->setMinValue(1); property("size")->setMaxValue(MAX_BUS_SIZE); property("size")->setValue(8); } BusSplitter::~BusSplitter() { } void BusSplitter::dataChanged() { unsigned busSize = dataInt("size"); if ( busSize < 1 ) busSize = 1; else if ( busSize > MAX_BUS_SIZE ) busSize = MAX_BUS_SIZE; if ( busSize == m_busSize ) return; m_pInNode->setNumPins(busSize); if ( busSize > m_busSize ) { m_pWires.resize(busSize); for ( unsigned i = m_busSize; i < unsigned(busSize); i++ ) { Pin * pin = createPin( 16, 0, 180, outNodeID(i) )->pin(); m_pWires[i] = new Wire( m_pInNode->pin(i), pin ); } } else { for ( unsigned i = busSize; i < unsigned(m_busSize); i++ ) { removeNode( outNodeID(i) ); delete m_pWires[i]; } m_pWires.resize(busSize); } m_busSize = busSize; // Position pins setSize( 0, -int(m_busSize+1)*8, 8, int(m_busSize+1)*16, true ); for ( int i = 0; i < int(m_busSize); i++ ) m_nodeMap[ outNodeID(i) ].y = 16*i - int(m_busSize+1)*8 + 24; m_nodeMap["n1"].y = -int(m_busSize+1)*8 + 8; updateAttachedPositioning(); } QString BusSplitter::outNodeID( unsigned node ) const { return QString("out_%1").arg(QString::number(node)); } void BusSplitter::drawShape( QPainter &p ) { initPainter(p); // QPen pen(p.pen()); // pen.setWidth(); // p.setPen(pen); int _x = int(x()); int _y = int(y()); QRect r = m_sizeRect; r.translate( _x, _y ); p.drawRect(r); deinitPainter(p); } diff --git a/src/electronics/components/bussplitter.h b/src/electronics/components/bussplitter.h index 1ec71afa..9e92b024 100644 --- a/src/electronics/components/bussplitter.h +++ b/src/electronics/components/bussplitter.h @@ -1,43 +1,43 @@ /*************************************************************************** * Copyright (C) 2005 by David Saxton * * david@bluehaze.org * * * * 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. * ***************************************************************************/ #ifndef BUSSPLITTER_H #define BUSSPLITTER_H #include -#include +#include // #include class Wire; /** @author David Saxton */ class BusSplitter : public Component { public: BusSplitter( ICNDocument *icnDocument, bool newItem, const char *id = nullptr ); ~BusSplitter() override; static Item* construct( ItemDocument *itemDocument, bool newItem, const char *id ); static LibraryItem *libraryItem(); protected: QString outNodeID( unsigned node ) const; void dataChanged() override; void drawShape( QPainter &p ) override; unsigned m_busSize; QVector > m_pWires; ECNode * m_pInNode; }; #endif diff --git a/src/electronics/components/capacitor.cpp b/src/electronics/components/capacitor.cpp index 6ba1d0df..b146652d 100644 --- a/src/electronics/components/capacitor.cpp +++ b/src/electronics/components/capacitor.cpp @@ -1,88 +1,88 @@ /*************************************************************************** * Copyright (C) 2003 by David Saxton * * david@bluehaze.org * * * * 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 "capacitance.h" #include "capacitor.h" #include "ecnode.h" #include "libraryitem.h" #include -#include +#include Item* Capacitor::construct( ItemDocument *itemDocument, bool newItem, const char *id ) { return new Capacitor( (ICNDocument*)itemDocument, newItem, id ); } LibraryItem* Capacitor::libraryItem() { return new LibraryItem( QStringList(QString("ec/capacitor")), i18n("Capacitor"), i18n("Passive"), "capacitor.png", LibraryItem::lit_component, Capacitor::construct ); } Capacitor::Capacitor( ICNDocument *icnDocument, bool newItem, const char *id ) : Component( icnDocument, newItem, id ? id : "capacitor" ) { m_name = i18n("Capacitor"); setSize( -8, -8, 16, 16 ); init1PinLeft(); init1PinRight(); m_capacitance = createCapacitance( m_pNNode[0], m_pPNode[0], 0.001 ); createProperty( "Capacitance", Variant::Type::Double ); property("Capacitance")->setCaption( i18n("Capacitance") ); property("Capacitance")->setUnit("F"); property("Capacitance")->setMinValue(1e-12); property("Capacitance")->setMaxValue(1e12); property("Capacitance")->setValue(1e-3); addDisplayText( "capacitance", QRect( -8, -24, 16, 16 ), "", false ); } Capacitor::~Capacitor() { } void Capacitor::dataChanged() { double capacitance = dataDouble("Capacitance"); QString display = QString::number( capacitance / getMultiplier(capacitance), 'g', 3 ) + getNumberMag(capacitance) + "F"; setDisplayText( "capacitance", display ); m_capacitance->setCapacitance(capacitance); } void Capacitor::drawShape( QPainter &p ) { initPainter(p); int _y = (int)y()-8; int _x = (int)x()-8; QPen pen; pen.setWidth(1); pen.setColor( p.pen().color() ); p.setPen(pen); p.drawRect( _x, _y, 5, 16 ); p.drawRect( _x+11, _y, 5, 16 ); deinitPainter(p); } diff --git a/src/electronics/components/dependentsource.cpp b/src/electronics/components/dependentsource.cpp index 07b8d01a..bf4e1df1 100644 --- a/src/electronics/components/dependentsource.cpp +++ b/src/electronics/components/dependentsource.cpp @@ -1,319 +1,319 @@ /*************************************************************************** * Copyright (C) 2005 by David Saxton * * david@bluehaze.org * * * * 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 "dependentsource.h" #include "ecnode.h" #include "libraryitem.h" #include "pin.h" #include "cccs.h" #include "ccvs.h" #include "vccs.h" #include "vcvs.h" #include -#include +#include //BEGIN class DependentSource DependentSource::DependentSource( ICNDocument *icnDocument, bool newItem, const char *id ) : Component( icnDocument, newItem, id ) { setSize( -16, -16, 32, 32 ); init2PinLeft(); init2PinRight(); m_pNNode[1]->setLength( 13 ); m_pPNode[1]->setLength( 13 ); createProperty( "gain", Variant::Type::Double ); property("gain")->setCaption( i18n("Gain") ); property("gain")->setValue(1.0); addDisplayText( "gain", QRect( -16, -32, 32, 16 ), "" ); } DependentSource::~DependentSource() { } void DependentSource::drawOutline( QPainter & p ) { const int _x = (int)x()-16; const int _y = (int)y()-32; // Top rectangle p.drawRect( _x, _y+19, width(), 11 ); #if 0 p.save(); bool canSetCol = (p.pen().color() != Qt::color0) && (p.pen().color() != Qt::color1); // Bottom lines if (canSetCol) p.setPen( m_pNNode[1]->isSelected() ? m_selectedCol : Qt::black ); p.drawLine( _x, _y+40, _x+8, _y+40 ); // Left inny if (canSetCol) p.setPen( m_pPNode[1]->isSelected() ? m_selectedCol : Qt::black ); p.drawLine( _x+width(), _y+40, _x+24, _y+40 ); // Right inny p.restore(); #endif // Bottom diamond QPolygon pa4(4); pa4[0] = QPoint( _x+6, _y+40 ); pa4[1] = QPoint( _x+16, _y+32 ); pa4[2] = QPoint( _x+26, _y+40 ); pa4[3] = QPoint( _x+16, _y+48 ); p.drawPolygon(pa4); } void DependentSource::drawTopArrow( QPainter & p ) { const int _x = (int)x()-16; const int _y = (int)y()-32; if ( p.pen().color() == m_selectedCol ) p.setPen(Qt::black); if ( p.brush().color() == m_brushCol ) p.setBrush(Qt::black); p.drawLine( _x+8, _y+24, _x+24, _y+24 ); QPolygon pa3(3); pa3[0] = QPoint( _x+24, _y+24 ); pa3[1] = QPoint( _x+19, _y+21 ); pa3[2] = QPoint( _x+19, _y+27 ); p.drawPolygon(pa3); } void DependentSource::drawBottomArrow( QPainter & p ) { const int _x = (int)x()-16; const int _y = (int)y()-32; if ( p.pen().color() == m_selectedCol ) p.setPen(Qt::black); if ( p.brush().color() == m_brushCol ) p.setBrush(Qt::black); p.drawLine( _x+11, _y+40, _x+21, _y+40 ); QPolygon pa3(3); pa3[0] = QPoint( _x+21, _y+40 ); pa3[1] = QPoint( _x+16, _y+37 ); pa3[2] = QPoint( _x+16, _y+43 ); p.drawPolygon(pa3); } //END class DependentSource //BEGIN class ECCCCS Item* ECCCCS::construct( ItemDocument *itemDocument, bool newItem, const char *id ) { return new ECCCCS( (ICNDocument*)itemDocument, newItem, id ); } LibraryItem* ECCCCS::libraryItem() { return new LibraryItem( QStringList(QString("ec/cccs")), i18n("CCCS"), i18n("Sources"), "cccs.png", LibraryItem::lit_component, ECCCCS::construct ); } ECCCCS::ECCCCS( ICNDocument *icnDocument, bool newItem, const char *id ) : DependentSource( icnDocument, newItem, id ? id : "cccs" ) { m_name = i18n("Current Controlled Current Source"); m_cccs = createCCCS( m_pNNode[0], m_pPNode[0], m_pNNode[1], m_pPNode[1], 1. ); m_pNNode[1]->pin()->setGroundType( Pin::gt_medium ); } ECCCCS::~ECCCCS() { } void ECCCCS::dataChanged() { double gain = dataDouble("gain"); QString display = QString::number( gain / getMultiplier(gain), 'g', 3 ) + getNumberMag(gain) + QChar(' '); setDisplayText( "gain", display ); m_cccs->setGain(gain); } void ECCCCS::drawShape( QPainter &p ) { initPainter(p); drawOutline(p); drawTopArrow(p); drawBottomArrow(p); deinitPainter(p); } //END class ECCCCS //BEGIN class ECCCVS Item* ECCCVS::construct( ItemDocument *itemDocument, bool newItem, const char *id ) { return new ECCCVS( (ICNDocument*)itemDocument, newItem, id ); } LibraryItem* ECCCVS::libraryItem() { return new LibraryItem( QStringList(QString("ec/ccvs")), i18n("CCVS"), i18n("Sources"), "ccvs.png", LibraryItem::lit_component, ECCCVS::construct ); } ECCCVS::ECCCVS( ICNDocument *icnDocument, bool newItem, const char *id ) : DependentSource( icnDocument, newItem, id ? id : "ccvs" ) { m_name = i18n("Current Controlled Voltage Source"); m_ccvs = createCCVS( m_pNNode[0], m_pPNode[0], m_pNNode[1], m_pPNode[1], 1. ); m_pNNode[1]->pin()->setGroundType( Pin::gt_medium ); } ECCCVS::~ECCCVS() { } void ECCCVS::dataChanged() { double gain = dataDouble("gain"); QString display = QString::number( gain / getMultiplier(gain), 'g', 3 ) + getNumberMag(gain) + QChar(' '); setDisplayText( "gain", display ); m_ccvs->setGain(gain); } void ECCCVS::drawShape( QPainter &p ) { initPainter(p); drawOutline(p); drawTopArrow(p); deinitPainter(p); } //END class ECCCVS //BEGIN class ECVCCS Item* ECVCCS::construct( ItemDocument *itemDocument, bool newItem, const char *id ) { return new ECVCCS( (ICNDocument*)itemDocument, newItem, id ); } LibraryItem* ECVCCS::libraryItem() { return new LibraryItem( QStringList(QString("ec/vccs")), i18n("VCCS"), i18n("Sources"), "vccs.png", LibraryItem::lit_component, ECVCCS::construct ); } ECVCCS::ECVCCS( ICNDocument *icnDocument, bool newItem, const char *id ) : DependentSource( icnDocument, newItem, id ? id : "vccs" ) { m_name = i18n("Voltage Controlled Current Source"); m_vccs = createVCCS( m_pNNode[0], m_pPNode[0], m_pNNode[1], m_pPNode[1], 1. ); m_pNNode[1]->pin()->setGroundType( Pin::gt_medium ); } ECVCCS::~ECVCCS() { } void ECVCCS::dataChanged() { double gain = dataDouble("gain"); QString display = QString::number( gain / getMultiplier(gain), 'g', 3 ) + getNumberMag(gain) + QChar(' '); setDisplayText( "gain", display ); m_vccs->setGain(gain); } void ECVCCS::drawShape( QPainter &p ) { initPainter(p); drawOutline(p); drawBottomArrow(p); deinitPainter(p); } //END class ECVCCS //BEGIN class ECVCVS Item* ECVCVS::construct( ItemDocument *itemDocument, bool newItem, const char *id ) { return new ECVCVS( (ICNDocument*)itemDocument, newItem, id ); } LibraryItem* ECVCVS::libraryItem() { return new LibraryItem( QStringList(QString("ec/vcvs")), i18n("VCVS"), i18n("Sources"), "vcvs.png", LibraryItem::lit_component, ECVCVS::construct ); } ECVCVS::ECVCVS( ICNDocument *icnDocument, bool newItem, const char *id ) : DependentSource( icnDocument, newItem, id ? id : "vcvs" ) { m_name = i18n("Voltage Controlled Voltage Source"); m_vcvs = createVCVS( m_pNNode[0], m_pPNode[0], m_pNNode[1], m_pPNode[1], 1. ); m_pNNode[1]->pin()->setGroundType( Pin::gt_medium ); } ECVCVS::~ECVCVS() { } void ECVCVS::dataChanged() { double gain = dataDouble("gain"); QString display = QString::number( gain / getMultiplier(gain), 'g', 3 ) + getNumberMag(gain) + QChar(' '); setDisplayText( "gain", display ); m_vcvs->setGain(gain); } void ECVCVS::drawShape( QPainter &p ) { initPainter(p); drawOutline(p); deinitPainter(p); } //END class ECVCVS diff --git a/src/electronics/components/discretelogic.cpp b/src/electronics/components/discretelogic.cpp index 16facde1..6b0dfbbf 100644 --- a/src/electronics/components/discretelogic.cpp +++ b/src/electronics/components/discretelogic.cpp @@ -1,303 +1,303 @@ /*************************************************************************** * Copyright (C) 2005 by David Saxton * * david@bluehaze.org * * * * 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 "canvasitemparts.h" #include "discretelogic.h" #include "ecnode.h" #include "logic.h" #include "libraryitem.h" #include "simulator.h" #include -#include +#include //BEGIN class Inverter Item* Inverter::construct( ItemDocument *itemDocument, bool newItem, const char *id ) { return new Inverter( (ICNDocument*)itemDocument, newItem, id ); } LibraryItem* Inverter::libraryItem() { QStringList ids; ids << "ec/inverter" << "ec/not"; return new LibraryItem( ids, i18n("Inverter"), i18n("Logic"), "not.png", LibraryItem::lit_component, Inverter::construct ); } Inverter::Inverter( ICNDocument *icnDocument, bool newItem, const char *id ) : Component( icnDocument, newItem, id ? id : "not" ) { m_name = i18n("Inverter"); setSize( -8, -8, 16, 16 ); init1PinLeft(); init1PinRight(); m_pIn = createLogicIn(m_pNNode[0]); m_pOut = createLogicOut( m_pPNode[0], true ); m_pIn->setCallback( this, (CallbackPtr)(&Inverter::inStateChanged) ); inStateChanged(false); } Inverter::~Inverter() { } void Inverter::inStateChanged( bool newState ) { (static_cast(m_pOut))->setHigh( !newState ); } void Inverter::drawShape( QPainter &p ) { initPainter(p); int _x = (int)x()-8; int _y = (int)y()-8; QPolygon pa(3); pa[0] = QPoint( _x, _y ); pa[1] = QPoint( _x+width()-6, _y+(height()/2) ); pa[2] = QPoint( _x, _y+height() ); p.drawPolygon(pa); p.drawPolyline(pa); p.drawEllipse( _x+width()-6, _y+(height()/2)-3, 7, 7 ); deinitPainter(p); } //END class Inverter //BEGIN class Buffer Item* Buffer::construct( ItemDocument *itemDocument, bool newItem, const char *id ) { return new Buffer( (ICNDocument*)itemDocument, newItem, id ); } LibraryItem* Buffer::libraryItem() { return new LibraryItem( QStringList(QString("ec/buffer")), i18n("Buffer"), i18n("Logic"), "buffer.png", LibraryItem::lit_component, Buffer::construct ); } Buffer::Buffer( ICNDocument *icnDocument, bool newItem, const char *id ) : Component( icnDocument, newItem, id ? id : "buffer" ) { m_name = i18n("Buffer"); setSize( -8, -8, 16, 16 ); init1PinLeft(); init1PinRight(); m_pIn = createLogicIn(m_pNNode[0]); m_pOut = createLogicOut( m_pPNode[0], true ); m_pIn->setCallback( this, (CallbackPtr)(&Buffer::inStateChanged) ); inStateChanged(false); } Buffer::~Buffer() { } void Buffer::inStateChanged( bool newState ) { m_pOut->setHigh(newState); } void Buffer::drawShape( QPainter &p ) { initPainter(p); int _x = (int)x()-8; int _y = (int)y()-8; QPolygon pa(3); pa[0] = QPoint( _x, _y ); pa[1] = QPoint( _x+width(), _y+(height()/2) ); pa[2] = QPoint( _x, _y+height() ); p.drawPolygon(pa); p.drawPolyline(pa); deinitPainter(p); } //END class Buffer //BEGIN class ECLogicInput Item* ECLogicInput::construct( ItemDocument *itemDocument, bool newItem, const char *id ) { return new ECLogicInput( (ICNDocument*)itemDocument, newItem, id ); } LibraryItem* ECLogicInput::libraryItem() { return new LibraryItem( QStringList(QString("ec/logic_input")), i18n("Logic Input"), i18n("Logic"), "logic_input.png", LibraryItem::lit_component, ECLogicInput::construct ); } ECLogicInput::ECLogicInput( ICNDocument *icnDocument, bool newItem, const char *id ) : Component( icnDocument, newItem, (id) ? id : "logic_input" ) { m_name = i18n("Logic Input"); setSize( -8, -8, 16, 16 ); b_state = false; addButton( "button", QRect( -24, -8, 16, 16 ), "", true ); createProperty( "useToggle", Variant::Type::Bool ); property("useToggle")->setCaption( i18n("Use Toggle") ); property("useToggle")->setValue(true); init1PinRight(); m_pOut = createLogicOut( m_pPNode[0], false ); } ECLogicInput::~ECLogicInput() { } void ECLogicInput::dataChanged() { button("button")->setToggle( dataBool("useToggle") ); } void ECLogicInput::drawShape( QPainter &p ) { initPainter(p); if (b_state) p.setBrush( QColor( 255, 166, 0 ) ); else p.setBrush( Qt::white ); p.drawEllipse( (int)x()-4, (int)y()-6, 12, 12 ); deinitPainter(p); } void ECLogicInput::buttonStateChanged( const QString &, bool state ) { b_state = state; m_pOut->setHigh(b_state); } //END class ECLogicInput //BEGIN class ECLogicOutput Item* ECLogicOutput::construct( ItemDocument *itemDocument, bool newItem, const char *id ) { return new ECLogicOutput( (ICNDocument*)itemDocument, newItem, id ); } LibraryItem* ECLogicOutput::libraryItem() { return new LibraryItem( QStringList(QString("ec/logic_output")), i18n("Logic Output"), i18n("Logic"), "logic_output.png", LibraryItem::lit_component, ECLogicOutput::construct ); } ECLogicOutput::ECLogicOutput( ICNDocument *icnDocument, bool newItem, const char *id ) : Component( icnDocument, newItem, id ? id : "logic_output" ) { m_name = i18n("Logic Output"); setSize( -8, -8, 16, 16 ); init1PinLeft(); m_pIn = createLogicIn(m_pNNode[0]); m_pSimulator = Simulator::self(); m_lastDrawState = 0.0; m_lastSwitchTime = m_lastDrawTime = m_pSimulator->time(); m_highTime = 0; m_bLastState = false; m_bDynamicContent = true; m_pIn->setCallback( this, (CallbackPtr)(&ECLogicOutput::inStateChanged) ); } ECLogicOutput::~ECLogicOutput() { } void ECLogicOutput::inStateChanged( bool newState ) { if ( m_bLastState == newState ) return; unsigned long long newTime = m_pSimulator->time(); unsigned long long dt = newTime - m_lastSwitchTime; m_lastSwitchTime = newTime; m_bLastState = newState; if (!newState) { // Gone from high to low m_highTime += dt; } } void ECLogicOutput::drawShape( QPainter &p ) { unsigned long long newTime = m_pSimulator->time(); unsigned long long runTime = newTime - m_lastDrawTime; m_lastDrawTime = newTime; if (m_bLastState) { // Logic in is currently high m_highTime += newTime - m_lastSwitchTime; } double state; if ( runTime == 0 ) state = m_lastDrawState; else state = m_lastDrawState = double(m_highTime)/double(runTime); initPainter(p); p.setBrush( QColor( 255, uint(255-state*(255-166)), uint((1-state)*255) ) ); p.drawEllipse( int(x()-8), int(y()-8), width(), height() ); deinitPainter(p); m_lastSwitchTime = newTime; m_highTime = 0; } //END class ECLogicOutput diff --git a/src/electronics/components/ec555.cpp b/src/electronics/components/ec555.cpp index b468d825..7edce62f 100644 --- a/src/electronics/components/ec555.cpp +++ b/src/electronics/components/ec555.cpp @@ -1,152 +1,152 @@ /*************************************************************************** * Copyright (C) 2003 by David Saxton * * david@bluehaze.org * * * * 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 "ec555.h" #include "ecnode.h" #include "libraryitem.h" #include "pin.h" #include "resistance.h" #include -#include +#include Item* EC555::construct( ItemDocument *itemDocument, bool newItem, const char *id ) { return new EC555( (ICNDocument*)itemDocument, newItem, id ); } LibraryItem* EC555::libraryItem() { return new LibraryItem( QStringList(QString("ec/555")), i18n("555"), i18n("Integrated Circuits"), "ic1.png", LibraryItem::lit_component, EC555::construct ); } EC555::EC555( ICNDocument *icnDocument, bool newItem, const char *id ) : Component( icnDocument, newItem, (id) ? id : "555" ) { m_name = i18n("555"); // m_pins = QStringList::split( ',', "Gnd,Trg,Out,Res,CV,Th,Dis,Vcc" ); // m_pins = QStringList::split( ',', "Dis,Th,Trg,Gnd,CV,Out,Res,Vcc" ); old_com1 = false; old_com2 = false; old_q = false; setSize( -32, -32, 64, 64 ); // Pins down left // Pin 7 discharge = createPin( -40, -16, 0, "Dis" )->pin(); addDisplayText( "dis", QRect( -32, -24, 24, 16 ), "Dis" ); // Pin 6 threshold = createPin( -40, 0, 0, "Th" )->pin(); addDisplayText( "th", QRect( -32, -8, 24, 16 ), "Th" ); // Pin 2 trigger = createPin( -40, 16, 0, "Trg" )->pin(); addDisplayText( "trg", QRect( -32, 8, 24, 16 ), "Trg" ); // Top two // Pin 8 vcc = createPin( -16, -40, 90, "Vcc" )->pin(); addDisplayText( "vcc", QRect( -24, -32, 16, 8 ), "+" ); // Pin 4 reset = createPin( 16, -40, 90, "Res" )->pin(); addDisplayText( "res", QRect( 8, -28, 16, 16 ), "Res" ); // Bottom two // Pin 1 ground = createPin( -16, 40, 270, "Gnd" )->pin(); addDisplayText( "gnd", QRect( -24, 20, 16, 8 ), "-" ); // Pin 5 control = createPin( 16, 40, 270, "CV" )->pin(); addDisplayText( "cv", QRect( 8, 12, 16, 16 ), "CV" ); // Output on right // Pin 3 output = createPin( 40, 0, 180, "Out" )->pin(); addDisplayText( "out", QRect( 8, -8, 16, 16 ), "Out" ); m_r1 = createResistance( vcc, control, 5e3 ); m_r23 = createResistance( control, ground, 1e4 ); m_po_sink = createResistance( output, ground, 0. ); m_po_source = createResistance( output, vcc, 0. ); m_po_source->setConductance(0.); m_r_discharge = createResistance( discharge, ground, 0. ); } EC555::~EC555() { } // TODO: This is simulation code not UI code, so it shouldn't be here. // Would it be better to simulate the appropriate elements, ie comparator, voltage divider, // and flip-flop instead of all this hand-wavy logic? void EC555::stepNonLogic() { double v_threshold = threshold->voltage(); double v_control = control->voltage(); double v_ground = ground->voltage(); double v_trigger = trigger->voltage(); double v_reset = reset->voltage(); double v_vcc = vcc->voltage(); double v_r = (v_control+v_ground)/2; bool com1 = (v_threshold == v_control ) ? old_com1 : ( v_threshold < v_control ); bool com2 = (v_r == v_trigger ) ? old_com2 : ( v_r > v_trigger ); bool reset = v_reset >= (v_control - v_ground) / 2 + v_ground; old_com1 = com1; old_com2 = com2; bool r = !(reset && com1); bool s = com2; bool q = old_q; if ( v_vcc - v_ground >= 2.5 ) { if ( s && !r ) { q = true; } else if ( r && !s ) { q = false; } } else { q = false; } old_q = q; m_r_discharge->setConductance(0.); if (q) { m_po_source->setResistance(10.); m_po_sink->setConductance(0.); } else { m_po_source->setConductance(0.); m_po_sink->setResistance(10.); if ( v_ground+0.7 <= v_vcc ) { m_r_discharge->setResistance(10.); } } } diff --git a/src/electronics/components/ec555.h b/src/electronics/components/ec555.h index 9fd43b74..9fa49a07 100644 --- a/src/electronics/components/ec555.h +++ b/src/electronics/components/ec555.h @@ -1,55 +1,55 @@ /*************************************************************************** * Copyright (C) 2003 by David Saxton * * david@bluehaze.org * * * * 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. * ***************************************************************************/ #ifndef EC555_H #define EC555_H #include "component.h" -#include +#include /** @short 555 IC @author David Saxton */ class EC555 : public Component { public: EC555( ICNDocument *icnDocument, bool newItem, const char *id = nullptr ); ~EC555() override; static Item* construct( ItemDocument *itemDocument, bool newItem, const char *id ); static LibraryItem *libraryItem(); void stepNonLogic() override; bool doesStepNonLogic() const override { return true; } private: Pin * ground; Pin * trigger; Pin * output; Pin * reset; Pin * control; Pin * threshold; Pin * discharge; Pin * vcc; Resistance *m_r1; Resistance *m_r23; Resistance *m_po_sink; Resistance *m_po_source; Resistance *m_r_discharge; bool old_com1; bool old_com2; bool old_q; }; #endif diff --git a/src/electronics/components/ecbjt.cpp b/src/electronics/components/ecbjt.cpp index f9394ac3..65c603c2 100644 --- a/src/electronics/components/ecbjt.cpp +++ b/src/electronics/components/ecbjt.cpp @@ -1,152 +1,152 @@ /*************************************************************************** * Copyright (C) 2003-2005 by David Saxton * * david@bluehaze.org * * * * 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 "bjt.h" #include "ecbjt.h" #include "ecnode.h" #include "libraryitem.h" #include -#include +#include Item * ECBJT::constructNPN( ItemDocument * itemDocument, bool newItem, const char * id ) { return new ECBJT( true, (ICNDocument*)itemDocument, newItem, id ); } Item * ECBJT::constructPNP( ItemDocument * itemDocument, bool newItem, const char * id ) { return new ECBJT( false, (ICNDocument*)itemDocument, newItem, id ); } LibraryItem* ECBJT::libraryItemNPN() { return new LibraryItem( QStringList(QString("ec/npnbjt")), i18n("NPN"), i18n("Discrete"), "npn.png", LibraryItem::lit_component, ECBJT::constructNPN ); } LibraryItem* ECBJT::libraryItemPNP() { return new LibraryItem( QStringList(QString("ec/pnpbjt")), i18n("PNP"), i18n("Discrete"), "pnp.png", LibraryItem::lit_component, ECBJT::constructPNP ); } ECBJT::ECBJT( bool isNPN, ICNDocument * icnDocument, bool newItem, const char * id ) : Component( icnDocument, newItem, id ? id : (isNPN ? "npnbjt" : "pnpbjt") ) { m_bIsNPN = isNPN; if ( m_bIsNPN ) m_name = i18n("NPN Transistor"); else m_name = i18n("PNP Transistor"); setSize( -8, -8, 16, 16 ); m_pBJT = createBJT( createPin( 8, -16, 90, "c" ), createPin( -16, 0, 0, "b" ), createPin( 8, 16, 270, "e" ), m_bIsNPN ); BJTSettings s; // will be created with the default settings Variant * v = createProperty( "I_S", Variant::Type::Double ); v->setCaption( i18n("Saturation Current") ); v->setUnit("A"); v->setMinValue(1e-20); v->setMaxValue(1e-0); v->setValue( s.I_S ); v->setAdvanced(true); v = createProperty( "N_F", Variant::Type::Double ); v->setCaption( i18n("Forward Coefficient") ); v->setMinValue(1e0); v->setMaxValue(1e1); v->setValue( s.N_F ); v->setAdvanced(true); v = createProperty( "N_R", Variant::Type::Double ); v->setCaption( i18n("Reverse Coefficient") ); v->setMinValue(1e0); v->setMaxValue(1e1); v->setValue( s.N_R ); v->setAdvanced(true); v = createProperty( "B_F", Variant::Type::Double ); v->setCaption( i18n("Forward Beta") ); v->setMinValue(1e-1); v->setMaxValue(1e3); v->setValue( s.B_F ); v->setAdvanced(true); v = createProperty( "B_R", Variant::Type::Double ); v->setCaption( i18n("Reverse Beta") ); v->setMinValue(1e-1); v->setMaxValue(1e3); v->setValue( s.B_R ); v->setAdvanced(true); } ECBJT::~ECBJT() { } void ECBJT::dataChanged() { BJTSettings s; s.I_S = dataDouble( "I_S" ); s.N_F = dataDouble( "N_F" ); s.N_R = dataDouble( "N_R" ); s.B_F = dataDouble( "B_F" ); s.B_R = dataDouble( "B_R" ); m_pBJT->setBJTSettings( s ); } void ECBJT::drawShape( QPainter &p ) { const int _x = int(x()); const int _y = int(y()); initPainter(p); p.drawLine( _x-8, _y-8, _x-8, _y+8 ); p.drawLine( _x+8, _y-8, _x-8, _y ); p.drawLine( _x+8, _y+8, _x-8, _y ); QPolygon pa(3); if ( m_bIsNPN ) { pa[0] = QPoint( _x+6, _y+7 ); pa[1] = QPoint( _x+2, _y+8 ); pa[2] = QPoint( _x+5, _y+3 ); } else { pa[0] = QPoint( _x-7, _y+1 ); pa[1] = QPoint( _x-4, _y+5 ); pa[2] = QPoint( _x-2, _y ); } p.setBrush( p.pen().color() ); p.drawPolygon(pa); deinitPainter(p); } diff --git a/src/electronics/components/ecclockinput.cpp b/src/electronics/components/ecclockinput.cpp index d522a57a..828bd3a1 100644 --- a/src/electronics/components/ecclockinput.cpp +++ b/src/electronics/components/ecclockinput.cpp @@ -1,205 +1,205 @@ /*************************************************************************** * Copyright (C) 2003-2005 by David Saxton * * david@bluehaze.org * * * * 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 "ecclockinput.h" #include "logic.h" #include "libraryitem.h" #include "simulator.h" #include -#include +#include -#include +#include #include using namespace std; // was a constant, this is my guess for an appropriate name. //#define TIME_INTERVAL 100 // 2015.09.27 - added proper constant to simulator class static inline uint roundDouble( const double x ) { return uint(std::floor(x+0.5)); } Item* ECClockInput::construct( ItemDocument *itemDocument, bool newItem, const char *id ) { return new ECClockInput( (ICNDocument*)itemDocument, newItem, id ); } LibraryItem* ECClockInput::libraryItem() { return new LibraryItem( QStringList(QString("ec/clock_input")), i18n("Clock Input"), i18n("Logic"), "clockinput.png", LibraryItem::lit_component, ECClockInput::construct ); } ECClockInput::ECClockInput( ICNDocument *icnDocument, bool newItem, const char *id ) : Component( icnDocument, newItem, (id) ? id : "clock_input" ) { m_name = i18n("Clock Input"); setSize( -16, -8, 32, 16 ); m_lastSetTime = 0; m_time = 0; m_high_time = 0; m_low_time = 0; m_period = 0; m_bSetStepCallbacks = true; m_pSimulator = Simulator::self(); for ( unsigned i = 0; i < LOGIC_UPDATE_PER_STEP; i++ ) { ComponentCallback * ccb = new ComponentCallback( this, (VoidCallbackPtr)(&ECClockInput::stepCallback) ); m_pComponentCallback[i] = new list; m_pComponentCallback[i]->push_back(*ccb); } init1PinRight(); m_pOut = createLogicOut( m_pPNode[0], false ); createProperty( "low-time", Variant::Type::Double ); property("low-time")->setUnit("S"); property("low-time")->setCaption( i18n("Low Time") ); property("low-time")->setMinValue(1.0/LOGIC_UPDATE_RATE); property("low-time")->setValue(0.5); createProperty( "high-time", Variant::Type::Double ); property("high-time")->setUnit("S"); property("high-time")->setCaption( i18n("High Time") ); property("high-time")->setMinValue(1.0/LOGIC_UPDATE_RATE); property("high-time")->setValue(0.5); addDisplayText( "freq", QRect( -16, -24, 32, 14 ), "", false ); } ECClockInput::~ECClockInput() { for ( unsigned i = 0; i < LOGIC_UPDATE_PER_STEP; i++ ) delete m_pComponentCallback[i]; } void ECClockInput::dataChanged() { m_high_time = roundDouble(dataDouble("high-time") * LOGIC_UPDATE_RATE); m_low_time = roundDouble(dataDouble("low-time") * LOGIC_UPDATE_RATE); m_period = m_low_time + m_high_time; const double frequency = 1. / (dataDouble("high-time") + dataDouble("low-time")); QString display = QString::number( frequency / getMultiplier(frequency), 'g', 3 ) + getNumberMag(frequency) + "Hz"; setDisplayText( "freq", display ); bool setStepCallbacks = m_period > LOGIC_UPDATE_PER_STEP; if ( setStepCallbacks != m_bSetStepCallbacks ) { m_bSetStepCallbacks = setStepCallbacks; if (setStepCallbacks) { m_pSimulator->detachComponentCallbacks(*this); } else { m_pSimulator->attachComponentCallback( this, (VoidCallbackPtr)(&ECClockInput::stepLogic) ); } } m_bLastStepCallbackOut = false; m_lastSetTime = m_pSimulator->time(); if (m_lastSetTime < 0) { qWarning() << Q_FUNC_INFO << " m_lastSetTime = " << m_lastSetTime; } } void ECClockInput::stepLogic() { m_pOut->setHigh( m_time>m_low_time ); ++m_time; if ( m_time > m_period ) { m_time -= int(m_time / m_period) * m_period; } } void ECClockInput::stepCallback() { m_pOut->setHigh(m_bLastStepCallbackOut); m_bLastStepCallbackOut = !m_bLastStepCallbackOut; } void ECClockInput::stepNonLogic() { if (!m_bSetStepCallbacks) { return; } bool addingHigh = !m_bLastStepCallbackOut; // { // for testing // const long long remainingLogicSteps = m_pSimulator->time() % LOGIC_UPDATE_PER_STEP; // if (remainingLogicSteps != 0) { // qWarning() << Q_FUNC_INFO << "remainingLogicSteps should be 0, but got " << remainingLogicSteps; // } // } // TODO review this method long long lowerTime = m_pSimulator->time(); long long upperTime = lowerTime + LOGIC_UPDATE_PER_STEP; long long upTo = m_lastSetTime; while ( upTo + (addingHigh?m_high_time:m_low_time) < upperTime ) { upTo += addingHigh ? m_high_time : m_low_time; addingHigh = !addingHigh; long long at = upTo-lowerTime; if ( at >= 0 && at < LOGIC_UPDATE_PER_STEP ) m_pSimulator->addStepCallback( at, &m_pComponentCallback[at]->front()); } m_lastSetTime = upTo; if (m_lastSetTime < 0) { qWarning() << Q_FUNC_INFO << " m_lastSetTime = " << m_lastSetTime; } } void ECClockInput::drawShape( QPainter &p ) { initPainter(p); int _x = (int)x()-10; int _y = (int)y()-8; p.drawRect( _x-6, _y, 32, 16 ); p.drawLine( _x, _y+8, _x, _y+4 ); p.drawLine( _x, _y+4, _x+4, _y+4 ); p.drawLine( _x+4, _y+4, _x+4, _y+12 ); p.drawLine( _x+4, _y+12, _x+8, _y+12 ); p.drawLine( _x+8, _y+12, _x+8, _y+4 ); p.drawLine( _x+8, _y+4, _x+12, _y+4 ); p.drawLine( _x+12, _y+4, _x+12, _y+12 ); p.drawLine( _x+12, _y+12, _x+16, _y+12 ); p.drawLine( _x+16, _y+12, _x+16, _y+4 ); p.drawLine( _x+16, _y+4, _x+20, _y+4 ); p.drawLine( _x+20, _y+4, _x+20, _y+8 ); deinitPainter(p); } diff --git a/src/electronics/components/eccurrentsignal.cpp b/src/electronics/components/eccurrentsignal.cpp index ad4d85dd..098fdbc7 100644 --- a/src/electronics/components/eccurrentsignal.cpp +++ b/src/electronics/components/eccurrentsignal.cpp @@ -1,91 +1,91 @@ /*************************************************************************** * Copyright (C) 2003-2004 by David Saxton * * david@bluehaze.org * * * * 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 "currentsignal.h" #include "eccurrentsignal.h" #include "ecnode.h" #include "libraryitem.h" #include "pin.h" #include "simulator.h" #include -#include +#include Item* ECCurrentSignal::construct( ItemDocument *itemDocument, bool newItem, const char *id ) { return new ECCurrentSignal( (ICNDocument*)itemDocument, newItem, id ); } LibraryItem* ECCurrentSignal::libraryItem() { return new LibraryItem( QStringList(QString("ec/ac_current")), i18n("Current Signal"), i18n("Sources"), "currentsignal.png", LibraryItem::lit_component, ECCurrentSignal::construct ); } ECCurrentSignal::ECCurrentSignal( ICNDocument *icnDocument, bool newItem, const char *id ) : Component( icnDocument, newItem, id ? id : "current_signal" ) { m_name = i18n("Current Signal"); setSize( -8, -8, 16, 16 ); init1PinLeft(); init1PinRight(); m_pNNode[0]->pin()->setGroundType( Pin::gt_low ); m_currentSignal = createCurrentSignal( m_pNNode[0], m_pPNode[0], 0. ); m_currentSignal->setStep(ElementSignal::st_sinusoidal, 50. ); createProperty( "1-frequency", Variant::Type::Double ); property("1-frequency")->setCaption( i18n("Frequency") ); property("1-frequency")->setUnit("Hz"); property("1-frequency")->setMinValue(1e-9); property("1-frequency")->setMaxValue(1e3); property("1-frequency")->setValue(50.0); createProperty( "1-current", Variant::Type::Double ); property("1-current")->setCaption( i18n("Current Range") ); property("1-current")->setUnit("A"); property("1-current")->setMinValue(-1e12); property("1-current")->setMaxValue(1e12); property("1-current")->setValue(0.02); addDisplayText( "~", QRect( -8, -8, 16, 16 ), "~" ); addDisplayText( "current", QRect( -16, -24, 32, 16 ), "" ); } ECCurrentSignal::~ECCurrentSignal() { } void ECCurrentSignal::dataChanged() { const double current = dataDouble("1-current"); const double frequency = dataDouble("1-frequency"); QString display = QString::number( current / getMultiplier(current), 'g', 3 ) + getNumberMag(current) + "A"; setDisplayText( "current", display ); m_currentSignal->setStep(ElementSignal::st_sinusoidal, frequency ); m_currentSignal->setCurrent(current); } void ECCurrentSignal::drawShape( QPainter &p ) { initPainter(p); p.drawEllipse( (int)x()-8, (int)y()-8, width(), height() ); deinitPainter(p); } diff --git a/src/electronics/components/eccurrentsource.cpp b/src/electronics/components/eccurrentsource.cpp index 33de16d7..73d0746b 100644 --- a/src/electronics/components/eccurrentsource.cpp +++ b/src/electronics/components/eccurrentsource.cpp @@ -1,93 +1,93 @@ /*************************************************************************** * Copyright (C) 2003 by David Saxton * * david@bluehaze.org * * * * 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 "currentsource.h" #include "eccurrentsource.h" #include "ecnode.h" #include "libraryitem.h" #include "pin.h" #include -#include +#include Item* ECCurrentSource::construct( ItemDocument *itemDocument, bool newItem, const char *id ) { return new ECCurrentSource( (ICNDocument*)itemDocument, newItem, id ); } LibraryItem* ECCurrentSource::libraryItem() { return new LibraryItem( QStringList(QString("ec/current_source")), i18n("Current Source"), i18n("Sources"), "current_source.png", LibraryItem::lit_component, ECCurrentSource::construct ); } ECCurrentSource::ECCurrentSource( ICNDocument *icnDocument, bool newItem, const char *id ) : Component( icnDocument, newItem, id ? id : "current_source" ) { m_name = i18n("Current Source"); setSize( -16, -8, 24, 24 ); init1PinLeft(8); init1PinRight(8); m_pNNode[0]->pin()->setGroundType( Pin::gt_low ); m_currentSource = createCurrentSource( m_pNNode[0], m_pPNode[0], 0. ); createProperty( "current", Variant::Type::Double ); property("current")->setCaption( i18n("Current") ); property("current")->setUnit("A"); property("current")->setMinValue(-1e12); property("current")->setMaxValue(1e12); property("current")->setValue(0.02); addDisplayText("current", QRect( -16, -16, 24, 0 ), "" ); } ECCurrentSource::~ECCurrentSource() { } void ECCurrentSource::dataChanged() { double current = dataDouble("current"); m_currentSource->setCurrent(current); QString display = QString::number( current / getMultiplier(current), 'g', 3 ) + getNumberMag(current) + "A"; setDisplayText("current", display ); } void ECCurrentSource::drawShape( QPainter &p ) { initPainter(p); int _x = (int)x()-16; int _y = (int)y()-24; // Top arrow indicating current direction p.drawLine( _x+width(), _y+19, _x, _y+19 ); p.drawLine( _x+width(), _y+19, _x+width()-3, _y+16 ); p.drawLine( _x+width(), _y+19, _x+width()-3, _y+22 ); // Double circules p.drawEllipse( _x, _y+24, 16, 16 ); p.drawEllipse( _x+8, _y+24, 16, 16 ); deinitPainter(p); } diff --git a/src/electronics/components/ecdiode.cpp b/src/electronics/components/ecdiode.cpp index 3910246f..3dd4f5f2 100644 --- a/src/electronics/components/ecdiode.cpp +++ b/src/electronics/components/ecdiode.cpp @@ -1,119 +1,119 @@ /*************************************************************************** * Copyright (C) 2003,2005 by David Saxton * * david@bluehaze.org * * * * 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 "diode.h" #include "ecdiode.h" #include "ecnode.h" #include "libraryitem.h" #include -#include +#include Item* ECDiode::construct( ItemDocument *itemDocument, bool newItem, const char *id ) { return new ECDiode( (ICNDocument*)itemDocument, newItem, id ); } LibraryItem* ECDiode::libraryItem() { return new LibraryItem( QStringList(QString("ec/diode")), i18n("Diode"), i18n("Discrete"), "diode.png", LibraryItem::lit_component, ECDiode::construct ); } ECDiode::ECDiode( ICNDocument *icnDocument, bool newItem, const char *id ) : Component( icnDocument, newItem, id ? id : "diode" ) { m_name = i18n("Diode"); setSize( -8, -8, 16, 16 ); init1PinLeft(); init1PinRight(); m_diode = createDiode( m_pNNode[0], m_pPNode[0] ); DiodeSettings ds; // it will have the default properties that we use createProperty( "I_S", Variant::Type::Double ); property("I_S")->setCaption("Saturation Current"); property("I_S")->setUnit("A"); property("I_S")->setMinValue(1e-20); property("I_S")->setMaxValue(1e-0); property("I_S")->setValue( ds.I_S ); property("I_S")->setAdvanced(true); createProperty( "N", Variant::Type::Double ); property("N")->setCaption( i18n("Emission Coefficient") ); property("N")->setMinValue(1e0); property("N")->setMaxValue(1e1); property("N")->setValue( ds.N ); property("N")->setAdvanced(true); createProperty( "V_B", Variant::Type::Double ); property("V_B")->setCaption( i18n("Breakdown Voltage") ); property("V_B")->setUnit("V"); property("V_B")->setMinAbsValue(1e-5); property("V_B")->setMaxValue(1e10); property("V_B")->setValue( ds.V_B ); property("V_B")->setAdvanced(true); // createProperty( "R", Variant::Type::Double ); // property("R")->setCaption( i18n("Series Resistance") ); // property("R")->setUnit( QChar(0x3a9) ); // property("R")->setMinValue(1e-5); // property("R")->setMaxValue(1e0); // property("R")->setValue( ds.R ); // property("R")->setAdvanced(true); } ECDiode::~ECDiode() { } void ECDiode::dataChanged() { DiodeSettings ds; ds.I_S = dataDouble("I_S"); ds.V_B = dataDouble("V_B"); ds.N = dataDouble("N"); // ds.R = dataDouble("R"); m_diode->setDiodeSettings( ds ); } void ECDiode::drawShape( QPainter & p ) { initPainter(p); int _x = int(x()); int _y = int(y()); QPolygon pa(3); pa[0] = QPoint( 8, 0 ); pa[1] = QPoint( -8, -8 ); pa[2] = QPoint( -8, 8 ); pa.translate( _x, _y ); p.drawPolygon(pa); p.drawPolyline(pa); p.drawLine( _x+8, _y-8, _x+8, _y+8 ); deinitPainter(p); } diff --git a/src/electronics/components/ecfixedvoltage.cpp b/src/electronics/components/ecfixedvoltage.cpp index 57623549..20498b59 100644 --- a/src/electronics/components/ecfixedvoltage.cpp +++ b/src/electronics/components/ecfixedvoltage.cpp @@ -1,73 +1,73 @@ /*************************************************************************** * Copyright (C) 2003-2004 by David Saxton * * david@bluehaze.org * * * * 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 "ecfixedvoltage.h" #include "ecnode.h" #include "voltagepoint.h" #include "libraryitem.h" #include -#include +#include Item* ECFixedVoltage::construct( ItemDocument *itemDocument, bool newItem, const char *id ) { return new ECFixedVoltage( (ICNDocument*)itemDocument, newItem, id ); } LibraryItem* ECFixedVoltage::libraryItem() { return new LibraryItem( QStringList(QString("ec/fixed_voltage")), i18n("Fixed Voltage"), i18n("Sources"), "voltage.png", LibraryItem::lit_component, ECFixedVoltage::construct ); } ECFixedVoltage::ECFixedVoltage( ICNDocument *icnDocument, bool newItem, const char *id ) : Component( icnDocument, newItem, id ? id : "fixed_voltage" ) { m_name = i18n("Fixed Voltage"); setSize( -8, -8, 16, 16 ); init1PinRight(); m_pPNode[0]->setLength( 11 ); m_voltagePoint = createVoltagePoint( m_pPNode[0], 5.0 ); addDisplayText( "voltage", QRect( -24, -20, width()+32, 12 ), "" ); createProperty( "voltage", Variant::Type::Double ); property("voltage")->setUnit("V"); property("voltage")->setCaption( i18n("Voltage") ); property("voltage")->setMinValue(-1e15); property("voltage")->setMaxValue(1e15); property("voltage")->setValue(5.0); } ECFixedVoltage::~ECFixedVoltage() { } void ECFixedVoltage::dataChanged() { const double voltage = dataDouble("voltage"); QString display = QString::number( voltage / getMultiplier(voltage), 'g', 3 ) + getNumberMag(voltage) + "V"; setDisplayText( "voltage", display ); m_voltagePoint->setVoltage(voltage); } void ECFixedVoltage::drawShape( QPainter &p ) { initPainter( p ); p.drawEllipse( int(x()-4), int(y()-4), 9, 9 ); deinitPainter( p ); } diff --git a/src/electronics/components/ecground.cpp b/src/electronics/components/ecground.cpp index d22aff37..72dcc286 100644 --- a/src/electronics/components/ecground.cpp +++ b/src/electronics/components/ecground.cpp @@ -1,65 +1,65 @@ /*************************************************************************** * Copyright (C) 2003-2004 by David Saxton * * david@bluehaze.org * * * * 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 "ecground.h" #include "ecnode.h" #include "libraryitem.h" #include "pin.h" #include -#include +#include Item* ECGround::construct( ItemDocument *itemDocument, bool newItem, const char *id ) { return new ECGround( (ICNDocument*)itemDocument, newItem, id ); } LibraryItem* ECGround::libraryItem() { return new LibraryItem( QStringList(QString("ec/ground")), i18n("Ground (0V)"), i18n("Sources"), "ground.png", LibraryItem::lit_component, ECGround::construct ); } ECGround::ECGround( ICNDocument *icnDocument, bool newItem, const char *id ) : Component( icnDocument, newItem, (id) ? id : "ground" ) { m_name = i18n("Ground"); setSize( -8, -8, 16, 16 ); init1PinRight(); m_pPNode[0]->pin()->setGroundType( Pin::gt_always ); setAngleDegrees(270); } ECGround::~ECGround() { } void ECGround::drawShape( QPainter &p ) { initPainter(p); int _x = (int)x()-8; int _y = (int)y()-8; QPen pen; pen.setWidth(2); pen.setColor( p.pen().color() ); p.setPen(pen); p.drawLine( _x+15, _y, _x+15, _y+16 ); p.drawLine( _x+10, _y+3, _x+10, _y+13 ); p.drawLine( _x+5, _y+6, _x+5, _y+10 ); deinitPainter(p); } diff --git a/src/electronics/components/ecjfet.cpp b/src/electronics/components/ecjfet.cpp index c8e616f0..8f5b5546 100644 --- a/src/electronics/components/ecjfet.cpp +++ b/src/electronics/components/ecjfet.cpp @@ -1,168 +1,168 @@ /*************************************************************************** * Copyright (C) 2005 by David Saxton * * david@bluehaze.org * * * * 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 "ecjfet.h" #include "jfet.h" #include "libraryitem.h" #include -#include +#include Item * ECJFET::constructNJFET( ItemDocument * itemDocument, bool newItem, const char * id ) { return new ECJFET( JFET::nJFET, (ICNDocument*)itemDocument, newItem, id ? id : "njfet" ); } Item * ECJFET::constructPJFET( ItemDocument * itemDocument, bool newItem, const char * id ) { return new ECJFET( JFET::pJFET, (ICNDocument*)itemDocument, newItem, id ? id : "pjfet" ); } LibraryItem* ECJFET::libraryItemNJFET() { return new LibraryItem( QStringList(QString("ec/njfet")), // i18n("n JFET"), i18n("n-JFET"), i18n("Discrete"), "njfet.png", LibraryItem::lit_component, ECJFET::constructNJFET ); } LibraryItem* ECJFET::libraryItemPJFET() { return new LibraryItem( QStringList(QString("ec/pjfet")), // i18n("p JFET"), i18n("p-JFET"), i18n("Discrete"), "pjfet.png", LibraryItem::lit_component, ECJFET::constructPJFET ); } ECJFET::ECJFET( int JFET_type, ICNDocument * icnDocument, bool newItem, const char * id ) : Component( icnDocument, newItem, id ) { m_JFET_type = JFET_type; if ( JFET_type == JFET::nJFET ) m_name = i18n("N-Channel JFET"); else m_name = i18n("P-Channel JFET"); setSize( -8, -8, 16, 16 ); m_pJFET = createJFET( createPin( 8, -16, 90, "D" ), createPin( -16, 0, 0, "G" ), createPin( 8, 16, 270, "S" ), JFET_type ); JFETSettings s; // will be created with the default settings Variant * v = createProperty( "V_Th", Variant::Type::Double ); v->setCaption( i18n("Threshold voltage") ); v->setUnit("V"); v->setMinValue(-1e6); v->setMaxValue(1e6); v->setValue( s.V_Th ); v->setAdvanced( true ); v = createProperty( "beta", Variant::Type::Double ); v->setCaption( i18n("Transcondutance") ); v->setUnit(QString("A/V") + QChar(0xb2)); v->setMinValue(1e-12); v->setMaxValue(1e0); v->setValue( s.beta ); v->setAdvanced( true ); v = createProperty( "I_S", Variant::Type::Double ); v->setCaption( i18n("Saturation current") ); v->setUnit("A"); v->setMinValue(1e-20); v->setMaxValue(1e0); v->setValue( s.I_S ); v->setAdvanced( true ); v = createProperty( "N", Variant::Type::Double ); v->setCaption( i18n("PN emission coefficient") ); v->setUnit(""); v->setMinValue(0.0); v->setMaxValue(10.0); v->setValue( s.N ); v->setAdvanced( true ); v = createProperty( "N_R", Variant::Type::Double ); v->setCaption( i18n("Isr emission coefficient") ); v->setUnit(""); v->setMinValue(0.0); v->setMaxValue(10.0); v->setValue( s.N_R ); v->setAdvanced( true ); } ECJFET::~ECJFET() { } void ECJFET::dataChanged() { JFETSettings s; s.V_Th = dataDouble( "V_Th" ); s.beta = dataDouble( "beta" ); s.I_S = dataDouble( "I_S" ); s.N = dataDouble( "N" ); s.N_R = dataDouble( "N_R" ); m_pJFET->setJFETSettings( s ); } void ECJFET::drawShape( QPainter &p ) { const int _x = int(x()); const int _y = int(y()); initPainter( p ); // back lines p.drawLine( _x-8, _y, _x+2, _y ); p.drawLine( _x+2, _y-8, _x+2, _y+8 ); // top corner p.drawLine( _x+2, _y-5, _x+8, _y-5 ); p.drawLine( _x+8, _y-5, _x+8, _y-8 ); // bottom corner p.drawLine( _x+2, _y+5, _x+8, _y+5 ); p.drawLine( _x+8, _y+5, _x+8, _y+8 ); QPolygon pa(3); if ( m_JFET_type == JFET::nJFET ) { // right pointing arrow pa[0] = QPoint( 1, 0 ); pa[1] = QPoint( -4, -3 ); pa[2] = QPoint( -4, +3 ); } else { // left pointing arrow pa[0] = QPoint( -8, 0 ); pa[1] = QPoint( -3, -3 ); pa[2] = QPoint( -3, +3 ); } pa.translate( _x, _y ); p.setBrush( p.pen().color() ); p.drawPolygon( pa ); deinitPainter( p ); } diff --git a/src/electronics/components/ecmosfet.cpp b/src/electronics/components/ecmosfet.cpp index b2864cf1..6378d194 100644 --- a/src/electronics/components/ecmosfet.cpp +++ b/src/electronics/components/ecmosfet.cpp @@ -1,278 +1,279 @@ /*************************************************************************** * Copyright (C) 2005 by David Saxton * * david@bluehaze.org * * * * 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 "ecmosfet.h" #include "libraryitem.h" #include "mosfet.h" -#include #include -#include + +#include +#include Item * ECMOSFET::constructNEM( ItemDocument * itemDocument, bool newItem, const char * id ) { return new ECMOSFET( MOSFET::neMOSFET, (ICNDocument*)itemDocument, newItem, id ? id : "nemosfet" ); } Item * ECMOSFET::constructPEM( ItemDocument * itemDocument, bool newItem, const char * id ) { return new ECMOSFET( MOSFET::peMOSFET, (ICNDocument*)itemDocument, newItem, id ? id : "pemosfet" ); } #if 0 Item * ECMOSFET::constructNDM( ItemDocument * itemDocument, bool newItem, const char * id ) { return new ECMOSFET( MOSFET::ndMOSFET, (ICNDocument*)itemDocument, newItem, id ? id : "ndmosfet" ); } Item * ECMOSFET::constructPDM( ItemDocument * itemDocument, bool newItem, const char * id ) { return new ECMOSFET( MOSFET::pdMOSFET, (ICNDocument*)itemDocument, newItem, id ? id : "pdmosfet" ); } #endif LibraryItem* ECMOSFET::libraryItemNEM() { return new LibraryItem( QStringList(QString("ec/nemosfet")), // i18n("n-e MOSFET"), i18n("n-MOSFET"), i18n("Discrete"), "nemosfet.png", LibraryItem::lit_component, ECMOSFET::constructNEM ); } LibraryItem* ECMOSFET::libraryItemPEM() { return new LibraryItem( QStringList(QString("ec/pemosfet")), // i18n("p-e MOSFET"), i18n("p-MOSFET"), i18n("Discrete"), "pemosfet.png", LibraryItem::lit_component, ECMOSFET::constructPEM ); } #if 0 LibraryItem* ECMOSFET::libraryItemNDM() { return new LibraryItem( QStringList(QString("ec/ndmosfet")), i18n("n-d MOSFET"), i18n("Discrete"), "ndmosfet.png", LibraryItem::lit_component, ECMOSFET::constructNDM ); } LibraryItem* ECMOSFET::libraryItemPDM() { return new LibraryItem( QStringList(QString("ec/pdmosfet")), i18n("p-d MOSFET"), i18n("Discrete"), "pdmosfet.png", LibraryItem::lit_component, ECMOSFET::constructPDM ); } #endif ECMOSFET::ECMOSFET( int MOSFET_type, ICNDocument * icnDocument, bool newItem, const char * id ) : Component( icnDocument, newItem, id ) { m_MOSFET_type = MOSFET_type; switch ( (MOSFET::MOSFET_type) m_MOSFET_type ) { case MOSFET::neMOSFET: { m_name = i18n("N-Channel Enhancement MOSFET"); break; } case MOSFET::peMOSFET: { m_name = i18n("P-Channel Enhancement MOSFET"); break; } #if 0 case MOSFET::ndMOSFET: { m_name = i18n("N-Channel Depletion MOSFET"); break; } case MOSFET::pdMOSFET: { m_name = i18n("P-Channel Depletion MOSFET"); break; } #endif } setSize( -8, -16, 16, 32 ); ECNode * NodeS = createPin( 8, 24, 270, "s" ); m_pMOSFET = createMOSFET( createPin( 8, -24, 90, "d" ), createPin( -16, 8, 0, "g" ), NodeS, NodeS, m_MOSFET_type ); m_bHaveBodyPin = false; Variant * v = createProperty( "bodyPin", Variant::Type::Bool ); v->setCaption( i18nc( "mosfet body/bulk pin", "Body Pin") ); v->setValue( false ); #if 0 MOSFETSettings s; // will be created with the default settings v = createProperty( "I_S", Variant::Type::Double ); v->setCaption( i18n("Saturation Current") ); v->setUnit("A"); v->setMinValue(1e-20); v->setMaxValue(1e-0); v->setValue( s.I_S ); v->setAdvanced(true); v = createProperty( "N_F", Variant::Type::Double ); v->setCaption( i18n("Forward Coefficient") ); v->setMinValue(1e0); v->setMaxValue(1e1); v->setValue( s.N_F ); v->setAdvanced(true); v = createProperty( "N_R", Variant::Type::Double ); v->setCaption( i18n("Reverse Coefficient") ); v->setMinValue(1e0); v->setMaxValue(1e1); v->setValue( s.N_R ); v->setAdvanced(true); v = createProperty( "B_F", Variant::Type::Double ); v->setCaption( i18n("Forward Beta") ); v->setMinValue(1e-1); v->setMaxValue(1e3); v->setValue( s.B_F ); v->setAdvanced(true); v = createProperty( "B_R", Variant::Type::Double ); v->setCaption( i18n("Reverse Beta") ); v->setMinValue(1e-1); v->setMaxValue(1e3); v->setValue( s.B_R ); v->setAdvanced(true); #endif } ECMOSFET::~ECMOSFET() { } void ECMOSFET::dataChanged() { bool haveBodyPin = dataBool( "bodyPin" ); if ( haveBodyPin != m_bHaveBodyPin ) { m_bHaveBodyPin = haveBodyPin; if ( m_bHaveBodyPin ) { // Creating a body pin ECNode * NodeB = createPin( 16, 0, 180, "b" ); removeElement( m_pMOSFET, false ); m_pMOSFET = createMOSFET( ecNodeWithID( "d" ), ecNodeWithID( "g" ), ecNodeWithID( "s" ), NodeB, m_MOSFET_type ); } else { // Removing a body pin removeNode( "b" ); removeElement( m_pMOSFET, false ); m_pMOSFET = createMOSFET( ecNodeWithID( "d" ), ecNodeWithID( "g" ), ecNodeWithID( "s" ), ecNodeWithID( "s" ), m_MOSFET_type ); } } #if 0 MOSFETSettings s; s.I_S = dataDouble( "I_S" ); s.N_F = dataDouble( "N_F" ); s.N_R = dataDouble( "N_R" ); s.B_F = dataDouble( "B_F" ); s.B_R = dataDouble( "B_R" ); m_pMOSFET->setMOSFETSettings( s ); #endif } void ECMOSFET::drawShape( QPainter & p ) { const int _x = int(x()); const int _y = int(y()); initPainter(p); // Middle three horizontal lines p.drawLine( _x-3, _y-11, _x+8, _y-11 ); p.drawLine( _x-3, _y, _x+8, _y ); p.drawLine( _x-3, _y+11, _x+8, _y+11 ); // Right middle vertical line if ( m_bHaveBodyPin ) p.drawLine( _x+8, _y+11, _x+8, _y+16 ); else p.drawLine( _x+8, _y, _x+8, _y+16 ); // Right top vertical line p.drawLine( _x+8, _y-11, _x+8, _y-16 ); QPen pen = p.pen(); pen.setWidth( 2 ); p.setPen( pen ); // Back line p.drawLine( _x-7, _y-10, _x-7, _y+11 ); if ( m_MOSFET_type == MOSFET::neMOSFET || m_MOSFET_type == MOSFET::peMOSFET ) { // Middle three vertical lines p.drawLine( _x-2, _y-14, _x-2, _y-7 ); p.drawLine( _x-2, _y-3, _x-2, _y+4 ); p.drawLine( _x-2, _y+8, _x-2, _y+15 ); } else { // Middle vertical line p.drawLine( _x-3, _y-14, _x-3, _y+15 ); } QPolygon pa(3); if ( m_MOSFET_type == MOSFET::neMOSFET /*|| m_MOSFET_type == MOSFET::ndMOSFET*/ ) { // Inwards facing arrow pa[0] = QPoint( 0, 0 ); pa[1] = QPoint( 5, -3 ); pa[2] = QPoint( 5, 3 ); } else { // Outwards facing arrow pa[0] = QPoint( 2, -3 ); pa[1] = QPoint( 7, 0 ); pa[2] = QPoint( 2, 3 ); } pa.translate( _x, _y ); p.setPen( p.pen().color() ); p.setBrush( p.pen().color() ); p.drawPolygon( pa ); deinitPainter(p); } diff --git a/src/electronics/components/ecopamp.cpp b/src/electronics/components/ecopamp.cpp index c57ebf7e..3e8835ed 100644 --- a/src/electronics/components/ecopamp.cpp +++ b/src/electronics/components/ecopamp.cpp @@ -1,84 +1,84 @@ /*************************************************************************** * Copyright (C) 2005 by David Saxton * * david@bluehaze.org * * * * 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 "ecopamp.h" #include "ecnode.h" #include "libraryitem.h" #include -#include +#include Item* ECOpAmp::construct( ItemDocument *itemDocument, bool newItem, const char *id ) { return new ECOpAmp( (ICNDocument*)itemDocument, newItem, id ); } LibraryItem* ECOpAmp::libraryItem() { return new LibraryItem( QStringList(QString("ec/opamp")), i18n("Op Amp"), i18n("Integrated Circuits"), "opamp.png", LibraryItem::lit_component, ECOpAmp::construct ); } ECOpAmp::ECOpAmp( ICNDocument *icnDocument, bool newItem, const char *id ) : Component( icnDocument, newItem, id ? id : "opamp" ) { m_name = i18n("Operational Amplifier"); QPolygon pa(3); pa[0] = QPoint( -16, -16 ); pa[1] = QPoint( 16, 0 ); pa[2] = QPoint( -16, 16 ); setItemPoints( pa, true ); init2PinLeft( -8, 8 ); init1PinRight(); createOpAmp( m_pNNode[0], m_pPNode[0], m_pNNode[1] ); } ECOpAmp::~ECOpAmp() { } void ECOpAmp::drawShape( QPainter & p ) { initPainter(p); int _x = int(x()); int _y = int(y()); QPolygon pa(3); pa[0] = QPoint( _x-16, _y-16 ); pa[1] = QPoint( _x+16, _y ); pa[2] = QPoint( _x-16, _y+16 ); p.drawPolygon(pa); p.drawPolyline(pa); // Plus symbol p.drawLine( _x-9, _y-8, _x-9, _y-2 ); p.drawLine( _x-12, _y-5, _x-6, _y-5 ); // Minus symbol p.drawLine( _x-11, _y+6, _x-7, _y+6 ); deinitPainter(p); } diff --git a/src/electronics/components/ecpotentiometer.cpp b/src/electronics/components/ecpotentiometer.cpp index 9b08e72f..3c81fee5 100644 --- a/src/electronics/components/ecpotentiometer.cpp +++ b/src/electronics/components/ecpotentiometer.cpp @@ -1,118 +1,118 @@ /*************************************************************************** * Copyright (C) 2003-2005 by David Saxton * * david@bluehaze.org * * * * 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 "canvasitemparts.h" #include "ecnode.h" #include "ecpotentiometer.h" #include "libraryitem.h" #include "resistance.h" #include -#include -#include +#include +#include Item* ECPotentiometer::construct( ItemDocument *itemDocument, bool newItem, const char *id ) { return new ECPotentiometer( (ICNDocument*)itemDocument, newItem, id ); } LibraryItem* ECPotentiometer::libraryItem() { return new LibraryItem( QStringList(QString("ec/potentiometer")), i18n("Potentiometer"), i18n("Passive"), "potentiometer.png", LibraryItem::lit_component, ECPotentiometer::construct ); } ECPotentiometer::ECPotentiometer( ICNDocument *icnDocument, bool newItem, const char *id ) : Component( icnDocument, newItem, id ? id : "potentiometer" ) { m_name = i18n("Potentiometer"); setSize( -16, -16, 40, 32 ); m_p1 = createPin( 32, 0, 180, "p1" ); m_sliderProp = 0.0; m_resistance = 5000.; m_r1 = createResistance( createPin( -8, -24, 90, "n1" ), m_p1, 1. ); m_r2 = createResistance( createPin( -8, 24, 270, "n2" ), m_p1, 1. ); Slider * s = addSlider( "slider", 0, 100, 5, 50, Qt::Vertical, QRect( 0, -16, 16, 32 ) ); m_pSlider = static_cast(s->widget()); createProperty( "resistance", Variant::Type::Double ); property("resistance")->setCaption( i18n("Resistance") ); property("resistance")->setUnit( QChar(0x3a9) ); property("resistance")->setMinValue(1e-6); property("resistance")->setValue(1e5); addDisplayText( "res", QRect( -56, -8, 40, 16 ), "" ); } ECPotentiometer::~ECPotentiometer() { } void ECPotentiometer::dataChanged() { m_resistance = dataDouble("resistance"); QString display = QString::number( m_resistance / getMultiplier(m_resistance), 'g', 3 ) + getNumberMag(m_resistance) + QChar(0x3a9); setDisplayText( "res", display ); sliderValueChanged( "slider", slider("slider")->value() ); } void ECPotentiometer::sliderValueChanged( const QString &id, int newValue ) { if ( id != "slider" ) return; m_sliderProp = (newValue-50.0)/100.0; m_r1->setResistance( m_resistance*(double)newValue/100. ); m_r2->setResistance( m_resistance*(double)(100.-newValue)/100. ); } void ECPotentiometer::drawShape( QPainter &p ) { initPainter(p); int _x = int(x()); int _y = int(y()); p.drawRect( _x-14, _y-16, 12, 32 ); QPolygon pa(3); pa[0] = QPoint( 0, 0 ); pa[1] = QPoint( 4, -3 ); pa[2] = QPoint( 4, 3 ); int space = m_pSlider->style()->pixelMetric( QStyle::PM_SliderSpaceAvailable /*, m_pSlider TODO investigate parameter */ ); int base_y = _y + int( space * m_sliderProp ); pa.translate( _x+16, base_y ); QColor c = m_p1->isSelected() ? m_selectedCol : Qt::black; p.setPen(c); p.setBrush(c); p.drawPolygon(pa); p.drawLine( _x+20, base_y, _x+24, base_y ); p.drawLine( _x+24, base_y, _x+24, _y ); deinitPainter(p); } diff --git a/src/electronics/components/ecsevensegment.cpp b/src/electronics/components/ecsevensegment.cpp index b3fc014d..ae4011e3 100644 --- a/src/electronics/components/ecsevensegment.cpp +++ b/src/electronics/components/ecsevensegment.cpp @@ -1,210 +1,210 @@ /*************************************************************************** * Copyright (C) 2003-2005 by David Saxton * * david@bluehaze.org * * * * 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 "colorcombo.h" #include "diode.h" #include "led.h" #include "ecnode.h" #include "ecsevensegment.h" #include "libraryitem.h" #include "simulator.h" #include -#include -#include +#include +#include Item* ECSevenSegment::construct( ItemDocument *itemDocument, bool newItem, const char *id ) { return new ECSevenSegment( (ICNDocument*)itemDocument, newItem, id ); } LibraryItem* ECSevenSegment::libraryItem() { return new LibraryItem( QStringList(QString("ec/seven_segment")), i18n("Seven Segment"), i18n("Outputs"), "seven_segment.png", LibraryItem::lit_component, ECSevenSegment::construct ); } ECSevenSegment::ECSevenSegment( ICNDocument *icnDocument, bool newItem, const char *id ) : Component( icnDocument, newItem, id ? id : "seven_segment" ) { m_name = i18n("Seven Segment LED"); m_bDynamicContent = true; //QStringList pins = QStringList::split( ',', "g,f,e,d,"+QString(QChar(0xB7))+",c,b,a" ); QStringList pins = QString("g,f,e,d,"+QString(QChar(0xB7))+",c,b,a" ).split(','); createProperty( "0-color", Variant::Type::Color ); property("0-color")->setCaption( i18n("Color") ); property("0-color")->setColorScheme( ColorCombo::LED ); createProperty( "diode-polarity", Variant::Type::Select ); property("diode-polarity")->setCaption( i18n("Configuration") ); QStringMap allowed; allowed["Common Cathode"] = i18n("Common Cathode"); allowed["Common Anode"] = i18n("Common Anode"); property("diode-polarity")->setAllowed( allowed ); property("diode-polarity")->setValue("Common Cathode"); for ( int i=0; i<8; i++ ) { m_diodes[i] = nullptr; m_nodes[i] = nullptr; avg_brightness[i] = 0.; last_brightness[i] = 255; } m_nNode = nullptr; lastUpdatePeriod = 1.; initDIPSymbol( pins, 64 ); initDIP(pins); m_nNode = createPin( width()/2+offsetX(), height()+8+offsetY(), 270, "-v" ); for ( int i=0; i<7; i++ ) m_nodes[i] = ecNodeWithID( QChar('a'+i) ); m_nodes[7] = ecNodeWithID(QChar(0xB7)); m_bCommonCathode = false; // Force update } ECSevenSegment::~ECSevenSegment() { } void ECSevenSegment::dataChanged() { QColor color = dataColor("0-color"); r = color.red() / 0x100; g = color.green() / 0x100; b = color.blue() / 0x100; bool commonCathode = dataString("diode-polarity") == "Common Cathode"; if ( commonCathode != m_bCommonCathode ) { m_bCommonCathode = commonCathode; for ( int i=0; i<7; i++ ) { removeElement( m_diodes[i], false ); if (commonCathode) m_diodes[i] = createDiode( m_nodes[i], m_nNode ); else m_diodes[i] = createDiode( m_nNode, m_nodes[i] ); } removeElement( m_diodes[7], false ); if (commonCathode) m_diodes[7] = createDiode( m_nodes[7], m_nNode ); else m_diodes[7] = createDiode( m_nNode, m_nodes[7] ); } update(); } void ECSevenSegment::stepNonLogic() { if ( !m_diodes[0] ) return; for ( int i=0; i<8; i++ ) { avg_brightness[i] += LED::brightness( m_diodes[i]->current() ) * LINEAR_UPDATE_PERIOD; } lastUpdatePeriod += LINEAR_UPDATE_PERIOD; } void ECSevenSegment::drawShape( QPainter &p ) { CNItem::drawShape(p); initPainter(p); const int _width = 20; const int _height = 32; const int x1 = (int)x()+offsetX() + (width()-_width)/2 - 1; const int x2 = x1 + _width; const int y1 = (int)y()+offsetY() + (height()-_height)/2; const int y2 = y1 + _height/2; const int y3 = y1 + _height; const int ds = 2; // "Slope" // QPen pen; // pen.setWidth(2); // pen.setCapStyle(Qt::RoundCap); // p.setPen(pen); if ( lastUpdatePeriod != 0. ) { for ( uint i=0; i<8; ++i ) { last_brightness[i] = (uint)(avg_brightness[i]/lastUpdatePeriod); } } double _b; // Top _b = last_brightness[0]; p.setPen( QPen( QColor( uint(255-(255-_b)*(1-r)), uint(255-(255-_b)*(1-g)), uint(255-(255-_b)*(1-b)) ), 2 ) ); p.drawLine( x1+3+ds, y1+0, x2-3+ds, y1+0 ); // Top right _b = last_brightness[1]; p.setPen( QPen( QColor( uint(255-(255-_b)*(1-r)), uint(255-(255-_b)*(1-g)), uint(255-(255-_b)*(1-b)) ), 2 ) ); p.drawLine( x2+0+ds, y1+3, x2+0, y2-3 ); // Bottom right _b = last_brightness[2]; p.setPen( QPen( QColor( uint(255-(255-_b)*(1-r)), uint(255-(255-_b)*(1-g)), uint(255-(255-_b)*(1-b)) ), 2 ) ); p.drawLine( x2+0, y2+3, x2+0-ds, y3-3 ); // Bottom _b = last_brightness[3]; p.setPen( QPen( QColor( uint(255-(255-_b)*(1-r)), uint(255-(255-_b)*(1-g)), uint(255-(255-_b)*(1-b)) ), 2 ) ); p.drawLine( x2-3-ds, y3+0, x1+3-ds, y3+0 ); // Bottom left _b = last_brightness[4]; p.setPen( QPen( QColor( uint(255-(255-_b)*(1-r)), uint(255-(255-_b)*(1-g)), uint(255-(255-_b)*(1-b)) ), 2 ) ); p.drawLine( x1+0-ds, y3-3, x1+0, y2+3 ); // Top left _b = last_brightness[5]; p.setPen( QPen( QColor( uint(255-(255-_b)*(1-r)), uint(255-(255-_b)*(1-g)), uint(255-(255-_b)*(1-b)) ), 2 ) ); p.drawLine( x1+0, y2-3, x1+0+ds, y1+3 ); // Middle _b = last_brightness[6]; p.setPen( QPen( QColor( uint(255-(255-_b)*(1-r)), uint(255-(255-_b)*(1-g)), uint(255-(255-_b)*(1-b)) ), 2 ) ); p.drawLine( x1+3, y2+0, x2-3, y2+0 ); // Decimal point _b = last_brightness[7]; p.setBrush( QBrush( QColor( uint(255-(255-_b)*(1-r)), uint(255-(255-_b)*(1-g)), uint(255-(255-_b)*(1-b)) ) ) ); p.setPen( Qt::NoPen ); p.drawPie( x2+3, y3-2, 3, 3, 0, 16*360 ); lastUpdatePeriod = 0.; for ( uint i=0; i<8; ++i ) { avg_brightness[i] = 0.; } deinitPainter(p); } diff --git a/src/electronics/components/ecsignallamp.cpp b/src/electronics/components/ecsignallamp.cpp index e1aebed3..54589afc 100644 --- a/src/electronics/components/ecsignallamp.cpp +++ b/src/electronics/components/ecsignallamp.cpp @@ -1,97 +1,97 @@ /*************************************************************************** * Copyright (C) 2003-2005 by David Saxton * * david@bluehaze.org * * * * 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 "ecnode.h" #include "ecsignallamp.h" #include "element.h" #include "libraryitem.h" #include "pin.h" #include -#include +#include #include // TODO: resistance and power rating should be user definable properties. #define RESISTANCE 100 #define WATTAGE 0.5 // minimal power to create glow. (looks low...) #define LIGHTUP (WATTAGE / 20) Item* ECSignalLamp::construct( ItemDocument *itemDocument, bool newItem, const char *id ) { return new ECSignalLamp( (ICNDocument*)itemDocument, newItem, id ); } LibraryItem* ECSignalLamp::libraryItem() { return new LibraryItem( QStringList(QString("ec/signal_lamp")), i18n("Signal Lamp"), i18n("Outputs"), "signal_lamp.png", LibraryItem::lit_component, ECSignalLamp::construct ); } ECSignalLamp::ECSignalLamp( ICNDocument *icnDocument, bool newItem, const char *id ) : Component( icnDocument, newItem, id ? id : "signal_lamp" ) { m_name = i18n("Signal Lamp"); setSize( -8, -8, 16, 16 ); init1PinLeft(); init1PinRight(); createResistance( m_pPNode[0], m_pNNode[0], RESISTANCE ); advanceSinceUpdate = 0; avgPower = 0.; m_bDynamicContent = true; } ECSignalLamp::~ECSignalLamp() { } void ECSignalLamp::stepNonLogic() { const double voltage = m_pPNode[0]->pin()->voltage()-m_pNNode[0]->pin()->voltage(); if (advanceSinceUpdate == 0) { advanceSinceUpdate = 1; // do not try to divide by 0 } avgPower = fabs(avgPower * advanceSinceUpdate + (voltage * voltage / RESISTANCE)) / advanceSinceUpdate; ++advanceSinceUpdate; } void ECSignalLamp::drawShape( QPainter &p ) { initPainter(p); int _x = int(x()); int _y = int(y()); // Calculate the brightness as a linear function of power, bounded below by // 25 milliWatts and above by 500 milliWatts. int brightness = (avgPower < LIGHTUP) ? 255 : ((avgPower > WATTAGE) ? 0 : (int)(255 * (1 - ((avgPower - LIGHTUP) / (WATTAGE - LIGHTUP))))); advanceSinceUpdate = 0; p.setBrush( QColor( 255, 255, brightness ) ); p.drawEllipse( _x-8, _y-8, 16, 16 ); int pos = 8 - int(8 * M_SQRT1_2); p.drawLine( _x-8+pos, _y-8+pos, _x+8-pos, _y+8-pos ); p.drawLine( _x+8-pos, _y-8+pos, _x-8+pos, _y+8-pos ); deinitPainter(p); } diff --git a/src/electronics/components/ecsubcircuit.cpp b/src/electronics/components/ecsubcircuit.cpp index d6e677d4..58048f72 100644 --- a/src/electronics/components/ecsubcircuit.cpp +++ b/src/electronics/components/ecsubcircuit.cpp @@ -1,124 +1,125 @@ /*************************************************************************** * Copyright (C) 2005 by David Saxton * * david@bluehaze.org * * * * 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 "canvasitemparts.h" #include "circuitdocument.h" #include "ecsubcircuit.h" #include "node.h" #include "libraryitem.h" #include "subcircuits.h" -#include #include -#include + +#include +#include Item* ECSubcircuit::construct( ItemDocument *itemDocument, bool newItem, const char *id ) { return new ECSubcircuit( (ICNDocument*)itemDocument, newItem, id ); } LibraryItem* ECSubcircuit::libraryItem() { return new LibraryItem( QStringList(QString("ec/subcircuit")), QString::null, QString::null, QString::null, LibraryItem::lit_subcircuit, ECSubcircuit::construct ); } ECSubcircuit::ECSubcircuit( ICNDocument *icnDocument, bool newItem, const char *id ) : Component( icnDocument, newItem, (id) ? id : "subcircuit" ) { m_name = i18n("Subcircuit"); createProperty( "id", Variant::Type::Int ); property("id")->setMinValue(1); property("id")->setMaxValue(1<<15); property("id")->setValue(1); property("id")->setHidden(true); } ECSubcircuit::~ECSubcircuit() { } void ECSubcircuit::removeItem() { emit subcircuitDeleted(); Component::removeItem(); } void ECSubcircuit::setNumExtCon( unsigned numExtCon ) { m_conNames.resize(numExtCon); // Remove old pins const NodeInfoMap::iterator nodeMapEnd = m_nodeMap.end(); for ( NodeInfoMap::iterator it = m_nodeMap.begin(); it != nodeMapEnd; ++it ) { p_icnDocument->appendDeleteList( p_icnDocument->nodeWithID(it.value().id) ); } p_icnDocument->flushDeleteList(); m_nodeMap.clear(); QStringList pins; for ( unsigned i=0; i m_conNames.size() ) return; m_conNames[numId] = name; } void ECSubcircuit::doneSCInit() { QStringList pins; for ( unsigned i = 0; i < m_conNames.size(); ++i ) pins << m_conNames[i]; initDIPSymbol( pins, 80 ); } void ECSubcircuit::drawShape( QPainter &p ) { Component::drawShape(p); } diff --git a/src/electronics/components/ecvoltagesignal.cpp b/src/electronics/components/ecvoltagesignal.cpp index 425ce9a0..cc7895bd 100644 --- a/src/electronics/components/ecvoltagesignal.cpp +++ b/src/electronics/components/ecvoltagesignal.cpp @@ -1,108 +1,108 @@ /*************************************************************************** * Copyright (C) 2003-2004 by David Saxton * * david@bluehaze.org * * * * 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. * * * * Peak/RMS added (c)19/06/2007 by Jason Lucas * ***************************************************************************/ #include "ecnode.h" #include "ecvoltagesignal.h" #include "libraryitem.h" #include "pin.h" #include "simulator.h" #include "voltagesignal.h" #include #include -#include -#include +#include +#include Item* ECVoltageSignal::construct( ItemDocument *itemDocument, bool newItem, const char *id ) { return new ECVoltageSignal( (ICNDocument*)itemDocument, newItem, id ); } LibraryItem* ECVoltageSignal::libraryItem() { return new LibraryItem( QStringList(QString("ec/voltage_signal")), i18n("Voltage Signal"), i18n("Sources"), "voltagesignal.png", LibraryItem::lit_component, ECVoltageSignal::construct ); } ECVoltageSignal::ECVoltageSignal( ICNDocument *icnDocument, bool newItem, const char *id ) : Component( icnDocument, newItem, id ? id : "voltage_signal" ) { m_name = i18n("Voltage Signal"); setSize( -8, -8, 16, 16 ); init1PinLeft(); init1PinRight(); m_pNNode[0]->pin()->setGroundType( Pin::gt_medium ); m_voltageSignal = createVoltageSignal( m_pNNode[0], m_pPNode[0], 0. ); m_voltageSignal->setStep(ElementSignal::st_sinusoidal, 50. ); createProperty( "frequency", Variant::Type::Double ); property("frequency")->setCaption( i18n("Frequency") ); property("frequency")->setUnit("Hz"); property("frequency")->setMinValue(1e-9); property("frequency")->setMaxValue(1e3); property("frequency")->setValue(50.0); createProperty( "voltage", Variant::Type::Double ); property("voltage")->setCaption( i18n("Voltage Range") ); property("voltage")->setUnit("V"); property("voltage")->setMinValue(-1e12); property("voltage")->setMaxValue(1e12); property("voltage")->setValue(5.0); addDisplayText( "~", QRect( -8, -8, 16, 16 ), "~" ); addDisplayText( "voltage", QRect( -16, -24, 32, 16 ), "" ); createProperty( "peak-rms", Variant::Type::Select ); property("peak-rms")->setCaption( i18n("Output") ); QStringMap allowed; allowed["Peak"] = i18n("Peak"); allowed["RMS"] = i18n("RMS"); property("peak-rms")->setAllowed( allowed ); property("peak-rms")->setValue("Peak"); } ECVoltageSignal::~ECVoltageSignal() { } void ECVoltageSignal::dataChanged() { const double voltage = dataDouble("voltage"); const double frequency = dataDouble("frequency"); bool rms = dataString("peak-rms") == "RMS"; m_voltageSignal->setStep(ElementSignal::st_sinusoidal, frequency ); if (rms) { QString display = QString::number( voltage / getMultiplier(voltage), 'g', 3 ) + getNumberMag(voltage) + "V RMS"; setDisplayText( "voltage", display ); m_voltageSignal->setVoltage(voltage* M_SQRT2); } else { QString display = QString::number( voltage / getMultiplier(voltage), 'g', 3 ) + getNumberMag(voltage) + "V Peak"; setDisplayText( "voltage", display ); m_voltageSignal->setVoltage(voltage); } } void ECVoltageSignal::drawShape( QPainter &p ) { initPainter(p); p.drawEllipse( (int)x()-8, (int)y()-8, width(), height() ); deinitPainter(p); } diff --git a/src/electronics/components/ecvoltagesource.cpp b/src/electronics/components/ecvoltagesource.cpp index 1034e063..8f58e9ed 100644 --- a/src/electronics/components/ecvoltagesource.cpp +++ b/src/electronics/components/ecvoltagesource.cpp @@ -1,90 +1,90 @@ /*************************************************************************** * Copyright (C) 2003 by David Saxton * * david@bluehaze.org * * * * 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 "ecvoltagesource.h" #include "ecnode.h" #include "voltagesource.h" #include "libraryitem.h" #include "pin.h" #include -#include +#include Item* ECCell::construct( ItemDocument *itemDocument, bool newItem, const char *id ) { return new ECCell( (ICNDocument*)itemDocument, newItem, id ); } LibraryItem* ECCell::libraryItem() { QStringList ids; ids << "ec/battery" << "ec/cell"; return new LibraryItem( ids, i18n("Battery"), i18n("Sources"), "cell.png", LibraryItem::lit_component, ECCell::construct ); } ECCell::ECCell( ICNDocument *icnDocument, bool newItem, const char *id ) : Component( icnDocument, newItem, id ? id : "cell" ) { m_name = i18n("Battery"); setSize( -8, -8, 16, 16 ); voltage = 0; init1PinLeft(); init1PinRight(); m_pNNode[0]->pin()->setGroundType( Pin::gt_medium ); m_voltageSource = createVoltageSource( m_pNNode[0], m_pPNode[0], voltage ); createProperty( "voltage", Variant::Type::Double ); property("voltage")->setUnit("V"); property("voltage")->setCaption( i18n("Voltage") ); property("voltage")->setMinValue(-1e12); property("voltage")->setMaxValue(1e12); property("voltage")->setValue(5.0); addDisplayText( "voltage", QRect( -16, -24, 32, 16 ), "" ); } ECCell::~ECCell() { } void ECCell::dataChanged() { voltage = dataDouble("voltage"); m_voltageSource->setVoltage(voltage); QString display = QString::number( voltage / getMultiplier(voltage), 'g', 3 ) + getNumberMag(voltage) + "V"; setDisplayText( "voltage", display ); } void ECCell::drawShape( QPainter &p ) { initPainter(p); int _x = (int)x()-8; int _y = (int)y()-24; p.drawLine( _x, _y+20, _x, _y+28 ); p.drawLine( _x+5, _y+16, _x+5, _y+32 ); p.drawLine( _x+10, _y+20, _x+10, _y+28 ); p.drawLine( _x+15, _y+16, _x+15, _y+32 ); deinitPainter(p); // p.drawPolyline( areaPoints() ); } diff --git a/src/electronics/components/externalconnection.cpp b/src/electronics/components/externalconnection.cpp index 47bd754f..a0c905be 100644 --- a/src/electronics/components/externalconnection.cpp +++ b/src/electronics/components/externalconnection.cpp @@ -1,77 +1,77 @@ /*************************************************************************** * Copyright (C) 2004 by David Saxton * * david@bluehaze.org * * * * 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 "externalconnection.h" #include "libraryitem.h" #include -#include +#include Item* ExternalConnection::construct( ItemDocument *itemDocument, bool newItem, const char *id ) { return new ExternalConnection( (ICNDocument*)itemDocument, newItem, id ); } LibraryItem* ExternalConnection::libraryItem() { return new LibraryItem( QStringList(QString("ec/external_connection")), i18n("External Connection"), i18n("Connections"), "external_connection.png", LibraryItem::lit_component, ExternalConnection::construct ); } ExternalConnection::ExternalConnection( ICNDocument *icnDocument, bool newItem, const char *id ) : Component( icnDocument, newItem, id ? id : "external_connection" ) { m_name = i18n("External Connection"); setSize( -8, -8, 16, 16 ); createProperty( "name", Variant::Type::Combo ); property("name")->setCaption( i18n("Name") ); property("name")->setValue("ExtCon"); init1PinLeft(); addDisplayText( "name", QRect( -24, 8, 3*width(), 16 ), "ExtCon" ); } ExternalConnection::~ExternalConnection() { } void ExternalConnection::dataChanged() { QString name = dataString("name"); QRect r( -width(), 16, 3*width(), 16 ); setDisplayText( "name", name ); } void ExternalConnection::drawShape( QPainter &p ) { initPainter(p); int _x = (int)x()-8; int _y = (int)y()-8; p.drawEllipse( _x, _y, width(), height() ); p.drawLine( _x+3, _y+6, _x+12, _y+6 ); p.drawLine( _x+8, _y+3, _x+12, _y+6 ); p.drawLine( _x+3, _y+9, _x+12, _y+9 ); p.drawLine( _x+3, _y+9, _x+8, _y+12 ); deinitPainter(p); } diff --git a/src/electronics/components/flipflop.cpp b/src/electronics/components/flipflop.cpp index 91839efe..3a9454dd 100644 --- a/src/electronics/components/flipflop.cpp +++ b/src/electronics/components/flipflop.cpp @@ -1,453 +1,453 @@ /*************************************************************************** * Copyright (C) 2005 by David Saxton * * david@bluehaze.org * * * * 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 "ecnode.h" #include "flipflop.h" #include "icndocument.h" #include "logic.h" #include "libraryitem.h" #include "node.h" #include "simulator.h" #include -#include +#include //BEGIN class ClockedFlipFlop ClockedFlipFlop::ClockedFlipFlop( ICNDocument *icnDocument, bool newItem, const char * id ) : Component( icnDocument, newItem, id ) { createProperty( "trig", Variant::Type::Select ); property("trig")->setCaption( i18n("Trigger Edge") ); QStringMap allowed; allowed["Rising"] = i18n("Rising"); allowed["Falling"] = i18n("Falling"); property("trig")->setAllowed( allowed ); property("trig")->setValue("Rising"); m_edgeTrigger = Rising; } void ClockedFlipFlop::dataChanged() { EdgeTrigger t = (dataString("trig") == "Rising") ? Rising : Falling; if ( t == m_edgeTrigger ) return; m_edgeTrigger = t; initSymbolFromTrigger(); } //END class ClockedFlipFlop //BEGIN class ECDFlipFlop Item* ECDFlipFlop::construct( ItemDocument *itemDocument, bool newItem, const char *id ) { return new ECDFlipFlop( dynamic_cast(itemDocument), newItem, id ); } LibraryItem* ECDFlipFlop::libraryItem() { return new LibraryItem( QStringList(QString("ec/d_flipflop")), i18n("D Flip-Flop"), i18n("Integrated Circuits"), "ic3.png", LibraryItem::lit_component, ECDFlipFlop::construct ); } ECDFlipFlop::ECDFlipFlop( ICNDocument *icnDocument, bool newItem, const char *id ) : ClockedFlipFlop( icnDocument, newItem, id ? id : "d_flipflop" ) { m_name = i18n("D-Type Flip-Flop"); setSize( -32, -24, 64, 48 ); init2PinLeft( -8, 8 ); init2PinRight( -8, 8 ); initSymbolFromTrigger(); m_prevD = false; m_pSimulator = Simulator::self(); m_bPrevClock = false; m_pD = createLogicIn( m_pNNode[0] ); m_pClock = createLogicIn( m_pNNode[1] ); m_pQ = createLogicOut( m_pPNode[0], false ); m_pQBar = createLogicOut( m_pPNode[1], false ); setp = createLogicIn( createPin( 0, -32, 90, "set" ) ); rstp = createLogicIn( createPin( 0, 32, 270, "rst" ) ); // (The display text for D, >, Set, Rst is set in initSymbolFromTrigger addDisplayText( "Q", QRect( 12, -16, 20, 16 ), "Q" ); addDisplayText( "Q'", QRect( 12, 0, 20, 16 ), "Q'" ); m_pD->setCallback( this, static_cast(&ECDFlipFlop::inputChanged) ); m_pClock->setCallback( this, static_cast(&ECDFlipFlop::clockChanged) ); setp->setCallback( this, static_cast(&ECDFlipFlop::asyncChanged) ); rstp->setCallback( this, static_cast(&ECDFlipFlop::asyncChanged) ); inStateChanged(false); } ECDFlipFlop::~ECDFlipFlop() { m_pD->setCallback( nullptr, static_cast(nullptr) ); m_pClock->setCallback( nullptr, static_cast(nullptr) ); setp->setCallback( nullptr, static_cast(nullptr) ); rstp->setCallback( nullptr, static_cast(nullptr) ); } void ECDFlipFlop::initSymbolFromTrigger() { int offset = (m_edgeTrigger == Rising) ? 0 : 6; int w = 64-offset; setSize( offset-32, -24, w, 48, true ); m_pNNode[0]->setLength( 8+offset ); addDisplayText( "D", QRect( offset-28, -16, 20, 16 ), "D", true, Qt::AlignLeft ); addDisplayText( ">", QRect( offset-28, 0, 20, 16 ), ">", true, Qt::AlignLeft ); addDisplayText( "Set", QRect( offset-28, -20, w-8, 16 ), "Set", true, Qt::AlignHCenter ); addDisplayText( "Rst", QRect( offset-28, 4, w-8, 16 ), "Rst", true, Qt::AlignHCenter ); updateAttachedPositioning(); } void ECDFlipFlop::drawShape( QPainter & p ) { Component::drawShape( p ); if ( m_edgeTrigger == Falling ) { initPainter( p ); p.drawEllipse( int(x()-32), int(y()+5), 6, 6 ); deinitPainter( p ); } } void ECDFlipFlop::asyncChanged(bool) { bool set = setp->isHigh(); bool rst = rstp->isHigh(); if(set || rst) { m_pQ->setHigh(set); m_pQBar->setHigh(rst); } } void ECDFlipFlop::inputChanged( bool newState ) { if ( newState == m_prevD ) { // Only record the time that the input state changes return; } m_prevD = newState; m_prevDChangeSimTime = m_pSimulator->time(); } void ECDFlipFlop::clockChanged( bool newState ) { bool set = setp->isHigh(); bool rst = rstp->isHigh(); bool fallingEdge = m_bPrevClock && !newState; bool edge = (m_edgeTrigger == Falling) ? fallingEdge : !fallingEdge; m_bPrevClock = newState; if( set || rst ) return; if ( edge ) { // The D Flip-Flop takes the input before the edge fall/rise - not after // the edge. So see if the input state last changed before now or at // now to work out if we should take the current value of m_prevD, or // its inverse. unsigned long long simTime = m_pSimulator->time(); bool d = (simTime == m_prevDChangeSimTime) ? !m_prevD : m_prevD; m_pQ->setHigh( d ); m_pQBar->setHigh( !d ); } } void ECDFlipFlop::inStateChanged(bool) { // Only called when the flipflop is created. m_pQ->setHigh(false); m_pQBar->setHigh(true); } //END class ECDFlipFlop //BEGIN class ECJKFlipFlop Item* ECJKFlipFlop::construct( ItemDocument *itemDocument, bool newItem, const char *id ) { return new ECJKFlipFlop( dynamic_cast(itemDocument), newItem, id ); } LibraryItem* ECJKFlipFlop::libraryItem() { return new LibraryItem( QStringList(QString("ec/jk_flipflop")), i18n("JK Flip-Flop"), i18n("Integrated Circuits"), "ic3.png", LibraryItem::lit_component, ECJKFlipFlop::construct ); } ECJKFlipFlop::ECJKFlipFlop( ICNDocument *icnDocument, bool newItem, const char *id ) : ClockedFlipFlop( icnDocument, newItem, id ? id : "jk_flipflop" ) { m_name = i18n("JK-Type Flip-Flop"); setSize( -32, -32, 64, 64 ); init3PinLeft( -16, 0, 16 ); init2PinRight( -16, 16 ); initSymbolFromTrigger(); m_bPrevClock = false; createProperty( "trig", Variant::Type::Select ); property("trig")->setCaption( i18n("Trigger Edge") ); QStringMap allowed; allowed["Rising"] = i18n("Rising"); allowed["Falling"] = i18n("Falling"); property("trig")->setAllowed( allowed ); property("trig")->setValue("Rising"); m_edgeTrigger = Rising; initSymbolFromTrigger(); m_pJ = createLogicIn( m_pNNode[0] ); m_pClock = createLogicIn( m_pNNode[1] ); m_pK = createLogicIn( m_pNNode[2] ); m_pQ = createLogicOut( m_pPNode[0], false ); m_pQBar = createLogicOut( m_pPNode[1], false ); setp = createLogicIn( createPin( 0, -40, 90, "set" ) ); rstp = createLogicIn( createPin( 0, 40, 270, "rst" ) ); addDisplayText( "Q", QRect( 12, -24, 20, 16 ), "Q" ); addDisplayText( "Q'", QRect( 12, 8, 20, 16 ), "Q'" ); m_pClock->setCallback( this, static_cast(&ECJKFlipFlop::clockChanged) ); setp->setCallback( this, static_cast(&ECJKFlipFlop::asyncChanged) ); rstp->setCallback( this, static_cast(&ECJKFlipFlop::asyncChanged) ); inStateChanged(false); } ECJKFlipFlop::~ECJKFlipFlop() { m_pClock->setCallback( nullptr, static_cast(nullptr) ); setp->setCallback( nullptr, static_cast(nullptr) ); rstp->setCallback( nullptr, static_cast(nullptr) ); } void ECJKFlipFlop::initSymbolFromTrigger() { int offset = (m_edgeTrigger == Rising) ? 0 : 6; int w = 64-offset; setSize( offset-32, -32, w, 64, true ); m_pNNode[0]->setLength( 8+offset ); m_pNNode[2]->setLength( 8+offset ); addDisplayText( "J", QRect( offset-28, -24, 20, 16 ), "J", true, Qt::AlignLeft ); addDisplayText( ">", QRect( offset-28, -8, 20, 16 ), ">", true, Qt::AlignLeft ); addDisplayText( "K", QRect( offset-28, 8, 20, 16 ), "K", true, Qt::AlignLeft ); addDisplayText( "Set", QRect( offset-28, -28, w-8, 16 ), "Set", true, Qt::AlignHCenter ); addDisplayText( "Rst", QRect( offset-28, 12, w-8, 16 ), "Rst", true, Qt::AlignHCenter ); updateAttachedPositioning(); } void ECJKFlipFlop::drawShape( QPainter & p ) { Component::drawShape( p ); if ( m_edgeTrigger == Falling ) { initPainter( p ); p.drawEllipse( int(x()-32), int(y()-3), 6, 6 ); deinitPainter( p ); } } void ECJKFlipFlop::clockChanged(bool newvalue) { bool fallingEdge = (m_bPrevClock && !newvalue); bool edge = (m_edgeTrigger == Falling) ? fallingEdge : !fallingEdge; m_bPrevClock = newvalue; bool j = m_pJ->isHigh(); bool k = m_pK->isHigh(); bool set = setp->isHigh(); bool rst = rstp->isHigh(); if( set || rst ) return; // a JK flip-flop change state when clock do 1->0 if ( edge && (j || k)) { if ( j && k ) { m_pQ->setHigh(!prev_state); m_pQBar->setHigh(prev_state); prev_state = !prev_state; } else { // (J=1 && K=0) || (J=0 && K=1) m_pQ->setHigh(j); m_pQBar->setHigh(k); prev_state = j; } } } void ECJKFlipFlop::asyncChanged(bool) { bool set = setp->isHigh(); bool rst = rstp->isHigh(); if (set || rst) { m_pQ->setHigh(set); m_pQBar->setHigh(rst); prev_state = set; } } void ECJKFlipFlop::inStateChanged(bool) { m_pQBar->setHigh(true); m_pQ->setHigh(false); prev_state = false; } //END class ECJKFlipFlop //BEGIN class ECSRFlipFlop Item* ECSRFlipFlop::construct( ItemDocument *itemDocument, bool newItem, const char *id ) { return new ECSRFlipFlop( dynamic_cast(itemDocument), newItem, id ); } LibraryItem* ECSRFlipFlop::libraryItem() { return new LibraryItem( QStringList(QString("ec/sr_flipflop")), i18n("SR Flip-Flop"), i18n("Integrated Circuits"), "ic3.png", LibraryItem::lit_component, ECSRFlipFlop::construct ); } ECSRFlipFlop::ECSRFlipFlop( ICNDocument *icnDocument, bool newItem, const char *id ) : Component( icnDocument, newItem, id ? id : "sr_flipflop" ) { m_name = i18n("SR Flip-Flop"); setSize( -24, -24, 48, 48 ); init2PinLeft( -8, 8 ); init2PinRight( -8, 8 ); m_pS = createLogicIn( m_pNNode[0] ); m_pR = createLogicIn( m_pNNode[1] ); m_pQ = createLogicOut( m_pPNode[0], true ); m_pQBar = createLogicOut( m_pPNode[1], false ); old_q1 = true; old_q2 = false; m_pQ->setHigh(old_q1); m_pQBar->setHigh(old_q2); addDisplayText( "S", QRect( -24, -16, 20, 16 ), "S" ); addDisplayText( "R", QRect( -24, 0, 20, 16 ), "R" ); addDisplayText( "Q", QRect( 4, -16, 20, 16 ), "Q" ); addDisplayText( "Q'", QRect( 4, 0, 20, 16 ), "Q'" ); m_pS->setCallback( this, static_cast(&ECSRFlipFlop::inStateChanged) ); m_pR->setCallback( this, static_cast(&ECSRFlipFlop::inStateChanged) ); m_pQ->setCallback( this, static_cast(&ECSRFlipFlop::inStateChanged) ); m_pQBar->setCallback( this, static_cast(&ECSRFlipFlop::inStateChanged) ); } ECSRFlipFlop::~ECSRFlipFlop() { m_pS->setCallback( nullptr, static_cast(nullptr) ); m_pR->setCallback( nullptr, static_cast(nullptr) ); m_pQ->setCallback( nullptr, static_cast(nullptr) ); m_pQBar->setCallback( nullptr, static_cast(nullptr) ); } void ECSRFlipFlop::inStateChanged(bool) { // Q = v_q1, Q-bar = v_q2 bool new_q1 = false; bool new_q2 = false; bool s = m_pS->isHigh(); bool r = m_pR->isHigh(); bool q1 = m_pQ->isHigh(); bool q2 = m_pQBar->isHigh(); // Easy ones to do :-) if (!q1) new_q2 = true; if (!q2) new_q1 = true; if ( q1 && q2 ) { if ( s && !r ) { new_q1 = true; new_q2 = false; } else if ( !s && r ) { new_q1 = false; new_q2 = true; } else if ( s && r ) { new_q1 = old_q1; new_q2 = old_q2; } else if ( !s && !r ) { new_q1 = false; new_q2 = false; } } else if ( q1 && !q2 ) { // Note: We only need to set the value of v_q2 if ( r && !s ) new_q2 = true; else new_q2 = false; } else if ( !q1 && q2 ) { // Note: We only need to set the value of v_q1 if ( s && !r ) new_q1 = true; else new_q1 = false; } old_q1 = new_q1; old_q2 = new_q2; m_pQ->setHigh(new_q1); m_pQBar->setHigh(new_q2); } //END class ECSRFlipFlop diff --git a/src/electronics/components/inductor.cpp b/src/electronics/components/inductor.cpp index f2ec3b23..5e12ebb8 100644 --- a/src/electronics/components/inductor.cpp +++ b/src/electronics/components/inductor.cpp @@ -1,82 +1,82 @@ /*************************************************************************** * Copyright (C) 2005 by David Saxton * * david@bluehaze.org * * * * 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 "inductance.h" #include "inductor.h" #include "libraryitem.h" #include -#include +#include Item* Inductor::construct( ItemDocument *itemDocument, bool newItem, const char *id ) { return new Inductor( (ICNDocument*)itemDocument, newItem, id ); } LibraryItem* Inductor::libraryItem() { return new LibraryItem( QStringList(QString("ec/inductor")), i18n("Inductor"), i18n("Passive"), "inductor.png", LibraryItem::lit_component, Inductor::construct ); } Inductor::Inductor( ICNDocument *icnDocument, bool newItem, const char *id ) : Component( icnDocument, newItem, id ? id : "inductor" ) { m_name = i18n("Inductor"); setSize( -16, -8, 32, 16 ); init1PinLeft(); init1PinRight(); m_pInductance = createInductance( m_pNNode[0], m_pPNode[0], 0.001 ); createProperty( "Inductance", Variant::Type::Double ); property("Inductance")->setCaption( i18n("Inductance") ); property("Inductance")->setUnit("H"); property("Inductance")->setMinValue(1e-12); property("Inductance")->setMaxValue(1e12); property("Inductance")->setValue(1e-3); addDisplayText( "inductance", QRect( -8, -24, 16, 16 ), "", false ); } Inductor::~Inductor() { } void Inductor::dataChanged() { double inductance = dataDouble("Inductance"); QString display = QString::number( inductance / getMultiplier(inductance), 'g', 3 ) + getNumberMag(inductance) + "H"; setDisplayText( "inductance", display ); m_pInductance->setInductance(inductance); } void Inductor::drawShape( QPainter &p ) { initPainter(p); int _y = int(y()); int _x = int(x()); p.drawArc( _x-16, _y-5, 11, 11, 0, 180*16 ); p.drawArc( _x-5, _y-5, 11, 11, 0, 180*16 ); p.drawArc( _x+6, _y-5, 11, 11, 0, 180*16 ); deinitPainter(p); } diff --git a/src/electronics/components/led.cpp b/src/electronics/components/led.cpp index 50f6dadb..9d8da01d 100644 --- a/src/electronics/components/led.cpp +++ b/src/electronics/components/led.cpp @@ -1,124 +1,124 @@ /*************************************************************************** * Copyright (C) 2003 by David Saxton * * david@bluehaze.org * * * * 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 "colorcombo.h" #include "diode.h" #include "led.h" #include "ecnode.h" #include "libraryitem.h" #include "simulator.h" #include -#include +#include Item* LED::construct( ItemDocument *itemDocument, bool newItem, const char *id ) { return new LED( (ICNDocument*)itemDocument, newItem, id ); } LibraryItem* LED::libraryItem() { return new LibraryItem( QStringList(QString("ec/led")), i18n("LED"), i18n("Outputs"), "led.png", LibraryItem::lit_component, LED::construct); } LED::LED( ICNDocument *icnDocument, bool newItem, const char *id ) : ECDiode( icnDocument, newItem, id ? id : "led" ) { m_bDynamicContent = true; m_name = i18n("LED"); setSize( -8, -16, 24, 24, true ); avg_brightness = 255; lastUpdatePeriod = 1.; r=g=b=0; last_brightness = 255; createProperty( "0-color", Variant::Type::Color ); property("0-color")->setCaption( i18n("Color") ); property("0-color")->setColorScheme( ColorCombo::LED ); } LED::~LED() { } void LED::dataChanged() { QColor color = dataColor("0-color"); r = color.red() / (double)0x100; g = color.green() / (double)0x100; b = color.blue() / (double)0x100; } void LED::stepNonLogic() { avg_brightness += brightness(m_diode->current()) * LINEAR_UPDATE_PERIOD; lastUpdatePeriod += LINEAR_UPDATE_PERIOD; } void LED::drawShape( QPainter &p ) { int _x = int(x()); int _y = int(y()); initPainter(p); //BEGIN draw "Diode" part uint _b; if(lastUpdatePeriod != 0.) { last_brightness = (uint)(avg_brightness/lastUpdatePeriod); } _b = last_brightness; avg_brightness = 0.; lastUpdatePeriod = 0.; p.setBrush(QColor(uint(255 - (255 - _b) * (1 - r)), uint(255 - (255 - _b) * (1 - g)), uint(255 - (255 - _b) * (1 - b)))); QPolygon pa(3); pa[0] = QPoint( 8, 0 ); pa[1] = QPoint( -8, -8 ); pa[2] = QPoint( -8, 8 ); pa.translate( _x, _y ); p.drawPolygon(pa); p.drawPolyline(pa); p.drawLine( _x+8, _y-8, _x+8, _y+8 ); //END draw "Diode" part //BEGIN draw "Arrows" part p.drawLine( _x+7, _y-10, _x+10, _y-13 ); // Tail of left arrow p.drawLine( _x+10, _y-13, _x+8, _y-13 ); // Left edge of left arrow tip p.drawLine( _x+10, _y-13, _x+10, _y-11 ); // Right edge of left arrow tip p.drawLine( _x+10, _y-7, _x+13, _y-10 ); // Tail of right arrow p.drawLine( _x+13, _y-10, _x+11, _y-10 ); // Left edge of right arrow tip p.drawLine( _x+13, _y-10, _x+13, _y-8 ); // Right edge of right arrow tip p.drawLine( _x+8, _y-13, _x+13, _y-8 ); // Diagonal line that forms base of both arrow tips //END draw "Arrows" part1 deinitPainter(p); } uint LED::brightness( double i ) { if ( i > 0.018 ) return 0; if ( i < 0.002 ) return 255; return (uint)(255 * (1 - ((i - 0.002) / 0.016))); } diff --git a/src/electronics/components/ledbargraphdisplay.cpp b/src/electronics/components/ledbargraphdisplay.cpp index 29899ff2..225231bf 100644 --- a/src/electronics/components/ledbargraphdisplay.cpp +++ b/src/electronics/components/ledbargraphdisplay.cpp @@ -1,253 +1,254 @@ /*************************************************************************** * Copyright (C) 2006 by William Hillerby - william.hillerby@ntlworld.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 "ledbargraphdisplay.h" #include "colorcombo.h" #include "libraryitem.h" #include "simulator.h" #include "led.h" #include -#include -#include -#include + +#include +#include +#include LEDPart::LEDPart( Component *pParent, const QString& strPNode, const QString& strNNode ) { m_pParent = pParent; m_strPNode = strPNode; m_strNNode = strNNode; m_pDiode = pParent->createDiode( pParent->ecNodeWithID( strPNode ), pParent->ecNodeWithID( strNNode ) ); avg_brightness = 255; lastUpdatePeriod = 1.; last_brightness = 255; r=g=b=0; } LEDPart::~LEDPart() { m_pParent->removeNode( m_strPNode ); m_pParent->removeNode( m_strNNode ); m_pParent->removeElement( m_pDiode, false ); } void LEDPart::setDiodeSettings( const DiodeSettings& ds ) { m_pDiode->setDiodeSettings( ds ); } void LEDPart::setColor( const QColor &color ) { r = color.red() / (double)0x100; g = color.green() / (double)0x100; b = color.blue() / (double)0x100; } void LEDPart::step() { avg_brightness += LED::brightness( m_pDiode->current() ) * LINEAR_UPDATE_PERIOD; lastUpdatePeriod += LINEAR_UPDATE_PERIOD; } void LEDPart::draw( QPainter &p, int x, int y, int w, int h ) { uint _b; if ( lastUpdatePeriod == 0. ) _b = last_brightness; else { _b = (uint)(avg_brightness/lastUpdatePeriod); last_brightness = _b; } avg_brightness = 0.; lastUpdatePeriod = 0.; p.setBrush( QColor( uint(255-(255-_b)*(1-r)), uint(255-(255-_b)*(1-g)), uint(255-(255-_b)*(1-b)) ) ); p.drawRect( x, y, w, h ); } Item* LEDBarGraphDisplay::construct( ItemDocument *itemDocument, bool newItem, const char *id ) { return new LEDBarGraphDisplay( (ICNDocument*)itemDocument, newItem, id ); } LibraryItem* LEDBarGraphDisplay::libraryItem() { return new LibraryItem( QStringList(QString("ec/bar_graph_display")), i18n("Bar Graph Display"), i18n("Outputs"), "bar_graph_display.png", LibraryItem::lit_component, LEDBarGraphDisplay::construct ); } LEDBarGraphDisplay::LEDBarGraphDisplay( ICNDocument* icnDocument, bool newItem, const QString& id ) : Component( icnDocument, newItem, (!id.isEmpty()) ? id : "bar_graph_display" ) { m_name = i18n("Bar Graph Display"); m_bDynamicContent = true; m_numRows = 0; for( unsigned i = 0; i < max_LED_rows; i++ ) m_LEDParts[i] = nullptr; // Create a Row property. createProperty( "rows", Variant::Type::Int ); property("rows")->setCaption( i18n("Rows") ); property("rows")->setMinValue(1); property("rows")->setMaxValue( max_LED_rows ); property("rows")->setValue( 7 ); createProperty( "color", Variant::Type::Color ); property("color")->setCaption( i18n("Color") ); property("color")->setColorScheme( ColorCombo::LED ); DiodeSettings ds; createProperty( "I_S", Variant::Type::Double ); property("I_S")->setCaption("Saturation Current"); property("I_S")->setUnit("A"); property("I_S")->setMinValue(1e-20); property("I_S")->setMaxValue(1e-0); property("I_S")->setValue( ds.I_S ); property("I_S")->setAdvanced(true); createProperty( "N", Variant::Type::Double ); property("N")->setCaption( i18n("Emission Coefficient") ); property("N")->setMinValue(1e0); property("N")->setMaxValue(1e1); property("N")->setValue( ds.N ); property("N")->setAdvanced(true); createProperty( "V_B", Variant::Type::Double ); property("V_B")->setCaption( i18n("Breakdown Voltage") ); property("V_B")->setUnit("V"); property("V_B")->setMinAbsValue(1e-5); property("V_B")->setMaxValue(1e10); property("V_B")->setValue( ds.V_B ); property("V_B")->setAdvanced(true); } LEDBarGraphDisplay::~LEDBarGraphDisplay() { } void LEDBarGraphDisplay::dataChanged() { DiodeSettings ds; QColor color = dataColor("color"); ds.I_S = dataDouble("I_S"); ds.V_B = dataDouble("V_B"); ds.N = dataDouble("N"); initPins(); // Update each diode in array with new diode setting as they are acting individually. for( unsigned i = 0; i < m_numRows; i++ ) { m_LEDParts[i]->setDiodeSettings( ds ); m_LEDParts[i]->setColor( color ); } } void LEDBarGraphDisplay::initPins() { unsigned int numRows = dataInt( "rows" ); if( numRows == m_numRows ) return; if( numRows > max_LED_rows ) numRows = max_LED_rows; if( numRows < 1 ) numRows = 1; // Create a list of named pins. // A default setup looks like: // ------- // p_0--|1 14|--n_0 // p_1--|2 13|--n_1 // p_2--|3 12|--n_2 // p_3--|4 11|--n_3 // p_4--|5 10|--n_4 // p_5--|6 9|--n_5 // p_6--|7 8|--n_6 // ------- // // And this is the scheme used to create the nodes and diodes. // // Create the positive & negative pin names in an anticlockwise fashion // as shown in the pin schematic above. QStringList pins; for( unsigned i = 0; i < numRows; i++ ) pins += QString( "p_" + QString::number( i ) ); for( int i = numRows - 1; i >= 0; i-- ) pins += QString( "n_" + QString::number( i ) ); // Set the size of the component *BEFORE* initDIP() is called // as initDIP() uses this data to initialise the pin positions. setSize( -16, -( numRows + 1 ) * 8, 32, ( numRows + 1 ) * 16, true ); // Create the nodes. initDIP( pins ); // Create or remove LED parts if( numRows > m_numRows ) { // Create the extra LED parts required. for( unsigned i = m_numRows; i < numRows; i++ ) m_LEDParts[i] = new LEDPart( (Component*)this, QString( "p_" + QString::number( i ) ), QString( "n_" + QString::number( i ) ) ); } else { // Remove excess LED parts. for( unsigned i = numRows; i < m_numRows; i++ ) { delete m_LEDParts[i]; m_LEDParts[i] = nullptr; } } m_numRows = numRows; } void LEDBarGraphDisplay::stepNonLogic() { // Update LED brightnesses. for( unsigned i = 0; i < m_numRows; i++ ) m_LEDParts[i]->step(); } void LEDBarGraphDisplay::drawShape( QPainter &p ) { Component::drawShape(p); initPainter(p); // Init _x and _y to top left hand corner of component. int _x = (int)(x()+offsetX()); int _y = (int)(y()+offsetY()); // Draw LED elements, passing in a position for each. for( unsigned i = 0; i < m_numRows; i++ ) m_LEDParts[i]->draw( p, _x + 4, _y + (i*16) + 10, 24, 12 ); deinitPainter(p); } diff --git a/src/electronics/components/ledbargraphdisplay.h b/src/electronics/components/ledbargraphdisplay.h index e97293c3..edc5565b 100644 --- a/src/electronics/components/ledbargraphdisplay.h +++ b/src/electronics/components/ledbargraphdisplay.h @@ -1,71 +1,71 @@ /*************************************************************************** * Copyright (C) 2006 by William Hillerby - william.hillerby@ntlworld.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. * ***************************************************************************/ #ifndef LEDBARGRAPHDISPLAY_H #define LEDBARGRAPHDISPLAY_H #include #include "diode.h" // #include -#include +#include /** @author William Hillerby @short Simulates an LED Bar Graph Display */ const unsigned int max_LED_rows = 24; class LEDPart { public: LEDPart( Component *pParent, const QString& strPNode, const QString& strNNode ); ~LEDPart(); void setDiodeSettings( const DiodeSettings& ds ); void setColor( const QColor &color ); void step(); void draw( QPainter &p, int x, int y, int w, int h ); private: Component *m_pParent; Diode *m_pDiode; DiodeSettings ds; QString m_strPNode, m_strNNode; double r, g, b; double lastUpdatePeriod; double avg_brightness; uint last_brightness; }; class LEDBarGraphDisplay : public Component { public: LEDBarGraphDisplay( ICNDocument* icnDocument, bool newItem, const QString& id = nullptr ); ~LEDBarGraphDisplay() override; static Item* construct( ItemDocument *itemDocument, bool newItem, const char *id ); static LibraryItem *libraryItem(); private: void initPins(); void dataChanged() override; void stepNonLogic() override; bool doesStepNonLogic() const override { return true; } void drawShape( QPainter &p ) override; LEDPart* m_LEDParts[max_LED_rows]; unsigned int m_numRows; }; #endif diff --git a/src/electronics/components/magnitudecomparator.h b/src/electronics/components/magnitudecomparator.h index 6f273bda..46e0db5d 100644 --- a/src/electronics/components/magnitudecomparator.h +++ b/src/electronics/components/magnitudecomparator.h @@ -1,49 +1,49 @@ /*************************************************************************** * Copyright (C) 2005 by Fredy Yanardi * * * * 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. * ***************************************************************************/ #ifndef MAGNITUDECOMPARATOR_H #define MAGNITUDECOMPARATOR_H #include "component.h" #include "logic.h" -#include +#include //#include // 2018.10.17 /** @author Fredy Yanardi */ class MagnitudeComparator : public CallbackClass, public Component { public: MagnitudeComparator( ICNDocument *icnDocument, bool newItem, const char *id = nullptr ); ~MagnitudeComparator() override; static Item * construct( ItemDocument *itemDocument, bool newItem, const char *id ); static LibraryItem * libraryItem(); protected: void initPins(); void dataChanged() override; void inStateChanged(); int m_oldABLogicCount; int cascadingInputs; int outputs; bool firstTime; QBitArray m_data; QVector m_aLogic; QVector m_bLogic; QVector m_cLogic; QVector m_output; }; #endif diff --git a/src/electronics/components/matrixdisplay.cpp b/src/electronics/components/matrixdisplay.cpp index b4a0cb71..547fb020 100644 --- a/src/electronics/components/matrixdisplay.cpp +++ b/src/electronics/components/matrixdisplay.cpp @@ -1,289 +1,290 @@ /*************************************************************************** * Copyright (C) 2005 by David Saxton * * david@bluehaze.org * * * * 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 "colorcombo.h" #include "diode.h" #include "led.h" #include "ecnode.h" #include "libraryitem.h" #include "matrixdisplay.h" #include "simulator.h" -#include #include -#include -#include + +#include +#include +#include Item* MatrixDisplay::construct( ItemDocument *itemDocument, bool newItem, const char *id ) { return new MatrixDisplay( (ICNDocument*)itemDocument, newItem, id ); } LibraryItem* MatrixDisplay::libraryItem() { return new LibraryItem( QStringList(QString("ec/matrix_display")), i18n("Matrix Display"), i18n("Outputs"), "matrixdisplay.png", LibraryItem::lit_component, MatrixDisplay::construct ); } MatrixDisplay::MatrixDisplay( ICNDocument *icnDocument, bool newItem, const char *id ) : Component( icnDocument, newItem, id ? id : "matrix_display" ) { m_name = i18n("Matrix Display"); m_bDynamicContent = true; //BEGIN Reset members for ( unsigned i = 0; i < max_md_height; i++ ) m_pRowNodes[i] = nullptr; for ( unsigned i = 0; i < max_md_width; i++ ) m_pColNodes[i] = nullptr; m_lastUpdatePeriod = 0.0; m_r = m_g = m_b = 0.0; m_bRowCathode = true; m_numRows = 0; m_numCols = 0; //END Reset members createProperty( "0-rows", Variant::Type::Int ); property("0-rows")->setCaption( i18n("Rows") ); property("0-rows")->setMinValue(1); property("0-rows")->setMaxValue(max_md_height); property("0-rows")->setValue(7); createProperty( "1-cols", Variant::Type::Int ); property("1-cols")->setCaption( i18n("Columns") ); property("1-cols")->setMinValue(1); property("1-cols")->setMaxValue(max_md_width); property("1-cols")->setValue(5); createProperty( "color", Variant::Type::Color ); property("color")->setCaption( i18n("Color") ); property("color")->setColorScheme( ColorCombo::LED ); createProperty( "diode-configuration", Variant::Type::Select ); property("diode-configuration")->setCaption( i18n("Configuration") ); QStringMap allowed; allowed["Row Cathode"] = i18n("Row Cathode"); allowed["Column Cathode"] = i18n("Column Cathode"); property("diode-configuration")->setAllowed( allowed ); property("diode-configuration")->setValue("Row Cathode"); property("diode-configuration")->setAdvanced(true); } MatrixDisplay::~MatrixDisplay() { } void MatrixDisplay::dataChanged() { QColor color = dataColor("color"); m_r = double(color.red()) / 0x100; m_g = double(color.green()) / 0x100; m_b = double(color.blue()) / 0x100; int numRows = dataInt("0-rows"); int numCols = dataInt("1-cols"); bool ledsChanged = (numRows != int(m_numRows)) || (numCols != int(m_numCols)); if (ledsChanged) initPins( numRows, numCols ); bool rowCathode = dataString("diode-configuration") == "Row Cathode"; if ( (rowCathode != m_bRowCathode) || ledsChanged) { m_bRowCathode = rowCathode; for ( unsigned i = 0; i < m_numCols; i++ ) { for ( unsigned j = 0; j < m_numRows; j++ ) { removeElement( m_pDiodes[i][j], false ); if (rowCathode) m_pDiodes[i][j] = createDiode( m_pColNodes[i], m_pRowNodes[j] ); else m_pDiodes[i][j] = createDiode( m_pRowNodes[j], m_pColNodes[i] ); } } } } void MatrixDisplay::initPins( unsigned numRows, unsigned numCols ) { if ( (numRows == m_numRows) && (numCols == m_numCols) ) return; if ( numRows > max_md_height ) numRows = max_md_height; if ( numCols > max_md_width ) numCols = max_md_width; m_lastUpdatePeriod = 0.0; //BEGIN Remove diodes // All the diodes are going to be readded from dataChanged (where this // function is called from), so easiest just to delete the diodes now and // resize. for ( unsigned i = 0; i < m_numCols; i++ ) { for ( unsigned j = 0; j < m_numRows; j++ ) removeElement( m_pDiodes[i][j], false ); } m_avgBrightness.resize(numCols); m_lastBrightness.resize(numCols); m_pDiodes.resize(numCols); for ( unsigned i = 0; i < numCols; i++ ) { m_avgBrightness[i].resize(numRows); m_lastBrightness[i].resize(numRows); m_pDiodes[i].resize(numRows); for ( unsigned j = 0; j < numRows; j++ ) { m_avgBrightness[i][j] = 0.0; m_lastBrightness[i][j] = 255; m_pDiodes[i][j] = nullptr; } } //END Remove diodes //BEGIN Create or destroy pins if ( numCols >= m_numCols ) { for ( unsigned i = m_numCols; i < numCols; i++ ) m_pColNodes[i] = createPin( 0, 0, 270, colPinID(i) ); } else { for ( unsigned i = numCols; i < m_numCols; i++ ) { removeNode( colPinID(i) ); m_pColNodes[i] = nullptr; } } m_numCols = numCols; if ( numRows >= m_numRows ) { for ( unsigned i = m_numRows; i < numRows; i++ ) m_pRowNodes[i] = createPin( 0, 0, 0, rowPinID(i) ); } else { for ( unsigned i = numRows; i < m_numRows; i++ ) { removeNode( rowPinID(i) ); m_pRowNodes[i] = nullptr; } } m_numRows = numRows; //END Create or destroy pins //BEGIN Position pins et al setSize( -int(numCols+1)*8, -int(numRows+1)*8, int(numCols+1)*16, int(numRows+1)*16, true ); for ( int i = 0; i < int(m_numCols); i++ ) { m_nodeMap[colPinID(i)].x = offsetX() + 16 + 16*i; m_nodeMap[colPinID(i)].y = offsetY() + height() + 8; } for ( int i = 0; i < int(m_numRows); i++ ) { m_nodeMap[rowPinID(i)].x = offsetX() - 8; m_nodeMap[rowPinID(i)].y = offsetY() + 16 + 16*i; } updateAttachedPositioning(); //END Position pins et al } QString MatrixDisplay::colPinID( int col ) const { return QString("col_%1").arg(QString::number(col)); } QString MatrixDisplay::rowPinID( int row ) const { return QString("row_%1").arg(QString::number(row)); } void MatrixDisplay::stepNonLogic() { for ( unsigned i = 0; i < m_numCols; i++ ) { for ( unsigned j = 0; j < m_numRows; j++ ) m_avgBrightness[i][j] += LED::brightness( m_pDiodes[i][j]->current() ) * LINEAR_UPDATE_PERIOD; } m_lastUpdatePeriod += LINEAR_UPDATE_PERIOD; } void MatrixDisplay::drawShape( QPainter &p ) { if ( isSelected() ) p.setPen(m_selectedCol); p.drawRect( boundingRect() ); initPainter(p); const int _x = int(x()+offsetX()); const int _y = int(y()+offsetY()); // To avoid flicker, require at least a 10 ms sample before changing // the brightness double minUpdatePeriod = 0.0099; for ( int i = 0; i < int(m_numCols); i++ ) { for ( int j = 0; j < int(m_numRows); j++ ) { if ( m_lastUpdatePeriod > minUpdatePeriod ) m_lastBrightness[i][j] = unsigned(m_avgBrightness[i][j]/m_lastUpdatePeriod); double _b = m_lastBrightness[i][j]; QColor brush = QColor( uint(255-(255-_b)*(1-m_r)), uint(255-(255-_b)*(1-m_g)), uint(255-(255-_b)*(1-m_b)) ); p.setBrush(brush); p.setPen( Qt::NoPen ); p.drawEllipse( _x+10+i*16, _y+10+j*16, 12, 12 ); } } if ( m_lastUpdatePeriod > minUpdatePeriod ) { m_lastUpdatePeriod = 0.0; for ( unsigned i = 0; i < m_numCols; i++ ) { for ( unsigned j = 0; j < m_numRows; j++ ) m_avgBrightness[i][j] = 0.0; } } deinitPainter(p); } diff --git a/src/electronics/components/matrixdisplaydriver.cpp b/src/electronics/components/matrixdisplaydriver.cpp index 3278003c..83700c41 100644 --- a/src/electronics/components/matrixdisplaydriver.cpp +++ b/src/electronics/components/matrixdisplaydriver.cpp @@ -1,384 +1,384 @@ /*************************************************************************** * Copyright (C) 2005 by David Saxton * * david@bluehaze.org * * * * 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 "libraryitem.h" #include "logic.h" #include "matrixdisplaydriver.h" #include -#include -#include +#include +#include #include // Thank you Scott Dattalo! // http://www.dattalo.com/gnupic/lcdfont.inc static char characterMap[256][5] = { { 0x00, 0x00, 0x00, 0x00, 0x00 }, //0 { 0x00, 0x00, 0x00, 0x00, 0x00 }, //1 { 0x00, 0x00, 0x00, 0x00, 0x00 }, //2 { 0x00, 0x00, 0x00, 0x00, 0x00 }, //3 { 0x00, 0x00, 0x00, 0x00, 0x00 }, //4 { 0x00, 0x00, 0x00, 0x00, 0x00 }, //5 { 0x00, 0x00, 0x00, 0x00, 0x00 }, //6 { 0x00, 0x00, 0x00, 0x00, 0x00 }, //7 { 0x00, 0x00, 0x00, 0x00, 0x00 }, //8 { 0x00, 0x00, 0x00, 0x00, 0x00 }, //9 { 0x00, 0x00, 0x00, 0x00, 0x00 }, //10 { 0x00, 0x00, 0x00, 0x00, 0x00 }, //11 { 0x00, 0x00, 0x00, 0x00, 0x00 }, //12 { 0x00, 0x00, 0x00, 0x00, 0x00 }, //13 { 0x00, 0x00, 0x00, 0x00, 0x00 }, //14 { 0x00, 0x00, 0x00, 0x00, 0x00 }, //15 { 0x00, 0x00, 0x00, 0x00, 0x00 }, //16 { 0x00, 0x00, 0x00, 0x00, 0x00 }, //17 { 0x00, 0x00, 0x00, 0x00, 0x00 }, //18 { 0x00, 0x00, 0x00, 0x00, 0x00 }, //19 { 0x00, 0x00, 0x00, 0x00, 0x00 }, //20 { 0x00, 0x00, 0x00, 0x00, 0x00 }, //21 { 0x00, 0x00, 0x00, 0x00, 0x00 }, //22 { 0x00, 0x00, 0x00, 0x00, 0x00 }, //23 { 0x00, 0x00, 0x00, 0x00, 0x00 }, //24 { 0x00, 0x00, 0x00, 0x00, 0x00 }, //25 { 0x00, 0x00, 0x00, 0x00, 0x00 }, //26 { 0x00, 0x00, 0x00, 0x00, 0x00 }, //27 { 0x00, 0x00, 0x00, 0x00, 0x00 }, //28 { 0x00, 0x00, 0x00, 0x00, 0x00 }, //29 { 0x00, 0x00, 0x00, 0x00, 0x00 }, //30 { 0x00, 0x00, 0x00, 0x00, 0x00 }, //31 { 0x00, 0x00, 0x00, 0x00, 0x00 }, //32 { 0x00, 0x00, 0x7d, 0x00, 0x00 }, //33 { 0x00, 0x70, 0x00, 0x70, 0x00 }, //34 { 0x14, 0x7f, 0x14, 0x7f, 0x14 }, //35 { 0x12, 0x2a, 0x7f, 0x2a, 0x24 }, //36 { 0x62, 0x64, 0x08, 0x13, 0x23 }, //37 { 0x36, 0x49, 0x55, 0x22, 0x05 }, //38 { 0x00, 0x50, 0x60, 0x00, 0x00 }, //39 { 0x00, 0x1c, 0x22, 0x41, 0x00 }, //40 { 0x00, 0x41, 0x22, 0x1c, 0x00 }, //41 { 0x14, 0x08, 0x3e, 0x08, 0x14 }, //42 { 0x08, 0x08, 0x3e, 0x08, 0x08 }, //43 { 0x00, 0x05, 0x06, 0x00, 0x00 }, //44 { 0x08, 0x08, 0x08, 0x08, 0x08 }, //45 { 0x00, 0x03, 0x03, 0x00, 0x00 }, //46 { 0x02, 0x04, 0x08, 0x10, 0x20 }, //47 { 0x3e, 0x45, 0x49, 0x51, 0x3e }, //48 { 0x00, 0x21, 0x7f, 0x01, 0x00 }, //49 { 0x21, 0x43, 0x45, 0x49, 0x31 }, //50 { 0x42, 0x41, 0x51, 0x69, 0x46 }, //51 { 0x0c, 0x14, 0x24, 0x7f, 0x04 }, //52 { 0x72, 0x51, 0x51, 0x51, 0x4e }, //53 { 0x1e, 0x29, 0x49, 0x49, 0x06 }, //54 { 0x40, 0x47, 0x48, 0x50, 0x60 }, //55 { 0x36, 0x49, 0x49, 0x49, 0x36 }, //56 { 0x30, 0x49, 0x49, 0x4a, 0x3c }, //57 { 0x00, 0x36, 0x36, 0x00, 0x00 }, //58 { 0x00, 0x35, 0x36, 0x00, 0x00 }, //59 { 0x08, 0x14, 0x22, 0x41, 0x00 }, //60 { 0x14, 0x14, 0x14, 0x14, 0x14 }, //61 { 0x41, 0x22, 0x14, 0x08, 0x00 }, //62 { 0x20, 0x40, 0x45, 0x48, 0x30 }, //63 { 0x26, 0x49, 0x4f, 0x41, 0x3e }, //64 { 0x3f, 0x44, 0x44, 0x44, 0x3f }, //65 { 0x7f, 0x49, 0x49, 0x49, 0x36 }, //66 { 0x3e, 0x41, 0x41, 0x41, 0x22 }, //67 { 0x7f, 0x41, 0x41, 0x41, 0x3e }, //68 { 0x7f, 0x49, 0x49, 0x49, 0x41 }, //69 { 0x7f, 0x48, 0x48, 0x48, 0x40 }, //70 { 0x3e, 0x41, 0x49, 0x49, 0x2f }, //71 { 0x7f, 0x08, 0x08, 0x08, 0x7f }, //72 { 0x00, 0x41, 0x7f, 0x41, 0x00 }, //73 { 0x02, 0x01, 0x41, 0x7e, 0x40 }, //74 { 0x7f, 0x08, 0x14, 0x22, 0x41 }, //75 { 0x7f, 0x01, 0x01, 0x01, 0x01 }, //76 { 0x7f, 0x40, 0x20, 0x40, 0x7f }, //77 { 0x7f, 0x10, 0x08, 0x04, 0x7f }, //78 { 0x3e, 0x41, 0x41, 0x41, 0x3e }, //79 { 0x7f, 0x48, 0x48, 0x48, 0x30 }, //80 { 0x3e, 0x41, 0x45, 0x42, 0x3d }, //81 { 0x7f, 0x48, 0x4c, 0x4a, 0x31 }, //82 { 0x31, 0x49, 0x49, 0x49, 0x46 }, //83 { 0x40, 0x40, 0x7f, 0x40, 0x40 }, //84 { 0x7e, 0x01, 0x01, 0x01, 0x7e }, //85 { 0x7c, 0x02, 0x01, 0x02, 0x7c }, //86 { 0x7e, 0x01, 0x0e, 0x01, 0x7e }, //87 { 0x63, 0x14, 0x08, 0x14, 0x63 }, //88 { 0x70, 0x08, 0x07, 0x08, 0x70 }, //89 { 0x43, 0x45, 0x49, 0x51, 0x61 }, //90 { 0x00, 0x7f, 0x41, 0x41, 0x00 }, //91 { 0x54, 0x34, 0x1f, 0x34, 0x54 }, //92 { 0x00, 0x41, 0x41, 0x7f, 0x00 }, //93 { 0x10, 0x20, 0x40, 0x20, 0x10 }, //94 { 0x01, 0x01, 0x01, 0x01, 0x01 }, //95 { 0x00, 0x40, 0x20, 0x10, 0x00 }, //96 { 0x02, 0x15, 0x15, 0x15, 0x0f }, //97 { 0x7f, 0x09, 0x11, 0x11, 0x0e }, //98 { 0x0e, 0x11, 0x11, 0x11, 0x02 }, //99 { 0x0e, 0x11, 0x11, 0x09, 0x7f }, //100 { 0x0e, 0x15, 0x15, 0x15, 0x0c }, //101 { 0x08, 0x3f, 0x48, 0x40, 0x20 }, //102 { 0x30, 0x49, 0x49, 0x49, 0x7e }, //103 { 0x7f, 0x08, 0x10, 0x10, 0x0f }, //104 { 0x00, 0x11, 0x5f, 0x01, 0x00 }, //105 { 0x02, 0x01, 0x21, 0x7e, 0x00 }, //106 { 0x7f, 0x04, 0x0a, 0x11, 0x00 }, //107 { 0x00, 0x41, 0x7f, 0x01, 0x00 }, //108 { 0x1f, 0x10, 0x0c, 0x10, 0x0f }, //109 { 0x1f, 0x08, 0x10, 0x10, 0x0f }, //110 { 0x0e, 0x11, 0x11, 0x11, 0x0e }, //111 { 0x1f, 0x14, 0x14, 0x14, 0x08 }, //112 { 0x08, 0x14, 0x14, 0x0c, 0x1f }, //113 { 0x1f, 0x08, 0x10, 0x10, 0x08 }, //114 { 0x09, 0x15, 0x15, 0x15, 0x12 }, //115 { 0x20, 0x7e, 0x21, 0x01, 0x02 }, //116 { 0x1e, 0x01, 0x01, 0x02, 0x1f }, //117 { 0x1c, 0x02, 0x01, 0x02, 0x1c }, //118 { 0x1e, 0x01, 0x06, 0x01, 0x1e }, //119 { 0x11, 0x0a, 0x04, 0x0a, 0x11 }, //120 { 0x18, 0x05, 0x05, 0x05, 0x1e }, //121 { 0x11, 0x13, 0x15, 0x19, 0x11 }, //122 { 0x00, 0x08, 0x36, 0x41, 0x00 }, //123 { 0x00, 0x00, 0x7f, 0x00, 0x00 }, //124 { 0x00, 0x41, 0x36, 0x08, 0x00 }, //125 { 0x08, 0x08, 0x2a, 0x1c, 0x08 }, //126 { 0x08, 0x1c, 0x2a, 0x08, 0x08 }, //127 { 0x00, 0x00, 0x00, 0x00, 0x00 }, //128 { 0x00, 0x00, 0x00, 0x00, 0x00 }, //129 { 0x00, 0x00, 0x00, 0x00, 0x00 }, //130 { 0x00, 0x00, 0x00, 0x00, 0x00 }, //131 { 0x00, 0x00, 0x00, 0x00, 0x00 }, //132 { 0x00, 0x00, 0x00, 0x00, 0x00 }, //133 { 0x00, 0x00, 0x00, 0x00, 0x00 }, //134 { 0x00, 0x00, 0x00, 0x00, 0x00 }, //135 { 0x00, 0x00, 0x00, 0x00, 0x00 }, //136 { 0x00, 0x00, 0x00, 0x00, 0x00 }, //137 { 0x00, 0x00, 0x00, 0x00, 0x00 }, //138 { 0x00, 0x00, 0x00, 0x00, 0x00 }, //139 { 0x00, 0x00, 0x00, 0x00, 0x00 }, //140 { 0x00, 0x00, 0x00, 0x00, 0x00 }, //141 { 0x00, 0x00, 0x00, 0x00, 0x00 }, //142 { 0x00, 0x00, 0x00, 0x00, 0x00 }, //143 { 0x00, 0x00, 0x00, 0x00, 0x00 }, //144 { 0x07, 0x05, 0x07, 0x00, 0x00 }, //145 { 0x00, 0x00, 0x78, 0x40, 0x40 }, //146 { 0x01, 0x01, 0x0f, 0x00, 0x00 }, //147 { 0x04, 0x02, 0x01, 0x00, 0x00 }, //148 { 0x00, 0x0c, 0x0c, 0x00, 0x00 }, //149 { 0x28, 0x28, 0x29, 0x2a, 0x3c }, //150 { 0x10, 0x11, 0x16, 0x14, 0x18 }, //151 { 0x02, 0x04, 0x0f, 0x10, 0x00 }, //152 { 0x0c, 0x08, 0x19, 0x09, 0x0e }, //153 { 0x09, 0x09, 0x0f, 0x09, 0x09 }, //154 { 0x09, 0x0a, 0x0c, 0x1f, 0x08 }, //155 { 0x08, 0x1f, 0x08, 0x0a, 0x0c }, //156 { 0x01, 0x09, 0x09, 0x0f, 0x01 }, //157 { 0x15, 0x15, 0x15, 0x1f, 0x00 }, //158 { 0x0c, 0x00, 0x0d, 0x01, 0x0e }, //159 { 0x04, 0x04, 0x04, 0x04, 0x04 }, //160 { 0x40, 0x41, 0x5e, 0x48, 0x70 }, //161 { 0x04, 0x08, 0x1f, 0x20, 0x40 }, //162 { 0x38, 0x20, 0x61, 0x22, 0x3c }, //163 { 0x11, 0x11, 0x1f, 0x11, 0x11 }, //164 { 0x22, 0x24, 0x28, 0x7f, 0x20 }, //165 { 0x21, 0x7e, 0x20, 0x21, 0x3e }, //166 { 0x28, 0x28, 0x7f, 0x28, 0x28 }, //167 { 0x08, 0x31, 0x21, 0x22, 0x3c }, //168 { 0x10, 0x60, 0x21, 0x3e, 0x20 }, //169 { 0x21, 0x21, 0x21, 0x21, 0x3f }, //170 { 0x20, 0x79, 0x22, 0x7c, 0x20 }, //171 { 0x29, 0x29, 0x01, 0x02, 0x1c }, //172 { 0x21, 0x22, 0x24, 0x2a, 0x31 }, //173 { 0x20, 0x7e, 0x21, 0x29, 0x31 }, //174 { 0x30, 0x09, 0x01, 0x02, 0x3c }, //175 { 0x08, 0x31, 0x29, 0x26, 0x3c }, //176 { 0x28, 0x29, 0x3e, 0x48, 0x08 }, //177 { 0x30, 0x00, 0x31, 0x02, 0x3c }, //178 { 0x10, 0x51, 0x5e, 0x50, 0x10 }, //179 { 0x00, 0x7f, 0x08, 0x04, 0x00 }, //180 { 0x11, 0x12, 0x7c, 0x10, 0x10 }, //181 { 0x01, 0x21, 0x21, 0x21, 0x01 }, //182 { 0x21, 0x2a, 0x24, 0x2a, 0x30 }, //183 { 0x22, 0x24, 0x6f, 0x34, 0x22 }, //184 { 0x00, 0x01, 0x02, 0x7c, 0x00 }, //185 { 0x0f, 0x00, 0x20, 0x10, 0x0f }, //186 { 0x7e, 0x11, 0x11, 0x11, 0x11 }, //187 { 0x20, 0x21, 0x21, 0x22, 0x3c }, //188 { 0x10, 0x20, 0x10, 0x08, 0x06 }, //189 { 0x26, 0x20, 0x7f, 0x20, 0x26 }, //190 { 0x20, 0x24, 0x22, 0x25, 0x38 }, //191 { 0x00, 0x2a, 0x2a, 0x2a, 0x01 }, //192 { 0x0e, 0x12, 0x22, 0x02, 0x07 }, //193 { 0x01, 0x0a, 0x04, 0x0a, 0x30 }, //194 { 0x28, 0x3e, 0x29, 0x29, 0x29 }, //195 { 0x10, 0x7f, 0x10, 0x14, 0x18 }, //196 { 0x01, 0x21, 0x21, 0x3f, 0x01 }, //197 { 0x29, 0x29, 0x29, 0x29, 0x3f }, //198 { 0x10, 0x50, 0x51, 0x52, 0x1c }, //199 { 0x78, 0x01, 0x02, 0x7c, 0x00 }, //200 { 0x1f, 0x00, 0x3f, 0x01, 0x06 }, //201 { 0x3f, 0x01, 0x02, 0x04, 0x08 }, //202 { 0x3f, 0x21, 0x21, 0x21, 0x3f }, //203 { 0x38, 0x20, 0x21, 0x22, 0x3c }, //204 { 0x21, 0x21, 0x01, 0x02, 0x0c }, //205 { 0x20, 0x10, 0x40, 0x20, 0x00 }, //206 { 0x70, 0x50, 0x70, 0x00, 0x00 }, //207 { 0x0e, 0x11, 0x09, 0x06, 0x19 }, //208 { 0x02, 0x55, 0x15, 0x55, 0x0f }, //209 { 0x1f, 0x2a, 0x2a, 0x2a, 0x14 }, //210 { 0x0a, 0x15, 0x15, 0x11, 0x02 }, //211 { 0x3f, 0x02, 0x02, 0x04, 0x3e }, //212 { 0x0e, 0x11, 0x19, 0x15, 0x12 }, //213 { 0x0f, 0x12, 0x22, 0x22, 0x1c }, //214 { 0x1c, 0x22, 0x22, 0x22, 0x3f }, //215 { 0x02, 0x01, 0x1e, 0x10, 0x10 }, //216 { 0x20, 0x20, 0x00, 0x70, 0x00 }, //217 { 0x00, 0x00, 0x10, 0x5f, 0x00 }, //218 { 0x28, 0x10, 0x28, 0x00, 0x00 }, //219 { 0x18, 0x24, 0x7e, 0x24, 0x08 }, //220 { 0x14, 0x7f, 0x15, 0x01, 0x01 }, //221 { 0x1f, 0x48, 0x50, 0x50, 0x0f }, //222 { 0x0e, 0x51, 0x11, 0x51, 0x0e }, //223 { 0x3f, 0x12, 0x22, 0x22, 0x1c }, //224 { 0x1c, 0x22, 0x22, 0x12, 0x3f }, //225 { 0x3c, 0x52, 0x52, 0x52, 0x3c }, //226 { 0x03, 0x05, 0x02, 0x05, 0x06 }, //227 { 0x1a, 0x26, 0x20, 0x26, 0x1a }, //228 { 0x1e, 0x41, 0x01, 0x42, 0x1f }, //229 { 0x63, 0x55, 0x49, 0x41, 0x41 }, //230 { 0x22, 0x3c, 0x20, 0x3e, 0x22 }, //231 { 0x51, 0x4a, 0x44, 0x4a, 0x51 }, //232 { 0x3c, 0x02, 0x02, 0x02, 0x3f }, //233 { 0x28, 0x28, 0x3e, 0x28, 0x48 }, //234 { 0x22, 0x3c, 0x28, 0x28, 0x2e }, //235 { 0x3e, 0x28, 0x38, 0x28, 0x3e }, //236 { 0x04, 0x04, 0x15, 0x04, 0x04 }, //237 { 0x00, 0x00, 0x00, 0x00, 0x00 }, //238 { 0x7f, 0x7f, 0x7f, 0x7f, 0x7f }, //239 { 0x00, 0x00, 0x00, 0x00, 0x00 }, //240 { 0x00, 0x00, 0x00, 0x00, 0x00 }, //241 { 0x00, 0x00, 0x00, 0x00, 0x00 }, //242 { 0x00, 0x00, 0x00, 0x00, 0x00 }, //243 { 0x00, 0x00, 0x00, 0x00, 0x00 }, //244 { 0x00, 0x00, 0x00, 0x00, 0x00 }, //245 { 0x00, 0x00, 0x00, 0x00, 0x00 }, //246 { 0x00, 0x00, 0x00, 0x00, 0x00 }, //247 { 0x00, 0x00, 0x00, 0x00, 0x00 }, //248 { 0x00, 0x00, 0x00, 0x00, 0x00 }, //249 { 0x00, 0x00, 0x00, 0x00, 0x00 }, //250 { 0x00, 0x00, 0x00, 0x00, 0x00 }, //251 { 0x00, 0x00, 0x00, 0x00, 0x00 }, //252 { 0x00, 0x00, 0x00, 0x00, 0x00 }, //253 { 0x00, 0x00, 0x00, 0x00, 0x00 }, //254 { 0x00, 0x00, 0x00, 0x00, 0x00 }, //255 }; inline static bool displayBit( unsigned value, unsigned row, unsigned column ) { assert( value < 256 ); assert( row < 7 ); assert( column < 5 ); return characterMap[value][column] & (1 << row); } Item* MatrixDisplayDriver::construct( ItemDocument *itemDocument, bool newItem, const char *id ) { return new MatrixDisplayDriver( (ICNDocument*)itemDocument, newItem, id ); } LibraryItem * MatrixDisplayDriver::libraryItem() { return new LibraryItem( QStringList(QString("ec/matrix_display_driver")), i18n("Matrix Display Driver"), i18n("Integrated Circuits"), "ic2.png", LibraryItem::lit_component, MatrixDisplayDriver::construct ); } MatrixDisplayDriver::MatrixDisplayDriver( ICNDocument *icnDocument, bool newItem, const char *id ) : Component( icnDocument, newItem, id ? id : "Matrix Display Driver" ) { m_name = i18n("Matrix Display Driver"); m_prevCol = 0; m_nextCol = 0; m_scanCount = 2; createProperty( "diode-configuration", Variant::Type::Select ); property("diode-configuration")->setCaption( i18n("Configuration") ); QStringMap allowed; allowed["Row Cathode"] = i18n("Row Cathode"); allowed["Column Cathode"] = i18n("Column Cathode"); property("diode-configuration")->setAllowed( allowed ); property("diode-configuration")->setValue("Row Cathode"); property("diode-configuration")->setAdvanced(true); //QStringList pins = QStringList::split( ',', "D0,D1,D2,D3,D4,D5,D6,D7,,,,,,C4,C3,C2,C1,C0,,R0,R1,R2,R3,R4,R5,R6", true ); QStringList pins = QString("D0,D1,D2,D3,D4,D5,D6,D7,,,,,,C4,C3,C2,C1,C0,,R0,R1,R2,R3,R4,R5,R6").split( ',', QString::KeepEmptyParts); initDIPSymbol( pins, 64 ); initDIP(pins); m_pValueLogic.resize( 8 /*, nullptr - 2018.06.02 - initialized below */ ); for ( unsigned i = 0; i < 8; ++i ) m_pValueLogic[i] = createLogicIn( ecNodeWithID("D"+QString::number(i)) ); m_pRowLogic.resize( 7 /*, nullptr - 2018.06.02 - initialized below */ ); for ( unsigned i = 0; i < 7; ++i ) { m_pRowLogic[i] = createLogicOut( ecNodeWithID("R"+QString::number(i)), false ); m_pRowLogic[i]->setOutputLowConductance( 1.0 ); m_pRowLogic[i]->setOutputHighVoltage(5.0); } m_pColLogic.resize( 5 /*, nullptr - 2018.06.02 - initialized below */ ); for ( unsigned i = 0; i < 5; ++i ) { m_pColLogic[i] = createLogicOut( ecNodeWithID("C"+QString::number(i)), false ); m_pColLogic[i]->setOutputHighVoltage(5.0); } } MatrixDisplayDriver::~MatrixDisplayDriver() { } void MatrixDisplayDriver::stepNonLogic() { if ( ++m_scanCount < 5 ) return; m_scanCount = 0; m_pColLogic[m_prevCol]->setHigh(false); m_pColLogic[m_nextCol]->setHigh(true); unsigned value = 0; for ( unsigned i = 0; i < 8; ++i ) value |= ( m_pValueLogic[i]->isHigh() ) ? (1 << i) : 0; for ( unsigned row = 0; row < 7; row++ ) { m_pRowLogic[row]->setHigh( !displayBit( value, row, m_nextCol) ); } m_prevCol = m_nextCol; m_nextCol++; if ( m_nextCol >= 5 ) m_nextCol = 0; } diff --git a/src/electronics/components/meter.cpp b/src/electronics/components/meter.cpp index 9f0cf4b9..4009355c 100644 --- a/src/electronics/components/meter.cpp +++ b/src/electronics/components/meter.cpp @@ -1,278 +1,278 @@ /*************************************************************************** * Copyright (C) 2005 by David Saxton * * david@bluehaze.org * * * * 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 "canvasitemparts.h" #include "ecnode.h" #include "element.h" #include "libraryitem.h" #include "meter.h" #include "variant.h" #include "voltagesource.h" #include "pin.h" #include "simulator.h" #include #include -#include +#include //BEGIN class Meter Meter::Meter( ICNDocument *icnDocument, bool newItem, const char *id ) : Component( icnDocument, newItem, id ) { b_timerStarted = false; m_timeSinceUpdate = 0.; m_old_value = 0.; m_avgValue = 0.; b_firstRun = true; m_prevProp = 0.0; setSize( -16, -16, 32, 32 ); p_displayText = addDisplayText( "meter", QRect( -16, 16, 32, 16 ), displayText() ); createProperty( "0-minValue", Variant::Type::Double ); property("0-minValue")->setCaption( i18n("Minimum Value") ); property("0-minValue")->setMinValue(1e-12); property("0-minValue")->setMaxValue(1e12); property("0-minValue")->setValue(1e-3); createProperty( "1-maxValue", Variant::Type::Double ); property("1-maxValue")->setCaption( i18n("Maximum Value") ); property("1-maxValue")->setMinValue(1e-12); property("1-maxValue")->setMaxValue(1e12); property("1-maxValue")->setValue(1e3); } Meter::~Meter() { } void Meter::dataChanged() { m_minValue = dataDouble("0-minValue"); m_maxValue = dataDouble("1-maxValue"); setChanged(); } void Meter::stepNonLogic() { if (b_firstRun) { p_displayText->setText(displayText()); updateAttachedPositioning(); setChanged(); property("0-minValue")->setUnit(m_unit); property("1-maxValue")->setUnit(m_unit); b_firstRun = false; } const double v = meterValue(); if ( !b_timerStarted && std::abs(((v-m_old_value)/m_old_value)) > 1e-6 ) { b_timerStarted = true; } if (b_timerStarted) { m_timeSinceUpdate += LINEAR_UPDATE_PERIOD; m_avgValue += v * LINEAR_UPDATE_PERIOD; // setChanged(); if ( m_timeSinceUpdate > 0.05 ) { if ( p_displayText->setText(displayText()) ) updateAttachedPositioning(); } } } bool Meter::contentChanged() const { return (m_prevProp != calcProp( m_old_value )); } void Meter::drawShape( QPainter &p ) { initPainter(p); p.drawEllipse( (int)x()-16, (int)y()-16, width(), width() ); p.setPen(QPen(Qt::black,2)); p.setBrush(Qt::black); // The proportion between 0.1mV and 10KV, on a logarithmic scale m_prevProp = calcProp( m_old_value ); double sin_prop = 10*std::sin(m_prevProp*1.571); // 1.571 = pi/2 double cos_prop = 10*std::cos(m_prevProp*1.571); // 1.571 = pi/2 int cx = int(x()-16+(width()/2)); int cy = int(y()-16+(height()/2)); p.drawLine( int(cx-sin_prop), int(cy-cos_prop), int(cx+sin_prop), int(cy+cos_prop) ); QPolygon pa(3); pa[0] = QPoint( int(cx-sin_prop), int(cy-cos_prop) ); // Arrow head pa[1] = QPoint( int(cx-sin_prop + 8*std::sin(1.571*(-0.3+m_prevProp))), int(cy-cos_prop + 8*std::cos(1.571*(-0.3+m_prevProp))) ); pa[2] = QPoint( int(cx-sin_prop + 8*std::sin(1.571*(0.3+m_prevProp))), int(cy-cos_prop + 8*std::cos(1.571*(0.3+m_prevProp))) ); p.drawPolygon(pa); deinitPainter(p); } double Meter::calcProp( double v ) const { double abs_value = std::abs( v ); double prop; if ( abs_value <= m_minValue ) prop = 0.0; else if ( abs_value >= m_maxValue ) prop = 1.0; else prop = std::log10( abs_value/m_minValue ) / std::log10( m_maxValue/m_minValue ); if ( m_old_value>0 ) prop *= -1; return prop; } QString Meter::displayText() { double value = m_avgValue/m_timeSinceUpdate; if ( !std::isfinite(value) ) value = m_old_value; if ( std::abs((value)) < 1e-9 ) value = 0.; m_old_value = value; m_avgValue = 0.; m_timeSinceUpdate = 0.; b_timerStarted = false; return QString::number( value/CNItem::getMultiplier(value), 'g', 3 ) + CNItem::getNumberMag(value) + m_unit; } //END class Meter //BEGIN class FrequencyMeter Item* FrequencyMeter::construct( ItemDocument *itemDocument, bool newItem, const char *id ) { return new FrequencyMeter( (ICNDocument*)itemDocument, newItem, id ); } LibraryItem* FrequencyMeter::libraryItem() { return new LibraryItem( QStringList(QString("ec/frequencymeter")), i18n("Frequency Meter (TODO)"), i18n("Outputs"), "frequencymeter.png", LibraryItem::lit_component, FrequencyMeter::construct ); } FrequencyMeter::FrequencyMeter( ICNDocument *icnDocument, bool newItem, const char *id ) : Meter( icnDocument, newItem, id ? id : "frequencymeter" ) { m_name = i18n("Frequency Meter"); m_unit = "Hz"; m_probeNode = createPin( 0, -24, 90, "n1" ); } FrequencyMeter::~FrequencyMeter() { } double FrequencyMeter::meterValue() { return 0; } //END class FrequencyMeter //BEGIN class ECAmmeter Item* ECAmmeter::construct( ItemDocument *itemDocument, bool newItem, const char *id ) { return new ECAmmeter( (ICNDocument*)itemDocument, newItem, id ); } LibraryItem* ECAmmeter::libraryItem() { QStringList ids; ids << "ec/ammeter" << "ec/ammmeter"; return new LibraryItem( ids, i18n("Ammeter"), i18n("Outputs"), "ammeter.png", LibraryItem::lit_component, ECAmmeter::construct ); } ECAmmeter::ECAmmeter( ICNDocument *icnDocument, bool newItem, const char *id ) : Meter( icnDocument, newItem, id ? id : "ammeter" ) { m_name = i18n("Ammeter"); setSize( -16, -16, 32, 32 ); m_unit = "A"; init1PinLeft(0); init1PinRight(0); m_voltageSource = createVoltageSource( m_pNNode[0], m_pPNode[0], 0. ); } ECAmmeter::~ECAmmeter() { } double ECAmmeter::meterValue() { return -m_voltageSource->cbranchCurrent(0); } //END class ECAmmeter //BEGIN class ECVoltmeter Item* ECVoltMeter::construct( ItemDocument *itemDocument, bool newItem, const char *id ) { return new ECVoltMeter( (ICNDocument*)itemDocument, newItem, id ); } LibraryItem* ECVoltMeter::libraryItem() { return new LibraryItem( QStringList(QString("ec/voltmeter")), i18n("Voltmeter"), i18n("Outputs"), "voltmeter.png", LibraryItem::lit_component, ECVoltMeter::construct ); } ECVoltMeter::ECVoltMeter( ICNDocument *icnDocument, bool newItem, const char *id ) : Meter( icnDocument, newItem, id ? id : "voltmeter" ) { m_name = i18n("Voltmeter"); m_unit = "V"; init1PinLeft(0); init1PinRight(0); } ECVoltMeter::~ECVoltMeter() { } double ECVoltMeter::meterValue() { return m_pNNode[0]->pin()->voltage() - m_pPNode[0]->pin()->voltage(); } //END class ECVoltMeter diff --git a/src/electronics/components/multiinputgate.cpp b/src/electronics/components/multiinputgate.cpp index 06bef61c..8f199799 100644 --- a/src/electronics/components/multiinputgate.cpp +++ b/src/electronics/components/multiinputgate.cpp @@ -1,622 +1,622 @@ /*************************************************************************** * Copyright (C) 2004-2006 by David Saxton * * david@bluehaze.org * * * * 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 "ecnode.h" #include "icndocument.h" #include "libraryitem.h" #include "logic.h" #include "multiinputgate.h" #include #include -#include +#include #include //BEGIN class MultiInputGate MultiInputGate::MultiInputGate( ICNDocument *icnDocument, bool newItem, const char *id, const QString & rectangularShapeText, bool invertedOutput, int baseWidth, bool likeOR ) : Component( icnDocument, newItem, id ) { m_bLikeOR = likeOR; m_bDoneInit = false; m_numInputs = 0; m_distinctiveWidth = baseWidth; m_bInvertedOutput = invertedOutput; m_rectangularShapeText = rectangularShapeText; for ( int i=0; isetCaption( i18n("Number Inputs") ); property("numInput")->setMinValue(2); property("numInput")->setMaxValue(maxGateInput); property("numInput")->setValue(2); m_bDoneInit = true; } MultiInputGate::~MultiInputGate() { } void MultiInputGate::slotUpdateConfiguration() { updateLogicSymbolShape(); Component::slotUpdateConfiguration(); } void MultiInputGate::updateLogicSymbolShape() { // Set the canvas changed for the old shape setChanged(); if ( KTLConfig::logicSymbolShapes() == KTLConfig::EnumLogicSymbolShapes::Distinctive ) { m_logicSymbolShape = Distinctive; setSize( -m_distinctiveWidth/2, offsetY(), m_distinctiveWidth, height(), true ); } else { m_logicSymbolShape = Rectangular; setSize( -16, offsetY(), 32, height(), true ); } updateSymbolText(); updateAttachedPositioning(); if ( p_itemDocument ) p_itemDocument->requestEvent( ItemDocument::ItemDocumentEvent::RerouteInvalidatedConnectors ); // Set the canvas changed for the new shape setChanged(); } void MultiInputGate::updateSymbolText() { if ( m_logicSymbolShape == Distinctive ) removeDisplayText( "rect-shape-text" ); else { int w = 32 - (m_bInvertedOutput ? 6 : 0); QRect r( -16, 4-height()/2, w, height()-4 ); addDisplayText( "rect-shape-text", r, m_rectangularShapeText, true, Qt::AlignTop | Qt::AlignHCenter ); } } int MultiInputGate::logicSymbolShapeToWidth() const { return (m_logicSymbolShape == Distinctive) ? m_distinctiveWidth : 32; } void MultiInputGate::dataChanged() { updateInputs( qMin( maxGateInput, dataInt("numInput") ) ); } void MultiInputGate::updateInputs( int newNum ) { if ( newNum == m_numInputs ) return; if ( newNum < 2 ) newNum = 2; else if ( newNum > maxGateInput ) newNum = maxGateInput; int newWidth = logicSymbolShapeToWidth(); QRect r( -newWidth/2, -8*newNum, newWidth, 16*newNum ); setSize( r, true ); updateSymbolText(); const bool added = ( newNum > m_numInputs ); if (added) { for ( int i = m_numInputs; isetCallback( this, (CallbackPtr)(&MultiInputGate::inStateChanged) ); } } else { for ( int i=newNum; isetLength( length ); } } if (m_bDoneInit) Component::updateAttachedPositioning(); } void MultiInputGate::drawShape( QPainter & p ) { initPainter( p ); int _x = int(x()+offsetX()); int _y = int(y()+offsetY()); if ( m_bInvertedOutput ) { p.drawRect( _x, _y, 32-6, height() ); p.drawEllipse( _x+32-6, int(y())-3, 6, 6 ); } else { p.drawRect( _x, _y, 32, height() ); } deinitPainter( p ); } //END class MultiInputGate //BEGIN class ECXNor Item* ECXnor::construct( ItemDocument *itemDocument, bool newItem, const char *id ) { return new ECXnor( (ICNDocument*)itemDocument, newItem, id ); } LibraryItem* ECXnor::libraryItem() { return new LibraryItem( QStringList(QString("ec/xnor")), i18n("XNOR gate"), i18n("Logic"), "xnor.png", LibraryItem::lit_component, ECXnor::construct ); } ECXnor::ECXnor( ICNDocument *icnDocument, bool newItem, const char *id ) : MultiInputGate( icnDocument, newItem, id ? id : "xnor", "=1", true, 48, true ) { m_name = i18n("XNOR gate"); inStateChanged(false); } ECXnor::~ECXnor() { } void ECXnor::inStateChanged(bool) { int highCount = 0; for ( int i=0; iisHigh() ) highCount++; } m_pOut->setHigh( highCount != 1 ); } void ECXnor::drawShape( QPainter &p ) { if ( m_logicSymbolShape == Rectangular ) { MultiInputGate::drawShape( p ); return; } initPainter(p); int _x = (int)x()+offsetX(); int _y = (int)y()+offsetY(); p.save(); p.setPen( Qt::NoPen ); p.drawChord( _x-width()+22, _y, 2*width()-28, height(), -16*81, 16*162 ); p.restore(); p.drawArc( _x-width()+22, _y, 2*width()-28, height(), -16*90, 16*180 ); p.drawArc( _x-8, _y, 16, height(), -16*90, 16*180 ); p.drawArc( _x, _y, 16, height(), -16*90, 16*180 ); p.drawEllipse( _x+width()-6, _y+(height()/2)-3, 6, 6 ); deinitPainter(p); } //END class ECXnor //BEGIN class ECXor Item* ECXor::construct( ItemDocument *itemDocument, bool newItem, const char *id ) { return new ECXor( (ICNDocument*)itemDocument, newItem, id ); } LibraryItem* ECXor::libraryItem() { return new LibraryItem( QStringList(QString("ec/xor")), i18n("XOR gate"), i18n("Logic"), "xor.png", LibraryItem::lit_component, ECXor::construct ); } ECXor::ECXor( ICNDocument *icnDocument, bool newItem, const char *id ) : MultiInputGate( icnDocument, newItem, id ? id : "xor", "=1", false, 48, true ) { m_name = i18n("XOR gate"); inStateChanged(false); } ECXor::~ECXor() { } void ECXor::inStateChanged(bool) { int highCount = 0; for ( int i=0; iisHigh() ) highCount++; } m_pOut->setHigh( highCount == 1 ); } void ECXor::drawShape( QPainter &p ) { if ( m_logicSymbolShape == Rectangular ) { MultiInputGate::drawShape( p ); return; } initPainter(p); int _x = (int)x()+offsetX(); int _y = (int)y()+offsetY(); p.save(); p.setPen( Qt::NoPen ); p.drawChord( _x-width()+16, _y, 2*width()-16, height(), -16*81, 16*162 ); p.restore(); p.drawArc( _x-width()+16, _y, 2*width()-16, height(), -16*90, 16*180 ); p.drawArc( _x-8, _y, 16, height(), -16*90, 16*180 ); p.drawArc( _x, _y, 16, height(), -16*90, 16*180 ); deinitPainter(p); } //END class ECXor //BEGIN class ECOr Item* ECOr::construct( ItemDocument *itemDocument, bool newItem, const char *id ) { return new ECOr( (ICNDocument*)itemDocument, newItem, id ); } LibraryItem* ECOr::libraryItem() { return new LibraryItem( QStringList(QString("ec/or")), i18n("OR gate"), i18n("Logic"), "or.png", LibraryItem::lit_component, ECOr::construct ); } ECOr::ECOr( ICNDocument *icnDocument, bool newItem, const char *id ) : MultiInputGate( icnDocument, newItem, id ? id : "or", QChar(0x2265) + QString("1"), false, 48, true ) { m_name = i18n("OR gate"); inStateChanged(false); } ECOr::~ECOr() { } void ECOr::inStateChanged(bool) { bool allLow = true; for ( int i=0; iisHigh() ) allLow = false; } m_pOut->setHigh(!allLow); } void ECOr::drawShape( QPainter &p ) { if ( m_logicSymbolShape == Rectangular ) { MultiInputGate::drawShape( p ); return; } initPainter(p); int _x = (int)x()+offsetX(); int _y = (int)y()+offsetY(); p.save(); p.setPen( Qt::NoPen ); // p.setBrush( Qt::red ); p.drawChord( _x-width(), _y, 2*width(), height(), -16*81, 16*162 ); // p.drawPie( _x-width()+16, _y, 2*width()-16, height(), -16*100, 16*200 ); p.restore(); p.drawArc( _x-width(), _y, 2*width(), height(), -16*90, 16*180 ); p.drawArc( _x-8, _y, 16, height(), -16*90, 16*180 ); deinitPainter(p); } //END class ECOr //BEGIN class ECNor Item* ECNor::construct( ItemDocument *itemDocument, bool newItem, const char *id ) { return new ECNor( (ICNDocument*)itemDocument, newItem, id ); } LibraryItem* ECNor::libraryItem() { return new LibraryItem( QStringList(QString("ec/nor")), i18n("NOR gate"), i18n("Logic"), "nor.png", LibraryItem::lit_component, ECNor::construct ); } ECNor::ECNor( ICNDocument *icnDocument, bool newItem, const char *id ) : MultiInputGate( icnDocument, newItem, id ? id : "nor", QChar(0x2265) + QString("1"), true, 48, true ) { m_name = i18n("NOR Gate"); inStateChanged(false); } ECNor::~ECNor() { } void ECNor::inStateChanged(bool) { bool allLow = true; for ( int i=0; iisHigh() ) allLow = false; } m_pOut->setHigh(allLow); } void ECNor::drawShape( QPainter &p ) { if ( m_logicSymbolShape == Rectangular ) { MultiInputGate::drawShape( p ); return; } initPainter(p); int _x = (int)x()+offsetX(); int _y = (int)y()+offsetY(); p.save(); p.setPen( Qt::NoPen ); p.drawChord( _x-width()+6, _y, 2*width()-12, height(), -16*81, 16*162 ); p.restore(); p.drawArc( _x-width()+6, _y, 2*width()-12, height(), -16*90, 16*180 ); p.drawArc( _x-8, _y, 16, height(), -16*90, 16*180 ); p.drawEllipse( _x+width()-6, _y+(height()/2)-3, 6, 6 ); deinitPainter(p); } //END class ECNor //BEGIN class ECNand Item* ECNand::construct( ItemDocument *itemDocument, bool newItem, const char *id ) { return new ECNand( (ICNDocument*)itemDocument, newItem, id ); } LibraryItem* ECNand::libraryItem() { return new LibraryItem( QStringList(QString("ec/nand")), i18n("NAND gate"), i18n("Logic"), "nand.png", LibraryItem::lit_component, ECNand::construct ); } ECNand::ECNand( ICNDocument *icnDocument, bool newItem, const char *id ) : MultiInputGate( icnDocument, newItem, id ? id : "nand", "&", true, 32, false ) { m_name = i18n("NAND Gate"); inStateChanged(false); } ECNand::~ECNand() { } void ECNand::inStateChanged(bool) { for ( int i=0; i < m_numInputs; ++i ) { if ( !inLogic[i]->isHigh() ) { m_pOut->setHigh( true ); return; } } m_pOut->setHigh( false ); } void ECNand::drawShape( QPainter &p ) { if ( m_logicSymbolShape == Rectangular ) { MultiInputGate::drawShape( p ); return; } initPainter(p); int _x = (int)x()+offsetX(); int _y = (int)y()+offsetY(); p.drawChord( _x-width()+6, _y, 2*width()-12, height(), -16*90, 16*180 ); p.drawEllipse( _x+width()-6, _y+(height()/2)-3, 6, 6 ); deinitPainter(p); } //END class ECNand //BEGIN class ECAnd Item* ECAnd::construct( ItemDocument *itemDocument, bool newItem, const char *id ) { return new ECAnd( (ICNDocument*)itemDocument, newItem, id ); } LibraryItem* ECAnd::libraryItem() { QStringList idList; idList << "ec/and" << "ec/and_2"; return new LibraryItem( idList, i18n("AND gate"), i18n("Logic"), "and.png", LibraryItem::lit_component, ECAnd::construct ); } ECAnd::ECAnd( ICNDocument *icnDocument, bool newItem, const char *id ) : MultiInputGate( icnDocument, newItem, id ? id : "and", "&", false, 32, false ) { m_name = i18n("AND Gate"); inStateChanged(false); } ECAnd::~ECAnd() { } void ECAnd::inStateChanged(bool) { for ( int i=0; iisHigh() ) { m_pOut->setHigh( false ); return; } } m_pOut->setHigh( true ); } void ECAnd::drawShape( QPainter &p ) { if ( m_logicSymbolShape == Rectangular ) { MultiInputGate::drawShape( p ); return; } initPainter(p); int _x = (int)x()+offsetX(); int _y = (int)y()+offsetY(); p.drawChord( _x-width(), _y, 2*width(), height(), -16*90, 16*180 ); deinitPainter(p); } //END class ECAnd diff --git a/src/electronics/components/parallelportcomponent.cpp b/src/electronics/components/parallelportcomponent.cpp index 55b91d83..4da639e7 100644 --- a/src/electronics/components/parallelportcomponent.cpp +++ b/src/electronics/components/parallelportcomponent.cpp @@ -1,222 +1,223 @@ /*************************************************************************** * Copyright (C) 2005 by David Saxton * * david@bluehaze.org * * * * 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 "port.h" #include "parallelportcomponent.h" #include "ecnode.h" #include "itemdocument.h" #include "libraryitem.h" #include "pin.h" #include "resistance.h" -#include #include -#include + +#include +#include #include #include Item* ParallelPortComponent::construct( ItemDocument *itemDocument, bool newItem, const char *id ) { return new ParallelPortComponent( (ICNDocument*)itemDocument, newItem, id ); } LibraryItem* ParallelPortComponent::libraryItem() { return new LibraryItem( QStringList(QString("ec/parallel_port")), i18n("Parallel Port"), i18n("Connections"), "ic1.png", LibraryItem::lit_component, ParallelPortComponent::construct ); } ParallelPortComponent::ParallelPortComponent( ICNDocument *icnDocument, bool newItem, const char *id ) : Component( icnDocument, newItem, id ? id : "parallel_port" ) { m_name = i18n("Parallel Port"); QPolygon pa( 4 ); pa[0] = QPoint( -32, -112 ); pa[1] = QPoint( 32, -104 ); pa[2] = QPoint( 32, 104 ); pa[3] = QPoint( -32, 112 ); setItemPoints( pa ); m_pParallelPort = new ParallelPort(); for ( unsigned i = 0; i < 24; ++i ) m_pLogic[i] = nullptr; ECNode * pin = nullptr; //BEGIN Data register for ( int i = 0; i < 8; ++i ) { QString id = QString("D%1").arg(i); QString name = id; pin = createPin( -40, -80 + 16*i, 0, id ); addDisplayText( id, QRect( -28, -88 + 16*i, 28, 16 ), name, true, Qt::AlignLeft | Qt::AlignVCenter ); m_pLogic[i] = createLogicOut( pin, false ); m_pLogic[i]->setCallback( this, (CallbackPtr)(&ParallelPortComponent::dataCallback) ); } //END Data register //BEGIN Status register QString statusNames[] = { "ERR", "ON", "PE", "ACK", "BUSY" }; // The statusIDs are referenced in the save file and must not change QString statusIDs[] = { "ERROR", "ONLINE", "PE", "ACK", "BUSY" }; // Bits 0...2 in the Status register are not used for ( int i = 3; i < 8; ++i ) { QString id = statusIDs[i-3]; QString name = statusNames[i-3]; // Bit 3 (pin 15) doesn't not follow the same positioning pattern as // the other pins in the Status register. if ( i == 3 ) { pin = createPin( 40, -72, 180, id ); addDisplayText( id, QRect( 0, -80, 28, 16 ), name, true, Qt::AlignRight | Qt::AlignVCenter ); } else { pin = createPin( -40, -16 + 16*i, 0, id ); addDisplayText( id, QRect( -28, -24 + 16*i, 28, 16 ), name, true, Qt::AlignLeft | Qt::AlignVCenter ); } m_pLogic[i+8] = createLogicOut( pin, false ); } //END Status register //BEGIN Control register QString controlNames[] = { "STR", "AUT", "INIT", "SEL" }; // The controlIDs are referenced in the save file and must not change QString controlIDs[] = { "STROBE", "AUTO", "INIT", "SELECT" }; // Bits 4..7 are not used (well; bit 5 is, but not as a pin) for ( int i = 0; i < 4; ++i ) { QString id = controlIDs[i]; QString name = controlNames[i]; if ( i == 0 ) { pin = createPin( -40, -96, 0, id ); addDisplayText( id, QRect( -28, -104, 28, 16 ), name, true, Qt::AlignLeft | Qt::AlignVCenter ); } else if ( i == 1 ) { pin = createPin( 40, -88, 180, id ); addDisplayText( id, QRect( 0, -96, 28, 16 ), name, true, Qt::AlignRight | Qt::AlignVCenter ); } else { pin = createPin( 40, -88 + i*16, 180, id ); addDisplayText( id, QRect( 0, -96 + i*16, 28, 16 ), name, true, Qt::AlignRight | Qt::AlignVCenter ); } m_pLogic[i+16] = createLogicOut( pin, false ); m_pLogic[i+16]->setCallback( this, (CallbackPtr)(&ParallelPortComponent::controlCallback) ); } //END Control register #if 0 // And make the rest of the pins ground for ( int i = 0; i < 8; ++i ) { pin = createPin( 40, -24 + i*16, 180, QString("GND%1").arg( i ) ); pin->pin()->setGroundType( Pin::gt_always ); } #endif Variant * v = createProperty( "port", Variant::Type::Combo ); v->setAllowed( ParallelPort::ports( Port::ExistsAndRW ) ); v->setCaption( i18n("Port") ); } ParallelPortComponent::~ParallelPortComponent() { for (int i = 0; i < 24; i++) { if (m_pLogic[i]) { m_pLogic[i]->setCallback(nullptr, nullptr); } } delete m_pParallelPort; } void ParallelPortComponent::dataChanged() { initPort( dataString("port") ); } void ParallelPortComponent::initPort( const QString & port ) { if ( port.isEmpty() ) { m_pParallelPort->closePort(); return; } if ( ! m_pParallelPort->openPort( port ) ) { p_itemDocument->canvas()->setMessage( i18n("Could not open port %1", port ) ); return; } } void ParallelPortComponent::dataCallback( bool ) { uchar value = 0; for ( unsigned i = 0; i < 8; ++ i ) value |= m_pLogic[ i + 0 ]->isHigh() ? 0 : (1 << i); m_pParallelPort->writeToData( value ); } void ParallelPortComponent::controlCallback( bool ) { uchar value = 0; for ( unsigned i = 0; i < 4; ++ i ) value |= m_pLogic[ i + 16 ]->isHigh() ? 0 : (1 << i); m_pParallelPort->writeToControl( value ); } void ParallelPortComponent::stepNonLogic() { uchar status = m_pParallelPort->readFromRegister( ParallelPort::Status ); // Bits 0...2 in the Status register are not used for ( int i = 3; i < 8; ++i ) m_pLogic[i + 8]->setHigh( status | (1 << i) ); } void ParallelPortComponent::drawShape( QPainter & p ) { drawPortShape( p ); } diff --git a/src/electronics/components/piccomponent.cpp b/src/electronics/components/piccomponent.cpp index 6579d176..bfa4c07a 100644 --- a/src/electronics/components/piccomponent.cpp +++ b/src/electronics/components/piccomponent.cpp @@ -1,449 +1,450 @@ /*************************************************************************** * Copyright (C) 2003-2005 by David Saxton * * david@bluehaze.org * * * * 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 "config.h" #ifndef NO_GPSIM #include "canvasitemparts.h" #include "circuitdocument.h" #include "docmanager.h" #include "gpsimprocessor.h" #include "libraryitem.h" #include "logic.h" #include "ktechlab.h" #include "micropackage.h" #include "picinfo.h" #include "microlibrary.h" #include "piccomponent.h" #include "piccomponentpin.h" #include "projectmanager.h" -#include -#include #include #include -#include -#include + +#include +#include +#include +#include #include "gpsim/ioports.h" #include "gpsim/pic-processor.h" QString PICComponent::_def_PICComponent_fileName = QString::null; Item* PICComponent::construct( ItemDocument *itemDocument, bool newItem, const char *id ) { return new PICComponent( (ICNDocument*)itemDocument, newItem, id ); } LibraryItem* PICComponent::libraryItem() { QStringList IDs; IDs << "ec/pic" << "ec/picitem" << "ec/picitem_18pin"; return new LibraryItem( IDs, "PIC", i18n("Integrated Circuits"), "ic2.png", LibraryItem::lit_component, PICComponent::construct ); } PICComponent::PICComponent( ICNDocument *icnDocument, bool newItem, const char *id ) : Component( icnDocument, newItem, id ? id : "pic" ) { m_name = i18n("PIC Micro"); if ( _def_PICComponent_fileName.isEmpty() ) _def_PICComponent_fileName = i18n(""); m_bCreatedInitialPackage = false; m_bLoadingProgram = false; m_pGpsim = nullptr; addButton( "run", QRect(), QIcon::fromTheme( "media-playback-start" ) ); addButton( "pause", QRect(), QIcon::fromTheme( "media-playback-pause" ) ); addButton( "reset", QRect(), QIcon::fromTheme( "process-stop" ) ); addButton( "reload", QRect(), QIcon::fromTheme( "view-refresh" ) ); connect( KTechlab::self(), &KTechlab::recentFileAdded, this, &PICComponent::slotUpdateFileList); connect( ProjectManager::self(), SIGNAL(projectOpened()), this, SLOT(slotUpdateFileList()) ); connect( ProjectManager::self(), SIGNAL(projectClosed()), this, SLOT(slotUpdateFileList()) ); connect( ProjectManager::self(), SIGNAL(projectCreated()), this, SLOT(slotUpdateFileList()) ); connect( ProjectManager::self(), SIGNAL(subprojectCreated()), this, SLOT(slotUpdateFileList()) ); connect( ProjectManager::self(), SIGNAL(filesAdded()), this, SLOT(slotUpdateFileList()) ); connect( ProjectManager::self(), SIGNAL(filesRemoved()), this, SLOT(slotUpdateFileList()) ); createProperty( "program", Variant::Type::FileName ); property("program")->setCaption( i18n("Program") ); QString filter; filter = QString("*.flowcode *.cod *.asm *.basic *.c|%1").arg(i18n("All Supported Files")); filter += QString("\n*.flowcode|FlowCode (*.flowcode)"); filter += QString("\n*.cod|%1 (*.cod)").arg(i18n("Symbol File")); filter += QString("\n*.asm|%1 (*.asm)").arg(i18n("Assembly Code")); filter += QString("\n*.basic *.microbe|Microbe (*.basic, *.microbe)"); filter += QString("\n*.c|C (*.c)"); filter += QString("\n*|%1").arg(i18n("All Files")); property("program")->setFilter( filter ); // Used for restoring the pins on file loading before we have had a change // to compile the PIC program createProperty( "lastPackage", Variant::Type::String ); property("lastPackage")->setHidden( true ); // //HACK This is to enable loading with pre-0.3 files (which didn't set a "lastPackage" // // property). This will allow a P16F84 PIC to be initialized (which agrees with pre-0.3 // // behaviour), but it will also load it if // This to allow loading of the PIC component from pre-0.3 files (which didn't set a // "lastPackage" property). if ( !newItem ) property("lastPackage")->setValue("P16F84"); slotUpdateFileList(); slotUpdateBtns(); initPackage( nullptr ); } PICComponent::~PICComponent() { deletePICComponentPins(); delete m_pGpsim; } void PICComponent::dataChanged() { qDebug() << Q_FUNC_INFO; initPIC(false); } void PICComponent::initPIC( bool forceReload ) { if ( !m_bCreatedInitialPackage ) { qDebug() << Q_FUNC_INFO << " creating initial package"; // We are still being created, so other connectors will be expecting us to // have grown pins soonish. MicroInfo * microInfo = MicroLibrary::self()->microInfoWithID( dataString("lastPackage") ); if ( microInfo ) { initPackage( microInfo ); } else { qDebug() << Q_FUNC_INFO << " unknown last package: " << dataString("lastPackage"); } } QString newProgram = dataString("program"); qDebug() << Q_FUNC_INFO << "newProgram=" << newProgram; bool newFile = (m_picFile != newProgram); if ( !newFile && !forceReload ) { qDebug() << Q_FUNC_INFO << "not new program, not force reload, exiting"; return; } delete m_pGpsim; m_pGpsim = nullptr; switch ( GpsimProcessor::isValidProgramFile(newProgram) ) { case GpsimProcessor::DoesntExist: if ( newProgram == _def_PICComponent_fileName && !newProgram.isEmpty() ) break; KMessageBox::sorry( nullptr, i18n("The file \"%1\" does not exist.", newProgram ) ); m_picFile = QString::null; break; case GpsimProcessor::IncorrectType: if ( newProgram == _def_PICComponent_fileName && !newProgram.isEmpty() ) break; KMessageBox::sorry( nullptr, i18n("\"%1\" is not a valid PIC program.\nThe file must exist, and the extension should be \".cod\", \".asm\", \".flowcode\", \".basic\", \".microbe\" or \".c\".\n\".hex\" is allowed, provided that there is a corresponding \".cod\" file.", newProgram) ); m_picFile = QString::null; break; case GpsimProcessor::Valid: m_picFile = newProgram; m_symbolFile = createSymbolFile(); break; } slotUpdateBtns(); } void PICComponent::deletePICComponentPins() { const PICComponentPinMap::iterator picComponentMapEnd = m_picComponentPinMap.end(); for ( PICComponentPinMap::iterator it = m_picComponentPinMap.begin(); it != picComponentMapEnd; ++it ) delete it.value(); m_picComponentPinMap.clear(); } void PICComponent::initPackage( MicroInfo * microInfo ) { MicroPackage * microPackage = microInfo ? microInfo->package() : nullptr; if ( microPackage ) { m_bCreatedInitialPackage = true; //BEGIN Get pin IDs QStringList allPinIDs = microPackage->pinIDs(); QStringList ioPinIDs = microPackage->pinIDs( PicPin::type_bidir | PicPin::type_input | PicPin::type_open ); // Now, we make the unwanted pin ids blank, so a pin is not created for them const QStringList::iterator allPinIDsEnd = allPinIDs.end(); for ( QStringList::iterator it = allPinIDs.begin(); it != allPinIDsEnd; ++it ) { if ( !ioPinIDs.contains(*it) ) *it = ""; } //END Get pin IDs //BEGIN Remove old stuff // Remove old text TextMap textMapCopy = m_textMap; const TextMap::iterator textMapEnd = textMapCopy.end(); for ( TextMap::iterator it = textMapCopy.begin(); it != textMapEnd; ++it ) removeDisplayText(it.key()); // Remove the old pins deletePICComponentPins(); // Remove old nodes NodeInfoMap nodeMapCopy = m_nodeMap; const NodeInfoMap::iterator nodeMapEnd = nodeMapCopy.end(); for ( NodeInfoMap::iterator it = nodeMapCopy.begin(); it != nodeMapEnd; ++it ) { if ( !ioPinIDs.contains(it.key()) ) removeNode( it.key() ); } removeElements(); //END Remove old stuff //BEGIN Create new stuff initDIPSymbol( allPinIDs, 80 ); initDIP(allPinIDs); PicPinMap picPinMap = microPackage->pins( PicPin::type_bidir | PicPin::type_input | PicPin::type_open ); const PicPinMap::iterator picPinMapEnd = picPinMap.end(); for ( PicPinMap::iterator it = picPinMap.begin(); it != picPinMapEnd; ++it ) m_picComponentPinMap[it.key()] = new PICComponentPin( this, it.value() ); //END Create new stuff removeDisplayText( "no_file" ); addDisplayText( "picid", QRect(offsetX(), offsetY()-16, width(), 16), microInfo->id() ); } else { setSize( -48, -72, 96, 144 ); removeDisplayText( "picid" ); addDisplayText( "no_file", sizeRect(), i18n("(No\nprogram\nloaded)") ); } //BEGIN Update button positions int leftpos = (width()-88)/2+offsetX(); button("run")->setOriginalRect( QRect( leftpos, height()+4+offsetY(), 20, 20 ) ); button("pause")->setOriginalRect( QRect( leftpos+23, height()+4+offsetY(), 20, 20 ) ); button("reset")->setOriginalRect( QRect( leftpos+46, height()+4+offsetY(), 20, 20 ) ); button("reload")->setOriginalRect( QRect( leftpos+69, height()+4+offsetY(), 20, 20 ) ); updateAttachedPositioning(); //END Update button positions } void PICComponent::attachPICComponentPins() { if ( !m_pGpsim || !m_pGpsim->picProcessor() ) return; pic_processor * picProcessor = m_pGpsim->picProcessor(); const PICComponentPinMap::iterator end = m_picComponentPinMap.end(); for ( PICComponentPinMap::iterator it = m_picComponentPinMap.begin(); it != end; ++it ) it.value()->attach( picProcessor->get_pin( it.key() ) ); } void PICComponent::slotUpdateFileList() { QStringList preFileList = KTechlab::self()->recentFiles(); QStringList fileList; if ( ProjectInfo * info = ProjectManager::self()->currentProject() ) { const QList urls = info->childOutputURLs( ProjectItem::AllTypes, ProjectItem::ProgramOutput ); for (const QUrl &url : urls) fileList << url.toLocalFile(); } const QStringList::iterator end = preFileList.end(); for ( QStringList::iterator it = preFileList.begin(); it != end; ++it ) { const QUrl recentFile = QUrl::fromUserInput(*it); if (!recentFile.isLocalFile()) continue; const QString file = recentFile.toLocalFile(); if ( (file.endsWith(".flowcode") || file.endsWith(".asm") || file.endsWith(".cod") || file.endsWith(".basic") || file.endsWith(".microbe") ) && !fileList.contains(file) ) { fileList.append(file); } } QString fileName = dataString("program"); property("program")->setAllowed(fileList); property("program")->setValue( fileName.isEmpty() ? _def_PICComponent_fileName : fileName ); } void PICComponent::buttonStateChanged( const QString &id, bool state ) { if (!state) return; if ( id == "reload" ) { programReload(); return; } if (!m_pGpsim) return; if ( id == "run" ) m_pGpsim->setRunning(true); else if ( id == "pause" ) m_pGpsim->setRunning(false); else if ( id == "reset" ) { m_pGpsim->reset(); // Set all pin outputs to low const PICComponentPinMap::iterator end = m_picComponentPinMap.end(); for ( PICComponentPinMap::iterator it = m_picComponentPinMap.begin(); it != end; ++it ) it.value()->resetOutput(); } slotUpdateBtns(); } bool PICComponent::mouseDoubleClickEvent ( const EventInfo &eventInfo ) { Q_UNUSED(eventInfo); if ( m_picFile.isEmpty() || (m_picFile == _def_PICComponent_fileName) ) return false; (void) DocManager::self()->openURL(QUrl::fromLocalFile(m_picFile)); return true; } QString PICComponent::createSymbolFile() { qDebug() << Q_FUNC_INFO; m_bLoadingProgram = true; slotUpdateBtns(); return GpsimProcessor::generateSymbolFile( dataString("program"), this, SLOT(slotCODCreationSucceeded()), SLOT(slotCODCreationFailed()) ); } void PICComponent::slotCODCreationSucceeded() { qDebug() << Q_FUNC_INFO << " m_symbolFile=" << m_symbolFile; m_bLoadingProgram = false; delete m_pGpsim; m_pGpsim = new GpsimProcessor(m_symbolFile); if ( m_pGpsim->codLoadStatus() == GpsimProcessor::CodSuccess ) { MicroInfo * microInfo = m_pGpsim->microInfo(); if(!microInfo){ // FIXME we should be select somehow the type of the PIC. this is only a stability hack. qWarning() << Q_FUNC_INFO << "cannot identify the PIC, defaulting to P16F84" << endl; microInfo = MicroLibrary::self()->microInfoWithID("P16F84"); } property("lastPackage")->setValue( microInfo->id() ); initPackage( microInfo ); connect( m_pGpsim, SIGNAL(runningStatusChanged(bool )), this, SLOT(slotUpdateBtns()) ); attachPICComponentPins(); } else { m_pGpsim->displayCodLoadStatus(); delete m_pGpsim; m_pGpsim = nullptr; } slotUpdateBtns(); } void PICComponent::slotCODCreationFailed() { m_bLoadingProgram = false; slotUpdateBtns(); } void PICComponent::programReload() { qDebug() << Q_FUNC_INFO; delete m_pGpsim; m_pGpsim = nullptr; initPIC(true); slotUpdateBtns(); } void PICComponent::slotUpdateBtns() { // We can get called by the destruction of gpsim after our canvas has been set to nullptr if (!canvas()) { qDebug() << Q_FUNC_INFO << " no canvas, exiting"; return; } button("run")->setEnabled( m_pGpsim && !m_pGpsim->isRunning() ); button("pause")->setEnabled( m_pGpsim && m_pGpsim->isRunning() ); button("reset")->setEnabled( m_pGpsim ); button("reload")->setEnabled( !m_bLoadingProgram && (dataString("program") != _def_PICComponent_fileName) ); canvas()->setChanged( button("run")->boundingRect() ); canvas()->setChanged( button("pause")->boundingRect() ); canvas()->setChanged( button("reset")->boundingRect() ); canvas()->setChanged( button("reload")->boundingRect() ); } #endif diff --git a/src/electronics/components/piccomponent.h b/src/electronics/components/piccomponent.h index 42e2d87c..7bbff6e6 100644 --- a/src/electronics/components/piccomponent.h +++ b/src/electronics/components/piccomponent.h @@ -1,97 +1,97 @@ /*************************************************************************** * Copyright (C) 2003-2005 by David Saxton * * david@bluehaze.org * * * * 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. * ***************************************************************************/ #ifndef PICCOMPONENT_H #define PICCOMPONENT_H #include "config.h" #ifndef NO_GPSIM #include "component.h" -#include -#include +#include +#include class Document; class ECNode; class GpsimProcessor; class IOPIN; class KTechlab; class MicroInfo; class MicroPackage; class PIC_IOPORT; class PICComponent; class PICComponentPin; class PicPin; class TextDocument; typedef QMap< int, PICComponentPin * > PICComponentPinMap; /** @short Electronic PIC device @author David Saxton */ class PICComponent : public Component { Q_OBJECT public: PICComponent( ICNDocument * icnDocument, bool newItem, const char *id = nullptr ); ~PICComponent() override; static Item * construct( ItemDocument *itemDocument, bool newItem, const char *id ); static LibraryItem * libraryItem(); void buttonStateChanged( const QString &id, bool state ) override; bool mouseDoubleClickEvent( const EventInfo &eventInfo ) override; void programReload(); /** * Sets up the pins, text, etc for the given PIC type. If info is null, * then a generic rectangle is displayed (used when no file has been * loaded yet). */ void initPackage( MicroInfo * info ); public slots: void slotUpdateFileList(); void slotUpdateBtns(); protected slots: void slotCODCreationSucceeded(); void slotCODCreationFailed(); protected: /** * Attaches all PICComponentPins to the current instance of gpsim. */ void attachPICComponentPins(); void deletePICComponentPins(); /** * Attempts to compile the program to a symbol file, and connects the assembly * finish signal to loadGpsim */ QString createSymbolFile(); void dataChanged() override; /** * Initializes the PIC from the options the user has selected. */ void initPIC( bool forceReload ); QPointer m_pGpsim; QString m_picFile; ///< The input program that the user selected QString m_symbolFile; ///< The symbol file that was generated from m_picFile bool m_bLoadingProgram; ///< True between createSymbolFile being called and the file being created PICComponentPinMap m_picComponentPinMap; bool m_bCreatedInitialPackage; ///< Set true once the initial package is loaded; until then, will load a package from the lastPackage data static QString _def_PICComponent_fileName; }; #endif #endif diff --git a/src/electronics/components/piccomponentpin.cpp b/src/electronics/components/piccomponentpin.cpp index 0024e5ef..67dd5f99 100644 --- a/src/electronics/components/piccomponentpin.cpp +++ b/src/electronics/components/piccomponentpin.cpp @@ -1,178 +1,178 @@ /*************************************************************************** * Copyright (C) 2005 by David Saxton * * david@bluehaze.org * * * * 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 "config.h" #ifndef NO_GPSIM #include "micropackage.h" #include "piccomponent.h" #include "piccomponentpin.h" -#include +#include PICComponentPin::PICComponentPin( PICComponent * picComponent, PicPin picPin ) : m_id( picPin.pinID ) { m_gOutHigh = 0.0; m_gOutLow = 0.0; m_picPin = picPin; m_pPICComponent = picComponent; m_pLogicOut = nullptr; m_pLogicIn = nullptr; m_pIOPIN = nullptr; m_pStimulusNode = nullptr; Zth = 0.0; Vth = 0.0; switch ( picPin.type ) { case PicPin::type_input: { m_pLogicIn = picComponent->createLogicIn( picComponent->ecNodeWithID(picPin.pinID) ); break; } case PicPin::type_bidir: { m_pLogicOut = picComponent->createLogicOut( picComponent->ecNodeWithID(picPin.pinID), false ); m_gOutHigh = 0.004; m_gOutLow = 0.004; break; } case PicPin::type_open: { m_pLogicOut = picComponent->createLogicOut( picComponent->ecNodeWithID(picPin.pinID), false ); m_pLogicOut->setOutputHighVoltage(0.0); m_pLogicOut->setOutputHighConductance(0.0); m_gOutHigh = 0.0; m_gOutLow = 0.004; break; } case PicPin::type_vss: case PicPin::type_vdd: case PicPin::type_mclr: case PicPin::type_osc: default: break; } if (m_pLogicIn) m_pLogicIn->setCallback( this, (CallbackPtr)(&PICComponentPin::logicCallback) ); if (m_pLogicOut) m_pLogicOut->setCallback( this, (CallbackPtr)(&PICComponentPin::logicCallback) ); } PICComponentPin::~PICComponentPin() { if (m_pLogicIn) m_pLogicIn->setCallback( nullptr, (CallbackPtr)nullptr ); if (m_pLogicOut) m_pLogicOut->setCallback( nullptr, (CallbackPtr)nullptr ); delete m_pStimulusNode; } void PICComponentPin::attach( IOPIN * iopin ) { if (!iopin) { qWarning() << Q_FUNC_INFO << " iopin is nullptr" << endl; return; } if (m_pStimulusNode) { qWarning() << Q_FUNC_INFO << " Already have a node stimulus" << endl; return; } if (m_pIOPIN) { qWarning() << Q_FUNC_INFO << " Already have an iopin" << endl; return; } m_pIOPIN = iopin; m_pStimulusNode = new Stimulus_Node(m_id.toAscii()); m_pStimulusNode->attach_stimulus(iopin); m_pStimulusNode->attach_stimulus(this); // We need to tell the iopin whether or not we are high if (m_pLogicOut) logicCallback( m_pLogicOut->isHigh() ); else if (m_pLogicIn) logicCallback( m_pLogicIn->isHigh() ); } double PICComponentPin::get_Vth( ) { if (!m_pIOPIN) return 0.0; if ( m_pIOPIN->get_direction() == IOPIN::DIR_INPUT ) return Vth; else return m_pIOPIN->get_Vth(); } void PICComponentPin::set_nodeVoltage( double v ) { Q_UNUSED(v); if ( !m_pLogicOut || !m_pIOPIN ) return; if ( m_pIOPIN->get_direction() == IOPIN::DIR_INPUT ) { m_pLogicOut->setOutputHighConductance(0.0); m_pLogicOut->setOutputLowConductance(0.0); } else { m_pLogicOut->setHigh( m_pIOPIN->getDrivingState() ); m_pLogicOut->setOutputHighConductance(m_gOutHigh); m_pLogicOut->setOutputLowConductance(m_gOutLow); } } void PICComponentPin::logicCallback( bool state ) { if (!m_pIOPIN) return; Vth = state ? 5e10 : 0; bDrivingState = state; if ( m_pIOPIN->get_direction() == IOPIN::DIR_INPUT ) { Zth = 1e5; m_pIOPIN->setDrivenState(state); if (m_pStimulusNode) m_pStimulusNode->update(); } else Zth = 0; } void PICComponentPin::resetOutput() { if ( m_pLogicOut ) m_pLogicOut->setHigh( false ); } #endif diff --git a/src/electronics/components/piccomponentpin.h b/src/electronics/components/piccomponentpin.h index e6b46e47..e53a9b31 100644 --- a/src/electronics/components/piccomponentpin.h +++ b/src/electronics/components/piccomponentpin.h @@ -1,69 +1,69 @@ /*************************************************************************** * Copyright (C) 2005 by David Saxton * * david@bluehaze.org * * * * 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. * ***************************************************************************/ #ifndef PICCOMPONENTPIN_H #define PICCOMPONENTPIN_H #include "config.h" #ifndef NO_GPSIM #include "logic.h" #include "gpsim/stimuli.h" -#include +#include /** @short Controls a pin on the PIC component @author David Saxton */ class PICComponentPin : public CallbackClass, public stimulus { public: PICComponentPin( PICComponent * picComponent, PicPin picPin ); ~PICComponentPin() override; /** * Attach this to gpsim */ void attach( IOPIN * iopin ); /** * Called when the IOPIN this class is associated with changes state. * Updates the associated LogicOut / LogicIn / etc according to what * type of pin this is. */ void set_nodeVoltage( double v ) override; /** * Called from our logic pin when the logic changes state. */ void logicCallback( bool state ); /** * Sets the output (if has one) to low. Called when the user stops the * PIC. */ void resetOutput(); double get_Vth() override; protected: // Conductance of pin in different configurations double m_gOutHigh; double m_gOutLow; PicPin m_picPin; IOPIN * m_pIOPIN; LogicOut * m_pLogicOut; LogicIn * m_pLogicIn; PICComponent * m_pPICComponent; Stimulus_Node * m_pStimulusNode; const QString m_id; }; #endif #endif diff --git a/src/electronics/components/probe.cpp b/src/electronics/components/probe.cpp index d77cffdf..00edaabe 100644 --- a/src/electronics/components/probe.cpp +++ b/src/electronics/components/probe.cpp @@ -1,294 +1,294 @@ /*************************************************************************** * Copyright (C) 2005 by David Saxton * * david@bluehaze.org * * * * 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 "ecnode.h" #include "libraryitem.h" #include "logic.h" #include "pin.h" #include "probe.h" //HACK: This has to be included before the oscilloscope headers #include "oscilloscope.h" #include "oscilloscopedata.h" #include "simulator.h" #include "voltagesource.h" #include -#include +#include //BEGIN class Probe Probe::Probe( ICNDocument *icnDocument, bool newItem, const char *id ) : Component( icnDocument, newItem, id ) { p_probeData = nullptr; setSize( -16, -8, 32, 16 ); createProperty( "color", Variant::Type::Color ); property("color")->setCaption( i18n("Color") ); property("color")->setValue( QColor(Qt::black) ); } Probe::~ Probe() { delete p_probeData; } void Probe::dataChanged() { m_color = dataColor("color"); if (p_probeData) p_probeData->setColor(m_color); setChanged(); } //END class Probe //BEGIN class FloatingProbe FloatingProbe::FloatingProbe( ICNDocument *icnDocument, bool newItem, const char *id ) : Probe( icnDocument, newItem, id ) { p_probeData = m_pFloatingProbeData = static_cast(registerProbe(this)); property("color")->setValue( p_probeData->color() ); createProperty( "scaling", Variant::Type::Select ); property("scaling")->setCaption( i18n("Scaling") ); QStringMap allowed; allowed["Linear"] = i18n("Linear"); allowed["Logarithmic"] = i18n("Logarithmic"); property("scaling")->setAllowed( allowed ); property("scaling")->setValue("Linear"); property("scaling")->setAdvanced( true ); createProperty( "upper_abs_value", Variant::Type::Double ); property("upper_abs_value")->setCaption( i18n("Upper Absolute Value") ); property("upper_abs_value")->setValue(10.0); property("upper_abs_value")->setMinValue(0.0); property("upper_abs_value")->setUnit("V"); property("upper_abs_value")->setAdvanced(true); createProperty( "lower_abs_value", Variant::Type::Double ); property("lower_abs_value")->setCaption( i18n("Lower Absolute Value") ); property("lower_abs_value")->setValue(0.1); property("lower_abs_value")->setMinValue(0.0); property("lower_abs_value")->setUnit("V"); property("lower_abs_value")->setAdvanced(true); } FloatingProbe::~FloatingProbe() { } void FloatingProbe::dataChanged() { Probe::dataChanged(); if ( dataString("scaling") == "Linear" ) m_pFloatingProbeData->setScaling( FloatingProbeData::Linear ); else m_pFloatingProbeData->setScaling( FloatingProbeData::Logarithmic ); m_pFloatingProbeData->setUpperAbsValue( dataDouble("upper_abs_value") ); m_pFloatingProbeData->setLowerAbsValue( dataDouble("lower_abs_value") ); } void FloatingProbe::drawShape( QPainter &p ) { initPainter(p); int _x = int(x())-16; int _y = int(y())-8; p.drawRect( _x, _y, 32, 16 ); QPolygon bezier(4); bezier[0] = QPoint( _x+4, _y+10 ); bezier[1] = QPoint( _x+12, _y-6 ); bezier[2] = QPoint( _x+20, _y+24 ); bezier[3] = QPoint( _x+28, _y+4 ); p.setPen( QPen( m_color, 2 ) ); //p.drawCubicBezier(bezier); // 2018.12.07 QPainterPath path; path.moveTo( bezier.at(0) );; path.cubicTo( bezier.at(1), bezier.at(2), bezier.at(3) ); p.strokePath(path, p.pen()); deinitPainter(p); } //END class FloatingProbe //BEGIN class VoltageProbe Item* VoltageProbe::construct( ItemDocument *itemDocument, bool newItem, const char *id ) { return new VoltageProbe( (ICNDocument*)itemDocument, newItem, id ); } LibraryItem* VoltageProbe::libraryItem() { return new LibraryItem( QStringList(QString("ec/voltageprobe")), i18n("Voltage Probe"), i18n("Outputs"), "floatingprobe.png", LibraryItem::lit_component, VoltageProbe::construct ); } VoltageProbe::VoltageProbe( ICNDocument *icnDocument, bool newItem, const char *id ) : FloatingProbe( icnDocument, newItem, id ? id : "voltageprobe" ) { m_name = i18n("Voltage Probe"); init1PinLeft(); init1PinRight(); m_pPin1 = m_pNNode[0]->pin(); m_pPin2 = m_pPNode[0]->pin(); } VoltageProbe::~VoltageProbe() { } void VoltageProbe::stepNonLogic() { m_pFloatingProbeData->addDataPoint( m_pPin1->voltage() - m_pPin2->voltage() ); } //END class VoltageProbe //BEGIN class CurrentProbe Item* CurrentProbe::construct( ItemDocument *itemDocument, bool newItem, const char *id ) { return new CurrentProbe( (ICNDocument*)itemDocument, newItem, id ); } LibraryItem* CurrentProbe::libraryItem() { return new LibraryItem( QStringList(QString("ec/currentprobe")), i18n("Current Probe"), i18n("Outputs"), "floatingprobe.png", LibraryItem::lit_component, CurrentProbe::construct ); } CurrentProbe::CurrentProbe( ICNDocument *icnDocument, bool newItem, const char *id ) : FloatingProbe( icnDocument, newItem, id ? id : "currentprobe" ) { m_name = i18n("Current Probe"); init1PinLeft(0); init1PinRight(0); m_voltageSource = createVoltageSource( m_pNNode[0], m_pPNode[0], 0. ); } CurrentProbe::~CurrentProbe() { } void CurrentProbe::stepNonLogic() { m_pFloatingProbeData->addDataPoint( -m_voltageSource->cbranchCurrent(0) ); } //END class CurrentProbe //BEGIN class LogicProbe Item* LogicProbe::construct( ItemDocument *itemDocument, bool newItem, const char *id ) { return new LogicProbe( (ICNDocument*)itemDocument, newItem, id ); } LibraryItem* LogicProbe::libraryItem() { QStringList ids; ids << "ec/probe" << "ec/logicprobe"; return new LibraryItem( ids, i18n("Logic Probe"), i18n("Outputs"), "logicprobe.png", LibraryItem::lit_component, LogicProbe::construct ); } LogicProbe::LogicProbe( ICNDocument *icnDocument, bool newItem, const char *id ) : Probe( icnDocument, newItem, id ? id : "probe" ) { m_name = i18n("Logic Probe"); init1PinRight(); m_pIn = createLogicIn( m_pPNode[0] ); p_probeData = p_logicProbeData = static_cast(registerProbe(this)); property("color")->setValue( p_probeData->color() ); m_pSimulator = Simulator::self(); m_pIn->setCallback( this, (CallbackPtr)(&LogicProbe::logicCallback) ); logicCallback(false); } LogicProbe::~LogicProbe() { } void LogicProbe::logicCallback( bool value ) { p_logicProbeData->addDataPoint( LogicDataPoint( value, m_pSimulator->time() ) ); } void LogicProbe::drawShape( QPainter &p ) { initPainter(p); int _x = int(x())-16; int _y = int(y())-8; p.drawRect( _x, _y, 32, 16 ); p.setPen( QPen( m_color, 2 ) ); p.drawLine( _x+4, _y+11, _x+6, _y+11 ); p.drawLine( _x+6, _y+11, _x+6, _y+4 ); p.drawLine( _x+6, _y+4, _x+10, _y+4 ); p.drawLine( _x+10, _y+4, _x+10, _y+11 ); p.drawLine( _x+10, _y+11, _x+16, _y+11 ); p.drawLine( _x+16, _y+11, _x+16, _y+4 ); p.drawLine( _x+16, _y+4, _x+23, _y+4 ); p.drawLine( _x+23, _y+4, _x+23, _y+11 ); p.drawLine( _x+23, _y+11, _x+28, _y+11 ); // p.drawLine( _x+23, _y+11, _x+26, _y+11 ); // p.drawLine( _x+26, _y+11, _x+26, _y+4 ); // p.drawLine( _x+26, _y+4, _x+28, _y+4 ); deinitPainter(p); } //END class LogicProbe diff --git a/src/electronics/components/pushswitch.cpp b/src/electronics/components/pushswitch.cpp index 8627018d..7be2b17d 100644 --- a/src/electronics/components/pushswitch.cpp +++ b/src/electronics/components/pushswitch.cpp @@ -1,204 +1,204 @@ /*************************************************************************** * Copyright (C) 2005 by David Saxton * * david@bluehaze.org * * * * 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 "canvasitemparts.h" #include "libraryitem.h" #include "pushswitch.h" #include "switch.h" #include -#include -#include -#include +#include +#include +#include //#include //BEGIN class ECPTBSwitch Item* ECPTBSwitch::construct( ItemDocument *itemDocument, bool newItem, const char *id ) { return new ECPTBSwitch( (ICNDocument*)itemDocument, newItem, id ); } LibraryItem* ECPTBSwitch::libraryItem() { return new LibraryItem( QStringList(QString("ec/ptb_switch")), i18n("Push-to-Break"), i18n("Switches"), "ptb.png", LibraryItem::lit_component, ECPTBSwitch::construct ); } ECPTBSwitch::ECPTBSwitch( ICNDocument *icnDocument, bool newItem, const char *id ) : Component( icnDocument, newItem, (id) ? id : "ptb_switch" ) { m_name = i18n("Push to Break"); setSize( -16, -16, 32, 24 ); addButton( "button", QRect( -16, 8, 32, 20 ), "" ); createProperty( "button_text", Variant::Type::String ); property("button_text")->setCaption( i18n("Button Text") ); Variant * v = createProperty( "bounce", Variant::Type::Bool ); v->setCaption( i18n("Bounce") ); v->setAdvanced(true); v->setValue(false); v = createProperty( "bounce_period", Variant::Type::Double ); v->setCaption( i18n("Bounce Period") ); v->setAdvanced(true); v->setUnit("s"); v->setValue(5e-3); init1PinLeft(0); init1PinRight(0); m_switch = createSwitch( m_pPNode[0], m_pNNode[0], false ); pressed = false; } ECPTBSwitch::~ECPTBSwitch() { } void ECPTBSwitch::dataChanged() { button("button")->setText( dataString("button_text") ); bool bounce = dataBool("bounce"); int bouncePeriod_ms = int(dataDouble("bounce_period")*1e3); m_switch->setBounce( bounce, bouncePeriod_ms ); } void ECPTBSwitch::drawShape( QPainter &p ) { initPainter(p); int _x = (int)x()-16; int _y = (int)y()-8; const int radius = 2; const int _height = height()-8; int dy = pressed ? 6 : 4; p.drawLine( _x+width()/4, _y+dy, _x+(3*width())/4, _y+dy ); // Top horizontal line p.drawLine( _x, _y+(_height/2)-radius+dy, _x+width(), _y+(_height/2)-radius+dy ); // Bottom horizontal line p.drawLine( _x+width()/2, _y+dy, _x+width()/2, _y+(_height/2)-radius+dy ); // Vertical line p.drawEllipse( _x, _y+(_height/2)-radius, 2*radius, 2*radius ); // Left circle p.drawEllipse( _x+width()-2*radius+1, _y+(_height/2)-radius, 2*radius, 2*radius ); // Right circle deinitPainter(p); } void ECPTBSwitch::buttonStateChanged( const QString &id, bool state ) { if ( id != "button" ) return; m_switch->setState( state ? Switch::Open : Switch::Closed ); pressed = state; } //END class ECPTBSwitch //BEGIN class ECPTMSwitch Item* ECPTMSwitch::construct( ItemDocument *itemDocument, bool newItem, const char *id ) { return new ECPTMSwitch( (ICNDocument*)itemDocument, newItem, id ); } LibraryItem* ECPTMSwitch::libraryItem() { return new LibraryItem( QStringList(QString("ec/ptm_switch")), i18n("Push-to-Make"), i18n("Switches"), "ptm.png", LibraryItem::lit_component, ECPTMSwitch::construct ); } ECPTMSwitch::ECPTMSwitch( ICNDocument *icnDocument, bool newItem, const char *id ) : Component( icnDocument, newItem, (id) ? id : "ptm_switch" ) { m_name = i18n("Push to Make"); setSize( -16, -16, 32, 24 ); addButton( "button", QRect( -16, 8, 32, 20 ), "" ); createProperty( "button_text", Variant::Type::String ); property("button_text")->setCaption( i18n("Button Text") ); Variant * v = createProperty( "bounce", Variant::Type::Bool ); v->setCaption("Bounce"); v->setAdvanced(true); v->setValue(false); v = createProperty( "bounce_period", Variant::Type::Double ); v->setCaption("Bounce Period"); v->setAdvanced(true); v->setUnit("s"); v->setValue(5e-3); init1PinLeft(0); init1PinRight(0); m_switch = createSwitch( m_pPNode[0], m_pNNode[0], true ); pressed = false; } ECPTMSwitch::~ECPTMSwitch() { } void ECPTMSwitch::dataChanged() { button("button")->setText( dataString("button_text") ); bool bounce = dataBool("bounce"); int bouncePeriod_ms = int(dataDouble("bounce_period")*1e3); m_switch->setBounce( bounce, bouncePeriod_ms ); } void ECPTMSwitch::drawShape( QPainter &p ) { initPainter(p); int _x = (int)x()-16; int _y = (int)y()-8; const int radius = 2; const int _height = height()-8; int dy = pressed ? 1 : 3; p.drawLine( _x+width()/4, _y-dy, _x+(3*width())/4, _y-dy ); // Top horizontal line p.drawLine( _x, _y+(_height/2)-radius-dy, _x+width(), _y+(_height/2)-radius-dy ); // Bottom horizontal line p.drawLine( _x+width()/2, _y-dy, _x+width()/2, _y+(_height/2)-radius-dy ); // Vertical line p.drawEllipse( _x, _y+(_height/2)-radius, 2*radius, 2*radius ); // Left circle p.drawEllipse( _x+width()-2*radius+1, _y+(_height/2)-radius, 2*radius, 2*radius ); // Right circle deinitPainter(p); } void ECPTMSwitch::buttonStateChanged( const QString &id, bool state ) { if ( id != "button" ) return; m_switch->setState( state ? Switch::Closed : Switch::Open ); pressed = state; } //END class ECPTMSwitch diff --git a/src/electronics/components/ram.h b/src/electronics/components/ram.h index 2a56a11d..9450a9cc 100644 --- a/src/electronics/components/ram.h +++ b/src/electronics/components/ram.h @@ -1,50 +1,50 @@ /*************************************************************************** * Copyright (C) 2005 by David Saxton * * david@bluehaze.org * * * * 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. * ***************************************************************************/ #ifndef RAM_H #define RAM_H #include "component.h" #include "logic.h" -#include +#include //#include // 2018.10.17 /** @author David Saxton */ class RAM : public CallbackClass, public Component { public: RAM( ICNDocument *icnDocument, bool newItem, const char *id = nullptr ); ~RAM() override; static Item * construct( ItemDocument *itemDocument, bool newItem, const char *id ); static LibraryItem * libraryItem(); protected: void initPins(); void dataChanged() override; void inStateChanged( bool newState ); QBitArray m_data; LogicIn * m_pCS; // Chip select LogicIn * m_pOE; // Output enable LogicIn * m_pWE; // Write enable int m_wordSize; int m_addressSize; QVector m_address; QVector m_dataIn; QVector m_dataOut; }; #endif diff --git a/src/electronics/components/resistor.cpp b/src/electronics/components/resistor.cpp index a275ffcf..f2e80431 100644 --- a/src/electronics/components/resistor.cpp +++ b/src/electronics/components/resistor.cpp @@ -1,75 +1,75 @@ /*************************************************************************** * Copyright (C) 2003 by David Saxton * * david@bluehaze.org * * * * 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 "resistor.h" #include "libraryitem.h" #include "resistance.h" #include -#include +#include Item* Resistor::construct( ItemDocument *itemDocument, bool newItem, const char *id ) { return new Resistor( (ICNDocument*)itemDocument, newItem, id ); } LibraryItem* Resistor::libraryItem() { return new LibraryItem( QStringList(QString("ec/resistor")), i18n("Resistor"), i18n("Passive"), "resistor.png", LibraryItem::lit_component, Resistor::construct ); } Resistor::Resistor( ICNDocument *icnDocument, bool newItem, const char *id ) : Component( icnDocument, newItem, id ? id : "resistor" ) { m_name = i18n("Resistor"); setSize( -16, -8, 32, 16 ); init1PinLeft(); init1PinRight(); m_resistance = createResistance( m_pPNode[0], m_pNNode[0], 1. ); createProperty( "resistance", Variant::Type::Double ); property("resistance")->setCaption( i18n("Resistance") ); property("resistance")->setUnit( QChar(0x3a9) ); property("resistance")->setValue(1e4); property("resistance")->setMinValue(1e-6); addDisplayText( "res", QRect( -16, -22, 32, 12 ), "", false ); } Resistor::~Resistor() { } void Resistor::dataChanged() { double resistance = dataDouble("resistance"); QString display = QString::number( resistance / getMultiplier(resistance), 'g', 3 ) + getNumberMag(resistance) + QChar(0x3a9); setDisplayText( "res", display ); m_resistance->setResistance(resistance); } void Resistor::drawShape( QPainter &p ) { initPainter(p); p.drawRect( (int)x()-16, (int)y()-6, width(), 12 ); deinitPainter(p); } diff --git a/src/electronics/components/resistordip.cpp b/src/electronics/components/resistordip.cpp index 9fce5427..fd1ae0f1 100644 --- a/src/electronics/components/resistordip.cpp +++ b/src/electronics/components/resistordip.cpp @@ -1,130 +1,130 @@ /*************************************************************************** * Copyright (C) 2005 by David Saxton * * david@bluehaze.org * * * * 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 "libraryitem.h" #include "node.h" #include "resistance.h" #include "resistordip.h" #include -#include +#include Item* ResistorDIP::construct( ItemDocument *itemDocument, bool newItem, const char *id ) { return new ResistorDIP( (ICNDocument*)itemDocument, newItem, id ); } LibraryItem* ResistorDIP::libraryItem() { return new LibraryItem( QStringList(QString("ec/resistordip")), i18n("Resistor DIP"), i18n("Passive"), "resistordip.png", LibraryItem::lit_component, ResistorDIP::construct ); } ResistorDIP::ResistorDIP( ICNDocument *icnDocument, bool newItem, const char *id ) : Component( icnDocument, newItem, id ? id : "multiplexer" ) { m_name = i18n("Resistor DIP"); m_resistorCount = 0; for ( int i=0; isetCaption( i18n("Resistance") ); property("resistance")->setUnit( QChar(0x3a9) ); property("resistance")->setValue(1e4); property("resistance")->setMinValue(1e-6); createProperty( "count", Variant::Type::Int ); property("count")->setCaption( i18n("Count") ); property("count")->setMinValue(2); property("count")->setMaxValue(maxCount); property("count")->setValue(8); } ResistorDIP::~ResistorDIP() { } void ResistorDIP::dataChanged() { initPins(); const double resistance = dataDouble("resistance"); for ( int i=0; isetResistance(resistance); const QString display = QString::number( resistance / getMultiplier(resistance), 'g', 3 ) + getNumberMag(resistance) + QChar(0x3a9); addDisplayText( "res", QRect( offsetX(), offsetY()-16, 32, 12 ), display ); } void ResistorDIP::initPins() { const int count = dataInt("count"); const double resistance = dataDouble("resistance"); if ( count == m_resistorCount ) return; if ( count < m_resistorCount ) { for ( int i=count; i -#include +#include #include #include -#include +#include //BEGIN class ECRotoSwitch Item* ECRotoSwitch::construct( ItemDocument *itemDocument, bool newItem, const char *id ) { return new ECRotoSwitch( (ICNDocument*)itemDocument, newItem, id ); } LibraryItem* ECRotoSwitch::libraryItem() { return new LibraryItem( QStringList(QString("ec/roto_switch")), i18n("Rotary"), i18n("Switches"), "rotary.png", LibraryItem::lit_component, ECRotoSwitch::construct ); } ECRotoSwitch::ECRotoSwitch( ICNDocument *icnDocument, bool newItem, const char *id ) : Component( icnDocument, newItem, id ? id : "roto_switch" ), m_numPositions(0) { m_name = i18n("Rotary Switch"); //Q3PointArray pa; // 2018.08.14 - see below //pa.makeArc( -_pinInnerRadius, -_pinInnerRadius, 2*_pinInnerRadius, 2*_pinInnerRadius , 0, 16*360 ); QPainterPath path; path.addEllipse( -_pinInnerRadius, -_pinInnerRadius, 2*_pinInnerRadius, 2*_pinInnerRadius ); QPolygon pa = path.toFillPolygon().toPolygon(); setItemPoints( pa ); //setSize( -64, -64, 128, 128 ); //half the side length of the buttons int buttonRadius = 10; addButton( "go_left", QRect( -_pinOuterRadius / 3 - buttonRadius, _pinOuterRadius - 3 * buttonRadius, 2 * buttonRadius, 2 * buttonRadius ), "<", false ); addButton( "go_right", QRect(_pinOuterRadius / 3 - buttonRadius, _pinOuterRadius - 3 * buttonRadius, 2 * buttonRadius, 2 * buttonRadius ), ">", false ); /*Variant * v = createProperty( "button_map", Variant::Type::String ); v->setCaption( i18n("Button Map") ); v->setAdvanced(true); const QString defButtonMap("SSSSSSSSSSSM"); v->setValue(defButtonMap); */ Variant * v = createProperty( "num_positions", Variant::Type::Int ); v->setCaption( i18n("Number of Positions") ); v->setAdvanced(false); v->setValue(6); v->setMinValue(3); m_inNode = createPin(0,height()/2,270,"in"); v = createProperty( "bounce", Variant::Type::Bool ); v->setCaption("Bounce"); v->setAdvanced(true); v->setValue(false); v = createProperty( "bounce_period", Variant::Type::Double ); v->setCaption("Bounce Period"); v->setAdvanced(true); v->setUnit("s"); v->setValue(5e-3); v = createProperty( "cur_position", Variant::Type::Int ); v->setHidden( true ); v->setValue( 0 ); //v = createProperty( "left_momentary", Variant::Type::Bool ); //v->setCaption(i18n("Left Momentary" ) ); //v->setValue(false); } ECRotoSwitch::~ECRotoSwitch() { } void ECRotoSwitch::dataChanged() { bool bounce = dataBool("bounce"); int bouncePeriod_ms = int(dataDouble("bounce_period")*1e3); m_curPosition = dataInt( "cur_position" ); setUpSwitches(); if(m_positions[m_curPosition].posSwitch->state() != Switch::Closed) { m_positions[m_curPosition].posSwitch->setState(Switch::Closed); } for(int i = 0; i < m_numPositions; i++) { m_positions[i].posSwitch->setBounce( bounce, bouncePeriod_ms ); } } inline int roundTo10(int a){return ((a/10)+(a%10<5?0:1))*10;} void ECRotoSwitch::drawShape( QPainter &p ) { initPainter(p); int cx = static_cast(x()); int cy = static_cast(y()); const int rotorRadius = 5; //draw the rotor p.drawEllipse(cx - rotorRadius, cy - rotorRadius, 2 * rotorRadius, 2 * rotorRadius); //and its connection p.drawLine(cx, cy + rotorRadius, cx, cy + _pinInnerRadius); //draw the output positions double angleBetweenPositions = (4 * M_PI / 3) / (m_numPositions - 1); /// \internal \brief Round to the nearest multiple of 8 #define round_8(a) (((a) > 0) ? int(((a)+4)/8)*8 : int(((a)-4)/8)*8) for(int i = 0; i < m_numPositions ; i++) { double angle = (7*M_PI/6) - (i * angleBetweenPositions); int contactX = static_cast(_contactRingRadius * cos(angle)); int contactY = static_cast(_contactRingRadius * sin(angle)); p.drawEllipse(cx + contactX - _contactRadius, cy - contactY - _contactRadius, 2 * _contactRadius, 2 * _contactRadius); int pinX, pinY; switch(m_positions[i].pinAngle) { case 180: pinX = _pinInnerRadius; pinY = round_8(contactY); break; case 90: pinX = round_8(contactX); pinY = _pinInnerRadius; break; case 0: pinX = -_pinInnerRadius; pinY = round_8(contactY); break; default: assert(!"Bad pin angle"); } p.drawLine(cx + contactX, cy - contactY, cx + pinX, cy - pinY); } #undef round_8 //draw the connection to the selected position double angle = (7 * M_PI / 6) - (m_curPosition * angleBetweenPositions); int contactX = static_cast(_contactRingRadius * cos(angle)); int contactY = static_cast(_contactRingRadius * sin(angle)); int rotorX = static_cast(rotorRadius * cos(angle)); int rotorY = static_cast(rotorRadius * sin(angle)); p.drawLine(cx + rotorX, cy - rotorY, cx + contactX, cy - contactY); deinitPainter(p); } void ECRotoSwitch::buttonStateChanged( const QString & id, bool state ) { SwitchPosition& curSP = m_positions[m_curPosition]; int nextPos = m_curPosition; if(m_numPositions < 2) { return; } if(!state) //release { if(!curSP.isMomentary) return; if(m_curPosition == 0) { nextPos = m_curPosition + 1; } else if(m_curPosition == m_numPositions - 1) { nextPos = m_curPosition - 1; } } else //press { if(id == "go_left" && m_curPosition > 0) { nextPos = m_curPosition - 1; } else if(id == "go_right" && m_curPosition < m_numPositions - 1) { nextPos = m_curPosition + 1; } } if(nextPos != m_curPosition) { SwitchPosition& nextSP = m_positions[nextPos]; curSP.posSwitch->setState(Switch::Open); nextSP.posSwitch->setState(Switch::Closed); m_curPosition = nextPos; property( "cur_position" )->setValue( m_curPosition ); } } /*! Set up the switches according to the button_map * */ void ECRotoSwitch::setUpSwitches() { if( dataInt("num_positions") == m_numPositions ) { // number of positions didn't change, so we don't have to do anything. return; } //this uses the _old_ value of m_numPositions! for(int i=0; i= m_numPositions ) { setActivePosition( m_numPositions - 1 ); } m_positions.clear(); m_positions.reserve(m_numPositions); double angleBetweenPositions = (4 * M_PI / 3)/(m_numPositions - 1); for(int i = 0; i < m_numPositions; i++) { double angle = (7 * M_PI / 6) - (i * angleBetweenPositions); int contactX = static_cast(_contactRingRadius * cos(angle)); int contactY = static_cast(_contactRingRadius * sin(angle)); SwitchPosition sp; if(angle > 3 * M_PI / 4) { sp.pinAngle = 0; contactX = -_pinOuterRadius; } else if(angle > M_PI / 4) { sp.pinAngle = 90; contactY=_pinOuterRadius; } else { sp.pinAngle = 180; contactX = _pinOuterRadius; } // qDebug() << contactX <<", "<< contactY <setState(Switch::Open); nextSP.posSwitch->setState(Switch::Closed); m_curPosition = newPosition; property( "cur_position" )->setValue( m_curPosition ); } //END class ECRotoSwitch diff --git a/src/electronics/components/serialportcomponent.cpp b/src/electronics/components/serialportcomponent.cpp index a26216f7..2ec4ee34 100644 --- a/src/electronics/components/serialportcomponent.cpp +++ b/src/electronics/components/serialportcomponent.cpp @@ -1,230 +1,231 @@ /*************************************************************************** * Copyright (C) 2005 by David Saxton * * david@bluehaze.org * * * * 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 "port.h" #include "serialportcomponent.h" #include "ecnode.h" #include "itemdocument.h" #include "libraryitem.h" #include "pin.h" #include "resistance.h" -#include #include -#include + +#include +#include #include #include Item* SerialPortComponent::construct( ItemDocument *itemDocument, bool newItem, const char *id ) { return new SerialPortComponent( (ICNDocument*)itemDocument, newItem, id ); } LibraryItem* SerialPortComponent::libraryItem() { return new LibraryItem( QStringList(QString("ec/serial_port")), i18n("Serial Port"), i18n("Connections"), "ic1.png", LibraryItem::lit_component, SerialPortComponent::construct ); } SerialPortComponent::SerialPortComponent( ICNDocument *icnDocument, bool newItem, const char *id ) : Component( icnDocument, newItem, id ? id : "serial_port" ) { m_name = i18n("Serial Port"); QPolygon pa( 4 ); pa[0] = QPoint( -32, -48 ); pa[1] = QPoint( 32, -40 ); pa[2] = QPoint( 32, 40 ); pa[3] = QPoint( -32, 48 ); setItemPoints( pa ); m_pSerialPort = new SerialPort(); ECNode * pin = nullptr; // Works pin = createPin( -40, 32, 0, "CD" ); addDisplayText( "CD", QRect( -28, 24, 28, 16 ), "CD", true, Qt::AlignLeft | Qt::AlignVCenter ); m_pCD = createLogicOut( pin, false ); // Doesn't work // pin = createPin( -40, 16, 0, "RD" ); addDisplayText( "RD", QRect( -28, 8, 28, 16 ), "RD", true, Qt::AlignLeft | Qt::AlignVCenter ); // m_pRD = createLogicOut( pin, false ); // Works pin = createPin( -40, 0, 0, "TD" ); addDisplayText( "TD", QRect( -28, -8, 28, 16 ), "TD", true, Qt::AlignLeft | Qt::AlignVCenter ); m_pTD = createLogicIn( pin); m_pTD->setCallback( this, (CallbackPtr)(&SerialPortComponent::tdCallback) ); // Works pin = createPin( -40, -16, 0, "DTR" ); addDisplayText( "DTR", QRect( -28, -24, 28, 16 ), "DTR", true, Qt::AlignLeft | Qt::AlignVCenter ); m_pDTR = createLogicIn( pin ); m_pDTR->setCallback( this, (CallbackPtr)(&SerialPortComponent::dtrCallback) ); // N/A pin = createPin( -40, -32, 0, "GND" ); addDisplayText( "GND", QRect( -28, -40, 28, 16 ), "GND", true, Qt::AlignLeft | Qt::AlignVCenter ); pin->pin()->setGroundType( Pin::gt_always ); // Doesn't work // pin = createPin( 40, 24, 180, "DSR" ); addDisplayText( "DSR", QRect( 0, 16, 28, 16 ), "DSR", true, Qt::AlignRight | Qt::AlignVCenter ); // m_pDSR = createLogicIn( pin ); // m_pDSR->setCallback( this, (CallbackPtr)(&SerialPortComponent::dsrCallback) ); // Doesn't work // pin = createPin( 40, 8, 180, "RTS" ); addDisplayText( "RTS", QRect( 0, 0, 28, 16 ), "RTS", true, Qt::AlignRight | Qt::AlignVCenter ); // m_pRTS = createLogicIn( pin ); // m_pRTS->setCallback( this, (CallbackPtr)(&SerialPortComponent::rtsCallback) ); // Works pin = createPin( 40, -8, 180, "CTS" ); addDisplayText( "CTS", QRect( 0, -16, 28, 16 ), "CTS", true, Qt::AlignRight | Qt::AlignVCenter ); m_pCTS = createLogicOut( pin, false ); // Works pin = createPin( 40, -24, 180, "RI" ); addDisplayText( "RI", QRect( 0, -32, 28, 16 ), "RI", true, Qt::AlignRight | Qt::AlignVCenter ); m_pRI = createLogicOut( pin, false ); Variant * v = createProperty( "port", Variant::Type::Combo ); v->setAllowed( SerialPort::ports( Port::ExistsAndRW ) ); v->setCaption( i18n("Port") ); // v = createProperty( "baudRate", Variant::Type::Select ); // v->setAllowed( QStringList::split( ",", "B0,B50,B75,B110,B134,B150,B200,B300,B600,B1200,B1800,B2400,B4800,B9600,B19200,B38400" ) ); // v->setCaption( i18n("Baud rate") ); // v->setValue("B9600"); } SerialPortComponent::~SerialPortComponent() { delete m_pSerialPort; } void SerialPortComponent::dataChanged() { #if 0 QString baudString = dataString("baudRate"); unsigned baudRate = 0; if ( baudString == "B0" ) baudRate = B0; else if ( baudString == "B50" ) baudRate = B50; else if ( baudString == "B75" ) baudRate = B75; else if ( baudString == "B110" ) baudRate = B110; else if ( baudString == "B134" ) baudRate = B134; else if ( baudString == "B150" ) baudRate = B150; else if ( baudString == "B200" ) baudRate = B200; else if ( baudString == "B300" ) baudRate = B300; else if ( baudString == "B600" ) baudRate = B600; else if ( baudString == "B1200" ) baudRate = B1200; else if ( baudString == "B1800" ) baudRate = B1800; else if ( baudString == "B2400" ) baudRate = B2400; else if ( baudString == "B4800" ) baudRate = B4800; else if ( baudString == "B9600" ) baudRate = B9600; else if ( baudString == "B19200" ) baudRate = B19200; else if ( baudString == "B38400" ) baudRate = B38400; else { qCritical() << Q_FUNC_INFO << "Unknown baud rate = \""<closePort(); return; } if ( ! m_pSerialPort->openPort( port, baudRate ) ) { p_itemDocument->canvas()->setMessage( i18n("Could not open port %1", port ) ); return; } } void SerialPortComponent::stepNonLogic() { m_pCD->setHigh( m_pSerialPort->pinState( SerialPort::CD ) ); // m_pRD->setHigh( m_pSerialPort->pinState( SerialPort::RD ) ); m_pCTS->setHigh( m_pSerialPort->pinState( SerialPort::CTS ) ); m_pRI->setHigh( m_pSerialPort->pinState( SerialPort::RI ) ); } void SerialPortComponent::tdCallback( bool isHigh ) { m_pSerialPort->setPinState( SerialPort::TD, isHigh ); } void SerialPortComponent::dtrCallback( bool isHigh ) { m_pSerialPort->setPinState( SerialPort::DTR, isHigh ); } void SerialPortComponent::dsrCallback( bool isHigh ) { m_pSerialPort->setPinState( SerialPort::DSR, isHigh ); } void SerialPortComponent::rtsCallback( bool isHigh ) { m_pSerialPort->setPinState( SerialPort::RTS, isHigh ); } void SerialPortComponent::drawShape( QPainter & p ) { drawPortShape( p ); } diff --git a/src/electronics/components/toggleswitch.cpp b/src/electronics/components/toggleswitch.cpp index b8b71d1f..e0571ea4 100644 --- a/src/electronics/components/toggleswitch.cpp +++ b/src/electronics/components/toggleswitch.cpp @@ -1,403 +1,403 @@ /*************************************************************************** * Copyright (C) 2005 by David Saxton * * david@bluehaze.org * * * * 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 "toggleswitch.h" #include "canvasitemparts.h" #include "ecnode.h" #include "libraryitem.h" #include "switch.h" #include -#include +#include //BEGIN class ECDPDT Item* ECDPDT::construct( ItemDocument *itemDocument, bool newItem, const char *id ) { return new ECDPDT( (ICNDocument*)itemDocument, newItem, id ); } LibraryItem* ECDPDT::libraryItem() { return new LibraryItem( QStringList(QString("ec/dpdt_toggle")), i18n("DPDT"), i18n("Switches"), "dpdt.png", LibraryItem::lit_component, ECDPDT::construct ); } ECDPDT::ECDPDT( ICNDocument *icnDocument, bool newItem, const char *id ) : Component( icnDocument, newItem, id ? id : "dpdt_toggle" ) { m_name = i18n("DPDT Toggle"); setSize( -16, -32, 32, 64 ); addButton( "button", QRect( -16, 32, 32, 20 ), "", true ); createProperty( "button_text", Variant::Type::String ); property("button_text")->setCaption( i18n("Button Text") ); Variant * v = createProperty( "bounce", Variant::Type::Bool ); v->setCaption("Bounce"); v->setAdvanced(true); v->setValue(false); v = createProperty( "bounce_period", Variant::Type::Double ); v->setCaption("Bounce Period"); v->setAdvanced(true); v->setUnit("s"); v->setValue(5e-3); init4PinRight( -24, -8, 8, 24 ); init2PinLeft( -16, 16 ); m_switch1 = createSwitch( m_pNNode[0], m_pPNode[0], false ); m_switch2 = createSwitch( m_pNNode[0], m_pPNode[1], true ); m_switch3 = createSwitch( m_pNNode[1], m_pPNode[2], false ); m_switch4 = createSwitch( m_pNNode[1], m_pPNode[3], true ); pressed = false; } ECDPDT::~ECDPDT() { } void ECDPDT::dataChanged() { button("button")->setText( dataString("button_text") ); bool bounce = dataBool("bounce"); int bouncePeriod_ms = int(dataDouble("bounce_period")*1e3); m_switch1->setBounce( bounce, bouncePeriod_ms ); m_switch2->setBounce( bounce, bouncePeriod_ms ); m_switch3->setBounce( bounce, bouncePeriod_ms ); m_switch4->setBounce( bounce, bouncePeriod_ms ); } void ECDPDT::drawShape( QPainter &p ) { initPainter(p); int _x = (int)x()-16; int _y = (int)y()-32; const int radius = 2; p.drawEllipse( _x, _y+15, 2*radius, 2*radius ); p.drawEllipse( _x, _y+47, 2*radius, 2*radius ); p.drawEllipse( _x+width()-2*radius+1, _y+7, 2*radius, 2*radius ); p.drawEllipse( _x+width()-2*radius+1, _y+23, 2*radius, 2*radius ); p.drawEllipse( _x+width()-2*radius+1, _y+39, 2*radius, 2*radius ); p.drawEllipse( _x+width()-2*radius+1, _y+55, 2*radius, 2*radius ); const int dy = pressed ? 6 : -6; p.drawLine( _x+2*radius, _y+16, _x+width()-2*radius+2, _y+16+dy ); p.drawLine( _x+2*radius, _y+48, _x+width()-2*radius+2, _y+48+dy ); deinitPainter(p); } void ECDPDT::buttonStateChanged( const QString &, bool state ) { pressed = state; m_switch1->setState( state ? Switch::Open : Switch::Closed ); m_switch2->setState( state ? Switch::Closed : Switch::Open ); m_switch3->setState( state ? Switch::Open : Switch::Closed ); m_switch4->setState( state ? Switch::Closed : Switch::Open ); } //END class ECDPDT //BEGIN class ECDPST Item* ECDPST::construct( ItemDocument *itemDocument, bool newItem, const char *id ) { return new ECDPST( (ICNDocument*)itemDocument, newItem, id ); } LibraryItem* ECDPST::libraryItem() { return new LibraryItem( QStringList(QString("ec/dpst_toggle")), i18n("DPST"), i18n("Switches"), "dpst.png", LibraryItem::lit_component, ECDPST::construct ); } ECDPST::ECDPST( ICNDocument *icnDocument, bool newItem, const char *id ) : Component( icnDocument, newItem, id ? id : "dpst_toggle" ) { m_name = i18n("DPST Toggle"); setSize( -16, -16, 32, 32 ); addButton( "button", QRect( -16, 16, 32, 20 ), "", true ); createProperty( "button_text", Variant::Type::String ); property("button_text")->setCaption( i18n("Button Text") ); Variant * v = createProperty( "bounce", Variant::Type::Bool ); v->setCaption("Bounce"); v->setAdvanced(true); v->setValue(false); v = createProperty( "bounce_period", Variant::Type::Double ); v->setCaption("Bounce Period"); v->setAdvanced(true); v->setUnit("s"); v->setValue(5e-3); init2PinLeft( -8, 8 ); init2PinRight( -8, 8 ); m_switch1 = createSwitch( m_pPNode[0], m_pNNode[0], true ); m_switch2 = createSwitch( m_pPNode[1], m_pNNode[1], true ); pressed = false; } ECDPST::~ECDPST() { } void ECDPST::dataChanged() { button("button")->setText( dataString("button_text") ); bool bounce = dataBool("bounce"); int bouncePeriod_ms = int(dataDouble("bounce_period")*1e3); m_switch1->setBounce( bounce, bouncePeriod_ms ); m_switch2->setBounce( bounce, bouncePeriod_ms ); } void ECDPST::drawShape( QPainter &p ) { initPainter(p); int _x = (int)x()-16; int _y = (int)y()-16; const int radius = 2; p.drawEllipse( _x, _y+6, 2*radius, 2*radius ); p.drawEllipse( _x, _y+22, 2*radius, 2*radius ); p.drawEllipse( _x+width()-2*radius+1, _y+6, 2*radius, 2*radius ); p.drawEllipse( _x+width()-2*radius+1, _y+22, 2*radius, 2*radius ); const int dy = pressed ? 6 : 0; p.drawLine( _x+2*radius,_y+7,_x+width()-2*radius,_y+1+dy ); p.drawLine( _x+2*radius,_y+24,_x+width()-2*radius,_y+18+dy ); deinitPainter(p); } void ECDPST::buttonStateChanged( const QString &, bool state ) { m_switch1->setState( state ? Switch::Closed : Switch::Open ); m_switch2->setState( state ? Switch::Closed : Switch::Open ); pressed = state; } //END class ECDPST //BEGIN class ECSPDT Item* ECSPDT::construct( ItemDocument *itemDocument, bool newItem, const char *id ) { return new ECSPDT( (ICNDocument*)itemDocument, newItem, id ); } LibraryItem* ECSPDT::libraryItem() { return new LibraryItem( QStringList(QString("ec/spdt_toggle")), i18n("SPDT"), i18n("Switches"), "spdt.png", LibraryItem::lit_component, ECSPDT::construct ); } ECSPDT::ECSPDT( ICNDocument *icnDocument, bool newItem, const char *id ) : Component( icnDocument, newItem, id ? id : "spdt_toggle" ) { m_name = i18n("SPDT Toggle"); setSize( -16, -16, 32, 32 ); addButton( "button", QRect( -16, 16, width(), 20 ), "", true ); createProperty( "button_text", Variant::Type::String ); property("button_text")->setCaption( i18n("Button Text") ); Variant * v = createProperty( "bounce", Variant::Type::Bool ); v->setCaption("Bounce"); v->setAdvanced(true); v->setValue(false); v = createProperty( "bounce_period", Variant::Type::Double ); v->setCaption("Bounce Period"); v->setAdvanced(true); v->setUnit("s"); v->setValue(5e-3); init1PinLeft( 0 ); init2PinRight( -8, 8 ); m_switch1 = createSwitch( m_pNNode[0], m_pPNode[0], false ); m_switch2 = createSwitch( m_pNNode[0], m_pPNode[1], true ); pressed = false; } ECSPDT::~ECSPDT() { } void ECSPDT::dataChanged() { button("button")->setText( dataString("button_text") ); bool bounce = dataBool("bounce"); int bouncePeriod_ms = int(dataDouble("bounce_period")*1e3); m_switch1->setBounce( bounce, bouncePeriod_ms ); m_switch2->setBounce( bounce, bouncePeriod_ms ); } void ECSPDT::drawShape( QPainter &p ) { initPainter(p); int _x = (int)x()-16; int _y = (int)y()-16; const int radius = 2; p.drawEllipse( _x, _y+15, 2*radius, 2*radius ); p.drawEllipse( _x+width()-2*radius+1, _y+6, 2*radius, 2*radius ); p.drawEllipse( _x+width()-2*radius+1, _y+22, 2*radius, 2*radius ); const int dy = pressed ? 21 : 10; p.drawLine( _x+2*radius, _y+16, _x+width()-2*radius+2, _y+dy ); deinitPainter(p); } void ECSPDT::buttonStateChanged( const QString &, bool state ) { pressed = state; m_switch1->setState( state ? Switch::Open : Switch::Closed ); m_switch2->setState( state ? Switch::Closed : Switch::Open ); } //END class ECSPDT //BEGIN class ECSPST Item* ECSPST::construct( ItemDocument *itemDocument, bool newItem, const char *id ) { return new ECSPST( (ICNDocument*)itemDocument, newItem, id ); } LibraryItem* ECSPST::libraryItem() { return new LibraryItem( QStringList(QString("ec/spst_toggle")), i18n("SPST"), i18n("Switches"), "spst.png", LibraryItem::lit_component, ECSPST::construct ); } ECSPST::ECSPST( ICNDocument *icnDocument, bool newItem, const char *id ) : Component( icnDocument, newItem, id ? id : "spst_toggle" ) { m_name = i18n("SPST Toggle"); setSize( -16, -8, 32, 16 ); pressed = false; addButton( "button", QRect( -16, 8, width(), 20 ), "", true ); createProperty( "button_text", Variant::Type::String ); property("button_text")->setCaption( i18n("Button Text") ); Variant * v = createProperty( "bounce", Variant::Type::Bool ); v->setCaption("Bounce"); v->setAdvanced(true); v->setValue(false); v = createProperty( "bounce_period", Variant::Type::Double ); v->setCaption("Bounce Period"); v->setAdvanced(true); v->setUnit("s"); v->setValue(5e-3); button("button")->setState(pressed); init1PinLeft(); init1PinRight(); m_switch = createSwitch( m_pNNode[0], m_pPNode[0], !pressed ); } ECSPST::~ECSPST() { } void ECSPST::dataChanged() { button("button")->setText( dataString("button_text") ); bool bounce = dataBool("bounce"); int bouncePeriod_ms = int(dataDouble("bounce_period")*1e3); m_switch->setBounce( bounce, bouncePeriod_ms ); } void ECSPST::drawShape( QPainter &p ) { initPainter(p); int _x = (int)x()-16; int _y = (int)y()-8; const int radius = 2; p.drawEllipse( _x, _y+7, 2*radius, 2*radius ); p.drawEllipse( _x+width()-2*radius+1, _y+7, 2*radius, 2*radius ); const int dy = pressed ? 0 : -6; p.drawLine( _x+2*radius, _y+8, _x+width()-2*radius, _y+8+dy ); deinitPainter(p); } void ECSPST::buttonStateChanged( const QString &, bool state ) { pressed = state; m_switch->setState( state ? Switch::Closed : Switch::Open ); } //END class ECSPST diff --git a/src/electronics/components/variablecapacitor.cpp b/src/electronics/components/variablecapacitor.cpp index c6703777..c0683fc1 100644 --- a/src/electronics/components/variablecapacitor.cpp +++ b/src/electronics/components/variablecapacitor.cpp @@ -1,179 +1,180 @@ /*************************************************************************** * Copyright (C) 2006 by William Hillerby * * william.hillerby@ntlworld.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 "canvasitemparts.h" #include "capacitance.h" #include "variablecapacitor.h" #include "ecnode.h" #include "libraryitem.h" #include -#include -#include -#include + +#include +#include +#include Item* VariableCapacitor::construct( ItemDocument *itemDocument, bool newItem, const char *id ) { return new VariableCapacitor( (ICNDocument*)itemDocument, newItem, id ); } LibraryItem* VariableCapacitor::libraryItem() { return new LibraryItem( QStringList(QString("ec/variablecapacitor")), i18n("Variable Capacitor"), i18n("Passive"), "variable_capacitor.png", LibraryItem::lit_component, VariableCapacitor::construct ); } VariableCapacitor::VariableCapacitor( ICNDocument* icnDocument, bool newItem, const QString& id ) : Component( icnDocument, newItem, (!id.isEmpty()) ? id : "variable capacitor" ) { m_name = i18n("Variable Capacitor"); // Top Left(x,y) from centre point, width, height. setSize( -16, -8, 32, 16 ); // william - you might want to change this value. I added this line as it // was being used unitialized (in the sliderValueChanged function when // addSlider is called later on), and causing a crash - david. m_tickValue = 1; m_maxCapacitance = 0.0001; m_minCapacitance = 0.00005; m_currCapacitance = m_minCapacitance + ( ( m_maxCapacitance - m_minCapacitance ) / 2 ); init1PinLeft(); init1PinRight(); m_pNNode[0]->setLength( 15 ); m_pPNode[0]->setLength( 15 ); m_pCapacitance = createCapacitance( m_pNNode[0], m_pPNode[0], m_currCapacitance ); addDisplayText( "capacitance", QRect( -8, -26, 16, 16 ), "", false ); createProperty( "currcapacitance", Variant::Type::Double ); property("currcapacitance")->setCaption( i18n("Capacitance") ); property("currcapacitance")->setUnit("F"); property("currcapacitance")->setMinValue(1e-12); property("currcapacitance")->setMaxValue(1e12); property("currcapacitance")->setValue( m_currCapacitance ); createProperty( "maximum capacitance", Variant::Type::Double ); property("maximum capacitance")->setCaption( i18n("Max") ); property("maximum capacitance")->setUnit("F"); property("maximum capacitance")->setMinValue(1e-12); property("maximum capacitance")->setMaxValue(1e12); property("maximum capacitance")->setValue( m_maxCapacitance ); createProperty( "minimum capacitance", Variant::Type::Double ); property("minimum capacitance")->setCaption( i18n("Min") ); property("minimum capacitance")->setUnit("F"); property("minimum capacitance")->setMinValue(1e-12); property("minimum capacitance")->setMaxValue(1e12); property("minimum capacitance")->setValue( m_minCapacitance ); Slider * s = addSlider( "slider", 0, 100, 1, 50, Qt::Horizontal, QRect( -16, 10, 32, 16 ) ); m_pSlider = static_cast(s->widget()); } VariableCapacitor::~VariableCapacitor() {} void VariableCapacitor::dataChanged() { double new_minCapacitance = dataDouble( "minimum capacitance" ); double new_maxCapacitance = dataDouble( "maximum capacitance" ); if( new_minCapacitance != m_minCapacitance ) { if( new_minCapacitance >= m_maxCapacitance ) { m_minCapacitance = m_maxCapacitance; property( "minimum capacitance" )->setValue( m_minCapacitance ); } else m_minCapacitance = new_minCapacitance; } if( new_maxCapacitance != m_maxCapacitance ) { if( new_maxCapacitance <= m_minCapacitance ) { m_maxCapacitance = m_minCapacitance; property( "maximum capacitance" )->setValue( m_maxCapacitance ); } else m_maxCapacitance = new_maxCapacitance; } /* Attempt at fixme. m_currCapacitance = property( "currcapacitance" )->value().asDouble(); if(m_currCapacitance > m_maxCapacitance) m_currCapacitance = m_maxCapacitance; else if(m_currCapacitance < m_minCapacitance) m_currCapacitance = m_minCapacitance; */ m_tickValue = ( m_maxCapacitance - m_minCapacitance ) / m_pSlider->maximum(); property( "currcapacitance" )->setValue( m_currCapacitance ); // Calculate the capacitance jump per tick of a 100 tick slider. sliderValueChanged( "slider", slider("slider")->value() ); } void VariableCapacitor::sliderValueChanged( const QString &id, int newValue ) { if ( id != "slider" ) return; /** @todo fix slider so current cap can be set in toolbar and editor and slider updates */ m_currCapacitance = m_minCapacitance + ( newValue * m_tickValue ); // Set the new capacitance value. m_pCapacitance->setCapacitance( m_currCapacitance ); // Update property. property( "currcapacitance" )->setValue( m_currCapacitance ); QString display = QString::number( m_currCapacitance / getMultiplier( m_currCapacitance ), 'g', 3 ) + getNumberMag( m_currCapacitance ) + "F"; setDisplayText( "capacitance", display ); } void VariableCapacitor::drawShape( QPainter &p ) { initPainter(p); // Get centre point of component. int _y = (int)y(); int _x = (int)x(); p.drawRect( _x-8, _y-8, 5, 16 ); p.drawRect( _x+3, _y-8, 5, 16 ); // p.drawLine( _x-8, _y, _x-16, _y ); // p.drawLine( _x+8, _y, _x+16, _y ); // Diagonally pointing arrow QPolygon pa(3); pa[0] = QPoint( -4, 0 ); pa[1] = QPoint( -2, 4 ); pa[2] = QPoint( 0, 0 ); pa.translate( _x+16, _y-8 ); p.setBrush( p.pen().color() ); p.drawPolygon( pa ); p.drawLine( _x-16, _y+8, _x+16, _y-8 ); deinitPainter(p); } diff --git a/src/electronics/components/variableresistor.cpp b/src/electronics/components/variableresistor.cpp index c605e374..f67564e6 100644 --- a/src/electronics/components/variableresistor.cpp +++ b/src/electronics/components/variableresistor.cpp @@ -1,161 +1,162 @@ /*************************************************************************** * Copyright (C) 2006 by William Hillerby - william.hillerby@ntlworld.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 "variableresistor.h" #include "canvasitemparts.h" #include "resistance.h" #include "ecnode.h" #include "libraryitem.h" #include -#include -#include -#include + +#include +#include +#include Item* VariableResistor::construct( ItemDocument *itemDocument, bool newItem, const char *id ) { return new VariableResistor( (ICNDocument*)itemDocument, newItem, id ); } LibraryItem* VariableResistor::libraryItem() { return new LibraryItem( QStringList(QString("ec/variableresistor")), i18n("Variable Resistor"), i18n("Passive"), "variable_resistor.png", LibraryItem::lit_component, VariableResistor::construct ); } VariableResistor::VariableResistor( ICNDocument* icnDocument, bool newItem, const QString& id ) : Component( icnDocument, newItem, (!id.isEmpty()) ? id : "variable resistor" ) { m_name = i18n("Resistor"); // Top Left(x,y) from centre point, width, height. setSize( -16, -16, 32, 32 ); init1PinLeft(); init1PinRight(); // (see comment in variablecapacitor.cpp) - david m_tickValue = 1; m_minResistance = 0.5; m_maxResistance = 1.0; m_currResistance = m_minResistance + ( ( m_maxResistance - m_minResistance ) / 2 ) ; m_pResistance = createResistance( m_pPNode[0], m_pNNode[0], m_currResistance ); createProperty( "resistance", Variant::Type::Double ); property("resistance")->setCaption( i18n("Resistance") ); property("resistance")->setUnit( QChar( 0x3a9 ) ); property("resistance")->setMinValue( 1e-6 ); property("resistance")->setValue( m_currResistance ); createProperty( "minimum resistance", Variant::Type::Double ); property("minimum resistance")->setCaption( i18n("Min") ); property("minimum resistance")->setUnit( QChar( 0x3a9 ) ); property("minimum resistance")->setMinValue( 1e-6 ); property("minimum resistance")->setValue( m_minResistance ); createProperty( "maximum resistance", Variant::Type::Double ); property("maximum resistance")->setCaption( i18n("Max") ); property("maximum resistance")->setUnit( QChar( 0x3a9 ) ); property("maximum resistance")->setMinValue( 1e-6 ); property("maximum resistance")->setValue( m_maxResistance ); addDisplayText( "res", QRect( -16, -26, 32, 12 ), "", false ); Slider * s = addSlider( "slider", 0, 100, 1, 50, Qt::Horizontal, QRect( -16, 14, width(), 16 ) ); m_pSlider = static_cast( s->widget() ); } VariableResistor::~VariableResistor() { } void VariableResistor::dataChanged() { double new_minResistance = dataDouble( "minimum resistance" ); double new_maxResistance = dataDouble( "maximum resistance" ); if( new_minResistance != m_minResistance ) { if( new_minResistance >= m_maxResistance ) { m_minResistance = m_maxResistance; property( "minimum resistance" )->setValue( m_minResistance ); } else m_minResistance = new_minResistance; } if( new_maxResistance != m_maxResistance ) { if( new_maxResistance <= m_minResistance ) { m_maxResistance = m_minResistance; property( "maximum resistance" )->setValue( m_maxResistance ); } else m_maxResistance = new_maxResistance; } m_tickValue = ( m_maxResistance - m_minResistance ) / m_pSlider->maximum(); // Calculate the resistance jump per tick of a 100 tick slider. sliderValueChanged( "slider", slider("slider")->value() ); } void VariableResistor::sliderValueChanged( const QString &id, int newValue ) { if ( id != "slider" ) return; /** @todo fix slider so current cap can be set in toolbar and editor and slider updates */ m_currResistance = m_minResistance + ( newValue * m_tickValue ); // Set the new capacitance value. m_pResistance->setResistance( m_currResistance ); // Update property. property( "resistance" )->setValue( m_currResistance ); QString display = QString::number( m_currResistance / getMultiplier( m_currResistance ), 'g', 3 ) + getNumberMag( m_currResistance ) + QChar( 0x3a9 ); setDisplayText( "res", display ); } void VariableResistor::drawShape( QPainter &p ) { initPainter(p); // Get centre point of component. int _y = (int)y(); int _x = (int)x(); p.drawRect( _x-16, _y-6, width(), 12 ); p.drawLine( _x-12, _y+12, _x+13, _y-13 ); QPolygon pa(3); // Diagonally pointing arrow pa[0] = QPoint( 0, 0 ); pa[1] = QPoint( -4, 0 ); pa[2] = QPoint( 0, 4 ); pa.translate( _x+13, _y-13 ); p.setBrush( p.pen().color() ); p.drawPolygon( pa ); deinitPainter(p); } diff --git a/src/electronics/components/voltageregulator.cpp b/src/electronics/components/voltageregulator.cpp index aa016f28..abddb5b3 100644 --- a/src/electronics/components/voltageregulator.cpp +++ b/src/electronics/components/voltageregulator.cpp @@ -1,69 +1,70 @@ /*************************************************************************** * Copyright (C) 2006 by William Hillerby - william.hillerby@ntlworld.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 "voltageregulator.h" #include "canvasitemparts.h" #include "libraryitem.h" #include "ecnode.h" #include -#include -#include -#include + +#include +#include +#include Item* VoltageRegulator::construct( ItemDocument *itemDocument, bool newItem, const char *id ) { return new VoltageRegulator( (ICNDocument*)itemDocument, newItem, id ); } LibraryItem* VoltageRegulator::libraryItem() { return new LibraryItem( QStringList(QString("ec/voltageregulator")), i18n("Voltage Regulator"), i18n("Passive"), "voltage_regulator.png", LibraryItem::lit_component, VoltageRegulator::construct ); } VoltageRegulator::VoltageRegulator( ICNDocument* icnDocument, bool newItem, const QString& id ) : Component( icnDocument, newItem, (!id.isEmpty()) ? id : "voltageregulator" ) { createProperty( "voltageout", Variant::Type::Double ); property("voltageout")->setCaption( i18n( "Voltage Out" ) ); property("voltageout")->setMinValue( 2 ); property("voltageout")->setMaxValue( maxVoltageOut ); property("voltageout")->setValue( 5 ); } VoltageRegulator::~VoltageRegulator() { } void VoltageRegulator::dataChanged() { } void VoltageRegulator::drawShape( QPainter &p ) { initPainter(p); // Get centre point of component. //int _y = (int)y(); //int _x = (int)x(); deinitPainter(p); } diff --git a/src/electronics/ecnode.cpp b/src/electronics/ecnode.cpp index f83ffdc1..7f3f8663 100644 --- a/src/electronics/ecnode.cpp +++ b/src/electronics/ecnode.cpp @@ -1,328 +1,328 @@ /*************************************************************************** * Copyright (C) 2003-2005 by David Saxton * * david@bluehaze.org * * * * 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 "circuitdocument.h" #include "component.h" #include "connector.h" #include "ecnode.h" #include "electronicconnector.h" #include "pin.h" -#include -#include +#include +#include #include ECNode::ECNode( ICNDocument *icnDocument, Node::node_type _type, int dir, const QPoint &pos, QString *_id ) : Node( icnDocument, _type, dir, pos, _id ) { m_prevV = 0; m_prevI = 0; m_pinPoint = nullptr; m_bShowVoltageBars = KTLConfig::showVoltageBars(); m_bShowVoltageColor = KTLConfig::showVoltageColor(); if ( icnDocument ) icnDocument->registerItem(this); m_pins.resize(1); m_pins[0] = new Pin(this); } ECNode::~ECNode() { if (m_pinPoint) { m_pinPoint->setCanvas(nullptr); delete m_pinPoint; } for ( unsigned i = 0; i < m_pins.size(); i++ ) delete m_pins[i]; m_pins.resize(0); } void ECNode::setNumPins( unsigned num ) { unsigned oldNum = m_pins.size(); if ( num == oldNum ) return; if ( num > oldNum ) { m_pins.resize(num); for ( unsigned i = oldNum; i < num; i++ ) m_pins[i] = new Pin(this); } else { for ( unsigned i = num; i < oldNum; i++ ) delete m_pins[i]; m_pins.resize(num); } emit numPinsChanged(num); } Pin *ECNode::pin( unsigned num ) const { return (num < m_pins.size()) ? m_pins[num] : nullptr; } void ECNode::setNodeChanged() { if ( !canvas() || numPins() != 1 ) return; Pin * pin = m_pins[0]; double v = pin->voltage(); double i = pin->current(); if ( v != m_prevV || i != m_prevI ) { QRect r = boundingRect(); // r.setCoords( r.left()+(r.width()/2)-1, r.top()+(r.height()/2)-1, r.right()-(r.width()/2)+1, r.bottom()-(r.height()/2)+1 ); canvas()->setChanged(r); m_prevV = v; m_prevI = i; } } void ECNode::setParentItem( CNItem * parentItem ) { Node::setParentItem(parentItem); if ( Component * component = dynamic_cast(parentItem) ) { connect( component, SIGNAL(elementDestroyed(Element* )), this, SLOT(removeElement(Element* )) ); connect( component, SIGNAL(switchDestroyed( Switch* )), this, SLOT(removeSwitch( Switch* )) ); } } void ECNode::removeElement( Element * e ) { for ( unsigned i = 0; i < m_pins.size(); i++ ) m_pins[i]->removeElement(e); } void ECNode::removeSwitch( Switch * sw ) { for ( unsigned i = 0; i < m_pins.size(); i++ ) m_pins[i]->removeSwitch( sw ); } // -- functionality from node.cpp -- bool ECNode::isConnected( Node *node, NodeList *checkedNodes ) { if ( this == node ) return true; bool firstNode = !checkedNodes; if (firstNode) checkedNodes = new NodeList(); else if ( checkedNodes->contains(this) ) return false; checkedNodes->append(this); const ConnectorList::const_iterator inputEnd = m_connectorList.end(); for ( ConnectorList::const_iterator it = m_connectorList.begin(); it != inputEnd; ++it ) { Connector *connector = *it; if (connector) { Node *startNode = connector->startNode(); if ( startNode && startNode->isConnected( node, checkedNodes ) ) { if (firstNode) { delete checkedNodes; } return true; } } } if (firstNode) { delete checkedNodes; } return false; } void ECNode::checkForRemoval( Connector *connector ) { removeConnector(connector); setNodeSelected(false); removeNullConnectors(); if (!p_parentItem) { int conCount = m_connectorList.count(); if ( conCount < 1 ) removeNode(); } } void ECNode::setVisible( bool yes ) { if ( isVisible() == yes ) return; KtlQCanvasPolygon::setVisible(yes); const ConnectorList::iterator inputEnd = m_connectorList.end(); for ( ConnectorList::iterator it = m_connectorList.begin(); it != inputEnd; ++it ) { Connector *connector = *it; if (connector) { if ( isVisible() ) connector->setVisible(true); else { Node *node = connector->startNode(); connector->setVisible( node && node->isVisible() ); } } } } QPoint ECNode::findConnectorDivergePoint( bool * found ) { // FIXME someone should check that this function is OK ... I just don't understand what it does bool temp; if (!found) found = &temp; *found = false; if ( numCon( false, false ) != 2 ) return QPoint(0,0); QPointList p1; QPointList p2; int inSize = m_connectorList.count(); const ConnectorList connectors = m_connectorList; const ConnectorList::const_iterator end = connectors.end(); bool gotP1 = false; bool gotP2 = false; int at = -1; for ( ConnectorList::const_iterator it = connectors.begin(); it != end && !gotP2; ++it ) { at++; if ( !(*it) || !(*it)->canvas() ) continue; if (gotP1) { p2 = (*it)->connectorPoints( at < inSize ); gotP2 = true; } else { p1 = (*it)->connectorPoints( at < inSize ); gotP1 = true; } } if ( !gotP1 || !gotP2 ) return QPoint(0,0); unsigned maxLength = p1.size() > p2.size() ? p1.size() : p2.size(); for ( unsigned i = 1; i < maxLength; ++i ) { if ( p1[i] != p2[i] ) { *found = true; return p1[i-1]; } } return QPoint(0, 0); } void ECNode::addConnector( Connector * const connector ) { if ( !handleNewConnector(connector) ) return; m_connectorList.append(connector); } bool ECNode::handleNewConnector( Connector * connector ) { if (!connector) return false; if ( m_connectorList.contains(connector) ) { qWarning() << Q_FUNC_INFO << " Already have connector = " << connector << endl; return false; } connect( this, SIGNAL(removed(Node*)), connector, SLOT(removeConnector(Node*)) ); connect( connector, SIGNAL(removed(Connector*)), this, SLOT(checkForRemoval(Connector*)) ); connect( connector, SIGNAL(selected(bool)), this, SLOT(setNodeSelected(bool)) ); if ( !isChildNode() ) p_icnDocument->slotRequestAssignNG(); return true; } Connector* ECNode::createConnector( Node * node) { // FIXME dynamic_cast used Connector *connector = new ElectronicConnector( dynamic_cast(node), dynamic_cast(this), p_icnDocument ); addConnector(connector); return connector; } void ECNode::removeNullConnectors() { m_connectorList.removeAll((Connector*)nullptr); } int ECNode::numCon( bool includeParentItem, bool includeHiddenConnectors ) const { unsigned count = 0; const ConnectorList connectors = m_connectorList; ConnectorList::const_iterator end = connectors.end(); for ( ConnectorList::const_iterator it = connectors.begin(); it != end; ++it ) { if ( *it && (includeHiddenConnectors || (*it)->canvas()) ) count++; } if ( isChildNode() && includeParentItem ) count++; return count; } void ECNode::removeConnector( Connector *connector ) { if (!connector) return; ConnectorList::iterator it; //it = m_connectorList.find(connector); // 2018.12.02 int i = m_connectorList.indexOf(connector); it = (i == -1 ? m_connectorList.end() : (m_connectorList.begin()+i)); if ( it != m_connectorList.end() ) { (*it)->removeConnector(); (*it) = nullptr; } } Connector* ECNode::getAConnector() const { if( ! m_connectorList.isEmpty() ) return *m_connectorList.begin(); else return nullptr; } diff --git a/src/electronics/gpsimprocessor.cpp b/src/electronics/gpsimprocessor.cpp index d9c63bd7..f705bef5 100644 --- a/src/electronics/gpsimprocessor.cpp +++ b/src/electronics/gpsimprocessor.cpp @@ -1,901 +1,902 @@ /*************************************************************************** * Copyright (C) 2005 by David Saxton * * david@bluehaze.org * * * * 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 "config.h" #ifndef NO_GPSIM #include "asmparser.h" #include "debugmanager.h" #include "flowcodedocument.h" #include "gpsimprocessor.h" #include "language.h" #include "languagemanager.h" #include "microlibrary.h" #include "processchain.h" #include "simulator.h" #include -#include #include #include + +#include #include #include -#include -#include -#include +#include +#include +#include #include "gpsim/cod.h" #include "gpsim/interface.h" #include "gpsim/gpsim_classes.h" #include "gpsim/pic-processor.h" #include "gpsim/registers.h" #include "gpsim/14bit-registers.h" #include "gpsim/symbol.h" #include "gpsim/sim_context.h" bool bDoneGpsimInit = false; bool bUseGUI = true; // extern "C" void initialize_gpsim(); // void initialize_gpsim(void); extern void initialize_commands(); extern void initialize_readline(); extern void gui_main(void); extern void cli_main(); void gpsim_version() {} void quit_gui() {} //BEGIN class GpsimProcessor /** Work around a bug in gpsim: the directory in a filename is recorded twice, e.g. "/home/david/afile.asm" is recorded as "/home/david//home/david/afile.asm". This function will remove the duplicated directory path (by searching for a "//"). */ QString sanitizeGpsimFile( QString file ) { int pos = file.indexOf("//"); if ( pos != -1 ) { file.remove( 0, pos + 1 ); } return file; } GpsimProcessor::GpsimProcessor( QString symbolFile, QObject *parent ) : QObject(parent), m_symbolFile(symbolFile) { if (!bDoneGpsimInit) { initialize_gpsim_core(); initialization_is_complete(); bDoneGpsimInit = true; } m_bCanExecuteNextCycle = true; m_bIsRunning = false; m_pPicProcessor = nullptr; m_codLoadStatus = CodUnknown; m_pRegisterMemory = nullptr; m_debugMode = GpsimDebugger::AsmDebugger; m_pDebugger[0] = m_pDebugger[1] = nullptr; Processor * tempProcessor = nullptr; const char * fileName = symbolFile.toAscii(); #ifdef GPSIM_0_21_4 qDebug() << "GPSIM_0_21_4 GpsimProcessor " << fileName; switch ( (cod_errors)load_symbol_file( &tempProcessor, fileName ) ) { case COD_SUCCESS: m_codLoadStatus = CodSuccess; break; case COD_FILE_NOT_FOUND: m_codLoadStatus = CodFileNotFound; break; case COD_UNRECOGNIZED_PROCESSOR: m_codLoadStatus = CodUnrecognizedProcessor; break; case COD_FILE_NAME_TOO_LONG: m_codLoadStatus = CodFileNameTooLong; break; case COD_LST_NOT_FOUND: m_codLoadStatus = CodLstNotFound; break; case COD_BAD_FILE: m_codLoadStatus = CodBadFile; break; default: m_codLoadStatus = CodUnknown; } #else // GPSIM_0_21_11+ qDebug() << "GPSIM_0_21_11+ GpsimProcessor " << fileName; FILE * pFile = fopen( fileName, "r" ); if ( !pFile ) m_codLoadStatus = CodFileUnreadable; else m_codLoadStatus = ( ProgramFileTypeList::GetList().LoadProgramFile( & tempProcessor, fileName, pFile ) ) ? CodSuccess : CodFailure; #endif qDebug() << " m_codLoadStatus=" << m_codLoadStatus; m_pPicProcessor = dynamic_cast(tempProcessor); if ( codLoadStatus() == CodSuccess ) { m_pRegisterMemory = new RegisterSet( m_pPicProcessor ); m_pDebugger[0] = new GpsimDebugger( GpsimDebugger::AsmDebugger, this ); m_pDebugger[1] = new GpsimDebugger( GpsimDebugger::HLLDebugger, this ); Simulator::self()->attachGpsimProcessor(this); DebugManager::self()->registerGpsim(this); } } GpsimProcessor::~GpsimProcessor() { if (!Simulator::isDestroyedSim()) { Simulator::self()->detachGpsimProcessor(this); } delete m_pRegisterMemory; if ( m_pDebugger[0] ) m_pDebugger[0]->deleteLater(); if ( m_pDebugger[1] ) m_pDebugger[1]->deleteLater(); } void GpsimProcessor::displayCodLoadStatus( ) { switch (m_codLoadStatus) { case CodSuccess: break; case CodFileNotFound: KMessageBox::sorry( nullptr, i18n("The cod file \"%1\" was not found.", m_symbolFile), i18n("File Not Found") ); break; case CodUnrecognizedProcessor: KMessageBox::sorry( nullptr, i18n("The processor for cod file \"%1\" is unrecognized.", m_symbolFile), i18n("Unrecognized Processor") ); break; case CodFileNameTooLong: KMessageBox::sorry( nullptr, i18n("The file name \"%1\" is too long.", m_symbolFile), i18n("Filename Too Long") ); break; case CodLstNotFound: KMessageBox::sorry( nullptr, i18n("The lst file associated with the cod file \"%1\" was not found.", m_symbolFile), i18n("LST File Not Found") ); break; case CodBadFile: KMessageBox::sorry( nullptr, i18n("The cod file \"%1\" is bad.", m_symbolFile), i18n("Bad File") ); break; case CodFileUnreadable: KMessageBox::sorry( nullptr, i18n("The cod file \"%1\" could not be read from.", m_symbolFile), i18n("Unreadable File") ); break; case CodFailure: case CodUnknown: KMessageBox::sorry( nullptr, i18n("An error occurred with the cod file \"%1\".", m_symbolFile), i18n("Error") ); break; } } unsigned GpsimProcessor::programMemorySize() const { return m_pPicProcessor->program_memory_size(); } QStringList GpsimProcessor::sourceFileList() { QStringList files; // Work around nasty bug in gpsim 0.21.4 where nsrc_files value might be used uninitiazed int max = m_pPicProcessor->files.nsrc_files(); #ifdef GPSIM_0_21_4 if ( max > 10 ) max = 10; #endif for ( int i = 0; i < max; ++i ) { if ( !m_pPicProcessor->files[i] ) continue; files << sanitizeGpsimFile( m_pPicProcessor->files[i]->name().c_str() ); } return files; } void GpsimProcessor::emitLineReached() { m_pDebugger[0]->emitLineReached(); m_pDebugger[1]->emitLineReached(); } void GpsimProcessor::setRunning( bool run ) { if ( m_bIsRunning == run ) return; m_bIsRunning = run; emit runningStatusChanged(run); } void GpsimProcessor::executeNext() { if ( !m_bIsRunning ) return; if ( !m_bCanExecuteNextCycle ) { m_bCanExecuteNextCycle = true; return; } unsigned long long beforeExecuteCount = get_cycles().get(); if(get_bp().have_interrupt()) { m_pPicProcessor->interrupt(); } else { m_pPicProcessor->step_one(false); // Don't know what the false is for; gpsim ignores its value anyway // Some instructions take more than one cycle to execute, so ignore next cycle if this was the case if ( (get_cycles().get() - beforeExecuteCount) > 1 ) m_bCanExecuteNextCycle = false; } currentDebugger()->checkForBreak(); // Let's also update the values of RegisterInfo every 25 milliseconds if ( (beforeExecuteCount % 10000) == 0 ) registerMemory()->update(); } void GpsimProcessor::reset() { bool wasRunning = isRunning(); m_pPicProcessor->reset(SIM_RESET); setRunning(false); if (!wasRunning) { // If we weren't running before, then the next signal won't have been emitted emitLineReached(); } } MicroInfo * GpsimProcessor::microInfo( ) const { if ( !m_pPicProcessor ){ qWarning() << Q_FUNC_INFO << " m_pPicProcessor == nullptr" << endl; return nullptr; } return MicroLibrary::self()->microInfoWithID( m_pPicProcessor->name().c_str() ); } int GpsimProcessor::operandRegister( unsigned address ) { instruction * ins = m_pPicProcessor->program_memory[ address ]; if ( Register_op * reg = dynamic_cast(ins) ) return reg->register_address; return -1; } int GpsimProcessor::operandLiteral( unsigned address ) { instruction * ins = m_pPicProcessor->program_memory[ address ]; if ( Literal_op * lit = dynamic_cast(ins) ) return lit->L; return -1; } GpsimProcessor::ProgramFileValidity GpsimProcessor::isValidProgramFile( const QString & programFile ) { if ( !QFile::exists(programFile) ) return DoesntExist; QString extension = programFile.right( programFile.length() - programFile.lastIndexOf('.') - 1 ).toLower(); if ( extension == "flowcode" || extension == "asm" || extension == "cod" || extension == "basic" || extension == "microbe" || extension == "c" ) return Valid; if ( extension == "hex" && QFile::exists( QString(programFile).replace(".hex",".cod") ) ) return Valid; return IncorrectType; } QString GpsimProcessor::generateSymbolFile( const QString &fileName, QObject *receiver, const char *successMember, const char * failMember ) { qDebug() << Q_FUNC_INFO << "fileName=" << fileName ; if (isValidProgramFile(fileName) != GpsimProcessor::Valid) { qDebug() << Q_FUNC_INFO << "not valid program file"; return QString::null; } QString extension = fileName.right( fileName.length() - fileName.lastIndexOf('.') - 1 ).toLower(); if ( extension == "cod" ) { QTimer::singleShot( 0, receiver, successMember ); return fileName; } if ( extension == "hex" ) { QTimer::singleShot( 0, receiver, successMember ); // We've already checked for the existance of the ".cod" file in GpsimProcessor::isValidProgramFile return QString(fileName).replace(".hex",".cod"); } else if ( extension == "basic" || extension == "microbe" ) { compileMicrobe( fileName, receiver, successMember, failMember ); return QString(fileName).replace( "."+extension, ".cod" ); } else if ( extension == "flowcode" ) { QTemporaryFile tmpFile(QDir::tempPath() + QLatin1String("/ktechlab_XXXXXX.hex")); if (!tmpFile.open()) { qWarning() << " failed to open " << tmpFile.fileName() << " error " << tmpFile.errorString(); return QString::null; } const QString hexFile = tmpFile.fileName(); ProcessOptions o; o.b_addToProject = false; o.setTargetFile( hexFile ); o.setInputFiles( QStringList(fileName) ); o.setMethod( ProcessOptions::Method::Forget ); o.setProcessPath( ProcessOptions::ProcessPath::FlowCode_Program ); ProcessChain * pc = LanguageManager::self()->compile(o); if (receiver) { if (successMember) connect( pc, SIGNAL(successful()), receiver, successMember ); if (failMember) connect( pc, SIGNAL(failed()), receiver, failMember ); } return QString(hexFile).replace( ".hex", ".cod" ); } else if ( extension == "asm" ) { ProcessOptions o; o.b_addToProject = false; o.setTargetFile( QString(fileName).replace(".asm",".hex")); o.setInputFiles(QStringList(fileName)); o.setMethod( ProcessOptions::Method::Forget ); o.setProcessPath( ProcessOptions::ProcessPath::path( ProcessOptions::guessMediaType(fileName), ProcessOptions::ProcessPath::Program ) ); ProcessChain *pc = LanguageManager::self()->compile(o); if (receiver) { if (successMember) connect( pc, SIGNAL(successful()), receiver, successMember ); if (failMember) connect( pc, SIGNAL(failed()), receiver, failMember ); } return QString(fileName).replace(".asm",".cod"); } else if ( extension == "c" ) { ProcessOptions o; o.b_addToProject = false; o.setTargetFile( QString(fileName).replace(".c",".hex")); o.setInputFiles(QStringList(fileName)); o.setMethod( ProcessOptions::Method::Forget ); o.setProcessPath( ProcessOptions::ProcessPath::C_Program ); ProcessChain *pc = LanguageManager::self()->compile(o); if (receiver) { if (successMember) connect( pc, SIGNAL(successful()), receiver, successMember ); if (failMember) connect( pc, SIGNAL(failed()), receiver, failMember ); } return QString(fileName).replace(".c",".cod"); } if ( failMember ) QTimer::singleShot( 0, receiver, failMember ); return QString::null; } void GpsimProcessor::compileMicrobe( const QString &filename, QObject *receiver, const char * successMember, const char * failMember ) { ProcessOptions o; o.b_addToProject = false; o.setTargetFile( QString(filename).replace(".microbe",".hex") ); o.setInputFiles(QStringList(filename)); o.setMethod( ProcessOptions::Method::Forget ); o.setProcessPath( ProcessOptions::ProcessPath::Microbe_Program ); ProcessChain * pc = LanguageManager::self()->compile(o); if (receiver) { if (successMember) connect( pc, SIGNAL(successful()), receiver, successMember ); if (failMember) connect( pc, SIGNAL(failed()), receiver, failMember ); } } //END class GpsimProcessor //BEGIN class GpsimDebugger GpsimDebugger::GpsimDebugger( Type type, GpsimProcessor * gpsim ) : QObject() { m_pGpsim = gpsim; m_type = type; m_pBreakFromOldLine = nullptr; m_addressToLineMap = nullptr; m_stackLevelLowerBreak = -1; m_addressSize = 0; connect( m_pGpsim, SIGNAL(runningStatusChanged(bool )), this, SLOT(gpsimRunningStatusChanged(bool )) ); if ( type == HLLDebugger ) { const QStringList sourceFileList = m_pGpsim->sourceFileList(); QStringList::const_iterator sflEnd = sourceFileList.end(); for ( QStringList::const_iterator it = sourceFileList.begin(); it != sflEnd; ++it ) { AsmParser p(*it); p.parse(this); } } initAddressToLineMap(); } GpsimDebugger::~GpsimDebugger() { QList debugLinesToDelete; for ( unsigned i = 0; i < m_addressSize; ++i ) { DebugLine * dl = m_addressToLineMap[i]; if ( !dl || dl->markedAsDeleted() ) continue; dl->markAsDeleted(); debugLinesToDelete += dl; } const QList::iterator end = debugLinesToDelete.end(); for ( QList::iterator it = debugLinesToDelete.begin(); it != end; ++it ) delete *it; delete [] m_addressToLineMap; } void GpsimDebugger::gpsimRunningStatusChanged( bool isRunning ) { if (!isRunning) { m_stackLevelLowerBreak = -1; m_pBreakFromOldLine = nullptr; emitLineReached(); } } void GpsimDebugger::associateLine( const QString & sourceFile, int sourceLine, const QString & assemblyFile, int assemblyLine ) { if ( assemblyLine < 0 || sourceLine < 0 ) { qWarning() << Q_FUNC_INFO << "Invalid lines: assemblyLine="<programMemorySize(); delete [] m_addressToLineMap; m_addressToLineMap = new DebugLine*[m_addressSize]; memset( m_addressToLineMap, 0, m_addressSize * sizeof(DebugLine*) ); if ( m_type == AsmDebugger ) { for ( unsigned i = 0; i < m_addressSize; ++i ) { int line = m_pGpsim->picProcessor()->pma->get_src_line(i) - 1; int fileID = m_pGpsim->picProcessor()->pma->get_file_id(i); FileContext * fileContext = m_pGpsim->picProcessor()->files[fileID]; if (fileContext) m_addressToLineMap[i] = new DebugLine( sanitizeGpsimFile( fileContext->name().c_str() ), line ); } } else { SourceLineMap::const_iterator slmEnd = m_sourceLineMap.end(); for ( SourceLineMap::const_iterator it = m_sourceLineMap.begin(); it != slmEnd; ++it ) { SourceLineMap::const_iterator next = it; ++next; int asmToLine = ((next == slmEnd) || (next.key().fileName() != it.key().fileName())) ? -1 : next.key().line() - 1; QString asmFile = it.key().fileName(); int asmFromLine = it.key().line(); SourceLine sourceLine = it.value(); std::string stdAsmFile( asmFile.toAscii() ); int fileID = m_pGpsim->picProcessor()->files.Find( stdAsmFile ); if ( fileID == -1 ) { qWarning() << Q_FUNC_INFO << "Could not find FileContext (asmFile=\""<picProcessor()->files[fileID]->max_line() - 2; if ( (asmFromLine < 0) || (asmToLine < asmFromLine) ) { qWarning() << Q_FUNC_INFO << "Invalid lines: asmFromLine="<isRunning() ) return; int initialStack = (m_pGpsim->picProcessor()->stack->pointer & m_pGpsim->picProcessor()->stack->stack_mask) + dl; DebugLine * initialLine = currentDebugLine(); if ( initialStack < 0 ) initialStack = 0; // Reset any previous stackStep, and step m_pBreakFromOldLine = nullptr; m_stackLevelLowerBreak = -1; m_pGpsim->picProcessor()->step_one(false); int currentStack = m_pGpsim->picProcessor()->stack->pointer & m_pGpsim->picProcessor()->stack->stack_mask; DebugLine * currentLine = currentDebugLine(); if ( (initialStack >= currentStack) && (initialLine != currentLine) ) emitLineReached(); else { // Looks like we stepped into something or haven't gone onto the next // instruction, wait until we step back out.... m_stackLevelLowerBreak = initialStack; m_pBreakFromOldLine = initialLine; m_pGpsim->setRunning(true); } } //END class Debugger //BEGIN class RegisterSet RegisterSet::RegisterSet( pic_processor * picProcessor ) { unsigned numRegisters = picProcessor->rma.get_size(); qDebug() << Q_FUNC_INFO << "numRegisters="<rma[i] ); m_registers[i] = info; m_nameToRegisterMap[ info->name() ] = info; qDebug() << Q_FUNC_INFO << " add register info " << info->name() << " at pos " << i << " addr " << info; } #if defined(HAVE_GPSIM_0_26) RegisterInfo * info = new RegisterInfo( picProcessor->Wreg ); // is tihs correct for "W" member? TODO #else RegisterInfo * info = new RegisterInfo( picProcessor->W ); #endif m_registers.append( info ); m_nameToRegisterMap[ info->name() ] = info; qDebug() << Q_FUNC_INFO << " add register info " << info->name() << " at end, addr " << info; qDebug() << Q_FUNC_INFO << " registers.size " << m_registers.size() << " ; numRegisters " << numRegisters; } RegisterSet::~RegisterSet() { for ( unsigned i = 0; i < m_registers.size(); ++i ) delete m_registers[i]; } RegisterInfo * RegisterSet::fromAddress( unsigned address ) { return (address < m_registers.size()) ? m_registers[address] : nullptr; } RegisterInfo * RegisterSet::fromName( const QString & name ) { // First try the name as case sensitive, then as case insensitive. if ( m_nameToRegisterMap.contains( name ) ) return m_nameToRegisterMap[ name ]; QString nameLower = name.toLower(); RegisterInfoMap::iterator end = m_nameToRegisterMap.end(); for ( RegisterInfoMap::iterator it = m_nameToRegisterMap.begin(); it != end; ++ it ) { if ( it.key().toLower() == nameLower ) return it.value(); } return nullptr; } void RegisterSet::update() { for ( unsigned i = 0; i < m_registers.size(); ++i ) m_registers[i]->update(); } //END class RegisterSet //BEGIN class RegisterInfo RegisterInfo::RegisterInfo( Register * reg ) { assert(reg); m_pRegister = reg; m_type = Invalid; m_prevEmitValue = 0; switch ( m_pRegister->isa() ) { case Register::GENERIC_REGISTER: m_type = Generic; break; case Register::FILE_REGISTER: m_type = File; break; case Register::SFR_REGISTER: m_type = SFR; break; case Register::BP_REGISTER: m_type = Breakpoint; break; case Register::INVALID_REGISTER: m_type = Invalid; break; } m_name = QString::fromLatin1(m_pRegister->baseName().c_str()); } unsigned RegisterInfo::value() const { return m_pRegister->value.data; } void RegisterInfo::update() { unsigned newValue = value(); if ( newValue != m_prevEmitValue ) { m_prevEmitValue = newValue; emit valueChanged(newValue); } } QString RegisterInfo::toString( RegisterType type ) { switch ( type ) { case Generic: return i18n("Generic"); case File: return i18n("File"); case SFR: return i18n("SFR"); case Breakpoint: return i18n("Breakpoint"); case Invalid: return i18n("Invalid"); } return i18n("Unknown"); } //END class RegisterInfo //BEGIN class DebugLine DebugLine::DebugLine( const QString & fileName, int line ) : SourceLine( fileName, line ) { m_bIsBreakpoint = false; m_bMarkedAsDeleted = false; } DebugLine::DebugLine() : SourceLine() { m_bIsBreakpoint = false; m_bMarkedAsDeleted = false; } //END class DebugLine #endif diff --git a/src/electronics/gpsimprocessor.h b/src/electronics/gpsimprocessor.h index 59484fcb..1fdba525 100644 --- a/src/electronics/gpsimprocessor.h +++ b/src/electronics/gpsimprocessor.h @@ -1,397 +1,397 @@ /*************************************************************************** * Copyright (C) 2005 by David Saxton * * david@bluehaze.org * * * * 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 "config.h" #ifndef NO_GPSIM #ifndef GPSIMPROCESSOR_H #define GPSIMPROCESSOR_H #include "sourceline.h" -#include +#include // #include -#include -#include +#include +#include #include class DebugLine; class GpsimProcessor; class MicroInfo; class pic_processor; // from gpsim class Register; class RegisterMemoryAccess; typedef QMap SourceLineMap; typedef QList IntList; class DebugLine : public SourceLine { public: DebugLine(); /// @param fileName a path to a file in the local filesystem DebugLine( const QString & fileName, int line ); /** * Whether or not to break when we reach this line. */ bool isBreakpoint() const { return m_bIsBreakpoint; } /** * Set whether or not to break when we reach this line. */ void setBreakpoint( bool breakpoint ) { m_bIsBreakpoint = breakpoint; } /** * Used for efficiency purposes by GpsimProcessor. Sets a flag. */ void markAsDeleted() { m_bMarkedAsDeleted = true; } /** * Used for efficiency purposes by GpsimProcessor. */ bool markedAsDeleted() const { return m_bMarkedAsDeleted; } protected: bool m_bIsBreakpoint; bool m_bMarkedAsDeleted; private: DebugLine( const DebugLine & dl ); DebugLine & operator = ( const DebugLine & dl ); }; /** @short Stores info from gpsim register, used to hide gpsim interface @author David Saxton */ class RegisterInfo : public QObject { Q_OBJECT public: RegisterInfo( Register * reg ); enum RegisterType { Invalid, Generic, File, SFR, Breakpoint }; RegisterType type() const { return m_type; } QString name() const { return m_name; } unsigned value() const; static QString toString( RegisterType type ); /** * Checks to see if the value has changed; if so, emit new value. */ void update(); signals: void valueChanged( unsigned newValue ); protected: QString m_name; RegisterType m_type; Register * m_pRegister; unsigned m_prevEmitValue; }; /** @short Stores information about a set of registers, used to hide gpsim interface. @author David Saxton */ class RegisterSet { public: RegisterSet( pic_processor * picProcessor ); ~RegisterSet(); /** * Calls update for each RegisterInfo in this set. */ void update(); /** * Returns the number of registers. */ unsigned size() const { return m_registers.size(); } RegisterInfo * fromAddress( unsigned address ); RegisterInfo * fromName( const QString & name ); protected: typedef QMap< QString, RegisterInfo * > RegisterInfoMap; RegisterInfoMap m_nameToRegisterMap; QVector< RegisterInfo * > m_registers; }; /** @author David Saxton */ class GpsimDebugger : public QObject { friend class GpsimProcessor; Q_OBJECT public: enum Type { AsmDebugger = 0, HLLDebugger = 1 }; GpsimDebugger( Type type, GpsimProcessor * gpsim ); ~GpsimDebugger() override; GpsimProcessor * gpsim() const { return m_pGpsim; } /** * When an assembly file was generated by a high level language compiler * like SDCC, it will insert markers like ";#CSRC" that show which line * of source-code generated the given set of assembly instructions. This * matches up the assembly file lines with the associated source file * lines. * @param sourceFile the path to an assembly file in the local filesystem * @param assemblyFile the path to a source file in the local filesystem */ void associateLine( const QString & sourceFile, int sourceLine, const QString & assemblyFile, int assemblyLine ); /** * Check to see if we've hit a breakpoint or similar; if so, this * function will stop the execution of the PIC program. */ void checkForBreak(); /** * Sets the breakpoints used for the given file to exactly those that * are contained in this list. Breakpoints for other files are not * affected. * @param path the location of the file (which gpsim must recognise). */ void setBreakpoints( const QString & path, const IntList & lines ); /** * Sets / removes the breakpoint at the given line */ void setBreakpoint( const QString & path, int line, bool isBreakpoint ); /** * Returns the current source line that gpsim is at. By default, this * will be the corresponding assembly line. That can be overwritten * using mapAddressBlockToLine. */ SourceLine currentLine(); /** * Returns a pointer to the debug info for the current line. */ DebugLine * currentDebugLine(); /** * @return the program address for the given line (or -1 if no such * line). */ int programAddress( const QString & path, int line ); /** * Step into the next program line. */ void stepInto(); /** * Step over the next program instruction. If we are currently running, * this function will do nothing. Otherwise, it will record the current * stack level, step, and if the new stack level is <= the initial level * then return - otherwise, this processor will set a breakpoint for * stack levels <= initial, and go to running mode. */ void stepOver(); /** * Similar to stepOver, except we break when the stack level becomes < * the initial stack level (instead of <= initial). */ void stepOut(); signals: /** * Emitted when a line is reached. By default, this is the line of the * input assembly file; however, the line associated with an address in * the PIC memory can be changed with mapAddressBlockToLine. */ void lineReached( const SourceLine & sourceLine ); protected slots: void gpsimRunningStatusChanged( bool isRunning ); protected: void initAddressToLineMap(); void stackStep( int dl ); void emitLineReached(); int m_stackLevelLowerBreak; // Set by step-over, for when the stack level decreases to the one given SourceLine m_previousAtLineEmit; // Used for working out whether we should emit a new line reached signal DebugLine ** m_addressToLineMap; DebugLine * m_pBreakFromOldLine; GpsimProcessor * m_pGpsim; Type m_type; unsigned m_addressSize; SourceLineMap m_sourceLineMap; // assembly <--> High level language }; /** @author David Saxton */ class GpsimProcessor : public QObject { friend class GpsimDebugger; Q_OBJECT public: /** * Create a new gpsim processor. After calling this constructor, you * should always call codLoadStatus() to ensure that the cod file was * loaded successfully. */ GpsimProcessor( QString symbolFile, QObject *parent = nullptr ); ~GpsimProcessor() override; void setDebugMode( GpsimDebugger::Type mode ) { m_debugMode = mode; } GpsimDebugger * currentDebugger() const { return m_pDebugger[m_debugMode]; } enum CodLoadStatus { CodSuccess, CodFileNotFound, CodUnrecognizedProcessor, CodFileNameTooLong, CodLstNotFound, CodBadFile, CodFileUnreadable, CodFailure, CodUnknown // Should never be this, but just in case load_symbol_file returns something funny }; enum InstructionType { LiteralOp, BitOp, RegisterOp, UnknownOp }; /** * @return status of opening the COD file * @see displayCodLoadStatus */ CodLoadStatus codLoadStatus() const { return m_codLoadStatus; } /** * Popups a messagebox to the user according to the CodLoadStatus. Will * only popup a messagebox if the CodLoadStatus wasn't CodSuccess. */ void displayCodLoadStatus(); /** * Returns a list of source files for the currently running program. * Each entry is a path in the local filesystem. */ QStringList sourceFileList(); /** * Set whether or not to run gpsim. (i.e. whether or not the step * function should do anything when called with force=false). */ void setRunning( bool run ); /** * Returns true if running (currently simulating), else gpsim is paused. */ bool isRunning() const { return m_bIsRunning; } /** * Execute the next program instruction. If we are not in a running * mode, then this function will do nothing. */ void executeNext(); /** * Reset all parts of the simulation. Gpsim will not run until * setRunning(true) is called. Breakpoints are not affected. */ void reset(); /** * Returns the microinfo describing this processor. */ MicroInfo * microInfo() const; pic_processor * picProcessor() const { return m_pPicProcessor; } unsigned programMemorySize() const; RegisterSet * registerMemory() const { return m_pRegisterMemory; } /** * @return the instruction type at the given address. */ InstructionType instructionType( unsigned address ); /** * @return the address of the operand's register at address if the * instruction at address is a register operation, and -1 otherwise. */ int operandRegister( unsigned address ); /** * @return the literal if the instruction at address is a literal * operation, and -1 otherwise. */ int operandLiteral( unsigned address ); //BEGIN Convenience functions for PIC files enum ProgramFileValidity { DoesntExist, IncorrectType, Valid }; /** * @return information on the validity of the given program file (either * DoesntExist, IncorrectType, or Valid). * @see static QString generateSymbolFile */ static ProgramFileValidity isValidProgramFile( const QString & programFile ); /** * Converts the file at programFile to a Symbol file for emulation, * and returns that symbol file's path * @param fileName The full url to the file * @param receiver The slot to connect the assembled signal to * @see static bool isValidProgramFile( const QString &programFile ) */ static QString generateSymbolFile( const QString &fileName, QObject *receiver, const char *successMember, const char * failMember = nullptr ); /** *Compile microbe to output to the given filename */ static void compileMicrobe( const QString &filename, QObject *receiver, const char * successMember, const char * failMember = nullptr ); //END convenience functions for PIC files signals: /** * Emitted when the running status of gpsim changes. */ void runningStatusChanged( bool isRunning ); protected: /** * Calls emitLineReached for each debugger. */ void emitLineReached(); pic_processor * m_pPicProcessor; CodLoadStatus m_codLoadStatus; const QString m_symbolFile; RegisterSet * m_pRegisterMemory; GpsimDebugger::Type m_debugMode; GpsimDebugger * m_pDebugger[2]; // Asm, HLL /** * We are called effectively for each cycle of the cycle of the * processor. This value is used as some instructions (e.g. goto) take * two cycles to execute, and so we must ignore one cycle to ensure * realtime simulation. */ bool m_bCanExecuteNextCycle; private: bool m_bIsRunning; }; #endif #endif // !NO_GPSIM diff --git a/src/electronics/junctionnode.cpp b/src/electronics/junctionnode.cpp index 53bef2c8..0b688838 100644 --- a/src/electronics/junctionnode.cpp +++ b/src/electronics/junctionnode.cpp @@ -1,62 +1,62 @@ // // C++ Implementation: junctionnode // // Description: // // // // Copyright: See COPYING file that comes with this distribution // // #include "junctionnode.h" #include "pin.h" #include "component.h" -#include +#include JunctionNode::JunctionNode(ICNDocument* icnDocument, int dir, const QPoint& pos, QString* id): ECNode(icnDocument, Node::ec_junction, dir, pos, id) { QString name("JunctionNode"); if (id) { name.append(QString("-%1").arg(*id)); } else { name.append("-Unknown"); } setObjectName( name.toLatin1().data() ); } JunctionNode::~JunctionNode() { } void JunctionNode::drawShape( QPainter & p ) { initPainter( p ); double v = pin() ? pin()->voltage() : 0.0; QColor voltageColor = Component::voltageColor( v ); QPen pen = p.pen(); if ( isSelected() ) pen = m_selectedColor; else if ( m_bShowVoltageColor ) pen = voltageColor; p.setPen( pen ); p.setBrush( pen.color() ); p.drawRect( -1, -1, 3, 3 ); deinitPainter( p ); } void JunctionNode::initPoints() { setPoints( QPolygon( QRect( -4, -4, 8, 8 ) ) ); } diff --git a/src/electronics/models/utils/spice-to-nice.cpp b/src/electronics/models/utils/spice-to-nice.cpp index 405235c7..2ffc357b 100644 --- a/src/electronics/models/utils/spice-to-nice.cpp +++ b/src/electronics/models/utils/spice-to-nice.cpp @@ -1,182 +1,182 @@ #include #include -#include -#include -#include -#include +#include +#include +#include +#include using namespace std; const int minPrefixExp = -24; const int maxPrefixExp = 24; const int numPrefix = int((maxPrefixExp-minPrefixExp)/3)+1; const QString SIprefix[] = {"y","z","a","f","p","n",QChar(0xB5),"m","","k","M","G","T","P","E","Z","Y"}; /** * Converts a number string (including an optional SI suffix) to a real number. */ double toReal( QString text ); const QString inputFile = "spicemodels_bipolar_transistors.txt"; const QString outputFile = "output.lib"; #define setProperty( property, value ) outputStream << QString("%1=%2\n").arg( property ).arg( value ) int main() { QFile input( inputFile ); if ( !input.open( QIODevice::ReadOnly ) ) { cerr << "Could not open input file \""<= 0 ) { QString property = rx.cap( 1 ); QString value = rx.cap( 2 ); setProperty( property, toReal( value ) ); pos += 4; // avoid the string we just found } } else cerr << "Unknown line for line \""< -#include +#include -#include +#include Pin::Pin( ECNode * parent ) { assert(parent); m_pECNode = parent; m_voltage = 0.; m_current = 0.; m_eqId = -2; m_bCurrentIsKnown = false; m_groundType = Pin::gt_never; } Pin::~Pin() { WireList::iterator end = m_inputWireList.end(); for ( WireList::iterator it = m_inputWireList.begin(); it != end; ++it ) delete (Wire *)(*it); end = m_outputWireList.end(); for ( WireList::iterator it = m_outputWireList.begin(); it != end; ++it ) delete (Wire *)(*it); } PinList Pin::localConnectedPins( ) const { // qDebug() << Q_FUNC_INFO << "Input wires: "<startPin(); } end = m_outputWireList.end(); for ( WireList::const_iterator it = m_outputWireList.begin(); it != end; ++it ) { if (*it) pins << (*it)->endPin(); } pins += m_switchConnectedPins; return pins; } void Pin::setSwitchConnected( Pin * pin, bool isConnected ) { if (!pin) return; if (isConnected) { if ( !m_switchConnectedPins.contains(pin) ) m_switchConnectedPins.append(pin); } else m_switchConnectedPins.removeAll(pin); } void Pin::setSwitchCurrentsUnknown() { if (!m_switchList.empty()) { m_switchList.removeAt( 0l ); } else { qDebug() << "Pin::setSwitchCurrentsUnknown - WARN - unexpected empty switch list"; } m_unknownSwitchCurrents = m_switchList; } void Pin::addCircuitDependentPin( Pin * pin ) { if ( pin && !m_circuitDependentPins.contains(pin) ) m_circuitDependentPins.append(pin); } void Pin::addGroundDependentPin( Pin * pin ) { if ( pin && !m_groundDependentPins.contains(pin) ) m_groundDependentPins.append(pin); } void Pin::removeDependentPins() { m_circuitDependentPins.clear(); m_groundDependentPins.clear(); } void Pin::addElement( Element * e ) { if ( !e || m_elementList.contains(e) ) return; m_elementList.append(e); } void Pin::removeElement( Element * e ) { m_elementList.removeAll(e); } void Pin::addSwitch( Switch * sw ) { if ( !sw || m_switchList.contains( sw ) ) return; m_switchList << sw; } void Pin::removeSwitch( Switch * sw ) { m_switchList.removeAll( sw ); } void Pin::addInputWire( Wire * wire ) { if ( wire && !m_inputWireList.contains(wire) ) m_inputWireList << wire; } void Pin::addOutputWire( Wire * wire ) { if ( wire && !m_outputWireList.contains(wire) ) m_outputWireList << wire; } bool Pin::calculateCurrentFromWires() { m_inputWireList.removeAll( (Wire*)nullptr ); m_outputWireList.removeAll( (Wire*)nullptr ); const WireList inputs = m_inputWireList; const WireList outputs = m_outputWireList; m_current = 0.0; WireList::const_iterator end = inputs.end(); for ( WireList::const_iterator it = inputs.begin(); it != end; ++it ) { if ( !(*it)->currentIsKnown() ) return false; m_current -= (*it)->current(); } end = outputs.end(); for ( WireList::const_iterator it = outputs.begin(); it != end; ++it ) { if ( !(*it)->currentIsKnown() ) return false; m_current += (*it)->current(); } m_bCurrentIsKnown = true; return true; } diff --git a/src/electronics/pin.h b/src/electronics/pin.h index 07bc614e..e5927aa0 100644 --- a/src/electronics/pin.h +++ b/src/electronics/pin.h @@ -1,217 +1,217 @@ /*************************************************************************** * Copyright (C) 2005 by David Saxton * * david@bluehaze.org * * * * 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. * ***************************************************************************/ #ifndef PIN_H #define PIN_H #include "wire.h" -#include -#include -#include +#include +#include +#include class ECNode; class Element; class Pin; class Switch; class Wire; typedef QList ElementList; typedef QList > PinList; typedef QList SwitchList; typedef QList > WireList; /** @author David Saxton */ class Pin : public QObject { public: /** * Priorities for ground pin. gt_always will (as expected) always assign * the given pin as ground, gt_never will never do. If no gt_always pins * exist, then the pin with the highest priority will be set as ground - * if there is at least one pin that is not of ground type gt_never. These * are only predefined recommended values, so if you choose not to use one * of these, please respect the priorities with respect to the examples, and * always specify a priority between 0 and 20. * @see groundLevel */ enum GroundType { gt_always = 0, // ground gt_high = 5, // voltage points gt_medium = 10, // voltage sources gt_low = 15, // current sources gt_never = 20 // everything else }; Pin( ECNode * parent ); ~Pin() override; ECNode * parentECNode() const { return m_pECNode; } /** * This function returns the pins that are directly connected to this pins: * either at the ends of connected wires, or via switches. */ PinList localConnectedPins() const; /** * Adds/removes the given pin to the list of ones that this pin is/isn't * connected to via a switch. */ void setSwitchConnected( Pin * pin, bool isConnected ); /** * After calculating the nodal voltages in the circuit, this function should * be called to tell the pin what its voltage is. */ void setVoltage( double v ) { m_voltage = v; } /** * Returns the voltage as set by setVoltage. */ double voltage() const { return m_voltage; } /** * After calculating nodal voltages, each component will be called to tell * its pins what the current flowing *into* the component is. This sets it * to zero in preparation to merging the current. */ void resetCurrent() { m_current = 0.0; } /** * Adds the given current to that already flowing into the pin. * @see setCurrent */ void mergeCurrent( double i ) { m_current += i; } /** * Returns the current as set by mergeCurrent. */ double current() const { return m_current; } /** * In many cases (such as if this pin is a ground pin), the current * flowing into the pin has not been calculated, and so the value * returned by current() cannot be trusted. */ void setCurrentKnown( bool isKnown ) { m_bCurrentIsKnown = isKnown; } /** * Tell thie Pin that none of the currents from the switches have yet * been merged. */ void setSwitchCurrentsUnknown(); // { m_switchList.erase(nullptr); m_unknownSwitchCurrents = m_switchList; } /** * This returns the value given by setCurrentKnown AND'd with whether * we know the current from each switch attached to this pin. * @see setCurrentKnown */ bool currentIsKnown() const { return m_bCurrentIsKnown && m_unknownSwitchCurrents.isEmpty(); } /** * Tells the Pin that the current from the given switch has been merged. */ void setSwitchCurrentKnown( Switch * sw ) { m_unknownSwitchCurrents.removeAll( sw ); } /** * Tries to calculate the Pin current from the input / output wires. * @return whether was successful. */ bool calculateCurrentFromWires(); /** * Sets the "ground type" - i.e. the priority that this pin has for being * ground over other pins in the circuit. Lower gt = higher priority. It's * recommended to use Pin::GroundType. */ void setGroundType( int gt ) { m_groundType = gt; } /** * Returns the priority for ground. */ int groundType() const { return m_groundType; } /** * Adds a dependent pin - one whose voltages will (or might) affect the * voltage of this pin. This is set by Component. */ void addCircuitDependentPin( Pin * pin ); /** * Adds a dependent pin - one whose voltages will (or might) affect the * voltage of this pin. This is set by Component. */ void addGroundDependentPin( Pin * pin ); /** * Removes all Circuit and Ground dependent pins. */ void removeDependentPins(); /** * Returns the ids of the pins whose voltages will affect this pin. * @see void setDependentPins( QStringList ids ) */ PinList circuitDependentPins() const { return m_circuitDependentPins; } /** * Returns the ids of the pins whose voltages will affect this pin. * @see void setDependentPins( QStringList ids ) */ PinList groundDependentPins() const { return m_groundDependentPins; } /** * Use this function to set the pin identifier for equations, * which should be done every time new pins are registered. */ void setEqId( int id ) { m_eqId = id; } /** * The equation identifier. * @see setEqId */ int eqId() const { return m_eqId; } /** * Returns a list of elements that will affect this pin (e.g. if this * pin is part of a resistor, then that list will contain a pointer to a * Resistance element) */ ElementList elements() const { return m_elementList; } /** * Adds an element to the list of those that will affect this pin. */ void addElement( Element *e ); /** * Removes an element from the list of those that will affect this pin. */ void removeElement( Element *e ); /** * Adds an switch to the list of those that will affect this pin. */ void addSwitch( Switch *e ); /** * Removes an switch from the list of those that will affect this pin. */ void removeSwitch( Switch *e ); void addInputWire( Wire * wire ); void addOutputWire( Wire * wire ); void removeWire( Wire * wire ); WireList inputWireList() const { return m_inputWireList; } WireList outputWireList() const { return m_outputWireList; } int numWires() const { return m_inputWireList.size() + m_outputWireList.size(); } protected: double m_voltage; double m_current; int m_eqId; int m_groundType; bool m_bCurrentIsKnown; PinList m_circuitDependentPins; PinList m_groundDependentPins; PinList m_switchConnectedPins; ElementList m_elementList; WireList m_inputWireList; WireList m_outputWireList; ECNode * m_pECNode; SwitchList m_switchList; SwitchList m_unknownSwitchCurrents; }; #endif diff --git a/src/electronics/pinnode.cpp b/src/electronics/pinnode.cpp index 487d5a19..2ada815b 100644 --- a/src/electronics/pinnode.cpp +++ b/src/electronics/pinnode.cpp @@ -1,134 +1,134 @@ // // C++ Implementation: pinnode // // Description: // // // // Copyright: See COPYING file that comes with this distribution // // #include "pinnode.h" #include "pin.h" #include "component.h" -#include +#include #include /// The maximum length of the voltage indiactor const int vLength = 8; /// The current at the middle of the current indicator const double iMidPoint = 0.03; /// The maximum thicnkess of the current indicator const int iLength = 6; inline double calcIProp( const double i ) { return 1 - iMidPoint/(iMidPoint+std::abs(i)); } inline int calcThickness( const double prop ) { return (int)((iLength-2)*prop+2); } inline int calcLength( double v ) { double prop = Component::voltageLength( v ); if ( v > 0 ) prop *= -1.0; return int(vLength * prop); } PinNode::PinNode(ICNDocument* icnDocument, int dir, const QPoint& pos, QString* id) : ECNode(icnDocument, Node::ec_pin, dir, pos, id) { QString name("PinNode"); if (id) { name.append(QString("-%1").arg(*id)); } else { name.append("-Unknown"); } setObjectName( name.toLatin1().data() ); m_pinPoint = new KtlQCanvasRectangle( 0, 0, 3, 3, canvas() ); m_pinPoint->setBrush(Qt::black); m_pinPoint->setPen( QPen(Qt::black)); } PinNode::~PinNode() { } void PinNode::drawShape( QPainter & p ) { initPainter( p ); double v = pin() ? pin()->voltage() : 0.0; QColor voltageColor = Component::voltageColor( v ); QPen pen = p.pen(); if ( isSelected() ) pen = m_selectedColor; else if ( m_bShowVoltageColor ) pen = voltageColor; if (m_pinPoint) { bool drawDivPoint; QPoint divPoint = findConnectorDivergePoint(&drawDivPoint); m_pinPoint->setVisible(drawDivPoint); m_pinPoint->move( divPoint.x()-1, divPoint.y()-1 ); m_pinPoint->setBrush( pen.color() ); m_pinPoint->setPen( pen.color() ); } // Now to draw on our current/voltage bar indicators int length = calcLength( v ); if ( (numPins() == 1) && m_bShowVoltageBars && length != 0 ) { // we can assume that v != 0 as length != 0 double i = pin()->current(); double iProp = calcIProp(i); int thickness = calcThickness(iProp); p.setPen( QPen( voltageColor, thickness ) ); // The node line (drawn at the end of this function) will overdraw // some of the voltage bar, so we need to adapt the length if ( (v > 0) && (((225 < m_dir) && (m_dir < 315)) || ((45 < m_dir) && (m_dir < 135))) ) length--; else if ( (v < 0) && (((135 < m_dir) && (m_dir < 225)) || ((315 < m_dir) || (m_dir < 45))) ) length++; if ( (m_dir > 270) || (m_dir <= 90) ) p.drawLine( 3, 0, 3, length ); else p.drawLine( 3, 0, 3, -length ); } pen.setWidth( (numPins() > 1) ? 2 : 1 ); p.setPen( pen ); p.drawLine( 0, 0, m_length, 0 ); deinitPainter( p ); } void PinNode::initPoints() { int l = - m_length; // Bounding rectangle, facing right QPolygon pa( QRect( 0, -8, l, 16 ) ); QMatrix m; m.rotate( m_dir ); pa = m.map(pa); setPoints(pa); } diff --git a/src/electronics/port.cpp b/src/electronics/port.cpp index 6d939380..76b3d438 100644 --- a/src/electronics/port.cpp +++ b/src/electronics/port.cpp @@ -1,572 +1,572 @@ /*************************************************************************** * Copyright (C) 2005 by David Saxton * * david@bluehaze.org * * * * 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 section should be kept at the top to handle detection of os -#include +#include #if defined(Q_OS_DARWIN) || defined(Q_OS_MACX) #define DARWIN #endif #include "port.h" -#include +#include #include #include #include #include #if !defined(DARWIN) && !defined(__FreeBSD__) #include #endif //BEGIN class Port Port::Port() { } Port::~Port() { } QStringList Port::ports( unsigned probeResult ) { #if !defined(DARWIN) && !defined(__FreeBSD__) return SerialPort::ports(probeResult) + ParallelPort::ports(probeResult); #else return SerialPort::ports(probeResult); #endif } //END class Port //BEGIN class SerialPort SerialPort::SerialPort() { m_file = -1; } SerialPort::~SerialPort() { closePort(); } void SerialPort::setPinState( Pin pin, bool state ) { if ( m_file == -1 ) return; int flags = -1; switch ( pin ) { case TD: ioctl( m_file, state ? TIOCSBRK : TIOCCBRK, 0 ); return; case DTR: flags = TIOCM_DTR; break; case DSR: flags = TIOCM_DSR; break; case RTS: flags = TIOCM_RTS; break; case CD: case RD: case GND: case CTS: case RI: break; }; if ( flags == -1 ) { qCritical() << Q_FUNC_INFO << "Bad pin " << pin << endl; return; } if ( ioctl( m_file, state ? TIOCMBIS : TIOCMBIC, & flags ) == -1 ) qCritical() << Q_FUNC_INFO << "Could not set pin " << pin << " errno = " << errno << endl; } bool SerialPort::pinState( Pin pin ) { if ( m_file == -1 ) return false; int mask = 0; switch ( pin ) { case CD: mask = TIOCM_CD; break; case RD: mask = TIOCM_SR; break; case CTS: mask = TIOCM_CTS; break; case RI: mask = TIOCM_RI; break; case TD: case DTR: case GND: case DSR: case RTS: break; } if ( mask == 0 ) { qCritical() << Q_FUNC_INFO << "Bad pin " << pin << endl; return false; } int bits = 0; if ( ioctl( m_file, TIOCMGET, & bits ) == -1 ) { qCritical() << Q_FUNC_INFO << "Could not read pin" << pin << " errno = " << errno << endl; return false; } return bits & mask; } Port::ProbeResult SerialPort::probe( const QString & port ) { int file = open( port.toAscii(), O_NOCTTY | O_NONBLOCK | O_RDONLY ); if ( file == -1 ) return Port::DoesntExist; close(file); file = open( port.toAscii(), O_NOCTTY | O_NONBLOCK | O_RDWR ); if ( file == -1 ) return Port::ExistsButNotRW; close(file); return Port::ExistsAndRW; } bool SerialPort::openPort( const QString & port, speed_t baudRate ) { closePort(); m_file = open( port.toAscii(), O_NOCTTY | O_NONBLOCK | O_RDWR ); if ( m_file == -1 ) { qCritical() << Q_FUNC_INFO << "Could not open port " << port << endl; return false; } termios state; tcgetattr( m_file, & state ); // Save the previous state for restoration in close. m_previousState = state; state.c_iflag = IGNBRK | IGNPAR; state.c_oflag = 0; state.c_cflag = baudRate | CS8 | CREAD | CLOCAL; state.c_lflag = 0; tcsetattr( m_file, TCSANOW, & state ); return true; } void SerialPort::closePort() { if ( m_file == -1 ) return; ioctl( m_file, TIOCCBRK, 0 ); usleep(1); tcsetattr( m_file, TCSANOW, & m_previousState ); close( m_file ); m_file = -1; } QStringList SerialPort::ports( unsigned probeResult ) { QStringList list; for ( int i = 0; i < 8; ++i ) { QString dev = QString("/dev/ttyS%1").arg(i); if ( probe(dev) & probeResult ) list << dev; } for ( unsigned i = 0; i < 8; ++i ) { QString dev = QString("/dev/tts/%1").arg(i); if ( probe(dev) & probeResult ) list << dev; } for ( unsigned i = 0; i < 8; ++i ) { QString dev = QString("/dev/ttyUSB%1").arg(i); if ( probe(dev) & probeResult ) list << dev; } for ( unsigned i = 0; i < 8; ++i ) { QString dev = QString("/dev/usb/tts/%1").arg(i); if ( probe(dev) & probeResult ) list << dev; } return list; } //END class SerialPort //BEGIN class ParallelPort // I wasn't able to find any documentation on programming the parallel port // in Darwin, so I've just functionally neutered this section. Apparently // parallel output is handled on a case by case basis (???) by the // manufacturer of whatever USB dongle is, unless they build it as a // Comms class device, in which case it is treated as a serial device. // ( Info from Garth Cummings, Apple Developer Technical Support ) const int IRQ_MODE_BIT = 1 << 20; // Controls if pin 10 (Ack) causes interrupts const int INPUT_MODE_BIT = 1 << 21; // Controls if the data pins are input or output // No code using these values will be reached on Darwin, this is just to // keep the preprocessor happy. #if defined(DARWIN) || defined(__FreeBSD__) #define PPRDATA 0xFACADE #define PPRCONTROL 0xC001D00D #define PPWDATA 0xC0EDBABE #define PPWCONTROL 0xFEEDFACE #define PPRSTATUS 0xBAADF00D #define PPCLAIM 0xDEADBEEF #define PPRELEASE 0xCAFE #endif const unsigned long IOCTL_REG_READ[3] = { PPRDATA, PPRSTATUS, PPRCONTROL, }; const unsigned long IOCTL_REG_WRITE[3] = { PPWDATA, 0, PPWCONTROL, }; // const int INVERT_MASK[3] = { // 2017.10.01 - comment unused code // 0x0, // 0x80, // 10000000 // 0x0b, // 00001011 // }; ParallelPort::ParallelPort() { reset(); } ParallelPort::~ParallelPort() { } void ParallelPort::reset() { m_file = -1; m_reg[Data] = 0; m_reg[Status] = 0; m_reg[Control] = 0; m_outputPins = INPUT_MODE_BIT | IRQ_MODE_BIT; m_inputPins = STATUS_PINS | INPUT_MODE_BIT | IRQ_MODE_BIT; } //BEGIN Pin-oriented operations void ParallelPort::setPinState( int pins, bool state ) { // only allow writing to output pins pins &= m_outputPins; if ( pins & DATA_PINS ) setDataState( (pins & DATA_PINS) >> 0, state ); if ( pins & CONTROL_PINS ) setControlState( (pins & CONTROL_PINS) >> 16, state ); } int ParallelPort::pinState( int pins ) { int value = 0; // only allow reading from input pins pins &= m_inputPins; if ( pins & DATA_PINS ) value |= ((readFromRegister( Data ) & ((pins & DATA_PINS) >> 0)) << 0); if ( pins & STATUS_PINS ) value |= ((readFromRegister( Status ) & ((pins & STATUS_PINS) >> 8)) << 8); if ( pins & CONTROL_PINS ) value |= ((readFromRegister( Control ) & ((pins & CONTROL_PINS) >> 16)) << 16); return value; } void ParallelPort::setDataState( uchar pins, bool state ) { uchar value = readFromRegister( Data ); if ( state ) value |= pins; else value &= ~pins; writeToData( value ); } void ParallelPort::setControlState( uchar pins, bool state ) { uchar value = readFromRegister( Control ); if ( state ) value |= pins; else value &= ~pins; writeToControl( value ); } //END Pin-oriented operations //BEGIN Register-oriented operations uchar ParallelPort::readFromRegister( Register reg ) { #if defined(DARWIN) || defined(__FreeBSD__) return 0; #endif if ( m_file == -1 ) return 0; // uchar value = inb( m_lpBase + reg ) ^ INVERT_MASK[reg]; uchar value = 0; if ( ioctl( m_file, IOCTL_REG_READ[reg], &value ) ) qCritical() << Q_FUNC_INFO << "errno=" << errno << endl; else m_reg[reg] = value; return value; } void ParallelPort::writeToRegister( Register reg, uchar value ) { #if defined(DARWIN) || defined(__FreeBSD__) return; #endif if ( m_file == -1 ) return; // outb( value ^ INVERT_MASK[reg], m_lpBase + reg ); if ( ioctl( m_file, IOCTL_REG_WRITE[reg], & value ) ) qCritical() << Q_FUNC_INFO << "errno=" << errno << endl; else m_reg[reg] = value; } void ParallelPort::writeToData( uchar value ) { writeToRegister( Data, value ); } void ParallelPort::writeToControl( uchar value ) { // Set all inputs to ones value |= ((m_inputPins & CONTROL_PINS) >> 16); writeToRegister( Control, value ); } //END Register-oriented operations //BEGIN Changing pin directions void ParallelPort::setDataDirection( Direction dir ) { if ( dir == Input ) { m_inputPins |= DATA_PINS; m_outputPins &= ~DATA_PINS; } else { m_inputPins &= DATA_PINS; m_outputPins |= ~DATA_PINS; } setPinState( INPUT_MODE_BIT, dir == Input ); } void ParallelPort::setControlDirection( int pins, Direction dir ) { pins &= CONTROL_PINS; if ( dir == Input ) { m_inputPins |= pins; m_outputPins &= ~pins; } else { m_inputPins &= pins; m_outputPins |= ~pins; } setControlState( 0, true ); } //END Changing pin directions Port::ProbeResult ParallelPort::probe( const QString & port ) { #if defined(DARWIN) || defined(__FreeBSD__) return Port::DoesntExist; #endif int file = open( port.toAscii(), O_RDWR ); if ( file == -1 ) return Port::DoesntExist; if ( ioctl( file, PPCLAIM ) != 0 ) { close(file); return Port::ExistsButNotRW; } ioctl( file, PPRELEASE ); close(file); return Port::ExistsAndRW; } QStringList ParallelPort::ports( unsigned probeResult ) { QStringList list; #if defined(DARWIN) || defined(__FreeBSD__) return list; #endif for ( unsigned i = 0; i < 8; ++i ) { QString dev = QString("/dev/parport%1").arg(i); if ( probe(dev) & probeResult ) list << dev; } for ( unsigned i = 0; i < 8; ++i ) { QString dev = QString("/dev/parports/%1").arg(i); if ( probe(dev) & probeResult ) list << dev; } return list; } bool ParallelPort::openPort( const QString & port ) { #if defined(__FreeBSD__) qWarning() << Q_FUNC_INFO << "Parallel ports disabled on FreeBSD" << endl; return false; #elif defined(DARWIN) qWarning() << Q_FUNC_INFO << "Parallel ports disabled on Darwin" << endl; return false; #endif if ( m_file != -1 ) { qWarning() << Q_FUNC_INFO << "Port already open" << endl; return false; } m_file = open( port.toAscii(), O_RDWR ); if ( m_file == -1 ) { qCritical() << Q_FUNC_INFO << "Could not open port \"" << port << "\": errno="< +#include #include "elementset.h" class CircuitDocument; class Wire; class Pin; class Element; class LogicOut; typedef QList > PinList; typedef QList ElementList; class LogicCacheNode { public: LogicCacheNode(); ~LogicCacheNode(); LogicCacheNode * high; LogicCacheNode * low; QuickVector * data; }; /** Usage of this class (usually invoked from CircuitDocument): (1) Add Wires, Pins and Elements to the class as appropriate (2) Call init to initialize the simulation (3) Control the simulation with step() This class can be considered a bridge between the gui-tainted CircuitDocument - specific to this implementation, and the pure untainted ElementSet. Please keep it that way. @short Simulates a collection of components @author David Saxton */ class Circuit { public: Circuit(); ~Circuit(); void addPin( Pin *node ); void addElement( Element *element ); bool contains( Pin *node ); bool containsNonLinear() const { return m_elementSet->containsNonLinear(); } void init(); /** * Called after everything else has been setup - before doNonLogic or * doLogic are called for the first time. Preps the circuit. */ void initCache(); /** * Marks all cached results as invalidated and removes them. */ void setCacheInvalidated(); /** * Solves for non-logic elements */ void doNonLogic(); /** * Solves for logic elements (i.e just does fbSub) */ void doLogic() { m_elementSet->doLinear(false); } void displayEquations(); void updateCurrents(); void createMatrixMap(); /** * This will identify the ground node and non-ground nodes in the given set. * Ground will be given the eqId -1, non-ground of 0. * @param highest The highest ground type of the groundnodes found. If no ground nodes were found, this will be (gt_never-1). * @returns the number of ground nodes. If all nodes are at or below the * gt_never threshold, then this will be zero. */ static int identifyGround( PinList nodeList, int *highest = nullptr ); void setNextChanged( Circuit * circuit, unsigned char chain ) { m_pNextChanged[chain] = circuit; } Circuit * nextChanged( unsigned char chain ) const { return m_pNextChanged[chain]; } void setCanAddChanged( bool canAdd ) { m_bCanAddChanged = canAdd; } bool canAddChanged() const { return m_bCanAddChanged; } protected: void cacheAndUpdate(); /** * Update the nodal voltages from those calculated in ElementSet */ void updateNodalVoltages(); /** * Step the reactive elements. */ void stepReactive(); /** * Returns true if any of the nodes are ground */ static bool recursivePinAdd( Pin *node, PinList *unassignedNodes, PinList *associated, PinList *nodes ); int m_cnodeCount; int m_branchCount; int m_prepNLCount; // Count until next m_elementSet->prepareNonLinear() is called PinList m_pinList; ElementList m_elementList; ElementSet *m_elementSet; //Stuff for caching bool m_bCanCache; LogicCacheNode * m_pLogicCacheBase; unsigned m_logicOutCount; LogicOut ** m_pLogicOut; bool m_bCanAddChanged; Circuit * m_pNextChanged[2]; }; #endif diff --git a/src/electronics/simulation/element.cpp b/src/electronics/simulation/element.cpp index 59050f07..b4f7e03c 100644 --- a/src/electronics/simulation/element.cpp +++ b/src/electronics/simulation/element.cpp @@ -1,159 +1,159 @@ /*************************************************************************** * Copyright (C) 2003-2004 by David Saxton * * david@bluehaze.org * * * * 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 "element.h" #include "elementset.h" #include -#include +#include //BEGIN class Element Element::Element() { b_status = false; p_eSet = nullptr; b_componentDeleted = false; for ( int i = 0; i < MAX_CNODES; i++ ) p_cnode[i] = nullptr; resetCurrents(); for ( int i = 0; i < MAX_CBRANCHES; i++ ) p_cbranch[i] = nullptr; m_numCBranches = 0; m_numCNodes = 0; } Element::~ Element() { } void Element::resetCurrents() { for ( int i=0; i<8; i++ ) m_cnodeI[i] = 0.0; } void Element::setElementSet( ElementSet *c ) { assert(!b_componentDeleted); assert(!p_eSet); if (!c) return elementSetDeleted(); p_eSet = c; updateStatus(); } void Element::componentDeleted() { b_componentDeleted = true; b_status = false; p_eSet = nullptr; setCNodes(); setCBranches(); } void Element::elementSetDeleted() { if (b_componentDeleted) return delete this; b_status = false; // qDebug() << "Element::elementSetDeleted(): Setting b_status to false, this="<-1)?p_eSet->cnodes()[n0]:(n0==-1?p_eSet->ground():nullptr); p_cnode[1] = (n1>-1)?p_eSet->cnodes()[n1]:(n1==-1?p_eSet->ground():nullptr); p_cnode[2] = (n2>-1)?p_eSet->cnodes()[n2]:(n2==-1?p_eSet->ground():nullptr); p_cnode[3] = (n3>-1)?p_eSet->cnodes()[n3]:(n3==-1?p_eSet->ground():nullptr); updateStatus(); } void Element::setCBranches( const int b0, const int b1, const int b2, const int b3 ) { if ( !p_eSet ) { // cerr << "Element::setCBranches: can't set branches without circuit!"<-1)?p_eSet->cbranches()[b0]:nullptr; p_cbranch[1] = (b1>-1)?p_eSet->cbranches()[b1]:nullptr; p_cbranch[2] = (b2>-1)?p_eSet->cbranches()[b2]:nullptr; p_cbranch[3] = (b3>-1)?p_eSet->cbranches()[b3]:nullptr; updateStatus(); } bool Element::updateStatus() { // First, set status to false if all nodes in use are ground b_status = false; for ( int i=0; iisGround:false; } // Set status to false if any of the nodes are not set for ( int i=0; i +#include #include #include #include ElementSet::ElementSet( Circuit * circuit, const int n, const int m ) : m_cb(m), m_cn(n), m_pCircuit(circuit) { int tmp = m_cn + m_cb; p_logicIn = nullptr; if( tmp) { p_A = new Matrix( m_cn, m_cb ); p_b = new QuickVector(tmp); p_x = new QuickVector(tmp); } else { p_A = nullptr; p_x = p_b = nullptr; } m_cnodes = new CNode*[m_cn]; for ( uint i=0; iisGround = true; b_containsNonLinear = false; } ElementSet::~ElementSet() { const ElementList::iterator end = m_elementList.end(); for ( ElementList::iterator it = m_elementList.begin(); it != end; ++it ) { // Note: By calling setElementSet(nullptr), we might have deleted it (the Element will commit // suicide when both the ElementSet and Component to which it belongs have deleted // themselves). So be very careful it you plan to do anything with the (*it) pointer if (*it) (*it)->elementSetDeleted(); } for ( uint i=0; isetCacheInvalidated(); } void ElementSet::addElement( Element *e ) { if ( !e || m_elementList.contains(e) ) return; e->setElementSet(this); m_elementList.append(e); if ( e->isNonLinear() ) { b_containsNonLinear = true; m_cnonLinearList.append( static_cast(e) ); } } void ElementSet::createMatrixMap() { // mapping nolonger done, overly ambitious optimization... // And do our logic as well... m_clogic = 0; ElementList::iterator end = m_elementList.end(); for ( ElementList::iterator it = m_elementList.begin(); it != end; ++it ) { if ( dynamic_cast(*it) ) m_clogic++; } p_logicIn = new LogicIn*[m_clogic]; int i=0; for ( ElementList::iterator it = m_elementList.begin(); it != end; ++it ) { if ( LogicIn * in = dynamic_cast(*it) ) p_logicIn[i++] = in; } } void ElementSet::doNonLinear( int maxIterations, double maxErrorV, double maxErrorI ) { QuickVector *p_x_prev = new QuickVector(p_x); // And now tell the cnodes and cbranches about their new voltages & currents updateInfo(); const NonLinearList::iterator end = m_cnonLinearList.end(); int k = 0; do { // Tell the nonlinear elements to update its J, A and b from the newly calculated x for ( NonLinearList::iterator it = m_cnonLinearList.begin(); it != end; ++it ) (*it)->update_dc(); *p_x = *p_b; // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< p_A->performLU(); p_A->fbSub(p_x); updateInfo(); // Now, check for convergence bool converged = true; for ( unsigned i = 0; i < m_cn; ++i ) { double diff = std::abs( (*p_x_prev)[i] - (*p_x)[i] ); if ( diff > maxErrorI ) { converged = false; break; } } if ( converged ) { for ( unsigned i = m_cn; i < m_cn+m_cb; ++i ) { double diff = std::abs( (*p_x_prev)[i] - (*p_x)[i] ); if ( diff > maxErrorV ) { converged = false; break; } } } *p_x_prev = *p_x; // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< if ( converged ) break; } while ( ++k < maxIterations ); delete p_x_prev; } bool ElementSet::doLinear( bool performLU ) { if ( b_containsNonLinear || (!p_b->isChanged() && ((performLU && !p_A->isChanged()) || !performLU)) ) return false; if (performLU) p_A->performLU(); *p_x = *p_b; // <<< why does this code work, when I try it, I always get the default shallow copy. p_A->fbSub(p_x); updateInfo(); p_b->setUnchanged(); return true; } void ElementSet::updateInfo() { for ( uint i=0; iv = v; } else { (*p_x)[i] = 0.; m_cnodes[i]->v = 0.; } } for ( uint i=0; ii = I; } else { (*p_x)[i+m_cn] = 0.; m_cbranches[i]->i = 0.; } } // Tell logic to check themselves for ( uint i=0; icheck(); } } void ElementSet::displayEquations() { std::cout.setf(std::ios_base::fixed); std::cout.precision(5); std::cout.setf(std::ios_base::showpoint); std::cout << "A x = b :"<g(i,j); // if ( value > 0 ) cout <<"+"; // else if ( value == 0 ) cout <<" "; std::cout.width(10); std::cout << value<<" "; } std::cout << ") ( "<<(*p_x)[i]<<" ) = ( "; std::cout<<(*p_b)[i]<<" )"<displayLU(); } diff --git a/src/electronics/simulation/elementset.h b/src/electronics/simulation/elementset.h index 0d0ae23a..d1c4082f 100644 --- a/src/electronics/simulation/elementset.h +++ b/src/electronics/simulation/elementset.h @@ -1,136 +1,136 @@ /*************************************************************************** * Copyright (C) 2003-2004 by David Saxton * * david@bluehaze.org * * * * 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. * ***************************************************************************/ #ifndef ELEMENTSET_H #define ELEMENTSET_H //#include -#include +#include class CBranch; class Circuit; class CNode; class Element; class ElementSet; class LogicIn; class Matrix; class NonLinear; class QuickVector; // not exactly sure how these types of declarations work. typedef QList ElementList; typedef QList NonLinearList; /** Steps in simulation of a set of elements: (1) Create this class with given number of nodes "n" and voltage sources "m" (2) Add the various elements with addElement. (3) Call performDC() (4) Get the nodal voltages and voltage currents with x() (5) Repeat steps 3 and 4 if necessary for further transient analysis. This class shouldn't be confused with the Circuit class, but considered a helper class to Circuit. Circuit will handle the simulation of a set of components over time. This just finds the DC-operating point of the circuit for a given set of elements. @short Handles a set of circuit elements @author David Saxton */ class ElementSet { public: /** * Create a new circuit, with "n" nodes and "m" voltage sources. * After creating the circuit, you must call setGround to specify * the ground nodes, before adding any elements. */ ElementSet( Circuit * circuit, const int n, const int m ); /** * Destructor. Note that only the matrix and supporting data is deleted. * i.e. Any elements added to the circuit will not be deleted. */ ~ElementSet(); Circuit * circuit() const { return m_pCircuit; } void addElement( Element *e ); void setCacheInvalidated(); /** * Returns the matrix in use. This is created once on the creation of the ElementSet * class, and deleted in the destructor, so the pointer returned will never change. */ Matrix *matrix() const { return p_A; } /** * Returns the vector for b (i.e. the independent currents & voltages) */ QuickVector *b() const { return p_b; } /** * Returns the vector for x (i.e. the currents & voltages at the branches and nodes) */ QuickVector *x() const { return p_x; } /** * @return if we have any nonlinear elements (e.g. diodes, tranaistors). */ bool containsNonLinear() const { return b_containsNonLinear; } /** * Solves for nonlinear elements, or just does linear if it doesn't contain * any nonlinear. */ void doNonLinear( int maxIterations, double maxErrorV = 1e-9, double maxErrorI = 1e-12 ); /** * Solves for linear and logic elements. * @returns true if anything changed */ bool doLinear( bool performLU ); CBranch **cbranches() const { return m_cbranches; } CNode **cnodes() const { return m_cnodes; } CNode *ground() const { return m_ground; } /** * Returns the number of nodes in the circuit (excluding ground 'nodes') */ int cnodeCount() const { return m_cn; } /** * Returns the number of voltage sources in the circuit */ int cbranchCount() const { return m_cb; } void createMatrixMap(); /** * Displays the matrix equations Ax=b and J(dx)=-r */ void displayEquations(); /** * Update the nodal voltages and branch currents from the x vector */ void updateInfo(); private: // calc engine stuff Matrix *p_A; QuickVector *p_x; QuickVector *p_b; // end calc engine stuff. ElementList m_elementList; NonLinearList m_cnonLinearList; uint m_cb; CBranch **m_cbranches; // Pointer to an array of cbranches uint m_cn; CNode **m_cnodes; // Pointer to an array of cnodes CNode *m_ground; uint m_clogic; LogicIn **p_logicIn; bool b_containsNonLinear; Circuit * m_pCircuit; }; #endif diff --git a/src/electronics/simulation/logic.h b/src/electronics/simulation/logic.h index 1b6f1abf..5875acc7 100644 --- a/src/electronics/simulation/logic.h +++ b/src/electronics/simulation/logic.h @@ -1,215 +1,215 @@ /*************************************************************************** * Copyright (C) 2003-2005 by David Saxton * * david@bluehaze.org * * * * 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. * ***************************************************************************/ #ifndef LOGIC_H #define LOGIC_H #include "element.h" -#include -#include +#include +#include class Component; class Pin; class Simulator; typedef QList > PinList; class LogicConfig { public: LogicConfig(); float risingTrigger; ///< Trigger on rising edge float fallingTrigger; ///< Trigger on falling edge float output; ///< Output voltage float highImpedance; ///< Output impedance when high float lowImpedance; ///< Output impedance when low }; class CallbackClass {}; typedef void(CallbackClass::*CallbackPtr)( bool isHigh ); /** Use this class for Logic Inputs - this will have infinite impedance. Use isHigh() will return whether the voltage level at the pin is high than the predetermined voltage threshold, and setHigh() will make the output high/low, also according to the predetermined logic type / voltages. @short Boolean Logic input */ class LogicIn : public Element { public: LogicIn( LogicConfig config ); ~LogicIn() override; Type type() const override { return Element_LogicIn; } void setElementSet( ElementSet *c ) override; /** * Set logic values from the LogicConfig. */ virtual void setLogic( LogicConfig config ); /** * Check if the input state has changed, to see if we need to callback. */ void check(); /** * Returns whether the pin is 'high', as defined for the logic type * Note: this is defined as the voltage on the pin, as opposed to what the * state was set to (the two are not necessarily the same). */ bool isHigh() const { return m_bLastState; } /** * When the logic state on this LogicIn changes, the function passed in this * function will be called. At most one Callback can be added per LogicIn. */ void setCallback( CallbackClass * object, CallbackPtr func ); /** * Reads the LogicConfig values in from KTLConfig, and returns them in a * nice object form. */ static LogicConfig getConfig(); /** * If this belongs to a logic chain, then this will be called from the chain. */ void setLastState( bool state ) { m_bLastState = state; } /** * Returns a pointer to the next LogicIn in the chain. */ LogicIn * nextLogic() const { return m_pNextLogic; } /** * Sets the next LogicIn in the chain. */ void setNextLogic( LogicIn * next ) { m_pNextLogic = next; } /** * Calls the callback function, if there is one. */ void callCallback() { if (m_pCallbackFunction) (m_pCallbackObject->*m_pCallbackFunction)(m_bLastState); } protected: void updateCurrents() override; void add_initial_dc() override; // TODO: fix this crap NO FUNCTION POINTERS CallbackPtr m_pCallbackFunction; CallbackClass * m_pCallbackObject; bool m_bLastState; LogicIn * m_pNextLogic; LogicConfig m_config; }; /** @short Logic output/input */ class LogicOut : public LogicIn { public: LogicOut( LogicConfig config, bool _high ); ~LogicOut() override; void setLogic( LogicConfig config ) override; void setElementSet( ElementSet *c ) override; Type type() const override { return Element_LogicOut; } /** * Call this function to override the logic-high output impedance as set by * the user. Once set, the impedance will not be changed by the user * updating the config; only by subsequent calls to this function. */ void setOutputHighConductance( double g ); /** * Call this function to override the logic-low output impedance as set by * the user. Once set, the impedance will not be changed by the user * updating the config; only by subsequent calls to this function. */ void setOutputLowConductance( double g ); /** * Call this function to override the logic out voltage as set by the * user. Once set, the impedance will not be changed by the user * updating the config; only by subsequent calls to this function. */ void setOutputHighVoltage( double v ); /** * Returns the voltage that this will output when high. */ double outputHighVoltage() const { return m_vHigh; } /** * Sets the pin to be high/low */ void setHigh( bool high ); /** * @returns the state that this is outputting (regardless of voltage level on logic) */ bool outputState() const { return b_state; } /** * Set whether or not this LogicOut is the head of a LogicChain (controls * itself and a bunch of LogicIns). */ void setUseLogicChain( bool use ); /** * When a LogicOut configured as the start of a LogicChain changes start, it * appends a pointer to itself to the list of change LogicOut, starting from * the Simulator. This functions enables appending the next changed LogicOut * to this one. */ void setNextChanged( LogicOut * logicOut, unsigned char chain ) { m_pNextChanged[chain] = logicOut; } /** * To avoid a pointer to this LogicOut being added twice in one * iteration due to the state changing twice, this LogicOut sets an * added flag to true after adding it to the list of changed. The flag * must be reset to false with this function (done by Simulator). */ void setCanAddChanged( bool canAdd ) { m_bCanAddChanged = canAdd; } /** * Returns the next LogicOut that has changed, when configured as the start * of a LogicChain. * @see setNextChanged */ LogicOut * nextChanged( unsigned char chain ) const { return m_pNextChanged[chain]; } PinList pinList; PinList::iterator pinListBegin; PinList::iterator pinListEnd; protected: void configChanged(); void updateCurrents() override; void add_initial_dc() override; // Pre-initalized levels from config double m_gHigh; double m_gLow; double m_vHigh; // Whether to use the user-defined logic values bool m_bOutputHighConductanceConst; bool m_bOutputLowConductanceConst; bool m_bOutputHighVoltageConst; double m_g_out; double m_v_out; double m_old_g_out; double m_old_v_out; bool b_state; bool m_bCanAddChanged; LogicOut * m_pNextChanged[2]; Simulator * m_pSimulator; bool m_bUseLogicChain; }; #endif diff --git a/src/electronics/simulation/matrix.cpp b/src/electronics/simulation/matrix.cpp index b1fbf3cd..2788bb17 100644 --- a/src/electronics/simulation/matrix.cpp +++ b/src/electronics/simulation/matrix.cpp @@ -1,276 +1,276 @@ /*************************************************************************** * Copyright (C) 2003-2004 by David Saxton * * david@bluehaze.org * * * * 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 "matrix.h" -#include +#include #include #include #include #include /// Minimum value before an entry is deemed "zero" const double epsilon = 1e-50; Matrix::Matrix( CUI n, CUI m ) : m_n(n) { unsigned int size = m_n+m; m_mat = new QuickMatrix(size); m_lu = new QuickMatrix(size); m_y = new double[size]; m_inMap = new int[size]; for ( unsigned int i=0; ifillWithZero(); m_lu->fillWithZero(); unsigned int size = m_mat->size_m(); for ( unsigned int i=0; iswapRows( a, b ); const int old = m_inMap[a]; m_inMap[a] = m_inMap[b]; m_inMap[b] = old; max_k = 0; } void Matrix::performLU() { unsigned int n = m_mat->size_m(); if ( n == 0 ) return; // Copy the affected segment to LU for ( uint i=max_k; i 1e-12 ) { m_lu->partialSAF(k, i, foo, -lu_I_K); } } } max_k = n; } void Matrix::fbSub( QuickVector* b ) { unsigned int size = m_mat->size_m(); for ( uint i=0; i= 0; i-- ) { double sum = 0; for ( uint j=i+1; jfillWithZeros(); unsigned int size = m_mat->size_m(); for ( uint _i=0; _iatAdd(_i, (*m_mat)[i][j] * (*rhs)[j]); } } } void Matrix::displayMatrix() { uint n = m_mat->size_m(); for ( uint _i=0; _i 0 && (*m_mat)[i][j] >= 0 ) qDebug() << "+"; qDebug() << (*m_mat)[i][j] << "("<size_m(); for ( uint _i=0; _i 0 && (*m_lu)[i][j] >= 0 ) std::cout << "+"; std::cout << (*m_lu)[i][j] << "("<"<"< +// #include Resistance::Resistance( const double resistance ) : Element::Element() { m_g = resistance < 1e-9 ? 1e9 : 1./resistance; m_numCNodes = 2; // qDebug() << Q_FUNC_INFO << endl; } Resistance::~Resistance() { // qDebug() << Q_FUNC_INFO << endl; } void Resistance::setConductance( const double g ) { if ( g == m_g ) return; if (p_eSet) p_eSet->setCacheInvalidated(); // Remove old resistance m_g = -m_g; add_initial_dc(); m_g = g; add_initial_dc(); } void Resistance::setResistance( const double r ) { setConductance( r < 1e-9 ? 1e9 : 1./r ); } void Resistance::add_initial_dc() { if (!b_status) return; A_g( 0, 0 ) += m_g; A_g( 1, 1 ) += m_g; A_g( 0, 1 ) -= m_g; A_g( 1, 0 ) -= m_g; } void Resistance::updateCurrents() { if (!b_status) return; const double v=p_cnode[0]->v-p_cnode[1]->v; m_cnodeI[1] = v*m_g; m_cnodeI[0] = -m_cnodeI[1]; } diff --git a/src/electronics/subcircuits.cpp b/src/electronics/subcircuits.cpp index 1ef41ab1..7060747e 100644 --- a/src/electronics/subcircuits.cpp +++ b/src/electronics/subcircuits.cpp @@ -1,189 +1,189 @@ /*************************************************************************** * Copyright (C) 2004-2005 by David Saxton * * david@bluehaze.org * * * * 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 "circuitdocument.h" #include "ecsubcircuit.h" #include "itemdocumentdata.h" #include "itemlibrary.h" #include "itemselector.h" #include "subcircuits.h" #include #include #include #include -#include -#include -#include +#include +#include +#include #include #include Subcircuits::Subcircuits() : QObject() { connect( ComponentSelector::self(), SIGNAL(itemRemoved(const QString& )), this, SLOT(slotItemRemoved(const QString& )) ); } Subcircuits::~Subcircuits() { } static QList asIntList(const QString& string) { QList entries = string.toLatin1().split(','); QList list; for (const QByteArray& s : entries) list << s.toInt(); return list; } void Subcircuits::initECSubcircuit( int subcircuitId, ECSubcircuit *ecSubcircuit ) { const QString fileName = genFileName(subcircuitId); if ( !QFile::exists(fileName) ) { qDebug() << "Subcircuits::createSubcircuit: Subcircuit \""<(itemLibrary()->createItem( "ec/subcircuit", circuitDocument, newItem, newId, false )); ecSubcircuit->property("id")->setValue(id); return ecSubcircuit; } void Subcircuits::loadSubcircuits() { //KConfig *config = kapp->config(); KSharedConfigPtr config = KSharedConfig::openConfig(); //config->setGroup("Subcircuits"); KConfigGroup configGrSubcirc = config->group("Subcircuits"); QList idList = asIntList( configGrSubcirc.readEntry(QString("Ids"), QString()) ); const QList::iterator idListEnd = idList.end(); for ( QList::iterator it = idList.begin(); it != idListEnd; ++it ) { QFile file( genFileName(*it) ); if ( file.open(QIODevice::ReadOnly) == false ) { // File has mysteriously disappeared.... *it = -1; } else { KConfigGroup configGrSubcNr = config->group("Subcircuit_"+QString::number(*it)); updateComponentSelector( *it, configGrSubcNr.readEntry("Name") ); } file.close(); } idList.removeAll(-1); // Update the config file if any ids have been removed //config->setGroup("Subcircuits"); configGrSubcirc.writeEntry( "Ids", idList ); } QString Subcircuits::genFileName( const int nextId ) { return QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/subcircuit_"+QString::number(nextId)+".circuit"; } void Subcircuits::updateComponentSelector( int id, const QString &name ) { if ( name.isEmpty() ) return; ComponentSelector::self()->addItem( name, "sc/"+QString::number(id), i18n("Subcircuits"), KIconLoader::global()->loadIcon( "application-x-circuit", KIconLoader::Small ), true ); } void Subcircuits::addSubcircuit( const QString &name, const QString &subcircuitXml ) { //KConfig *config = kapp->config(); KSharedConfigPtr config = KSharedConfig::openConfig(); KConfigGroup subcircGroup = config->group("Subcircuits"); int nextId = subcircGroup.readEntry( "NextId", 0 ); // ensure dir exists QDir().mkpath(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation)); while ( QFile::exists( genFileName(nextId) ) ) { nextId++; } const int id = nextId; const QString fileName = genFileName(id); QFile file(fileName); if ( file.open(QIODevice::WriteOnly) == false ) { qCritical() << "Subcircuits::addSubcircuit: couldn't open subcircuit save file: "< idList = asIntList( subcircGroup.readEntry(QString("Ids"), QString()) ); idList += id; subcircGroup.writeEntry( "Ids", idList ); subcircGroup.writeEntry( "NextId", ++nextId ); KConfigGroup grSubcircNr = config->group("Subcircuit_"+QString::number(id)); grSubcircNr.writeEntry( "Name", name ); // It's important that we write the configuration *now*, lest the subcircuits be lost grSubcircNr.sync(); updateComponentSelector( id, name ); } void Subcircuits::slotItemRemoved( const QString &id ) { if ( !id.startsWith("sc/") ) { return; } QString temp = id; temp.remove("sc/"); const int id_num = temp.toInt(); const QString fileName = genFileName(id_num); QFile file(fileName); file.remove(); //KConfig *config = kapp->config(); KSharedConfigPtr config = KSharedConfig::openConfig(); KConfigGroup grSc = config->group("Subcircuits"); QList idList = asIntList( grSc.readEntry(QString("Ids"), QString()) ); idList.removeAll(id_num); grSc.writeEntry( "Ids", idList ); } diff --git a/src/electronics/subcircuits.h b/src/electronics/subcircuits.h index 79e1325f..623ca515 100644 --- a/src/electronics/subcircuits.h +++ b/src/electronics/subcircuits.h @@ -1,76 +1,76 @@ /*************************************************************************** * Copyright (C) 2004 by David Saxton * * david@bluehaze.org * * * * 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. * ***************************************************************************/ #ifndef SUBCIRCUITS_H #define SUBCIRCUITS_H -#include +#include class CircuitDocument; class ECSubcircuit; class Subcircuits; inline Subcircuits *subcircuits(); /** Interface for dealing with loading / saving / etc of subcircuits @author David Saxton */ class Subcircuits : public QObject { Q_OBJECT public: ~Subcircuits() override; /** * Handles subcircuit creation when the user selects the subcircuit to be * created. * @param id Id of subcircuit; e.g. "sc/10" */ static ECSubcircuit* createSubcircuit( int id, CircuitDocument *circuitDocument, bool newItem, const char *newId ); /** * Loads a subcircuit into a subcircuit component */ static void initECSubcircuit( int subcircuitId, ECSubcircuit *ecSubcircuit ); /** * Reads in the config entries and adds the subcircuits found to the * component selector */ static void loadSubcircuits(); /** * Saves the given subcircuit to the appdata dir, updates the appropriate * config entries, and adds the subcircuit to the component selector. */ static void addSubcircuit( const QString &name, const QString &subcircuitXml ); /** * returns a path to the appdata dir, e.g. genFileName(2) might return * ~/.kde/share/apps/ktechlab/subcircuit_2.circuit */ static QString genFileName( const int nextId ); /** * Adds the given entry to the component selector */ static void updateComponentSelector( int id, const QString &name ); protected slots: void slotItemRemoved( const QString &id ); private: Subcircuits(); friend Subcircuits* subcircuits(); }; inline Subcircuits* subcircuits() { static Subcircuits *_subcircuits = new Subcircuits(); return _subcircuits; } #endif diff --git a/src/electronics/switch.cpp b/src/electronics/switch.cpp index e06215ef..f9b74fd0 100644 --- a/src/electronics/switch.cpp +++ b/src/electronics/switch.cpp @@ -1,191 +1,191 @@ /*************************************************************************** * Copyright (C) 2005 by David Saxton * * david@bluehaze.org * * * * 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 #include // for rand #include -#include -#include +#include +#include #include "circuitdocument.h" #include "component.h" #include "ecnode.h" #include "pin.h" #include "resistance.h" #include "simulator.h" #include "switch.h" Switch::Switch(Component *parent, Pin *p1, Pin *p2, State state) { m_bouncePeriod_ms = 5; m_bBounce = false; m_bounceStart = 0; m_pBounceResistance = nullptr; m_pP1 = p1; m_pP2 = p2; m_pComponent = parent; m_pStopBouncingTimer = new QTimer(this); connect(m_pStopBouncingTimer, SIGNAL(timeout()), this, SLOT(stopBouncing())); // Force update m_state = (state == Open) ? Closed : Open; setState(state); } Switch::~ Switch() { if (m_pP1) m_pP1->setSwitchConnected(m_pP2, false); if (m_pP2) m_pP2->setSwitchConnected(m_pP1, false); } void Switch::setState(State state) { if (m_state == state) return; m_state = state; if (m_bBounce) startBouncing(); else { // I'm being lazy...calling stopBouncing will connect the stuff stopBouncing(); } } void Switch::setBounce(bool bounce, int msec) { m_bBounce = bounce; m_bouncePeriod_ms = msec; } void Switch::startBouncing() { if (m_pBounceResistance) { // Already active? return; } if (!m_pComponent->circuitDocument()) return; // qDebug() << Q_FUNC_INFO << endl; m_pBounceResistance = m_pComponent->createResistance(m_pP1, m_pP2, 10000); m_bounceStart = Simulator::self()->time(); //FIXME: I broke the bounce feature when I cleaned this out of the simulator, // should probably be put into circuit document or some other solution which doesn't // contaminate that many other classes. // Simulator::self()->attachSwitch( this ); // qDebug() << "m_bounceStart="< -#include +#include Wire::Wire( Pin *startPin, Pin *endPin ) { assert(startPin); assert(endPin); m_pStartPin = startPin; m_pEndPin = endPin; m_current = 0.; m_bCurrentIsKnown = false; m_pStartPin->addOutputWire(this); m_pEndPin->addInputWire(this); } Wire::~Wire() { } bool Wire::calculateCurrent() { if ( m_pStartPin->currentIsKnown() && m_pStartPin->numWires() < 2 ) { m_current = m_pStartPin->current(); m_bCurrentIsKnown = true; return true; } if ( m_pEndPin->currentIsKnown() && m_pEndPin->numWires() < 2 ) { m_current = -m_pEndPin->current(); m_bCurrentIsKnown = true; return true; } if ( m_pStartPin->currentIsKnown() ) { double i = m_pStartPin->current(); bool ok = true; const WireList outlist = m_pStartPin->outputWireList(); WireList::const_iterator end = outlist.end(); for ( WireList::const_iterator it = outlist.begin(); it != end && ok; ++it ) { if ( *it && (Wire*)*it != this ) { if ( (*it)->currentIsKnown() ) i -= (*it)->current(); else ok = false; } } const WireList inlist = m_pStartPin->inputWireList(); end = inlist.end(); for ( WireList::const_iterator it = inlist.begin(); it != end && ok; ++it ) { if ( *it && (Wire*)*it != this ) { if ( (*it)->currentIsKnown() ) i += (*it)->current(); else ok = false; } } if (ok) { m_current = i; m_bCurrentIsKnown = true; return true; } } if ( m_pEndPin->currentIsKnown() ) { double i = -m_pEndPin->current(); bool ok = true; const WireList outlist = m_pEndPin->outputWireList(); WireList::const_iterator end = outlist.end(); for ( WireList::const_iterator it = outlist.begin(); it != end && ok; ++it ) { if ( *it && (Wire*)*it != this ) { if ( (*it)->currentIsKnown() ) i += (*it)->current(); else ok = false; } } const WireList inlist = m_pEndPin->inputWireList(); end = inlist.end(); for ( WireList::const_iterator it = inlist.begin(); it != end && ok; ++it ) { if ( *it && (Wire*)*it != this ) { if ( (*it)->currentIsKnown() ) i -= (*it)->current(); else ok = false; } } if (ok) { m_current = i; m_bCurrentIsKnown = true; return true; } } m_bCurrentIsKnown = false; return false; } double Wire::voltage() const { double temp; if( (temp=m_pStartPin->voltage() - m_pEndPin->voltage()) ) { qCritical() << "Wire voltage error: " << temp << endl; } return m_pStartPin->voltage(); } void Wire::setCurrentKnown( bool known ) { m_bCurrentIsKnown = known; if (!known) m_current = 0.; } diff --git a/src/electronics/wire.h b/src/electronics/wire.h index 334b5bed..0dea6c03 100644 --- a/src/electronics/wire.h +++ b/src/electronics/wire.h @@ -1,68 +1,68 @@ /*************************************************************************** * Copyright (C) 2005 by David Saxton * * david@bluehaze.org * * * * 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. * ***************************************************************************/ #ifndef WIRE_H #define WIRE_H #include -#include -#include +#include +#include class Pin; /** @author David Saxton */ class Wire : public QObject { public: Wire( Pin *startPin, Pin *endPin ); ~Wire() override; /** * Attempts to calculate the current that is flowing through * the connector. Returns true if successfuly, otherwise returns false */ bool calculateCurrent(); /** * Returns true if the current flowing through the connector is known */ bool currentIsKnown() const { return m_bCurrentIsKnown; } /** * Set whether the actual current flowing into this node is known (in some * cases - such as this node being ground - it is not known, and so the * value returned by current() cannot be relied on. */ void setCurrentKnown( bool known ); /** * Returns the current flowing through the connector. * This only applies for electronic connectors */ double current() const { return m_current; } /** * Returns the voltage at the connector. This is an average of the * voltages at either end. */ double voltage() const; Pin *startPin() const { return m_pStartPin; } Pin *endPin() const { return m_pEndPin; } // protected: private: double m_current; bool m_bCurrentIsKnown; QPointer m_pStartPin; QPointer m_pEndPin; }; #endif diff --git a/src/eventinfo.cpp b/src/eventinfo.cpp index c0eadb7c..1080199f 100644 --- a/src/eventinfo.cpp +++ b/src/eventinfo.cpp @@ -1,142 +1,142 @@ /*************************************************************************** * Copyright (C) 2005 by David Saxton * * david@bluehaze.org * * * * 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 "eventinfo.h" #include "itemdocument.h" #include "itemview.h" -#include +#include EventInfo::EventInfo() { reset(); } EventInfo::EventInfo( ItemView *itemView, QEvent *e ) { Q_UNUSED(itemView); Q_UNUSED(e); reset(); } void EventInfo::extractPos( ItemView * itemView, const QPoint & contentsMouseClick ) { pos = itemView->mousePosToCanvasPos( contentsMouseClick ); } EventInfo::EventInfo( ItemView *itemView, QMouseEvent *e ) { reset(); extractPos( itemView, e->pos() ); globalPos = e->globalPos(); isRightClick = e->button() == Qt::RightButton; isMiddleClick = e->button() == Qt::MidButton; ctrlPressed = e->modifiers() & Qt::ControlModifier; // QMouseEvent::ControlButton; shiftPressed = e->modifiers() & Qt::ShiftModifier; // QMouseEvent::ShiftButton; altPressed = e->modifiers() & Qt::AltModifier; //QMouseEvent::AltButton; if ( ItemDocument * id = dynamic_cast(itemView->document()) ) qcanvasItemClickedOn = id->itemAtTop(pos); scrollDelta = 0; scrollOrientation = Qt::Vertical; } EventInfo::EventInfo( ItemView *itemView, QWheelEvent *e ) { reset(); extractPos( itemView, e->pos() ); globalPos = e->globalPos(); ctrlPressed = e->modifiers() & Qt::ControlModifier; // QMouseEvent::ControlButton; shiftPressed = e->modifiers() & Qt::ShiftModifier; // QMouseEvent::ShiftButton; altPressed = e->modifiers() & Qt::AltModifier; // QMouseEvent::AltButton; if ( ItemDocument * id = dynamic_cast(itemView->document()) ) qcanvasItemClickedOn = id->itemAtTop(pos); scrollDelta = e->delta(); scrollOrientation = e->orientation(); } void EventInfo::reset() { isRightClick = false; isMiddleClick = false; ctrlPressed = false; shiftPressed = false; altPressed = false; qcanvasItemClickedOn = nullptr; scrollDelta = 0; scrollOrientation = Qt::Vertical; } QMouseEvent *EventInfo::mousePressEvent( int dx, int dy ) const { return new QMouseEvent( QEvent::MouseButtonPress, pos + QPoint( dx, dy ), (isRightClick ? Qt::RightButton : Qt::LeftButton), (isRightClick ? Qt::RightButton : Qt::LeftButton), (ctrlPressed ? Qt::ControlModifier : Qt::NoModifier ) | (shiftPressed ? Qt::ShiftModifier : Qt::NoModifier ) | (altPressed ? Qt::AltModifier : Qt::NoModifier ) ); } QMouseEvent *EventInfo::mouseReleaseEvent( int dx, int dy ) const { return new QMouseEvent( QEvent::MouseButtonRelease, pos + QPoint( dx, dy ), (isRightClick ? Qt::RightButton : Qt::LeftButton), Qt::NoButton /*(isRightClick ? Qt::RightButton : Qt::LeftButton) * - 2017.09.18 - this is the state after the mouse has been released */, (ctrlPressed ? Qt::ControlModifier : Qt::NoModifier ) | (shiftPressed ? Qt::ShiftModifier : Qt::NoModifier ) | (altPressed ? Qt::AltModifier : Qt::NoModifier ) ); } QMouseEvent *EventInfo::mouseDoubleClickEvent( int dx, int dy ) const { return new QMouseEvent( QEvent::MouseButtonDblClick, pos + QPoint( dx, dy ), (isRightClick ? Qt::RightButton : Qt::LeftButton), (isRightClick ? Qt::RightButton : Qt::LeftButton), (ctrlPressed ? Qt::ControlModifier : Qt::NoModifier ) | (shiftPressed ? Qt::ShiftModifier : Qt::NoModifier ) | (altPressed ? Qt::AltModifier : Qt::NoModifier ) ); } QMouseEvent *EventInfo::mouseMoveEvent( int dx, int dy ) const { return new QMouseEvent( QEvent::MouseMove, pos + QPoint( dx, dy ), Qt::NoButton, Qt::NoButton, (ctrlPressed ? Qt::ControlModifier : Qt::NoModifier ) | (shiftPressed ? Qt::ShiftModifier : Qt::NoModifier ) | (altPressed ? Qt::AltModifier : Qt::NoModifier ) ); } QWheelEvent *EventInfo::wheelEvent( int dx, int dy ) const { return new QWheelEvent( pos + QPoint( dx, dy ), scrollDelta, Qt::NoButton, (ctrlPressed ? Qt::ControlModifier : Qt::NoModifier ) | (shiftPressed ? Qt::ShiftModifier : Qt::NoModifier ) | (altPressed ? Qt::AltModifier : Qt::NoModifier ), scrollOrientation ); } diff --git a/src/eventinfo.h b/src/eventinfo.h index e467e67c..beba760d 100644 --- a/src/eventinfo.h +++ b/src/eventinfo.h @@ -1,59 +1,59 @@ /*************************************************************************** * Copyright (C) 2005 by David Saxton * * david@bluehaze.org * * * * 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. * ***************************************************************************/ #ifndef EVENTINFO_H #define EVENTINFO_H -#include +#include class ItemView; class KtlQCanvasItem; class QEvent; class QMouseEvent; class QWheelEvent; /** Contains information from for a mouse event that occurred on a canvas. Like a QMouseEvent / QEvent / QWheelEvent, but abstracted to the canvas coordinate system, as well as holding lots of useful information. @author David Saxton */ class EventInfo { public: EventInfo(); EventInfo( ItemView *itemView, QMouseEvent *e ); EventInfo( ItemView *itemView, QWheelEvent *e ); EventInfo( ItemView *itemView, QEvent *e ); QMouseEvent *mousePressEvent( int dx, int dy ) const; QMouseEvent *mouseReleaseEvent( int dx, int dy ) const; QMouseEvent *mouseDoubleClickEvent( int dx, int dy ) const; QMouseEvent *mouseMoveEvent( int dx, int dy ) const; QWheelEvent *wheelEvent( int dx, int dy ) const; QPoint pos; QPoint globalPos; KtlQCanvasItem * qcanvasItemClickedOn; short scrollDelta; Qt::Orientation scrollOrientation; bool isRightClick; bool isMiddleClick; bool ctrlPressed; bool shiftPressed; bool altPressed; protected: void extractPos( ItemView * itemView, const QPoint & contentsMouseClick ); void reset(); }; #endif diff --git a/src/flowcodedocument.cpp b/src/flowcodedocument.cpp index b4d2fb75..c48e4f3e 100644 --- a/src/flowcodedocument.cpp +++ b/src/flowcodedocument.cpp @@ -1,294 +1,295 @@ /*************************************************************************** * Copyright (C) 2003-2005 by David Saxton * * david@bluehaze.org * * * * 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 "canvasmanipulator.h" #include "documentiface.h" #include "drawpart.h" #include "flowcode.h" #include "flowcodedocument.h" #include "flowcodeview.h" #include "flowpart.h" #include "itemdocumentdata.h" #include "languagemanager.h" #include "ktechlab.h" #include "microinfo.h" #include "microlibrary.h" #include "outputmethoddlg.h" #include "picitem.h" #include "programmerdlg.h" #include "textdocument.h" -#include #include +#include + FlowCodeDocument::FlowCodeDocument( const QString &caption, const char *name ) : FlowICNDocument( caption, name ) { m_pDocumentIface = new FlowCodeDocumentIface(this); m_type = Document::dt_flowcode; m_microInfo = nullptr; m_microSettings = nullptr; m_picItem = nullptr; m_pLastTextOutputTarget = nullptr; m_cmManager->addManipulatorInfo( CMSelect::manipulatorInfo() ); m_cmManager->addManipulatorInfo( CMDraw::manipulatorInfo() ); m_cmManager->addManipulatorInfo( CMRightClick::manipulatorInfo() ); m_cmManager->addManipulatorInfo( CMRepeatedItemAdd::manipulatorInfo() ); m_cmManager->addManipulatorInfo( CMItemResize::manipulatorInfo() ); m_cmManager->addManipulatorInfo( CMItemDrag::manipulatorInfo() ); m_fileExtensionInfo = QString("*.flowcode|FlowCode (*.flowcode)\n*|%1").arg( i18n("All Files") ); requestStateSave(); } FlowCodeDocument::~FlowCodeDocument() { m_bDeleted = true; if (m_picItem) m_picItem->removeItem(); delete m_microSettings; delete m_pDocumentIface; } View *FlowCodeDocument::createView( ViewContainer *viewContainer, uint viewAreaId, const char *name ) { View* view = new FlowCodeView( this, viewContainer, viewAreaId, name ); handleNewView(view); return view; } void FlowCodeDocument::setPicType( const QString &id ) { if ( m_microSettings && m_microSettings->microInfo() && m_microSettings->microInfo()->id() == id ) return; MicroInfo *microInfo = MicroLibrary::self()->microInfoWithID(id); if ( !microInfo ) { qWarning() << "FlowCodeDocument::setPicType: Could not set the pic type to PIC \""<show(); emit picTypeChanged(); } bool FlowCodeDocument::isValidItem( const QString &itemId ) { return itemId.startsWith("flow/") || itemId.startsWith("dp/"); } bool FlowCodeDocument::isValidItem( Item *item ) { if ( !dynamic_cast(item) && !dynamic_cast(item) ) return false; if ( !item->id().startsWith("START") && !item->id().startsWith("PPEND") ) return true; const ItemMap::iterator ciEnd = m_itemList.end(); if ( item->id().startsWith("START") ) { int count = 0; for ( ItemMap::iterator it = m_itemList.begin(); it != ciEnd; ++it ) { if ( (*it)->id().startsWith("START") ) count++; } if ( count > 1 ) return false; } else if ( item->id().startsWith("PPEND") ) { int count = 0; for ( ItemMap::iterator it = m_itemList.begin(); it != ciEnd; ++it ) { if ( (*it)->id().startsWith("PPEND") ) count++; } if ( count > 1 ) return false; } return true; } void FlowCodeDocument::setLastTextOutputTarget( TextDocument * target ) { m_pLastTextOutputTarget = target; } void FlowCodeDocument::slotConvertTo( QAction *action ) { int target = action->data().toInt(); switch ( (ConvertToTarget)target ) { case FlowCodeDocument::MicrobeOutput: convertToMicrobe(); break; case FlowCodeDocument::AssemblyOutput: convertToAssembly(); break; case FlowCodeDocument::HexOutput: convertToHex(); break; case FlowCodeDocument::PICOutput: convertToPIC(); break; } } void FlowCodeDocument::convertToMicrobe() { OutputMethodDlg *dlg = new OutputMethodDlg( i18n("Microbe Code Output"), url(), false, activeView() ); dlg->setOutputExtension(".microbe"); dlg->setFilter( QString("*.microbe|Microbe (*.microbe)\n*|%1").arg(i18n("All Files")) ); const int accepted = dlg->exec(); if (accepted != QDialog::Accepted) { delete dlg; return; } ProcessOptions o( dlg->info() ); o.setTextOutputTarget( m_pLastTextOutputTarget, this, SLOT(setLastTextOutputTarget( TextDocument* )) ); o.p_flowCodeDocument = this; o.setProcessPath( ProcessOptions::ProcessPath::FlowCode_Microbe ); LanguageManager::self()->compile(o); delete dlg; } void FlowCodeDocument::convertToAssembly() { OutputMethodDlg *dlg = new OutputMethodDlg( i18n("Assembly Code Output"), url(), false, activeView() ); dlg->setOutputExtension(".asm"); dlg->setFilter( QString("*.asm *.src *.inc|%1 (*.asm, *.src, *.inc)\n*|%2").arg(i18n("Assembly Code")).arg(i18n("All Files")) ); const int accepted = dlg->exec(); if (accepted != QDialog::Accepted) { delete dlg; return; } ProcessOptions o( dlg->info() ); o.setTextOutputTarget( m_pLastTextOutputTarget, this, SLOT(setLastTextOutputTarget( TextDocument* )) ); o.p_flowCodeDocument = this; o.setProcessPath( ProcessOptions::ProcessPath::FlowCode_AssemblyAbsolute ); LanguageManager::self()->compile(o); delete dlg; } void FlowCodeDocument::convertToHex() { OutputMethodDlg *dlg = new OutputMethodDlg( i18n("Hex Code Output"), url(), false, KTechlab::self() ); dlg->setOutputExtension(".hex"); dlg->setFilter( QString("*.hex|Hex (*.hex)\n*|%1").arg(i18n("All Files")) ); const int accepted = dlg->exec(); if (accepted != QDialog::Accepted) { delete dlg; return; } ProcessOptions o( dlg->info() ); o.setTextOutputTarget( m_pLastTextOutputTarget, this, SLOT(setLastTextOutputTarget( TextDocument* )) ); o.p_flowCodeDocument = this; o.setProcessPath( ProcessOptions::ProcessPath::FlowCode_Program ); LanguageManager::self()->compile(o); delete dlg; } void FlowCodeDocument::convertToPIC() { ProgrammerDlg * dlg = new ProgrammerDlg( microSettings()->microInfo()->id(), (QWidget*)KTechlab::self(), "Programmer Dlg" ); const int accepted = dlg->exec(); if (accepted != QDialog::Accepted) { dlg->deleteLater(); return; } ProcessOptions o; dlg->initOptions( & o ); o.p_flowCodeDocument = this; o.setProcessPath( ProcessOptions::ProcessPath::FlowCode_PIC ); LanguageManager::self()->compile( o ); dlg->deleteLater(); } void FlowCodeDocument::varNameChanged( const QString &newValue, const QString &oldValue ) { if (m_bDeleted) return; // Decrease the old variable count // If none are left after, remove it from microsettings StringIntMap::iterator it = m_varNames.find(oldValue); if ( it != m_varNames.end() ) { --(it.value()); if ( it.value() <= 0 ) { VariableInfo *info = microSettings()->variableInfo(it.key()); if ( info && !info->permanent ) microSettings()->deleteVariable(oldValue); m_varNames.erase(it); } } // Add the new variable to a count, tell microsettings about it if it is new if ( !newValue.isEmpty() ) { it = m_varNames.find(newValue); if ( it != m_varNames.end() ) { ++it.value(); } else { m_varNames[newValue] = 1; microSettings()->setVariable( newValue, QVariant(), false ); } } // Tell all FlowParts to update their variable lists const ItemMap::iterator end = m_itemList.end(); for ( ItemMap::iterator it = m_itemList.begin(); it != end; ++it ) { if ( FlowPart *flowPart = dynamic_cast(*it) ) flowPart->updateVarNames(); } } diff --git a/src/flowcodedocument.h b/src/flowcodedocument.h index 367eb06f..0aba96ee 100644 --- a/src/flowcodedocument.h +++ b/src/flowcodedocument.h @@ -1,102 +1,102 @@ /*************************************************************************** * Copyright (C) 2003-2005 by David Saxton * * david@bluehaze.org * * * * 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. * ***************************************************************************/ #ifndef FLOWCODEDOCUMENT_H #define FLOWCODEDOCUMENT_H #include "flowicndocument.h" -#include +#include class KTechlab; class FlowCode; class MicroInfo; class PicItem; class FlowPart; class MicroSettings; class TextDocument; class QString; typedef QList FlowPartList; typedef QMap StringIntMap; /** @short View for editing FlowCode @author David Saxton */ class FlowCodeDocument : public FlowICNDocument { Q_OBJECT public: FlowCodeDocument( const QString &caption, const char *name = nullptr); ~FlowCodeDocument() override; View *createView( ViewContainer *viewContainer, uint viewAreaId, const char *name = nullptr ) override; /** * Returns a pointer used for the MicroSettings in this FlowCode document */ MicroSettings *microSettings() const { return m_microSettings; } /** * Sets the type of PIC to be used. FlowCodeDocument se virtual void convertToMicrobe();ts the internal MicroInfo pointer to that * returned by MicroLibrary for the given id. The pic type must be set before anything useful * (such as compilage) can be done. */ void setPicType( const QString &id ); enum ConvertToTarget { MicrobeOutput, AssemblyOutput, HexOutput, PICOutput }; // TODO clean up this preprocessor jewel :P #define protected public signals: void picTypeChanged(); #undef protected signals: void pinMappingsChanged(); public slots: /** * @param target as ConvertToTarget */ void slotConvertTo( QAction* action ); void convertToMicrobe() override; void convertToAssembly() override; void convertToHex() override; void convertToPIC() override; /** * Called when a variable name has changed (from an entry box) */ void varNameChanged( const QString &newValue, const QString &oldValue ); protected: bool isValidItem( Item *item ) override; bool isValidItem( const QString &itemId ) override; private slots: void setLastTextOutputTarget( TextDocument * target ); private: QPointer m_pLastTextOutputTarget; MicroInfo *m_microInfo; // Stores information about the PIC MicroSettings *m_microSettings; // Stores initial settings of the PIC PicItem *m_picItem; // Allows the user to change the PIC settings StringIntMap m_varNames; }; #endif diff --git a/src/flowcodeview.cpp b/src/flowcodeview.cpp index c4a3919c..12163da2 100644 --- a/src/flowcodeview.cpp +++ b/src/flowcodeview.cpp @@ -1,91 +1,92 @@ /*************************************************************************** * Copyright (C) 2005 by David Saxton * * david@bluehaze.org * * * * 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 "flowcodedocument.h" #include "flowcodeview.h" #include "ktechlab.h" #include "viewiface.h" #include #include #include #include #include -#include +#include +#include FlowCodeView::FlowCodeView( FlowCodeDocument * flowCodeDocument, ViewContainer *viewContainer, uint viewAreaId, const char *name ) : ICNView( flowCodeDocument, viewContainer, viewAreaId, name ) { KActionCollection * ac = actionCollection(); //BEGIN Convert To * Actions //KToolBarPopupAction * pa = new KToolBarPopupAction( i18n("Convert to..."), "fork", 0, 0, 0, ac, "program_convert" ); KToolBarPopupAction * pa = new KToolBarPopupAction( QIcon::fromTheme("fork"), i18n("Convert to..."), ac); pa->setObjectName( "program_convert" ); pa->setDelayed(false); QMenu * m = pa->menu(); m->setTitle( i18n("Convert To") ); m->addAction( QIcon::fromTheme( "convert_to_microbe" ), i18n("Microbe"))->setData( FlowCodeDocument::MicrobeOutput ); m->addAction( QIcon::fromTheme( "convert_to_assembly" ), i18n("Assembly"))->setData( FlowCodeDocument::AssemblyOutput ); m->addAction( QIcon::fromTheme( "convert_to_hex" ), i18n("Hex"))->setData( FlowCodeDocument::HexOutput ); m->addAction( QIcon::fromTheme( "convert_to_pic" ), i18n("PIC (upload)"))->setData( FlowCodeDocument::PICOutput ); connect( m, SIGNAL(triggered(QAction*)), flowCodeDocument, SLOT(slotConvertTo(QAction*)) ); ac->addAction("program_convert" , pa); //END Convert To * Actions // new KAction( i18n("Convert to Microbe"), "convert_to_microbe", Qt::Key_F7, flowCodeDocument, SLOT(convertToMicrobe()), ac, "tools_to_microbe" ); // new KAction( i18n("Convert to Assembly"), "convert_to_assembly", Qt::Key_F8, flowCodeDocument, SLOT(convertToAssembly()), ac, "tools_to_assembly" ); // new KAction( i18n("Convert to Hex"), "convert_to_hex", Qt::Key_F9, flowCodeDocument, SLOT(convertToHex()), ac, "tools_to_hex" ); // new KAction( i18n("Upload PIC Program"), "convert_to_pic", 0, flowCodeDocument, SLOT(convertToPIC()), ac, "tools_to_pic" ); setXMLFile( "ktechlabflowcodeui.rc", true ); setWhatsThis( i18n( "Construct a FlowCode document by dragging FlowParts from the list on the left. All FlowCharts require an initial \"Start\" part, of which there can only be one.

" "Some FlowParts, such as Subroutines, act as a container element for other FlowParts. Drag the items in or out of a container as appropriate. The container that will become the parent of the part being dragged is indicated by being selected.

" "Note that connections cannot be made between FlowParts in different containers, or at different levels." ) ); m_pViewIface = new FlowCodeViewIface(this); } FlowCodeView::~FlowCodeView() { delete m_pViewIface; } void FlowCodeView::dragEnterEvent( QDragEnterEvent * e ) { ICNView::dragEnterEvent(e); if ( e->isAccepted() ) return; //bool acceptable = e->provides("ktechlab/flowpart"); bool acceptable = e->mimeData()->hasFormat("ktechlab/flowpart"); if ( !acceptable ) return; e->setAccepted( true ); createDragItem( e ); } diff --git a/src/flowcontainer.cpp b/src/flowcontainer.cpp index 7841c72a..5c990d10 100644 --- a/src/flowcontainer.cpp +++ b/src/flowcontainer.cpp @@ -1,405 +1,405 @@ /*************************************************************************** * Copyright (C) 2003-2005 by David Saxton * * david@bluehaze.org * * * * 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 "canvasitemparts.h" #include "cells.h" #include "icndocument.h" #include "flowcontainer.h" #include "fpnode.h" #include "nodegroup.h" #include "resizeoverlay.h" -#include -#include +#include +#include #include const int topStrip = 24; const int botStrip = 16; FlowContainer::FlowContainer( ICNDocument *_icnDocument, bool newItem, const QString &id ) : FlowPart( _icnDocument, newItem, id ) { m_ext_in = m_int_in = m_int_out = m_ext_out = nullptr; b_expanded = true; addButton( "expandBtn", QRect( offsetX(), offsetY()+24 - 11, 22, 22 ), QIcon::fromTheme( "go-down" ), true ); m_rectangularOverlay = new RectangularOverlay( this, 8, 8 ); setSize( -160, -120, 320, 240 ); m_int_in = (FPNode*)createNode( width()/2, 8+topStrip, 90, "int_in", Node::fp_out ); m_int_out = (FPNode*)createNode( width()/2, height()-8-botStrip, 270, "int_out", Node::fp_in ); button("expandBtn")->setState(true); updateAttachedPositioning(); updateNodeLevels(); } FlowContainer::~FlowContainer() { } void FlowContainer::updateNodeLevels() { FlowPart::updateNodeLevels(); int l = level(); if (m_ext_in) m_ext_in->setLevel(l); if (m_ext_out) m_ext_out->setLevel(l); if (m_int_in) m_int_in->setLevel(l+1); if (m_int_out) m_int_out->setLevel(l+1); } void FlowContainer::filterEndPartIDs( QStringList *ids ) { // Remove *all* nodes except for very bottom one if (m_int_out) { ids->removeAll(m_int_out->childId()); } if (m_ext_in) { ids->removeAll(m_ext_in->childId()); } if (m_int_in) { ids->removeAll(m_int_in->childId()); } } void FlowContainer::createTopContainerNode() { m_ext_in = (FPNode*)createNode( width()/2, -8, 270, "ext_in", Node::fp_in ); m_ext_in->setLevel( level() ); m_rectangularOverlay->removeTopMiddle(); updateAttachedPositioning(); } void FlowContainer::createBotContainerNode() { m_ext_out = (FPNode*)createNode( width()/2, height()+8, 90, "ext_out", Node::fp_out ); m_ext_out->setLevel( level() ); m_rectangularOverlay->removeBotMiddle(); updateAttachedPositioning(); } QSize FlowContainer::minimumSize() const { return QSize( 160, 64 ); } void FlowContainer::drawShape( QPainter &p ) { if (b_deleted) return; if ( !m_sizeRect.isValid() ) return; const int _x = (int)x()+offsetX(); const int _y = (int)y()+offsetY(); int col = 0xef + level()*0x6; if ( col > 0xff ) col = 0xff; p.setBrush( QColor( col, 0xff, col ) ); if( b_expanded ) { p.setPen(Qt::DotLine); p.drawRoundRect( _x, _y, width(), topStrip, 1500/width(), 1500/topStrip ); p.drawRoundRect( _x, _y+height()-botStrip, width(), botStrip, 1500/width(), 1500/botStrip ); } else { p.setPen(QPen((isSelected()?m_selectedCol:Qt::black),1,Qt::SolidLine)); p.drawRoundRect( _x, _y, width(), topStrip, 1500/width(), 1500/topStrip ); } p.setPen( Qt::black ); p.setFont( font() ); p.drawText( QRect( 22 + _x+8, _y, width()-8, topStrip ), Qt::AlignLeft | Qt::AlignVCenter, m_caption ); if( b_expanded ) { p.setPen(Qt::SolidLine); p.setBrush( Qt::NoBrush ); p.drawRoundRect( _x, _y, width(), height(), 1500/width(), 1500/height() ); } } void FlowContainer::childAdded( Item *child ) { if (!child) return; FlowPart::childAdded(child); connect( this, SIGNAL(movedBy(double, double )), child, SLOT(moveBy(double, double )) ); child->setZ( ICNDocument::Z::Item + child->level() ); updateContainedVisibility(); } void FlowContainer::childRemoved( Item *child ) { FlowPart::childRemoved(child); if (!b_expanded) child->setVisible(true); disconnect( this, SIGNAL(movedBy(double, double )), child, SLOT(moveBy(double, double )) ); } void FlowContainer::updateConnectorPoints( bool add ) { if ( b_deleted || !isVisible() ) add = false; if ( b_pointsAdded == add ) return; b_pointsAdded = add; Cells *cells = p_icnDocument->cells(); if (!cells) return; int _x = (int)x()+offsetX(); int _y = (int)y()+offsetY(); int w = width(); int h = b_expanded ? height() : topStrip; const int mult = add ? 1 : -1; // Top strip for ( int y=_y; y <_y+24; ++y ) { for ( int x = _x; x<=_x+w; x += 8 ) { if ( cells->haveCellContaing( x, y ) ) { cells->cellContaining( x, y ).CIpenalty += mult*ICNDocument::hs_item; } } } // Bottom strip for ( int y = _y+h-16; y <= _y+h; ++y ) { for ( int x = _x; x<=_x+width(); x += 8 ) { if ( cells->haveCellContaing( x, y ) ) { cells->cellContaining( x, y ).CIpenalty += mult*ICNDocument::hs_item; } } } // Left strip int x = _x; for ( int y = _y+24; y<_y+h-16; y += 8 ) { if ( cells->haveCellContaing( x, y ) ) { cells->cellContaining( x, y ).CIpenalty += mult*ICNDocument::hs_item; } } // Right strip x = _x+width(); for ( int y = _y+24; y<_y+h-16; y += 8 ) { if ( cells->haveCellContaing( x, y ) ) { cells->cellContaining( x, y ).CIpenalty += mult*ICNDocument::hs_item; } } } void FlowContainer::setFullBounds( bool full ) { if ( full || !b_expanded ) { QRect bounds = b_expanded ? m_sizeRect : QRect( m_sizeRect.x(), m_sizeRect.y(), m_sizeRect.width(), topStrip ); setPoints( QPolygon(bounds) ); return; } // qDebug() << Q_FUNC_INFO << "width="<setGuiPartSize( 22, 22 ); button("expandBtn")->move( int(x())+offsetX()+7, int(y())+offsetY()+1 ); } void FlowContainer::updateContainedVisibility() { if (b_deleted) return; if (m_ext_in) m_ext_in->setVisible( isVisible() ); if (m_int_in) m_int_in->setVisible( isVisible() && b_expanded ); if (m_int_out) m_int_out->setVisible( isVisible() && b_expanded ); if (m_ext_out) m_ext_out->setVisible( isVisible() ); const ItemList::iterator cEnd = m_children.end(); for ( ItemList::iterator it = m_children.begin(); it != cEnd; ++it ) { if (*it) (*it)->setVisible( isVisible() && b_expanded ); } m_rectangularOverlay->setVisible( isVisible() && b_expanded ); NodeGroupList hidableNodeGroups; p_icnDocument->getTranslatable( children(true) += GuardedItem(this), nullptr, nullptr, &hidableNodeGroups ); NodeGroupList::iterator hngEnd = hidableNodeGroups.end(); for ( NodeGroupList::iterator it = hidableNodeGroups.begin(); it != hngEnd; ++it ) (*it)->setVisible(b_expanded); } void FlowContainer::setVisible( bool yes ) { if (b_deleted) { FlowPart::setVisible(false); return; } FlowPart::setVisible(yes); updateContainedVisibility(); } diff --git a/src/flowparts/flowconnectorlist.cpp b/src/flowparts/flowconnectorlist.cpp index 3905c550..921991f3 100644 --- a/src/flowparts/flowconnectorlist.cpp +++ b/src/flowparts/flowconnectorlist.cpp @@ -1,119 +1,119 @@ // // C++ Implementation: flowconnectorlist // // Description: // // // Author: David Saxton, Alan Grimes, Zoltan Padrah , (C) 2009 // // Copyright: See COPYING file that comes with this distribution // // #include "flowconnectorlist.h" #include "flowconnector.h" -#include -#include +#include +#include FlowConnectorList::FlowConnectorList ( const QList & l ) : flowList(l) { // O(n) FlowConnectorList::iterator it, end = flowList.end(); for( it = flowList.begin(); it != end; it++) list.append( CAST_POINTER *it); } // FlowConnectorList::FlowConnectorList ( const std::list & l ) : flowList(l) { // FlowConnectorList::iterator it, end = flowList.end(); // for( it = flowList.begin(); it != end; it++) // list.append( CAST_POINTER *it); // } QList & FlowConnectorList::operator= ( const QList & l ) { // -> O(n) flowList = l; list.clear(); FlowConnectorList::iterator it, end = flowList.end(); for( it = flowList.begin(); it != end; it++) list.append( CAST_POINTER *it); return flowList; } // QList & FlowConnectorList::operator= ( const std::list & l ) { // O(n) // flowList = l; // list.clear(); // FlowConnectorList::iterator it, end = flowList.end(); // for( it = flowList.begin(); it != end; it++) // list.append( CAST_POINTER *it); // // return flowList; // } // bool FlowConnectorList::operator== ( const std::list & l ) const { // return flowList == l; // } FlowConnectorList::iterator FlowConnectorList::insert ( FlowConnectorList::iterator it, const FlowConnectorList::T & x ) { // O(n) list.insert( convertIterator( it ), CAST_POINTER x); return flowList.insert(it,x); } uint FlowConnectorList::remove ( const FlowConnectorList::T & x ) { list.removeAll( CAST_POINTER x); return flowList.removeAll(x); } QList & FlowConnectorList::operator<< ( const FlowConnectorList::T & x ) { list << CAST_POINTER x; return flowList << x; } void FlowConnectorList::push_front ( const FlowConnectorList::T & x ) { list.push_front(CAST_POINTER x); flowList.push_front(x); } void FlowConnectorList::push_back ( const FlowConnectorList::T & x ) { list.push_back(CAST_POINTER x); flowList.push_back(x); } // void FlowConnectorList::insert ( FlowConnectorList::iterator pos, size_type n, const FlowConnectorList::T & x ) { // O(n) // list.insert( convertIterator(pos) ,n, CAST_POINTER x); // flowList.insert(pos,n,x); // } QList & FlowConnectorList::operator+= ( const QList & l ) { // O(n) const_iterator end = l.end(); for(const_iterator it = l.begin(); it != end; it++) list.append( CAST_POINTER *it ); return flowList += l; } // FlowConnectorList::iterator FlowConnectorList::fromLast () { // return flowList.fromLast(); // } FlowConnectorList::iterator FlowConnectorList::append ( const FlowConnectorList::T & x ){ list.append(CAST_POINTER x); // return flowList.append(x); flowList.append(x); iterator ret = flowList.end(); --ret; return ret; } FlowConnectorList::iterator FlowConnectorList::prepend ( const FlowConnectorList::T & x ){ list.prepend(CAST_POINTER x); //return flowList.prepend(x); flowList.prepend(x); return flowList.begin(); } QList & FlowConnectorList::operator+= ( const FlowConnectorList::T & x ) { list += CAST_POINTER x; return flowList += x; } // FlowConnectorList::const_iterator FlowConnectorList::fromLast () const { // return flowList.fromLast(); // } diff --git a/src/flowparts/flowconnectorlist.h b/src/flowparts/flowconnectorlist.h index 642b5246..a55b3c2a 100644 --- a/src/flowparts/flowconnectorlist.h +++ b/src/flowparts/flowconnectorlist.h @@ -1,361 +1,361 @@ // // C++ Interface: flowconnectorlist // // Description: // // // Author: David Saxton, Alan Grimes, Zoltan Padrah , (C) 2009 // // Copyright: See COPYING file that comes with this distribution // // #ifndef FLOWCONNECTORLIST_H #define FLOWCONNECTORLIST_H #include "flowconnector.h" -#include +#include class Connector; class FlowConnector; // these typedef's shoud go in a separate header one day typedef QList< QPointer > ConnectorList; /** * @short a list of connector between FlowNodes * @author David Saxton, Alan Grimes, Zoltan Padrah * * This class implements a list of FlowConnector objects; this class fulfills two * requirements: * 1. it provides type safety for classes related to flowparts * 2. can be cast to a generic ConnectorList, to be used in other contexts * * For QList interface see http://doc.trolltech.com/3.3/qlist.html */ /* Notations used in the source code: O(n) : the method has this complexitiy assert ? : in that method some assertions could be made, considering that the two list should have the same contents */ class FlowConnectorList { public : // cast operator, for casting it to ConnectorList operator ConnectorList() { return list; } // QList's interface typedef QPointer T; #define CAST_POINTER (QPointer)(Connector *)(FlowConnector *) typedef QList::iterator iterator; typedef QList::const_iterator const_iterator; typedef T value_type; typedef value_type * pointer; typedef const value_type * const_pointer; typedef value_type & reference; typedef const value_type & const_reference; typedef size_t size_type; FlowConnectorList () : list(), flowList() { } FlowConnectorList ( const QList & l ) ; /* : flowList(l) { // O(n) FlowConnectorList::iterator it, end = flowList.end(); for( it = flowList.begin(); it != end; it++) list.append( CAST_POINTER *it); } */ /* FlowConnectorList ( const std::list & l ) ; */ /* : flowList(l) { FlowConnectorList::iterator it, end = flowList.end(); for( it = flowList.begin(); it != end; it++) list.append( CAST_POINTER *it); } */ ~FlowConnectorList () { } // leak check ? QList & operator= ( const QList & l ) ; /* { // -> O(n) flowList = l; list.clear(); FlowConnectorList::iterator it, end = flowList.end(); for( it = flowList.begin(); it != end; it++) list.append( CAST_POINTER *it); return flowList; } */ /* QList & operator= ( const std::list & l ) ; */ /* { // O(n) flowList = l; list.clear(); FlowConnectorList::iterator it, end = flowList.end(); for( it = flowList.begin(); it != end; it++) list.append( CAST_POINTER *it); return flowList; } */ /* bool operator== ( const std::list & l ) const ; */ /* { return flowList == l; } */ bool operator== ( const QList & l ) const { return flowList == l; } bool operator!= ( const QList & l ) const { return flowList != l; } iterator begin () { return flowList.begin(); } const_iterator begin () const { return flowList.begin(); // ? } const_iterator constBegin () const { return flowList.constBegin(); } iterator end () { return flowList.end(); } const_iterator end () const { return flowList.end(); // ? } const_iterator constEnd () const { return flowList.constEnd(); } iterator insert ( iterator it, const T & x ) ; /* { // O(n) list.insert( convertIterator( it ), CAST_POINTER x); return flowList.insert(it,x); } */ uint remove ( const T & x ) ; /* { list.remove( CAST_POINTER x); return flowList.remove(x); } */ void clear () { flowList.clear(); list.clear(); } QList & operator<< ( const T & x ) ; /* { list << CAST_POINTER x; return flowList << x; } */ size_type size () const { // assert ? return flowList.size(); } bool empty () const { // assert ? return flowList.empty(); } void push_front ( const T & x ) ; /* { list.push_front(CAST_POINTER x); flowList.push_front(x); } */ void push_back ( const T & x ) ; /* { list.push_back(CAST_POINTER x); flowList.push_back(x); } */ iterator erase ( iterator it ){ // O(n) list.erase( convertIterator(it) ); return flowList.erase(it); } iterator erase ( iterator first, iterator last ){ // O(n) list.erase( convertIterator(first), convertIterator(last) ); return flowList.erase(first, last); } reference front () { return flowList.front(); } const_reference front () const { return flowList.front(); } reference back () { return flowList.back(); } const_reference back () const { return flowList.back(); } void pop_front () { flowList.pop_front(); list.pop_front(); } void pop_back () { flowList.pop_back(); list.pop_back(); } /* void insert ( iterator pos, size_type n, const T & x ) ; */ /* { // O(n) list.insert( convertIterator(pos) ,n, CAST_POINTER x); flowList.insert(pos,n,x); } */ QList operator+ ( const QList & l ) const { return flowList + l; } QList & operator+= ( const QList & l ) ; /* { // O(n) const_iterator end = l.end(); for(const_iterator it = l.begin(); it != end; it++) list.append( CAST_POINTER *it ); return flowList += l; } */ /* iterator fromLast () ; */ /* { return flowList.fromLast(); } */ /* const_iterator fromLast () const ; */ /* { return flowList.fromLast(); } */ bool isEmpty () const { return flowList.isEmpty(); } iterator append ( const T & x ) ; /* { list.append(CAST_POINTER x); // return flowList.append(x); flowList.append(x); iterator ret = flowList.end(); --ret; return ret; } */ iterator prepend ( const T & x ) ; /* { list.prepend(CAST_POINTER x); //return flowList.prepend(x); flowList.prepend(x); return flowList.begin(); } */ iterator remove ( iterator it ){ // -> O(n) list.erase( convertIterator( it ) ); return flowList.erase(it); } T & first () { // assert ? return flowList.first(); } const T & first () const { // assert ? return flowList.first(); } T & last () { // assert ? return flowList.last(); } const T & last () const { // assert ? return flowList.last(); } T & operator[] ( size_type i ) { // assert ? return flowList[i]; } const T & operator[] ( size_type i ) const { // assert ? return flowList[i]; } iterator at ( size_type i ) { // assert ? //return flowList.at(i); iterator ret = flowList.begin(); ret += i; return ret; } const_iterator at ( size_type i ) const { // assert ? //return flowList.at(i); const_iterator ret = flowList.constBegin(); ret += i; return ret; } iterator find ( const T & x ) { // assert ? int i = flowList.indexOf(x); return (i == -1 ? flowList.end() : (flowList.begin()+i)); //return flowList.find(x); // 2018.11.30 } const_iterator find ( const T & x ) const { // assert ? int i = flowList.indexOf(x); return (i == -1 ? flowList.end() : (flowList.begin()+i)); //return flowList.find(x); // 2018.11.30 } iterator find ( iterator it, const T & x ) { // assert ? //return flowList.find(it, x); // 2018.11.30 int i = flowList.indexOf(x, it - flowList.begin()); return i == -1 ? flowList.end() : flowList.begin()+i; } const_iterator find ( const_iterator it, const T & x ) const { // assert ? //return flowList.find(it, x); // 2018.11.30 int i = flowList.indexOf(x, it - flowList.begin()); return i == -1 ? flowList.end() : flowList.begin()+i; } int indexOf ( const T & x ) const { // assert ? return flowList.indexOf(x); } size_type contains ( const T & x ) const { //return flowList.contains(x); return flowList.count(x); } size_type count () const { // assert ? return flowList.count(); } QList & operator+= ( const T & x ) ; /* { list += CAST_POINTER x; return flowList += x; } */ private: ConnectorList list; QList< T > flowList; /** * Converts an iterator from FlowConnector list to Connector list. Complexity: O(n) ! * @param orig original iterator from FlowConnector list * @return iterator converted to Connector list */ ConnectorList::iterator convertIterator(QList< T >::iterator orig){ ConnectorList::iterator it2 = list.begin(); for(QList< T >::iterator it = flowList.begin(); it != orig; it++) it2++; return it2; } }; #endif diff --git a/src/flowparts/flowicndocument.cpp b/src/flowparts/flowicndocument.cpp index 3edd45a5..5d8ebe35 100644 --- a/src/flowparts/flowicndocument.cpp +++ b/src/flowparts/flowicndocument.cpp @@ -1,517 +1,517 @@ // // C++ Implementation: flowicndocument // // Description: // // // Author: Zoltan P , (C) 2008 // // Copyright: See COPYING file that comes with this distribution // // #include "flowicndocument.h" #include "connector.h" #include "conrouter.h" #include "cnitemgroup.h" #include "fpnode.h" #include "flowcontainer.h" #include "item.h" #include "junctionflownode.h" #include "nodegroup.h" -#include +#include FlowICNDocument::FlowICNDocument( const QString &caption, const char *name) : ICNDocument(caption, name ) { } FlowICNDocument::~FlowICNDocument() { // Go to hell, KtlQCanvas. I'm in charge of what gets deleted. KtlQCanvasItemList all = m_canvas->allItems(); const KtlQCanvasItemList::Iterator end = all.end(); for ( KtlQCanvasItemList::Iterator it= all.begin(); it != end; ++it ) (*it)->setCanvas(nullptr); // Remove all items from the canvas selectAll(); deleteSelection(); // Delete anything that got through the above couple of lines ConnectorList connectorsToDelete = m_connectorList; connectorsToDelete.clear(); const ConnectorList::iterator connectorListEnd = connectorsToDelete.end(); for ( ConnectorList::iterator it = connectorsToDelete.begin(); it != connectorListEnd; ++it ) delete *it; deleteAllNodes(); } void FlowICNDocument::deleteAllNodes() { FPNodeMap nodesToDelete = m_flowNodeList; m_flowNodeList.clear(); const FPNodeMap::iterator nodeListEnd = nodesToDelete.end(); for ( FPNodeMap::iterator it = nodesToDelete.begin(); it != nodeListEnd; ++it ) delete *it; } bool FlowICNDocument::canConnect( KtlQCanvasItem *qcanvasItem1, KtlQCanvasItem *qcanvasItem2 ) const { // Rough outline of what can and can't connect: // * At most three connectors to a node // * Can't have connectors going between different levels (e.g. can't have // a connector coming outside a FlowContainer from inside). // * Can't have more than one route between any two nodes // * In all connections between nodes, must have at least one input and one // output node at the ends. FPNode *startNode = dynamic_cast(qcanvasItem1); FPNode *endNode = dynamic_cast(qcanvasItem2); if ( (startNode && startNode->numCon( true, false ) > 2) || (endNode && endNode->numCon( true, false ) > 2) ) return false; Connector *startConnector = dynamic_cast(qcanvasItem1); Connector *endConnector = dynamic_cast(qcanvasItem2); // Can't have I-junction in flowcode document if ( startConnector && endConnector ) return false; //BEGIN Change connectors to nodes FPNode * startNode1 = nullptr; FPNode * startNode2 = nullptr; if (startConnector) { startNode1 = dynamic_cast ( startConnector->startNode() ); startNode2 = dynamic_cast ( startConnector->endNode() ); if ( !startNode1 || !startNode2 ) return false; } else if (!startNode) return false; FPNode * endNode1 = nullptr; FPNode * endNode2 = nullptr; if (endConnector) { endNode1 = dynamic_cast ( endConnector->startNode() ); endNode2 = dynamic_cast ( endConnector->endNode() ); if ( !endNode1 || !endNode2 ) return false; } else if ( !endNode ) return false; //END Change connectors to nodes //BEGIN Check we have appropriate input and output allowance if ( type() == Document::dt_flowcode ) // this is obviouse { if ( startNode1 && startNode2 && endNode1 && endNode2 ) { // Can't have I-configuration return false; } if ( startNode && endNode ) { // Nice and easy straight line to check if ( !startNode->acceptInput() && !endNode->acceptInput() ) return false; if ( !startNode->acceptOutput() && !endNode->acceptOutput() ) return false; } else { // We're in a T-configuration, we can only make this if the base of // the T is an output FPNode * base = startNode ? startNode : endNode; if ( !base->acceptOutput() ) return false; } } else qCritical() << Q_FUNC_INFO << "BUG: document type is not dt_flowcode" << endl; //END Check we have appropriate input and output allowance return ICNDocument::canConnect(qcanvasItem1, qcanvasItem2); } Connector *FlowICNDocument::createConnector( Connector *con1, Connector *con2, const QPoint & /*pos1*/, const QPoint & /*pos2*/, QPointList * /*pointList*/ ) { // FIXME isn't all this dead code? /* if ( !canConnect( con1, con2 ) ) return nullptr; const bool con1UsedManual = con1->usesManualPoints(); const bool con2UsedManual = con2->usesManualPoints(); QList oldCon1Points = con1->splitConnectorPoints(pos1); QList oldCon2Points = con2->splitConnectorPoints(pos2); // FIXME dynamic_cast used because Connector doesn't know about FPNode FPNode *node1a = dynamic_cast ( con1->startNode() ); FPNode *node1b = dynamic_cast ( con1->endNode() ); FPNode *node2a = dynamic_cast ( con2->startNode() ); FPNode *node2b = dynamic_cast ( con2->endNode() ); if ( !node1a || !node1b || !node2a || !node2b ) return nullptr; */ con1->hide(); con2->hide(); // if ( type() != Document::dt_circuit ) return nullptr; } Connector * FlowICNDocument::createConnector( Node *node, Connector *con, const QPoint &pos2, QPointList *pointList ) { if ( !canConnect( node, con ) ) return nullptr; // FIXME dynamic_cast used, fix it in Connector class FPNode *conStartNode = dynamic_cast ( con->startNode() ); FPNode *conEndNode = dynamic_cast ( con->endNode() ); FPNode *fpNode = dynamic_cast ( node ); const bool usedManual = con->usesManualPoints(); FPNode *newNode = new JunctionFlowNode( this, 0, pos2 ); QPointList autoPoints; if (!pointList) { addAllItemConnectorPoints(); ConRouter cr(this); cr.mapRoute( int(node->x()), int(node->y()), pos2.x(), pos2.y() ); autoPoints = cr.pointList(false); pointList = &autoPoints; } QList oldConPoints = con->splitConnectorPoints(pos2); con->hide(); // The actual new connector Connector *new1 = newNode->createInputConnector(node); fpNode->addOutputConnector(new1); new1->setRoutePoints(*pointList,usedManual); // The two connectors formed from the original one when split Connector *new2 = newNode->createInputConnector(conStartNode); conStartNode->addOutputConnector(new2); new2->setRoutePoints( oldConPoints.at(0), usedManual ); Connector *new3 = conEndNode->createInputConnector(newNode); newNode->addOutputConnector(new3); new3->setRoutePoints( oldConPoints.at(1), usedManual ); // Avoid flicker: tell them to update their draw lists now con->updateConnectorPoints(false); new1->updateDrawList(); new2->updateDrawList(); new3->updateDrawList(); // Now it's safe to remove the connector... con->removeConnector(); flushDeleteList(); deleteNodeGroup(conStartNode); deleteNodeGroup(conEndNode); createNodeGroup(newNode)->init(); return new1; } Connector* FlowICNDocument::createConnector( const QString &startNodeId, const QString &endNodeId, QPointList *pointList ) { FPNode *startNode = getFPnodeWithID(startNodeId); FPNode *endNode = getFPnodeWithID(endNodeId); if ( !startNode || !endNode ) { qDebug() << "Either/both the connector start node and end node could not be found" << endl; return nullptr; } if ( !canConnect( startNode, endNode ) ) return nullptr; Connector *connector = endNode->createInputConnector(startNode); if (!connector) { qCritical() << Q_FUNC_INFO << "End node did not create the connector" << endl; return nullptr; } startNode->addOutputConnector(connector); flushDeleteList(); // Delete any connectors that might have been removed by the nodes // Set the route to the manual created one if the user created such a route if (pointList) connector->setRoutePoints(*pointList,true); // FIXME WTF is going on here? Redundant/meaningless code? ConnectorList connectorList; connectorList.append(connector); setModified(true); requestRerouteInvalidatedConnectors(); return connector; } Node *FlowICNDocument::nodeWithID( const QString &id ) { if ( m_flowNodeList.contains( id ) ) return m_flowNodeList[id]; else return nullptr; } FPNode *FlowICNDocument::getFPnodeWithID( const QString &id ) { if ( m_flowNodeList.contains( id ) ) return m_flowNodeList[id]; else return nullptr; } void FlowICNDocument::slotAssignNodeGroups() { ICNDocument::slotAssignNodeGroups(); const FPNodeMap::iterator end = m_flowNodeList.end(); for ( FPNodeMap::iterator it = m_flowNodeList.begin(); it != end; ++it ) { NodeGroup *ng = createNodeGroup ( *it ); if ( ng ) ng->init(); } // We've destroyed the old node groups, so any collapsed flowcontainers // containing new node groups need to update them to make them invisible. const ItemMap::const_iterator itemListEnd = m_itemList.end(); for ( ItemMap::const_iterator it = m_itemList.begin(); it != itemListEnd; ++it ) { if ( FlowContainer * fc = dynamic_cast ( *it ) ) fc->updateContainedVisibility(); } } void FlowICNDocument::flushDeleteList() { // Remove duplicate items in the delete list KtlQCanvasItemList::iterator end = m_itemDeleteList.end(); for ( KtlQCanvasItemList::iterator it = m_itemDeleteList.begin(); it != end; ++it ) { if ( *it && (m_itemDeleteList.count ( *it ) > 1) ) { *it = nullptr; } } m_itemDeleteList.removeAll(nullptr); end = m_itemDeleteList.end(); for ( KtlQCanvasItemList::iterator it = m_itemDeleteList.begin(); it != end; ++it ) { KtlQCanvasItem *qcanvasItem = *it; m_selectList->removeQCanvasItem ( *it ); if ( Item *item = dynamic_cast ( qcanvasItem ) ) m_itemList.remove ( item->id() ); else if ( FPNode * node = dynamic_cast ( qcanvasItem ) ) m_flowNodeList.remove ( node->id() ); else if ( Connector * con = dynamic_cast ( qcanvasItem ) ) m_connectorList.removeAll ( con ); else qCritical() << Q_FUNC_INFO << "Unknown qcanvasItem! "<setCanvas ( nullptr ); delete qcanvasItem; *it = nullptr; } // Check connectors for merging bool doneJoin = false; const FPNodeMap::iterator nlEnd = m_flowNodeList.end(); for ( FPNodeMap::iterator it = m_flowNodeList.begin(); it != nlEnd; ++it ) { ( *it )->removeNullConnectors(); int conCount = ( *it )->getAllConnectors().count(); if ( conCount == 2 && ! ( *it )->parentItem() ) { if ( joinConnectors ( *it ) ) doneJoin = true; } } if ( doneJoin ) flushDeleteList(); requestRerouteInvalidatedConnectors(); } bool FlowICNDocument::registerItem( KtlQCanvasItem *qcanvasItem ) { if (!qcanvasItem) return false; if ( !ItemDocument::registerItem(qcanvasItem) ) { if ( FPNode * node = dynamic_cast(qcanvasItem) ) { m_flowNodeList[ node->id() ] = node; emit nodeAdded( (Node*)node ); } else if ( Connector * connector = dynamic_cast(qcanvasItem) ) { m_connectorList.append(connector); emit connectorAdded(connector); } else { qCritical() << Q_FUNC_INFO << "Unrecognised item"<parentItem() ) return false; node->removeNullConnectors(); int conCount = node->getAllConnectors().count(); if ( conCount != 2 ) return false; Connector *con1, *con2; Node *startNode, *endNode; QPointList conPoints; if ( node->inputConnectorList().count() == 0 ) { // Both connectors emerge from node - output - i.e. node is pure start node con1 = node->outputConnectorList().at(0).data(); con2 = node->outputConnectorList().at(1).data(); if ( con1 == con2 ) { return false; } startNode = con1->endNode(); endNode = con2->endNode(); conPoints = con1->connectorPoints(true) + con2->connectorPoints(false); } else if ( node->inputConnectorList().count() == 1 ) { // Ont input, one output con1 = node->inputConnectorList().at(0).data(); con2 = node->outputConnectorList().at(0).data(); if ( con1 == con2 ) { return false; } startNode = con1->startNode(); endNode = con2->endNode(); conPoints = con1->connectorPoints(false) + con2->connectorPoints(false); } else { // Both input - i.e. node is pure end node con1 = node->inputConnectorList().at(0).data(); con2 = node->inputConnectorList().at(1).data(); if ( con1 == con2 ) { return false; } startNode = con1->startNode(); endNode = con2->startNode(); conPoints = con1->connectorPoints(false) + con2->connectorPoints(true); } if ( !startNode || !endNode ) return false; // HACK // FIXME // dynamic_cast used FPNode *startFpNode, *endFpNode; startFpNode = dynamic_cast (startNode); endFpNode = dynamic_cast (endNode); Connector *newCon = endFpNode->createInputConnector(startFpNode); if (!newCon) return false; startFpNode->addOutputConnector(newCon); newCon->setRoutePoints( conPoints, con1->usesManualPoints() || con2->usesManualPoints() ); // Avoid flicker: update draw lists now con1->updateConnectorPoints(false); con2->updateConnectorPoints(false); newCon->updateDrawList(); node->removeNode(); con1->removeConnector(); con2->removeConnector(); return true; } diff --git a/src/flowparts/flowpart.cpp b/src/flowparts/flowpart.cpp index 75a201f9..f56274d2 100644 --- a/src/flowparts/flowpart.cpp +++ b/src/flowparts/flowpart.cpp @@ -1,972 +1,971 @@ /*************************************************************************** * Copyright (C) 2003-2005 by David Saxton * * david@bluehaze.org * * * * 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 "canvasitemparts.h" #include "connector.h" #include "flowcodedocument.h" #include "flowcode.h" #include "flowpart.h" #include "fpnode.h" #include "inputflownode.h" #include "itemdocument.h" #include "itemdocumentdata.h" #include "microsettings.h" #include "micropackage.h" #include "picinfo.h" #include "pinmapping.h" #include "variant.h" -#include - -#include -#include -#include -#include -#include +#include +#include +#include +#include +#include +#include #include #include #include // Degrees per radian const double DPR = 57.29577951308232087665461840231273527024; // The following arrays of numbers represent the positions of nodes in different configurations, // with the numbers as NodeInfo::Position. int diamondNodePositioning[8][3] = { {270, 90, 0}, {270, 90, 180}, {270, 0, 90}, {270, 0, 180}, {180, 0, 90}, {180, 0, 270}, {180, 90, 0}, {180, 90, 270} }; int inOutNodePositioning[8][2] = { {270, 90}, {270, 0}, {270, 180}, {0, 0}, // (invalid) {180, 0}, {180, 90}, {180, 270}, {0, 0} }; // (invalid) int inNodePositioning[4] = {270,0,90,180}; int outNodePositioning[4] = {90,180,270,0}; FlowPart::FlowPart( ICNDocument *icnDocument, bool newItem, const QString &id ) : CNItem( icnDocument, newItem, id ) { m_flowSymbol = FlowPart::ps_other; m_orientation = 0; m_stdInput = nullptr; m_stdOutput = nullptr; m_altOutput = nullptr; if ( icnDocument ) { icnDocument->registerItem(this); m_pFlowCodeDocument = dynamic_cast(icnDocument); assert( m_pFlowCodeDocument ); connect( m_pFlowCodeDocument, SIGNAL(picTypeChanged()), this, SLOT(slotUpdateFlowPartVariables()) ); connect( m_pFlowCodeDocument, SIGNAL(pinMappingsChanged()), this, SLOT(slotUpdateFlowPartVariables()) ); } } FlowPart::~FlowPart() { // We have to check view, as if the item is deleted before the CNItem constructor // is called, then there will be no view if (m_pFlowCodeDocument) { const VariantDataMap::iterator end = m_variantData.end(); for ( VariantDataMap::iterator it = m_variantData.begin(); it != end; ++it ) { Variant *v = it.value(); if (v) m_pFlowCodeDocument->varNameChanged( "", v->value().toString() ); } } } void FlowPart::setCaption( const QString &caption ) { if ( m_flowSymbol == FlowPart::ps_other ) { m_caption = caption; return; } // 2016.05.03 - do not use temporary widget for getting font metrics // QWidget *w = new QWidget(); // //QPainter p(w); // QPainter p; // const bool isSuccess = p.begin(w); // if (!isSuccess) { // qWarning() << Q_FUNC_INFO << " painter not active"; // } // p.setFont( font() ); // const int text_width = p.boundingRect( boundingRect(), (Qt::SingleLine | Qt::AlignHCenter | Qt::AlignVCenter), caption ).width(); // p.end(); // delete w; QFontMetrics fontMetrics( font() ); const int text_width = fontMetrics.boundingRect( boundingRect(), (Qt::TextSingleLine | Qt::AlignHCenter | Qt::AlignVCenter), caption ).width(); int width = std::max( ((int)(text_width/16))*16, 48 ); switch(m_flowSymbol) { case FlowPart::ps_call: width += 48; break; case FlowPart::ps_io: case FlowPart::ps_round: width += 32; break; case FlowPart::ps_decision: width += 64; break; case FlowPart::ps_process: default: width += 32; break; } bool hasSideConnectors = m_flowSymbol == FlowPart::ps_decision; if ( hasSideConnectors && (width != this->width()) ) p_icnDocument->requestRerouteInvalidatedConnectors(); initSymbol( m_flowSymbol, width ); m_caption = caption; } void FlowPart::postResize() { updateNodePositions(); CNItem::postResize(); } void FlowPart::createStdInput() { m_stdInput = (FPNode*)createNode( 0, 0, 270, "stdinput", Node::fp_in ); updateNodePositions(); } void FlowPart::createStdOutput() { m_stdOutput = (FPNode*)createNode( 0, 0, 90, "stdoutput", Node::fp_out ); updateNodePositions(); } void FlowPart::createAltOutput() { m_altOutput = (FPNode*)createNode( 0, 0, 0, "altoutput", Node::fp_out ); updateNodePositions(); } void FlowPart::initSymbol( FlowPart::FlowSymbol symbol, int width ) { m_flowSymbol = symbol; switch(symbol) { case FlowPart::ps_other: return; case FlowPart::ps_call: case FlowPart::ps_process: setItemPoints( QRect( -width/2, -16, width, 24 ) ); break; case FlowPart::ps_io: { // define parallelogram shape QPolygon pa(4); pa[0] = QPoint( -(width-10)/2, -16 ); pa[1] = QPoint( width/2, -16 ); pa[2] = QPoint( (width-10)/2, 8 ); pa[3] = QPoint( -width/2, 8 ); setItemPoints(pa); break; } case FlowPart::ps_round: { // define rounded rectangles as two semicricles with RP_NUM/2 points with gap inbetween // These points are not used for drawing; merely for passing to qcanvaspolygonitem for collision detection // If there is a better way for a rounder rectangle + collision detection, please let me know... int halfHeight = 12; // Draw semicircle double x; const int RP_NUM = 48; QPolygon pa(RP_NUM); int point = 0; for ( double y = -1.0; y <= 1.0; y+= 4.0/(RP_NUM-2) ) { x = sqrt(1-y*y)*halfHeight; pa[point] = QPoint( (int)(width+x)-halfHeight, (int)(halfHeight*y) ); pa[RP_NUM-1-point] = QPoint ( (int)(halfHeight-x), (int)(halfHeight*y) ); point++; } pa.translate( -width/2, 4 ); setItemPoints(pa); break; } case FlowPart::ps_decision: { // define rhombus QPolygon pa(6); pa[0] = QPoint( 0, -24 ); pa[1] = QPoint( width/2, -6 ); pa[2] = QPoint( width/2, 6 ); pa[3] = QPoint( 0, 24 ); pa[4] = QPoint( -width/2, 6 ); pa[5] = QPoint( -width/2, -6 ); setItemPoints(pa); break; } default: qCritical() << Q_FUNC_INFO << "Unknown flowSymbol: "<id(); } FlowPart* FlowPart::outputPart( const QString& internalNodeId ) { Node *node = p_icnDocument->nodeWithID( nodeId(internalNodeId) ); FPNode *fpnode = dynamic_cast(node); // FIXME dynamic_cast used to replace fpnode::type() call if ( !fpnode || ( dynamic_cast(fpnode) != nullptr) ) // if ( !fpnode || fpnode->type() == Node::fp_in ) return nullptr; return fpnode->outputFlowPart(); } FlowPartList FlowPart::inputParts( const QString& id ) { Node *node = p_icnDocument->nodeWithID(id); if ( FPNode *fpNode = dynamic_cast(node) ) return fpNode->inputFlowParts(); return FlowPartList(); } FlowPartList FlowPart::inputParts() { FlowPartList list; const NodeInfoMap::iterator nEnd = m_nodeMap.end(); for ( NodeInfoMap::iterator it = m_nodeMap.begin(); it != nEnd; ++it ) { Node *node = p_icnDocument->nodeWithID( it.value().id ); FlowPartList newList; if ( FPNode *fpNode = dynamic_cast(node) ) newList = fpNode->inputFlowParts(); const FlowPartList::iterator nlEnd = newList.end(); for ( FlowPartList::iterator it = newList.begin(); it != nlEnd; ++it ) { if (*it) list.append(*it); } } return list; } FlowPartList FlowPart::outputParts() { FlowPartList list; const NodeInfoMap::iterator end = m_nodeMap.end(); for ( NodeInfoMap::iterator it = m_nodeMap.begin(); it != end; ++it ) { FlowPart *part = outputPart( it.key() ); if (part) list.append(part); } return list; } FlowPart* FlowPart::endPart( QStringList ids, FlowPartList *previousParts ) { if ( ids.empty() ) { const NodeInfoMap::iterator end = m_nodeMap.end(); for ( NodeInfoMap::iterator it = m_nodeMap.begin(); it != end; ++it ) { ids.append( it.key() ); } filterEndPartIDs( &ids ); } const bool createdList = (!previousParts); if (createdList) { previousParts = new FlowPartList; } else if ( previousParts->contains(this) ) { return nullptr; } previousParts->append(this); if ( ids.empty() ) { return nullptr; } if ( ids.size() == 1 ) { return outputPart( *(ids.begin()) ); } typedef QList ValidPartsList; ValidPartsList validPartsList; const QStringList::iterator idsEnd = ids.end(); for ( QStringList::iterator it = ids.begin(); it != idsEnd; ++it ) { int prevLevel = level(); FlowPartList validParts; FlowPart *part = outputPart(*it); while (part) { if ( !validParts.contains(part) ) { validParts.append(part); // if ( part->level() >= level() ) { const int _l = part->level(); part = part->endPart( QStringList(), previousParts ); prevLevel = _l; // } else { // part = nullptr; // } } else { part = nullptr; } } if ( !validParts.empty() ) { validPartsList.append(validParts); } } if (createdList) { delete previousParts; previousParts = nullptr; } if ( validPartsList.empty() ) return nullptr; FlowPartList firstList = *(validPartsList.begin()); const FlowPartList::iterator flEnd = firstList.end(); const ValidPartsList::iterator vplEnd = validPartsList.end(); for ( FlowPartList::iterator it = firstList.begin(); it != flEnd; ++it ) { bool ok = true; for ( ValidPartsList::iterator vplit = validPartsList.begin(); vplit != vplEnd; ++vplit ) { if ( !(*vplit).contains(*it) ) ok = false; } if (ok) return *it; } return nullptr; } void FlowPart::handleIfElse( FlowCode *code, const QString &case1Statement, const QString &case2Statement, const QString &case1, const QString &case2 ) { if (!code) return; FlowPart *stop = nullptr; FlowPart *part1 = outputPart(case1); FlowPart *part2 = outputPart(case2); if ( part1 && part2 ) stop = endPart( (QStringList(case1) << case2 ) ); if ( (!part1 && !part2) || (part1 == stop && part2 == stop) ) return; code->addStopPart(stop); if ( part1 && part1 != stop && code->isValidBranch(part1) ) { // Use the case1 statement code->addCode( "if "+case1Statement+" then "+"\n{" ); code->addCodeBranch(part1); code->addCode("}"); if ( part2 && part2 != stop && code->isValidBranch(part2) ) { code->addCode( "else\n{" ); code->addCodeBranch(part2); code->addCode("}"); } } else if ( code->isValidBranch(part2) ) { // Use the case2 statement code->addCode( "if "+case2Statement+" then "+"\n{" ); code->addCodeBranch(part2); code->addCode("}"); } code->removeStopPart(stop); code->addCodeBranch(stop); } Variant * FlowPart::createProperty( const QString & id, Variant::Type::Value type ) { if ( type != Variant::Type::Port && type != Variant::Type::Pin && type != Variant::Type::VarName && type != Variant::Type::SevenSegment && type != Variant::Type::KeyPad ) return CNItem::createProperty( id, type ); Variant * v = createProperty( id, Variant::Type::String ); v->setType(type); if ( type == Variant::Type::VarName ) { if ( m_pFlowCodeDocument ) { if ( MicroSettings * settings = m_pFlowCodeDocument->microSettings() ) v->setAllowed( settings->variableNames() ); } connect( property(id), SIGNAL(valueChanged(QVariant, QVariant )), this, SLOT(varNameChanged(QVariant, QVariant )) ); } else slotUpdateFlowPartVariables(); return v; } void FlowPart::slotUpdateFlowPartVariables() { if (!m_pFlowCodeDocument) return; MicroSettings *s = m_pFlowCodeDocument->microSettings(); if (!s) return; const PinMappingMap pinMappings = s->pinMappings(); QStringList sevenSegMaps; QStringList keyPadMaps; PinMappingMap::const_iterator pEnd = pinMappings.end(); for ( PinMappingMap::const_iterator it = pinMappings.begin(); it != pEnd; ++it ) { switch ( it.value().type() ) { case PinMapping::SevenSegment: sevenSegMaps << it.key(); break; case PinMapping::Keypad_4x3: case PinMapping::Keypad_4x4: keyPadMaps << it.key(); break; case PinMapping::Invalid: break; } } QStringList ports = s->microInfo()->package()->portNames(); ports.sort(); QStringList pins = s->microInfo()->package()->pinIDs(PicPin::type_bidir | PicPin::type_input | PicPin::type_open); pins.sort(); const VariantDataMap::iterator vEnd = m_variantData.end(); for ( VariantDataMap::iterator it = m_variantData.begin(); it != vEnd; ++it ) { Variant * v = it.value(); if ( !v ) continue; if ( v->type() == Variant::Type::Port ) v->setAllowed( ports ); else if ( v->type() == Variant::Type::Pin ) v->setAllowed( pins ); else if ( v->type() == Variant::Type::SevenSegment ) { v->setAllowed( sevenSegMaps ); if ( !sevenSegMaps.isEmpty() && !sevenSegMaps.contains( v->value().toString() ) ) v->setValue( sevenSegMaps.first() ); } else if ( v->type() == Variant::Type::KeyPad ) { v->setAllowed( keyPadMaps ); if ( !keyPadMaps.isEmpty() && !keyPadMaps.contains( v->value().toString() ) ) v->setValue( keyPadMaps.first() ); } } } void FlowPart::updateVarNames() { if (!m_pFlowCodeDocument) return; MicroSettings *s = m_pFlowCodeDocument->microSettings(); if (!s) return; const QStringList names = s->variableNames(); const VariantDataMap::iterator end = m_variantData.end(); for ( VariantDataMap::iterator it = m_variantData.begin(); it != end; ++it ) { Variant *v = it.value(); if ( v && v->type() == Variant::Type::VarName ) v->setAllowed(names); } } void FlowPart::varNameChanged( QVariant newValue, QVariant oldValue ) { if (!m_pFlowCodeDocument) return; m_pFlowCodeDocument->varNameChanged( newValue.toString(), oldValue.toString() ); } inline int nodeDirToPos( int dir ) { switch ( dir ) { case 0: return 0; case 270: return 1; case 180: return 2; case 90: return 3; } return 0; } void FlowPart::updateAttachedPositioning( ) { if (b_deleted) return; //BEGIN Rearrange text if appropriate const QRect textPos[4] = { QRect( offsetX()+width(), 6, 40, 16 ), QRect( 0, offsetY()-16, 40, 16 ), QRect( offsetX()-40, 6, 40, 16 ), QRect( 0, offsetY()+height(), 40, 16 ) }; NodeInfo * stdOutputInfo = m_stdOutput ? &m_nodeMap["stdoutput"] : nullptr; NodeInfo * altOutputInfo = m_altOutput ? &m_nodeMap["altoutput"] : nullptr; Text *outputTrueText = m_textMap.contains("output_true") ? m_textMap["output_true"] : nullptr; Text *outputFalseText = m_textMap.contains("output_false") ? m_textMap["output_false"] : nullptr; if ( stdOutputInfo && outputTrueText ) outputTrueText->setOriginalRect( textPos[ nodeDirToPos( stdOutputInfo->orientation ) ] ); if ( altOutputInfo && outputFalseText ) outputFalseText->setOriginalRect( textPos[ nodeDirToPos( altOutputInfo->orientation ) ] ); const TextMap::iterator textMapEnd = m_textMap.end(); for ( TextMap::iterator it = m_textMap.begin(); it != textMapEnd; ++it ) { QRect pos = it.value()->recommendedRect(); it.value()->move( pos.x() + x(), pos.y() + y() ); it.value()->setGuiPartSize( pos.width(), pos.height() ); } //END Rearrange text if appropriate const NodeInfoMap::iterator end = m_nodeMap.end(); for ( NodeInfoMap::iterator it = m_nodeMap.begin(); it != end; ++it ) { if ( !it.value().node ) { qCritical() << Q_FUNC_INFO << "Node in nodemap is null" << endl; continue; } double nx = it.value().x; double ny = it.value().y; #define round_8(x) (((x) > 0) ? int(((x)+4)/8)*8 : int(((x)-4)/8)*8) nx = round_8(nx); ny = round_8(ny); #undef round_8 it.value().node->move( int(nx+x()), int(ny+y()) ); it.value().node->setOrientation( it.value().orientation ); } } ItemData FlowPart::itemData( ) const { ItemData itemData = CNItem::itemData(); itemData.orientation = m_orientation; return itemData; } void FlowPart::restoreFromItemData( const ItemData & itemData ) { CNItem::restoreFromItemData(itemData); if ( itemData.orientation >= 0 ) setOrientation( uint(itemData.orientation) ); } void FlowPart::updateNodePositions() { if ( m_orientation > 7 ) { qWarning() << Q_FUNC_INFO << "Invalid orientation: "<orientation = diamondNodePositioning[m_orientation][0]; stdOutputInfo->orientation = diamondNodePositioning[m_orientation][1]; altOutputInfo->orientation = diamondNodePositioning[m_orientation][2]; } else if ( m_stdInput && m_stdOutput ) { stdInputInfo->orientation = inOutNodePositioning[m_orientation][0]; stdOutputInfo->orientation = inOutNodePositioning[m_orientation][1]; } else if ( m_orientation < 4 ) { if (stdInputInfo) stdInputInfo->orientation = inNodePositioning[m_orientation]; else if (stdOutputInfo) stdOutputInfo->orientation = outNodePositioning[m_orientation]; } else { qWarning() << Q_FUNC_INFO << "Invalid orientation: "<requestRerouteInvalidatedConnectors(); } uint FlowPart::allowedOrientations( ) const { // The bit positions shown here represent whether or not that orientation is allowed, the orientation being // what is displayed in the i'th position (0 to 3 on top, 4 to 7 on bottom) of orientation widget if ( m_stdInput && m_stdOutput && m_altOutput ) return 255; if ( m_stdInput && m_stdOutput ) return 119; if ( m_stdInput || m_stdOutput ) return 15; return 0; } void FlowPart::orientationPixmap( uint orientation, QPixmap & pm ) const { const QSize size = pm.size(); if ( ! ( allowedOrientations() & ( 1 << orientation ) ) ) { qWarning() << Q_FUNC_INFO << "Requesting invalid orientation of " << orientation << endl; return; } QBitmap mask( 50, 50 ); //QPainter maskPainter(&mask); // 2016.05.03 - initialize painter explicitly QPainter maskPainter; { const bool isSuccess = maskPainter.begin(&mask); if (!isSuccess) { qWarning() << Q_FUNC_INFO << " painter not active"; } } mask.fill( Qt::color0 ); maskPainter.setBrush(Qt::color1); maskPainter.setPen(Qt::color1); //BEGIN painter on pm { //QPainter p(&pm); // 2016.05.03 - explicitly initialize painter QPainter p; const bool isBeginSuccess = p.begin(&pm); if (!isBeginSuccess) { qWarning() << Q_FUNC_INFO << " painter not active"; } p.setBrush(m_brushCol); p.setPen( Qt::black ); // In order: right corner, top corner, left corner, bottom corner QPoint c[4] = { QPoint( int(0.7*size.width()), int(0.5*size.height()) ), QPoint( int(0.5*size.width()), int(0.4*size.height()) ), QPoint( int(0.3*size.width()), int(0.5*size.height()) ), QPoint( int(0.5*size.width()), int(0.6*size.height()) ) }; QPoint d[4]; d[0] = c[0] + QPoint( 7, 0 ); d[1] = c[1] + QPoint( 0, -7 ); d[2] = c[2] + QPoint( -7, 0 ); d[3] = c[3] + QPoint( 0, 7 ); if ( m_stdInput && m_stdOutput && m_altOutput ) { //BEGIN Draw diamond outline QPolygon diamond(4); for ( uint i=0; i<4; ++i ) diamond[i] = c[i]; p.drawPolygon(diamond); maskPainter.drawPolygon(diamond); //END Draw diamond outline //BEGIN Draw input int pos0 = nodeDirToPos( diamondNodePositioning[orientation][0] ); p.drawLine( c[pos0], d[pos0] ); maskPainter.drawLine( c[pos0], d[pos0] ); //END Draw input //BEGIN Draw "true" output as a tick QPolygon tick(4); tick[0] = QPoint( -3, 0 ); tick[1] = QPoint( 0, 2 ); tick[2] = QPoint( 0, 2 ); tick[3] = QPoint( 4, -2 ); int pos1 = nodeDirToPos( diamondNodePositioning[orientation][1] ); tick.translate( d[pos1].x(), d[pos1].y() ); p.drawLines(tick); maskPainter.drawLines(tick); //END Draw "true" output as a tick //BEGIN Draw "false" output as a cross QPolygon cross(4); cross[0] = QPoint( -2, -2 ); cross[1] = QPoint( 2, 2 ); cross[2] = QPoint( -2, 2 ); cross[3] = QPoint( 2, -2 ); int pos2 = nodeDirToPos( diamondNodePositioning[orientation][2] ); cross.translate( d[pos2].x(), d[pos2].y() ); p.drawLines(cross); maskPainter.drawLines(cross); //END Draw "false" output as a cross } else if ( m_stdInput || m_stdOutput ) { p.drawRoundRect( int(0.3*size.width()), int(0.4*size.height()), int(0.4*size.width()), int(0.2*size.height()) ); maskPainter.drawRoundRect( int(0.3*size.width()), int(0.4*size.height()), int(0.4*size.width()), int(0.2*size.height()) ); int hal = 5; // half arrow length int haw = 3; // half arrow width QPoint arrows[4][6] = { { QPoint( hal, 0 ), QPoint( 0, -haw ), QPoint( hal, 0 ), QPoint( -hal, 0 ), QPoint( hal, 0 ), QPoint( 0, haw ) }, { QPoint( 0, -hal ), QPoint( -haw, 0 ), QPoint( 0, -hal ), QPoint( 0, hal ), QPoint( 0, -hal ), QPoint( haw, 0 ) }, { QPoint( -hal, 0 ), QPoint( 0, -haw ), QPoint( -hal, 0 ), QPoint( hal, 0 ), QPoint( -hal, 0 ), QPoint( 0, haw ) }, { QPoint( 0, hal ), QPoint( -haw, 0 ), QPoint( 0, hal ), QPoint( 0, -hal ), QPoint( 0, hal ), QPoint( haw, 0 ) } }; int inPos = -1; int outPos = -1; if ( m_stdInput && m_stdOutput ) { inPos = nodeDirToPos( inOutNodePositioning[orientation][0] ); outPos = nodeDirToPos( inOutNodePositioning[orientation][1] ); } else if ( m_stdInput ) { inPos = nodeDirToPos( inNodePositioning[orientation] ); } else if ( m_stdOutput ) { outPos = nodeDirToPos( outNodePositioning[orientation] ); } if ( inPos != -1 ) { QPolygon inArrow(6); for ( int i=0; i<6; ++i ) { inArrow[i] = arrows[(inPos+2)%4][i]; } inArrow.translate( d[inPos].x(), d[inPos].y() ); p.drawPolygon(inArrow); maskPainter.drawPolygon(inArrow); } if ( outPos != -1 ) { QPolygon outArrow(6); for ( int i=0; i<6; ++i ) { outArrow[i] = arrows[outPos][i]; } outArrow.translate( d[outPos].x(), d[outPos].y() ); p.drawPolygon(outArrow); maskPainter.drawPolygon(outArrow); } } } //END painter on pm pm.setMask(mask); // pm needs not to have active painters on it } diff --git a/src/flowparts/inputflownode.cpp b/src/flowparts/inputflownode.cpp index 6eb92b84..744b588a 100644 --- a/src/flowparts/inputflownode.cpp +++ b/src/flowparts/inputflownode.cpp @@ -1,141 +1,141 @@ // // C++ Implementation: inputflownode // // Description: // // // // Copyright: See COPYING file that comes with this distribution // // #include "connector.h" #include "inputflownode.h" #include "flowconnector.h" #include "flowpart.h" -#include -#include +#include +#include InputFlowNode::InputFlowNode(ICNDocument *icnDocument, int dir, const QPoint &pos, QString *id) : FPNode(icnDocument, Node::fp_in, dir, pos, id) { } InputFlowNode::~InputFlowNode() { } FlowPart *InputFlowNode::outputFlowPart() const { return dynamic_cast(parentItem()); } FlowPartList InputFlowNode::inputFlowParts() const { FlowPartList list; const FlowConnectorList::const_iterator end = m_inFlowConnList.end(); for ( FlowConnectorList::const_iterator it = m_inFlowConnList.begin(); it != end; ++it ) { if (*it) { Node *startNode = (*it)->startNode(); FlowPart *flowPart = startNode ? dynamic_cast(startNode->parentItem()) : nullptr; if (flowPart) list.append(flowPart); } } return list; } bool InputFlowNode::acceptInput() const { return true; } bool InputFlowNode::acceptOutput() const { return false; } void InputFlowNode::addOutputConnector( Connector * const /*connector*/ ) { qCritical() << Q_FUNC_INFO << "BUG: adding output connector to an input node" << endl; } inline QPolygon arrowPoints( int dir ) { QPolygon pa(3); switch ( dir ) { case 0: pa[0] = QPoint( 3, 0 ); pa[1] = QPoint( 0, 2 ); pa[2] = QPoint( 0, -2 ); break; case 180: pa[0] = QPoint( -3, 0 ); pa[1] = QPoint( 0, 2 ); pa[2] = QPoint( 0, -2 ); break; case 90: pa[0] = QPoint( 2, 0 ); pa[1] = QPoint( -2, 0 ); pa[2] = QPoint( 0, 3 ); break; case 270: pa[0] = QPoint( 2, 0 ); pa[1] = QPoint( -2, 0 ); pa[2] = QPoint( 0, -3 ); break; }; return pa; } void InputFlowNode::drawShape ( QPainter &p ) { const int _x = ( int ) x(); const int _y = ( int ) y(); if ( m_dir == 0 ) p.drawLine ( _x, _y, _x-8, _y ); else if ( m_dir == 90 ) p.drawLine ( _x, _y, _x, _y-8 ); else if ( m_dir == 180 ) p.drawLine ( _x, _y, _x+8, _y ); else if ( m_dir == 270 ) p.drawLine ( _x, _y, _x, _y+8 ); QPolygon pa ( 3 ); switch ( m_dir ) { case 180: // right pa = arrowPoints ( 0 ); break; case 0: // left pa = arrowPoints ( 180 ); break; case 270: // down pa = arrowPoints ( 90 ); break; case 90: // up pa = arrowPoints ( 270 ); break; default: qCritical() << Q_FUNC_INFO << "BUG: m_dir = " << m_dir << endl; } // Note: I have not tested the positioning of the arrows for all combinations. // In fact, most almost definitely do not work. So feel free to change the code // as you see fit if necessary. if ( m_dir == 0 ); else if ( m_dir == 90 ); else if ( m_dir == 180 ) pa.translate ( 3, 0 ); else if ( m_dir == 270 ) pa.translate ( 0, 3 ); pa.translate ( _x, _y ); p.drawPolygon ( pa ); } diff --git a/src/flowparts/junctionflownode.cpp b/src/flowparts/junctionflownode.cpp index 0bd4e8a6..242b3c87 100644 --- a/src/flowparts/junctionflownode.cpp +++ b/src/flowparts/junctionflownode.cpp @@ -1,139 +1,139 @@ // // C++ Implementation: junctionflownode // // Description: // // // Copyright: See COPYING file that comes with this distribution // // #include "connector.h" #include "junctionflownode.h" #include "flowconnector.h" -#include +#include JunctionFlowNode::JunctionFlowNode(ICNDocument* _icnView, int dir, const QPoint& pos, QString* id ): FPNode(_icnView, Node::fp_junction, dir, pos, id) { } JunctionFlowNode::~JunctionFlowNode() { } void JunctionFlowNode::initPoints() { setPoints( QPolygon( QRect( -4, -4, 9, 9 ) ) ); } bool JunctionFlowNode::acceptInput() const { return true; } bool JunctionFlowNode::acceptOutput() const { return true; } void JunctionFlowNode::checkForRemoval( Connector *connector ) { FPNode::checkForRemoval(connector); if( !m_outputConnector ) removeNode(); } inline QPolygon arrowPoints( int dir ) { QPolygon pa(3); switch ( dir ) { case 0: pa[0] = QPoint( 3, 0 ); pa[1] = QPoint( 0, 2 ); pa[2] = QPoint( 0, -2 ); break; case 180: pa[0] = QPoint( -3, 0 ); pa[1] = QPoint( 0, 2 ); pa[2] = QPoint( 0, -2 ); break; case 90: pa[0] = QPoint( 2, 0 ); pa[1] = QPoint( -2, 0 ); pa[2] = QPoint( 0, 3 ); break; case 270: pa[0] = QPoint( 2, 0 ); pa[1] = QPoint( -2, 0 ); pa[2] = QPoint( 0, -3 ); break; }; return pa; } void JunctionFlowNode::drawShape ( QPainter &p ) { const int _x = ( int ) x(); const int _y = ( int ) y(); if ( !m_inFlowConnList.isEmpty() ) { const FlowConnectorList::iterator end = m_inFlowConnList.end(); for ( FlowConnectorList::iterator it = m_inFlowConnList.begin(); it != end; ++ it ) { Connector * connector = *it; if ( !connector ) continue; // Work out the direction of the connector const QPointList points = connector->connectorPoints ( false ); const int count = points.size(); if ( count < 2 ) continue; QPoint end_0 = points[count-1]; QPoint end_1 = points[count-2]; QPolygon pa; if ( end_0.x() < end_1.x() ) { pa = arrowPoints ( 180 ); pa.translate ( 4, 0 ); } else if ( end_0.x() > end_1.x() ) { pa = arrowPoints ( 0 ); pa.translate ( -4, 0 ); } else if ( end_0.y() < end_1.y() ) { pa = arrowPoints ( 270 ); pa.translate ( 0, 4 ); } else if ( end_0.y() > end_1.y() ) { pa = arrowPoints ( 90 ); pa.translate ( 0, -4 ); } else continue; pa.translate ( _x, _y ); p.setPen ( connector->isSelected() ? m_selectedColor : Qt::black ); p.drawPolygon ( pa ); } return; } if ( m_dir == 0 ) p.drawLine ( _x, _y, _x-8, _y ); else if ( m_dir == 90 ) p.drawLine ( _x, _y, _x, _y-8 ); else if ( m_dir == 180 ) p.drawLine ( _x, _y, _x+8, _y ); else if ( m_dir == 270 ) p.drawLine ( _x, _y, _x, _y+8 ); } diff --git a/src/flowparts/outputflownode.cpp b/src/flowparts/outputflownode.cpp index 9e9423d2..2652543d 100644 --- a/src/flowparts/outputflownode.cpp +++ b/src/flowparts/outputflownode.cpp @@ -1,119 +1,119 @@ // // C++ Implementation: outputflownode // // Description: // // // Author: David Saxton , (C) 2008 // // Copyright: See COPYING file that comes with this distribution // // #include "connector.h" #include "outputflownode.h" -#include -#include +#include +#include OutputFlowNode::OutputFlowNode(ICNDocument* _icnView, int dir, const QPoint& pos, QString* id): FPNode(_icnView, Node::fp_out, dir, pos, id) { } OutputFlowNode::~OutputFlowNode() { } bool OutputFlowNode::acceptInput() const { return false; } bool OutputFlowNode::acceptOutput() const { return true; } void OutputFlowNode::addInputConnector( Connector * const /*connector*/ ) { qDebug() << Q_FUNC_INFO << "BUG: trying to add input connector to an output node" << endl; } inline QPolygon arrowPoints( int dir ) { QPolygon pa(3); switch ( dir ) { case 0: pa[0] = QPoint( 3, 0 ); pa[1] = QPoint( 0, 2 ); pa[2] = QPoint( 0, -2 ); break; case 180: pa[0] = QPoint( -3, 0 ); pa[1] = QPoint( 0, 2 ); pa[2] = QPoint( 0, -2 ); break; case 90: pa[0] = QPoint( 2, 0 ); pa[1] = QPoint( -2, 0 ); pa[2] = QPoint( 0, 3 ); break; case 270: pa[0] = QPoint( 2, 0 ); pa[1] = QPoint( -2, 0 ); pa[2] = QPoint( 0, -3 ); break; }; return pa; } void OutputFlowNode::drawShape ( QPainter &p ) { const int _x = ( int ) x(); const int _y = ( int ) y(); if ( m_dir == 0 ) p.drawLine ( _x, _y, _x-8, _y ); else if ( m_dir == 90 ) p.drawLine ( _x, _y, _x, _y-8 ); else if ( m_dir == 180 ) p.drawLine ( _x, _y, _x+8, _y ); else if ( m_dir == 270 ) p.drawLine ( _x, _y, _x, _y+8 ); QPolygon pa ( 3 ); switch ( m_dir ) { case 0: // right pa = arrowPoints ( 0 ); break; case 180: // left pa = arrowPoints ( 180 ); break; case 90: // down pa = arrowPoints ( 90 ); break; case 270: // up pa = arrowPoints ( 270 ); break; default: qCritical() << Q_FUNC_INFO << "BUG: m_dir = " << m_dir << endl; } // Note: I have not tested the positioning of the arrows for all combinations. // In fact, most almost definitely do not work. So feel free to change the code // as you see fit if necessary. if ( m_dir == 0 ) pa.translate ( -5, 0 ); else if ( m_dir == 90 ) pa.translate ( 0, -5 ); else if ( m_dir == 180 ) pa.translate ( 5, 0 ); else if ( m_dir == 270 ) pa.translate ( 0, 5 ); pa.translate ( _x, _y ); p.drawPolygon ( pa ); } diff --git a/src/flowparts/pinmapping.cpp b/src/flowparts/pinmapping.cpp index 16c24e53..ebe19fa8 100644 --- a/src/flowparts/pinmapping.cpp +++ b/src/flowparts/pinmapping.cpp @@ -1,426 +1,426 @@ /*************************************************************************** * Copyright (C) 2005 by David Saxton * * david@bluehaze.org * * * * 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 "canvasmanipulator.h" #include "cnitemgroup.h" #include "eckeypad.h" #include "ecsevensegment.h" #include "libraryitem.h" #include "microinfo.h" #include "micropackage.h" #include "node.h" #include "pinmapping.h" #include "viewcontainer.h" #include #include -#include -#include -#include -#include +#include +#include +#include +#include #include #include #include //BEGIN class PinMapping PinMapping::PinMapping( Type type ) { m_type = type; } PinMapping::PinMapping() { m_type = Invalid; } PinMapping::~PinMapping() { } //END class PinMapping //BEGIN class PinMapEditor PinMapEditor::PinMapEditor( PinMapping * pinMapping, MicroInfo * picInfo, QWidget * parent, const char * name ) : QDialog(parent) { setObjectName(name); setModal(true); setWindowTitle(i18n("Pin Map Editor")); QVBoxLayout *mainLayout = new QVBoxLayout; setLayout(mainLayout); m_pPinMapping = pinMapping; m_pPinMapDocument = new PinMapDocument(); { QAction * actionDelSel = new QAction( this ); actionDelSel->setShortcut(Qt::Key_Delete); connect(actionDelSel, SIGNAL(triggered(bool)), m_pPinMapDocument, SLOT(deleteSelection()) ); addAction(actionDelSel); } { QAction * actionSelAll = new QAction( this ); actionSelAll->setShortcut( KStandardShortcut::selectAll().first() ); connect(actionSelAll, SIGNAL(triggered(bool)), m_pPinMapDocument, SLOT(selectAll()) ); addAction(actionSelAll); } { QAction * actionUndo = new QAction( this ); actionUndo->setShortcut( KStandardShortcut::undo().first() ); connect(actionUndo, SIGNAL(triggered(bool)), m_pPinMapDocument, SLOT(undo()) ); addAction(actionUndo); } { QAction * actionRedo = new QAction( this ); actionRedo->setShortcut( KStandardShortcut::redo().first() ); connect(actionRedo, SIGNAL(triggered(bool)), m_pPinMapDocument, SLOT(redo()) ); addAction(actionRedo); } QFrame * f = new QFrame(this); f->setMinimumWidth( 480 ); f->setMinimumHeight( 480 ); f->setFrameShape( QFrame::Box ); f->setFrameShadow( QFrame::Plain ); QVBoxLayout * fLayout = new QVBoxLayout; // ( f, 1, 0, "fLayout" ); // 2018.08.18 - use non-deprected constructor fLayout->setContentsMargins(1,1,1,1); fLayout->setSpacing(0); fLayout->setObjectName("fLayout"); ViewContainer * vc = new ViewContainer( nullptr, f ); fLayout->addWidget( vc ); m_pPinMapView = static_cast(m_pPinMapDocument->createView( vc, 0 )); //qApp->processEvents(); // 2015.07.07 - do not process events, if it is not urgently needed; might generate crashes? m_pPinMapDocument->init( *m_pPinMapping, picInfo ); mainLayout->addWidget(f); QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok|QDialogButtonBox::Cancel|QDialogButtonBox::Apply); mainLayout->addWidget(buttonBox); QPushButton *okButton = buttonBox->button(QDialogButtonBox::Ok); okButton->setDefault(true); okButton->setShortcut(Qt::CTRL | Qt::Key_Return); connect(buttonBox, &QDialogButtonBox::accepted, this, &PinMapEditor::slotOk); connect(buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject); connect(buttonBox->button(QDialogButtonBox::Apply), &QPushButton::clicked, this, &PinMapEditor::slotApply); } void PinMapEditor::slotApply() { savePinMapping(); } void PinMapEditor::slotOk() { savePinMapping(); accept(); } void PinMapEditor::savePinMapping() { *m_pPinMapping = m_pPinMapDocument->pinMapping(); } //END class PinMapEditor //BEGIN class PinMapDocument PinMapDocument::PinMapDocument() : CircuitICNDocument( nullptr, nullptr ) { m_pPicComponent = nullptr; m_pKeypad = nullptr; m_pSevenSegment = nullptr; m_type = dt_pinMapEditor; m_cmManager->addManipulatorInfo( CMSelect::manipulatorInfo() ); } PinMapDocument::~PinMapDocument() { } void PinMapDocument::init( const PinMapping & pinMapping, MicroInfo * microInfo ) { m_pinMappingType = pinMapping.type(); m_pPicComponent = static_cast( addItem( "PIC_IC", QPoint( 336, 224 ), true ) ); m_pPicComponent->initPackage( microInfo ); const QStringList pins = pinMapping.pins(); const QStringList::const_iterator end = pins.end(); int keypadCols = -1; // -1 means no keypad switch ( m_pinMappingType ) { case PinMapping::SevenSegment: { m_pSevenSegment = static_cast( addItem( "ec/seven_segment", QPoint( 144, 232 ), true ) ); char ssPin = 'a'; for ( QStringList::const_iterator it = pins.begin(); it != end; ++it ) { ICNDocument::createConnector( m_pSevenSegment->childNode( QChar(ssPin) ), m_pPicComponent->childNode(*it) ); ssPin++; } break; } case PinMapping::Keypad_4x3: m_pKeypad = static_cast( addItem( "ec/keypad", QPoint( 144, 232 ), true ) ); m_pKeypad->property("numCols")->setValue(3); keypadCols = 3; break; case PinMapping::Keypad_4x4: m_pKeypad = static_cast( addItem( "ec/keypad", QPoint( 144, 232 ), true ) ); m_pKeypad->property("numCols")->setValue(4); keypadCols = 4; break; case PinMapping::Invalid: qDebug() << Q_FUNC_INFO << "m_pinMappingType == Invalid" << endl; break; } if ( keypadCols != -1 ) { QStringList::const_iterator it = pins.begin(); for ( unsigned row = 0; (row < 4) && (it != end); ++row, ++it ) ICNDocument::createConnector( m_pKeypad->childNode( QString("row_%1").arg( row ) ), m_pPicComponent->childNode( *it ) ); for ( int col = 0; (col < keypadCols) && (it != end); ++col, ++it ) ICNDocument::createConnector( m_pKeypad->childNode( QString("col_%1").arg( col ) ), m_pPicComponent->childNode( *it ) ); } clearHistory(); // Don't allow undoing of initial creation of stuff } bool PinMapDocument::isValidItem( Item * item ) { return isValidItem( item->type() ); } bool PinMapDocument::isValidItem( const QString & id ) { if ( !m_pPicComponent && id == "PIC_IC" ) return true; switch ( m_pinMappingType ) { case PinMapping::SevenSegment: return ( !m_pSevenSegment && id == "ec/seven_segment" ); case PinMapping::Keypad_4x3: return ( !m_pKeypad && id == "ec/keypad" ); case PinMapping::Keypad_4x4: return ( !m_pKeypad && id == "ec/keypad" ); case PinMapping::Invalid: return false; } return false; } void PinMapDocument::deleteSelection() { m_selectList->removeQCanvasItem( m_pPicComponent ); m_selectList->removeQCanvasItem( m_pSevenSegment ); m_selectList->removeQCanvasItem( m_pKeypad ); ICNDocument::deleteSelection(); } PinMapping PinMapDocument::pinMapping() const { const NodeInfoMap picNodeInfoMap = m_pPicComponent->nodeMap(); const NodeInfoMap::const_iterator picNodeInfoMapEnd = picNodeInfoMap.end(); QStringList picPinIDs; QStringList attachedIDs; Component * attached = nullptr; switch ( m_pinMappingType ) { case PinMapping::SevenSegment: for ( unsigned i = 0; i < 7; ++i ) attachedIDs << QChar('a'+i); attached = m_pSevenSegment; break; case PinMapping::Keypad_4x3: for ( unsigned i = 0; i < 4; ++i ) attachedIDs << QString::fromLatin1("row_%1").arg(i); for ( unsigned i = 0; i < 3; ++i ) attachedIDs << QString::fromLatin1("col_%1").arg(i); attached = m_pKeypad; break; case PinMapping::Keypad_4x4: for ( unsigned i = 0; i < 4; ++i ) attachedIDs << QString::fromLatin1("row_%1").arg(i); for ( unsigned i = 0; i < 4; ++i ) attachedIDs << QString::fromLatin1("col_%1").arg(i); attached = m_pKeypad; break; case PinMapping::Invalid: break; } if ( !attached ) return PinMapping(); QStringList::iterator end = attachedIDs.end(); for ( QStringList::iterator attachedIt = attachedIDs.begin(); attachedIt != end; ++ attachedIt ) { Node * node = attached->childNode( *attachedIt ); QString pinID; for ( NodeInfoMap::const_iterator it = picNodeInfoMap.begin(); it != picNodeInfoMapEnd; ++it ) { if ( it.value().node->isConnected( node ) ) { pinID = it.key(); break; } } picPinIDs << pinID; } PinMapping pinMapping( m_pinMappingType ); pinMapping.setPins( picPinIDs ); return pinMapping; } //END class PinMapDocument //BEGIN class PinMapView PinMapView::PinMapView( PinMapDocument * pinMapDocument, ViewContainer * viewContainer, uint viewAreaId, const char * name ) : ICNView( pinMapDocument, viewContainer, viewAreaId, name ) { } PinMapView::~PinMapView() { } //END class PinMapView //BEGIN class PIC_IC Item* PIC_IC::construct( ItemDocument *itemDocument, bool newItem, const char *id ) { return new PIC_IC( (ICNDocument*)itemDocument, newItem, id ); } LibraryItem* PIC_IC::libraryItem() { return new LibraryItem( QStringList(QString::fromLatin1("PIC_IC")), nullptr, nullptr, LibraryItem::lit_other, PIC_IC::construct ); } PIC_IC::PIC_IC( ICNDocument * icnDocument, bool newItem, const char * id ) : Component( icnDocument, newItem, id ? id : "PIC_IC" ) { } PIC_IC::~PIC_IC() { } void PIC_IC::initPackage( MicroInfo * microInfo ) { // The code in this function is a stripped down version of that in PICComponent::initPackage if (!microInfo) return; MicroPackage * microPackage = microInfo->package(); if (!microPackage) return; //BEGIN Get pin IDs QStringList allPinIDs = microPackage->pinIDs(); QStringList ioPinIDs = microPackage->pinIDs( PicPin::type_bidir | PicPin::type_input | PicPin::type_open ); // Now, we make the unwanted pin ids blank, so a pin is not created for them const QStringList::iterator allPinIDsEnd = allPinIDs.end(); for ( QStringList::iterator it = allPinIDs.begin(); it != allPinIDsEnd; ++it ) { if ( !ioPinIDs.contains(*it) ) *it = ""; } //END Get pin IDs //BEGIN Remove old stuff // Remove old text TextMap textMapCopy = m_textMap; const TextMap::iterator textMapEnd = textMapCopy.end(); for ( TextMap::iterator it = textMapCopy.begin(); it != textMapEnd; ++it ) removeDisplayText(it.key()); // Remove old nodes NodeInfoMap nodeMapCopy = m_nodeMap; const NodeInfoMap::iterator nodeMapEnd = nodeMapCopy.end(); for ( NodeInfoMap::iterator it = nodeMapCopy.begin(); it != nodeMapEnd; ++it ) { if ( !ioPinIDs.contains(it.key()) ) removeNode( it.key() ); } //END Remove old stuff //BEGIN Create new stuff initDIPSymbol( allPinIDs, 80 ); initDIP(allPinIDs); //END Create new stuff addDisplayText( "picid", QRect(offsetX(), offsetY()-16, width(), 16), microInfo->id() ); } //END class PIC_IC diff --git a/src/fpnode.cpp b/src/fpnode.cpp index 91ea2e62..7291de49 100644 --- a/src/fpnode.cpp +++ b/src/fpnode.cpp @@ -1,403 +1,403 @@ /*************************************************************************** * Copyright (C) 2003-2005 by David Saxton * * david@bluehaze.org * * * * 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 "connector.h" #include "flowconnector.h" #include "flowpart.h" #include "fpnode.h" #include "icndocument.h" -#include -#include +#include +#include FPNode::FPNode( ICNDocument *icnDocument, Node::node_type type, int dir, const QPoint &pos, QString *id ) : Node( icnDocument, type, dir, pos, id ) { if ( icnDocument ) icnDocument->registerItem(this); m_outputConnector = nullptr; } FPNode::~FPNode() { } FlowPart *FPNode::outputFlowPart() const { // for InputFlowNode this member is overridden if (!m_outputConnector) return nullptr; if( m_outputConnector->endNode() == nullptr) return nullptr; return (dynamic_cast(m_outputConnector->endNode()))->outputFlowPart(); } FlowPartList FPNode::inputFlowParts() const { // for InputFlowNode it's overridden FlowPartList list; FlowPart *flowPart = dynamic_cast(parentItem()); if( flowPart ) { list.append(flowPart); return list; } const FlowConnectorList::const_iterator end = m_inFlowConnList.end(); for ( FlowConnectorList::const_iterator it = m_inFlowConnList.begin(); it != end; ++it ) { if (*it) { Node *startNode = (*it)->startNode(); FlowPart *flowPart = startNode ? dynamic_cast(startNode->parentItem()) : nullptr; if (flowPart) list.append(flowPart); } } return list; } inline QPolygon arrowPoints( int dir ) { QPolygon pa(3); switch ( dir ) { case 0: pa[0] = QPoint( 3, 0 ); pa[1] = QPoint( 0, 2 ); pa[2] = QPoint( 0, -2 ); break; case 180: pa[0] = QPoint( -3, 0 ); pa[1] = QPoint( 0, 2 ); pa[2] = QPoint( 0, -2 ); break; case 90: pa[0] = QPoint( 2, 0 ); pa[1] = QPoint( -2, 0 ); pa[2] = QPoint( 0, 3 ); break; case 270: pa[0] = QPoint( 2, 0 ); pa[1] = QPoint( -2, 0 ); pa[2] = QPoint( 0, -3 ); break; }; return pa; } void FPNode::addOutputConnector( Connector * const connector ) { // for Junction and output flownodes if( !handleNewConnector(connector) ) return ; if( m_outputConnector) qCritical() << Q_FUNC_INFO << "BUG: adding an output connector when we already have one" << endl; // FIXME dynamic_cast connector m_outputConnector = dynamic_cast(connector); } void FPNode::addInputConnector( Connector * const connector ) { // for Junction and Input flownodes if( !handleNewConnector(connector) ) return; // FIXME dynamic_cast connector m_inFlowConnList.append( dynamic_cast (connector) ); } bool FPNode::handleNewConnector( Connector * connector ) { if (!connector) return false; // FIXME dynamic_cast connector if ( m_inFlowConnList.contains(dynamic_cast (connector) ) || ((Connector*)m_outputConnector == connector) ) { qWarning() << Q_FUNC_INFO << " Already have connector = " << connector << endl; return false; } connect( this, SIGNAL(removed(Node*)), connector, SLOT(removeConnector(Node*)) ); connect( connector, SIGNAL(removed(Connector*)), this, SLOT(checkForRemoval(Connector*)) ); connect( connector, SIGNAL(selected(bool)), this, SLOT(setNodeSelected(bool)) ); if ( !isChildNode() ) p_icnDocument->slotRequestAssignNG(); return true; } Connector* FPNode::createInputConnector( Node * startNode ) { if( (!acceptInput()) || !startNode ) return nullptr; // FIXME dynamic_cast used Connector *connector = new FlowConnector( dynamic_cast(startNode), dynamic_cast(this), p_icnDocument ); addInputConnector(connector); return connector; } int FPNode::numCon( bool includeParentItem, bool includeHiddenConnectors ) const { unsigned count = 0; FlowConnectorList connectors = m_inFlowConnList; if ( m_outputConnector ) connectors.append ( m_outputConnector ); FlowConnectorList::const_iterator end = connectors.end(); for ( FlowConnectorList::const_iterator it = connectors.begin(); it != end; ++it ) { if ( *it && ( includeHiddenConnectors || ( *it )->canvas() ) ) count++; } if ( isChildNode() && includeParentItem ) count++; return count; } void FPNode::removeConnector( Connector *connector ) { if (!connector) return; FlowConnectorList::iterator it; // FIXME dynamic_cast connector it = m_inFlowConnList.find( dynamic_cast(connector) ); if ( it != m_inFlowConnList.end() ) { (*it)->removeConnector(); (*it) = nullptr; } if((Connector *)m_outputConnector == connector) { connector->removeConnector(); m_outputConnector = nullptr; } } void FPNode::checkForRemoval( Connector *connector ) { removeConnector(connector); setNodeSelected(false); removeNullConnectors(); if (!p_parentItem) { int conCount = m_inFlowConnList.count(); if( m_outputConnector) conCount++; if ( conCount < 2 ) removeNode(); } // for JunctionFlowNode this method is overridden! } void FPNode::removeNullConnectors() { m_inFlowConnList.remove((FlowConnector*)nullptr); } QPoint FPNode::findConnectorDivergePoint( bool * found ) { bool temp; if (!found) found = &temp; *found = false; if ( numCon( false, false ) != 2 ) return QPoint(0,0); QPointList p1; QPointList p2; int inSize = m_inFlowConnList.count(); FlowConnectorList connectors = m_inFlowConnList; if(m_outputConnector) connectors.append(m_outputConnector); const FlowConnectorList::const_iterator end = connectors.end(); bool gotP1 = false; bool gotP2 = false; int at = -1; for ( FlowConnectorList::const_iterator it = connectors.begin(); it != end && !gotP2; ++it ) { at++; if ( !(*it) || !(*it)->canvas() ) continue; if (gotP1) { p2 = (*it)->connectorPoints( at < inSize ); gotP2 = true; } else { p1 = (*it)->connectorPoints( at < inSize ); gotP1 = true; } } if ( !gotP1 || !gotP2 ) return QPoint(0,0); unsigned maxLength = p1.size() > p2.size() ? p1.size() : p2.size(); for ( unsigned i = 1; i < maxLength; ++i ) { if ( p1[i] != p2[i] ) { *found = true; return p1[i-1]; } } return QPoint(0, 0); } void FPNode::setVisible( bool yes ) { if ( isVisible() == yes ) return; KtlQCanvasPolygon::setVisible(yes); const FlowConnectorList::iterator inputEnd = m_inFlowConnList.end(); for ( FlowConnectorList::iterator it = m_inFlowConnList.begin(); it != inputEnd; ++it ) { Connector *connector = *it; if ( connector ) { if ( isVisible() ) connector->setVisible ( true ); else { Node *node = connector->startNode(); connector->setVisible ( node && node->isVisible() ); } } } Connector *connector = m_outputConnector; if ( connector ) { if ( isVisible() ) connector->setVisible ( true ); else { Node *node = connector->endNode(); connector->setVisible ( node && node->isVisible() ); } } } bool FPNode::isConnected( Node *node, NodeList *checkedNodes ) { if ( this == node ) return true; bool firstNode = !checkedNodes; if (firstNode) checkedNodes = new NodeList(); else if ( checkedNodes->contains(this) ) return false; checkedNodes->append(this); const FlowConnectorList::const_iterator inputEnd = m_inFlowConnList.end(); for ( FlowConnectorList::const_iterator it = m_inFlowConnList.begin(); it != inputEnd; ++it ) { Connector *connector = *it; if (connector) { Node *startNode = connector->startNode(); if ( startNode && startNode->isConnected( node, checkedNodes ) ) { if (firstNode) { delete checkedNodes; } return true; } } } Connector *connector = m_outputConnector; if ( connector ) { Node *endNode = connector->endNode(); if ( endNode && endNode->isConnected ( node, checkedNodes ) ) { if ( firstNode ) { delete checkedNodes; } return true; } } if (firstNode) { delete checkedNodes; } return false; } ConnectorList FPNode::inputConnectorList() const { return (ConnectorList)(FlowConnectorList) m_inFlowConnList; } ConnectorList FPNode::outputConnectorList() const { ConnectorList out; if( m_outputConnector) out.append( (Connector *) m_outputConnector); // un upcast between downcasts :o return out; } ConnectorList FPNode::getAllConnectors() const { ConnectorList all = (ConnectorList)(FlowConnectorList)m_inFlowConnList; if ( m_outputConnector ) all.append ( (Connector *) m_outputConnector ); return all; } Connector* FPNode::getAConnector() const { if( ! m_inFlowConnList.isEmpty() ) return *m_inFlowConnList.begin(); if( m_outputConnector) return m_outputConnector; return nullptr; } diff --git a/src/gui/colorcombo.cpp b/src/gui/colorcombo.cpp index ff6bf66b..7046d509 100644 --- a/src/gui/colorcombo.cpp +++ b/src/gui/colorcombo.cpp @@ -1,202 +1,202 @@ /*************************************************************************** * Copyright (C) 2005 by David Saxton * * david@bluehaze.org * * * * 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 "colorcombo.h" #include -#include +#include #include -#include +#include bool ColorCombo::createdPalettes = false; QColor * ColorCombo::palette[ NumberOfSchemes ]; int ColorCombo::paletteSize[ NumberOfSchemes ]; ColorCombo::ColorCombo( ColorScheme colorScheme, QWidget *parent, const char *name ) : QComboBox( parent /*, name */ ) { setObjectName( name ); m_colorScheme = colorScheme; customColor.setRgb( 255, 255, 255 ); internalColor.setRgb( 255, 255, 255 ); createPalettes(); addColors(); connect( this, SIGNAL( activated(int) ), SLOT( slotActivated(int) ) ); connect( this, SIGNAL( highlighted(int) ), SLOT( slotHighlighted(int) ) ); } ColorCombo::~ColorCombo() { } void ColorCombo::createPalettes() { if ( createdPalettes ) return; createdPalettes = true; paletteSize[ QtStandard ] = 17; palette[ QtStandard ] = new QColor[ paletteSize[ QtStandard ] ]; int i = 0; palette[ QtStandard ][i++] = Qt::red; palette[ QtStandard ][i++] = Qt::green; palette[ QtStandard ][i++] = Qt::blue; palette[ QtStandard ][i++] = Qt::cyan; palette[ QtStandard ][i++] = Qt::magenta; palette[ QtStandard ][i++] = Qt::yellow; palette[ QtStandard ][i++] = Qt::darkRed; palette[ QtStandard ][i++] = Qt::darkGreen; palette[ QtStandard ][i++] = Qt::darkBlue; palette[ QtStandard ][i++] = Qt::darkCyan; palette[ QtStandard ][i++] = Qt::darkMagenta; palette[ QtStandard ][i++] = Qt::darkYellow; palette[ QtStandard ][i++] = Qt::white; palette[ QtStandard ][i++] = Qt::lightGray; palette[ QtStandard ][i++] = Qt::gray; palette[ QtStandard ][i++] = Qt::darkGray; palette[ QtStandard ][i++] = Qt::black; paletteSize[ LED ] = 6; palette[ LED ] = new QColor[ paletteSize[ LED ] ]; i = 0; palette[ LED ][i++] = "#f62a2a"; palette[ LED ][i++] = "#ff7733"; palette[ LED ][i++] = "#ffbb33"; palette[ LED ][i++] = "#eeee22"; palette[ LED ][i++] = "#4cc308"; palette[ LED ][i++] = "#22aaee"; } void ColorCombo::setColor( const QColor &col ) { internalColor = col; addColors(); } void ColorCombo::resizeEvent( QResizeEvent *re ) { QComboBox::resizeEvent( re ); addColors(); } void ColorCombo::slotActivated( int index ) { if ( index == 0 ) { const QColor selectedColor = QColorDialog::getColor(customColor, this); if (selectedColor.isValid()) { customColor = selectedColor; QPainter painter; QPen pen; QRect rect( 0, 0, width(), QFontMetrics(font()).height()+4); QPixmap pixmap( rect.width(), rect.height() ); if ( qGray( customColor.rgb() ) < 128 ) pen.setColor( Qt::white ); else pen.setColor( Qt::black ); const bool isSuccess = painter.begin( &pixmap ); if (!isSuccess) { qWarning() << Q_FUNC_INFO << " painter not active"; } QBrush brush( customColor ); painter.fillRect( rect, brush ); painter.setPen( pen ); painter.drawText( 2, QFontMetrics(painter.font()).ascent()+2, i18n("Custom...") ); painter.end(); setItemIcon(0, QIcon(pixmap) ); pixmap.detach(); } internalColor = customColor; } else internalColor = palette[ m_colorScheme ][ index - 1 ]; emit activated( internalColor ); } void ColorCombo::slotHighlighted( int index ) { if ( index == 0 ) internalColor = customColor; else internalColor = palette[ m_colorScheme ][ index - 1 ]; emit highlighted( internalColor ); } void ColorCombo::addColors() { QPainter painter; QPen pen; QRect rect( 0, 0, width(), QFontMetrics( font() ).height()+4 ); QPixmap pixmap( rect.width(), rect.height() ); int i; clear(); createPalettes(); for ( i = 0; i < paletteSize[ m_colorScheme ]; i++ ) if ( palette[ m_colorScheme ][i] == internalColor ) break; if ( i == paletteSize[ m_colorScheme ] ) customColor = internalColor; if ( qGray( customColor.rgb() ) < 128 ) pen.setColor( Qt::white ); else pen.setColor( Qt::black ); const bool isSuccess = painter.begin( &pixmap ); if (!isSuccess) { qWarning() << Q_FUNC_INFO << " painter not active"; } QBrush brush( customColor ); painter.fillRect( rect, brush ); painter.setPen( pen ); painter.drawText( 2, QFontMetrics(painter.font()).ascent()+2, i18n("Custom...") ); painter.end(); insertItem( count(), QIcon(pixmap), QString() ); pixmap.detach(); for ( i = 0; i < paletteSize[ m_colorScheme ]; i++ ) { painter.begin( &pixmap ); QBrush brush( palette[ m_colorScheme ][i] ); painter.fillRect( rect, brush ); painter.end(); insertItem( count(), QIcon( pixmap ), QString() ); pixmap.detach(); if ( palette[ m_colorScheme ][i] == internalColor ) { setCurrentIndex( i + 1 ); } } } diff --git a/src/gui/colorcombo.h b/src/gui/colorcombo.h index 9403b611..6a7e2397 100644 --- a/src/gui/colorcombo.h +++ b/src/gui/colorcombo.h @@ -1,81 +1,81 @@ /*************************************************************************** * Copyright (C) 2005 by David Saxton * * david@bluehaze.org * * * * 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. * ***************************************************************************/ #ifndef COLORCOMBO_H #define COLORCOMBO_H -#include -#include +#include +#include /** Based on KColorCombo, Copyright (C) 1997 Martin Jones (mjones@kde.org). Allows which colours are displayed to be changed. @author David Saxton */ class ColorCombo : public QComboBox { Q_OBJECT Q_PROPERTY( QColor color READ color WRITE setColor ) public: enum ColorScheme { QtStandard = 0, LED = 1, NumberOfSchemes = 2 ///< for internal usage; this should be one less than the number of items in the enum }; /** * Constructs a color combo box. */ ColorCombo( ColorScheme colorScheme, QWidget *parent, const char *name = nullptr ); ~ColorCombo() override; /** * Returns the currently selected color. **/ QColor color() const { return internalColor; } public slots: /** * Selects the color @p col. */ void setColor( const QColor & col ); signals: /** * Emitted when a new color box has been selected. */ void activated( const QColor &col ); /** * Emitted when a new item has been highlighted. */ void highlighted( const QColor &col ); protected slots: void slotActivated( int index ); void slotHighlighted( int index ); protected: void resizeEvent( QResizeEvent *re ) override; void addColors(); void createPalettes(); QColor customColor; QColor internalColor; ColorScheme m_colorScheme; static bool createdPalettes; static QColor * palette[ NumberOfSchemes ]; static int paletteSize[ NumberOfSchemes ]; }; #endif diff --git a/src/gui/contexthelp.cpp b/src/gui/contexthelp.cpp index 9c799145..717d2b95 100644 --- a/src/gui/contexthelp.cpp +++ b/src/gui/contexthelp.cpp @@ -1,516 +1,514 @@ /*************************************************************************** * Copyright (C) 2003-2006 by David Saxton * * david@bluehaze.org * * * * 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 "cnitem.h" #include "cnitemgroup.h" #include "contexthelp.h" #include "docmanager.h" #include "itemlibrary.h" #include "itemselector.h" #include "katemdi.h" #include "libraryitem.h" #include "richtexteditor.h" #include #include #include #include #include #include #include #include // #include #include // #include #include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include +#include +#include +#include +#include +#include +#include +#include +#include +#include // #include -#include +#include #include #include ContextHelp * ContextHelp::m_pSelf = nullptr; ContextHelp * ContextHelp::self( KateMDI::ToolView * parent ) { if (!m_pSelf) { assert(parent); m_pSelf = new ContextHelp(parent); } return m_pSelf; } ContextHelp::ContextHelp( KateMDI::ToolView * parent ) : QWidget(parent), Ui::ContextHelpWidget( /* parent */ ) { setupUi(this); setWhatsThis( i18n("Provides context-sensitive help relevant to the current editing being performed.") ); setAcceptDrops( true ); if (parent->layout()) { parent->layout()->addWidget(this); qDebug() << Q_FUNC_INFO << " added item selector to parent's layout " << parent; } else { qWarning() << Q_FUNC_INFO << " unexpected null layout on parent " << parent ; } QFont font; font.setBold( true ); if ( font.pointSize() != 0 ) font.setPointSize( int(font.pointSize() * 1.4) ); m_pNameLabel->setFont( font ); m_pNameLabel->setTextFormat( Qt::RichText ); m_pBrowser = new KHTMLPart( m_pWidgetStack->widget( 0 ) ); m_pBrowserView = m_pBrowser->view(); m_pBrowserView->setFocusPolicy( Qt::NoFocus ); m_pBrowserLayout->addWidget( m_pBrowserView ); connect(m_pBrowser->browserExtension(), &KParts::BrowserExtension::openUrlRequest, this, &ContextHelp::openURL); // Adjust appearance of browser m_pBrowserView->setMarginWidth( 4 ); m_pEditor = new RichTextEditor( m_pWidgetStack->widget( 1 ), "ContextHelpEditor" ); m_pTopLayout->addWidget( m_pEditor ); m_pEditor->installEventFilter( this ); m_pEditor->editorViewport()->installEventFilter( this ); slotClear(); connect( m_pEditButton, SIGNAL(clicked()), this, SLOT(slotEdit()) ); connect( m_pSaveButton, SIGNAL(clicked()), this, SLOT(slotSave()) ); connect( m_pResetButton, SIGNAL(clicked()), this, SLOT(slotEditReset()) ); connect( m_pChangeDescriptionsDirectory, SIGNAL(clicked()), this, SLOT(requestItemDescriptionsDirectory()) ); connect( m_pLanguageSelect, QOverload::of(&QComboBox::activated), this, &ContextHelp::setCurrentLanguage); m_pResetButton->setIcon( QIcon::fromTheme( "dialog-cancel" ) ); m_pChangeDescriptionsDirectory->setIcon( QIcon::fromTheme( "folder" ) ); connect( ComponentSelector::self(), SIGNAL(itemSelected( const QString& )), this, SLOT(setBrowserItem( const QString& )) ); connect( FlowPartSelector::self(), SIGNAL(itemSelected( const QString& )), this, SLOT(setBrowserItem( const QString& )) ); #ifdef MECHANICS connect( MechanicsSelector::self(), SIGNAL(itemSelected( const QString& )), this, SLOT(setBrowserItem( const QString& )) ); #endif QTimer::singleShot( 10, this, SLOT(slotInitializeLanguageList()) ); } ContextHelp::~ContextHelp() { } bool ContextHelp::eventFilter( QObject * watched, QEvent * e ) { // qDebug() << Q_FUNC_INFO << "watched="<editorViewport()) ) return false; switch ( e->type() ) { case QEvent::DragEnter: { QDragEnterEvent * dragEnter = static_cast(e); if ( !dragEnter->mimeData()->text().startsWith("ktechlab/") ) break; //dragEnter->acceptAction(); // 2018.12.07 dragEnter->acceptProposedAction(); return true; } case QEvent::Drop: { QDropEvent * dropEvent = static_cast(e); const QMimeData * mimeData = dropEvent->mimeData(); if ( !mimeData->text().startsWith("ktechlab/") ) break; dropEvent->accept(); QString type; QDataStream stream( mimeData->data( mimeData->text() ) /*, QIODevice::ReadOnly */ ); stream >> type; LibraryItem * li = itemLibrary()->libraryItem( type ); if ( !li ) return true; m_pEditor->insertURL( "ktechlab-help:///" + type, li->name() ); return true; } default: break; } return false; } void ContextHelp::slotInitializeLanguageList() { const QStringList descriptionLanguages = itemLibrary()->descriptionLanguages(); for (const QString& languageCode : descriptionLanguages) { QString text = languageCode; QLocale locale(languageCode); if (locale != QLocale::c()) { text = locale.nativeLanguageName(); // For some languages the native name might be empty. // In this case use the non native language name as fallback. // See: QTBUG-51323 if (text.isEmpty()) { text = QLocale::languageToString(locale.language()); } } m_pLanguageSelect->addItem(text, languageCode); } m_currentLanguage = QLocale().name(); const int currentIndex = m_pLanguageSelect->findData(m_currentLanguage); m_pLanguageSelect->setCurrentIndex(currentIndex); } bool ContextHelp::isEditChanged() { if ( m_lastItemType.isEmpty() ) return false; // Is the edit widget raised? if ( m_pWidgetStack->currentIndex() != 1 ) { return false; } // We are currently editing an item. Is it changed? return ( m_pEditor->text() != itemLibrary()->description( m_lastItemType, m_currentLanguage ).trimmed() ); } void ContextHelp::slotUpdate( Item * item ) { if ( isEditChanged() ) { return; } m_lastItemType = item ? item->type() : QString::null; m_pEditButton->setEnabled( item ); if ( !item ) { slotClear(); return; } m_pWidgetStack->setCurrentIndex( 0 ); setContextHelp(item->name(), itemLibrary()->description(m_lastItemType, QLocale().name())); } void ContextHelp::setBrowserItem( const QString & type ) { if ( isEditChanged() ) return; QString description = itemLibrary()->description(type, QLocale().name()); if ( description.isEmpty() ) return; QString name; LibraryItem * li = itemLibrary()->libraryItem( type ); if ( li ) name = li->name(); else name = type; m_lastItemType = type; setContextHelp( name, description ); m_pEditButton->setEnabled( true ); } void ContextHelp::slotClear() { setContextHelp( i18n("No Item Selected"), nullptr ); m_pEditButton->setEnabled( false ); // Can we go hide the edit widget? if ( !isEditChanged() ) m_pWidgetStack->setCurrentIndex( 0 ); } void ContextHelp::slotMultipleSelected() { setContextHelp( i18n("Multiple Items"), nullptr ); } void ContextHelp::setContextHelp( QString name, QString help ) { //BEGIN modify help string as appropriate help = help.trimmed(); parseInfo( help ); RichTextEditor::makeUseStandardFont( & help ); addLinkTypeAppearances( & help ); //END modify help string as appropriate // HACK Adjust top spacing according to whether the item description uses

. // This is because the help editor uses paragraphs, but old item help stored // in the items just uses
QFont f; int fontPixelSize = QFontInfo( f ).pixelSize(); if ( help.contains( "

" ) ) m_pBrowserView->setMarginHeight( 3-fontPixelSize ); else m_pBrowserView->setMarginHeight( 3 ); m_pNameLabel->setText( name ); #warning "m_pBrowser->write() disabled, crashes on m_pBrowser->end()" // m_pBrowser->begin( QUrl::fromLocalFile( itemLibrary()->itemDescriptionsDirectory() ) ); // m_pBrowser->write( help ); // m_pBrowser->end(); } void ContextHelp::parseInfo( QString &info ) { info.replace("","

Example:

"); info.replace("","
"); } void ContextHelp::slotEdit() { if ( m_lastItemType.isEmpty() ) return; QStringList resourcePaths; QString currentResourcePath = itemLibrary()->itemDescriptionsDirectory(); QString defaultResourcePath = QStandardPaths::locate( QStandardPaths::AppDataLocation, "contexthelp/", QStandardPaths::LocateDirectory); resourcePaths << currentResourcePath; if ( currentResourcePath != defaultResourcePath ) resourcePaths << defaultResourcePath; m_pEditor->setResourcePaths( resourcePaths ); QString description = itemLibrary()->description( m_lastItemType, m_currentLanguage ); m_pEditor->setText( description ); m_pWidgetStack->setCurrentIndex( 1 ); } void ContextHelp::setCurrentLanguage(int languageIndex ) { const QString language = m_pLanguageSelect->itemData(languageIndex).toString(); if ( !saveDescription( m_currentLanguage ) ) { m_pLanguageSelect->blockSignals( true ); const int currentIndex = m_pLanguageSelect->findData(m_currentLanguage); m_pLanguageSelect->setCurrentIndex(currentIndex); m_pLanguageSelect->blockSignals( false ); return; } m_currentLanguage = language; slotEdit(); } void ContextHelp::requestItemDescriptionsDirectory() { const QString path = QFileDialog::getExistingDirectory(nullptr, QString(), itemLibrary()->itemDescriptionsDirectory()); if (!path.isEmpty()) { itemLibrary()->setItemDescriptionsDirectory(path); } } void ContextHelp::slotEditReset() { if ( isEditChanged() ) { KGuiItem continueItem = KStandardGuiItem::cont(); //KStandardGuiItem::cont(); continueItem.setText( i18n("Reset") ); int answer = KMessageBox::warningContinueCancel( this, i18n("Reset item help to last saved changes?"), i18n("Reset"), continueItem ); if ( answer == KMessageBox::Cancel ) return; } m_pWidgetStack->setCurrentIndex( 0 ); } void ContextHelp::slotSave() { if ( !saveDescription( m_currentLanguage ) ) return; setContextHelp( m_pNameLabel->text(), itemLibrary()->description( m_lastItemType, QLocale().name() ) ); m_pWidgetStack->setCurrentIndex( 0 ); } bool ContextHelp::saveDescription( const QString & language ) { if ( m_lastItemType.isEmpty() ) { KMessageBox::sorry( nullptr, i18n("Cannot save item description.") ); return false; } return itemLibrary()->setDescription( m_lastItemType, m_pEditor->text(), language ); } // static function void ContextHelp::addLinkTypeAppearances( QString * html ) { QRegExp rx("([^<]*)"); int pos = 0; while ( (pos = rx.indexIn( *html, pos )) >= 0 ) { QString anchorText = rx.cap( 0 ); // contains e.g.: KTechlab website const QString urlString = rx.cap( 1 ); // contains e.g.: http://ktechlab.org/ QString text = rx.cap( 2 ); // contains e.g.: KTechlab website int length = anchorText.length(); const QUrl url(urlString); LinkType lt = extractLinkType( url ); QColor color; // default constructor gives an "invalid" color QString imageURL; switch ( lt ) { case HelpLink: break; case NewHelpLink: color = Qt::red; break; case ExampleLink: { //QString iconName = KMimeType::iconNameForURL( examplePathToFullPath( KUrl( url ).path() ) ); QString iconName = KIO::iconNameForUrl(QUrl::fromLocalFile(examplePathToFullPath(url.path()))); imageURL = KIconLoader::global()->iconPath( iconName, - KIconLoader::SizeSmall ); break; } case ExternalLink: { imageURL = QStandardPaths::locate(QStandardPaths::AppDataLocation, "icons/external_link.png" ); break; } } QString newAnchorText; if ( color.isValid() ) { newAnchorText = QString("%3").arg(urlString, color.name(), text); } else if ( !imageURL.isEmpty() ) { newAnchorText = anchorText; newAnchorText += QString(" ").arg( imageURL ); } if ( !newAnchorText.isEmpty() ) html->replace( pos, length, newAnchorText ); pos++; // avoid the string we just found } } // static function ContextHelp::LinkType ContextHelp::extractLinkType( const QUrl & url ) { QString path = url.path(); if ( url.scheme() == "ktechlab-help" ) { if ( itemLibrary()->haveDescription(path, QLocale().name())) return HelpLink; else return NewHelpLink; } if ( url.scheme() == "ktechlab-example" ) return ExampleLink; return ExternalLink; } // static function QString ContextHelp::examplePathToFullPath( QString path ) { // quick security check path.remove( ".." ); if ( path.startsWith("/") ) path.remove( 0, 1 ); return QStandardPaths::locate(QStandardPaths::AppDataLocation, "examples/" + path); } void ContextHelp::openURL( const QUrl & url /*, const KParts::OpenUrlArguments & */ ) { QString path = url.path(); switch ( extractLinkType( url ) ) { case HelpLink: case NewHelpLink: setBrowserItem( path ); break; case ExampleLink: DocManager::self()->openURL(QUrl::fromLocalFile(examplePathToFullPath(path))); break; case ExternalLink: { // external url KRun * r = new KRun( url, this ); connect( r, SIGNAL(finished()), r, SLOT(deleteLater()) ); connect( r, SIGNAL(error()), r, SLOT(deleteLater()) ); break; } } } diff --git a/src/gui/contexthelp.h b/src/gui/contexthelp.h index 82ac0a74..fe992deb 100644 --- a/src/gui/contexthelp.h +++ b/src/gui/contexthelp.h @@ -1,144 +1,144 @@ /*************************************************************************** * Copyright (C) 2003-2006 by David Saxton * * david@bluehaze.org * * * * 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. * ***************************************************************************/ #ifndef CONTEXTHELP_H #define CONTEXTHELP_H #include -#include +#include #include class Item; class ContextHelp; class RichTextEditor; class KHTMLPart; class KHTMLView; class QUrl; class QLabel; class QTextBrowser; class QWidgetStack; namespace KateMDI { class ToolView; } namespace KParts { class URLArgs; } /** Sidebar that provides context-sensitive help for whatever the user is currently helping (e.g. pinouts, command references, etc). Not to be confused with ItemEditor, which which allows editing of data specific to the selected CNItem in a ICNDocument. @author David Saxton */ class ContextHelp : public QWidget, public Ui::ContextHelpWidget { Q_OBJECT public: static ContextHelp * self( KateMDI::ToolView * parent = nullptr ); static QString toolViewIdentifier() { return "ContextHelp"; } ~ContextHelp() override; /** * Replace special tags with appropriate html formatting code. */ void parseInfo( QString &info ); /** * Used as an event filter in context help. */ bool eventFilter( QObject * watched, QEvent * e ) override; public slots: void slotClear(); void slotMultipleSelected(); void slotUpdate( Item * item ); void setContextHelp( QString name, QString help ); /** * Set the help browser to the given location. */ void setBrowserItem( const QString & type ); void openURL( const QUrl& url /*, const KParts::OpenUrlArguments& */ ); protected slots: /** * Called when the user clicks the "Edit" button. */ void slotEdit(); /** * Called when the user clicks the "Save" button. */ void slotSave(); /** * Called when the user clicks the "Reset" button. */ void slotEditReset(); /** * Called from the language select combo when the current selection * changes. */ void setCurrentLanguage(int languageIndex); /** * Request a directory from the user for storing the context help in. */ void requestItemDescriptionsDirectory(); protected: enum LinkType { HelpLink, ///< Context help item (that exists) NewHelpLink, ///< Context help item that doesn't yet exist ExampleLink, ///< Example circuit or flowcode ExternalLink ///< External website, etc }; /** * Looks at url and tries to determine the link type. Will return * ExternalLink if the url can not be identified as any other type. */ static LinkType extractLinkType( const QUrl & url ); /** * Adjusts the appearance of links depending on their LinkType (e.g * external links are given an "external" icon, and new help links are * colored in red. */ static void addLinkTypeAppearances( QString * html ); /** * @return the physical location of the example file from an example url * (e.g. "/mosfets/and.circuit" might return * "/usr/share/apps/ktechlab/mosfets/and.circuit"). */ static QString examplePathToFullPath( QString path ); /** * Saves the current editor text for the given language. * @return if all ok (e.g. if the file could be written successfully). */ bool saveDescription( const QString & language ); bool isEditChanged(); QString m_currentLanguage; QString m_lastItemType; KHTMLPart * m_pBrowser; KHTMLView * m_pBrowserView; RichTextEditor * m_pEditor; private slots: /** * This has to be called after itemlibrary has constructed itself * and a list of languages (i.e. a little bit after the constructor). */ void slotInitializeLanguageList(); private: ContextHelp( KateMDI::ToolView * parent ); static ContextHelp * m_pSelf; }; #endif diff --git a/src/gui/doublespinbox.cpp b/src/gui/doublespinbox.cpp index 6ac50230..8e41093f 100644 --- a/src/gui/doublespinbox.cpp +++ b/src/gui/doublespinbox.cpp @@ -1,515 +1,515 @@ /*************************************************************************** * Copyright (C) 2003-2006 by David Saxton * * david@bluehaze.org * * * * 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 "cnitem.h" #include "doublespinbox.h" -#include #include +#include #include -#include -#include -#include -#include +#include +#include +#include +#include #include #include using namespace std; static bool isDoubleSpinboxDebugEnabled() { return false; // note: in the future, loggers should be used } static QString DoubleSpinBox_nullDebug; static QDebug DoubleSpinbox_qDebug() { if (isDoubleSpinboxDebugEnabled()) { return qDebug(); } else { DoubleSpinBox_nullDebug.clear(); return QDebug( &DoubleSpinBox_nullDebug ); } } inline int roundDouble( double val ) { return (val > 0) ? int(val+0.5) : int(val-0.5); } DoubleSpinBox::DoubleSpinBox( double lower, double upper, double minAbs, double value, const QString &unit, QWidget * parent ) : QDoubleSpinBox( parent ) { DoubleSpinbox_qDebug() << " lower=" << lower << " upper=" << upper << " minAbs=" << minAbs << " value=" << value << " unit=" << unit << " parent=" << parent; setDecimals(20); // should be enough // m_lastEmittedValue = value; m_unit = unit; // m_minValue = lower; setMinimum( lower ); // m_maxValue = upper; setMaximum( upper ); m_minAbsValue = minAbs; // m_queuedSuffix = QString::null; init(); setValue( value ); } DoubleSpinBox::DoubleSpinBox( QWidget * parent ) : QDoubleSpinBox( parent ) { setDecimals(20); // should be enough m_lastEmittedValue = 0; // m_minValue = 0; setMinimum( 0 ); // m_maxValue = 1e9; setMaximum( 1e9 ); m_minAbsValue = 1e-9; // m_queuedSuffix = QString::null; init(); setValue( 0 ); } void DoubleSpinBox::init() { lineEdit()->setAlignment( Qt::AlignRight ); // connect( this, SIGNAL(valueChanged(double)), this, SLOT(checkIfChanged()) ); // QSpinBox::setMinValue( -(1<<30) ); // QSpinBox::setMaxValue( +(1<<30) ); // setValidator( 0 ); // apparently in Qt4 there is no validator } DoubleSpinBox::~DoubleSpinBox() { } QValidator::State DoubleSpinBox::validate(QString& text, int& pos) const { DoubleSpinbox_qDebug() << Q_FUNC_INFO << "text = |" << text << "| pos= " << pos; return QValidator::Acceptable; // QValidator::Intermediate; // don't bother } // double DoubleSpinBox::value() // { // const double mult = getMult(); // const double displatedNumber = getDisplayedNumber( 0 ); // const double toRet = displatedNumber * mult ; // DoubleSpinbox_qDebug() << "value() mult = " << mult; // DoubleSpinbox_qDebug() << "value() displatedNumber = " << displatedNumber; // DoubleSpinbox_qDebug() << "value() toRet = " << toRet; // return toRet; // } // void DoubleSpinBox::setValue( double value ) // { // if ( this->value() == value ) // return; // // if ( value > maxValue() ) // value = maxValue(); // // else if ( value < minValue() ) // value = minValue(); // // if ( std::abs(value) < m_minAbsValue*0.9999 ) // value = 0.0; // // updateSuffix( value ); // // const int toBeStoredValue = roundDouble( (value / Item::getMultiplier( value )) /* * 100 */ ); // // DoubleSpinbox_qDebug() << "value = " << value; // DoubleSpinbox_qDebug() << "value() = " << QSpinBox::value(); // DoubleSpinbox_qDebug() << "to be stored = " << toBeStoredValue; // // QSpinBox::setValue( toBeStoredValue ); // } // void DoubleSpinBox::setUnit( const QString & unit ) // { // updateSuffix( value() ); // m_unit = unit; // } void DoubleSpinBox::updateSuffix( double value ) { QString nextSuffix = " " + CNItem::getNumberMag( value ) + m_unit; setSuffix( nextSuffix ); // m_queuedSuffix = " " + CNItem::getNumberMag( value ) + m_unit; // // Set suffix to be empty if it is nothing but white space // if ( m_queuedSuffix.trimmed().isEmpty() ) // m_queuedSuffix = ""; // // QTimer::singleShot( 0, this, SLOT(setQueuedSuffix()) ); } // void DoubleSpinBox::setQueuedSuffix() // { // bool changed = false; // if ( !m_queuedSuffix.isNull() && suffix().simplifyWhiteSpace() != m_queuedSuffix.simplifyWhiteSpace() ) // { // setSuffix( m_queuedSuffix ); // changed = true; // } // m_queuedSuffix = QString::null; // // if ( changed ) // emit valueChanged( value() ); // } DoubleSpinBox::StepEnabled DoubleSpinBox::stepEnabled () const { return QDoubleSpinBox::StepDownEnabled | QDoubleSpinBox::StepUpEnabled; } void DoubleSpinBox::stepBy(int steps) { double workVal = value(); while (steps != 0) { if (steps > 0) { workVal = getNextUpStepValue( workVal ); steps--; } else { workVal = getNextDownStepValue( workVal ); steps++; } } setValue( workVal ); } double DoubleSpinBox::getNextUpStepValue( double in ) { DoubleSpinbox_qDebug() << Q_FUNC_INFO << " in = " << in; double value = roundToOneSF( in ); if ( value == 0 ) { value = m_minAbsValue; } else if ( value > 0 ) { value += std::pow( 10., std::floor( std::log10(value) ) ); } else { double sub = std::pow(10., std::floor( std::log10(std::abs(value))-1) ); value += std::pow( 10., std::floor( std::log10(std::abs(value)-sub) ) ); } value *= 1.00001; if ( std::abs(value) < m_minAbsValue ) { value = 0.; } DoubleSpinbox_qDebug() << Q_FUNC_INFO << " out = " << value; return value; } double DoubleSpinBox::getNextDownStepValue( double in ) { DoubleSpinbox_qDebug() << Q_FUNC_INFO << " in = " << in; double value = roundToOneSF( in ); if ( value == 0 ) { value = -m_minAbsValue; } else if ( value > 0 ) { double sub = std::pow(10., std::floor( std::log10(value)-1) ); value -= std::pow( 10., std::floor( std::log10(value-sub) ) ); } else { double add = std::pow(10., std::floor( std::log10(std::abs(value))-1) ); value -= std::pow( 10., std::floor( std::log10(std::abs(value)+add) ) ); } value *= 1.00001; if ( std::abs(value) < m_minAbsValue ) { value = 0.; } DoubleSpinbox_qDebug() << Q_FUNC_INFO << " out = " << value; return value; } // double DoubleSpinBox::getMult() // { // QString text = this->text().trimmed(); // if ( !m_queuedSuffix.isNull() ) // { // QString nsSuffix = suffix().trimmed(); // // if ( nsSuffix.isEmpty() ) // text.append( m_queuedSuffix ); // else // text.replace( nsSuffix, m_queuedSuffix ); // } // // if ( text.length() == 0 ) // return 1.0; // // if ( text.endsWith( m_unit, false ) ) // text = text.remove( text.length() - m_unit.length(), m_unit.length() ); // // text.trimmed(); // // QChar siExp = text[ text.length()-1 ]; // // if ( siExp.isLetter() || siExp.isSymbol() ) // return CNItem::getMultiplier((QString)siExp); // // else // return 1; // } // double DoubleSpinBox::getDisplayedNumber( bool * ok ) // { // KLocale * locale = KGlobal::locale(); // // //Fetch the characters that we don't want to discard // const QString exclude = locale->decimalSymbol() // + locale->thousandsSeparator() // + locale->positiveSign() // + locale->negativeSign(); // // QString number = cleanText().remove( QRegExp("[^"+exclude+"\\d]") ); // // return locale->readNumber( number, ok ); // } // int DoubleSpinBox::mapTextToValue( bool * ok ) // { // (void)ok; // // double value = this->value(); // // if ( value > maxValue() ) // value = maxValue(); // // else if ( value < minValue() ) // value = minValue(); // // if ( std::abs(value) < m_minAbsValue*0.9999 ) // value = 0.0; // // updateSuffix( value ); // // value /= Item::getMultiplier( value ); // // Precision of 2 extra digits // return int( value /* * 100 */ ); // } // double DoubleSpinBox::valueFromText( const QString & text ) const { DoubleSpinbox_qDebug() << Q_FUNC_INFO << "text = " << text; QLocale locale; // Fetch the characters that we don't want to discard const QString exclude = QString(locale.decimalPoint()) + locale.groupSeparator() + locale.positiveSign() + locale.negativeSign(); QString textToStrip( text ); QString numberToRead = textToStrip.remove( QRegExp("[^"+exclude+"\\d]") ); bool ok; double value = locale.toDouble(numberToRead, &ok); if (!ok) { DoubleSpinbox_qDebug() << Q_FUNC_INFO << "numberToRead = |" << numberToRead << "| NOT OK"; value = 0; } DoubleSpinbox_qDebug() << Q_FUNC_INFO << "numberToRead = " << numberToRead << ", value = " << value; if ( value > maximum() ) { value = maximum(); } else if ( value < minimum() ) { value = minimum(); } if ( std::abs(value) < m_minAbsValue*0.9999 ) { value = 0.0; } double multiplier = 1.0; //updateSuffix( value ); QString textForSuffix( text ); if ( textForSuffix.length() != 0 ) { if ( textForSuffix.endsWith( m_unit, Qt::CaseInsensitive ) ) { textForSuffix = textForSuffix.remove( textForSuffix.length() - m_unit.length(), m_unit.length() ); } textForSuffix = textForSuffix.trimmed(); QChar siExp; if (textForSuffix.length() > 0) { siExp = textForSuffix[ textForSuffix.length()-1 ]; } else { siExp = '1'; } DoubleSpinbox_qDebug() << Q_FUNC_INFO << "SI exp = " << siExp; if ( siExp.isLetter() || siExp.isSymbol() ) { multiplier = CNItem::getMultiplier( QString(siExp) ); } else { multiplier = 1; } } DoubleSpinbox_qDebug() << Q_FUNC_INFO << "multiplier = " << multiplier; //value /= Item::getMultiplier( value ); value *= multiplier; DoubleSpinbox_qDebug() << Q_FUNC_INFO << "value = " << value; return value; } // // QString DoubleSpinBox::mapValueToText( int v ) // { // double val = double(v) /* /100.0 */; // // int leftDigits = (int)floor( log10( abs(val) ) ) + 1; // if ( leftDigits < 0 ) // leftDigits = 0; // else if ( leftDigits > 3 ) // leftDigits = 3; // // KLocale * locale = KGlobal::locale(); // return locale->formatNumber( val, 3-leftDigits ); // } QString DoubleSpinBox::textFromValue(double value) const { DoubleSpinbox_qDebug() << Q_FUNC_INFO << " value = " << value; // int leftDigits = (int)floor( log10( abs( value ) ) ) + 1; // if ( leftDigits < 0 ) { // leftDigits = 0; // } else if ( leftDigits > 3 ) { // leftDigits = 3; // } double multiplier = Item::getMultiplier( value ); double toDisplayNr = value / multiplier; DoubleSpinbox_qDebug() << Q_FUNC_INFO << "toDisplayNr = " << toDisplayNr; QString numberStr = QLocale().toString( toDisplayNr, 'f', 0 /* 3-leftDigits */ ); QString magStr = Item::getNumberMag( value ); QString toRet = numberStr + " " + magStr + m_unit; DoubleSpinbox_qDebug() << Q_FUNC_INFO << " text = " << toRet; return toRet; } // void DoubleSpinBox::checkIfChanged() // { // double newValue = value(); // // if ( m_lastEmittedValue == newValue ) // return; // // m_lastEmittedValue = newValue; // emit valueChanged( m_lastEmittedValue ); // } double DoubleSpinBox::roundToOneSF( double value ) { if ( value == 0.0 ) return 0.0; value *= 1.000001; double tens = pow( 10.0, floor(log10( abs(value) )) ); return int ( value / tens ) * tens; } // void DoubleSpinBox::stepUp() // { // double value = roundToOneSF( this->value() ); // // if ( value == 0 ) // value = m_minAbsValue; // // else if ( value > 0 ) // value += std::pow( 10., std::floor( std::log10(value) ) ); // // else // { // double sub = std::pow(10., std::floor( std::log10(std::abs(value))-1) ); // value += std::pow( 10., std::floor( std::log10(std::abs(value)-sub) ) ); // } // // value *= 1.00001; // // if ( std::abs(value) < m_minAbsValue ) // value = 0.; // // setValue( value ); // } // void DoubleSpinBox::stepDown() // { // double value = roundToOneSF( this->value() ); // // if ( value == 0 ) // value = -m_minAbsValue; // // else if ( value > 0 ) // { // double sub = std::pow(10., std::floor( std::log10(value)-1) ); // value -= std::pow( 10., std::floor( std::log10(value-sub) ) ); // } // else // { // double add = std::pow(10., std::floor( std::log10(std::abs(value))-1) ); // value -= std::pow( 10., std::floor( std::log10(std::abs(value)+add) ) ); // } // // value *= 1.00001; // // if ( std::abs(value) < m_minAbsValue ) // value = 0.; // // setValue( value ); // } diff --git a/src/gui/doublespinbox.h b/src/gui/doublespinbox.h index 4eb1ed4e..e0d9b0dd 100644 --- a/src/gui/doublespinbox.h +++ b/src/gui/doublespinbox.h @@ -1,150 +1,150 @@ /*************************************************************************** * Copyright (C) 2003-2006 by David Saxton * * david@bluehaze.org * * * * 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. * ***************************************************************************/ #ifndef DOUBLESPINBOX_H #define DOUBLESPINBOX_H -#include +#include /** Where appropriate, function names with value in them should be prefixed with "real" - e.g. realValue() - to get the value stored in the spin box plus the SI magnitude symbol it is showing @author David Saxton */ class DoubleSpinBox : public QDoubleSpinBox { Q_OBJECT public: DoubleSpinBox( double lower, double upper, double minAbs, double value, const QString & unit, QWidget * parent = nullptr ); DoubleSpinBox( QWidget * parent = nullptr ); ~DoubleSpinBox() override; /** * The minimum value is the lowest number that the user can enter. */ // double minValue() const { return m_minValue; } /** * @see minValue */ // void setMinValue( double minValue ) { m_minValue = minValue; } /** * The minimum value is the lowest number that the user can enter. */ // void setMinValue( int minValue ) { m_minValue = minValue; } /** * The maximum value is the highest number that the user can enter. */ // double maxValue() const { return m_maxValue; } /** * @see maxValue */ // void setMaxValue( double maxValue ) { m_maxValue = maxValue; } /** * @see maxValue */ // void setMaxValue( int maxValue ) { m_maxValue = maxValue; } /** * The minimum absolute value is the smallest value that the user can * enter before the value is considered 0. */ void setMinAbsValue( double minAbsValue ) { m_minAbsValue = minAbsValue; } /** * The actual value that the user has entered - e.g. if the spinbox * displays "100 kF", then the value returned will be 1e5. */ // double value(); /** * Sets the unit used, e.g. "F" */ // void setUnit( const QString & unit ); QValidator::State validate( QString & text, int & pos ) const override ; public slots: // virtual void stepUp(); // QDoubleSpinBox has these // virtual void stepDown(); /** * Set the value to be displayed - e.g. if value is 1e5, then the * spinbox might display "100 kF". */ // void setValue( double value ); signals: /** * This value is emitted whenever the value of the spinbox changes. */ // void valueChanged( double value ); // exists in QDoubleSpinBox protected slots: /** * Checks if the value has changed - and if so, emits a valueChanged * signal. */ // void checkIfChanged(double value); /** * Sets the suffix from m_queuedSuffix. Called from QTimer::singleShot * to avoid strange recursion problems. */ // void setQueuedSuffix(); protected: /** * make Qt enable the up/down step arrows */ StepEnabled stepEnabled () const override ; /** * Change the value of the spin box, because of user interaction */ void stepBy( int steps ) override; double getNextUpStepValue( double in ); double getNextDownStepValue( double in ); /** * Updates the suffix using m_unit and value. */ void updateSuffix( double value ); /** * Returns the multiplication number from what is displayed * in the box, e.g. "10 kV" will return "1000" due to the letter "k" presence */ // double getMult(); /** * Returns the number currently displayed in the spin box. */ // double getDisplayedNumber( bool * ok ); /** * Overloaded the method in QSpinxBox to allow SI prefixes to be entered */ // virtual int mapTextToValue( bool * ok ); double valueFromText( const QString & text ) const override ; /** * Overloaded the method in QSpinxBox to allow SI prefixes to be entered */ // virtual QString mapValueToText( int v ); QString textFromValue ( double value ) const override ; /** * Returns value rounded off to one significant figure. */ static double roundToOneSF( double value ); void init(); // QString m_queuedSuffix; ///< Used QString m_unit; // double m_minValue; // double m_maxValue; double m_minAbsValue; double m_lastEmittedValue; }; #endif diff --git a/src/gui/itemeditor/componentmodelwidget.cpp b/src/gui/itemeditor/componentmodelwidget.cpp index 13307b45..eba01d95 100644 --- a/src/gui/itemeditor/componentmodelwidget.cpp +++ b/src/gui/itemeditor/componentmodelwidget.cpp @@ -1,203 +1,203 @@ /*************************************************************************** * Copyright (C) 2006 David Saxton * * * * 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 "component.h" #include "componentmodellibrary.h" #include "componentmodelwidget.h" -#include #include #include #include //#include // converted to QToolButton -#include -#include -#include +#include +#include +#include +#include // #include // needed? -#include -#include -#include +#include +#include +#include //BEGIN class ComponentModelWidget ComponentModelWidget::ComponentModelWidget( QWidget *parent, const char *name ) : QWidget(parent /*, name */ ) { setObjectName(name); QVBoxLayout * vlayout = new QVBoxLayout( this /*, 0, 6 - 2018.12.07 */); vlayout->setMargin(0); vlayout->setSpacing(6); // parts of the following code are stolen from amarok/src/playlistwindow.cpp :) //BEGIN Filter lineedit QHBoxLayout *h1Layout = new QHBoxLayout; h1Layout->setMargin(0); KToolBar * bar = new KToolBar( this, "ComponentModelSearch" ); bar->setIconSize( QSize( 22, 22 ) /*, false ?? */ ); //looks more sensible //bar->setFlat( true ); //removes the ugly frame bar->setMovable( false ); //removes the ugly frame //bar->setMovingEnabled( false ); //removes the ugly frame // removed, apparently //QWidget * button = new QToolButton( "locationbar_erase", 1, bar ); QWidget * button = new QToolButton( bar ); button->setObjectName("locationbar_erase"); // TODO what is: "locationbar_erase", 1, // button: locationbar_erase is the name of the icon to be displayed on it m_pSearchEdit = new ClickLineEdit( i18n( "Filter here..." ), bar ); //bar->setStretchableWidget( m_pSearchEdit ); // TODO removed, investigate m_pSearchEdit->setSizePolicy(QSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred)); m_pSearchEdit->setFrame( true /* 2019.01.19: was QFrame::Sunken */ ); connect( m_pSearchEdit, SIGNAL(textChanged( const QString & )), this, SLOT(setFilter( const QString& )) ); connect( button, SIGNAL(clicked()), m_pSearchEdit, SLOT(clear()) ); button->setToolTip( i18n( "Clear filter" ) ); QString filtertip = i18n( "Enter space-separated terms to filter the component library." ); m_pSearchEdit->setToolTip( filtertip ); h1Layout->addWidget(m_pSearchEdit); h1Layout->addWidget(button); //END Filter lineedit m_pList = new QListWidget( this ); // m_pList->setItemMargin( 3 ); //m_pList->addColumn( "model" ); // 2018.06.02 - should not be needed //m_pList->setFullWidth( true ); // 2018.06.02 - is it fixed? m_pList->setSizePolicy( QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding) ); //m_pList->header()->hide(); m_pList->setToolTip( i18n( "Select a predefined component configuration from this list." ) ); vlayout->addWidget( bar ); //vlayout->addWidget( m_pSearchEdit ); vlayout->addLayout(h1Layout); vlayout->addWidget( m_pList ); } ComponentModelWidget::~ComponentModelWidget() { } void ComponentModelWidget::reset() { m_pList->clear(); m_pSearchEdit->clear(); } void ComponentModelWidget::init( Component * component ) { // for testing purposes reset(); if ( !component ) return; QStringList types; if ( component->type() == "ec/npnbjt" ) { // types = ComponentModelLibrary::self()->modelIDs( ComponentModelLibrary::NPN ); } else if ( component->type() == "ec/pnpbjt" ) { // types = ComponentModelLibrary::self()->modelIDs( ComponentModelLibrary::PNP ); } else return; QStringList::iterator end = types.end(); for ( QStringList::iterator it = types.begin(); it != end; ++it ) { QListWidgetItem *newItem = new QListWidgetItem( m_pList ); newItem->setText( *it ); } } void ComponentModelWidget::setFilter( const QString & filter ) { QString lower = filter.toLower(); for ( int itemNr = 0; itemNr < m_pList->count(); ++itemNr) { QListWidgetItem * item = m_pList->item(itemNr); bool hasText = item->text().toLower().contains( lower ); item->setHidden( !hasText ); } } //END class ComponentModelWidget //BEGIN class ClickLineEdit ClickLineEdit::ClickLineEdit( const QString &msg, QWidget *parent, const char* /*name*/ ) : KLineEdit( parent /*, name */) { mDrawClickMsg = true; setClickMessage( msg ); } void ClickLineEdit::setClickMessage( const QString &msg ) { mClickMessage = msg; repaint(); } void ClickLineEdit::setText( const QString &txt ) { mDrawClickMsg = txt.isEmpty(); repaint(); KLineEdit::setText( txt ); } void ClickLineEdit::drawContents( QPainter *p ) { // KLineEdit::drawContents( p ); // TODO this has been removed if ( mDrawClickMsg == true && !hasFocus() ) { QPen tmp = p->pen(); p->setPen( palette().color( QPalette::Disabled, QPalette::WindowText /* QColorGroup::Text */ ) ); QRect cr = contentsRect(); //p->drawPixmap( 3, 3, SmallIcon("search-filter") ); // Add two pixel margin on the left side //cr.rLeft() += 3; // 2018.12.07 cr.setLeft( cr.left() + 3 ); p->drawText( cr, Qt::AlignLeft | Qt::AlignVCenter, mClickMessage ); p->setPen( tmp ); } } void ClickLineEdit::focusInEvent( QFocusEvent *ev ) { if ( mDrawClickMsg == true ) { mDrawClickMsg = false; repaint(); } QLineEdit::focusInEvent( ev ); } void ClickLineEdit::focusOutEvent( QFocusEvent *ev ) { if ( text().isEmpty() ) { mDrawClickMsg = true; repaint(); } QLineEdit::focusOutEvent( ev ); } //END class ClickLineEdit diff --git a/src/gui/itemeditor/itemeditor.cpp b/src/gui/itemeditor/itemeditor.cpp index 453f083b..93af4315 100644 --- a/src/gui/itemeditor/itemeditor.cpp +++ b/src/gui/itemeditor/itemeditor.cpp @@ -1,151 +1,151 @@ /*************************************************************************** * Copyright (C) 2006 David Saxton * * * * 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 "cnitem.h" #include "cnitemgroup.h" #include "component.h" #include "componentmodelwidget.h" #include "itemeditor.h" #include "orientationwidget.h" #include "propertyeditor.h" #include "katemdi.h" #include -#include -#include -#include -#include +#include +#include +#include +#include #include ItemEditor * ItemEditor::m_pSelf = nullptr; ItemEditor * ItemEditor::self( KateMDI::ToolView * parent ) { if (!m_pSelf) { assert(parent); m_pSelf = new ItemEditor(parent); } return m_pSelf; } ItemEditor::ItemEditor( KateMDI::ToolView * parent ) : QWidget( (QWidget*)parent ) { setObjectName("Item Editor"); setWhatsThis( i18n("This allows editing of advanced properties of the selected item(s). Right click on the picture of the item to set the orientation.") ); if (parent->layout()) { parent->layout()->addWidget(this); qDebug() << Q_FUNC_INFO << " added item selector to parent's layout " << parent; } else { qWarning() << Q_FUNC_INFO << " unexpected null layout on parent " << parent ; } QVBoxLayout * vlayout = new QVBoxLayout( this /*, 0, 6 */ ); vlayout->setMargin(0); vlayout->setSpacing(6); //BEGIN Create Name Label m_pNameLabel = new QLabel( /* this, */ "" ); m_pNameLabel->setBuddy( this ); m_pNameLabel->setTextFormat( Qt::RichText ); QFont font; font.setBold( true ); if ( font.pointSize() != 0 ) font.setPointSize( int(font.pointSize() * 1.4) ); m_pNameLabel->setFont( font ); //END Create Name Label m_pPropertyEditor = new PropertyEditor(this); m_pPropertyEditor->setWhatsThis(i18n("

Shows properties associated with the currently selected item(s).
Select a property to change its value. If multiple items are selected with different values then the property will appear greyed out, use \"Merge Properties\" to make them the same.
Select \"Defaults\" to set all properties to their default values")); m_pComponentModelWidget = new ComponentModelWidget( this ); vlayout->addWidget( m_pNameLabel ); vlayout->addWidget( m_pPropertyEditor, 3 ); vlayout->addWidget( m_pComponentModelWidget, 5 ); // Orientation widget stuff QHBoxLayout *h2Layout = new QHBoxLayout( /* vlayout , 6 */ ); vlayout->addLayout(h2Layout); h2Layout->setMargin(6); h2Layout->addItem( new QSpacerItem( 1, 1 ) ); m_pOrientationWidget = new OrientationWidget(this); h2Layout->addWidget(m_pOrientationWidget); m_pOrientationWidget->setWhatsThis(i18n("Change the orientation of the selected item by selecting the appropriate button")); h2Layout->addItem( new QSpacerItem( 1, 1 ) ); slotClear(); } ItemEditor::~ItemEditor() { } void ItemEditor::slotClear() { m_pPropertyEditor->setRowCount(0); m_pComponentModelWidget->reset(); m_pOrientationWidget->slotClear(); updateNameLabel(nullptr); } void ItemEditor::slotMultipleSelected() { slotClear(); m_pNameLabel->setText( i18n("Multiple Items") ); } void ItemEditor::slotUpdate( ItemGroup * itemGroup ) { if (!itemGroup) { slotClear(); return; } m_pPropertyEditor->create(itemGroup); updateNameLabel(itemGroup->activeItem()); m_pOrientationWidget->slotUpdate( dynamic_cast(itemGroup) ); } void ItemEditor::itemGroupUpdated( ItemGroup * itemGroup ) { m_pPropertyEditor->updateDefaultsButton(); m_pOrientationWidget->slotUpdate( dynamic_cast(itemGroup) ); } void ItemEditor::slotUpdate( Item * item ) { m_pComponentModelWidget->init( dynamic_cast(item) ); } void ItemEditor::updateNameLabel( Item *item ) { if (item) m_pNameLabel->setText( item->name() ); else m_pNameLabel->setText( i18n("No Item Selected") ); } diff --git a/src/gui/itemeditor/itemeditor.h b/src/gui/itemeditor/itemeditor.h index 390d0912..7b326190 100644 --- a/src/gui/itemeditor/itemeditor.h +++ b/src/gui/itemeditor/itemeditor.h @@ -1,77 +1,77 @@ /*************************************************************************** * Copyright (C) 2006 David Saxton * * * * 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. * ***************************************************************************/ #ifndef ITEMEDITOR_H #define ITEMEDITOR_H -#include -#include +#include +#include class ComponentModelWidget; class CNItem; class CNItemGroup; class CNItemGroup; class ICNDocument; class Item; class ItemEditor; class ItemGroup; class OrientationWidget; class PropertyEditor; class QPushButton; class QLabel; namespace KateMDI { class ToolView; } /** @author Daniel Clarke @author David Saxton */ class ItemEditor : public QWidget { Q_OBJECT public: static ItemEditor * self( KateMDI::ToolView * parent = nullptr ); ~ItemEditor() override; static QString toolViewIdentifier() { return "ItemEditor"; } public slots: /** * Update the Properties Editor */ void slotUpdate( ItemGroup * itemGroup ); /** * Updates various widgets (orientation and component-model ). */ void slotUpdate( Item * item ); /** * Clear the properties editor and orientation widget */ void slotClear(); void slotMultipleSelected(); /** * Updates the merge / reset data parts (e.g. enabling or disabling the * "Merge" button) */ void itemGroupUpdated( ItemGroup * itemGroup ); protected: void updateNameLabel( Item * item ); PropertyEditor * m_pPropertyEditor; QLabel * m_pNameLabel; OrientationWidget * m_pOrientationWidget; ComponentModelWidget * m_pComponentModelWidget; private: static ItemEditor * m_pSelf; ItemEditor( KateMDI::ToolView * parent ); }; #endif diff --git a/src/gui/itemeditor/orientationwidget.cpp b/src/gui/itemeditor/orientationwidget.cpp index ac776182..ffac7321 100644 --- a/src/gui/itemeditor/orientationwidget.cpp +++ b/src/gui/itemeditor/orientationwidget.cpp @@ -1,285 +1,284 @@ /*************************************************************************** * Copyright (C) 2003-2006 David Saxton * * * * 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 "cnitem.h" #include "cnitemgroup.h" #include "component.h" #include "flowpart.h" #include "iteminterface.h" #include "itemlibrary.h" #include "orientationwidget.h" #include "node.h" -#include - -#include -#include -#include -#include -#include -#include +#include +#include +#include +#include +#include +#include +#include #include const int _size = 44; //BEGIN class DrawingPushButton class DrawingPushButton : public QPushButton { public: DrawingPushButton(QWidget *parent) : QPushButton(parent) { } void paintEvent(QPaintEvent *ev) override { QPushButton::paintEvent(ev); //QPainter painter(this);// 2016.05.03 - explicitly initialize painter QPainter painter; const bool isSuccess = painter.begin(this); if (!isSuccess) { qWarning() << Q_FUNC_INFO << " painter not active"; } painter.drawPixmap( 0, 0, toDiplayPixmap); } void setToDisplayPixmap(const QPixmap &pixmap) { toDiplayPixmap = pixmap.copy(); // need to explicitly copy, or all the buttons will look the same } QPixmap toDiplayPixmap; }; //END class DrawingPushButton //BEGIN class OrientationWidget OrientationWidget::OrientationWidget(QWidget *parent, const char *name) : QWidget( parent /*, name */ ) { setObjectName(name); QGridLayout * layout = new QGridLayout( this /*, 2, 4, 0, 4 */ ); layout->setMargin(0); layout->setSpacing(4); for ( int row=0; row<2; ++row ) { for ( int col=0; col<4; ++col ) { DrawingPushButton *btn = new DrawingPushButton(this); m_toolBtn[row][col] = btn; layout->addWidget( btn, row, col ); btn->setFixedSize( _size+6, _size+6 ); // btn->setFlat(true); btn->setCheckable( true ); btn->setEnabled(false); connect( btn, SIGNAL(clicked()), this, SLOT(slotButtonClicked()) ); } } } OrientationWidget::~OrientationWidget() { } void OrientationWidget::slotUpdate( CNItemGroup * itemGroup ) { if ( m_pCNItem ) m_pCNItem->disconnect( this ); m_pCNItem = dynamic_cast( itemGroup->activeItem() ); if ( m_pCNItem ) connect( m_pCNItem, SIGNAL(orientationChanged()), this, SLOT(updateShownOrientation()) ); bool haveSameOrientation = itemGroup->haveSameOrientation(); if ( FlowPart * flowPart = dynamic_cast((CNItem*)m_pCNItem) ) { // Do we actually need to udpate the interface? if ( m_pFlowPart && (m_bHaveSameOrientation == haveSameOrientation) ) return; m_pComponent = nullptr; m_pFlowPart = flowPart; m_bHaveSameOrientation = haveSameOrientation; initFromFlowPart( m_pFlowPart ); } else if ( Component * component = dynamic_cast((CNItem*)m_pCNItem) ) { // Do we actually need to udpate the interface? if ( m_pComponent && (m_bHaveSameOrientation == haveSameOrientation) ) return; m_pFlowPart = nullptr; m_pComponent = component; m_bHaveSameOrientation = haveSameOrientation; initFromComponent( m_pComponent ); } else slotClear(); } void OrientationWidget::initFromFlowPart( FlowPart * flowPart ) { if (!flowPart) return; uint valid = flowPart->allowedOrientations(); for ( uint i=0; i<2; ++i ) { for ( uint j=0; j<4; ++j ) { uint o = j + 4*i; if ( valid & (1<setEnabled(true); QPixmap pm( 50, 50 ); flowPart->orientationPixmap( o, pm ); //m_toolBtn[i][j]->setPixmap(pm); m_toolBtn[i][j]->setToDisplayPixmap(pm); m_toolBtn[i][j]->update(); } } } updateShownOrientation(); } void OrientationWidget::initFromComponent( Component * component ) { const QImage im = itemLibrary()->componentImage( component ); QRect bound = component->boundingRect(); // We want a nice square bounding rect const int dy = bound.width() - bound.height(); if ( dy > 0 ) { bound.setTop( bound.top()-(dy/2) ); bound.setBottom( bound.bottom()+(dy/2) ); } else if ( dy < 0 ) { bound.setLeft( bound.left()+(dy/2) ); bound.setRight( bound.right()-(dy/2) ); } QPixmap tbPm; for ( unsigned col = 0; col < 4; ++col ) { for ( unsigned row = 0; row < 2; ++row ) { bool flipped = bool(row); int angle = 90 * col; if ( col || row ) { tbPm.convertFromImage( im.transformed( Component::transMatrix( angle, flipped, bound.width()/2, bound.height()/2 ) ) ); } else { tbPm.convertFromImage( im ); } //m_toolBtn[row][col]->setPixmap(tbPm); m_toolBtn[row][col]->setToDisplayPixmap(tbPm); m_toolBtn[row][col]->update(); m_toolBtn[row][col]->setEnabled(true); } } updateShownOrientation(); } void OrientationWidget::slotClear() { if ( m_pCNItem ) m_pCNItem->disconnect( this ); m_pComponent = nullptr; m_pFlowPart = nullptr; m_pCNItem = nullptr; for ( int row=0; row<2; ++row ) { for ( int col=0; col<4; ++col ) { // Hmm...this line has crashed before //m_toolBtn[row][col]->setPixmap( QPixmap() ); m_toolBtn[row][col]->setToDisplayPixmap(QPixmap()); m_toolBtn[row][col]->update(); m_toolBtn[row][col]->setEnabled(false); } } } void OrientationWidget::slotButtonClicked() { QPushButton * btn = const_cast( dynamic_cast(sender()) ); assert( btn ); for ( int row=0; row<2; ++row ) { for ( int col=0; col<4; ++col ) { bool isButton = (m_toolBtn[row][col] == btn); m_toolBtn[row][col]->setChecked( isButton ); if ( !isButton ) continue; if ( m_pFlowPart ) ItemInterface::self()->setFlowPartOrientation( (4*row) + col ); else ItemInterface::self()->setComponentOrientation( 90*col, bool(row) ); } } } void OrientationWidget::updateShownOrientation() { if ( !m_pFlowPart && !m_pComponent ) return; CNItemGroup * ig = dynamic_cast( ItemInterface::self()->itemGroup() ); if ( !ig ) return; bool haveSameOrientation = ig->haveSameOrientation(); for ( unsigned col = 0; col < 4; ++col ) { for ( unsigned row = 0; row < 2; ++row ) { if ( m_pFlowPart ) { bool setOn = haveSameOrientation && (m_pFlowPart->orientation() == (4*row + col)); m_toolBtn[row][col]->setChecked( setOn ); } else { bool flipped = bool(row); int angle = 90 * col; bool setOn = haveSameOrientation && (m_pComponent->angleDegrees() == angle) && (m_pComponent->flipped() == flipped); m_toolBtn[row][col]->setChecked( setOn ); } } } } //END class OrientationWidget diff --git a/src/gui/itemeditor/orientationwidget.h b/src/gui/itemeditor/orientationwidget.h index aadad2f0..e2b2a0fe 100644 --- a/src/gui/itemeditor/orientationwidget.h +++ b/src/gui/itemeditor/orientationwidget.h @@ -1,59 +1,59 @@ /*************************************************************************** * Copyright (C) 2003-2006 David Saxton * * * * 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. * ***************************************************************************/ #ifndef ORIENTATIONWIDGET_H #define ORIENTATIONWIDGET_H -#include -#include +#include +#include class CNItem; class CNItemGroup; class Component; class FlowPart; class DrawingPushButton; /** @author David Saxton */ class OrientationWidget : public QWidget { Q_OBJECT public: OrientationWidget( QWidget *parent = nullptr, const char *name = nullptr ); ~OrientationWidget() override; public slots: void slotUpdate( CNItemGroup * itemGroup ); void slotClear(); signals: void orientationSelected( uint orientation ); protected slots: void slotButtonClicked(); /** * Updates which button is indented depending on the current orientation * of the item(s) being edited. */ void updateShownOrientation(); protected: void initFromComponent( Component * component ); void initFromFlowPart( FlowPart * flowPart ); DrawingPushButton *m_toolBtn[2][4]; QPointer m_pFlowPart; QPointer m_pComponent; QPointer m_pCNItem; // Either the flowpart or component bool m_bHaveSameOrientation; // Whether the items had the same orientation when last updated }; #endif diff --git a/src/gui/itemeditor/propertyeditor.cpp b/src/gui/itemeditor/propertyeditor.cpp index 852c35cc..268c554d 100644 --- a/src/gui/itemeditor/propertyeditor.cpp +++ b/src/gui/itemeditor/propertyeditor.cpp @@ -1,810 +1,809 @@ /*************************************************************************** * Copyright (C) 2002 Lucijan Busch * * Copyright (C) 2004 Cedric Pasteur * * Copyright (C) 2004 Jaroslaw Staniek * * Copyright (C) 2006 David Saxton * * * * 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 "item.h" #include "iteminterface.h" #include "itemgroup.h" #include "ktechlab.h" #include "propertyeditor.h" #include "propertyeditorcolor.h" #include "propertyeditorfile.h" #include "propertyeditorlist.h" #include "propertyeditorinput.h" #include "drawparts/drawpart.h" #include #include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include +#include +#include +#include +#include +#include +#include +#include +#include +#include struct PropertyEditorStyledItemColProperty : public QStyledItemDelegate { PropertyEditor *m_propEditor; PropertyEditorStyledItemColProperty(PropertyEditor *propEditor) : m_propEditor(propEditor) { } void paint(QPainter * painter, const QStyleOptionViewItem & option, const QModelIndex & index) const override { // if ( depth() == 0 ) // return; QTableWidgetItem *itemPtr = m_propEditor->item( index.row(), index.column()); if (!itemPtr) { qWarning() << Q_FUNC_INFO << " null item"; return; } PropertyEditorItem *itemProp = dynamic_cast( itemPtr ); if (!itemProp) { qWarning() << Q_FUNC_INFO << " cannot cast item"; return; } m_propEditor->contentsMargins(); int margin = 3; // listView()->itemMargin(); // TODO set decent value const int width = option.rect.width(); const int height = option.rect.height(); const int top = option.rect.top(); const int left = option.rect.left(); const bool isHighlighted = m_propEditor->currentRow() == index.row(); painter->save(); //qWarning() << " draw col " << index.column() << " row " << index.row() // << " isHighlighted=" << isHighlighted << " state_selected=" << option.state.testFlag(QStyle::State_Selected); if (isHighlighted || option.state.testFlag(QStyle::State_Selected)) { painter->fillRect(left,top, width, height, option.palette.highlight()); painter->setPen(option.palette.color(QPalette::BrightText) /* highlightedText() */ ); } else { QColor bgColor = option.palette.color(QPalette::Base); // 2018.12.07 painter->fillRect(left,top, width, height, QBrush(bgColor)); } QFont f = option.font; if ( itemProp->property()->changed() || (!itemProp->property()->isAdvanced())) { f.setBold(true); } painter->setFont(f); painter->drawText( QRect(left + margin, top, width-1, height-1), Qt::AlignVCenter, itemProp->text() ); //qWarning() << Q_FUNC_INFO << " draw " << itemProp->text() << " at " << option.rect; painter->setPen( QColor(200,200,200) ); //like in table view painter->drawLine(left + width-1, top, left + width-1, top + height-1); painter->setPen( QColor(200,200,200) ); //like in t.v. painter->drawLine(left-50, top + height-1, left + width-1, top + height-1 ); painter->restore(); } /* virtual QSize sizeHint(const QStyleOptionViewItem & option, const QModelIndex & index) const { // TODO } */ }; struct PropertyEditorStyledItemColValue : public QStyledItemDelegate { PropertyEditor *m_propEditor; PropertyEditorStyledItemColValue(PropertyEditor *propEditor) : m_propEditor(propEditor) { } void paint(QPainter * painter, const QStyleOptionViewItem & option, const QModelIndex & index) const override { // if ( depth() == 0 ) // return; QTableWidgetItem *itemPtr = m_propEditor->item( index.row(), index.column()); if (!itemPtr) { qWarning() << Q_FUNC_INFO << " null item"; return; } PropertyEditorItem *itemProp = dynamic_cast( itemPtr ); if (!itemProp) { qWarning() << Q_FUNC_INFO << " cannot cast item"; return; } int margin = 3; // listView()->itemMargin(); // TODO set decent value const int width = option.rect.width(); const int height = option.rect.height(); const int top = option.rect.top(); const int left = option.rect.left(); //const bool isHighlighted = m_propEditor->currentRow() == index.row(); // TODO QColor bgColor = option.palette.color(QPalette::Window); // backgroundColor(0); // 2018.06.02 - is this better? painter->save(); Property *property = itemProp->property(); switch(property->type()) { // case QVariant::Pixmap: // { // p->fillRect(0,0,width,height(),QBrush(backgroundColor())); // p->drawPixmap(margin, margin, m_property->value().toPixmap()); // break; // } case Variant::Type::Color: { painter->fillRect(left,top, width,height, QBrush(bgColor)); //QColor ncolor = m_property->value().toColor(); QColor ncolor = property->value().value(); painter->setBrush(ncolor); painter->drawRect(left + margin, top + margin, width - 2*margin, height - 2*margin); // QColorGroup nGroup(cg); break; } case Variant::Type::Bool: { painter->fillRect(left, top , width,height, QBrush(bgColor)); if(property->value().toBool()) { painter->drawPixmap(left + margin, top + height/2 -8, SmallIcon("dialog-ok")); painter->drawText(QRect(left + margin+20, top, width,height-1), Qt::AlignVCenter, i18n("Yes")); } else { painter->drawPixmap(left + margin, top + height/2 -8, SmallIcon("dialog-cancel")); painter->drawText(QRect(left + margin+20, top, width,height-1), Qt::AlignVCenter, i18n("No")); } break; } case Variant::Type::PenStyle: { painter->fillRect(left, top, width,height, QBrush(bgColor)); Qt::PenStyle style = DrawPart::nameToPenStyle( property->value().toString() ); int penWidth = 3; QPen pen( Qt::black, penWidth, style ); painter->setPen( pen ); painter->drawLine( left + height/2, top + height/2-1, left + width-height/2, top + height/2-1 ); break; } #if 0 case Variant::Type::PenCapStyle: { p->fillRect(0,0,width,height, QBrush(bgColor)); PenCapStyle style = DrawPart::nameToPenCapStyle( property->value().toString() ); int penWidth = 6; QPen pen( black, penWidth, SolidLine, style, MiterJoin ); p->setPen( pen ); p->drawLine( width/2-10, height/2-2, width/2+10, height/2-2 ); break; } #endif case Variant::Type::None: case Variant::Type::Int: case Variant::Type::Raw: case Variant::Type::Double: case Variant::Type::String: case Variant::Type::Multiline: case Variant::Type::RichText: case Variant::Type::Select: case Variant::Type::Combo: case Variant::Type::FileName: case Variant::Type::VarName: case Variant::Type::PenCapStyle: case Variant::Type::Port: case Variant::Type::Pin: case Variant::Type::SevenSegment: case Variant::Type::KeyPad: { QStyledItemDelegate::paint(painter, option, index); break; } } painter->setPen( QColor(200,200,200) ); //like in t.v. painter->drawLine( left-50, top + height-1, left + width, top + height-1 ); painter->restore(); } /* virtual QSize sizeHint(const QStyleOptionViewItem & option, const QModelIndex & index) const { // TODO } */ }; PropertyEditor::PropertyEditor( QWidget * parent, const char * name ) : QTableWidget( parent ) //, m_items(101, false) // 2018.08.13 - unused , justClickedItem(false) , m_lastCellWidgetRow(-1) , m_lastCellWidgetCol(-1) , m_colPropertyDelegate(nullptr) , m_colValueDelegate(nullptr) { setObjectName( name ); //m_items.setAutoDelete(false); // 2018.08.13 - unused setColumnCount(2); QStringList headerLabels; headerLabels.append( i18n("Property") ); headerLabels.append( i18n("Value") ); setHorizontalHeaderLabels(headerLabels); //addColumn( i18n("Property") ); // 2018.08.13 - ported to QTableWidget //addColumn( i18n("Value") ); m_colPropertyDelegate = new PropertyEditorStyledItemColProperty(this); setItemDelegateForColumn(0, m_colPropertyDelegate); m_colValueDelegate = new PropertyEditorStyledItemColValue(this); setItemDelegateForColumn(1, m_colValueDelegate); m_topItem = nullptr; m_editItem = nullptr; connect(this, SIGNAL(clicked(const QModelIndex&)), this, SLOT(slotClicked(const QModelIndex&))); connect(this, SIGNAL(itemActivated(QTableWidgetItem*)), this, SLOT(slotCurrentChanged(QTableWidgetItem *))); // connect(this, SIGNAL(expanded(Q3ListViewItem *)), this, SLOT(slotExpanded(Q3ListViewItem *))); // TODO // connect(this, SIGNAL(collapsed(Q3ListViewItem *)), this, SLOT(slotCollapsed(Q3ListViewItem *))); connect(this, SIGNAL(currentCellChanged(int, int, int, int)), this, SLOT(slotCurrentCellChanged(int,int,int,int))); // connect(header(), SIGNAL(sizeChange( int, int, int )), this, SLOT(slotColumnSizeChanged( int, int, int ))); // TODO // connect(header(), SIGNAL(clicked( int )), this, SLOT(moveEditor())); // connect(header(), SIGNAL(sectionHandleDoubleClicked ( int )), this, SLOT(slotColumnSizeChanged( int ))); m_defaults = new QPushButton(viewport()); m_defaults->setFocusPolicy(Qt::NoFocus); setFocusPolicy(Qt::ClickFocus); m_defaults->setIcon(QIcon::fromTheme("edit-undo")); m_defaults->setToolTip(i18n("Undo changes")); m_defaults->hide(); connect(m_defaults, SIGNAL(clicked()), this, SLOT(resetItem())); // TODO const int itemMargin=2; //setRootIsDecorated( false ); //setShowSortIndicator( false ); // setTooltipColumn(0); // TODO equivalent? setSortingEnabled(false /*true*/); // note: enabling it causes crashes, apperently horizontalHeader()->setSortIndicatorShown(false); horizontalHeader()->setContentsMargins(itemMargin, itemMargin, itemMargin, itemMargin); //setItemMargin(2); // needed? horizontalHeader()->setResizeMode(QHeaderView::QHeaderView::Stretch); horizontalHeader()->setMovable(false); //header()->setMovingEnabled( false ); verticalHeader()->setVisible(false); //setTreeStepSize(0); setSelectionMode(QAbstractItemView::SingleSelection); m_baseRowHeight = QFontMetrics(font()).height() + itemMargin*2; } PropertyEditor::~PropertyEditor() { // note: delete m_colPropertyDelegate and m_colValueDelegate } void PropertyEditor::slotClicked(const QModelIndex& index) { if (!index.isValid()) return; // 2019.01.19 - moved to slotCurrentCellChanged() // if (index.column() == 1) { // // PropertyEditorItem *i = static_cast(item);// 2018.08.13 - not needed // createEditor(index); // } justClickedItem = true; } void PropertyEditor::slotCurrentChanged(QTableWidgetItem* /*itemParam*/) { // TODO // if (itemParam == firstChild()) // { // Q3ListViewItem *oldItem = item; // while (item && (!item->isSelectable() || !item->isVisible())) // item = item->itemBelow(); // // if (item && item!=oldItem) // { // setSelected(item,true); // return; // } // } } void PropertyEditor::slotCurrentCellChanged(int currentRow, int currentColumn, int /*previousRow*/, int /*previousColumn*/) { viewport()->repaint(); // force a repaint to clear the "selected" background on items if (currentColumn == 0) { setCurrentCell(currentRow, 1); // move focus to the value column } if (currentColumn == 1) { createEditor(currentIndex()); } } void PropertyEditor::slotExpanded(QTableWidgetItem* item) { if (!item) return; moveEditor(); } void PropertyEditor::slotCollapsed(QTableWidgetItem* item) { if (!item) return; moveEditor(); } void PropertyEditor::createEditor( const QModelIndex& index ) { PropertyEditorItem *i = dynamic_cast(item(index.row(), index.column())); if (!i) { qWarning() << Q_FUNC_INFO << "no item"; return; } // int y = viewportToContents(QPoint(0, itemRect(i).y())).y(); // QRect geometry(columnWidth(0), y, columnWidth(1), i->height()); //delete m_currentEditor; //m_currentEditor->deleteLater(); if (m_lastCellWidgetRow >= 0 && m_lastCellWidgetCol >= 0) { removeCellWidget(m_lastCellWidgetRow, m_lastCellWidgetCol); m_lastCellWidgetRow = -1; m_lastCellWidgetCol = -1; } m_currentEditor = nullptr; m_editItem = i; PropertySubEditor *editor = nullptr; switch ( i->type() ) { case Variant::Type::String: editor = new PropertyEditorInput( viewport(), i->property() ); break; case Variant::Type::Port: case Variant::Type::Pin: case Variant::Type::Combo: case Variant::Type::VarName: case Variant::Type::Select: case Variant::Type::PenStyle: case Variant::Type::PenCapStyle: case Variant::Type::SevenSegment: case Variant::Type::KeyPad: editor = new PropertyEditorList( viewport(), i->property() ); break; case Variant::Type::FileName: qDebug() << Q_FUNC_INFO << "creating PropertyEditorFile"; editor = new PropertyEditorFile( viewport(), i->property() ); break; case Variant::Type::Int: editor = new PropertyEditorSpin( viewport(), i->property() ); break; case Variant::Type::Double: editor = new PropertyEditorDblSpin( viewport(), i->property() ); break; case Variant::Type::Color: editor = new PropertyEditorColor( viewport(), i->property() ); break; case Variant::Type::Bool: editor = new PropertyEditorBool( viewport(), i->property() ); break; case Variant::Type::Raw: case Variant::Type::Multiline: case Variant::Type::RichText: case Variant::Type::None: break; } if (editor) { //addChild(editor); //moveChild(editor, geometry.x(), geometry.y()); m_lastCellWidgetRow = index.row(); m_lastCellWidgetCol = index.column(); setCellWidget(index.row(), index.column(), editor); editor->show(); editor->setFocus(); } m_currentEditor = editor; showDefaultsButton( i->property()->changed() ); } void PropertyEditor::showDefaultsButton( bool show ) { QRect editItemRect = visualItemRect(m_editItem); int y = editItemRect.y(); // viewportToContents(QPoint(0, itemRect(m_editItem).y())).y(); // TODO QRect geometry( columnWidth(0), y, columnWidth(1), editItemRect.height() /* m_editItem->height() TOOD */ ); m_defaults->resize(m_baseRowHeight, m_baseRowHeight); if (!show) { if (m_currentEditor) { if (m_currentEditor->leavesTheSpaceForRevertButton()) { geometry.setWidth(geometry.width()-m_defaults->width()); } m_currentEditor->resize(geometry.width(), geometry.height()); } m_defaults->hide(); return; } QPoint p = geometry.topLeft() ; // = contentsToViewport(QPoint(0, geometry.y())); // TODO m_defaults->move(geometry.x() + geometry.width() - m_defaults->width(), p.y()); if (m_currentEditor) { m_currentEditor->move(m_currentEditor->x(), p.y()); m_currentEditor->resize(geometry.width()-m_defaults->width(), geometry.height()); } m_defaults->show(); } void PropertyEditor::updateDefaultsButton() { if (!m_editItem) return; showDefaultsButton( m_editItem->property()->changed() ); repaint(); //m_editItem->repaint(); } void PropertyEditor::slotColumnSizeChanged( int section, int, int newS) { if ( m_currentEditor ) { if(section == 0) { m_currentEditor->move(newS, m_currentEditor->y()); } else { if(m_defaults->isVisible()) m_currentEditor->resize(newS - m_defaults->width(), m_currentEditor->height()); else m_currentEditor->resize( newS-(m_currentEditor->leavesTheSpaceForRevertButton()?m_defaults->width():0), m_currentEditor->height()); } } } void PropertyEditor::slotColumnSizeChanged( int section) { setColumnWidth(1, viewport()->width() - columnWidth(0)); slotColumnSizeChanged(section, 0, horizontalHeader()->sectionSize(section)); if(m_currentEditor) { if(m_defaults->isVisible()) m_currentEditor->resize(columnWidth(1) - m_defaults->width(), m_currentEditor->height()); else m_currentEditor->resize( columnWidth(1)-(m_currentEditor->leavesTheSpaceForRevertButton()?m_defaults->width():0), m_currentEditor->height()); } } void PropertyEditor::reset() { //if ( m_currentEditor ) // m_currentEditor->deleteLater(); if (m_lastCellWidgetRow >= 0 && m_lastCellWidgetCol >= 0) { removeCellWidget(m_lastCellWidgetRow, m_lastCellWidgetCol); m_lastCellWidgetRow = -1; m_lastCellWidgetCol = -1; } m_currentEditor = nullptr; if ( m_defaults->isVisible() ) m_defaults->hide(); //clear(); QTableWidget::reset(); m_editItem = nullptr; m_topItem = nullptr; } QSize PropertyEditor::sizeHint() const { return QSize( QFontMetrics(font()).width( horizontalHeaderItem(0)->text() + horizontalHeaderItem(1)->text() + " "), QTableWidget::sizeHint().height()); } void PropertyEditor::create( ItemGroup * b ) { qDebug() << Q_FUNC_INFO << "b=" << b; m_pItemGroup = b; //QCString selectedPropertyName1, selectedPropertyName2; // QByteArray selectedPropertyName1, selectedPropertyName2; // 2018.08. 13 - dead code fill(); /* 2018.08. 13 - dead code //select prev. selecteed item PropertyEditorItem * item = 0; if (!selectedPropertyName2.isEmpty()) //try other one for old buffer item = m_items[selectedPropertyName2]; if (!item && !selectedPropertyName1.isEmpty()) //try old one for current buffer item = m_items[selectedPropertyName1]; if (item) { setItemSelected(item, true); scrollToItem(item); } else { qWarning() << Q_FUNC_INFO << "no item to select "; } */ qDebug() << Q_FUNC_INFO << "column count= " << columnCount() << "rowCount=" << rowCount(); } void PropertyEditor::fill() { reset(); if ( !m_pItemGroup || !m_pItemGroup->activeItem() ) { qWarning() << Q_FUNC_INFO << " no active item " << m_pItemGroup; return; } if(!m_topItem) { m_topItem = new PropertyEditorItem(this,"Top Item"); } //m_items.clear(); // 2018.08.13 - unused setRowCount(0); // remove all items from the table VariantDataMap *vmap = m_pItemGroup->activeItem()->variantMap(); // Build the list for( VariantDataMap::iterator vait = vmap->begin(); vait != vmap->end(); ++vait ) { Variant * v = *vait; if ( v->isHidden() ) { continue; } qDebug() << Q_FUNC_INFO << "add variant id=" << v->id() << " v=" << v; switch ( v->type() ) { case Variant::Type::String: case Variant::Type::Port: case Variant::Type::Pin: case Variant::Type::Combo: case Variant::Type::VarName: case Variant::Type::Select: case Variant::Type::PenStyle: case Variant::Type::PenCapStyle: case Variant::Type::SevenSegment: case Variant::Type::KeyPad: case Variant::Type::FileName: case Variant::Type::Int: case Variant::Type::Double: case Variant::Type::Color: case Variant::Type::Bool: // These are all handled by the ItemEditor break; case Variant::Type::Raw: case Variant::Type::Multiline: case Variant::Type::None: case Variant::Type::RichText: // These are not handled by the ItemEditor continue; } const int nextRow = rowCount(); setRowCount(nextRow + 1); { QTableWidgetItem *itemPropName = new PropertyEditorItem( m_topItem, v ); itemPropName->setText(v->editorCaption()); itemPropName->setFlags(Qt::ItemIsEnabled); setItem(nextRow, 0, itemPropName); } { PropertyEditorItem *itemPropValue = new PropertyEditorItem( m_topItem, v ); itemPropValue->setText( v->displayString() ); connect( v, SIGNAL(valueChanged( QVariant, QVariant )), itemPropValue, SLOT(propertyValueChanged()) ); itemPropValue->updateValue(); setItem(nextRow, 1, itemPropValue); } //m_items.insert( v->id().latin1(), item ); // 2018.08.13 - unused } } void PropertyEditor::setFocus() { selectedItems(); PropertyEditorItem *item = static_cast(selectedItem()); if (item) { if (!justClickedItem) { scrollToItem(item); } justClickedItem = false; } else { //select an item before focusing item = static_cast(itemAt(QPoint(10,1))); if (item) { scrollToItem(item); // ensureItemVisible(item); item->setSelected(true); //setSelected(item, true); } } if (m_currentEditor) { m_currentEditor->setFocus(); } else { QTableWidget::setFocus(); } } void PropertyEditor::resetItem() { if ( m_editItem ) { ItemInterface::self()->slotSetData( m_editItem->property()->id(), m_editItem->property()->defaultValue() ); } } void PropertyEditor::moveEditor() { if ( !m_currentEditor ) return; QPoint p = QPoint(0, visualItemRect(m_editItem).y()); // = contentsToViewport(QPoint(0, itemPos(m_editItem))); // TODO m_currentEditor->move(m_currentEditor->x(), p.y()); if( m_defaults->isVisible() ) { m_defaults->move(m_defaults->x(), p.y()); } } void PropertyEditor::resizeEvent(QResizeEvent *ev) { QTableWidget::resizeEvent(ev); updateDefaultsButton(); // if(m_defaults->isVisible()) // { // rect(); // QRect r = visualItemRect(m_editItem) ; // = itemRect(m_editItem); // TODO // if(r.y()) { // r.y() == 0 if the item is not visible on the screen // m_defaults->move(r.x() + r.width() - m_defaults->width(), r.y()); // } // } // // if ( m_currentEditor ) // { // m_currentEditor->resize( // columnWidth(1)-((m_currentEditor->leavesTheSpaceForRevertButton()||m_defaults->isVisible()) ? m_defaults->width() : 0), // m_currentEditor->height()); // } } bool PropertyEditor::handleKeyPress( QKeyEvent* /*ev*/ ) { #if 0 // TODO const int k = ev->key(); const Qt::ButtonState s = ev->state(); //selection moving QTableWidgetItem *item = 0; if ((s==Qt::NoButton && k==Qt::Key_Up) || k==Qt::Key_Backtab) { //find prev visible item = selectedItem() ? selectedItem()->itemAbove() : 0; while (item && (!item->flags().testFlag(Qt::ItemIsSelectable) || !item->isVisible())) item = item->itemAbove(); if (!item) return true; } else if (s==Qt::NoButton && (k==Qt::Key_Down || k==Qt::Key_Tab)) { //find next visible item = selectedItem() ? selectedItem()->itemBelow() : 0; while (item && (!item->flags().testFlag(Qt::ItemIsSelectable) || !item->isVisible())) item = item->itemBelow(); if (!item) return true; } else if(s==Qt::NoButton && k==Qt::Key_Home) { if (m_currentEditor && m_currentEditor->hasFocus()) return false; //find 1st visible item = firstChild(); while (item && (!item->flags().testFlag(Qt::ItemIsSelectable) || !item->isVisible())) item = item->itemBelow(); } else if(s==Qt::NoButton && k==Qt::Key_End) { if (m_currentEditor && m_currentEditor->hasFocus()) return false; //find last visible item = selectedItem(); Q3ListViewItem *lastVisible = item; while (item) { // && (!item->isSelectable() || !item->isVisible())) item = item->itemBelow(); if (item && item->flags().testFlag(Qt::ItemIsSelectable) && item->isVisible()) lastVisible = item; } item = lastVisible; } if(item) { ev->accept(); scrollToItem(item); item->setSelected(true); // setSelected(item, true); return true; } #endif return false; } PropertyEditorItem *PropertyEditor::selectedItem() { QModelIndexList selList = selectedIndexes(); if (selList.empty()) { return nullptr; } if (selList.size() > 1) { qWarning() << Q_FUNC_INFO << " unexpected selection size of " << selList.size(); } QModelIndex selIndex = selList.first(); PropertyEditorItem *itemProp = dynamic_cast(item(selIndex.row(), selIndex.column())); if (!itemProp) { qWarning() << Q_FUNC_INFO << " failed to cast " << selIndex; } return itemProp; } diff --git a/src/gui/itemeditor/propertyeditor.h b/src/gui/itemeditor/propertyeditor.h index 3a06dc88..69b5ecc3 100644 --- a/src/gui/itemeditor/propertyeditor.h +++ b/src/gui/itemeditor/propertyeditor.h @@ -1,150 +1,150 @@ /*************************************************************************** * Copyright (C) 2002 Lucijan Busch * * Copyright (C) 2004 Cedric Pasteur * * Copyright (C) 2004 Jaroslaw Staniek * * Copyright (C) 2006 by David Saxton david@bluehaze.org * * * * 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. * ***************************************************************************/ #ifndef PROPERTYEDITOR_H #define PROPERTYEDITOR_H -#include -#include -#include +#include +#include +#include // #include //#include // 2018.08.13 - ported to QTableWidget #include "propertyeditoritem.h" class ItemGroup; class Variant; class PropertySubEditor; class QPushButton; class QStyledItemDelegate; //! A list view to edit any type of properties class PropertyEditor : public QTableWidget // K3ListView { Q_OBJECT public: /*! Creates an empty PropertyEditor with \a parent as parent widget. */ PropertyEditor( QWidget * parent = nullptr, const char * name = nullptr ); ~PropertyEditor() override; /*! Reset the list, ie clears all items in the list. if \a editorOnly is true, then only the current editor will be cleared, not the whole list. */ void reset() override; /** * Updates the list of Property editors from the items selected in * \a itemGroup. */ void create( ItemGroup * itemGroup ); QSize sizeHint() const override; /** * @internal used by PropertySubEditor and PropertyEditor. */ bool handleKeyPress( QKeyEvent* ev ); /** * Updates the default button for the current editor. */ void updateDefaultsButton(); public slots: /** * On focus: * \li previously focused editor is activated * \li first visible item is activated if no item was active */ virtual void setFocus(); protected slots: /** * This slot resets the value of an item, using Property::oldValue(). * It is called when pressing the "Revert to defaults" button */ void resetItem(); /** * This slot updates the positions of current editor and revert button. * It is called when double-clicking list's header. */ void moveEditor(); /** * Fills the list with an item for each property in the buffer. * You shouldn't need to call this, as it is automatically called in create(). */ void fill(); /** * This slot updates editor and revert buttons position and size when * the columns are resized. */ void slotColumnSizeChanged( int section, int oldS, int newS); void slotColumnSizeChanged( int section); /** * This slot is called when the user clicks the list view. It takes care * of deleting current editor and creating a new editor for the newly * selected item. */ void slotClicked(const QModelIndex& index); /** * Used to fix selection when unselectable item was activated. */ void slotCurrentChanged(QTableWidgetItem *); void slotCurrentCellChanged(int currentRow, int currentColumn, int previousRow, int previousColumn); void slotExpanded(QTableWidgetItem *item); void slotCollapsed(QTableWidgetItem *item); protected: /** * Creates an editor for the list item \a i in the rect \a geometry, and * displays revert button if property is modified (ie * PropertyEditorItem::modified() == true). The editor type depends on * Property::type() of the item's property. */ void createEditor(const QModelIndex& index);//, const QRect &geometry); /** * Reimplemented from K3ListView to update editor and revert button * position. */ void resizeEvent(QResizeEvent *ev) override; void showDefaultsButton( bool show ); int baseRowHeight() const { return m_baseRowHeight; } PropertyEditorItem *selectedItem() ; QPointer m_pItemGroup; QPointer m_currentEditor; PropertyEditorItem *m_editItem; PropertyEditorItem *m_topItem; //The top item is used to control the drawing of every branches. QPushButton *m_defaults; // "Revert to defaults" button //PropertyEditorItem::Dict m_items; // 2018.08.13 - unused int m_baseRowHeight; //! Used in setFocus() to prevent scrolling to previously selected item on mouse click bool justClickedItem; int m_lastCellWidgetRow; int m_lastCellWidgetCol; QStyledItemDelegate *m_colPropertyDelegate; QStyledItemDelegate *m_colValueDelegate; friend class PropertyEditorItem; friend class PropertySubEditor; }; #endif diff --git a/src/gui/itemeditor/propertyeditorcolor.cpp b/src/gui/itemeditor/propertyeditorcolor.cpp index 56895f10..514f97af 100644 --- a/src/gui/itemeditor/propertyeditorcolor.cpp +++ b/src/gui/itemeditor/propertyeditorcolor.cpp @@ -1,59 +1,59 @@ /*************************************************************************** * Copyright (C) 2003 Cedric Pasteur * * Copyright (C) 2006 David Saxton * * * * 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 "colorcombo.h" #include "iteminterface.h" #include "property.h" #include "propertyeditorcolor.h" #include -#include -#include -#include -#include +#include +#include +#include +#include //BEGIN class PropertyEditorColor PropertyEditorColor::PropertyEditorColor( QWidget * parent, Property * property, const char * name ) : PropertySubEditor( parent, property, name ) { m_pColorCombo = new ColorCombo( (ColorCombo::ColorScheme)property->colorScheme(), this ); m_pColorCombo->setColor( property->value().value() ); m_pColorCombo->resize(width(), height()); m_pColorCombo->show(); setWidget(m_pColorCombo); connect( m_pColorCombo, SIGNAL(activated(const QColor&)), this, SLOT(valueChanged(const QColor&)) ); connect( property, SIGNAL(valueChanged( const QColor& )), m_pColorCombo, SLOT(setColor(const QColor &)) ); } void PropertyEditorColor::valueChanged( const QColor & color ) { m_property->setValue( color ); ItemInterface::self()->setProperty( m_property ); } bool PropertyEditorColor::eventFilter(QObject* watched, QEvent* e) { if(e->type() == QEvent::KeyPress) { QKeyEvent* ev = static_cast(e); if((ev->key() == Qt::Key_Enter) | (ev->key()== Qt::Key_Space) || (ev->key() == Qt::Key_Return)) { // m_pColorCombo->animateClick(); m_pColorCombo->showPopup(); return true; } } return PropertySubEditor::eventFilter(watched, e); } //END class PropertyEditorColor diff --git a/src/gui/itemeditor/propertyeditorfile.cpp b/src/gui/itemeditor/propertyeditorfile.cpp index 1e8ee55b..7eeca905 100644 --- a/src/gui/itemeditor/propertyeditorfile.cpp +++ b/src/gui/itemeditor/propertyeditorfile.cpp @@ -1,83 +1,84 @@ /*************************************************************************** * Copyright (C) 2003 Cedric Pasteur * * Copyright (C) 2006 David Saxton * * * * 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 "iteminterface.h" #include "propertyeditorfile.h" #include "property.h" #include #include -#include +#include #include #include -#include -#include -#include -#include -#include -#include +#include +#include +#include +#include +#include +#include +#include PropertyEditorFile::PropertyEditorFile( QWidget * parent, Property * property, const char * name ) : PropertySubEditor( parent, property, name ) { m_lineedit = new KLineEdit(this); m_lineedit->resize(width(), height()-2); m_button = new QPushButton(i18n(" ... "), this); m_button->resize(height(), height()-10); m_button->move(width() - m_button->width() -1, 1); m_lineedit->setText(property->value().toString()); m_lineedit->show(); m_button->show(); setWidget(m_lineedit); connect( m_button, SIGNAL(clicked()), this, SLOT(selectFile()) ); connect( property, SIGNAL(valueChanged( const QString& )), m_lineedit, SLOT(setText(const QString &)) ); } void PropertyEditorFile::selectFile() { const QString filePath = QFileDialog::getOpenFileName(this, i18n("Choose File"), QString(), m_property->filter()); qDebug() << Q_FUNC_INFO << "got QString: " << filePath; if (!filePath.isEmpty()) { qDebug() << Q_FUNC_INFO << "url is not valid, not setting it"; return; } m_property->setValue(filePath); ItemInterface::self()->setProperty( m_property ); } void PropertyEditorFile::resizeEvent(QResizeEvent *ev) { m_lineedit->resize(ev->size()); m_button->move(ev->size().width() - m_button->width()-1, 1); } bool PropertyEditorFile::eventFilter(QObject* watched, QEvent* e) { if(e->type() == QEvent::KeyPress) { QKeyEvent* ev = static_cast(e); if((ev->key() == Qt::Key_Enter) || (ev->key()== Qt::Key_Space) || (ev->key() == Qt::Key_Return)) { m_button->animateClick(); return true; } } return PropertySubEditor::eventFilter(watched, e); } diff --git a/src/gui/itemeditor/propertyeditorinput.cpp b/src/gui/itemeditor/propertyeditorinput.cpp index d63c5351..18a60ae3 100644 --- a/src/gui/itemeditor/propertyeditorinput.cpp +++ b/src/gui/itemeditor/propertyeditorinput.cpp @@ -1,231 +1,231 @@ /*************************************************************************** * Copyright (C) 2002 Lucijan Busch * * Copyright (C) 2004 Cedric Pasteur * * Copyright (C) 2004 Jaroslaw Staniek * * Copyright (C) 2006 David Saxton * * * * 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 "doublespinbox.h" #include "iteminterface.h" #include "propertyeditorinput.h" #include "property.h" #include #include -#include +#include //#include -#include -#include -#include +#include +#include +#include #include //BEGIN class PropertyEditorInput PropertyEditorInput::PropertyEditorInput( QWidget * parent, Property * property, const char * name ) : PropertySubEditor( parent, property, name ) { m_lineedit = new KLineEdit(this); m_lineedit->resize(width(), height()); m_lineedit->setText(property->value().toString()); m_lineedit->show(); setWidget(m_lineedit); connect( m_lineedit, SIGNAL(textChanged(const QString &)), this, SLOT(slotTextChanged(const QString &))); connect( m_property, SIGNAL(valueChanged( const QString& )), m_lineedit, SLOT(setText(const QString &)) ); } void PropertyEditorInput::slotTextChanged( const QString & text ) { m_property->setValue( text ); ItemInterface::self()->setProperty( m_property ); } //END class PropertyEditorInput //BEGIN class PropIntSpinBox PropIntSpinBox::PropIntSpinBox( int lower, int upper, int step, int value, int base=10, QWidget *parent = nullptr, const char *name = nullptr) : QSpinBox(parent /*, name */) { setMinimum(lower); setMaximum(upper); setSingleStep(step); setValue(value); setDisplayIntegerBase(base); setObjectName(name); lineEdit()->setAlignment(Qt::AlignLeft); } bool PropIntSpinBox::eventFilter(QObject *o, QEvent *e) { if(o == lineEdit()) { if(e->type() == QEvent::KeyPress) { QKeyEvent* ev = static_cast(e); if((ev->key()==Qt::Key_Up || ev->key()==Qt::Key_Down) && ev->modifiers()!=Qt::ControlModifier) { parentWidget()->eventFilter(o, e); return true; } } } return QSpinBox::eventFilter(o, e); } //END class PropIntSpinBox //BEGIN class PropertyEditorSpin PropertyEditorSpin::PropertyEditorSpin( QWidget * parent, Property * property, const char * name ) : PropertySubEditor(parent,property, name) { m_leaveTheSpaceForRevertButton = true; m_spinBox = new PropIntSpinBox( (int)property->minValue(), (int)property->maxValue(), 1, 0, 10, this ); m_spinBox->resize(width(), height()); m_spinBox->setValue(property->value().toInt()); m_spinBox->show(); setWidget( m_spinBox, m_spinBox->editor() ); connect( m_spinBox, SIGNAL(valueChanged( int )), this, SLOT(valueChange( int ))); connect( m_property, SIGNAL(valueChanged( int )), m_spinBox, SLOT(setValue( int )) ); } void PropertyEditorSpin::valueChange( int value ) { m_property->setValue( value ); ItemInterface::self()->setProperty( m_property ); } //END class PropertyEditorSpin //BEGIN class PropDoubleSpinBox PropDoubleSpinBox::PropDoubleSpinBox(double lower, double upper, double minAbs, double value, const QString &unit, QWidget *parent = nullptr) : DoubleSpinBox( lower, upper, minAbs, value, unit, parent ) { lineEdit()->setAlignment(Qt::AlignLeft); } bool PropDoubleSpinBox::eventFilter(QObject *o, QEvent *e) { if(o == lineEdit()) { if(e->type() == QEvent::KeyPress) { QKeyEvent* ev = static_cast(e); if((ev->key()==Qt::Key_Up || ev->key()==Qt::Key_Down) && ev->modifiers()!=Qt::ControlModifier) { parentWidget()->eventFilter(o, e); return true; } } } return DoubleSpinBox::eventFilter(o, e); } //END class PropDoubleSpinBox //BEGIN class PropertyEditorDblSpin PropertyEditorDblSpin::PropertyEditorDblSpin( QWidget * parent, Property * property, const char * name ) : PropertySubEditor( parent, property, name ) { m_leaveTheSpaceForRevertButton = true; m_spinBox = new PropDoubleSpinBox( property->minValue(), property->maxValue(), property->minAbsValue(), property->value().toDouble(), property->unit(), this ); m_spinBox->resize(width(), height()); m_spinBox->show(); setWidget( m_spinBox, m_spinBox->editor()); connect( m_spinBox, SIGNAL(valueChanged(double)), this, SLOT(valueChange(double))); connect( m_property, SIGNAL(valueChanged( double )), m_spinBox, SLOT(setValue( double )) ); } void PropertyEditorDblSpin::valueChange( double value ) { m_property->setValue( value ); ItemInterface::self()->setProperty( m_property ); } //END class PropertyEditorDblSpin //BEGIN class PropertyEditorBool PropertyEditorBool::PropertyEditorBool( QWidget * parent, Property * property, const char * name ) : PropertySubEditor( parent, property, name ) { m_toggle = new QToolButton(this); m_toggle->setFocusPolicy(Qt::NoFocus); m_toggle->setCheckable(true); m_toggle->setToolButtonStyle(Qt::ToolButtonTextUnderIcon); // 2018.12.02: see above //m_toggle->setTextPosition(QToolButton::Right); //js BesideIcon -didnt work before qt3.2); m_toggle->resize(width(), height()); connect( m_toggle, SIGNAL(toggled(bool)), this, SLOT(setState(bool))); connect( m_property, SIGNAL(valueChanged( bool )), m_toggle, SLOT(setChecked(bool)) ); if(property->value().toBool()) m_toggle->setChecked(true); else { m_toggle->toggle(); m_toggle->setChecked(false); } m_toggle->show(); setWidget(m_toggle); installEventFilter(this); } bool PropertyEditorBool::eventFilter(QObject* watched, QEvent* e) { if(e->type() == QEvent::KeyPress) { QKeyEvent* ev = static_cast(e); if(ev->key() == Qt::Key_Space) { m_toggle->toggle(); return true; } } return PropertySubEditor::eventFilter(watched, e); } void PropertyEditorBool::setState( bool state ) { if(state) { m_toggle->setIcon(QIcon::fromTheme("dialog-ok")); m_toggle->setToolTip(i18n("Yes")); } else { m_toggle->setIcon(QIcon::fromTheme("dialog-cancel")); m_toggle->setToolTip(i18n("No")); } m_property->setValue( state ); ItemInterface::self()->setProperty( m_property ); } //END class PropertyEditorBool diff --git a/src/gui/itemeditor/propertyeditoritem.cpp b/src/gui/itemeditor/propertyeditoritem.cpp index d66b35c2..18f1e5ca 100644 --- a/src/gui/itemeditor/propertyeditoritem.cpp +++ b/src/gui/itemeditor/propertyeditoritem.cpp @@ -1,225 +1,225 @@ /*************************************************************************** * Copyright (C) 2002 Lucijan Busch * * Copyright (C) 2004 Cedric Pasteur * * Copyright (C) 2004 Jaroslaw Staniek * * Copyright (C) 2006 David Saxton * * * * 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 "drawpart.h" #include "propertyeditor.h" #include "propertyeditoritem.h" -#include #include -#include -#include -#include -#include -#include -#include -#include +#include +#include +#include +#include +#include +#include +#include +#include //BEGIN Class PropertyEditorItem PropertyEditorItem::PropertyEditorItem( PropertyEditorItem * par, Property * property ) : QTableWidgetItem( property->editorCaption() /*, property->displayString() */ ) { // setParent(par); // table takes ownership of the item //setText(property->editorCaption()); // need to set 2 items for each property // setExpandable( false ); // TODO m_property=property; //connect( m_property, SIGNAL(valueChanged( QVariant, QVariant )), this, SLOT(propertyValueChanged()) ); //updateValue(); // need to set 2 items for each property //3 rows per item is enough? // setMultiLinesEnabled( true ); // TODO // setHeight(static_cast(listView())->baseRowHeight()*3); } PropertyEditorItem::PropertyEditorItem(QTableWidget* parent, const QString& text) : QTableWidgetItem( text ) { setParent(parent); setText(text); m_property = nullptr; setFlags(flags() &= (~Qt::ItemIsSelectable)); //setSelectable(false); //setOpen(true); //3 rows per item is enough? // setMultiLinesEnabled( true ); // TODO // setHeight(static_cast(par)->baseRowHeight()*3); } void PropertyEditorItem::propertyValueChanged() { setText( m_property->displayString() ); } #if 0 // 2018.08.13 - moved to property editor void PropertyEditorItem::paintCell(QPainter *p, const QColorGroup & cg, int column, int width, int align) { if ( depth() == 0 ) return; int margin = listView()->itemMargin(); QColor bgColor = cg.background(); // backgroundColor(0); // 2018.06.02 - is this better? if(column == 1) { switch(m_property->type()) { // case QVariant::Pixmap: // { // p->fillRect(0,0,width,height(),QBrush(backgroundColor())); // p->drawPixmap(margin, margin, m_property->value().toPixmap()); // break; // } case Variant::Type::Color: { p->fillRect(0,0,width,height(), QBrush(bgColor)); //QColor ncolor = m_property->value().toColor(); QColor ncolor = m_property->value().value(); p->setBrush(ncolor); p->drawRect(margin, margin, width - 2*margin, height() - 2*margin); QColorGroup nGroup(cg); break; } case Variant::Type::Bool: { p->fillRect(0,0,width,height(), QBrush(bgColor)); if(m_property->value().toBool()) { p->drawPixmap(margin, height()/2 -8, SmallIcon("dialog-ok")); p->drawText(QRect(margin+20,0,width,height()-1), Qt::AlignVCenter, i18n("Yes")); } else { p->drawPixmap(margin, height()/2 -8, SmallIcon("dialog-cancel")); p->drawText(QRect(margin+20,0,width,height()-1), Qt::AlignVCenter, i18n("No")); } break; } case Variant::Type::PenStyle: { p->fillRect(0,0,width,height(), QBrush(bgColor)); Qt::PenStyle style = DrawPart::nameToPenStyle( m_property->value().toString() ); int penWidth = 3; QPen pen( Qt::black, penWidth, style ); p->setPen( pen ); p->drawLine( height()/2, height()/2-1, width-height()/2, height()/2-1 ); break; } #if 0 case Variant::Type::PenCapStyle: { p->fillRect(0,0,width,height(), QBrush(bgColor)); PenCapStyle style = DrawPart::nameToPenCapStyle( m_property->value().toString() ); int penWidth = 6; QPen pen( black, penWidth, SolidLine, style, MiterJoin ); p->setPen( pen ); p->drawLine( width/2-10, height()/2-2, width/2+10, height()/2-2 ); break; } #endif case Variant::Type::None: case Variant::Type::Int: case Variant::Type::Raw: case Variant::Type::Double: case Variant::Type::String: case Variant::Type::Multiline: case Variant::Type::RichText: case Variant::Type::Select: case Variant::Type::Combo: case Variant::Type::FileName: case Variant::Type::VarName: case Variant::Type::PenCapStyle: case Variant::Type::Port: case Variant::Type::Pin: case Variant::Type::SevenSegment: case Variant::Type::KeyPad: { Q3ListViewItem::paintCell(p, cg, column, width, align); break; } } } else { if(isSelected()) { p->fillRect(0,0,width, height(), QBrush(cg.highlight())); p->setPen(cg.highlightedText()); } else p->fillRect(0,0,width, height(), QBrush(bgColor)); QFont f = listView()->font(); p->save(); if ( m_property->changed() ) f.setBold(true); p->setFont(f); p->drawText(QRect(margin,0,width, height()-1), Qt::AlignVCenter, text(0)); p->restore(); p->setPen( QColor(200,200,200) ); //like in table view p->drawLine(width-1, 0, width-1, height()-1); } p->setPen( QColor(200,200,200) ); //like in t.v. p->drawLine(-50, height()-1, width, height()-1 ); } #endif // void PropertyEditorItem::setup() // { // Q3ListViewItem::setup(); // if ( depth() == 0 ) // setHeight(0); // } PropertyEditorItem::~PropertyEditorItem() { } void PropertyEditorItem::updateValue(bool alsoParent) { QString text; if ( m_property ) { text = m_property->displayString(); } qDebug() << Q_FUNC_INFO << "text= " << text; setText( text ); if ( alsoParent && QObject::parent() ) static_cast(QObject::parent())->updateValue(); } // void PropertyEditorItem::paintFocus ( QPainter * , const QColorGroup & , const QRect & ) // { // } //END class PropertyEditorItem diff --git a/src/gui/itemeditor/propertyeditoritem.h b/src/gui/itemeditor/propertyeditoritem.h index f96d7929..12dba830 100644 --- a/src/gui/itemeditor/propertyeditoritem.h +++ b/src/gui/itemeditor/propertyeditoritem.h @@ -1,95 +1,95 @@ /*************************************************************************** * Copyright (C) 2002 Lucijan Busch * * Copyright (C) 2004 Cedric Pasteur * * Copyright (C) 2004 Jaroslaw Staniek * * Copyright (C) 2006 David Saxton * * * * 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. * ***************************************************************************/ #ifndef PROPERTYEDITORITEM_H #define PROPERTYEDITORITEM_H //#include // #include // #include // #include #include "property.h" // #include -#include +#include #define PropertyEditorItem_BranchBoxSize 9 /** This class is a subclass of K3ListViewItem which is associated to a property. It also takes care of drawing custom contents. **/ //! An item in PropertyEditorItem associated to a property class PropertyEditorItem : public QObject, public QTableWidgetItem // K3ListViewItem { Q_OBJECT public: //typedef Q3AsciiDict Dict; // 2018.08.13 - unused /** * Creates a PropertyEditorItem child of \a parent, associated to * \a property. Within property editor, items are created in * PropertyEditor::fill(), every time the buffer is updated. It * \a property has not desctiption set, its name (i.e. not i18n'ed) is * reused. */ PropertyEditorItem( PropertyEditorItem *parent, Property *property ); /** * Creates PropertyEditor Top Item which is necessary for drawing all * branches. */ PropertyEditorItem( /*K3ListView */ QTableWidget *parent, const QString &text); ~PropertyEditorItem() override; /** * \return property's name. */ QString name() const { return m_property->id(); } /** * \return properties's type. */ Variant::Type::Value type() { return m_property->type(); } /** * \return a pointer to the property associated to this item. */ Property* property() { return m_property;} /** * Updates text on of this item, for current property value. If * \a alsoParent is true, parent item (if present) is also updated. */ virtual void updateValue(bool alsoParent = true); // virtual void paintFocus ( QPainter * p, const QColorGroup & cg, const QRect & r ); protected slots: virtual void propertyValueChanged(); protected: /** * Reimplemented from K3ListViewItem to draw custom contents. Properties * names are wriiten in bold if modified. Also takes care of drawing * borders around the cells as well as pixmaps or colors if necessary. */ // virtual void paintCell(QPainter *p, const QColorGroup & cg, int column, int width, int align); /** * Reimplemented from K3ListViewItem to hide the top item. */ // virtual void setup(); private: Property *m_property; }; #endif diff --git a/src/gui/itemeditor/propertyeditorlist.cpp b/src/gui/itemeditor/propertyeditorlist.cpp index 65a9aeb8..596da968 100644 --- a/src/gui/itemeditor/propertyeditorlist.cpp +++ b/src/gui/itemeditor/propertyeditorlist.cpp @@ -1,155 +1,155 @@ /*************************************************************************** * Copyright (C) 2002 Lucijan Busch * * Copyright (C) 2004 Jaroslaw Staniek * * Copyright (C) 2006 David Saxton * * * * 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 "iteminterface.h" #include "propertyeditorlist.h" #include "property.h" -#include //#include #include -#include +#include +#include //#include -#include -#include -#include -#include -#include +#include +#include +#include +#include +#include //BEGIN class PropComboBox PropComboBox::PropComboBox( QWidget *parent ) : KComboBox(parent) { m_eventFilterEnabled = true; } bool PropComboBox::eventFilter(QObject *o, QEvent *e) { if (!m_eventFilterEnabled) return false; if(o == lineEdit()) { if(e->type() == QEvent::KeyPress) { QKeyEvent* ev = static_cast(e); if((ev->key()==Qt::Key_Up || ev->key()==Qt::Key_Down) && ( /* ev->state()!=Qt::ControlButton */ (dynamic_cast(ev))->modifiers() != Qt::ControlModifier )) { parentWidget()->eventFilter(o, e); return true; } } } return KComboBox::eventFilter(o, e); } void PropComboBox::hideList() { lineEdit()->setFocus(); } //END class PropComboBox //BEGIN class PropertyEditorList PropertyEditorList::PropertyEditorList( QWidget * parent, Property * property, const char * name ) : PropertySubEditor( parent, property, name ) { QWidget *box = new QWidget(this); QHBoxLayout *boxLayout = new QHBoxLayout; m_combo = new PropComboBox( box ); m_combo->setGeometry(frameGeometry()); boxLayout->addWidget(m_combo); box->setLayout(boxLayout); bool isEditable = false; switch ( property->type() ) { case Property::Type::Port: case Property::Type::Pin: case Property::Type::PenStyle: case Property::Type::PenCapStyle: case Property::Type::SevenSegment: case Property::Type::KeyPad: case Property::Type::Select: isEditable = false; break; case Property::Type::String: case Property::Type::Multiline: case Property::Type::RichText: case Property::Type::Combo: case Property::Type::FileName: case Property::Type::VarName: isEditable = true; break; case Property::Type::None: case Property::Type::Int: case Property::Type::Raw: case Property::Type::Double: case Property::Type::Color: case Property::Type::Bool: // not handled by this break; } m_combo->setEditable( isEditable ); m_combo->setInsertPolicy(QComboBox::InsertAtBottom); m_combo->setAutoCompletion(true); m_combo->setMinimumSize(10, 0); // to allow the combo to be resized to a small size m_combo->insertItems(m_combo->count(), m_property->allowed() ); //m_combo->setCurrentText( m_property->displayString() ); // 2018.12.07 { QString text = m_property->displayString(); int i = m_combo->findText(text); if (i != -1) m_combo->setCurrentIndex(i); else if (m_combo->isEditable()) m_combo->setEditText(text); else m_combo->setItemText(m_combo->currentIndex(), text); } KCompletion *comp = m_combo->completionObject(); comp->insertItems( m_property->allowed() ); setWidget(box, m_combo->lineEdit()); connect( m_combo, SIGNAL(activated( const QString & )), this, SLOT(valueChanged( const QString & )) ); connect( m_property, SIGNAL(valueChanged( const QString& )), m_combo, SLOT(setCurrentItem( const QString & )) ); } void PropertyEditorList::setList(QStringList l) { //m_combo->insertStringList(l); // 2018.12.07 m_combo->insertItems(m_combo->count(), l); } void PropertyEditorList::valueChanged( const QString & text ) { m_property->setValue( text ); ItemInterface::self()->setProperty( m_property ); } //END class PropertyEditorList diff --git a/src/gui/itemeditor/propertysubeditor.cpp b/src/gui/itemeditor/propertysubeditor.cpp index e9d59e38..608ecd0a 100644 --- a/src/gui/itemeditor/propertysubeditor.cpp +++ b/src/gui/itemeditor/propertysubeditor.cpp @@ -1,77 +1,76 @@ /*************************************************************************** * Copyright (C) 2002 by Lucijan Busch * * Copyright (C) 2006 David Saxton * * * * 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 -#include - -#include - #include "propertysubeditor.h" #include "propertyeditor.h" +#include +#include +#include + PropertySubEditor::PropertySubEditor( QWidget * parent, Property * property, const char * name ) : QWidget( parent /*, name */ ) { setObjectName( name ); m_childWidget = nullptr; m_property = property; m_leaveTheSpaceForRevertButton = false; } bool PropertySubEditor::eventFilter(QObject* /*watched*/, QEvent* e) { if ( e->type() == QEvent::KeyPress ) // || e->type()==QEvent::AccelOverride) { QKeyEvent * ev = static_cast(e); PropertyEditor *list = dynamic_cast( parentWidget()->parentWidget() ); if (!list) return false; //for sanity return list->handleKeyPress(ev); } return false; } void PropertySubEditor::resizeEvent(QResizeEvent *ev) { if(m_childWidget) { m_childWidget->resize(ev->size()); } } void PropertySubEditor::setWidget(QWidget *w, QWidget* focusProxy) { if (m_childWidget) m_childWidget->removeEventFilter(this); m_childWidget = w; if(!m_childWidget) return; if (focusProxy && focusProxy->focusPolicy()!=Qt::NoFocus) { setFocusProxy(focusProxy); focusProxy->installEventFilter(this); } else if (m_childWidget->focusPolicy()!=Qt::NoFocus) setFocusProxy(m_childWidget); m_childWidget->installEventFilter(this); // if (m_childWidget->inherits("QFrame")) { // static_cast(m_childWidget)->setFrameStyle( QFrame::Box | QFrame::Plain ); // } } PropertySubEditor::~PropertySubEditor() { } diff --git a/src/gui/itemeditor/propertysubeditor.h b/src/gui/itemeditor/propertysubeditor.h index 4e697d99..14e49c05 100644 --- a/src/gui/itemeditor/propertysubeditor.h +++ b/src/gui/itemeditor/propertysubeditor.h @@ -1,56 +1,56 @@ /*************************************************************************** * Copyright (C) 2002 Lucijan Busch * * Copyright (C) 2006 David Saxton * * * * 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. * ***************************************************************************/ #ifndef PROPERTYSUBEDITOR_H #define PROPERTYSUBEDITOR_H -#include +#include class Variant; typedef Variant Property; //! The base class for all editors used in PropertyEditor. class PropertySubEditor : public QWidget { Q_OBJECT public: PropertySubEditor( QWidget * parent, Property * property, const char * name = nullptr ); ~PropertySubEditor() override; bool eventFilter( QObject * watched, QEvent * e ) override; Property * property() const { return m_property; } /** * Sets \a w as editor 's widget, ie the widget which events are * filtered and which is resized. If \a focusProxy is not 0, it will be * used as focus proxy instead of \a w. */ void setWidget(QWidget *w, QWidget* focusProxy = nullptr); /** * \sa m_leaveTheSpaceForRevertButton description. */ bool leavesTheSpaceForRevertButton() const { return m_leaveTheSpaceForRevertButton; } protected: void resizeEvent(QResizeEvent *ev) override; Property * m_property; QWidget * m_childWidget; /** * true if there should be left space at the right hand for the Revert * Button. false by default. Integer editor (spinbox) sets this to true * to avoid spin arrows clicking inconvenience. */ bool m_leaveTheSpaceForRevertButton; }; #endif diff --git a/src/gui/itemselector.cpp b/src/gui/itemselector.cpp index f3cd9dfb..daca806a 100644 --- a/src/gui/itemselector.cpp +++ b/src/gui/itemselector.cpp @@ -1,500 +1,500 @@ /*************************************************************************** * Copyright (C) 2003-2006 by David Saxton * * david@bluehaze.org * * * * 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 // Temporay fix for pthread.h problem #include "circuitdocument.h" #include "docmanager.h" #include "flowcodedocument.h" #include "itemdocument.h" #include "itemlibrary.h" #include "itemselector.h" #include "libraryitem.h" #include "mechanicsdocument.h" #include "katemdi.h" #include #include #include // #include -#include -#include +#include // #include -#include -#include +#include +#include +#include #include ILVItem::ILVItem( QTreeWidget *parent, const QString &id ) : QTreeWidgetItem( parent, 0 /* note: add item types */ ) { setData(0, DataRole_ID, QVariant(id)); // m_id = id; // 2018.08.12 - use value() b_isRemovable = false; m_pProjectItem = nullptr; } ILVItem::ILVItem( QTreeWidgetItem *parent, const QString &id ) : QTreeWidgetItem( parent, 0 /* note: add item types */ ) { //m_id = id; // 2018.08.12 - use value() setData(0, DataRole_ID, QVariant(id)); b_isRemovable = false; m_pProjectItem = nullptr; } ItemSelector::ItemSelector( QWidget *parent, const char *name ) : QTreeWidget( parent /*, name */ ) { setObjectName(name); qDebug() << Q_FUNC_INFO << " this=" << this; setDragDropMode(QAbstractItemView::DragOnly); setColumnCount(1); setHeaderLabel( i18n( "Component" ) ); //addColumn( i18n( "Component" ) ); // 2018.08.12 - use setHeaderLabel() //setFullWidth(true); // 2018.06.02 - need to be fixed setSizePolicy( QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred) ); //setSorting( -1, false ); // 2018.08.12 - use setSortingEnabled setSortingEnabled(false); setRootIsDecorated(true); //setDragEnabled(true); // 2018.06.02 - needed? setFocusPolicy( Qt::NoFocus ); setSelectionMode( QAbstractItemView::SingleSelection ); // 2015.12.10 - need to allow selection for removing items if (parent->layout()) { parent->layout()->addWidget(this); qDebug() << Q_FUNC_INFO << " added item selector to parent's layout " << parent; } else { qWarning() << Q_FUNC_INFO << " unexpected null layout on parent " << parent ; } setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); // ? // connect( this, SIGNAL(executed(K3ListViewItem*) ), this, SLOT(slotItemExecuted(K3ListViewItem*)) ); connect( this, SIGNAL(itemClicked(QTreeWidgetItem*,int)), this, SLOT(slotItemClicked(QTreeWidgetItem*,int)) ); connect( this, SIGNAL(itemDoubleClicked(QTreeWidgetItem*,int)), this, SLOT(slotItemDoubleClicked(QTreeWidgetItem*,int)) ); // connect( this, SIGNAL(contextMenuRequested(Q3ListViewItem*, const QPoint&, int )), this, // SLOT(slotContextMenuRequested(Q3ListViewItem*, const QPoint&, int )) ); // 2018.08.12 - use signal from below setContextMenuPolicy(Qt::CustomContextMenu); connect(this, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(slotContextMenuRequested(QPoint))); connect( this, SIGNAL(itemSelectionChanged()), this, SLOT(slotItemSelected()) ); } ItemSelector::~ItemSelector() { writeOpenStates(); } void ItemSelector::clear() { m_categories.clear(); QTreeWidget::clear(); } void ItemSelector::addItem( const QString & caption, const QString & id, const QString & _category, const QPixmap & icon, bool removable ) { qDebug() << Q_FUNC_INFO << "id=" << id; ILVItem *parentItem = nullptr; QString category = _category; if ( !category.startsWith("/") ) { category.prepend('/'); } do { category.remove(0,1); QString cat; category.replace( "\\/", "|" ); int pos = category.indexOf('/'); if ( pos == -1 ) cat = category; else cat = category.left( pos ); cat.replace( "|", "/" ); if ( m_categories.indexOf(cat) == -1 ) { m_categories.append(cat); if (parentItem) { parentItem = new ILVItem( parentItem, "" ); } else { parentItem = new ILVItem( this, "" ); } //parentItem->setExpandable(true); // 2018.08.12 - is it needed? parentItem->setExpanded( readOpenState(cat) ); parentItem->setText( 0, cat ); } else { QList foundList = findItems( cat, Qt::MatchExactly); if (foundList.size() > 1) { qWarning() << Q_FUNC_INFO << "found multiple categories for '" << cat << "'"; } parentItem = dynamic_cast( foundList.front() ); } category.remove( 0, pos ); } while ( category.contains('/') ); if ( !parentItem ) { qCritical() << "Unexpected error in finding parent item for category list"<setPixmap( 0, icon ); // 2018.08.12 - replaced with line below item->setIcon( 0, QIcon(icon) ); item->setText( 0, caption ); //item->setDragEnabled(true); // 2018.08.12 - replaced with line below item->setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsDragEnabled); item->setRemovable(removable); } void ItemSelector::writeOpenStates() { //KConfig *config = kapp->config(); KSharedConfigPtr configPtr = KSharedConfig::openConfig(); //config->setGroup( name() ); KConfigGroup configGroup = configPtr->group( objectName() ); const QStringList::iterator end = m_categories.end(); for ( QStringList::iterator it = m_categories.begin(); it != end; ++it ) { QList itemsFound = findItems( *it, Qt::MatchExactly ); if (itemsFound.size() > 1) { qWarning() << Q_FUNC_INFO << " too many items " << itemsFound.size() << " for category '" << *it << "'"; } QTreeWidgetItem *item = itemsFound.first() /* findItem( *it, 0 ) */ ; if (item) { configGroup.writeEntry( *it+"IsOpen", item->isExpanded() /* isOpen() */ ); } } } bool ItemSelector::readOpenState( const QString &id ) { //KConfig *config = kapp->config(); KSharedConfigPtr configPtr = KSharedConfig::openConfig(); //config->setGroup( name() ); KConfigGroup configGroup = configPtr->group( objectName() ); return configGroup.readEntry( id+"IsOpen", true ); } QTreeWidgetItem *ItemSelector::selectedItem() const { QList< QTreeWidgetItem*> selectedList = selectedItems(); if (selectedList.empty()) { return nullptr; } if (selectedList.size() > 1) { qWarning() << Q_FUNC_INFO << " expected 1 item in selection, got " << selectedList.size(); } return selectedList.first(); } QMimeData * ItemSelector::mimeData(const QList items) const { qDebug() << Q_FUNC_INFO << " begin "; if (items.size() > 1) { qWarning() << Q_FUNC_INFO << "expected 1 item, got " << items.size(); } QTreeWidgetItem *theItem = items.first(); if (!theItem) { qWarning() << Q_FUNC_INFO << "unexpected null item"; return nullptr; } qDebug() << Q_FUNC_INFO << " theItem = " << theItem; QVariant idAsVariant = theItem->data(0, ILVItem::DataRole_ID); qDebug() << Q_FUNC_INFO << " idAsVariant = " << idAsVariant; const QString id = idAsVariant.toString(); qDebug() << Q_FUNC_INFO << "id='" << id << "'"; QMimeData * mime = new QMimeData(); QByteArray data; QDataStream stream( &data, QIODevice::WriteOnly ); stream << id; if ( id.startsWith("flow/") ) { mime->setData("ktechlab/flowpart", data); } else if ( id.startsWith("ec/") ) { mime->setData("ktechlab/component", data); } else if ( id.startsWith("sc/") ) { mime->setData("ktechlab/subcircuit", data); } else if ( id.startsWith("mech/") ) { mime->setData("ktechlab/mechanical", data); } else { qWarning() << Q_FUNC_INFO << "returning unset mime; uknown id '" << id << "'"; } // A pixmap cursor is often hard to make out // QPixmap *pixmap = const_cast(currentItem()->pixmap(0)); // if (pixmap) d->setPixmap(*pixmap); return mime; } void ItemSelector::slotContextMenuRequested(const QPoint& pos) { QTreeWidgetItem* item = itemAt(pos); if ( !item || !(static_cast(item))->isRemovable() ) { return; } QMenu *menu = new QMenu(this); /* menu->insertItem( //, Qt::Key_Delete // 2015.12.29 - do not specify shortcut key, because it does not work ); - 2018.12.01 */ menu->addAction(i18n("Remove %1", item->text(0)), this, SLOT(slotRemoveSelectedItem())); QPoint globalPos = mapToGlobal(pos); menu->popup(globalPos); } void ItemSelector::slotRemoveSelectedItem() { qDebug() << Q_FUNC_INFO << "removing selected item"; QList< QTreeWidgetItem*> selectedList = selectedItems(); if (selectedList.empty()) { qDebug() << Q_FUNC_INFO << "selection is empty"; return; } QTreeWidgetItem *selectedItem = selectedList.first(); ILVItem *item = dynamic_cast(selectedItem); if (!item) { qDebug() << Q_FUNC_INFO << "no selected item to remove"; return; } emit itemRemoved( item->data(0, ILVItem::DataRole_ID).toString() /*key( 0, 0 ) */ ); ILVItem *parent = dynamic_cast(item->QTreeWidgetItem::parent()); delete item; // Get rid of the category as well if it has no children if ( parent && !parent->childCount() /* firstChild() */ ) { m_categories.removeAll(parent->text(0)); delete parent; } } void ItemSelector::setListCaption( const QString &caption ) { //setColumnText( 0, caption ); // 2018.08.12 - see below setHeaderLabel( caption ); } void ItemSelector::slotItemSelected( ) { QTreeWidgetItem *item = selectedItem(); if (!item) { return; } emit itemSelected( item->data(0, ILVItem::DataRole_ID).toString() /* item->key( 0, 0 ) */ ); } void ItemSelector::slotItemClicked( QTreeWidgetItem *item, int ) { if (!item) return; if ( ItemDocument * itemDocument = dynamic_cast(DocManager::self()->getFocusedDocument()) ) itemDocument->slotUnsetRepeatedItemId(); const QString &itemIdString = item->data(0, ILVItem::DataRole_ID).toString(); emit itemClicked( itemIdString /* item->key( 0, 0 ) */ ); } void ItemSelector::slotItemDoubleClicked( QTreeWidgetItem *item, int ) { if (!item) return; //QString id = item->key( 0, 0 ); const QString &id = item->data(0, ILVItem::DataRole_ID).toString(); if ( Document * doc = DocManager::self()->getFocusedDocument() ) { if ( doc->type() == Document::dt_flowcode && id.startsWith("flow/") ) (static_cast(doc))->slotSetRepeatedItemId(id); else if ( doc->type() == Document::dt_circuit && (id.startsWith("ec/") || id.startsWith("sc/")) ) (static_cast(doc))->slotSetRepeatedItemId(id); else if ( doc->type() == Document::dt_mechanics && id.startsWith("mech/") ) (static_cast(doc))->slotSetRepeatedItemId(id); } emit itemDoubleClicked(id); } #if 0 // 2018.08.12 - needed? // Q3DragObject* ItemSelector::dragObject() // { // const QString &id = currentItem()->data(0, ILVItem::DataRole_ID).toString() /* key(0,0) */; // // Q3StoredDrag * d = nullptr; // // if ( id.startsWith("flow/") ) // d = new Q3StoredDrag( "ktechlab/flowpart", this ); // // else if ( id.startsWith("ec/") ) // d = new Q3StoredDrag( "ktechlab/component", this ); // // else if ( id.startsWith("sc/") ) // d = new Q3StoredDrag( "ktechlab/subcircuit", this ); // // else if ( id.startsWith("mech/") ) // d = new Q3StoredDrag( "ktechlab/mechanical", this ); // // if (d) // { // QByteArray data; // QDataStream stream( &data, QIODevice::WriteOnly ); // stream << id; // d->setEncodedData(data); // } else { // qWarning() << Q_FUNC_INFO << " null drag returned"; // } // // // A pixmap cursor is often hard to make out // // QPixmap *pixmap = const_cast(currentItem()->pixmap(0)); // // if (pixmap) d->setPixmap(*pixmap); // // return d; // } #endif //BEGIN class ComponentSelector ComponentSelector * ComponentSelector::m_pSelf = nullptr; ComponentSelector * ComponentSelector::self( KateMDI::ToolView * parent ) { if (!m_pSelf) { assert(parent); m_pSelf = new ComponentSelector(parent); } return m_pSelf; } ComponentSelector::ComponentSelector( KateMDI::ToolView * parent ) : ItemSelector( parent, "Component Selector" ) { qDebug() << Q_FUNC_INFO << " creating " << this; setWhatsThis( i18n( "Add components to the circuit diagram by dragging them into the circuit.

" "To add more than one component of the same type, doubleclick on a component, and click repeatedly in the circuit to place the component. Right click to stop placement.

" "Some components (such as subcircuits) can be removed by right clicking on the item and selecting \"Remove\"." ) ); setListCaption( i18n("Component") ); LibraryItemList *items = itemLibrary()->items(); qDebug() << Q_FUNC_INFO << " there are " << items->count() << " items"; const LibraryItemList::iterator end = items->end(); for ( LibraryItemList::iterator it = items->begin(); it != end; ++it ) { if ( (*it)->type() == LibraryItem::lit_component ) addItem( (*it)->name(), (*it)->activeID(), (*it)->category(), (*it)->icon16() ); } } //END class ComponentSelector //BEGIN class FlowPartSelector FlowPartSelector * FlowPartSelector::m_pSelf = nullptr; FlowPartSelector * FlowPartSelector::self( KateMDI::ToolView * parent ) { if (!m_pSelf) { assert(parent); m_pSelf = new FlowPartSelector(parent); } return m_pSelf; } FlowPartSelector::FlowPartSelector( KateMDI::ToolView * parent ) : ItemSelector( (QWidget*)parent, "Part Selector" ) { setWhatsThis( i18n("Add FlowPart to the FlowCode document by dragging them there.

To add more than one FlowPart of the same type, doubleclick on a FlowPart, and click repeatedly in the FlowChart to place the component. Right click to stop placement.") ); setListCaption( i18n("Flow Part") ); LibraryItemList *items = itemLibrary()->items(); const LibraryItemList::iterator end = items->end(); for ( LibraryItemList::iterator it = items->begin(); it != end; ++it ) { if ( (*it)->type() == LibraryItem::lit_flowpart ) addItem( (*it)->name(), (*it)->activeID(), (*it)->category(), (*it)->icon16() ); } } //END class FlowPartSelector //BEGIN class MechanicsSelector MechanicsSelector * MechanicsSelector::m_pSelf = nullptr; MechanicsSelector * MechanicsSelector::self( KateMDI::ToolView * parent ) { if (!m_pSelf) { assert(parent); m_pSelf = new MechanicsSelector( (QWidget*)parent ); } return m_pSelf; } MechanicsSelector::MechanicsSelector( QWidget *parent ) : ItemSelector( (QWidget*)parent, "Mechanics Selector" ) { setWhatsThis( i18n("Add mechanical parts to the mechanics work area by dragging them there.") ); LibraryItemList *items = itemLibrary()->items(); const LibraryItemList::iterator end = items->end(); for ( LibraryItemList::iterator it = items->begin(); it != end; ++it ) { if ( (*it)->type() == LibraryItem::lit_mechanical ) { addItem( (*it)->name(), (*it)->activeID(), (*it)->category(), (*it)->icon16() ); } } } //END class MechanicsSelector diff --git a/src/gui/itemselector.h b/src/gui/itemselector.h index b8f4c8a4..5638be28 100644 --- a/src/gui/itemselector.h +++ b/src/gui/itemselector.h @@ -1,184 +1,184 @@ /*************************************************************************** * Copyright (C) 2003-2006 by David Saxton * * david@bluehaze.org * * * * 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. * ***************************************************************************/ #ifndef ITEMSELECTOR_H #define ITEMSELECTOR_H // #include // gone in kde4.. -#include -#include -#include +#include +#include +#include // #include // #include class ProjectItem; // class Q3StoredDrag; // 2018.08.12 - move to QTreeWidget class QMimeData; namespace KateMDI { class ToolView; } /** @short Contains info about item for ItemSelector @author David Saxton */ class ILVItem : public QObject, public QTreeWidgetItem /* K3ListViewItem */ { public: enum { DataRole_ID = Qt::UserRole, // note: add here isRemovable, projectItem } ILVItemRole; ILVItem( QTreeWidget *parent, const QString &id ); ILVItem( QTreeWidgetItem *parent, const QString &id ); void setProjectItem( ProjectItem * projectItem ) { m_pProjectItem = projectItem; } ProjectItem * projectItem() const { return m_pProjectItem; } // QString id() const { return m_id; } // 2018.08.12 - use value() // QString key( int, bool ) const { return m_id; } // 2018.08.12 - use value() /** * Set whether the item can be removed from the listview by the user */ void setRemovable( bool isRemovable ) { b_isRemovable = isRemovable; } /** * Whether the item can be removed from the listview by the user */ bool isRemovable() const { return b_isRemovable; } protected: //QString m_id; // 2018.08.12 - use value() bool b_isRemovable; ProjectItem * m_pProjectItem; }; /** @short Allows selection of generic items for dragging / clicking @author David Saxton */ class ItemSelector : public QTreeWidget /* K3ListView */ { Q_OBJECT public: ItemSelector( QWidget *parent, const char *name ); ~ItemSelector() override; /** * Adds a listview item to the ListView * @param caption The displayed text * @param id A unique identification for when it is dragged or activated * @param category The category it is in, eg "Integrated Circuits * @param icon The icon to be displayed to the left of the text * @param removable Whether the user can right-click on the item and select Remove */ void addItem( const QString & caption, const QString & id, const QString & category, const QPixmap & icon = QPixmap(), bool removable = false ); public slots: virtual void slotContextMenuRequested(const QPoint& pos); virtual void clear(); void slotRemoveSelectedItem(); signals: /** * Emitted when a user selects an item and removes it */ void itemRemoved( const QString &id ); void itemDoubleClicked( const QString &id ); void itemClicked( const QString &id ); void itemSelected( const QString & id ); protected: /** * Sets the caption of the ListView (eg 'Components' or 'Files') */ void setListCaption( const QString &caption ); /** * Writes the open status (folded or unfolded) of "parent" items in the view * to the config file. */ void writeOpenStates(); /** * Reads the open status (folded or unfolded) of the given item. The default * status for non-existant items is true. */ bool readOpenState( const QString &id ); QTreeWidgetItem *selectedItem() const ; QMimeData * mimeData(const QList items) const override ; private slots: void slotItemSelected( ); void slotItemClicked( QTreeWidgetItem *item, int ); void slotItemDoubleClicked( QTreeWidgetItem*, int ); private: /** * @return a dragobject encoding the currently selected component item. */ // Q3DragObject * dragObject(); QStringList m_categories; }; /** @short Allows selection of electrical components @author David Saxton */ class ComponentSelector : public ItemSelector { Q_OBJECT public: static ComponentSelector * self( KateMDI::ToolView * parent = nullptr ); static QString toolViewIdentifier() { return "ComponentSelector"; } private: ComponentSelector( KateMDI::ToolView * parent ); static ComponentSelector * m_pSelf; }; /** @short Allows selection of PIC parts (eg 'Pause') @author David Saxton */ class FlowPartSelector : public ItemSelector { Q_OBJECT public: static FlowPartSelector * self( KateMDI::ToolView * parent = nullptr ); static QString toolViewIdentifier() { return "FlowPartSelector"; } private: FlowPartSelector( KateMDI::ToolView * parent ); static FlowPartSelector * m_pSelf; }; /** @author David Saxton */ class MechanicsSelector : public ItemSelector { Q_OBJECT public: static MechanicsSelector * self( KateMDI::ToolView * parent = nullptr ); static QString toolViewIdentifier() { return "MechanicsSelector"; } private: MechanicsSelector( QWidget *parent = nullptr ); static MechanicsSelector * m_pSelf; }; #endif diff --git a/src/gui/logview.cpp b/src/gui/logview.cpp index 45fa4078..42bd4274 100644 --- a/src/gui/logview.cpp +++ b/src/gui/logview.cpp @@ -1,156 +1,156 @@ /*************************************************************************** * Copyright (C) 2003-2005 by David Saxton * * david@bluehaze.org * * * * 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 "logview.h" -#include #include #include +#include // #include -#include -#include -#include +#include +#include +#include //BEGIN class LogView LogView::LogView( KateMDI::ToolView * parent, const char *name ) : KTextEdit( parent /* , name */ ) { setObjectName(name); if (parent->layout()) { parent->layout()->addWidget(this); qDebug() << Q_FUNC_INFO << " added item selector to parent's layout " << parent; } else { qWarning() << Q_FUNC_INFO << " unexpected null layout on parent " << parent ; } setReadOnly(true); //setPaper( Qt::white ); // TODO re-enable this, get an equivalent //setTextFormat( Qt::LogText ); // 2018.12.07 - use toHtml() and html() methods //setWordWrap( WidgetWidth ); setWordWrapMode( QTextOption::WrapAtWordBoundaryOrAnywhere ); setFocusPolicy( Qt::NoFocus ); // Connect up signal emitted when the user doubleclicks on a paragraph in the log view // connect( this, SIGNAL(clicked(int,int)), this, SLOT(slotParaClicked(int,int)) ); // ^ reimplemented by: mouseDoubleClickEvent() } LogView::~LogView() { } void LogView::clear() { m_messageInfoMap.clear(); KTextEdit::clear(); } void LogView::addOutput( QString text, OutputType outputType, MessageInfo messageInfo ) { tidyText(text); switch(outputType) { case LogView::ot_important: append( QString("%1").arg(text) ); break; case LogView::ot_info: append( QString("%1").arg(text) ); break; case LogView::ot_message: append( QString("%1").arg(text) ); break; case LogView::ot_warning: append( QString("%1").arg(text) ); break; case LogView::ot_error: append( QString("%1").arg(text) ); break; } //m_messageInfoMap[ paragraphs()-1 ] = messageInfo; m_messageInfoMap[ document()->blockCount()-1 ] = messageInfo; } void LogView::mouseDoubleClickEvent(QMouseEvent* e) { QTextCursor curs = cursorForPosition( e->pos() ); slotParaClicked(curs.blockNumber(), curs.columnNumber()); QTextEdit::mouseDoubleClickEvent(e); // note: is this needed? } void LogView::slotParaClicked( int para, int /*pos*/ ) { //QString t = text(para); QString t = document()->findBlockByLineNumber(para).text(); untidyText(t); emit paraClicked( t, m_messageInfoMap[para] ); } void LogView::tidyText( QString &t ) { t.replace( "&", "&" ); t.replace( "<", "<" ); t.replace( ">", ">" ); } void LogView::untidyText( QString &t ) { t.replace( "<", "<" ); t.replace( ">", ">" ); t.replace( "&", "&" ); } QMenu * LogView::createPopupMenu( const QPoint & pos ) { QMenu * menu = KTextEdit::createStandardContextMenu( pos ); //menu->insertSeparator(); // 2018.12.07 menu->addSeparator(); //int id = menu->insertItem( i18n("Clear All"), this, SLOT(clear()) ); // 2018.12.07 QAction *clearAllAct = menu->addAction( i18n("Clear All"), this, SLOT(clear()) ); clearAllAct->setEnabled( document()->blockCount() > 1 ); //// "an empty textedit is always considered to have one paragraph" - qt documentation //// although this does not always seem to be the case, so I don't know... ////menu->setItemEnabled( id, paragraphs() > 1 ); //menu->setItemEnabled( id, document()->blockCount() > 1 ); return menu; } //END class LogView //BEGIN class MessageInfo MessageInfo::MessageInfo() { m_fileLine = -1; } MessageInfo::MessageInfo( QString fileURL, int fileLine ) { m_fileURL = fileURL; m_fileLine = fileLine; } //END class MessageInfo diff --git a/src/gui/logview.h b/src/gui/logview.h index 8f1c1dda..df5c9cca 100644 --- a/src/gui/logview.h +++ b/src/gui/logview.h @@ -1,88 +1,88 @@ /*************************************************************************** * Copyright (C) 2003-2005 by David Saxton * * david@bluehaze.org * * * * 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. * ***************************************************************************/ #ifndef LOGVIEW_H #define LOGVIEW_H class KTechlab; // class Q3PopupMenu; #include -#include +#include namespace KateMDI { class ToolView; } class MessageInfo { public: MessageInfo(); MessageInfo( QString fileURL, int fileLine ); QString fileURL() const { return m_fileURL; } int fileLine() const { return m_fileLine; } protected: QString m_fileURL; int m_fileLine; }; typedef QMap MessageInfoMap; /** Base class for logviews (eg GpasmInterface) which output information, warnings, errors to a viewable log @short Dockable logview @author David Saxton */ class LogView : public KTextEdit { Q_OBJECT public: LogView( KateMDI::ToolView * parent, const char *name = nullptr ); ~LogView() override; enum OutputType { ot_important, // Bold ot_info, // Italic ot_message, // Plain ot_warning, // Grey ot_error // Red }; signals: /** * Emitted when the user clicks on a paragraph in the log view */ void paraClicked( const QString &text, MessageInfo messageInfo ); public slots: virtual void clear(); void addOutput( QString text, OutputType outputType, MessageInfo messageInfo = MessageInfo() ); protected: virtual QMenu * createPopupMenu( const QPoint & pos ); /** * Replaces "&" with &, "<" with <, etc */ void tidyText( QString &t ); /** * Replaces "<" with "<", "&" with "&", etc */ void untidyText( QString &t ); MessageInfoMap m_messageInfoMap; void mouseDoubleClickEvent ( QMouseEvent * e ) override; private slots: void slotParaClicked( int para, int pos ); }; #endif diff --git a/src/gui/microselectwidget.cpp b/src/gui/microselectwidget.cpp index c0b06900..c5fab0f4 100644 --- a/src/gui/microselectwidget.cpp +++ b/src/gui/microselectwidget.cpp @@ -1,191 +1,191 @@ /*************************************************************************** * Copyright (C) 2005 by David Saxton * * david@bluehaze.org * * * * 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 "asminfo.h" #include "microinfo.h" #include "microlibrary.h" #include "microselectwidget.h" #include #include // #include -#include -#include -#include +#include +#include +#include MicroSelectWidget::MicroSelectWidget( QWidget* parent, const char* name, Qt::WFlags ) //: Q3GroupBox( 4, Qt::Horizontal, i18n("Microprocessor"), parent, name ) : QGroupBox(parent /*, name */ ) { setObjectName( name ); setLayout(new QHBoxLayout); setTitle(i18n("Microprocessor")); m_allowedAsmSet = AsmInfo::AsmSetAll; m_allowedGpsimSupport = m_allowedFlowCodeSupport = m_allowedMicrobeSupport = MicroInfo::AllSupport; if ( !name ) { setObjectName( "MicroSelectWidget" ); } m_pMicroFamilyLabel = new QLabel( nullptr ); m_pMicroFamilyLabel->setObjectName( "m_pMicroFamilyLabel" ); m_pMicroFamilyLabel->setText( i18n("Family") ); layout()->addWidget( m_pMicroFamilyLabel ); m_pMicroFamily = new KComboBox( false ); //, "m_pMicroFamily" ); m_pMicroFamily->setObjectName("m_pMicroFamily"); m_pMicroFamily->setSizePolicy( QSizePolicy::MinimumExpanding, QSizePolicy::Preferred ); layout()->addWidget( m_pMicroFamily ); m_pMicroLabel = new QLabel( nullptr /*, "m_pMicroLabel" */ ); m_pMicroLabel->setObjectName("m_pMicroLabel"); m_pMicroLabel->setText( i18n("Micro") ); layout()->addWidget( m_pMicroLabel ); m_pMicro = new KComboBox( false ); //, "m_pMicro" ); m_pMicro->setObjectName("m_pMicro"); m_pMicro->setSizePolicy( QSizePolicy::MinimumExpanding, QSizePolicy::Preferred ); m_pMicro->setEditable( true ); m_pMicro->setAutoCompletion(true); layout()->addWidget( m_pMicro ); updateFromAllowed(); setMicro("P16F84"); connect( m_pMicroFamily, SIGNAL(activated(const QString & )), this, SLOT(microFamilyChanged(const QString& )) ); } MicroSelectWidget::~MicroSelectWidget() { } void MicroSelectWidget::setAllowedAsmSet( unsigned allowed ) { m_allowedAsmSet = allowed; updateFromAllowed(); } void MicroSelectWidget::setAllowedGpsimSupport( unsigned allowed ) { m_allowedGpsimSupport = allowed; updateFromAllowed(); } void MicroSelectWidget::setAllowedFlowCodeSupport( unsigned allowed ) { m_allowedFlowCodeSupport = allowed; updateFromAllowed(); } void MicroSelectWidget::setAllowedMicrobeSupport( unsigned allowed ) { m_allowedMicrobeSupport = allowed; updateFromAllowed(); } void MicroSelectWidget::updateFromAllowed() { QString oldFamily = m_pMicroFamily->currentText(); m_pMicroFamily->clear(); #define CHECK_ADD(family) \ if ( (m_allowedAsmSet & AsmInfo::family) \ && !MicroLibrary::self()->microIDs( AsmInfo::family, \ m_allowedGpsimSupport, m_allowedFlowCodeSupport, m_allowedMicrobeSupport ).isEmpty() ) { \ m_pMicroFamily->insertItem( m_pMicroFamily->count(), AsmInfo::setToString(AsmInfo::family) ); \ } CHECK_ADD(PIC12) CHECK_ADD(PIC14) CHECK_ADD(PIC16); #undef CHECK_ADD if ( m_pMicroFamily->contains(oldFamily) ) { //m_pMicroFamily->setCurrentText(oldFamily); // 2018.12.07 { QComboBox *c = m_pMicroFamily; QString text = oldFamily; int i = c->findText(text); if (i != -1) c->setCurrentIndex(i); else if (c->isEditable()) c->setEditText(text); else c->setItemText(c->currentIndex(), text); } } microFamilyChanged(oldFamily); } void MicroSelectWidget::setMicro( const QString & id ) { MicroInfo * info = MicroLibrary::self()->microInfoWithID(id); if (!info) return; m_pMicro->clear(); m_pMicro->insertItems( m_pMicro->count(), MicroLibrary::self()->microIDs( info->instructionSet()->set() ) ); //m_pMicro->setCurrentText(id); // 2018.12.07 { QComboBox *c = m_pMicro; QString text = id; int i = c->findText(text); if (i != -1) c->setCurrentIndex(i); else if (c->isEditable()) c->setEditText(text); else c->setItemText(c->currentIndex(), text); } //m_pMicroFamily->setCurrentText( AsmInfo::setToString( info->instructionSet()->set() ) ); // 2018.12.07 { QComboBox *c = m_pMicroFamily; QString text = AsmInfo::setToString( info->instructionSet()->set() ); int i = c->findText(text); if (i != -1) c->setCurrentIndex(i); else if (c->isEditable()) c->setEditText(text); else c->setItemText(c->currentIndex(), text); } } QString MicroSelectWidget::micro() const { return m_pMicro->currentText(); } void MicroSelectWidget::microFamilyChanged( const QString & family ) { QString oldID = m_pMicro->currentText(); m_pMicro->clear(); m_pMicro->insertItems( m_pMicro->count(), MicroLibrary::self()->microIDs( AsmInfo::stringToSet(family), m_allowedGpsimSupport, m_allowedFlowCodeSupport, m_allowedMicrobeSupport ) ); if ( m_pMicro->contains(oldID) ) { //m_pMicro->setCurrentText(oldID); // 2018.12.07 { int i = m_pMicro->findText(oldID); if (i != -1) m_pMicro->setCurrentIndex(i); else if (m_pMicro->isEditable()) m_pMicro->setEditText(oldID); else m_pMicro->setItemText(m_pMicro->currentIndex(), oldID); } } } diff --git a/src/gui/microselectwidget.h b/src/gui/microselectwidget.h index bcab0af3..a7e63893 100644 --- a/src/gui/microselectwidget.h +++ b/src/gui/microselectwidget.h @@ -1,74 +1,74 @@ /*************************************************************************** * Copyright (C) 2005 by David Saxton * * david@bluehaze.org * * * * 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. * ***************************************************************************/ #ifndef MICROSELECTWIDGET_H #define MICROSELECTWIDGET_H // #include -#include +#include class QVBoxLayout; class QHBoxLayout; class QGridLayout; class QSpacerItem; class QGroupBox; class QLabel; class KComboBox; /** @author David Saxton */ class MicroSelectWidget : public QGroupBox { Q_OBJECT public: MicroSelectWidget( QWidget* parent = nullptr, const char* name = nullptr, Qt::WindowFlags f = {} ); ~MicroSelectWidget() override; void setMicro( const QString & id ); QString micro() const; /** * @see MicroLibrary::microIDs */ void setAllowedAsmSet( unsigned allowed ); /** * @see MicroLibrary::microIDs */ void setAllowedGpsimSupport( unsigned allowed ); /** * @see MicroLibrary::microIDs */ void setAllowedFlowCodeSupport( unsigned allowed ); /** * @see MicroLibrary::microIDs */ void setAllowedMicrobeSupport( unsigned allowed ); protected slots: void microFamilyChanged( const QString & family ); protected: void updateFromAllowed(); unsigned int m_allowedAsmSet; unsigned int m_allowedGpsimSupport; unsigned int m_allowedFlowCodeSupport; unsigned int m_allowedMicrobeSupport; QHBoxLayout * m_pWidgetLayout; QLabel * m_pMicroFamilyLabel; KComboBox * m_pMicroFamily; QLabel * m_pMicroLabel; KComboBox * m_pMicro; }; #endif diff --git a/src/gui/microsettingsdlg.cpp b/src/gui/microsettingsdlg.cpp index 27c3255d..f759fa0b 100644 --- a/src/gui/microsettingsdlg.cpp +++ b/src/gui/microsettingsdlg.cpp @@ -1,487 +1,487 @@ /*************************************************************************** * Copyright (C) 2003-2005 by David Saxton * * david@bluehaze.org * * * * 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 "microinfo.h" #include "microsettings.h" #include "microsettingsdlg.h" #include "ui_microsettingswidget.h" #include "micropackage.h" #include "pinmapping.h" #include "inputdialog.h" #include -#include #include #include #include -#include -#include -#include +#include +#include +#include +#include #include #include #include // #include #include class MicroSettingsWidget : public QWidget, public Ui::MicroSettingsWidget { public: MicroSettingsWidget(QWidget *parent) : QWidget(parent) { setupUi(this); } }; class NewPinMappingWidget : public QWidget, public Ui::NewPinMappingWidget { public: NewPinMappingWidget(QWidget *parent) :QWidget(parent) { setupUi(this); } }; MicroSettingsDlg::MicroSettingsDlg( MicroSettings * microSettings, QWidget *parent, const char *name ) : QDialog(parent) { setObjectName(name); setModal(true); setWindowTitle(i18n("PIC Settings")); QVBoxLayout *mainLayout = new QVBoxLayout; setLayout(mainLayout); m_pMicroSettings = microSettings; m_pNewPinMappingWidget = nullptr; m_pNewPinMappingDlg = nullptr; m_pNewPinMappingOkButton = nullptr; m_pWidget = new MicroSettingsWidget(this); setWhatsThis( i18n("This dialog allows editing of the initial properties of the PIC") ); m_pWidget->portsGroupBox->setWhatsThis( i18n("Edit the initial value of the ports here. For each binary number, the order from right-to-left is pins 0 through 7.

The \"Type (TRIS)\" edit shows the initial input/output state of the ports; 1 represents an input, and 0 an output.

The \"State (PORT)\" edit shows the initial high/low state of the ports; 1 represents a high, and 0 a low.") ); m_pWidget->variables->setWhatsThis( i18n("Edit the initial value of the variables here.

Note that the value of the variable can only be in the range 0->255. These variables will be initialized before any other code is executed.") ); //BEGIN Initialize initial port settings m_portNames = microSettings->microInfo()->package()->portNames(); m_portTypeEdit.resize( m_portNames.size() /*, 0 - 2018.06.02 - initialized below */); m_portStateEdit.resize( m_portNames.size() /*, 0 - 2018.06.02 - initialized below */ ); uint row = 0; QStringList::iterator end = m_portNames.end(); for ( QStringList::iterator it = m_portNames.begin(); it != end; ++it, ++row ) { //BEGIN Get current Type / State text QString portType = QString::number( microSettings->portType(*it), 2 ); QString portState = QString::number( microSettings->portState(*it), 2 ); QString fill; fill.fill( '0', 8-portType.length() ); portType.prepend(fill); fill.fill( '0', 8-portState.length() ); portState.prepend(fill); //END Get current Type / State text QGroupBox * groupBox = new QGroupBox( *it, m_pWidget->portsGroupBox ); //groupBox->setColumnLayout(0, Qt::Vertical ); // 2018.06.02 - not needed groupBox->setLayout(new QVBoxLayout); groupBox->layout()->setSpacing( 6 ); groupBox->layout()->setMargin( 11 ); QGridLayout * groupBoxLayout = new QGridLayout( groupBox /*groupBox->layout() */ ); groupBoxLayout->setAlignment( Qt::AlignTop ); groupBoxLayout->setSpacing(groupBox->layout()->spacing()); groupBox->layout()->addItem(groupBoxLayout); // TODO: replace this with i18n( "the type", "Type (TRIS register):" ); groupBoxLayout->addWidget( new QLabel( i18n("Type (TRIS register):"), groupBox ), 0, 0 ); groupBoxLayout->addWidget( new QLabel( i18n("State (PORT register):"), groupBox ), 1, 0 ); m_portTypeEdit[row] = new KLineEdit( portType, groupBox ); groupBoxLayout->addWidget( m_portTypeEdit[row], 0, 1 ); m_portStateEdit[row] = new KLineEdit( portState, groupBox ); groupBoxLayout->addWidget( m_portStateEdit[row], 1, 1 ); // (dynamic_cast(m_pWidget->portsGroupBox->layout()))->insertWidget( row, groupBox ); (dynamic_cast(m_pWidget->portsGroupBox->layout()))->addWidget( groupBox ); } //END Initialize initial port settings //BEGIN Initialize initial variable settings // Hide row headers //m_pWidget->variables->setLeftMargin(0); // 2018.06.02 - fixed in UI file // Make columns as thin as possible //m_pWidget->variables->setColumnStretchable( 0, true ); // 2018.06.02 - to be fixed //m_pWidget->variables->setColumnStretchable( 1, true ); // 2018.06.02 - to be fixed { QStringList headerLabels; headerLabels.append( i18n("Variable name") ); headerLabels.append( i18n("Variable value") ); m_pWidget->variables->setHorizontalHeaderLabels(headerLabels); } QStringList variableNames = microSettings->variableNames(); row = 0; end = variableNames.end(); for ( QStringList::iterator it = variableNames.begin(); it != end; ++it ) { VariableInfo *info = microSettings->variableInfo(*it); if (info) { qDebug() << Q_FUNC_INFO << "add var: " << *it << " val: " << info->valueAsString(); m_pWidget->variables->insertRow( row ); QTableWidgetItem *varNameItem = new QTableWidgetItem( *it ); m_pWidget->variables->setItem(row, 0, varNameItem); QTableWidgetItem *varValItem = new QTableWidgetItem( info->valueAsString() ); m_pWidget->variables->setItem(row, 1, varValItem); ++row; } } m_pWidget->variables->insertRow( row ); qDebug() << Q_FUNC_INFO << "row count: " << m_pWidget->variables->rowCount(); connect( m_pWidget->variables, SIGNAL(cellChanged(int,int)), this, SLOT(checkAddVariableRow()) ); //END Initialize initial variable settings //BEGIN Initialize pin maps connect( m_pWidget->pinMapAdd, SIGNAL(clicked()), this, SLOT(slotCreatePinMap()) ); connect( m_pWidget->pinMapModify, SIGNAL(clicked()), this, SLOT(slotModifyPinMap()) ); connect( m_pWidget->pinMapRename, SIGNAL(clicked()), this, SLOT(slotRenamePinMap()) ); connect( m_pWidget->pinMapRemove, SIGNAL(clicked()), this, SLOT(slotRemovePinMap()) ); m_pinMappings = microSettings->pinMappings(); m_pWidget->pinMapCombo->insertItems(m_pWidget->pinMapCombo->count(), m_pinMappings.keys() ); updatePinMapButtons(); //END Initialize pin maps mainLayout->addWidget(m_pWidget); QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok|QDialogButtonBox::Cancel|QDialogButtonBox::Apply); mainLayout->addWidget(buttonBox); QPushButton *okButton = buttonBox->button(QDialogButtonBox::Ok); okButton->setDefault(true); okButton->setShortcut(Qt::CTRL | Qt::Key_Return); connect(buttonBox, &QDialogButtonBox::accepted, this, &QDialog::accept); connect(buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject); connect(buttonBox->button(QDialogButtonBox::Apply), &QPushButton::clicked, this, &MicroSettingsDlg::slotApplyClicked); m_pWidget->adjustSize(); adjustSize(); } MicroSettingsDlg::~MicroSettingsDlg() { } void MicroSettingsDlg::accept() { slotSaveStuff(); QDialog::accept(); deleteLater(); } void MicroSettingsDlg::slotApplyClicked() { slotSaveStuff(); emit applyClicked(); } void MicroSettingsDlg::slotSaveStuff() { for ( unsigned i = 0; i < m_portNames.size(); i++ ) savePort(i); m_pMicroSettings->removeAllVariables(); for ( int i=0; i< m_pWidget->variables->rowCount(); i++ ) saveVariable(i); m_pMicroSettings->setPinMappings( m_pinMappings ); } void MicroSettingsDlg::reject() { QDialog::reject(); deleteLater(); } QValidator::State MicroSettingsDlg::validatePinMapName( QString & name ) const { name.replace( ' ', '_' ); if ( name.isEmpty() ) return QValidator::Intermediate; for ( unsigned i = 0; i < name.length(); ++i ) { if ( !name[i].isLetterOrNumber() && name[i] != '_' ) return QValidator::Invalid; } if ( name[0].isNumber() ) return QValidator::Intermediate; if ( m_pWidget->pinMapCombo->contains( name ) ) return QValidator::Intermediate; return QValidator::Acceptable; } void MicroSettingsDlg::slotCheckNewPinMappingName( const QString & name ) { // Validate name might change the name so that it is valid QString newName = name; if ( m_pNewPinMappingOkButton ) { m_pNewPinMappingOkButton->setEnabled( validatePinMapName( newName ) == QValidator::Acceptable ); } if ( newName != name ) m_pNewPinMappingWidget->nameEdit->setText( newName ); } void MicroSettingsDlg::slotCreatePinMap() { m_pNewPinMappingDlg = new QDialog( this); m_pNewPinMappingDlg->setObjectName( "New Pin Mapping Dlg" ); m_pNewPinMappingDlg->setModal( true ); m_pNewPinMappingDlg->setWindowTitle(i18n("New Pin Mapping")); QVBoxLayout *mainLayout = new QVBoxLayout; m_pNewPinMappingDlg->setLayout(mainLayout); m_pNewPinMappingWidget = new NewPinMappingWidget( m_pNewPinMappingDlg ); mainLayout->addWidget(m_pNewPinMappingWidget); QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok|QDialogButtonBox::Cancel); m_pNewPinMappingOkButton = buttonBox->button(QDialogButtonBox::Ok); m_pNewPinMappingOkButton->setText(i18n("Create" )); m_pNewPinMappingOkButton->setDefault(true); m_pNewPinMappingOkButton->setShortcut(Qt::CTRL | Qt::Key_Return); connect(buttonBox, &QDialogButtonBox::accepted, m_pNewPinMappingDlg, &QDialog::accept); connect(buttonBox, &QDialogButtonBox::rejected, m_pNewPinMappingDlg, &QDialog::reject); mainLayout->addWidget(buttonBox); PinMappingNameValidator * validator = new PinMappingNameValidator( this ); m_pNewPinMappingWidget->nameEdit->setValidator( validator ); connect( m_pNewPinMappingWidget->nameEdit, SIGNAL(textChanged(const QString &)), this, SLOT(slotCheckNewPinMappingName(const QString &)) ); slotCheckNewPinMappingName( nullptr ); int accepted = m_pNewPinMappingDlg->exec(); unsigned selectedType = m_pNewPinMappingWidget->typeCombo->currentIndex(); QString name = m_pNewPinMappingWidget->nameEdit->text(); delete m_pNewPinMappingDlg; delete validator; m_pNewPinMappingDlg = nullptr; m_pNewPinMappingWidget = nullptr; m_pNewPinMappingOkButton = nullptr; if ( accepted != QDialog::Accepted ) return; PinMapping::Type type = PinMapping::Invalid; switch ( selectedType ) { case 0: type = PinMapping::SevenSegment; break; case 1: type = PinMapping::Keypad_4x3; break; case 2: type = PinMapping::Keypad_4x4; break; default: qCritical() << Q_FUNC_INFO << "Unknown selected type " << type << endl; break; } m_pinMappings[name] = PinMapping( type ); m_pWidget->pinMapCombo->insertItem( m_pWidget->pinMapCombo->count(), name ); //m_pWidget->pinMapCombo->setCurrentItem( m_pWidget->pinMapCombo->count() - 1 ); m_pWidget->pinMapCombo->setCurrentItem( name ); updatePinMapButtons(); slotModifyPinMap(); } void MicroSettingsDlg::slotRenamePinMap() { KComboBox * combo = m_pWidget->pinMapCombo; QString oldName = combo->currentText(); if ( oldName.isEmpty() ) return; PinMappingNameValidator * validator = new PinMappingNameValidator( this, oldName ); bool ok = false; QString newName = InputDialog::getText( i18n("New Pin Map Name"), i18n("Name"), oldName, & ok, this,/* 0, */ validator ); delete validator; if ( !ok ) return; if ( newName == oldName ) return; m_pinMappings[ newName ] = m_pinMappings[ oldName ]; m_pinMappings.remove( oldName ); //combo->setCurrentText( newName ); // 2018.12.02 combo->setItemText( combo->currentIndex(), newName ); } void MicroSettingsDlg::slotModifyPinMap() { QString name = m_pWidget->pinMapCombo->currentText(); PinMapping pinMapping = m_pinMappings[ name ]; PinMapEditor * pinMapEditor = new PinMapEditor( & pinMapping, m_pMicroSettings->microInfo(), this, "PinMapEditor" ); int accepted = pinMapEditor->exec(); delete pinMapEditor; if ( accepted != QDialog::Accepted ) return; m_pinMappings[ name ] = pinMapping; } void MicroSettingsDlg::slotRemovePinMap() { KComboBox * combo = m_pWidget->pinMapCombo; QString pinMapID = combo->currentText(); if ( pinMapID.isEmpty() ) return; m_pinMappings.remove( pinMapID ); combo->removeItem( combo->currentIndex() ); updatePinMapButtons(); } void MicroSettingsDlg::updatePinMapButtons() { bool havePinMaps = (m_pWidget->pinMapCombo->count() != 0); m_pWidget->pinMapModify->setEnabled( havePinMaps ); m_pWidget->pinMapRename->setEnabled( havePinMaps ); m_pWidget->pinMapRemove->setEnabled( havePinMaps ); } void MicroSettingsDlg::savePort( int row ) { QString port = m_portNames[row]; int type, state; QString typeText = m_portTypeEdit[row]->text(); bool typeOk = true; if ( typeText.startsWith( "0x", Qt::CaseInsensitive ) ) type = typeText.remove(0,2).toInt( &typeOk, 16 ); else if ( typeText.contains( QRegExp("[^01]") ) ) type = typeText.toInt( &typeOk, 10 ); else type = typeText.toInt( &typeOk, 2 ); if ( !typeOk ) { // KMessageBox::sorry( this, i18n("Unregnised Port Type: %1", typeText) ); return; } QString stateText = m_portStateEdit[row]->text(); bool stateOk = true; if ( stateText.startsWith( "0x", Qt::CaseInsensitive ) ) state = stateText.remove(0,2).toInt( &stateOk, 16 ); else if ( stateText.contains( QRegExp("[^01]") ) ) state = stateText.toInt( &stateOk, 10 ); else state = stateText.toInt( &stateOk, 2 ); if ( !stateOk ) { // KMessageBox::sorry( this, i18n("Unregnised Port State: %1", stateText) ); return; } m_pMicroSettings->setPortState( port, state ); m_pMicroSettings->setPortType( port, type ); } void MicroSettingsDlg::saveVariable( int row ) { QTableWidgetItem *nameItem = m_pWidget->variables->item( row, 0 ); if (!nameItem) { return; } QString name = nameItem->text(); if ( name.isEmpty() ) return; QTableWidgetItem *valueItem = m_pWidget->variables->item( row, 1 ); QString valueText; if (valueItem) { valueText = valueItem->text(); } int value; bool ok = true; if ( valueText.startsWith( "0x", Qt::CaseInsensitive ) ) value = valueText.remove(0,2).toInt( &ok, 16 ); else value = valueText.toInt( &ok, 10 ); if (!ok) { KMessageBox::sorry( this, i18n("Invalid variable value: %1", valueText) ); return; } qDebug() << Q_FUNC_INFO << "save variable: " << name << " val: " << value; m_pMicroSettings->setVariable( name, value, true ); VariableInfo *info = m_pMicroSettings->variableInfo(name); if ( info && info->valueAsString().toInt() != value ) { // info->setValue(value); // info->permanent = true; info->initAtStart = true; } } void MicroSettingsDlg::checkAddVariableRow() { int lastRow = m_pWidget->variables->rowCount()-1; if ( QTableWidgetItem *lastItem = m_pWidget->variables->item( lastRow, 0 ) ) { if ( !lastItem->text().isEmpty() ) { m_pWidget->variables->insertRow( lastRow+1 ); } } } diff --git a/src/gui/microsettingsdlg.h b/src/gui/microsettingsdlg.h index 4c90c99a..2695dfbe 100644 --- a/src/gui/microsettingsdlg.h +++ b/src/gui/microsettingsdlg.h @@ -1,141 +1,141 @@ /*************************************************************************** * Copyright (C) 2003-2005 by David Saxton * * david@bluehaze.org * * * * 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. * ***************************************************************************/ #ifndef MICROSETTINGSDLG_H #define MICROSETTINGSDLG_H #include -#include -#include +#include +#include // #include class KLineEdit; class MicroSettings; class MicroSettingsWidget; class NewPinMappingWidget; class PinMapping; class QPushButton; typedef QMap< QString, PinMapping > PinMappingMap; /** @author David Saxton */ class MicroSettingsDlg : public QDialog { Q_OBJECT public: MicroSettingsDlg( MicroSettings *_microSettings, QWidget *parent = nullptr, const char *name = nullptr ); ~MicroSettingsDlg() override; void reject() override; void accept() override; /** * @param pinMapName the pinMapName; may be changed to make it valid * (e.g. spaces replaced with underscores). * @returns Invalid for a pinMapName containing a non-variable name, * Intermediate for a pinMapName that starts with a number or is already * in use, and Acceptable otherwise. */ QValidator::State validatePinMapName( QString & pinMapName ) const; public slots: /** * Saves the port details in the given row to the MicroSettings class. * Usually called when the value is changed, or on 'Apply' of the * dialog. */ void savePort( int row ); /** * Saves the variable details to the MicroSettings class. */ void saveVariable( int row ); /** * Adds an extra row to the list of variable if one is required. */ void checkAddVariableRow(); /** * Called when the pinMapAdd button is pressed. */ void slotCreatePinMap(); /** * Called when the pinMapModify button is pressed. */ void slotModifyPinMap(); /** * Called when the pinMapRename button is pressed. */ void slotRenamePinMap(); /** * Called when the pinMapRemove button is pressed. */ void slotRemovePinMap(); /** * Called when the dialog is Applied or OK'd. */ void slotSaveStuff(); signals: void applyClicked(); protected slots: void slotCheckNewPinMappingName( const QString & name ); void slotApplyClicked(); protected: /** * Set each button enabled / disabled as appropriate. */ void updatePinMapButtons(); NewPinMappingWidget * m_pNewPinMappingWidget; // Used for checking that the variable name is ok QDialog * m_pNewPinMappingDlg; QPushButton *m_pNewPinMappingOkButton; MicroSettingsWidget * m_pWidget; MicroSettings * m_pMicroSettings; PinMappingMap m_pinMappings; QVector< KLineEdit * > m_portTypeEdit; QVector< KLineEdit * > m_portStateEdit; QStringList m_portNames; }; class PinMappingNameValidator : public QValidator { public: /** * Create a validator. If oldName is not empty, then the input is * allowed to be oldName. */ PinMappingNameValidator( MicroSettingsDlg * dlg, const QString & oldName = nullptr ) : QValidator(nullptr) { m_pDlg = dlg; m_oldName = oldName; } State validate( QString & input, int & ) const override { if ( (!m_oldName.isEmpty()) && (input == m_oldName) ) return QValidator::Acceptable; return m_pDlg->validatePinMapName( input ); } protected: MicroSettingsDlg * m_pDlg; QString m_oldName; }; #endif diff --git a/src/gui/newfiledlg.cpp b/src/gui/newfiledlg.cpp index c98ab4f8..0137c858 100644 --- a/src/gui/newfiledlg.cpp +++ b/src/gui/newfiledlg.cpp @@ -1,193 +1,194 @@ /*************************************************************************** * Copyright (C) 2003-2005 by David Saxton * * david@bluehaze.org * * * * 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 "config.h" #include "document.h" #include "microinfo.h" #include "newfiledlg.h" #include "microlibrary.h" #include "microselectwidget.h" #include "projectmanager.h" #include "textdocument.h" -#include +#include + #include // #include #include #include -#include -#include -#include -#include -#include +#include +#include +#include +#include +#include #include #include #include // #include #include class NewFileWidget : public QWidget, public Ui::NewFileWidget { public: NewFileWidget(QWidget *parent) : QWidget(parent) { setupUi(this); } }; NewFileDlg::NewFileDlg( QWidget *parent ) : QDialog(parent) { setObjectName("newfiledlg"); setModal(true); setWindowTitle(i18n("New File")); QVBoxLayout *mainLayout = new QVBoxLayout; setLayout(mainLayout); m_pMainParent = parent; m_pNewFileWidget = new NewFileWidget(this); m_pNewFileWidget->typeIconView->setSelectionMode(QAbstractItemView::SingleSelection /*Q3IconView::Single*/); //m_pNewFileWidget->typeIconView->setMode(K3IconView::Select); // 2017.12.01 - convert to qlistwidget m_pNewFileWidget->typeIconView->setIconSize(QSize(KIconLoader::SizeHuge, KIconLoader::SizeHuge)); KIconLoader *loader = KIconLoader::global(); QList items; //BEGIN insert icons QString text = QString("%1 (.asm)").arg(i18n("Assembly Code")); items << new QListWidgetItem(loader->loadIcon( "source", KIconLoader::NoGroup, KIconLoader::SizeHuge ), text, m_pNewFileWidget->typeIconView); text = "C (.c)"; items << new QListWidgetItem(loader->loadIcon( "text-x-csrc", KIconLoader::NoGroup, KIconLoader::SizeHuge ), text, m_pNewFileWidget->typeIconView ); text = QString("%1 (.circuit)").arg(i18n("Circuit")); items << new QListWidgetItem(loader->loadIcon( "application-x-circuit", KIconLoader::NoGroup, KIconLoader::SizeHuge ), text, m_pNewFileWidget->typeIconView); items << new QListWidgetItem(loader->loadIcon( "application-x-flowcode", KIconLoader::NoGroup, KIconLoader::SizeHuge ), "FlowCode (.flowcode)", m_pNewFileWidget->typeIconView ); #ifdef MECHANICS items << new QListWidgetItem(loader->loadIcon( "exec", KIconLoader::NoGroup, KIconLoader::SizeHuge ), "Mechanics (.mechanics)", m_pNewFileWidget->typeIconView); #endif items << new QListWidgetItem(loader->loadIcon( "application-x-microbe", KIconLoader::NoGroup, KIconLoader::SizeHuge ), "Microbe (.microbe)", m_pNewFileWidget->typeIconView); //END insert icons int minWidth = 20 + m_pNewFileWidget->typeIconView->spacing() * items.size(); int minHeight = 20; const QList::iterator end = items.end(); for ( QList::iterator it = items.begin(); it != end; ++it ) { //(*it)->setDragEnabled(false); // 2017.12.01 - use qlistwidget Qt::ItemFlags flags = (*it)->flags(); flags &= (~Qt::ItemIsDragEnabled); (*it)->setFlags(flags); QList listAvSizes = (*it)->icon().availableSizes(); if (listAvSizes.isEmpty()) { qWarning() << Q_FUNC_INFO << "no available sizes for " << (*it)->text(); } else { qDebug() << Q_FUNC_INFO << "W = " << (*it)->icon().availableSizes().first().width() << " H=" << (*it)->icon().availableSizes().first().height(); minWidth += (*it)->icon().availableSizes().first().width() + 20; minHeight = qMax( minHeight, (*it)->icon().availableSizes().first().height()+20 ); } } qDebug() << Q_FUNC_INFO << "minW = " << minWidth << " minH=" << minHeight; m_pNewFileWidget->typeIconView->setMinimumSize( minWidth, minHeight ); m_pNewFileWidget->typeIconView->setCurrentItem(items[3]); m_pNewFileWidget->addToProjectCheck->setChecked( ProjectManager::self()->currentProject() ); m_pNewFileWidget->addToProjectCheck->setEnabled( ProjectManager::self()->currentProject() ); microSelectWidget()->setAllowedFlowCodeSupport( MicroInfo::FullSupport | MicroInfo::PartialSupport ); mainLayout->addWidget(m_pNewFileWidget); // Our behaviour is to have single click selects and double click accepts the dialog connect( m_pNewFileWidget->typeIconView, SIGNAL(itemSelectionChanged()), this, SLOT(fileTypeChanged()) ); connect( m_pNewFileWidget->typeIconView, SIGNAL(doubleClicked(const QModelIndex &)), this, SLOT(accept())); QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok|QDialogButtonBox::Cancel); mainLayout->addWidget(buttonBox); QPushButton *okButton = buttonBox->button(QDialogButtonBox::Ok); okButton->setDefault(true); okButton->setShortcut(Qt::CTRL | Qt::Key_Return); connect(buttonBox, &QDialogButtonBox::accepted, this, &QDialog::accept); connect(buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject); setAcceptDrops(true); m_pNewFileWidget->typeIconView->adjustSize(); m_pNewFileWidget->adjustSize(); adjustSize(); } void NewFileDlg::accept() { QDialog::accept(); const QString fileText = m_pNewFileWidget->typeIconView->currentItem()->text(); if ( fileText.contains(".flowcode") ) m_fileType = Document::dt_flowcode; else if ( fileText.contains(".circuit") ) m_fileType = Document::dt_circuit; else if ( fileText.contains(".mechanics") ) m_fileType = Document::dt_mechanics; else if ( fileText.contains(".asm") ) { m_fileType = Document::dt_text; m_codeType = TextDocument::ct_asm; } else if ( fileText.contains(".basic") || fileText.contains(".microbe") ) { m_fileType = Document::dt_text; m_codeType = TextDocument::ct_microbe; } else if (fileText.contains(".c") ) { m_fileType = Document::dt_text; m_codeType = TextDocument::ct_c; } else m_fileType = Document::dt_text; m_bAddToProject = m_pNewFileWidget->addToProjectCheck->isChecked(); m_microID = m_pNewFileWidget->m_pMicroSelect->micro(); } void NewFileDlg::fileTypeChanged() { bool doEnableMicros = false; if (!m_pNewFileWidget->typeIconView->selectedItems().isEmpty()) { doEnableMicros = m_pNewFileWidget->typeIconView->selectedItems().first()->text().contains(".flowcode"); } m_pNewFileWidget->m_pMicroSelect->setEnabled( doEnableMicros ); } MicroSelectWidget * NewFileDlg::microSelectWidget() const { return m_pNewFileWidget->m_pMicroSelect; } diff --git a/src/gui/oscilloscope.cpp b/src/gui/oscilloscope.cpp index 92c0c3f5..f5768795 100644 --- a/src/gui/oscilloscope.cpp +++ b/src/gui/oscilloscope.cpp @@ -1,361 +1,361 @@ /*************************************************************************** * Copyright (C) 2005 by David Saxton * * david@bluehaze.org * * * * 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 "oscilloscope.h" #include "oscilloscopedata.h" #include "oscilloscopeview.h" #include "probe.h" #include "probepositioner.h" #include "simulator.h" #include "ktechlab.h" #include #include #include #include #include // #include -#include -#include -#include -#include -#include -#include +#include +#include +#include +#include +#include +#include #include //BEGIN Oscilloscope Class QColor probeColors[9] = { QColor( 0x52, 0x22, 0x00), QColor( 0xB5, 0x00, 0x2F), QColor( 0xF9, 0xBA, 0x07), QColor( 0x53, 0x93, 0x16), QColor( 0x00, 0x66, 0x2F), QColor( 0x00, 0x41, 0x88), QColor( 0x1B, 0x2D, 0x83), QColor( 0x55, 0x12, 0x7B), QColor( 0x7B, 0x0C, 0x82) }; Oscilloscope * Oscilloscope::m_pSelf = nullptr; Oscilloscope * Oscilloscope::self( KateMDI::ToolView * parent) { if( !m_pSelf) { assert(parent); m_pSelf = new Oscilloscope(parent); } return m_pSelf; } Oscilloscope::Oscilloscope( KateMDI::ToolView * parent) : QWidget(parent) { setupUi(this); m_nextColor = 0; m_nextId = 1; m_oldestId = -1; m_oldestProbe = nullptr; // b_isPaused = false; m_zoomLevel = 0.5; m_pSimulator = Simulator::self(); horizontalScroll->setSingleStep(32); horizontalScroll->setPageStep( oscilloscopeView->width()); connect( resetBtn, SIGNAL(clicked()), this, SLOT(reset())); connect( zoomSlider, SIGNAL(valueChanged(int)), this, SLOT(slotZoomSliderChanged(int))); connect( horizontalScroll, SIGNAL(valueChanged(int)), this, SLOT(slotSliderValueChanged(int))); // connect( pauseBtn, SIGNAL(clicked()), this, SLOT(slotTogglePause())); QTimer * updateScrollTmr = new QTimer(this); connect( updateScrollTmr, SIGNAL(timeout()), this, SLOT(updateScrollbars())); updateScrollTmr->start(20); //KGlobal::config()->setGroup("Oscilloscope"); KConfigGroup grOscill(KSharedConfig::openConfig(), "Oscilloscope"); setZoomLevel( grOscill.readEntry( "ZoomLevel", 0.5)); connect( this, SIGNAL(probeRegistered(int, ProbeData *)), probePositioner, SLOT(slotProbeDataRegistered(int, ProbeData *))); connect( this, SIGNAL(probeUnregistered(int)), probePositioner, SLOT(slotProbeDataUnregistered(int))); } Oscilloscope::~Oscilloscope() { m_pSelf = nullptr; } bool Oscilloscope::isInstantiated() { return m_pSelf != nullptr; } void Oscilloscope::slotTogglePause() { // b_isPaused = !b_isPaused; // pauseBtn->setText( b_isPaused ? i18n("Resume") : i18n("Pause")); // const ProbeDataMap::iterator end = m_probeDataMap.end(); // for( ProbeDataMap::iterator it = m_probeDataMap.begin(); it != end; ++it) // (*it)->setPaused(b_isPaused); } int Oscilloscope::sliderTicksPerSecond() const { return int(1e4); } void Oscilloscope::setZoomLevel( double zoomLevel) { if( zoomLevel < 0.0) zoomLevel = 0.0; else if( zoomLevel > 1.0) zoomLevel = 1.0; //KGlobal::config()->setGroup("Oscilloscope"); KConfigGroup grOscill = KSharedConfig::openConfig()->group("Oscilloscope"); //KGlobal::config()->writeEntry( "ZoomLevel", zoomLevel); grOscill.writeEntry( "ZoomLevel", zoomLevel); // We want to maintain the position of the *center* of the view, not the // left edge, so have to record time at center of view... We also have to // handle the case where the scroll is at the end separately. bool wasAtUpperEnd = horizontalScroll->maximum() == horizontalScroll->value(); int pageLength = int(oscilloscopeView->width()*sliderTicksPerSecond()/pixelsPerSecond()); int at_ticks = horizontalScroll->value() + (pageLength/2); m_zoomLevel = zoomLevel; zoomSlider->setValue( int((double(zoomSlider->maximum())*zoomLevel)+0.5)); updateScrollbars(); // And restore the center position of the slider if(!wasAtUpperEnd) { int pageLength = int(oscilloscopeView->width()*sliderTicksPerSecond()/pixelsPerSecond()); horizontalScroll->setValue( at_ticks - (pageLength/2)); oscilloscopeView->updateView(); } } void Oscilloscope::slotZoomSliderChanged( int value) { setZoomLevel( double(value)/double(zoomSlider->maximum())); } ProbeData * Oscilloscope::registerProbe( Probe * probe) { if(!probe) return nullptr; const uint id = m_nextId++; ProbeData * probeData = nullptr; if( dynamic_cast(probe)) { probeData = new LogicProbeData(id); m_logicProbeDataMap[id] = static_cast(probeData); } else { probeData = new FloatingProbeData(id); m_floatingProbeDataMap[id] = static_cast(probeData); } m_probeDataMap[id] = probeData; if(!m_oldestProbe) { m_oldestProbe = probeData; m_oldestId = id; } probeData->setColor( probeColors[m_nextColor]); m_nextColor = (m_nextColor+1)%9; // probeData->setPaused(b_isPaused); emit probeRegistered( id, probeData); return probeData; } void Oscilloscope::unregisterProbe( int id) { ProbeDataMap::iterator it = m_probeDataMap.find(id); if( it == m_probeDataMap.end()) return; m_logicProbeDataMap.remove(id); m_floatingProbeDataMap.remove(id); bool oldestDestroyed = it.value() == m_oldestProbe; if( it != m_probeDataMap.end()) m_probeDataMap.erase(it); if(oldestDestroyed) getOldestProbe(); emit probeUnregistered(id); } ProbeData * Oscilloscope::probeData( int id) const { const ProbeDataMap::const_iterator bit = m_probeDataMap.find(id); if( bit != m_probeDataMap.end()) return bit.value(); return nullptr; } int Oscilloscope::probeNumber( int id) const { const ProbeDataMap::const_iterator end = m_probeDataMap.end(); int i=0; for( ProbeDataMap::const_iterator it = m_probeDataMap.begin(); it != end; ++it) { if( it.key() == id) return i; i++; } return -1; } int Oscilloscope::numberOfProbes() const { return m_probeDataMap.size(); } void Oscilloscope::getOldestProbe() { if( m_probeDataMap.isEmpty()) { m_oldestProbe = nullptr; m_oldestId = -1; return; } m_oldestProbe = m_probeDataMap.begin().value(); m_oldestId = m_probeDataMap.begin().key(); } void Oscilloscope::reset() { const ProbeDataMap::iterator end = m_probeDataMap.end(); for( ProbeDataMap::iterator it = m_probeDataMap.begin(); it != end; ++it) (*it)->eraseData(); oscilloscopeView->updateView(); } void Oscilloscope::slotSliderValueChanged( int value) { Q_UNUSED(value); oscilloscopeView->updateView(); } void Oscilloscope::updateScrollbars() { bool wasAtUpperEnd = horizontalScroll->maximum() == horizontalScroll->value(); const float pps = pixelsPerSecond(); int pageLength = int(oscilloscopeView->width()*sliderTicksPerSecond()/pps); int64_t timeAsTicks = time()*sliderTicksPerSecond()/LOGIC_UPDATE_RATE; int64_t upper = (timeAsTicks > pageLength) ? (timeAsTicks - pageLength) : 0; horizontalScroll->setRange( 0, upper); horizontalScroll->setPageStep( uint64_t(oscilloscopeView->width()*sliderTicksPerSecond()/pps)); if(wasAtUpperEnd) { horizontalScroll->setValue( horizontalScroll->maximum()); oscilloscopeView->updateView(); } } uint64_t Oscilloscope::time() const { if(!m_oldestProbe) return 0; return uint64_t( m_pSimulator->time() - m_oldestProbe->resetTime()); } int64_t Oscilloscope::scrollTime() const { // if( b_isPaused || numberOfProbes() == 0) // return 0; if( numberOfProbes() == 0) return 0; if( horizontalScroll->maximum() == 0) { int64_t lengthAsTime = int64_t( oscilloscopeView->width() * LOGIC_UPDATE_RATE / pixelsPerSecond()); int64_t ret = m_pSimulator->time() - lengthAsTime; if(ret < 0) return 0; return ret; } else return int64_t( m_oldestProbe->resetTime() + (int64_t(horizontalScroll->value()) * LOGIC_UPDATE_RATE / sliderTicksPerSecond())); } double Oscilloscope::pixelsPerSecond() const { return 2 * MIN_BITS_PER_S * std::pow( 2.0, m_zoomLevel * MIN_MAX_LOG_2_DIFF); } //END Oscilloscope Class void addOscilloscopeAsToolView( KTechlab *ktechlab) { KateMDI::ToolView * tv; tv = ktechlab->createToolView( Oscilloscope::toolViewIdentifier(), KMultiTabBar::Bottom, KIconLoader::global()->loadIcon( "oscilloscope", KIconLoader::Small), i18n("Oscilloscope")); Oscilloscope::self(tv); } ProbeData * registerProbe( Probe * probe) { if (!Oscilloscope::isInstantiated()) { qDebug() << Q_FUNC_INFO << "no oscilloscope to register to, doing nothing"; return nullptr; } return Oscilloscope::self()->registerProbe(probe); } void unregisterProbe( int id) { if (!Oscilloscope::isInstantiated()) { qDebug() << Q_FUNC_INFO << "no oscilloscope to unregister from, doing nothing"; return; } Oscilloscope::self()->unregisterProbe(id); } diff --git a/src/gui/oscilloscope.h b/src/gui/oscilloscope.h index 92420484..1c170d0b 100644 --- a/src/gui/oscilloscope.h +++ b/src/gui/oscilloscope.h @@ -1,193 +1,193 @@ /*************************************************************************** * Copyright (C) 2005 by David Saxton * * david@bluehaze.org * * * * 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. * ***************************************************************************/ #ifndef OSCILLOSCOPE_H #define OSCILLOSCOPE_H #ifndef PROBE_H #ifndef KTECHLAB_H #ifndef OSCILLOSCOPEDATA_H #include #endif #endif #endif #include "simulator.h" -#include -#include +#include +#include #include class FloatingProbeData; class LogicProbe; class LogicProbeData; class KTechlab; class Oscilloscope; class Probe; class ProbeData; class VoltageProbe; class QTimer; namespace KateMDI { class ToolView; } typedef QMap< int, ProbeData * > ProbeDataMap; typedef QMap< int, LogicProbeData * > LogicProbeDataMap; typedef QMap< int, FloatingProbeData * > FloatingProbeDataMap; #if 0 const double MAX_BITS_PER_S = 100000; // NOTE: The 10 has to agree with the 2^10 = 1024.0 const int MIN_MAX_LOG_2_DIFF = 10; const double MIN_BITS_PER_S = MAX_BITS_PER_S / 1024.0; #else const double MAX_BITS_PER_S = LOGIC_UPDATE_RATE * 4; // NOTE: The 18 has to agree with the 2^18 = 262144.0 const int MIN_MAX_LOG_2_DIFF = 18; const double MIN_BITS_PER_S = MAX_BITS_PER_S / 262144.0; #endif /* Due to strangeness with generation of .[cpp/h] files from .ui files (that is, my inability to sort it out neatly), files other than those in /src/gui can't see header files such as "oscilloscopewidget.h", so we have to provide some interface functions for accessing the functionality in this class */ ProbeData * registerProbe( Probe * probe); void unregisterProbe( int id); void addOscilloscopeAsToolView( KTechlab *ktechlab); #ifndef PROBE_H #ifndef KTECHLAB_H #ifndef OSCILLOSCOPEDATA_H /** @author David Saxton */ class Oscilloscope : public QWidget, public Ui::OscilloscopeWidget { Q_OBJECT public: static Oscilloscope * self( KateMDI::ToolView * parent = nullptr); static QString toolViewIdentifier() { return "Oscilloscope"; } ~Oscilloscope() override; static bool isInstantiated() ; /** * Register a probe (that outputs boolean data) with the oscilloscope. * Returns a unique id that the probe can use to add data points */ ProbeData * registerProbe( Probe * probe); void unregisterProbe( int id); /** * Returns the Simulator time since recording started. */ uint64_t time() const; /** * Returns how much of an increment in value of the oscilloscope slider * is equivalent to one second. */ int sliderTicksPerSecond() const; /** * Returns the number of pixels per second the user has requested to be * displayed. */ double pixelsPerSecond() const; /** * Zoom level; a value between 0 and 1. 0 is maximum zoom out, and 1 is * maximum zoom in. */ double zoomLevel() const { return m_zoomLevel; } /** * Sets the zoom level (and in the process, checks that it is within the * bounds allowed). */ void setZoomLevel( double zoomLevel); /** * Returns the Simulator time as given by the current scrollbar * position. */ int64_t scrollTime() const; /** * @returns pointer to probe with given id, or nullptr if no such probe exists */ ProbeData * probeData( int id) const; /** * @returns the total number of probes */ int numberOfProbes() const; /** * @returns number of the probe with the given id, starting from 0, or -1 if no such probe */ int probeNumber( int id) const; signals: /** * Emitted when a probe is registered */ void probeRegistered( int id, ProbeData * probe); /** * Emitted when a probe is unregistered */ void probeUnregistered( int id); public slots: /** * Resets all recorded data */ void reset(); /** * Called when the zoom slider value was changed. */ void slotZoomSliderChanged( int value); /** * Called when the horizontal scrollbar was scrolled by the user */ void slotSliderValueChanged( int value); /** * Pause the data capture (e.g. user clicked on pause button) */ void slotTogglePause(); protected: void getOldestProbe(); int m_nextId; ProbeData * m_oldestProbe; int m_oldestId; int m_nextColor; // For giving the probes colours ProbeDataMap m_probeDataMap; LogicProbeDataMap m_logicProbeDataMap; FloatingProbeDataMap m_floatingProbeDataMap; Simulator * m_pSimulator; protected slots: void updateScrollbars(); private: Oscilloscope( KateMDI::ToolView * parent); static Oscilloscope * m_pSelf; double m_zoomLevel; friend class OscilloscopeView; friend class ProbePositioner; }; #endif // OSCILLOSCOPEDATA_H #endif // KTECHLAB_H #endif // PROBE_H #endif // OSCILLOSCOPE_H diff --git a/src/gui/oscilloscopeview.cpp b/src/gui/oscilloscopeview.cpp index de9d3c7f..57d1c559 100644 --- a/src/gui/oscilloscopeview.cpp +++ b/src/gui/oscilloscopeview.cpp @@ -1,450 +1,450 @@ /*************************************************************************** * Copyright (C) 2005 by David Saxton * * david@bluehaze.org * * * * 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 "oscilloscope.h" #include "oscilloscopedata.h" #include "oscilloscopeview.h" #include "probepositioner.h" #include "simulator.h" #include #include #include // #include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include #include #include using namespace std; inline uint64_t min( uint64_t a, uint64_t b) { return a < b ? a : b; } OscilloscopeView::OscilloscopeView( QWidget *parent, const char *name) : QFrame( parent /*, name */ /*, Qt::WNoAutoErase */ ), b_needRedraw(true), m_pixmap(nullptr), m_fps(10), m_sliderValueAtClick(-1), m_clickOffsetPos(-1), m_pSimulator( Simulator::self()), m_halfOutputHeight(0.0) { setObjectName( name ); //KGlobal::config()->setGroup("Oscilloscope"); KConfigGroup grOscill(KSharedConfig::openConfig(), "Oscilloscope"); m_fps = grOscill.readEntry( "FPS", 25); //setBackgroundMode(Qt::NoBackground); // 2018.12.07 setBackgroundRole( QPalette::NoRole ); setMouseTracking(true); m_updateViewTmr = new QTimer(this); connect( m_updateViewTmr, SIGNAL(timeout()), this, SLOT(updateViewTimeout())); } OscilloscopeView::~OscilloscopeView() { delete m_pixmap; } void OscilloscopeView::updateView() { if(m_updateViewTmr->isActive()) return; m_updateViewTmr->setSingleShot( true ); m_updateViewTmr->start( 1000/m_fps /*, true */ ); } void OscilloscopeView::updateViewTimeout() { b_needRedraw = true; repaint( /* false - 2018.12.07 */); updateTimeLabel(); } void OscilloscopeView::updateTimeLabel() { if( testAttribute(Qt::WA_UnderMouse) ) { int x = mapFromGlobal( QCursor::pos()).x(); double time = (double(Oscilloscope::self()->scrollTime()) / LOGIC_UPDATE_RATE) + (x / Oscilloscope::self()->pixelsPerSecond()); Oscilloscope::self()->timeLabel->setText( QString::number( time, 'f', 6)); } else Oscilloscope::self()->timeLabel->setText( QString::null); } void OscilloscopeView::resizeEvent( QResizeEvent *e) { delete m_pixmap; m_pixmap = new QPixmap( e->size()); b_needRedraw = true; QFrame::resizeEvent(e); } void OscilloscopeView::mousePressEvent( QMouseEvent *event) { switch ( event->button()) { case Qt::LeftButton: { event->accept(); m_clickOffsetPos = event->pos().x(); m_sliderValueAtClick = Oscilloscope::self()->horizontalScroll->value(); setCursor( Qt::SizeAllCursor); return; } case Qt::RightButton: { event->accept(); QMenu fpsMenu; //fpsMenu.insertTitle( i18n("Framerate")); // 2017.12.27 - use setTitle //fpsMenu.insertItem( i18n("Framerate"), 1 ); // 2018.12.07 - use actions //fpsMenu.setItemEnabled(1, false); { QAction *a = fpsMenu.addAction( i18n("Framerate") ); a->setData( 1 ); a->setEnabled( false ); } //fpsMenu.insertSeparator(); // 2018.12.07 fpsMenu.addSeparator(); const int fps[] = { 10, 25, 50, 75, 100 }; for( uint i=0; i<5; ++i) { const int num = fps[i]; //fpsMenu.insertItem( i18n("%1 fps", QString::number(num)), num); // 2018.12.07 //fpsMenu.setItemChecked( num, num == m_fps); QAction *a = fpsMenu.addAction( i18n("%1 fps", QString::number(num)) ); a->setData( num ); } connect( &fpsMenu, SIGNAL(triggered(QAction*)), this, SLOT(slotSetFrameRate(QAction*))); fpsMenu.exec( event->globalPos()); return; } default: { QFrame::mousePressEvent(event); return; } } } void OscilloscopeView::mouseMoveEvent( QMouseEvent *event) { event->accept(); updateTimeLabel(); if( m_sliderValueAtClick != -1) { int dx = event->pos().x() - m_clickOffsetPos; int dTick = int( dx * Oscilloscope::self()->sliderTicksPerSecond() / Oscilloscope::self()->pixelsPerSecond()); Oscilloscope::self()->horizontalScroll->setValue( m_sliderValueAtClick - dTick); } } void OscilloscopeView::mouseReleaseEvent( QMouseEvent *event) { if( m_sliderValueAtClick == -1) return QFrame::mouseReleaseEvent(event); event->accept(); m_sliderValueAtClick = -1; setCursor( Qt::ArrowCursor); } void OscilloscopeView::slotSetFrameRate( QAction *action) { int fps = action->data().toInt(); m_fps = fps; //KGlobal::config()->setGroup("Oscilloscope"); KConfigGroup grOscill(KSharedConfig::openConfig(), "Oscilloscope"); grOscill.writeEntry( "FPS", m_fps); } // returns a % b static double lld_modulus( int64_t a, double b) { return double(a) - int64_t(a/b)*b; } void OscilloscopeView::paintEvent( QPaintEvent *e) { QRect r = e->rect(); if(b_needRedraw) { updateOutputHeight(); const double pixelsPerSecond = Oscilloscope::self()->pixelsPerSecond(); if (!m_pixmap) { qWarning() << Q_FUNC_INFO << " unexpected null m_pixmap in " << this; return; } QPainter p; //m_pixmap->fill( paletteBackgroundColor()); m_pixmap->fill( palette().color( backgroundRole() )); const bool startSuccess = p.begin(m_pixmap); if ((!startSuccess) || (!p.isActive())) { qWarning() << Q_FUNC_INFO << " painter is not active"; } p.setClipRegion(e->region()); //BEGIN Draw vertical marker lines const double divisions = 5.0; const double min_sep = 10.0; double spacing = pixelsPerSecond/(std::pow( divisions, std::floor(std::log(pixelsPerSecond/min_sep)/std::log(divisions)))); // Pixels offset is the number of pixels that the view is scrolled along const int64_t pixelsOffset = int64_t(Oscilloscope::self()->scrollTime()*pixelsPerSecond/LOGIC_UPDATE_RATE); double linesOffset = - lld_modulus( pixelsOffset, spacing); int blackness = 256 - int(184.0 * spacing / (min_sep*divisions*divisions)); p.setPen( QColor( blackness, blackness, blackness)); for( double i = linesOffset; i <= frameRect().width(); i += spacing) p.drawLine( int(i), 1, int(i), frameRect().height()-2); spacing *= divisions; linesOffset = - lld_modulus( pixelsOffset, spacing); blackness = 256 - int(184.0 * spacing / (min_sep*divisions*divisions)); p.setPen( QColor( blackness, blackness, blackness)); for( double i = linesOffset; i <= frameRect().width(); i += spacing) p.drawLine( int(i), 1, int(i), frameRect().height()-2); spacing *= divisions; linesOffset = - lld_modulus( pixelsOffset, spacing); blackness = 256 - int(184.0); p.setPen( QColor( blackness, blackness, blackness)); for( double i = linesOffset; i <= frameRect().width(); i += spacing) p.drawLine( int(i), 1, int(i), frameRect().height()-2); //END Draw vertical marker lines drawLogicData(p); drawFloatingData(p); p.setPen(Qt::black); p.drawRect( frameRect()); b_needRedraw = false; } //bitBlt( this, r.x(), r.y(), m_pixmap, r.x(), r.y(), r.width(), r.height()); // 2018.12.07 QPainter p; const bool paintStarted = p.begin(this); if (!paintStarted) { qWarning() << Q_FUNC_INFO << " failed to start painting "; } p.drawImage(r, m_pixmap->toImage(), r); } void OscilloscopeView::updateOutputHeight() { m_halfOutputHeight = int((Oscilloscope::self()->probePositioner->probeOutputHeight() - (probeArrowWidth/Oscilloscope::self()->numberOfProbes()))/2)-1; } void OscilloscopeView::drawLogicData( QPainter & p) { const double pixelsPerSecond = Oscilloscope::self()->pixelsPerSecond(); const LogicProbeDataMap::iterator end = Oscilloscope::self()->m_logicProbeDataMap.end(); for( LogicProbeDataMap::iterator it = Oscilloscope::self()->m_logicProbeDataMap.begin(); it != end; ++it) { // When searching for the next logic value to display, we look along // until there is a recorded point which is at least one pixel along // If we are zoomed out far, there might be thousands of data points // between each pixel. It is time consuming searching for the next point // to display one at a time, so we record the average number of data points // between pixels ( = deltaAt / totalDeltaAt) int64_t deltaAt = 1; int totalDeltaAt = 1; LogicProbeData * probe = it.value(); vector *data = probe->m_data; if(!data->size()) continue; const int midHeight = Oscilloscope::self()->probePositioner->probePosition(probe); const int64_t timeOffset = Oscilloscope::self()->scrollTime(); // Draw the horizontal line indicating the midpoint of our output p.setPen( QColor( 228, 228, 228)); p.drawLine( 0, midHeight, width(), midHeight); // Set the pen colour according to the colour the user has selected for the probe p.setPen( probe->color()); // The smallest time step that will display in our oscilloscope const int minTimeStep = int(LOGIC_UPDATE_RATE/pixelsPerSecond); int64_t at = probe->findPos(timeOffset); const int64_t maxAt = probe->m_data->size(); int64_t prevTime = (*data)[at].time; int prevX = (at > 0) ? 0 : int((prevTime - timeOffset)*(pixelsPerSecond/LOGIC_UPDATE_RATE)); bool prevHigh = (*data)[at].value; int prevY = midHeight + int(prevHigh ? -m_halfOutputHeight : +m_halfOutputHeight); while ( at < maxAt) { // Search for the next pos which will show up at our zoom level int64_t previousAt = at; int64_t dAt = deltaAt / totalDeltaAt; while ( (dAt > 1) && (at < maxAt) && ( (int64_t((*data)[at].time) - prevTime) != minTimeStep)) { // Search forwards until we overshoot while ( at < maxAt && ( int64_t((*data)[at].time) - prevTime) < minTimeStep) at += dAt; dAt /= 2; // Search backwards until we undershoot while ( (at < maxAt) && ( int64_t((*data)[at].time) - prevTime) > minTimeStep) { at -= dAt; if( at < 0) at = 0; } dAt /= 2; } // Possibly increment the value of at found by one (or more if this is the first go) while ( (previousAt == at) || ((at < maxAt) && ( int64_t((*data)[at].time) - prevTime) < minTimeStep)) at++; if( at >= maxAt) break; // Update the average values deltaAt += at - previousAt; totalDeltaAt++; bool nextHigh = (*data)[at].value; if( nextHigh == prevHigh) continue; int64_t nextTime = (*data)[at].time; int nextX = int((nextTime - timeOffset)*(pixelsPerSecond/LOGIC_UPDATE_RATE)); int nextY = midHeight + int(nextHigh ? -m_halfOutputHeight : +m_halfOutputHeight); p.drawLine( prevX, prevY, nextX, prevY); p.drawLine( nextX, prevY, nextX, nextY); prevHigh = nextHigh; prevTime = nextTime; prevX = nextX; prevY = nextY; if( nextX > width()) break; }; // If we could not draw right to the end; it is because we exceeded // maxAt if( prevX < width()) p.drawLine( prevX, prevY, width(), prevY); } } #define v_to_y int(midHeight - (logarithmic ? ( (v>0) ? log(v/lowerAbsValue) : -log(-v/lowerAbsValue)) : v) * sf) void OscilloscopeView::drawFloatingData(QPainter &p) { const double pixelsPerSecond = Oscilloscope::self()->pixelsPerSecond(); const FloatingProbeDataMap::iterator end = Oscilloscope::self()->m_floatingProbeDataMap.end(); for(FloatingProbeDataMap::iterator it = Oscilloscope::self()->m_floatingProbeDataMap.begin(); it != end; ++it) { FloatingProbeData * probe = it.value(); vector *data = probe->m_data; if(!data->size()) continue; bool logarithmic = probe->scaling() == FloatingProbeData::Logarithmic; double lowerAbsValue = probe->lowerAbsValue(); double sf = m_halfOutputHeight / (logarithmic ? log(probe->upperAbsValue()/lowerAbsValue) : probe->upperAbsValue()); const int midHeight = Oscilloscope::self()->probePositioner->probePosition(probe); const int64_t timeOffset = Oscilloscope::self()->scrollTime(); // Draw the horizontal line indicating the midpoint of our output p.setPen( QColor( 228, 228, 228)); p.drawLine( 0, midHeight, width(), midHeight); // Set the pen colour according to the colour the user has selected for the probe p.setPen( probe->color()); int64_t at = probe->findPos(timeOffset); const int64_t maxAt = probe->m_data->size(); if(at > maxAt) at = maxAt; int64_t prevTime = probe->toTime(at); double v = (*data)[(at>0)?at:0]; int prevY = v_to_y; int prevX = int((prevTime - timeOffset)*(pixelsPerSecond/LOGIC_UPDATE_RATE)); while ( at < maxAt) { at++; uint64_t nextTime = prevTime + uint64_t(LOGIC_UPDATE_RATE * LINEAR_UPDATE_PERIOD); double v = (*data)[(at>0)?at:0]; int nextY = v_to_y; int nextX = int((nextTime - timeOffset)*(pixelsPerSecond/LOGIC_UPDATE_RATE)); p.drawLine( prevX, prevY, nextX, nextY); prevTime = nextTime; prevX = nextX; prevY = nextY; if( nextX > width()) break; }; // If we could not draw right to the end; it is because we exceeded // maxAt if( prevX < width()) p.drawLine( prevX, prevY, width(), prevY); } } diff --git a/src/gui/oscilloscopeview.h b/src/gui/oscilloscopeview.h index 9cc540d1..32ce3430 100644 --- a/src/gui/oscilloscopeview.h +++ b/src/gui/oscilloscopeview.h @@ -1,65 +1,65 @@ /*************************************************************************** * Copyright (C) 2005 by David Saxton * * david@bluehaze.org * * * * 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. * ***************************************************************************/ #ifndef OSCILLOSCOPEVIEW_H #define OSCILLOSCOPEVIEW_H -#include +#include class Oscilloscope; class Simulator; class QMouseEvent; class QPaintEvent; class QPixmap; class QTimer; /** @author David Saxton */ class OscilloscopeView : public QFrame { Q_OBJECT public: OscilloscopeView( QWidget *parent, const char *name = nullptr); ~OscilloscopeView() override; public slots: /** * Sets the needRedraw flag to true, and then class repaint */ void updateView(); void slotSetFrameRate( QAction * ); protected slots: void updateViewTimeout(); protected: void mousePressEvent( QMouseEvent *event) override; void mouseMoveEvent( QMouseEvent *event) override; void mouseReleaseEvent( QMouseEvent *event) override; void paintEvent( QPaintEvent *event) override; void resizeEvent( QResizeEvent *event) override; void drawLogicData( QPainter & p); void drawFloatingData( QPainter & p); void updateOutputHeight(); void updateTimeLabel(); bool b_needRedraw; QPixmap *m_pixmap; QTimer *m_updateViewTmr; int m_fps; int m_sliderValueAtClick; int m_clickOffsetPos; Simulator * m_pSimulator; double m_halfOutputHeight; }; #endif diff --git a/src/gui/outputmethoddlg.cpp b/src/gui/outputmethoddlg.cpp index 7dca6d42..01321fe1 100644 --- a/src/gui/outputmethoddlg.cpp +++ b/src/gui/outputmethoddlg.cpp @@ -1,189 +1,188 @@ /*************************************************************************** * Copyright (C) 2003-2004 by David Saxton * * david@bluehaze.org * * * * 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 "docmanager.h" #include "filemetainfo.h" #include "textdocument.h" #include "outputmethoddlg.h" #include "microlibrary.h" #include "microselectwidget.h" #include "projectmanager.h" -#include #include -#include -#include +#include +#include +#include #include -#include #include #include #include #include class OutputMethodWidget : public QWidget, public Ui::OutputMethodWidget { public: OutputMethodWidget(QWidget *parent) : QWidget(parent) { setupUi(this); } }; //BEGIN class OutputMethodInfo OutputMethodInfo::OutputMethodInfo() { m_method = Method::Direct; m_bAddToProject = false; } void OutputMethodInfo::initialize( OutputMethodDlg * dlg ) { if ( dlg->m_widget->displayDirectCheck->isChecked() ) { m_method = Method::Direct; //K3TempFile f( QString::null, dlg->m_outputExtension ); QTemporaryFile f(QDir::tempPath() + QLatin1String("/ktechlab_XXXXXX") + dlg->m_outputExtension); f.setAutoRemove(false); if (!f.open()) { qWarning() << "failed to open " << f.fileName() << " because " << f.errorString(); } f.close(); m_outputFile = QUrl::fromLocalFile(f.fileName()); m_bAddToProject = false; } else { if ( dlg->m_widget->loadFileCheck->isChecked() ) m_method = Method::SaveAndLoad; else m_method = Method::SaveAndForget; m_outputFile = dlg->m_widget->outputFileURL->url(); m_bAddToProject = dlg->m_widget->addToProjectCheck->isChecked(); } m_picID = dlg->m_widget->m_pMicroSelect->micro(); } //END class OutputMethodInfo //BEGIN class OutputMethodDlg OutputMethodDlg::OutputMethodDlg( const QString &caption, const QUrl & inputURL, bool showPICSelect, QWidget *parent, const char *name ) : QDialog(parent) { setObjectName(name); setModal(true); setWindowTitle(caption); QVBoxLayout *mainLayout = new QVBoxLayout; setLayout(mainLayout); m_inputURL = inputURL; m_widget = new OutputMethodWidget(this); m_widget->addToProjectCheck->setEnabled( ProjectManager::self()->currentProject() ); if (!showPICSelect) { m_widget->m_pMicroSelect->hide(); m_widget->adjustSize(); } m_widget->outputFileURL->setMode(KFile::File | KFile::LocalOnly); m_widget->outputFileURL->setAcceptMode(QFileDialog::AcceptSave); connect(m_widget->saveFileCheck, SIGNAL(toggled(bool)), m_widget->groupBoxSaveOptions, SLOT(setEnabled(bool))); fileMetaInfo()->initializeFromMetaInfo( m_inputURL, this ); mainLayout->addWidget(m_widget); QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok|QDialogButtonBox::Cancel); mainLayout->addWidget(buttonBox); QPushButton *okButton = buttonBox->button(QDialogButtonBox::Ok); okButton->setDefault(true); okButton->setShortcut(Qt::CTRL | Qt::Key_Return); connect(buttonBox, &QDialogButtonBox::accepted, this, &QDialog::accept); connect(buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject); } OutputMethodDlg::~OutputMethodDlg() { } void OutputMethodDlg::setOutputExtension( const QString & extension ) { m_outputExtension = extension; } void OutputMethodDlg::setFilter( const QString &filter ) { m_widget->outputFileURL->setFilter(filter); } void OutputMethodDlg::setMethod( OutputMethodInfo::Method::Type m ) { switch (m) { case OutputMethodInfo::Method::Direct: m_widget->displayDirectCheck->setChecked(true); break; case OutputMethodInfo::Method::SaveAndForget: m_widget->saveFileCheck->setChecked(true); m_widget->loadFileCheck->setChecked(false); break; case OutputMethodInfo::Method::SaveAndLoad: m_widget->saveFileCheck->setChecked(true); m_widget->loadFileCheck->setChecked(true); break; }; } void OutputMethodDlg::setPicID( const QString & id ) { m_widget->m_pMicroSelect->setMicro(id); } void OutputMethodDlg::setOutputFile( const QUrl & out ) { m_widget->outputFileURL->setUrl(out); } void OutputMethodDlg::accept() { m_outputMethodInfo.initialize(this); fileMetaInfo()->grabMetaInfo( m_inputURL, this ); QDialog::accept(); } MicroSelectWidget * OutputMethodDlg::microSelect() const { return m_widget->m_pMicroSelect; } //END class OutputMethodDlg diff --git a/src/gui/probepositioner.cpp b/src/gui/probepositioner.cpp index fef02e23..b6960035 100644 --- a/src/gui/probepositioner.cpp +++ b/src/gui/probepositioner.cpp @@ -1,226 +1,226 @@ /*************************************************************************** * Copyright (C) 2005 by David Saxton * * david@bluehaze.org * * * * 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 "oscilloscope.h" #include "oscilloscopedata.h" #include "oscilloscopeview.h" #include "probepositioner.h" -#include -#include +#include +#include // #include // 2018.08.14 -#include +#include #include #include ProbePositioner::ProbePositioner(QWidget *parent, const char *name) : QWidget( parent /*, name */ /* , Qt::WNoAutoErase */ ) { setObjectName( name ); m_probePosOffset = 0; p_draggedProbe = nullptr; setFixedWidth( int(probeArrowWidth) ); //setBackgroundMode(Qt::NoBackground); // 2018.12.07 setBackgroundRole( QPalette::NoRole ); b_needRedraw = true; m_pixmap = nullptr; } ProbePositioner::~ProbePositioner() { delete m_pixmap; } void ProbePositioner::forceRepaint() { b_needRedraw = true; repaint( /* false - 2018.12.07 */ ); } int ProbePositioner::probeOutputHeight() const { int height = int( Oscilloscope::self()->oscilloscopeView->height() - probeArrowHeight ); int numProbes = Oscilloscope::self()->numberOfProbes(); if ( numProbes == 0 ) numProbes = 1; return height / numProbes; } int ProbePositioner::probePosition( ProbeData *probeData ) const { if (!probeData) return -1; int spacing = probeOutputHeight(); int probeNum = Oscilloscope::self()->probeNumber(probeData->id()); return int( probeArrowHeight/2 + spacing*( probeNum + probeData->drawPosition() ) ); } void ProbePositioner::setProbePosition( ProbeData *probeData, int position ) { if (!probeData) return; int height = int( Oscilloscope::self()->oscilloscopeView->height() - probeArrowHeight ); int numProbes = Oscilloscope::self()->numberOfProbes(); int spacing = height / numProbes; int probeNum = Oscilloscope::self()->probeNumber(probeData->id()); int minPos = int(probeArrowHeight/2); int maxPos = int(Oscilloscope::self()->oscilloscopeView->height() - (probeArrowHeight/2)) - 1; if ( position < minPos ) position = minPos; else if ( position > maxPos ) position = maxPos; probeData->setDrawPosition( float(position - probeArrowHeight/2)/float(spacing) - probeNum ); forceRepaint(); Oscilloscope::self()->oscilloscopeView->updateView(); } ProbeData* ProbePositioner::probeAtPosition( const QPoint &pos ) { int relativeArrowHeight = int( probeArrowHeight * ( 1. - float(pos.x()/probeArrowWidth) ) ); const ProbeDataMap::const_iterator end = m_probeDataMap.end(); for ( ProbeDataMap::const_iterator it = m_probeDataMap.begin(); it != end; ++it ) { ProbeData *probeData = it.value(); int currentPos = probePosition(probeData); m_probePosOffset = pos.y() - currentPos; if ( std::abs(m_probePosOffset) <= relativeArrowHeight ) return probeData; } m_probePosOffset = 0; return nullptr; } void ProbePositioner::slotProbeDataRegistered( int id, ProbeData *probe ) { m_probeDataMap[id] = probe; connect( probe, SIGNAL(displayAttributeChanged()), this, SLOT(forceRepaint()) ); // This connect doesn't really belong here, but it save a lot of code connect( probe, SIGNAL(displayAttributeChanged()), Oscilloscope::self()->oscilloscopeView, SLOT(updateView()) ); forceRepaint(); Oscilloscope::self()->oscilloscopeView->updateView(); } void ProbePositioner::slotProbeDataUnregistered( int id ) { m_probeDataMap.remove(id); // We "set" the position of each probe to force it into proper bounds const ProbeDataMap::const_iterator end = m_probeDataMap.end(); for ( ProbeDataMap::const_iterator it = m_probeDataMap.begin(); it != end; ++it ) setProbePosition( it.value(), probePosition( it.value() ) ); forceRepaint(); } void ProbePositioner::resizeEvent( QResizeEvent *e ) { delete m_pixmap; m_pixmap = new QPixmap( e->size() ); QWidget::resizeEvent(e); forceRepaint(); } void ProbePositioner::mousePressEvent( QMouseEvent * e ) { p_draggedProbe = probeAtPosition(e->pos()); if (p_draggedProbe) e->accept(); else e->ignore(); } void ProbePositioner::mouseReleaseEvent( QMouseEvent * e ) { if (p_draggedProbe) e->accept(); else e->ignore(); } void ProbePositioner::mouseMoveEvent( QMouseEvent * e ) { if (!p_draggedProbe) { e->ignore(); return; } e->accept(); setProbePosition( p_draggedProbe, e->pos().y() - m_probePosOffset ); forceRepaint(); } void ProbePositioner::paintEvent( QPaintEvent *e ) { QRect r = e->rect(); if (b_needRedraw) { if (!m_pixmap) { qWarning() << Q_FUNC_INFO << " unexpected null m_pixmap in " << this; return; } QPainter p; //m_pixmap->fill( paletteBackgroundColor() ); m_pixmap->fill( palette().color( backgroundRole() ) ); const bool startSuccess = p.begin(m_pixmap); if ((!startSuccess) || (!p.isActive())) { qWarning() << Q_FUNC_INFO << " painter is not active"; } p.setClipRegion(e->region()); const ProbeDataMap::const_iterator end = m_probeDataMap.end(); for ( ProbeDataMap::const_iterator it = m_probeDataMap.begin(); it != end; ++it ) { ProbeData *probeData = it.value(); p.setBrush( probeData->color() ); int currentPos = probePosition(probeData); QPolygon pa(3); pa[0] = QPoint( 0, int(currentPos-(probeArrowHeight/2)) ); pa[1] = QPoint( int(probeArrowWidth), currentPos ); pa[2] = QPoint( 0, int(currentPos+(probeArrowHeight/2)) ); p.drawPolygon(pa); } b_needRedraw = false; } //bitBlt( this, r.x(), r.y(), m_pixmap, r.x(), r.y(), r.width(), r.height() ); // 2018.12.07 QPainter p; const bool paintStarted = p.begin(this); if (!paintStarted) { qWarning() << Q_FUNC_INFO << " failed to start painting "; } p.drawImage(r, m_pixmap->toImage(), r); } diff --git a/src/gui/probepositioner.h b/src/gui/probepositioner.h index 7911176b..c3a9f8e3 100644 --- a/src/gui/probepositioner.h +++ b/src/gui/probepositioner.h @@ -1,77 +1,77 @@ /*************************************************************************** * Copyright (C) 2005 by David Saxton * * david@bluehaze.org * * * * 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. * ***************************************************************************/ #ifndef PROBEPOSITIONER_H #define PROBEPOSITIONER_H -#include +#include class ProbeData; typedef QMap< int, ProbeData* > ProbeDataMap; const float probeArrowWidth = 9; const float probeArrowHeight = 12; /** Widget for positioning the output of Probes in the OscilloscopeView @author David Saxton */ class ProbePositioner : public QWidget { Q_OBJECT public: ProbePositioner(QWidget *parent = nullptr, const char *name = nullptr); ~ProbePositioner() override; /** * Returns the amount of space (height in pixels) that a probe output * takes up */ int probeOutputHeight() const; /** * Returns the probe position (from the top) in pixels that the probe * with the given id should be displayed at, or -1 if probe with the * given id couldn't be found */ int probePosition( ProbeData *probeData ) const; /** * Sets the probe position relative to the top of this widget (and hence * relative to the top of the oscilloscope view) in pixels */ void setProbePosition( ProbeData *probeData, int position ); /** * Returns the probe at the given position (plus or minus an arrow), * or nullptr if none. Records the offset of the position from the mouse * in m_probePosOffset. */ ProbeData* probeAtPosition( const QPoint &pos ); public slots: void forceRepaint(); protected slots: void slotProbeDataRegistered( int id, ProbeData *probe ); void slotProbeDataUnregistered( int id ); protected: void mousePressEvent( QMouseEvent * e ) override; void mouseReleaseEvent( QMouseEvent * e ) override; void mouseMoveEvent( QMouseEvent * e ) override; void paintEvent( QPaintEvent *e ) override; void resizeEvent( QResizeEvent *event ) override; ProbeDataMap m_probeDataMap; ProbeData *p_draggedProbe; int m_probePosOffset; bool b_needRedraw; QPixmap *m_pixmap; }; #endif diff --git a/src/gui/programmerdlg.cpp b/src/gui/programmerdlg.cpp index 49786d55..1b3b5c9a 100644 --- a/src/gui/programmerdlg.cpp +++ b/src/gui/programmerdlg.cpp @@ -1,118 +1,118 @@ /*************************************************************************** * Copyright (C) 2005 by David Saxton * * david@bluehaze.org * * * * 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 "language.h" #include "microselectwidget.h" #include "picprogrammer.h" #include "port.h" #include "programmerdlg.h" #include "ktlconfig.h" #include "ui_programmerwidget.h" -#include +#include #include #include #include #include class ProgrammerWidget : public QWidget, public Ui::ProgrammerWidget { public: ProgrammerWidget(QWidget *parent) : QWidget(parent) { setupUi(this); } }; ProgrammerDlg::ProgrammerDlg( const QString & picID, QWidget *parent, const char *name ) : QDialog(parent) { setObjectName(name); setModal(true); setWindowTitle(i18n("PIC Programmer")); QVBoxLayout *mainLayout = new QVBoxLayout; setLayout(mainLayout); m_pProgrammerWidget = new ProgrammerWidget( this ); m_pProgrammerSettings = new PicProgrammerSettings; // Setup the list of programmers QComboBox * programmerCombo = m_pProgrammerWidget->m_pProgrammerProgram; QStringList programmerNames = m_pProgrammerSettings->configNames( false ); programmerCombo->insertItems(programmerCombo->count(), programmerNames ); //programmerCombo->setSizeLimit( programmerNames.size() ); programmerCombo->setMaxCount( programmerNames.size() ); //programmerCombo->setCurrentText( KTLConfig::picProgrammerProgram() ); // 2018.12.07 { QComboBox *c = programmerCombo; QString text = KTLConfig::picProgrammerProgram(); int i = c->findText(text); if (i != -1) c->setCurrentIndex(i); else if (c->isEditable()) c->setEditText(text); else c->setItemText(c->currentIndex(), text); } // Sets up the list of ports m_pProgrammerWidget->m_pPicProgrammerPort->insertItems( m_pProgrammerWidget->m_pPicProgrammerPort->count(), Port::ports( Port::ExistsAndRW ) ); //m_pProgrammerWidget->m_pPicProgrammerPort->setCurrentText( KTLConfig::picProgrammerPort() ); // 2018.12.07 { QComboBox *c = m_pProgrammerWidget->m_pPicProgrammerPort; QString text = KTLConfig::picProgrammerPort(); int i = c->findText(text); if (i != -1) c->setCurrentIndex(i); else if (c->isEditable()) c->setEditText(text); else c->setItemText(c->currentIndex(), text); } // Set the pic type to the one requested if ( !picID.isEmpty() ) m_pProgrammerWidget->m_pMicroSelect->setMicro( picID ); mainLayout->addWidget(m_pProgrammerWidget); QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Cancel); QPushButton *burnButton = new QPushButton(QIcon::fromTheme(QStringLiteral("dialog-ok")), i18n("Burn"), buttonBox); buttonBox->addButton(burnButton, QDialogButtonBox::AcceptRole); burnButton->setDefault(true); burnButton->setShortcut(Qt::CTRL | Qt::Key_Return); mainLayout->addWidget(buttonBox); connect(buttonBox, &QDialogButtonBox::accepted, this, &QDialog::accept); connect(buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject); } ProgrammerDlg::~ProgrammerDlg() { } void ProgrammerDlg::initOptions( ProcessOptions * options ) { if ( !options ) return; options->m_picID = m_pProgrammerWidget->m_pMicroSelect->micro(); options->m_port = m_pProgrammerWidget->m_pPicProgrammerPort->currentText(); options->m_program = m_pProgrammerWidget->m_pProgrammerProgram->currentText(); } MicroSelectWidget * ProgrammerDlg::microSelect( ) const { return m_pProgrammerWidget->m_pMicroSelect; } diff --git a/src/gui/projectdlgs.cpp b/src/gui/projectdlgs.cpp index 73a94e14..cbe28e11 100644 --- a/src/gui/projectdlgs.cpp +++ b/src/gui/projectdlgs.cpp @@ -1,379 +1,379 @@ /*************************************************************************** * Copyright (C) 2005 by David Saxton * * david@bluehaze.org * * * * 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 "microlibrary.h" #include "microselectwidget.h" #include "projectdlgs.h" #include "projectmanager.h" #include #include #include // #include -#include -#include -#include -#include +#include +#include +#include +#include #include #include #include #include #include #include #include class NewProjectWidget : public QWidget, public Ui::NewProjectWidget { public: NewProjectWidget(QWidget *parent) : QWidget(parent) { setupUi(this); } }; //BEGIN class NewProjectDlg NewProjectDlg::NewProjectDlg( QWidget * parent ) : QDialog(parent) { setObjectName("newprojectdlg"); setModal(true); setWindowTitle(i18n("New Project")); QVBoxLayout *mainLayout = new QVBoxLayout; setLayout(mainLayout); m_pWidget = new NewProjectWidget(this); connect( m_pWidget->projectNameEdit, SIGNAL(textChanged(const QString & )), this, SLOT(locationChanged(const QString& )) ); connect( m_pWidget->projectLocationURL, SIGNAL(textChanged(const QString & )), this, SLOT(locationChanged(const QString& )) ); mainLayout->addWidget(m_pWidget); m_buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok|QDialogButtonBox::Cancel); mainLayout->addWidget(m_buttonBox); QPushButton *okButton = m_buttonBox->button(QDialogButtonBox::Ok); okButton->setDefault(true); okButton->setShortcut(Qt::CTRL | Qt::Key_Return); connect(m_buttonBox, &QDialogButtonBox::accepted, this, &QDialog::accept); connect(m_buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject); // Check if already valid dir locationChanged( QString::null ); m_pWidget->projectLocationURL->setUrl(QUrl::fromLocalFile(QDir::homePath())); m_pWidget->projectLocationURL->setMode( KFile::Directory ); } void NewProjectDlg::accept() { m_projectName = m_pWidget->projectNameEdit->text(); m_projectLocation = m_pWidget->projectLocationURL->url().toLocalFile(); QDialog::accept(); } void NewProjectDlg::locationChanged( const QString & ) { m_location = m_pWidget->projectLocationURL->url().toLocalFile(); qDebug() << "location changed to: " << m_location; QDir subDir(m_location); if ( !m_location.endsWith("/") ) m_location.append("/"); if ( !m_pWidget->projectNameEdit->text().isEmpty() ) m_location.append( m_pWidget->projectNameEdit->text().toLower() + "/" ); m_pWidget->locationLabel->setText( m_location ); QDir dir(m_location); qDebug() << "dir.exists: " << dir.exists() << " subdir.exists: " << subDir.exists(); QPushButton *okButton = m_buttonBox->button(QDialogButtonBox::Ok); if ( dir.exists() || !subDir.exists() ) { okButton->setEnabled(false); } else { okButton->setEnabled(true); } } //END class NewProjectDlg class CreateSubprojectWidget : public QWidget, public Ui::CreateSubprojectWidget { public: CreateSubprojectWidget(QWidget *parent) : QWidget(parent) { setupUi(this); } }; //BEGIN class CreateSubprojectDlg CreateSubprojectDlg::CreateSubprojectDlg( QWidget * parent ) : QDialog(parent) { setObjectName("Create Subproject Dialog"); setModal(true); setWindowTitle(i18n("Create Subproject")); QVBoxLayout *mainLayout = new QVBoxLayout; setLayout(mainLayout); m_pWidget = new CreateSubprojectWidget(this); if ( ProjectManager::self()->currentProject() ) m_pWidget->m_targetFile->setUrl( ProjectManager::self()->currentProject()->url() ); m_type = ProgramType; mainLayout->addWidget(m_pWidget); QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok|QDialogButtonBox::Cancel); mainLayout->addWidget(buttonBox); QPushButton *okButton = buttonBox->button(QDialogButtonBox::Ok); okButton->setDefault(true); okButton->setShortcut(Qt::CTRL | Qt::Key_Return); connect(buttonBox, &QDialogButtonBox::accepted, this, &QDialog::accept); connect(buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject); } CreateSubprojectDlg::~CreateSubprojectDlg() { } void CreateSubprojectDlg::accept() { m_targetFile = m_pWidget->m_targetFile->url().toLocalFile(); m_type = (Type)m_pWidget->m_typeCombo->currentIndex(); QDialog::accept(); } //END class CreateSubprojectDlg class LinkerOptionsWidget : public QWidget, public Ui::LinkerOptionsWidget { public: LinkerOptionsWidget(QWidget *parent) : QWidget(parent) { setupUi(this); } }; //BEGIN class LinkerOptionsDlg LinkerOptionsDlg::LinkerOptionsDlg( LinkerOptions * linkingOptions, QWidget *parent ) : QDialog(parent) { setObjectName("Linker Options Dialog"); setModal(true); setWindowTitle(i18n("Linker Options")); QVBoxLayout *mainLayout = new QVBoxLayout; setLayout(mainLayout); m_pLinkerOptions = linkingOptions; m_pWidget = new LinkerOptionsWidget(this); ProjectInfo * pi = ProjectManager::self()->currentProject(); assert(pi); //BEGIN Update gplink options m_pWidget->m_pHexFormat->setCurrentIndex( m_pLinkerOptions->hexFormat() ); m_pWidget->m_pOutputMap->setChecked( m_pLinkerOptions->outputMapFile() ); m_pWidget->m_pLibraryDir->setText( m_pLinkerOptions->libraryDir() ); m_pWidget->m_pLinkerScript->setText( m_pLinkerOptions->linkerScript() ); m_pWidget->m_pOther->setText( m_pLinkerOptions->linkerOther() ); //END Update gplink options //BEGIN Update library widgets const QList availableInternal = pi->childOutputURLs( ProjectItem::LibraryType ); const QStringList linkedInternal = m_pLinkerOptions->linkedInternal(); const QUrl projectUrl = pi->url(); const QDir projectDir(projectUrl.path()); // using QDir for relativeFilePath logic, not assuming local file for (const QUrl &internalUrl : availableInternal) { QString relativeURL; if (projectUrl.scheme() == internalUrl.scheme() && projectUrl.host() == internalUrl.host() && projectUrl.port() == internalUrl.port() && projectUrl.userInfo() == internalUrl.userInfo()) { relativeURL = projectDir.relativeFilePath(internalUrl.path()); } else { relativeURL = internalUrl.toDisplayString(QUrl::PreferLocalFile); } // 2017.12.1 - convert to QListWidgetItem //Q3CheckListItem * item = new Q3CheckListItem( m_pWidget->m_pInternalLibraries, relativeURL, Q3CheckListItem::CheckBox ); QListWidgetItem * item = new QListWidgetItem( relativeURL, m_pWidget->m_pInternalLibraries ); item->setCheckState( (linkedInternal.contains(relativeURL)) ? Qt::Checked : Qt::Unchecked ); //item->setOn( linkedInternal.contains(relativeURL) ); // 2017.12.1 - convert to QListWidgetItem } m_pExternalLibraryRequester = new KUrlRequester( nullptr ); //m_pExternalLibraryRequester->fileDialog()->setUrl( KUrl( "/usr/share/sdcc/lib" ) ); m_pExternalLibraryRequester->fileDialog()->setDirectoryUrl( QUrl::fromLocalFile( "/usr/share/sdcc/lib" ) ); // // m_pWidget->m_pExternalLibraries = new KEditListBox( // // i18n("Link libraries outside project"), // // //m_pExternalLibraryRequester->customEditor(), // // KEditListBox::CustomEditor( // // m_pExternalLibraryRequester->comboBox(), m_pExternalLibraryRequester->lineEdit()), // // m_pWidget ); // // m_pWidget->m_pExternalLibraries->setTitle(i18n("Link libraries outside project")); // //m_pExternalLibraryRequester->fileDialog()->setUrl( KUrl( "/usr/share/sdcc/lib" ) ); // m_pExternalLibraryRequester->fileDialog()->setDirectoryUrl( KUrl( "/usr/share/sdcc/lib" ) ); // // delete m_pWidget->m_pExternalLibraries; // KEditListBox b; // // m_pWidget->m_pExternalLibraries = new KEditListBox( // i18n("Link libraries outside project"), // //m_pExternalLibraryRequester->customEditor(), // KEditListBox::CustomEditor( // m_pExternalLibraryRequester->comboBox(), m_pExternalLibraryRequester->lineEdit()), // m_pWidget ); // m_pWidget->m_pExternalLibraries->setTitle(i18n("Link libraries outside project")); // m_pExternalLibraryRequester->fileDialog()->selectUrl( KUrl( "/usr/share/sdcc/lib" ) ); // delete m_pWidget->m_pExternalLibraries; m_pWidget->m_pExternalLibraries = new KEditListWidget( //i18n("Link libraries outside project"), m_pExternalLibraryRequester->customEditor(), m_pWidget ); m_pWidget->m_pExternalLibraries->layout()->setMargin(11); { QGridLayout* grLayout = (dynamic_cast(m_pWidget->layout())); //grLayout->addMultiCellWidget( m_pWidget->m_pExternalLibraries, 7, 7, 0, 1 ); // 2018.12.02 grLayout->addWidget( m_pWidget->m_pExternalLibraries, 7, 0, 1, 2); } m_pWidget->m_pExternalLibraries->setButtons( KEditListWidget::Add | KEditListWidget::Remove ); m_pWidget->m_pExternalLibraries->insertStringList( m_pLinkerOptions->linkedExternal() ); //END Update library widgets mainLayout->addWidget(m_pWidget); QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok|QDialogButtonBox::Cancel); mainLayout->addWidget(buttonBox); QPushButton *okButton = buttonBox->button(QDialogButtonBox::Ok); okButton->setDefault(true); okButton->setShortcut(Qt::CTRL | Qt::Key_Return); connect(buttonBox, &QDialogButtonBox::accepted, this, &QDialog::accept); connect(buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject); } LinkerOptionsDlg::~LinkerOptionsDlg() { delete m_pExternalLibraryRequester; } void LinkerOptionsDlg::accept() { QStringList linkedInternal; for (int itemNr = 0; itemNr < m_pWidget->m_pInternalLibraries->count(); ++itemNr) { QListWidgetItem * item = m_pWidget->m_pInternalLibraries->item(itemNr); if ( item->checkState() == Qt::Checked ) { linkedInternal << item->text(); } } m_pLinkerOptions->setLinkedInternal( linkedInternal ); m_pLinkerOptions->setLinkedExternal( m_pWidget->m_pExternalLibraries->items() ); m_pLinkerOptions->setHexFormat( (LinkerOptions::HexFormat::type) m_pWidget->m_pHexFormat->currentIndex() ); m_pLinkerOptions->setOutputMapFile( m_pWidget->m_pOutputMap->isChecked() ); m_pLinkerOptions->setLibraryDir( m_pWidget->m_pLibraryDir->text() ); m_pLinkerOptions->setLinkerScript( m_pWidget->m_pLinkerScript->text() ); m_pLinkerOptions->setLinkerOther( m_pWidget->m_pOther->text() ); QDialog::accept(); } //END class LinkerOptionsDlg class ProcessingOptionsWidget : public QWidget, public Ui::ProcessingOptionsWidget { public: ProcessingOptionsWidget(QWidget *parent) : QWidget(parent) { setupUi(this); } }; //BEGIN class ProcessingOptionsDlg ProcessingOptionsDlg::ProcessingOptionsDlg( ProjectItem * projectItem, QWidget *parent ) : QDialog(parent) { setObjectName("Processing Options Dialog"); setModal(true); setWindowTitle(i18n("Processing Options")); QVBoxLayout *mainLayout = new QVBoxLayout; setLayout(mainLayout); m_pProjectItem = projectItem; m_pWidget = new ProcessingOptionsWidget(this); m_pWidget->m_pMicroSelect->setEnabled( !projectItem->useParentMicroID() ); switch ( projectItem->type() ) { case ProjectItem::ProjectType: m_pWidget->m_pOutputURL->setEnabled(false); break; case ProjectItem::FileType: m_pWidget->m_pOutputURL->setEnabled(true); break; case ProjectItem::ProgramType: case ProjectItem::LibraryType: m_pWidget->m_pOutputURL->setEnabled(false); break; } m_pWidget->m_pOutputURL->setUrl( projectItem->outputURL() ); m_pWidget->m_pMicroSelect->setMicro( projectItem->microID() ); mainLayout->addWidget(m_pWidget); QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok|QDialogButtonBox::Cancel); mainLayout->addWidget(buttonBox); QPushButton *okButton = buttonBox->button(QDialogButtonBox::Ok); okButton->setDefault(true); okButton->setShortcut(Qt::CTRL | Qt::Key_Return); connect(buttonBox, &QDialogButtonBox::accepted, this, &QDialog::accept); connect(buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject); } ProcessingOptionsDlg::~ProcessingOptionsDlg() { } void ProcessingOptionsDlg::accept() { if ( m_pWidget->m_pOutputURL->isEnabled() ) m_pProjectItem->setOutputURL( m_pWidget->m_pOutputURL->url() ); if ( m_pWidget->m_pMicroSelect->isEnabled() ) m_pProjectItem->setMicroID( m_pWidget->m_pMicroSelect->micro() ); QDialog::accept(); } //END class ProcessingOptionsDlg diff --git a/src/gui/richtexteditor.cpp b/src/gui/richtexteditor.cpp index ecc56746..453c5225 100644 --- a/src/gui/richtexteditor.cpp +++ b/src/gui/richtexteditor.cpp @@ -1,455 +1,455 @@ /*************************************************************************** * Copyright (C) 2006 by David Saxton - david@bluehaze.org * * * * 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 "richtexteditor.h" #include #include #include // #include #include #include #include -#include -#include -#include -#include +#include +#include +#include +#include //#include -#include -#include -#include -#include +#include +#include +#include +#include #include #include #include #include #include // #include // #include // #include //BEGIN class RichTextEditor RichTextEditor::RichTextEditor(QWidget *parent, const char *name) : QWidget(parent /*, name*/ ) { setObjectName( name ); QVBoxLayout * layout = new QVBoxLayout( this /*, 0, 6 */ ); layout->setMargin(0); layout->setSpacing(6); m_pEditor = new QTextEdit( this ); //, "RichTextEdit" ); m_pEditor->setObjectName("RichTextEdit"); layout->addWidget( m_pEditor ); //m_pEditor->setTextFormat( Qt::RichText ); // 2018.12.07 - just use toHtml() and html() connect( m_pEditor, SIGNAL( textChanged() ), SIGNAL( textChanged() ) ); connect( m_pEditor, SIGNAL( currentCharFormatChanged(const QTextCharFormat &) ), this, SLOT( slotCurrentCharFormatChanged( const QTextCharFormat &) ) ); //connect( m_pEditor, SIGNAL( currentFontChanged( const QFont & ) ), this, SLOT( fontChanged( const QFont & ) ) ); // 2018.01.03 - use slotCurrentCharFormatChanged //connect( m_pEditor, SIGNAL( currentColorChanged( const QColor & ) ), this, SLOT( colorChanged( const QColor & ) ) ); // 2018.01.03 - use slotCurrentCharFormatChanged //connect( m_pEditor, SIGNAL( currentAlignmentChanged( int ) ), this, SLOT( alignmentChanged( int ) ) ); // 2018.01.03 - use slotCurrentCharFormatChanged //connect( m_pEditor, SIGNAL( currentVerticalAlignmentChanged( Q3TextEdit::VerticalAlignment ) ), this, SLOT(verticalAlignmentChanged()) ); // 2018.01.03 - use slotCurrentCharFormatChanged KToolBar * tools = new KToolBar( this, "RichTextEditorToops" ); layout->addWidget( tools ); KActionCollection * ac = new KActionCollection( m_pEditor ); //m_pTextBold = new KToggleAction( i18n("Bold"), "format-text-bold", Qt::CTRL + Qt::Key_B, 0, 0, ac, "format_bold" ); m_pTextBold = new KToggleAction( i18n("Bold"), ac); m_pTextBold->setObjectName("text_bold"); m_pTextBold->setShortcut(Qt::CTRL + Qt::Key_B); m_pTextBold->setIcon( QIcon::fromTheme("format-text-bold") ); connect( m_pTextBold, SIGNAL(toggled(bool)), this, SLOT(slotSetBold(bool)) ); //m_pTextBold->plug( tools ); tools->addAction(m_pTextBold); //m_pTextItalic = new KToggleAction( i18n("Italic"), "format-text-italic", Qt::CTRL + Qt::Key_I, 0, 0, ac, "format_italic" ); m_pTextItalic = new KToggleAction( i18n("Italic"), ac); m_pTextItalic->setObjectName("text_italic"); m_pTextItalic->setShortcut( Qt::CTRL + Qt::Key_I ); m_pTextItalic->setIcon( QIcon::fromTheme("format-text-italic") ); connect( m_pTextItalic, SIGNAL(toggled(bool)), this, SLOT(slotSetItalic(bool)) ); //m_pTextItalic->plug( tools ); tools->addAction(m_pTextItalic); //m_pTextUnderline = new KToggleAction( i18n("Underline"), "format-text-underline", Qt::CTRL + Qt::Key_U, 0, 0, ac, "format_underline" ); m_pTextUnderline = new KToggleAction( i18n("Underline"), ac); m_pTextUnderline->setObjectName("text_under"); m_pTextUnderline->setShortcut( Qt::CTRL + Qt::Key_U ); m_pTextItalic->setIcon( QIcon::fromTheme("format-text-underline") ); connect( m_pTextUnderline, SIGNAL(toggled(bool)), this, SLOT(slotSetUnderline(bool)) ); //m_pTextUnderline->plug( tools ); tools->addAction(m_pTextUnderline); //m_pTextList = new KToggleAction( i18n("List"), "unsorted_list", Qt::CTRL + Qt::Key_L, 0, 0, ac, "format_list" ); m_pTextList = new KToggleAction( i18n("List"), ac); m_pTextList->setObjectName("unsorted_list"); m_pTextList->setShortcut( Qt::CTRL + Qt::Key_L ); m_pTextItalic->setIcon( QIcon::fromTheme("format-list-unordered") ); connect( m_pTextList, SIGNAL(toggled(bool)), SLOT(slotSetList(bool)) ); //m_pTextList->plug( tools ); tools->addAction( m_pTextList ); //BEGIN Text horizontal-alignment actions //m_pTextAlignment = new KToolBarPopupAction( i18n("Text Alignment"), "format-justify-left", 0, 0, 0, ac, "text_alignment" ); m_pTextAlignment = new KToolBarPopupAction( QIcon::fromTheme("format-justify-left"), i18n("Text Alignment"), ac); m_pTextAlignment->setObjectName("text_left"); //m_pTextAlignment->plug( tools ); tools->addAction(m_pTextAlignment); m_pTextAlignment->setDelayed(false); //K3PopupMenu * m = m_pTextAlignment->menu(); QMenu * m = m_pTextAlignment->menu(); //m->insertTitle( i18n("Text Alignment") ); m->setTitle( i18n("Text Alignment")); //m->setCheckable( true ); // 2018.12.07 //m->insertItem( QIcon::fromTheme( "format-justify-left" ), i18n("Align Left"), Qt::AlignLeft ); m->addAction( QIcon::fromTheme( "format-justify-left" ), i18n("Align Left") )->setData( Qt::AlignLeft ); //m->insertItem( QIcon::fromTheme( "format-justify-center"), i18n("Align Center"), Qt::AlignHCenter ); m->addAction( QIcon::fromTheme( "format-justify-center"), i18n("Align Center") )->setData( Qt::AlignHCenter ); //m->insertItem( QIcon::fromTheme( "format-justify-right" ), i18n("Align Right"), Qt::AlignRight ); m->addAction( QIcon::fromTheme( "format-justify-right" ), i18n("Align Right") )->setData( Qt::AlignRight ); //m->insertItem( QIcon::fromTheme( "format-justify-fill" ), i18n("Align Block"), Qt::AlignJustify ); m->addAction( QIcon::fromTheme( "format-justify-fill" ), i18n("Align Block") )->setData( Qt::AlignJustify ); connect( m, SIGNAL(triggered(QAction*)), this, SLOT(slotSetAlignment(QAction*)) ); //END Text horizontal-alignment actions //BEGIN Text vertical-alignment actions //m_pTextVerticalAlignment = new KToolBarPopupAction( i18n("Text Vertical Alignment"), "text", 0, 0, 0, ac, "text_vertical_alignment" ); m_pTextVerticalAlignment = new KToolBarPopupAction( QIcon::fromTheme(QString("text_vertical_alignment")), i18n("Text Vertical Alignment"), ac); m_pTextVerticalAlignment->setObjectName("text"); //m_pTextVerticalAlignment->plug( tools ); tools->addAction(m_pTextVerticalAlignment); m_pTextVerticalAlignment->setDelayed(false); m = m_pTextVerticalAlignment->menu(); //m->insertTitle( i18n("Text Vertical Alignment") ); m->setTitle( i18n("Text Vertical Alignment") ); //m->setCheckable( true ); // 2018.12.07 m->addAction( QIcon::fromTheme( "format-text-superscript" ), i18n("Superscript") )->setData(QTextCharFormat::AlignSuperScript ); //m->insertItem( QIcon::fromTheme( "format-text-superscript" ), i18n("Superscript"), QTextCharFormat::AlignSuperScript ); m->addAction( i18n("Normal") )->setData( QTextCharFormat::AlignNormal ); //m->insertItem( i18n("Normal"), QTextCharFormat::AlignNormal ); m->addAction( QIcon::fromTheme( "format-text-subscript" ), i18n("Subscript") )->setData( QTextCharFormat::AlignSubScript ); //m->insertItem( QIcon::fromTheme( "format-text-subscript" ), i18n("Subscript"), QTextCharFormat::AlignSubScript ); connect( m, SIGNAL(triggered(QAction*)), this, SLOT(slotSetVerticalAlignment(QAction*)) ); //END Text vertical-alignment actions QPixmap pm( 16, 16 ); pm.fill( Qt::black ); //m_pTextColor = new KAction( i18n("Text Color..."), pm, 0, this, SLOT(textColor()), ac, "format_color" ); m_pTextColor = new QAction( i18n("Text Color..."), this); m_pTextColor->setIcon(pm); m_pTextColor->setObjectName("format_color"); connect(m_pTextColor, SIGNAL(triggered(bool)), this, SLOT(textColor())); //m_pTextColor->plug( tools ); ac->addAction("format_color", m_pTextColor); tools->addAction(m_pTextColor); } RichTextEditor::~RichTextEditor() { } void RichTextEditor::makeUseStandardFont( QString * html ) { if ( !html ) return; QFont f; QString bodyString = QString("").arg( f.pointSize() ).arg( f.family() ); if ( html->contains("") ) { // Set the correct font size QFont f; html->replace( "", bodyString ); } else if ( !html->startsWith("") ) { html->prepend( "" + bodyString ); html->append( "" ); } } QWidget * RichTextEditor::editorViewport() const { return m_pEditor->viewport(); } void RichTextEditor::setText( QString text ) { if ( !Qt::mightBeRichText( text ) ) { // Format the text to be HTML text.replace( '\n', "
" ); } m_pEditor->setText( text ); } QString RichTextEditor::text() const { QString text = m_pEditor->toHtml().trimmed(); // Remove the style info (e.g. style="font-size:8pt;font-family:DejaVu Sans") inserted into the body tag. text.replace( QRegExp( ""), "" ); // Replace all non-latin1 characters with HTML codes to represent them QString nonAsciiChars; for ( int i = 0; i < text.length(); ++i ) { QChar current = text[i]; if ( (current.toLatin1() == 0) && (current.unicode() != 0) ) { // A non-latin1 character if ( !nonAsciiChars.contains( current ) ) nonAsciiChars.append( current ); } } for ( int i = 0; i < nonAsciiChars.length(); ++i ) { text.replace( nonAsciiChars[i], QString("&#%1;").arg( nonAsciiChars[i].unicode() ) ); } return text; } void RichTextEditor::insertURL( const QString & url, const QString & text ) { insertHTML( QString("%2").arg( url ).arg( text ) ); } void RichTextEditor::insertHTML( const QString & html ) { // 2018.12.07 // // Save cursor position // //int cursorPara, cursorIndex; // //m_pEditor->getCursorPosition( & cursorPara, & cursorIndex ); // QPoint cursorPos; // cursorPos = m_pEditor->cursor().pos(); // // // replaceString is used so that the inserted text is at the cursor position. // // it's just a random set of characters, so that the chance of them actually being // // used is about zero. // QString replaceString = "SXbCk2CtqJ83"; // // m_pEditor->insert( replaceString ); // QString editorText = m_pEditor->text(); // //editorText.replace( replaceString, html, (uint)0 ); // 2018.12.07 // editorText.replace( replaceString, html, Qt::CaseInsensitive ); // m_pEditor->setText( editorText ); // // // Restore cursor position // //m_pEditor->setCursorPosition( cursorPara, cursorIndex ); // m_pEditor->cursor().setPos(cursorPos); m_pEditor->insertHtml(html); } void RichTextEditor::slotSetBold(bool isBold) { QTextCharFormat format; if (isBold) { format.setFontWeight(QFont::Bold); } else { format.setFontWeight(QFont::Normal); } m_pEditor->textCursor().mergeCharFormat(format); } void RichTextEditor::slotSetItalic(bool isItalic) { QTextCharFormat format; format.setFontItalic(isItalic); m_pEditor->textCursor().mergeCharFormat(format); } void RichTextEditor::slotSetUnderline(bool isUnderline) { QTextCharFormat format; format.setFontUnderline(isUnderline); m_pEditor->textCursor().mergeCharFormat(format); } void RichTextEditor::slotSetAlignment(QAction *act) { int alignment = act->data().toInt(); QTextBlockFormat format = m_pEditor->textCursor().blockFormat(); format.setAlignment( (Qt::AlignmentFlag) alignment ); m_pEditor->textCursor().mergeBlockFormat(format); } void RichTextEditor::slotSetVerticalAlignment(QAction *action ) { int a = action->data().toInt(); //m_pEditor->setVerticalAlignment( (Q3TextEdit::VerticalAlignment)a ); //m_pEditor->setAlignment(a); QTextCharFormat format; format.setVerticalAlignment( (QTextCharFormat::VerticalAlignment)a ); m_pEditor->mergeCurrentCharFormat(format); } void RichTextEditor::slotSetList( bool set ) { //m_pEditor->setParagType( set ? Q3StyleSheetItem::DisplayListItem : Q3StyleSheetItem::DisplayBlock, Q3StyleSheetItem::ListDisc ); if (set) { m_pEditor->textCursor().createList(QTextListFormat::ListDisc); } else { QTextCursor cursor = m_pEditor->textCursor(); QTextList *list = cursor.currentList(); if( list ) { QTextListFormat::Style style = QTextListFormat::ListStyleUndefined; QTextListFormat listFormat; listFormat.setIndent( 0 ); listFormat.setStyle( style ); list->setFormat( listFormat ); for( int i = list->count() - 1; i >= 0 ; --i ) list->removeItem( i ); } } } void RichTextEditor::slotCurrentCharFormatChanged(const QTextCharFormat & f) { fontChanged( f.font() ); //colorChanged( m_pEditor->foregroundColor() ); // 2018.12.07 colorChanged( m_pEditor->palette().color( m_pEditor->foregroundRole()) ); alignmentChanged( m_pEditor->alignment() ); verticalAlignmentChanged(); // note: consider removing this method } void RichTextEditor::fontChanged( const QFont & f ) { m_pTextBold->setChecked( f.bold() ); m_pTextItalic->setChecked( f.italic() ); m_pTextUnderline->setChecked( f.underline() ); } void RichTextEditor::textColor() { const QColor c = QColorDialog::getColor(m_pEditor->textColor(), this); if (c.isValid()) { m_pEditor->setTextColor( c ); } } void RichTextEditor::colorChanged( const QColor & c ) { QPixmap pix( 16, 16 ); pix.fill( c ); m_pTextColor->setIcon( pix ); } void RichTextEditor::alignmentChanged( int a ) { if ( /*( a == Qt::AlignAuto ) || */ ( a & Qt::AlignLeft )) m_pTextAlignment->setIcon( QIcon::fromTheme("format-justify-left") ); else if ( ( a & Qt::AlignHCenter ) ) m_pTextAlignment->setIcon( QIcon::fromTheme("format-justify-center") ); else if ( ( a & Qt::AlignRight ) ) m_pTextAlignment->setIcon( QIcon::fromTheme("format-justify-right") ); else if ( ( a & Qt::AlignJustify ) ) m_pTextAlignment->setIcon( QIcon::fromTheme("format-justify-fill") ); } void RichTextEditor::verticalAlignmentChanged() { // QTextEdit::VerticalAlignment a = // if ( a == KTextEdit::AlignNormal ) // m_pTextVerticalAlignment->setIcon( "text" ); // else if ( a == KTextEdit::AlignSuperScript ) // m_pTextVerticalAlignment->setIcon( "format-text-superscript" ); // else if ( a == KTextEdit::AlignSubScript ) // m_pTextVerticalAlignment->setIcon( "format-text-subscript" ); } void RichTextEditor::setResourcePaths( const QStringList & paths ) { //m_pEditor->mimeSourceFactory()->setFilePath( paths ); for (QStringList::const_iterator itStr = paths.begin(); itStr != paths.end(); ++itStr) { QString dirName = *itStr; QDir dir(dirName); dir.setFilter(QDir::Files); QStringList l; l << "*.png"; dir.setNameFilters(l); QFileInfoList fileInfoList = dir.entryInfoList(); qDebug() << Q_FUNC_INFO << " lsit size " << fileInfoList.size(); for (QFileInfoList::iterator itFile = fileInfoList.begin(); itFile != fileInfoList.end(); ++itFile) { QFileInfo &fi = *itFile; QString fullPath = fi.path() + "/" + fi.fileName(); QPixmap img(fullPath); if (img.isNull()) { qWarning() << Q_FUNC_INFO << " img is null " << fullPath; } m_pEditor->document()->addResource(QTextDocument::ImageResource, QUrl(fi.fileName()), QVariant(img)); qDebug() << Q_FUNC_INFO << " added resource: " << fi.fileName() << " to " << fullPath; } } } //END class RichTextEditor //BEGIN class RichTextEditorDlg RichTextEditorDlg::RichTextEditorDlg( QWidget * parent, const QString & caption ) : QDialog(parent) { setObjectName("RichTextEditorDlg"); setModal(true); setWindowTitle(caption); QVBoxLayout *mainLayout = new QVBoxLayout; setLayout(mainLayout); m_pEditor = new RichTextEditor(this); mainLayout->addWidget(m_pEditor); QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok|QDialogButtonBox::Cancel); mainLayout->addWidget(buttonBox); QPushButton *okButton = buttonBox->button(QDialogButtonBox::Ok); okButton->setDefault(true); okButton->setShortcut(Qt::CTRL | Qt::Key_Return); connect(buttonBox, &QDialogButtonBox::accepted, this, &QDialog::accept); connect(buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject); } //END class RichTextEditorDlg diff --git a/src/gui/scopescreen.cpp b/src/gui/scopescreen.cpp index cd4f6443..60ab895e 100644 --- a/src/gui/scopescreen.cpp +++ b/src/gui/scopescreen.cpp @@ -1,57 +1,57 @@ /*************************************************************************** * Copyright (C) 2005 by John Myers * * electronerd@electronerdia.net * * * * 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 "scopescreen.h" #include "probe.h" #include "probepositioner.h" #include "simulator.h" #include "ktechlab.h" #include -#include #include +#include // #include -#include -#include -#include -#include -#include +#include +#include +#include +#include +#include #include ScopeScreen::ScopeScreen( KateMDI::ToolView * parent) : QWidget(parent) { if (parent->layout()) { parent->layout()->addWidget(this); qDebug() << Q_FUNC_INFO << " added item selector to parent's layout " << parent; } else { qWarning() << Q_FUNC_INFO << " unexpected null layout on parent " << parent ; } setupUi(this); } ScopeScreen::~ScopeScreen() {} ScopeScreen * ScopeScreen::self( KateMDI::ToolView * parent ) { static ScopeScreen * pSelf = nullptr; if(pSelf) return pSelf; assert(parent); pSelf = new ScopeScreen(parent); return pSelf; } diff --git a/src/gui/scopescreenview.cpp b/src/gui/scopescreenview.cpp index b6b5764a..fb571203 100644 --- a/src/gui/scopescreenview.cpp +++ b/src/gui/scopescreenview.cpp @@ -1,276 +1,276 @@ /*************************************************************************** * Copyright (C) 2005 by John Myers * * electronerd@electronerdia.net * * * * 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 "oscilloscope.h" #include "oscilloscopedata.h" #include "probe.h" #include "scopescreenview.h" #include "simulator.h" #include "probepositioner.h" -#include -#include -#include +#include +#include +#include #include #define FADESPEED 1 ScopeScreenView::ScopeScreenView(QWidget *parent, const char *name) : ScopeViewBase(parent, name), m_intervalsX(10), m_ticksPerIntervalX(10000), m_offsetX(0) { m_updateViewTmr = new QTimer(this); connect( m_updateViewTmr, SIGNAL(timeout()), this, SLOT(updateViewTimeout()) ); m_updateViewTmr->start(50); } ScopeScreenView::~ScopeScreenView() {} #if 0 void ScopeScreenView::drawContents(QPainter * p) { QRect cr = contentsRect(); for(int i =1; i < m_intervalsX; i++) { int x = cr.left() + cr.width()*i/m_intervalsX; p->drawLine(x, cr.top(), x, cr.bottom()); } const int ticksPerScreen = m_intervalsX * m_ticksPerIntervalX; const double pixelsPerTick = cr.width()/double(ticksPerScreen); const double ticksPerPixel = m_intervalsX * m_ticksPerIntervalX / cr.width(); //draw the current time int curTimeX = ((Simulator::self()->time() + m_offsetX) % (ticksPerScreen)) * pixelsPerTick; //qDebug() << curTimeX <drawLine(curTimeX, cr.top(), curTimeX, cr.bottom()); //the following is liberally borrowed from OscilloscopeView::drawFloatingData const FloatingProbeDataMap::iterator end = Oscilloscope::self()->m_floatingProbeDataMap.end(); for ( FloatingProbeDataMap::iterator it = Oscilloscope::self()->m_floatingProbeDataMap.begin(); it != end; ++it ) { FloatingProbeData * probe = it.value(); StoredData * data = &(probe->m_data); if ( data->allocatedUpTo() == 0 ) continue; bool logarithmic = probe->scaling() == FloatingProbeData::Logarithmic; double lowerAbsValue = probe->lowerAbsValue(); double sf = ((cr.height()/Oscilloscope::self()->numberOfProbes())/2) / (logarithmic ? log(probe->upperAbsValue()/lowerAbsValue) : probe->upperAbsValue()); const int midHeight = Oscilloscope::self()->probePositioner->probePosition(probe); //const int midHeight = cr.top() + cr.height()/2; //const llong timeOffset = Oscilloscope::self()->scrollTime(); const llong timeOffset = Simulator::self()->time() - (FADESPEED * m_intervalsX * m_ticksPerIntervalX); // Draw the horizontal line indicating the midpoint of our output p->setPen( QColor( 228, 228, 228 ) ); p->drawLine( 0, midHeight, width(), midHeight ); // Set the pen colour according to the colour the user has selected for the probe p->setPen( probe->color() ); llong at = probe->findPos(timeOffset); const llong maxAt = probe->insertPos(); llong prevTime = probe->toTime(at); double v = data->dataAt((at>0)?at:0); int prevY = int(midHeight - (logarithmic ? ( (v>0) ? log(v/lowerAbsValue) : -log(-v/lowerAbsValue) ) : v) * sf); int prevX = (int((prevTime - timeOffset)*pixelsPerTick) + curTimeX) % cr.width(); while ( at < maxAt-1 ) { at++; ullong nextTime = probe->toTime(at); double v = data->dataAt((at>0)?at:0); int nextY = int(midHeight - (logarithmic ? ( (v>0) ? log(v/lowerAbsValue) : -log(-v/lowerAbsValue) ) : v) * sf); int nextX = (int((nextTime - timeOffset)*pixelsPerTick) + curTimeX) % cr.width(); if(nextX < prevX) { prevX = 0; } //qDebug() <drawLine( prevX, prevY, nextX, nextY ); prevTime = nextTime; prevX = nextX; prevY = nextY; //if ( nextX > width() ) //break; }; // If we could not draw right to the end; it is because we exceeded // maxAt //if ( prevX < curTimeX ) // p->drawLine( prevX, prevY, curTimeX, prevY ); } //and this was liberally borrowed from OscilloscopeView::DrawLogicData { const LogicProbeDataMap::iterator end = Oscilloscope::self()->m_logicProbeDataMap.end(); for ( LogicProbeDataMap::iterator it = Oscilloscope::self()->m_logicProbeDataMap.begin(); it != end; ++it ) { // When searching for the next logic value to display, we look along // until there is a recorded point which is at least one pixel along // If we are zoomed out far, there might be thousands of data points // between each pixel. It is time consuming searching for the next point // to display one at a time, so we record the average number of data points // between pixels ( = deltaAt / totalDeltaAt ) llong deltaAt = 1; int totalDeltaAt = 1; LogicProbeData * probe = it.value(); StoredData * data = &(probe->m_data); if ( data->allocatedUpTo() == 0 ) continue; const int midHeight = Oscilloscope::self()->probePositioner->probePosition(probe); const llong timeOffset = Simulator::self()->time() - (FADESPEED * m_intervalsX * m_ticksPerIntervalX);//Oscilloscope::self()->scrollTime(); const int halfOutputHeight = ((cr.height()/Oscilloscope::self()->numberOfProbes())/2); // Draw the horizontal line indicating the midpoint of our output p->setPen( QColor( 228, 228, 228 ) ); p->drawLine( 0, midHeight, width(), midHeight ); // Set the pen colour according to the colour the user has selected for the probe p->setPen( probe->color() ); // The smallest time step that will display in our oscilloscope const int minTimeStep = ticksPerPixel;//int(LOGIC_UPDATE_RATE/pixelsPerSecond); llong at = probe->findPos(timeOffset); const llong maxAt = probe->insertPos(); llong prevTime = data->dataAt(at).time; int prevX = (int((prevTime - timeOffset)*pixelsPerTick) + curTimeX) % cr.width(); bool prevHigh = data->dataAt(at).value; int prevY = midHeight + int(prevHigh ? -halfOutputHeight : +halfOutputHeight); while ( at < maxAt ) { // Search for the next pos which will show up at our zoom level llong previousAt = at; llong dAt = deltaAt / totalDeltaAt; while ( (dAt > 1) && (at < maxAt) && ( (llong(data->dataAt(at).time) - prevTime) != minTimeStep ) ) { // Search forwards until we overshoot while ( at < maxAt && ( llong(data->dataAt(at).time) - prevTime ) < minTimeStep ) at += dAt; dAt /= 2; // Search backwards until we undershoot while ( (at < maxAt) && ( llong(data->dataAt(at).time) - prevTime ) > minTimeStep ) { at -= dAt; if ( at < 0 ) at = 0; } dAt /= 2; } // Possibly increment the value of at found by one (or more if this is the first go) while ( (previousAt == at) || ((at < maxAt) && ( llong(data->dataAt(at).time) - prevTime ) < minTimeStep) ) at++; if ( at >= maxAt ) break; // Update the average values deltaAt += at - previousAt; totalDeltaAt++; bool nextHigh = data->dataAt(at).value; if ( nextHigh == prevHigh ) continue; llong nextTime = data->dataAt(at).time; int nextX = (int((nextTime - timeOffset)*pixelsPerTick) + curTimeX) % cr.width(); int nextY = midHeight + int(nextHigh ? -halfOutputHeight : +halfOutputHeight); p->drawLine( prevX, prevY, nextX, prevY ); p->drawLine( nextX, prevY, nextX, nextY ); prevHigh = nextHigh; prevTime = nextTime; prevX = nextX; prevY = nextY; if ( nextX > width() ) break; }; // If we could not draw right to the end; it is because we exceeded // maxAt //if ( prevX < width() ) // p->drawLine( prevX, prevY, width(), prevY ); } } } #endif void ScopeScreenView::setIntervalsX( int value ) { m_intervalsX = value; } void ScopeScreenView::setTicksPerIntervalX( int value ) { m_ticksPerIntervalX = value; } void ScopeScreenView::setOffsetX( int value ) { m_offsetX = value; } void ScopeScreenView::updateViewTimeout( ) { repaint(); } void ScopeScreenView::drawBackground( QPainter & p ) { QRect cr = contentsRect(); for(int i =1; i < m_intervalsX; i++) { int x = cr.left() + cr.width()*i/m_intervalsX; p.drawLine(x, cr.top(), x, cr.bottom()); } ///\todo REMOVE THIS NOTICE p.drawText(cr.left(), cr.top(), "NOT YET IMPLEMENTED"); } void ScopeScreenView::drawMidLine( QPainter & p, ProbeData * probe ) { const int midHeight = Oscilloscope::self()->probePositioner->probePosition(probe); // Draw the horizontal line indicating the midpoint of our output p.setPen( QColor( 228, 228, 228 ) ); p.drawLine( 0, midHeight, width(), midHeight ); } void ScopeScreenView::drawProbe( QPainter & p, LogicProbeData * probe ) { } void ScopeScreenView::drawProbe( QPainter & p, FloatingProbeData * probe ) { } diff --git a/src/gui/scopescreenview.h b/src/gui/scopescreenview.h index d72f78c2..a19854ec 100644 --- a/src/gui/scopescreenview.h +++ b/src/gui/scopescreenview.h @@ -1,65 +1,65 @@ /*************************************************************************** * Copyright (C) 2005 by John Myers * * electronerd@electronerdia.net * * * * 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. * ***************************************************************************/ #ifndef SCOPESCREENVIEW_H #define SCOPESCREENVIEW_H #include "scopeviewbase.h" -#include +#include class QTimer; ///Screen-type Oscilloscope data view /** An Oscilloscope screen, as opposed to a paper tape (the current Oscilloscope) @author John Myers */ class ScopeScreenView : public ScopeViewBase { Q_OBJECT public: ScopeScreenView(QWidget *parent = nullptr, const char *name = nullptr); ~ScopeScreenView() override; //virtual void drawContents(QPainter * p); void drawBackground(QPainter & p) override; ///Draw the horizontal line indicating the midpoint of our output for \c probe void drawMidLine(QPainter & p, ProbeData * probe) override; ///\TODO: remove virtual; draw one logic probe void drawProbe(QPainter& p, LogicProbeData * probe) override; ///\TODO: remove virtual; draw one floating-point probe void drawProbe(QPainter& p, FloatingProbeData * probe) override; /// gives the first Simulator tick visible in the view llong visibleStartTime() const override {return 0;} /// gives the last Simulator tick visible in the view llong visibleEndTime() const override {return 0;} double ticksPerPixel() const override {return 0;} llong pixelsPerTick() const override {return 0;} public slots: void setIntervalsX(int value); void setTicksPerIntervalX(int value); void setOffsetX(int value); void updateViewTimeout(); private: int m_intervalsX; int m_ticksPerIntervalX; int m_offsetX; QTimer* m_updateViewTmr; }; #endif diff --git a/src/gui/scopeviewbase.cpp b/src/gui/scopeviewbase.cpp index 223ecc1d..c3a18423 100644 --- a/src/gui/scopeviewbase.cpp +++ b/src/gui/scopeviewbase.cpp @@ -1,119 +1,119 @@ /*************************************************************************** * Copyright (C) 2005-2006 by Jonathan Myers and David Saxton * * electronerd@electronerdia.net david@bluehaze.org * * * * 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 "scopeviewbase.h" #include "oscilloscope.h" #include "oscilloscopedata.h" #include "probepositioner.h" -#include -#include -#include +#include +#include +#include //for testing //#include ScopeViewBase::ScopeViewBase(QWidget *parent, const char *name) : QFrame(parent /* ,name */ /* , Qt::WNoAutoErase*/ ), b_needRedraw(true), m_pixmap(nullptr), m_halfOutputHeight(0.0) { setObjectName( name ); } ScopeViewBase::~ScopeViewBase() { delete m_pixmap; } void ScopeViewBase::paintEvent( QPaintEvent * event ) { QRect r = event->rect(); if (b_needRedraw) { //CALLGRIND_TOGGLE_COLLECT(); updateOutputHeight(); if (!m_pixmap) { qWarning() << Q_FUNC_INFO << "unexpected null pixmap in " << this; return; } QPainter p; //m_pixmap->fill( paletteBackgroundColor() ); // 2018.12.07 m_pixmap->fill( palette().color(backgroundRole()) ); const bool startSuccess = p.begin(m_pixmap); if ((!startSuccess) || (!p.isActive())) { qWarning() << Q_FUNC_INFO << " painter is not active"; } p.setClipRegion(event->region()); //let the subclass draw the background (grids, etc.) drawBackground(p); // drawProbeMap(p, Oscilloscope::self()->m_logicProbeDataMap); // drawProbeMap(p, Oscilloscope::self()->m_floatingProbeDataMap); p.setPen(Qt::black); p.drawRect( frameRect() ); b_needRedraw = false; //CALLGRIND_TOGGLE_COLLECT(); } //bitBlt( this, r.x(), r.y(), m_pixmap, r.x(), r.y(), r.width(), r.height() ); // 2018.12.07 QPainter p; const bool paintStarted = p.begin(this); if (!paintStarted) { qWarning() << Q_FUNC_INFO << " failed to start painting "; } p.drawImage(r, m_pixmap->toImage(), r); } void ScopeViewBase::updateOutputHeight() { m_halfOutputHeight = int((Oscilloscope::self()->probePositioner->probeOutputHeight() - (probeArrowWidth/Oscilloscope::self()->numberOfProbes()))/2)-1; } void ScopeViewBase::resizeEvent( QResizeEvent * event ) { delete m_pixmap; m_pixmap = new QPixmap( event->size() ); b_needRedraw = true; QFrame::resizeEvent(event); } /** * This is the main drawing loop function. */ template void ScopeViewBase::drawProbeMap( QPainter & p, QMap< int, T * > & map ) { typedef typename QMap::iterator TheIterator; const TheIterator end = map.end(); for ( TheIterator it = map.begin(); it != end; ++it ) { T * probe = it.value(); if ( probe->isEmpty() ) return; drawMidLine( p, probe ); // Set the pen colour according to the colour the user has selected for the probe p.setPen( probe->color() ); drawProbe( p, probe ); } } diff --git a/src/gui/scopeviewbase.h b/src/gui/scopeviewbase.h index 40546a97..9493d90d 100644 --- a/src/gui/scopeviewbase.h +++ b/src/gui/scopeviewbase.h @@ -1,79 +1,79 @@ /*************************************************************************** * Copyright (C) 2005-2006 by Jonathan Myers and David Saxton * * electronerd@electronerdia.net david@bluehaze.org * * * * 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. * ***************************************************************************/ #ifndef SCOPEVIEWBASE_H #define SCOPEVIEWBASE_H -#include +#include class Oscilloscope; class Simulator; class ProbeData; class LogicProbeData; class FloatingProbeData; class QMouseEvent; class QPaintEvent; class QPixmap; class QTimer; typedef long long llong; ///base class of oscilloscope views /** * * This is a refactoring of OscilloscopeView and my ScopeScreenView to promote * code reuse, both between the classes and within them. This is an abstract * class. * @author John Myers * @author David Saxton * */ class ScopeViewBase : public QFrame { Q_OBJECT public: ScopeViewBase(QWidget *parent = nullptr, const char *name = nullptr); virtual void drawBackground(QPainter & p) = 0; void resizeEvent( QResizeEvent *event ) override; void updateOutputHeight(); ~ScopeViewBase() override; protected: ///Draw the horizontal line indicating the midpoint of our output for \c probe virtual void drawMidLine(QPainter & p, ProbeData * probe) = 0; ///\TODO: remove virtual; draw one logic probe virtual void drawProbe(QPainter& p, LogicProbeData * probe) = 0; ///\TODO: remove virtual; draw one floating-point probe virtual void drawProbe(QPainter& p, FloatingProbeData * probe) = 0; /// gives the first Simulator tick visible in the view virtual llong visibleStartTime() const = 0; /// gives the last Simulator tick visible in the view virtual llong visibleEndTime() const = 0; virtual double ticksPerPixel() const = 0; virtual llong pixelsPerTick() const = 0; bool b_needRedraw; QPixmap *m_pixmap; double m_halfOutputHeight; private: ///draws a mapping of probes template void drawProbeMap(QPainter& p, QMap& map); void paintEvent( QPaintEvent *event ) override; }; #endif diff --git a/src/gui/settingsdlg.cpp b/src/gui/settingsdlg.cpp index 4554937c..c665f8e3 100644 --- a/src/gui/settingsdlg.cpp +++ b/src/gui/settingsdlg.cpp @@ -1,436 +1,436 @@ /*************************************************************************** * Copyright (C) 2005 by David Saxton * * david@bluehaze.org * * * * 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 "picprogrammer.h" #include "port.h" #include "settingsdlg.h" #include "inputdialog.h" #include #include #include #include #include -#include -#include -#include -#include +#include +#include +#include +#include #include #include #include #include #include #include #include #include #include class GeneralOptionsWidget : public QWidget, public Ui::GeneralOptionsWidget { public: GeneralOptionsWidget(QWidget *parent, const char *name = nullptr) : QWidget(parent /*, name */) { setObjectName(name); setupUi(this); } }; class GpasmSettingsWidget : public QWidget, public Ui::GpasmSettingsWidget { public: GpasmSettingsWidget(QWidget *parent, const char *name = nullptr) : QWidget(parent /*, name*/) { setObjectName(name); setupUi(this); } }; class SDCCOptionsWidget : public QWidget, public Ui::SDCCOptionsWidget { public: SDCCOptionsWidget(QWidget *parent, const char *name = nullptr) : QWidget(parent /*, name*/) { setObjectName(name); setupUi(this); } }; class AsmFormattingWidget : public QWidget, public Ui::AsmFormattingWidget { public: AsmFormattingWidget(QWidget *parent, const char *name = nullptr) : QWidget(parent /*, name*/) { setObjectName(name); setupUi(this); } }; class LogicWidget : public QWidget, public Ui::LogicWidget { public: LogicWidget(QWidget *parent, const char *name = nullptr) : QWidget(parent /*, name*/) { setObjectName(name); setupUi(this); } }; class PicProgrammerConfigWidget : public QWidget, public Ui::PicProgrammerConfigWidget { public: PicProgrammerConfigWidget(QWidget *parent, const char *name = nullptr) : QWidget(parent /*, name*/) { setObjectName(name); setupUi(this); } }; class GplinkSettingsWidget : public QWidget, public Ui::GplinkSettingsWidget { public: GplinkSettingsWidget(QWidget *parent, const char *name = nullptr) : QWidget(parent /*, name*/) { setObjectName(name); setupUi(this); } }; // Make sure that this value is the same as that in ktechlab.kcfg const int defaultRefreshRate = 50; SettingsDlg::SettingsDlg( QWidget *parent, const char *name, KCoreConfigSkeleton *config ) : KConfigDialog( parent, name, config ) { m_generalOptionsWidget = new GeneralOptionsWidget( this, "generalOptionsWidget" ); m_gpasmSettingsWidget = new GpasmSettingsWidget( this, "gpasmSettingsWidget" ); m_sdccOptionsWidget = new SDCCOptionsWidget( this, "sdccOptionsWidget" ); m_asmFormattingWidget = new AsmFormattingWidget( this, "asmFormattingWidget" ); m_logicWidget = new LogicWidget( this, "logicWidget" ); m_picProgrammerConfigWidget = new PicProgrammerConfigWidget( this, "picProgrammerConfigWidget" ); m_gplinkSettingsWidget = new GplinkSettingsWidget( this, "gplinkSettingsWidget" ); m_pPicProgrammerSettings = new PicProgrammerSettings; m_logicWidget->kcfg_LogicOutputHighImpedance->setSuffix( QString(" ") + QChar(0x3a9) ); m_logicWidget->kcfg_LogicOutputLowImpedance->setSuffix( QString(" ") + QChar(0x3a9) ); addPage( m_generalOptionsWidget, i18n("General"), "ktechlab", i18n("General Options") ); addPage( m_picProgrammerConfigWidget, i18n("Programmer"), "network-connect", i18n("PIC Programmer") ); addPage( m_asmFormattingWidget, i18n("Formatter"), "indent_asm", i18n("Assembly Formatter") ); addPage( m_logicWidget, i18n("Logic"), "logic_or", i18n("Electronic Logic Values") ); addPage( m_gpasmSettingsWidget, "Gpasm", "convert_to_hex", "gpasm" ); addPage( m_gplinkSettingsWidget, "Gplink", "merge", "gplink" ); addPage( m_sdccOptionsWidget, "SDCC", "text-x-csrc", "SDCC" ); connect( m_generalOptionsWidget->refreshRateSlider, SIGNAL(valueChanged(int)), this, SLOT(slotUpdateRefreshRateLabel(int)) ); connect( m_picProgrammerConfigWidget->kcfg_PicProgrammerProgram, SIGNAL(activated(const QString &)), this, SLOT(slotUpdatePicProgrammerDescription()) ); connect( m_picProgrammerConfigWidget->removeButton, SIGNAL(clicked()), this, SLOT(slotRemoveProgrammerConfig()) ); connect( m_picProgrammerConfigWidget->addButton, SIGNAL(clicked()), this, SLOT(slotAddProgrammerConfig()) ); connect( m_picProgrammerConfigWidget->initCommand, SIGNAL(textChanged(const QString &)), this, SLOT(slotSaveCurrentProgrammerConfig()) ); connect( m_picProgrammerConfigWidget->readCommand, SIGNAL(textChanged(const QString &)), this, SLOT(slotSaveCurrentProgrammerConfig()) ); connect( m_picProgrammerConfigWidget->writeCommand, SIGNAL(textChanged(const QString &)), this, SLOT(slotSaveCurrentProgrammerConfig()) ); connect( m_picProgrammerConfigWidget->verifyCommand, SIGNAL(textChanged(const QString &)), this, SLOT(slotSaveCurrentProgrammerConfig()) ); connect( m_picProgrammerConfigWidget->blankCheckCommand, SIGNAL(textChanged(const QString &)), this, SLOT(slotSaveCurrentProgrammerConfig()) ); connect( m_picProgrammerConfigWidget->eraseCommand, SIGNAL(textChanged(const QString &)), this, SLOT(slotSaveCurrentProgrammerConfig()) ); m_generalOptionsWidget->kcfg_GridColor->setEnabled( KTLConfig::showGrid() ); m_picProgrammerConfigWidget->kcfg_PicProgrammerPort->insertItems( m_picProgrammerConfigWidget->kcfg_PicProgrammerPort->count(), Port::ports( Port::ExistsAndRW ) ); slotUpdatePicProgrammerDescription(); } SettingsDlg::~SettingsDlg() { delete m_pPicProgrammerSettings; } void SettingsDlg::show() { KComboBox * combo = m_picProgrammerConfigWidget->kcfg_PicProgrammerProgram; combo->setEditable( true ); KConfigDialog::show(); combo->setEditable( false ); } void SettingsDlg::slotUpdateRefreshRateLabel( int sliderValue ) { const QString number = QString::number( sliderValueToRefreshRate(sliderValue) ); switch(sliderValue) { case 0: m_generalOptionsWidget->refreshRateLabel->setText( i18n("Lowest (%1 FPS)", number) ); break; case 1: m_generalOptionsWidget->refreshRateLabel->setText( i18n("Low (%1 FPS)", number) ); break; case 2: m_generalOptionsWidget->refreshRateLabel->setText( i18n("Medium (%1 FPS)", number) ); break; case 3: m_generalOptionsWidget->refreshRateLabel->setText( i18n("High (%1 FPS)", number) ); break; case 4: m_generalOptionsWidget->refreshRateLabel->setText( i18n("Highest (%1 FPS)", number) ); break; default: m_generalOptionsWidget->refreshRateLabel->setText( i18n("Unknown value") ); break; } updateButtons(); } void SettingsDlg::slotUpdatePicProgrammerDescription() { QString program = m_picProgrammerConfigWidget->kcfg_PicProgrammerProgram->currentText(); ProgrammerConfig config = m_pPicProgrammerSettings->config( program ); QString description = config.description; bool customProgrammer = ! m_pPicProgrammerSettings->isPredefined( program ); QString executable = config.executable; if ( executable.isEmpty() ) executable = program.toLower(); QString programLocation = QStandardPaths::findExecutable( executable ); if ( programLocation.isNull() ) description.prepend( i18n("%1 cannot be found.
", executable ) ); else description.prepend( i18n("%1 found: %2
", executable, programLocation) ); m_picProgrammerConfigWidget->m_pProgrammerDescription->setText( description ); m_picProgrammerConfigWidget->removeButton->setEnabled( customProgrammer ); KLineEdit * edit; #define SETUP_COMMAND( name ) \ edit = m_picProgrammerConfigWidget->name; \ edit->setText( config.name ); \ edit->setEnabled(customProgrammer); \ edit->setToolTip( customProgrammer ? QString() : config.name ) SETUP_COMMAND( initCommand ); SETUP_COMMAND( readCommand ); SETUP_COMMAND( writeCommand ); SETUP_COMMAND( verifyCommand ); SETUP_COMMAND( blankCheckCommand ); SETUP_COMMAND( eraseCommand ); #undef SETUP_COMMAND } void SettingsDlg::slotSaveCurrentProgrammerConfig() { QString program = m_picProgrammerConfigWidget->kcfg_PicProgrammerProgram->currentText(); if ( m_pPicProgrammerSettings->isPredefined( program ) ) return; ProgrammerConfig config; config.initCommand = m_picProgrammerConfigWidget->initCommand->text(); config.readCommand = m_picProgrammerConfigWidget->readCommand->text(); config.writeCommand = m_picProgrammerConfigWidget->writeCommand->text(); config.verifyCommand = m_picProgrammerConfigWidget->verifyCommand->text(); config.blankCheckCommand = m_picProgrammerConfigWidget->blankCheckCommand->text(); config.eraseCommand = m_picProgrammerConfigWidget->eraseCommand->text(); m_pPicProgrammerSettings->saveConfig( program, config ); } void SettingsDlg::slotRemoveProgrammerConfig() { KComboBox * combo = m_picProgrammerConfigWidget->kcfg_PicProgrammerProgram; QString program = combo->currentText(); KMessageBox::ButtonCode confirm = (KMessageBox::ButtonCode)KMessageBox::warningContinueCancel( this, i18n("Remove programmer configuration \"%1\"?", program), i18n("Remove \"%1\"", program) //, i18n("Remove") ); if ( confirm == KMessageBox::Cancel ) return; m_pPicProgrammerSettings->removeConfig( program ); combo->removeItem( combo->currentIndex() ); slotUpdatePicProgrammerDescription(); } void SettingsDlg::slotAddProgrammerConfig() { KComboBox * combo = m_picProgrammerConfigWidget->kcfg_PicProgrammerProgram; QStringList takenNames; int count = combo->count(); for ( int i = 0; i < count; ++i ) takenNames << combo->itemText(i).toLower(); NameValidator * nv = new NameValidator( takenNames ); bool ok = false; QString name = InputDialog::getText( i18n("Configuration Name"), i18n("Name"), QString(),/* 0,*/ &ok, this,/* 0,*/ nv ); delete nv; if (!ok) return; ProgrammerConfig config; config.executable = name.toLower(); m_pPicProgrammerSettings->saveConfig( name, config ); combo->insertItem(combo->count(), name ); // combo->setCurrentItem( count ); combo->setCurrentItem( name ); slotUpdatePicProgrammerDescription(); } int SettingsDlg::refreshRateToSliderValue( int refreshRate ) { switch (refreshRate) { case 10: return 0; case 25: return 1; case 50: return 2; case 75: return 3; case 100: return 4; default: return -1; } } int SettingsDlg::sliderValueToRefreshRate( int sliderValue ) { switch (sliderValue) { case 0: return 10; case 1: return 25; case 2: return 50; case 3: return 75; case 4: return 100; default: return -1; } } void SettingsDlg::updateSettings() { //KConfig * config = kapp->config(); KSharedConfigPtr config = KSharedConfig::openConfig(); KConfigSkeleton::ItemInt *item = dynamic_cast(KTLConfig::self()->findItem( "RefreshRate" )); if ( !item ) return; int newRefreshRate = sliderValueToRefreshRate(m_generalOptionsWidget->refreshRateSlider->value()); if ( newRefreshRate != KTLConfig::refreshRate() ) { item->setValue(newRefreshRate); KConfigGroup grWorkArea = config->group("WorkArea"); if ( newRefreshRate != defaultRefreshRate ) grWorkArea.writeEntry("RefreshRate", newRefreshRate); else grWorkArea.deleteEntry("RefreshRate"); emit settingsChanged(objectName()); } QTimer::singleShot( 0, this, SLOT(slotUpdateSettings()) ); } void SettingsDlg::slotUpdateSettings() { //KConfig * config = kapp->config(); KSharedConfigPtr config = KSharedConfig::openConfig(); KConfigSkeleton::ItemString * item = dynamic_cast(KTLConfig::self()->findItem( "PicProgrammerProgram" )); if ( !item ) return; KComboBox * combo = m_picProgrammerConfigWidget->kcfg_PicProgrammerProgram; QString newProgram = combo->currentText(); if ( newProgram != KTLConfig::picProgrammerProgram() ) { item->setValue( newProgram ); KConfigGroup grPicProg = config->group( "PicProgramming" ); if ( newProgram != "picp" ) grPicProg.writeEntry( "PicProgrammerProgram", newProgram ); else grPicProg.deleteEntry( "PicProgrammerProgram" ); emit settingsChanged(objectName()); } m_pPicProgrammerSettings->save( config.data() ); config->sync(); } void SettingsDlg::updateWidgets() { m_generalOptionsWidget->refreshRateSlider->setValue( refreshRateToSliderValue( KTLConfig::refreshRate() ) ); //m_pPicProgrammerSettings->load( kapp->config() ); m_pPicProgrammerSettings->load( KSharedConfig::openConfig().data() ); QStringList programmerNames = m_pPicProgrammerSettings->configNames( false ); KComboBox * combo = m_picProgrammerConfigWidget->kcfg_PicProgrammerProgram; combo->clear(); combo->insertItems(combo->count(), programmerNames ); //combo->setSizeLimit( programmerNames.size() ); combo->setMaxCount( programmerNames.size() ); QTimer::singleShot( 0, this, SLOT(slotUpdateWidgets()) ); } void SettingsDlg::slotUpdateWidgets() { KComboBox * combo = m_picProgrammerConfigWidget->kcfg_PicProgrammerProgram; combo->setItemText(combo->currentIndex(), KTLConfig::picProgrammerProgram() ); slotUpdatePicProgrammerDescription(); } void SettingsDlg::updateWidgetsDefault() { m_generalOptionsWidget->refreshRateSlider->setValue( refreshRateToSliderValue( defaultRefreshRate ) ); slotUpdatePicProgrammerDescription(); } bool SettingsDlg::hasChanged() { if ( sliderValueToRefreshRate( m_generalOptionsWidget->refreshRateSlider->value() ) == KTLConfig::refreshRate() ) return KConfigDialog::hasChanged(); return true; } bool SettingsDlg::isDefault() { if ( sliderValueToRefreshRate( m_generalOptionsWidget->refreshRateSlider->value() ) == defaultRefreshRate ) return KConfigDialog::isDefault(); return false; } diff --git a/src/gui/settingsdlg.h b/src/gui/settingsdlg.h index 9c67bc64..01664ea8 100644 --- a/src/gui/settingsdlg.h +++ b/src/gui/settingsdlg.h @@ -1,91 +1,91 @@ /*************************************************************************** * Copyright (C) 2005 by David Saxton * * david@bluehaze.org * * * * 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. * ***************************************************************************/ #ifndef SETTINGSDLG_H #define SETTINGSDLG_H #include -#include -#include +#include +#include class AsmFormattingWidget; class GeneralOptionsWidget; class GpasmSettingsWidget; class GplinkSettingsWidget; class LogicWidget; class PicProgrammerConfigWidget; class PicProgrammerSettings; class SDCCOptionsWidget; /** @author David Saxton */ class SettingsDlg : public KConfigDialog { Q_OBJECT public: SettingsDlg( QWidget *parent, const char *name, KCoreConfigSkeleton *config ); ~SettingsDlg() override; static int refreshRateToSliderValue( int refreshRate ); static int sliderValueToRefreshRate( int sliderValue ); virtual void show(); public slots: void slotUpdateRefreshRateLabel( int sliderValue ); void slotUpdatePicProgrammerDescription(); void slotAddProgrammerConfig(); void slotRemoveProgrammerConfig(); void slotSaveCurrentProgrammerConfig(); protected slots: void slotUpdateSettings(); void slotUpdateWidgets(); protected: void updateSettings() override; void updateWidgets() override; void updateWidgetsDefault() override; bool hasChanged() override; bool isDefault() override; PicProgrammerSettings * m_pPicProgrammerSettings; GeneralOptionsWidget * m_generalOptionsWidget; GpasmSettingsWidget * m_gpasmSettingsWidget; SDCCOptionsWidget * m_sdccOptionsWidget; AsmFormattingWidget * m_asmFormattingWidget; LogicWidget * m_logicWidget; PicProgrammerConfigWidget * m_picProgrammerConfigWidget; GplinkSettingsWidget * m_gplinkSettingsWidget; }; class NameValidator : public QValidator { public: NameValidator( QStringList unallowed ) : QValidator(nullptr) { m_unallowed = unallowed; } State validate( QString & input, int & ) const override { return (input.isEmpty() || m_unallowed.contains( input.toLower() )) ? Intermediate : Acceptable; } protected: QStringList m_unallowed; }; #endif diff --git a/src/gui/symbolviewer.cpp b/src/gui/symbolviewer.cpp index e3001a67..6936ed9e 100644 --- a/src/gui/symbolviewer.cpp +++ b/src/gui/symbolviewer.cpp @@ -1,275 +1,275 @@ /*************************************************************************** * Copyright (C) 2005 by David Saxton * * david@bluehaze.org * * * * 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 "config.h" #ifndef NO_GPSIM #include "gpsimprocessor.h" #include "symbolviewer.h" #include "katemdi.h" #include -#include #include #include #include -#include -#include -#include +#include +#include +#include +#include #include static const int NAME_COLUMN = 0; static const int VALUE_COLUMN = 1; //BEGIN class SymbolViewerItem SymbolViewerItem::SymbolViewerItem( SymbolViewer* symbolViewer, const RegisterInfo* registerInfo, int intendedColumn ) : QObject(), QTableWidgetItem() , m_pRegisterInfo(registerInfo), m_pSymbolViewer(symbolViewer) { qDebug() << Q_FUNC_INFO << " reg info name " << m_pRegisterInfo->name(); qDebug() << Q_FUNC_INFO << " row " << row() << " column " << column(); assert(registerInfo); m_pRegisterInfo = registerInfo; m_pSymbolViewer = symbolViewer; // note: at initial update the column is set to -1, so don't rely on that if (intendedColumn == NAME_COLUMN) { setText( m_pRegisterInfo->name() ); } else { // VALUE_COLUMN... setText( m_pSymbolViewer->toDisplayString( m_pRegisterInfo->value() ) ); } connect( m_pRegisterInfo, SIGNAL(valueChanged(unsigned)), this, SLOT(valueChanged(unsigned)) ); connect( m_pSymbolViewer, SIGNAL(valueRadixChanged(SymbolViewer::Radix)), this, SLOT(radixChanged()) ); } void SymbolViewerItem::valueChanged( unsigned newValue ) { if (column() == VALUE_COLUMN) { setText( m_pSymbolViewer->toDisplayString( newValue ) ); } } void SymbolViewerItem::radixChanged() { if (column() == VALUE_COLUMN) { valueChanged( m_pRegisterInfo->value() ); } } //END class SymbolViewerItem //BEGIN class SymbolView SymbolViewer * SymbolViewer::m_pSelf = nullptr; SymbolViewer * SymbolViewer::self( KateMDI::ToolView * parent ) { if (!m_pSelf) { assert (parent); m_pSelf = new SymbolViewer(parent); } return m_pSelf; } SymbolViewer::SymbolViewer( KateMDI::ToolView * parent ) : QWidget( (QWidget*)parent ) { if (parent->layout()) { parent->layout()->addWidget(this); qDebug() << Q_FUNC_INFO << " added item selector to parent's layout " << parent; } else { qWarning() << Q_FUNC_INFO << " unexpected null layout on parent " << parent ; } QGridLayout * grid = new QGridLayout( this /*, 1, 1, 0, 6 */ ); grid->setMargin(0); grid->setSpacing(6); m_pSymbolList = new QTableWidget(this); m_pSymbolList->setFocusPolicy( Qt::NoFocus ); //grid->addMultiCellWidget( m_pSymbolList, 0, 0, 0, 1 ); // 2018.12.02 grid->addWidget( m_pSymbolList, 0, 0, 1, 2); grid->addWidget( new QLabel( i18n("Value radix:"), this ), 1, 0 ); m_pRadixCombo = new KComboBox( false, this ); grid->addWidget( m_pRadixCombo, 1, 1 ); m_pRadixCombo->insertItem( m_pRadixCombo->count(), i18n("Binary") ); m_pRadixCombo->insertItem( m_pRadixCombo->count(), i18n("Octal") ); m_pRadixCombo->insertItem( m_pRadixCombo->count(), i18n("Decimal") ); m_pRadixCombo->insertItem( m_pRadixCombo->count(), i18n("Hexadecimal") ); m_valueRadix = Decimal; m_pRadixCombo->setCurrentIndex(2); connect( m_pRadixCombo, SIGNAL(activated(int)), this, SLOT(selectRadix(int)) ); m_pGpsim = nullptr; m_pCurrentContext = nullptr; m_pSymbolList->verticalHeader()-> setVisible(false); m_pSymbolList->horizontalHeader()->setVisible(true); //m_pSymbolList->addColumn( i18n("Name") ); // 2018.06.02 - use QTableWidget //m_pSymbolList->addColumn( i18n("Value") ); // 2018.06.02 - use QTableWidget m_pSymbolList->setColumnCount(2); m_pSymbolList->setHorizontalHeaderItem(0, new QTableWidgetItem(i18n("Name"))); m_pSymbolList->setHorizontalHeaderItem(1, new QTableWidgetItem(i18n("Value"))); m_pSymbolList->horizontalHeaderItem(0)->setText(i18n("Name")); m_pSymbolList->horizontalHeaderItem(1)->setText(i18n("Value")); //m_pSymbolList->setFullWidth(true); //m_pSymbolList->setColumnWidthMode(1, Q3ListView::Maximum); // 2018.06.02 - use QTableWidget m_pSymbolList->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Preferred); //m_pSymbolList->setAllColumnsShowFocus( true ); // 2018.06.02 - use QTableWidget m_pSymbolList->setEditTriggers(QAbstractItemView::NoEditTriggers); } SymbolViewer::~SymbolViewer() { } void SymbolViewer::saveProperties( KConfig * config ) { //QString oldGroup = config->group(); KConfigGroup grSym = config->group( "SymbolEditor" ); grSym.writeEntry( "Radix", (int) m_valueRadix ); //config->setGroup( oldGroup ); } void SymbolViewer::readProperties( KConfig * config ) { //QString oldGroup = config->group(); KConfigGroup grSym = config->group( "SymbolEditor" ); m_valueRadix = (SymbolViewer::Radix)grSym.readEntry( "Radix", (int) Decimal ); int pos = 4; switch ( m_valueRadix ) { case Binary: pos--; case Octal: pos--; case Decimal: pos--; case Hexadecimal: pos--; } m_pRadixCombo->setCurrentIndex( pos ); //config->setGroup( oldGroup ); } void SymbolViewer::setContext( GpsimProcessor * gpsim ) { RegisterSet * registerSet = gpsim ? gpsim->registerMemory() : nullptr; if ( registerSet == m_pCurrentContext ) return; m_pSymbolList->clear(); m_pSymbolList->setColumnCount(2); m_pSymbolList->setRowCount(0); m_pSymbolList->setHorizontalHeaderItem(0, new QTableWidgetItem(i18n("Name"))); m_pSymbolList->setHorizontalHeaderItem(1, new QTableWidgetItem(i18n("Value"))); m_pSymbolList->horizontalHeaderItem(0)->setText(i18n("Name")); m_pSymbolList->horizontalHeaderItem(1)->setText(i18n("Value")); m_pGpsim = gpsim; m_pCurrentContext = registerSet; if (!m_pCurrentContext) return; connect( gpsim, SIGNAL(destroyed()), m_pSymbolList, SLOT(clearContents()) ); unsigned count = m_pCurrentContext->size(); for ( unsigned i = 0; i < count; ++i ) { RegisterInfo * reg = m_pCurrentContext->fromAddress(i); if (!reg) { qDebug() << " skip null register at " << i; continue; } if ( (reg->type() == RegisterInfo::Generic) || (reg->type() == RegisterInfo::Invalid) ) { continue; } qDebug() << Q_FUNC_INFO << " add reg at " << i << " info " << reg; m_pSymbolList->insertRow(i); SymbolViewerItem *itemName = new SymbolViewerItem( this, reg, 0 ); m_pSymbolList->setItem(i, 0, itemName); SymbolViewerItem *itemVal = new SymbolViewerItem( this, reg, 1 ); m_pSymbolList->setItem(i, 1, itemVal); } } void SymbolViewer::selectRadix( int selectIndex ) { if ( (selectIndex<0) || (selectIndex>3) ) { qWarning() << Q_FUNC_INFO << "Invalid select position for radix: " << selectIndex << endl; return; } Radix radii[] = { Binary, Octal, Decimal, Hexadecimal }; Radix newRadix = radii[selectIndex]; if ( newRadix == m_valueRadix ) return; m_valueRadix = newRadix; emit valueRadixChanged(m_valueRadix); } QString SymbolViewer::toDisplayString( unsigned value ) const { switch ( m_valueRadix ) { case Binary: return QString::number( value, 2 ).rightJustified( 8, '0', false ); case Octal: return "0" + QString::number( value, 8 ); case Decimal: return QString::number( value, 10 ); case Hexadecimal: return "0x" + QString::number( value, 16 ); } return "?"; } //END class SymbolView #endif diff --git a/src/gui/symbolviewer.h b/src/gui/symbolviewer.h index 376b6008..fcf3ea88 100644 --- a/src/gui/symbolviewer.h +++ b/src/gui/symbolviewer.h @@ -1,105 +1,105 @@ /*************************************************************************** * Copyright (C) 2005 by David Saxton * * david@bluehaze.org * * * * 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 "config.h" #ifndef NO_GPSIM #ifndef SYMBOLVIEWER_H #define SYMBOLVIEWER_H -#include -#include +#include +#include #include class KComboBox; class KConfig; class SymbolViewer; namespace KateMDI { class ToolView; } /** @author David Saxton */ class SymbolViewer : public QWidget { Q_OBJECT public: static SymbolViewer * self( KateMDI::ToolView * parent = nullptr ); static QString toolViewIdentifier() { return "SymbolViewer"; } ~SymbolViewer() override; enum Radix { Binary = 2, Octal = 8, Decimal = 10, Hexadecimal = 16 }; Radix valueRadix() const { return m_valueRadix; } //QTableWidget * symbolList() const { return m_pSymbolList; } // 2016.06.02 - unused /** * Write the current properties (such as currently selected radix) to * the config. */ void saveProperties( KConfig * config ); /** * Reads the properties (such as the last selected radix) from the * config file. */ void readProperties( KConfig * config ); void setContext( GpsimProcessor * gpsim ); /** * Converts the value to a string for display according to the currently * selected radix. */ QString toDisplayString( unsigned value ) const; signals: void valueRadixChanged( SymbolViewer::Radix newRadix ); public slots: void selectRadix( int selectIndex ); protected: QPointer m_pGpsim; RegisterSet * m_pCurrentContext; QTableWidget * m_pSymbolList; Radix m_valueRadix; private: SymbolViewer( KateMDI::ToolView * parent ); static SymbolViewer * m_pSelf; KComboBox * m_pRadixCombo; }; class SymbolViewerItem : public QObject, public QTableWidgetItem { Q_OBJECT public: SymbolViewerItem( SymbolViewer* symbolViewer, const RegisterInfo* registerInfo, int intendedColumn); public slots: void valueChanged( unsigned newValue ); void radixChanged(); protected: const RegisterInfo * m_pRegisterInfo; SymbolViewer * m_pSymbolViewer; }; #endif #endif diff --git a/src/icndocument.cpp b/src/icndocument.cpp index 809d8923..00420500 100644 --- a/src/icndocument.cpp +++ b/src/icndocument.cpp @@ -1,894 +1,894 @@ /*************************************************************************** * Copyright (C) 2005 by David Saxton * * david@bluehaze.org * * * * 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 "canvasmanipulator.h" #include "component.h" #include "connector.h" #include "conrouter.h" #include "cnitemgroup.h" #include "ecnode.h" #include "junctionnode.h" #include "flowcontainer.h" #include "fpnode.h" #include "junctionflownode.h" #include "icndocument.h" #include "icnview.h" #include "itemdocumentdata.h" #include "itemlibrary.h" #include "ktechlab.h" #include "nodegroup.h" #include "outputflownode.h" #include "utils.h" -#include -#include -#include +#include +#include +#include #include //BEGIN class ICNDocument ICNDocument::ICNDocument( const QString &caption, const char *name ) : ItemDocument( caption, name ), m_cells(nullptr) { m_canvas->retune(48); m_selectList = new CNItemGroup(this); createCellMap(); m_cmManager->addManipulatorInfo( CMItemMove::manipulatorInfo() ); m_cmManager->addManipulatorInfo( CMAutoConnector::manipulatorInfo() ); m_cmManager->addManipulatorInfo( CMManualConnector::manipulatorInfo() ); } ICNDocument::~ICNDocument() { m_bDeleted = true; GuardedNodeGroupList ngToDelete = m_nodeGroupList; m_nodeGroupList.clear(); const GuardedNodeGroupList::iterator nglEnd = ngToDelete.end(); for ( GuardedNodeGroupList::iterator it = ngToDelete.begin(); it != nglEnd; ++it ) delete (NodeGroup *)(*it); delete m_cells; delete m_selectList; } View *ICNDocument::createView( ViewContainer *viewContainer, uint viewAreaId, const char *name ) { ICNView *icnView = new ICNView( this, viewContainer, viewAreaId, name ); handleNewView(icnView); return icnView; } ItemGroup* ICNDocument::selectList() const { return m_selectList; } void ICNDocument::fillContextMenu( const QPoint &pos ) { ItemDocument::fillContextMenu(pos); slotInitItemActions(); } CNItem* ICNDocument::cnItemWithID( const QString &id ) { return dynamic_cast(itemWithID(id)); } Connector *ICNDocument::connectorWithID( const QString &id ) { const ConnectorList::iterator end = m_connectorList.end(); for( ConnectorList::iterator it = m_connectorList.begin(); it != end; ++it ) { if( (*it)->id() == id ) return *it; } return nullptr; } FlowContainer *ICNDocument::flowContainer( const QPoint &pos ) { KtlQCanvasItemList collisions = m_canvas->collisions(pos); FlowContainer *flowContainer = nullptr; int currentLevel = -1; const KtlQCanvasItemList::iterator end = collisions.end(); for ( KtlQCanvasItemList::iterator it = collisions.begin(); it != end; ++it ) { if ( FlowContainer *container = dynamic_cast(*it) ) { if ( container->level() > currentLevel && !m_selectList->contains(container) ) { currentLevel = container->level(); flowContainer = container; } } } return flowContainer; } bool ICNDocument::canConnect( KtlQCanvasItem *qcanvasItem1, KtlQCanvasItem *qcanvasItem2 ) const { // Rough outline of what can and can't connect: // * At most three connectors to a node // * Can't have connectors going between different levels (e.g. can't have // a connector coming outside a FlowContainer from inside). // * Can't have more than one route between any two nodes // * In all connections between nodes, must have at least one input and one // output node at the ends. Node *startNode = dynamic_cast(qcanvasItem1); Node *endNode = dynamic_cast(qcanvasItem2); if ( (startNode && startNode->numCon( true, false ) > 2) || (endNode && endNode->numCon( true, false ) > 2) ) return false; Connector *startConnector = dynamic_cast(qcanvasItem1); Connector *endConnector = dynamic_cast(qcanvasItem2); // FIXME: overload this instead of calling type(). // Can't have T- or I- junction in PinMapEditor document if ( type() == Document::dt_pinMapEditor && (startConnector || endConnector) ) return false; // Can't have I-junction in flowcode document if ( type() == Document::dt_flowcode && startConnector && endConnector ) return false; //BEGIN Change connectors to nodes Node * startNode1 = nullptr; Node * startNode2 = nullptr; if (startConnector) { startNode1 = startConnector->startNode(); startNode2 = startConnector->endNode(); if ( !startNode1 || !startNode2 ) return false; } else if (!startNode) return false; Node * endNode1 = nullptr; Node * endNode2 = nullptr; if (endConnector) { endNode1 = endConnector->startNode(); endNode2 = endConnector->endNode(); if ( !endNode1 || !endNode2 ) return false; } else if ( !endNode ) return false; Node * start[3]; start[0] = startNode; start[1] = startNode1; start[2] = startNode2; Node * end[3]; end[0] = endNode; end[1] = endNode1; end[2] = endNode2; //END Change connectors to nodes //BEGIN Check nodes aren't already connected for ( unsigned i = 0; i < 3; i++ ) { for ( unsigned j = 0; j < 3; j++ ) { if ( start[i] && end[j] && start[i]->isConnected(end[j]) ) return false; } } //END Check nodes aren't already connected together //BEGIN Simple level check for ( unsigned i = 0; i < 3; i++ ) { for ( unsigned j = 0; j < 3; j++ ) { if ( start[i] && end[j] && start[i]->level() != end[j]->level() ) return false; } } //END Simple level check //BEGIN Advanced level check CNItem *startParentItem[3]; for(unsigned i = 0; i < 3; i++ ) startParentItem[i] = start[i] ? start[i]->parentItem() : nullptr; CNItem * endParentItem[3]; for(unsigned i = 0; i < 3; i++ ) endParentItem[i] = end[i] ? end[i]->parentItem() : nullptr; Item *container[6] = {nullptr}; for ( unsigned i = 0; i < 3; i++ ) { if (startParentItem[i]) { int dl = start[i]->level() - startParentItem[i]->level(); if ( dl == 0 ) container[i] = startParentItem[i]->parentItem(); else if ( dl == 1 ) container[i] = startParentItem[i]; else qCritical() << Q_FUNC_INFO << " start, i="<(qcanvasItem) ) { /* m_nodeList[ node->id() ] = node; emit nodeAdded(node); */ qCritical() << Q_FUNC_INFO << "BUG: this member should have been overridden!" << endl; } else if ( Connector *connector = dynamic_cast(qcanvasItem) ) { m_connectorList.append(connector); emit connectorAdded(connector); } else { qCritical() << Q_FUNC_INFO << "Unrecognised item"<isEmpty()) return; ItemDocumentData data( type() ); // We only want to copy the connectors who have all ends attached to something in the selection ConnectorList connectorList = m_selectList->connectors(false); typedef QMap< Node*, ConnectorList > NCLMap; NCLMap nclMap; ConnectorList::iterator end = connectorList.end(); for ( ConnectorList::iterator it = connectorList.begin(); it != end; ++it ) { Node *startNode = (*it)->startNode(); if ( startNode && !startNode->isChildNode() ) nclMap[startNode].append(*it); Node *endNode = (*it)->endNode(); if ( endNode && !endNode->isChildNode() ) nclMap[endNode].append(*it); } NodeList nodeList; // Remove those connectors (and nodes) which are dangling on an orphan node NCLMap::iterator nclEnd = nclMap.end(); for ( NCLMap::iterator it = nclMap.begin(); it != nclEnd; ++it ) { if ( it.value().size() > 1 ) nodeList.append(it.key()); else if ( it.value().size() > 0 ) connectorList.removeAll( it.value().at(0) ); } data.addItems( m_selectList->items(false) ); data.addNodes( nodeList ); data.addConnectors( connectorList ); QApplication::clipboard()->setText( data.toXML(), QClipboard::Clipboard ); } void ICNDocument::selectAll() { selectAllNodes(); const ItemMap::iterator itemEnd = m_itemList.end(); for ( ItemMap::iterator itemIt = m_itemList.begin(); itemIt != itemEnd; ++itemIt ) { if (*itemIt) select(*itemIt); } const ConnectorList::iterator conEnd = m_connectorList.end(); for ( ConnectorList::iterator connectorIt = m_connectorList.begin(); connectorIt != conEnd; ++connectorIt ) { if (*connectorIt) select(*connectorIt); } } Item* ICNDocument::addItem( const QString &id, const QPoint &p, bool newItem ) { if ( !isValidItem(id) ) return nullptr; // First, we need to tell all containers to go to full bounding so that // we can detect a "collision" with them const ItemMap::iterator end = m_itemList.end(); for ( ItemMap::iterator it = m_itemList.begin(); it != end; ++it ) { if ( FlowContainer *flowContainer = dynamic_cast(*it) ) flowContainer->setFullBounds(true); } KtlQCanvasItemList preCollisions = canvas()->collisions(p); for ( ItemMap::iterator it = m_itemList.begin(); it != end; ++it ) { if ( FlowContainer *flowContainer = dynamic_cast(*it) ) flowContainer->setFullBounds(false); } Item *item = itemLibrary()->createItem( id, this, newItem ); if (!item) return nullptr; // Look through the CNItems at the given point (sorted by z-coordinate) for // a container item. FlowContainer *container = nullptr; const KtlQCanvasItemList::iterator pcEnd = preCollisions.end(); for ( KtlQCanvasItemList::iterator it = preCollisions.begin(); it != pcEnd && !container; ++it ) { if ( FlowContainer *flowContainer = dynamic_cast(*it) ) container = flowContainer; } // We want to check it is not a special item first as // isValidItem may prompt the user about his bad choice if ( !isValidItem(item) ) { item->removeItem(); flushDeleteList(); return nullptr; } int x = int(p.x()); int y = int(p.y()); if( x < 16 || x > (canvas()->width( )) ) x = 16; if( y < 16 || y > (canvas()->height()) ) y = 16; if ( CNItem *cnItem = dynamic_cast(item) ) { cnItem->move( snapToCanvas(p.x()), snapToCanvas(p.y()) ); if (container) container->addChild(cnItem); } else item->move( x, y ); item->show(); requestStateSave(); return item; } void ICNDocument::addAllItemConnectorPoints() { // FIXME The next line crashes sometimes??! const ItemMap::iterator ciEnd = m_itemList.end(); for ( ItemMap::iterator it = m_itemList.begin(); it != ciEnd; ++it ) { if ( CNItem *cnItem = dynamic_cast(*it) ) cnItem->updateConnectorPoints(true); } } void ICNDocument::requestRerouteInvalidatedConnectors() { requestEvent( ItemDocumentEvent::RerouteInvalidatedConnectors ); } void ICNDocument::rerouteInvalidatedConnectors() { //qApp->processEvents(QEventLoop::AllEvents, 300); // 2015.07.07 - do not process events, if it is not urgently needed; might generate crashes? // We only ever need to add the connector points for CNItem's when we're about to reroute... addAllItemConnectorPoints(); // List of connectors which are to be determined to need rerouting (and whose routes aren't controlled by NodeGroups) ConnectorList connectorRerouteList; // For those connectors that are controlled by node groups NodeGroupList nodeGroupRerouteList; const ConnectorList::iterator connectorListEnd = m_connectorList.end(); for ( ConnectorList::iterator it = m_connectorList.begin(); it != connectorListEnd; ++it ) { Connector *connector = *it; if ( connector && connector->isVisible() && connector->startNode() && connector->endNode() ) { // Perform a series of tests to see if the connector needs rerouting bool needsRerouting = false; // Test to see if we actually have any points const QPointList pointList = connector->connectorPoints(); if( pointList.isEmpty() ) needsRerouting = true; // Test to see if the route doesn't match up with the node positions at either end if (!needsRerouting) { const QPoint listStart = pointList.first(); const QPoint listEnd = pointList.last(); const QPoint nodeStart = QPoint( int(connector->startNode()->x()), int(connector->startNode()->y()) ); const QPoint nodeEnd = QPoint( int(connector->endNode()->x()), int(connector->endNode()->y()) ); if ( ((listStart != nodeStart) || (listEnd != nodeEnd )) && ((listStart != nodeEnd ) || (listEnd != nodeStart)) ) { needsRerouting = true; } } // Test to see if the route intersects any Items (we ignore if it is a manual route) if ( !needsRerouting && !connector->usesManualPoints() ) { const KtlQCanvasItemList collisions = connector->collisions(true); const KtlQCanvasItemList::const_iterator collisionsEnd = collisions.end(); for ( KtlQCanvasItemList::const_iterator collisionsIt = collisions.begin(); (collisionsIt != collisionsEnd) && !needsRerouting; ++collisionsIt ) { if ( dynamic_cast(*collisionsIt) ) needsRerouting = true; } } if (needsRerouting) { NodeGroup *nodeGroup = connector->nodeGroup(); if ( !nodeGroup && !connectorRerouteList.contains(connector) ) connectorRerouteList.append(connector); else if ( nodeGroup && !nodeGroupRerouteList.contains(nodeGroup) ) nodeGroupRerouteList.append(nodeGroup); } } } // To allow proper rerouting, we want to start with clean routes for all of the invalidated connectors const NodeGroupList::iterator nodeGroupRerouteEnd = nodeGroupRerouteList.end(); for ( NodeGroupList::iterator it = nodeGroupRerouteList.begin(); it != nodeGroupRerouteEnd; ++it ) { const ConnectorList contained = (*it)->connectorList(); const ConnectorList::const_iterator end = contained.end(); for ( ConnectorList::const_iterator it = contained.begin(); it != end; ++it ) (*it)->updateConnectorPoints(false); } const ConnectorList::iterator connectorRerouteEnd = connectorRerouteList.end(); for ( ConnectorList::iterator it = connectorRerouteList.begin(); it != connectorRerouteEnd; ++it ) (*it)->updateConnectorPoints(false); // And finally, reroute the connectors for ( NodeGroupList::iterator it = nodeGroupRerouteList.begin(); it != nodeGroupRerouteEnd; ++it ) (*it)->updateRoutes(); for ( ConnectorList::iterator it = connectorRerouteList.begin(); it != connectorRerouteEnd; ++it ) (*it)->rerouteConnector(); for ( ConnectorList::iterator it = m_connectorList.begin(); it != connectorListEnd; ++it ) { if (*it) (*it)->updateDrawList(); } } void ICNDocument::deleteSelection() { // End whatever editing mode we are in, as we don't want to start editing // something that is about to no longer exist... m_cmManager->cancelCurrentManipulation(); if ( m_selectList->isEmpty() ) return; m_selectList->deleteAllItems(); flushDeleteList(); setModified(true); // We need to emit this so that property widgets etc... // can clear themselves. emit selectionChanged(); requestRerouteInvalidatedConnectors(); requestStateSave(); } ConnectorList ICNDocument::getCommonConnectors( const ItemList &list ) { NodeList nodeList = getCommonNodes(list); // Now, get all the connectors, and remove the ones that don't have both end // nodes in the above generated list ConnectorList connectorList = m_connectorList; const ConnectorList::iterator connectorListEnd = connectorList.end(); for ( ConnectorList::iterator it = connectorList.begin(); it != connectorListEnd; ++it ) { Connector *con = *it; if ( !con || !nodeList.contains(con->startNode()) || !nodeList.contains(con->endNode()) ) { *it = nullptr; } } connectorList.removeAll((Connector*)nullptr); return connectorList; } NodeList ICNDocument::getCommonNodes( const ItemList &list ) { NodeList nodeList; const ItemList::const_iterator listEnd = list.end(); for(ItemList::const_iterator it = list.begin(); it != listEnd; ++it ) { NodeInfoMap nodeMap; CNItem *cnItem = dynamic_cast((Item*)*it); if(cnItem) nodeMap = cnItem->nodeMap(); const NodeInfoMap::iterator nodeMapEnd = nodeMap.end(); for(NodeInfoMap::iterator it = nodeMap.begin(); it != nodeMapEnd; ++it ) { Node *node = it.value().node; if(!nodeList.contains(node) ) nodeList += node; NodeGroup *ng = node->nodeGroup(); if(ng) { NodeList intNodeList = ng->internalNodeList(); const NodeList::iterator intNodeListEnd = intNodeList.end(); for ( NodeList::iterator it = intNodeList.begin(); it != intNodeListEnd; ++it ) { Node *intNode = *it; if(!nodeList.contains(intNode) ) nodeList += intNode; } } } } return nodeList; } void ICNDocument::unregisterUID( const QString & uid ) { ItemDocument::unregisterUID( uid ); } //END class ICNDocument DirCursor *DirCursor::m_self = nullptr; DirCursor::DirCursor() { initCursors(); } DirCursor::~DirCursor() { } DirCursor *DirCursor::self() { if (!m_self) m_self = new DirCursor; return m_self; } void DirCursor::initCursors() { // QCursor c(Qt::ArrowCursor); // QBitmap bitmap = *c.bitmap(); // QBitmap mask = *c.mask(); // QPixmap pm( bitmap->width(), bitmap->height() ); // pm.setMask(mask); // pm = c.pi } diff --git a/src/icndocument.h b/src/icndocument.h index 6087de36..705df668 100644 --- a/src/icndocument.h +++ b/src/icndocument.h @@ -1,334 +1,334 @@ /*************************************************************************** * Copyright (C) 2005 by David Saxton * * david@bluehaze.org * * * * 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. * ***************************************************************************/ #ifndef ICNDOCUMENT_H #define ICNDOCUMENT_H #include "itemdocument.h" -#include +#include class Cells; class CNItem; class CNItemGroup; class Connector; class ECNode; class FlowContainer; class Node; class NodeGroup; typedef QMap< QString, Node* > NodeMap; typedef QList > ConnectorList; typedef QList > NodeList; typedef QList NodeGroupList; typedef QList > GuardedNodeGroupList; /** @author David Saxton */ class ICNDocument : public ItemDocument { Q_OBJECT public: ICNDocument( const QString &caption, const char *name ); ~ICNDocument() override; enum hit_score { hs_none = 0, hs_connector = 4, hs_item = 1000 }; View *createView( ViewContainer *viewContainer, uint viewAreaId, const char *name = nullptr ) override; /** * Will attempt to create an item with the given id at position p. Some item * (such as PIC/START) have restrictions, and can only have one instance of * themselves on the canvas, and adds the operation to the undo list */ Item* addItem( const QString &id, const QPoint &p, bool newItem ) override; /** * short for casting whatever itemWithID(id) returns */ CNItem* cnItemWithID( const QString &id ); /** * Returns a pointer to a node on the canvas with the given id, * or nullptr if no such node exists */ virtual Node* nodeWithID( const QString &id ) = 0; /** * Returns a pointer to a Connector on the canvas with the given id, * or nullptr if no such Connector exists */ Connector* connectorWithID( const QString &id ); /** * Adds a KtlQCanvasItem to the delete list to be deleted, * when flushDeleteList() is called */ void appendDeleteList( KtlQCanvasItem *qcanvasItem ) override; /** * Permantly deletes all items that have been added to the delete list with * the appendDeleteList( KtlQCanvasItem *qcanvasItem ) function. */ void flushDeleteList() override = 0; /** * Reinherit this function to perform special checks on whether the two * given QCanvasItems (either nodes or connectors or both) can be * connected together. */ virtual bool canConnect( KtlQCanvasItem *qcanvasItem1, KtlQCanvasItem *qcanvasItem2 ) const; /** * copies the selected items to the clipboard, in an XML text form */ void copy() override; /** * selects everything in the current document */ void selectAll() override; /** * registers (adds to the document) an item (a connector or a node) * @param qcanvasItem the item to be registered * @return true if succeeded, false if it didn't */ bool registerItem( KtlQCanvasItem *qcanvasItem ) override; /** * Returns a pointer to the 2-dimension array of ICNDocument cells. */ Cells *cells() const { return m_cells; } /** * Adds score to the cells at the given cell referece */ void addCPenalty( int x, int y, int score ); /** * If there are two connectors joined to a node, then they can be merged * into one connector. The node will not be removed. * @param node The node between the two connectors * @param noCreate If true, no new connectors will be created * @returns true if it was successful in merging the connectors */ // bool joinConnectors( Node *node ); /** * Snaps a coordinate in the document to the grid * @param pos The coordinate * @return The snapped to grid coordinate */ static int gridSnap( int pos ); /// Returns 'pos' when snapped to grid /** * Snaps a point to the grid * @param pos The point * @return The adjusted coordinate */ static QPoint gridSnap( const QPoint &pos ); /** * Returns true if the CNItem is valid - e.g. will return true for a * component in a circuit, but not in a pic program */ bool isValidItem( Item *item ) override = 0; bool isValidItem( const QString &itemId ) override = 0; // TODO to document virtual ConnectorList getCommonConnectors( const ItemList &list ); virtual NodeList getCommonNodes( const ItemList &list ); /** * returns all the nodes contained by the document. Note that this function is inefficient, * so don't use it in loops * @return all the nodes contained by the document */ virtual NodeList nodeList() const = 0; /** * @return all the connectors from the document */ const ConnectorList & connectorList() const { return m_connectorList; } /** * @return all the nodegroups from the document */ const GuardedNodeGroupList & nodeGroupList() const { return m_nodeGroupList; } /** * @return the selected items from the document */ ItemGroup *selectList() const override; /** * Creates a connector between two nodes, and returns a pointer to it * and adds the operation to the undo list */ virtual Connector* createConnector( const QString &startNodeId, const QString &endNodeId, QPointList *pointList = nullptr ) = 0; /** * Creates a connector from node1 to node2. If pointList is non-null, then the * connector will be assigned those points */ //virtual Connector *createConnector( Node *node1, Node *node2, QPointList *pointList = nullptr); /** * Splits Connector con into two connectors at point pos2, and creates a connector from the node * to the intersection of the two new connectors. If pointList is non-null, then the new connector * from the node will be assigned those points */ virtual Connector * createConnector( Node *node, Connector *con, const QPoint &pos2, QPointList *pointList = nullptr ) = 0; /** * Splits con1 and con2 into two new connectors each at points pos1 and pos2, and creates a new connector * between the two points of intersection given by pos1 and pos2. If pointList is non-null, then the new * connector between the two points will be assigned those points */ virtual Connector * createConnector( Connector *con1, Connector *con2, const QPoint &pos1, const QPoint &pos2, QPointList *pointList = nullptr ) = 0; /** * Returns the flowcontainer at the given position at the highest level that * is not in the current select list, or nullptr if there isn't one */ FlowContainer *flowContainer( const QPoint &pos ); /** * Sets the drag (e.g. horizontal arrow) cursor for resizing a CNItem, depending on the corner clicked on */ void setItemResizeCursor( int cornerType ); void getTranslatable( const ItemList & itemList, ConnectorList * fixedConnectors = nullptr, ConnectorList * translatableConnectors = nullptr, NodeGroupList * translatableNodeGroups = nullptr ); /** * Reroutes invalidated directors. You shouldn't call this function * directly - instead use ItemDocument::requestEvent. */ void rerouteInvalidatedConnectors(); /** * Assigns the orphan nodes into NodeGroups. You shouldn't call this * function directly - instead use ItemDocument::requestEvent. */ virtual void slotAssignNodeGroups(); void unregisterUID( const QString & uid ) override; public slots: /** * Deletes all items in the selected item list, along with associated * connectors, etc, and adds the operation to the undo list */ void deleteSelection() override; /** * This function looks at all the connectors and the nodes, determines * which ones need rerouting, and then reroutes them */ void requestRerouteInvalidatedConnectors(); /** * Remaps the 2-dimension array of ICNDocument cells, and the various * hitscores / etc associated with them. This is used for connector * routing, and should be called after e.g. items have been moved */ void createCellMap(); /** * Call this to request NodeGroup reassignment. */ void slotRequestAssignNG(); signals: /** * Emitted when a Connector is added */ void connectorAdded( Connector *connector ); /** * Emitted when a Node is added */ void nodeAdded( Node *node ); protected: /** * Adds all connector points from the items (used in connector routing). * This only needs to be called when connector(s) need routing. */ void addAllItemConnectorPoints(); void fillContextMenu( const QPoint &pos ) override; /** * Creates a new NodeGroup to control the node, if there does not already * exist a NodeGroup containing the given node. The associated nodes will * also be added to the NodeGroup. * @returns a pointer to the NodeGroup if one was created, or a pointer to the existing one containing that node */ NodeGroup* createNodeGroup( Node *node ); /** * Finds (and deletes if found) the NodeGroup containing the given node. * @returns true if the NodeGroup was found and deleted */ bool deleteNodeGroup( Node *node ); friend class CanvasEditor; /** * deletes all the elements containde in the nodeList. Should be overridden. */ virtual void deleteAllNodes() = 0; /** * Selects all nodes on the document. Should be overridden. */ virtual void selectAllNodes() = 0; // this should be overridden in {Flow|Circuit}ICNDocument ConnectorList m_connectorList; CNItemGroup *m_selectList; // Selected objects // OVERLOADED KtlQCanvasItemList m_itemDeleteList; // List of canvas items to be deleted private: Cells *m_cells; GuardedNodeGroupList m_nodeGroupList; }; /** @author David Saxton */ class DirCursor { public: static DirCursor* self(); ~DirCursor(); static QPixmap leftArrow() { return self()->m_leftArrow; } static QPixmap rightArrow() { return self()->m_rightArrow; } static QPixmap upArrow() { return self()->m_upArrow; } static QPixmap downArrow() { return self()->m_downArrow; } protected: DirCursor(); void initCursors(); static DirCursor *m_self; QPixmap m_leftArrow; QPixmap m_rightArrow; QPixmap m_upArrow; QPixmap m_downArrow; }; #endif diff --git a/src/icnview.cpp b/src/icnview.cpp index 7cd69ee5..f063ddcb 100644 --- a/src/icnview.cpp +++ b/src/icnview.cpp @@ -1,140 +1,140 @@ /*************************************************************************** * Copyright (C) 2005 by David Saxton * * david@bluehaze.org * * * * 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 "canvasmanipulator.h" #include "icndocument.h" #include "icnview.h" #include "ktechlab.h" #include #include #include -#include -#include -#include +#include +#include +#include ICNView::ICNView( ICNDocument *icnDocument, ViewContainer *viewContainer, uint viewAreaId, const char *name ) : ItemView( icnDocument, viewContainer, viewAreaId, name ) { bool manualRouting = (icnDocument->m_cmManager->cmState() & CMManager::cms_manual_route); KActionCollection * ac = actionCollection(); //BEGIN Routing Actions // These actions get inserted into the main menu //m_pAutoRoutingAction = new QAction( i18n("Automatic"), "", 0, this, SLOT(slotSetRoutingAuto()), ac, "routing_mode_auto" ); m_pAutoRoutingAction = new QAction( i18n("Automatic"), ac); m_pAutoRoutingAction->setObjectName("routing_mode_auto"); connect(m_pAutoRoutingAction, SIGNAL(triggered(bool)), this, SLOT(slotSetRoutingAuto())); ac->addAction(m_pAutoRoutingAction->objectName(), m_pAutoRoutingAction); //m_pAutoRoutingAction->setExclusiveGroup("routing_mode");// TODO TEST if ( !manualRouting ) m_pAutoRoutingAction->setChecked( true ); //m_pManualRoutingAction = new QAction( i18n("Manual"), "", 0, this, SLOT(slotSetRoutingManual()), ac, "routing_mode_manual" ); m_pManualRoutingAction = new QAction( i18n("Manual"), ac); m_pManualRoutingAction->setObjectName("routing_mode_manual"); connect(m_pManualRoutingAction, SIGNAL(triggered(bool)), this, SLOT(slotSetRoutingManual())); ac->addAction(m_pManualRoutingAction->objectName(), m_pManualRoutingAction); //m_pManualRoutingAction->setExclusiveGroup("routing_mode"); // TODO TEST if ( manualRouting ) m_pManualRoutingAction->setChecked( true ); // This popup gets inserted into the toolbar //m_pRoutingModeToolbarPopup = new KToolBarPopupAction( i18n("Connection Routing Mode"), "pencil", 0, 0, 0, ac, "routing_mode" ); m_pRoutingModeToolbarPopup = new KToolBarPopupAction( QIcon(QString::fromLatin1("pencil")), i18n("Connection Routing Mode"), ac); m_pRoutingModeToolbarPopup->setObjectName( "routing_mode" ); m_pRoutingModeToolbarPopup->setDelayed(false); ac->addAction(m_pRoutingModeToolbarPopup->objectName(), m_pRoutingModeToolbarPopup); QMenu * m = m_pRoutingModeToolbarPopup->menu(); m->setTitle( i18n("Connection Routing Mode") ); m_actMenuRouteAutoRoute = m->addAction( /*QIconLoader::global()->loadIcon( "routing_mode_auto", QIconLoader::Small ), */i18n("Automatic")); m_actMenuRouteAutoRoute->setData( 0 ); m_actMenuRouteManRoute = m->addAction( /*QIconLoader::global()->loadIcon( "routing_mode_manual", QIconLoader::Small ),*/ i18n("Manual")); m_actMenuRouteManRoute->setData( 1 ); //m->setCheckable(true); // 2018.12.02 if (manualRouting) { m_actMenuRouteManRoute->setChecked( true ); } else { m_actMenuRouteAutoRoute->setChecked( true ); } //m->setItemChecked( manualRouting ? 1 : 0, true ); // 2018.12.02 connect( m, SIGNAL(triggered(QAction*)), this, SLOT(slotSetRoutingMode(QAction*)) ); //END Routing Actions connect( icnDocument->m_cmManager, SIGNAL(manualRoutingChanged(bool )), this, SLOT(slotUpdateRoutingToggles(bool )) ); } ICNView::~ICNView() { } void ICNView::slotSetRoutingMode( QAction *action ) { int mode = action->data().toInt(); // This function is called when the user selects a mode from the toolbar drop-down menu bool manualEnabled = (mode == 1); if ( bool(p_itemDocument->m_cmManager->cmState() & CMManager::cms_manual_route) == manualEnabled ) return; slotUpdateRoutingMode( manualEnabled ); slotUpdateRoutingToggles( manualEnabled ); } void ICNView::slotSetRoutingManual() { slotUpdateRoutingMode( true ); slotUpdateRoutingToggles( true ); } void ICNView::slotSetRoutingAuto() { slotUpdateRoutingMode( false ); slotUpdateRoutingToggles( false ); } void ICNView::slotUpdateRoutingMode( bool manualRouting ) { p_itemDocument->m_cmManager->slotSetManualRoute( manualRouting ); p_itemDocument->canvas()->setMessage( manualRouting ? i18n("Manual connection routing enabled.") : i18n("Automatic connection routing enabled.") ); } void ICNView::slotUpdateRoutingToggles( bool manualRouting ) { //m_pRoutingModeToolbarPopup->menu()->setItemChecked( !manualRouting, 0 ); // 2018.12.02 //m_pRoutingModeToolbarPopup->menu()->setItemChecked( manualRouting, 1 ); if ( manualRouting ) { m_actMenuRouteAutoRoute->setChecked(false); m_actMenuRouteManRoute->setChecked(true); } else { m_actMenuRouteAutoRoute->setChecked(true); m_actMenuRouteManRoute->setChecked(false); } if ( manualRouting ) m_pManualRoutingAction->setChecked(true); else m_pAutoRoutingAction->setChecked(true); } diff --git a/src/item.cpp b/src/item.cpp index 8ee91ce7..a5e1ae4d 100644 --- a/src/item.cpp +++ b/src/item.cpp @@ -1,664 +1,664 @@ /*************************************************************************** * Copyright (C) 2004-2006 by David Saxton * * david@bluehaze.org * * * * 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 "itemdocument.h" #include "itemdocumentdata.h" #include "ktechlab.h" #include "richtexteditor.h" #include -#include #include #include -#include -#include +#include +#include +#include #include #include #include #include #include const int minPrefixExp = -24; const int maxPrefixExp = 24; const int numPrefix = int((maxPrefixExp-minPrefixExp)/3)+1; const QString SIprefix[] = {"y","z","a","f","p","n",QChar(0xB5),"m","","k","M","G","T","P","E","Z","Y"}; Item::Item( ItemDocument *itemDocument, bool newItem, const QString &id ) : //QObject(), KtlQCanvasPolygon( itemDocument ? itemDocument->canvas() : nullptr ) { QString name(QString("Item-%1").arg(id)); setObjectName(name.toLatin1().data()); qDebug() << Q_FUNC_INFO << " this=" << this; m_bDynamicContent = false; m_bIsRaised = false; m_bDoneCreation = false; p_parentItem = nullptr; b_deleted = false; p_itemDocument = itemDocument; m_baseZ = -1; if ( p_itemDocument ) { if (newItem) m_id = p_itemDocument->generateUID(id); else { m_id = id; p_itemDocument->registerUID(id); } } m_pPropertyChangedTimer = new QTimer( this ); connect( m_pPropertyChangedTimer, SIGNAL(timeout()), this, SLOT(dataChanged()) ); } Item::~Item() { if ( p_itemDocument ) { p_itemDocument->requestEvent( ItemDocument::ItemDocumentEvent::ResizeCanvasToItems ); p_itemDocument->unregisterUID( id() ); } KtlQCanvasPolygon::hide(); const VariantDataMap::iterator variantDataEnd = m_variantData.end(); for ( VariantDataMap::iterator it = m_variantData.begin(); it != variantDataEnd; ++it ) delete it.value(); m_variantData.clear(); } void Item::removeItem() { if (b_deleted) return; b_deleted = true; hide(); setCanvas(nullptr); emit removed(this); p_itemDocument->appendDeleteList(this); } QFont Item::font() const { if ( KTechlab::self() ) return KTechlab::self()->itemFont(); else return QFont(); } void Item::moveBy( double dx, double dy ) { KtlQCanvasPolygon::moveBy(dx,dy); emit movedBy( dx, dy ); } void Item::setChanged() { if (b_deleted) return; if (canvas()) canvas()->setChanged(boundingRect()); } void Item::setItemPoints( const QPolygon & pa, bool setSizeFromPoints ) { m_itemPoints = pa; if (setSizeFromPoints) setSize( m_itemPoints.boundingRect() ); itemPointsChanged(); } void Item::itemPointsChanged() { setPoints(m_itemPoints); } void Item::setSize( QRect sizeRect, bool forceItemPoints ) { if ( !canvas() ) return; if ( m_sizeRect == sizeRect && !forceItemPoints ) return; if ( !preResize(sizeRect) ) return; canvas()->setChanged(areaPoints().boundingRect()); m_sizeRect = sizeRect; if ( m_itemPoints.isEmpty() || forceItemPoints ) { setItemPoints( QPolygon( m_sizeRect ), false ); } canvas()->setChanged(areaPoints().boundingRect()); postResize(); emit resized(); } ItemData Item::itemData() const { ItemData itemData; itemData.type = m_type; itemData.x = x(); itemData.y = y(); if ( !parentItem() ) itemData.z = m_baseZ; itemData.size = m_sizeRect; itemData.setSize = canResize(); if (p_parentItem) itemData.parentId = p_parentItem->id(); const VariantDataMap::const_iterator end = m_variantData.end(); for ( VariantDataMap::const_iterator it = m_variantData.begin(); it != end; ++it ) { switch( it.value()->type() ) { case Variant::Type::String: case Variant::Type::FileName: case Variant::Type::Port: case Variant::Type::Pin: case Variant::Type::VarName: case Variant::Type::Combo: case Variant::Type::Select: case Variant::Type::Multiline: case Variant::Type::RichText: case Variant::Type::SevenSegment: case Variant::Type::KeyPad: { itemData.dataString[it.key()] = it.value()->value().toString(); break; } case Variant::Type::Int: case Variant::Type::Double: { itemData.dataNumber[it.key()] = it.value()->value().toDouble(); break; } case Variant::Type::Color: { itemData.dataColor[it.key()] = it.value()->value().value(); break; } case Variant::Type::Bool: { itemData.dataBool[it.key()] = it.value()->value().toBool(); break; } case Variant::Type::Raw: { itemData.dataRaw[it.key()] = it.value()->value().toBitArray(); break; } case Variant::Type::PenStyle: case Variant::Type::PenCapStyle: { // These types are only created from DrawPart, and that class // deals with these, so we can ignore them break; } case Variant::Type::None: { // ? Maybe obsoleted data... break; } } } return itemData; } void Item::restoreFromItemData( const ItemData &itemData ) { move( itemData.x, itemData.y ); if ( canResize() ) setSize( itemData.size ); Item *parentItem = p_itemDocument->itemWithID( itemData.parentId ); if (parentItem) setParentItem(parentItem); else m_baseZ = itemData.z; //BEGIN Restore data const QStringMap::const_iterator stringEnd = itemData.dataString.end(); for ( QStringMap::const_iterator it = itemData.dataString.begin(); it != stringEnd; ++it ) { if ( hasProperty(it.key()) ) property( it.key() )->setValue( it.value() ); } const DoubleMap::const_iterator numberEnd = itemData.dataNumber.end(); for ( DoubleMap::const_iterator it = itemData.dataNumber.begin(); it != numberEnd; ++it ) { if ( hasProperty(it.key()) ) property( it.key() )->setValue( it.value() ); } const QColorMap::const_iterator colorEnd = itemData.dataColor.end(); for ( QColorMap::const_iterator it = itemData.dataColor.begin(); it != colorEnd; ++it ) { if ( hasProperty(it.key()) ) property( it.key() )->setValue( it.value() ); } const BoolMap::const_iterator boolEnd = itemData.dataBool.end(); for ( BoolMap::const_iterator it = itemData.dataBool.begin(); it != boolEnd; ++it ) { if ( hasProperty(it.key()) ) property( it.key() )->setValue( QVariant( it.value() /*, 0*/ ) ); } const QBitArrayMap::const_iterator rawEnd = itemData.dataRaw.end(); for ( QBitArrayMap::const_iterator it = itemData.dataRaw.begin(); it != rawEnd; ++it ) { if ( hasProperty(it.key()) ) property( it.key() )->setValue( it.value() ); } //END Restore Data } bool Item::mousePressEvent( const EventInfo &eventInfo ) { Q_UNUSED(eventInfo); return false; } bool Item::mouseReleaseEvent( const EventInfo &eventInfo ) { Q_UNUSED(eventInfo); return false; } bool Item::mouseMoveEvent( const EventInfo &eventInfo ) { Q_UNUSED(eventInfo); return false; } bool Item::wheelEvent( const EventInfo &eventInfo ) { Q_UNUSED(eventInfo); return false; } void Item::enterEvent(QEvent *) { } void Item::leaveEvent(QEvent *) { } bool Item::mouseDoubleClickEvent( const EventInfo & eventInfo ) { Q_UNUSED(eventInfo); Property * property = nullptr; Variant::Type::Value type = Variant::Type::None; const VariantDataMap::iterator variantDataEnd = m_variantData.end(); for ( VariantDataMap::iterator it = m_variantData.begin(); it != variantDataEnd; ++it ) { Property * current = *it; if ( current->type() == Variant::Type::Multiline || current->type() == Variant::Type::RichText ) { property = current; type = current->type(); break; } } if ( !property ) return false; if ( type == Variant::Type::Multiline ) { QDialog *dlg = new QDialog(nullptr); dlg->setModal(true); dlg->setWindowTitle( property->editorCaption() ); QVBoxLayout *mainLayout = new QVBoxLayout; dlg->setLayout(mainLayout); //QFrame *frame = dlg->makeMainWidget(); QFrame *frame = new QFrame(dlg); mainLayout->addWidget(frame); QVBoxLayout *layout = new QVBoxLayout( frame ); layout->setMargin(0); KTextEdit *textEdit = new KTextEdit( frame ); //textEdit->setTextFormat( Qt::PlainText ); // 2018.12.02 textEdit->setAcceptRichText(false); textEdit->setText( property->value().toString() ); layout->addWidget( textEdit, 10 ); textEdit->setFocus(); QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok|QDialogButtonBox::Cancel); QPushButton *clearButton = new QPushButton(buttonBox); KGuiItem::assign(clearButton, KStandardGuiItem::clear()); buttonBox->addButton(clearButton, QDialogButtonBox::ActionRole); mainLayout->addWidget(buttonBox); QPushButton *okButton = buttonBox->button(QDialogButtonBox::Ok); okButton->setDefault(true); okButton->setShortcut(Qt::CTRL | Qt::Key_Return); connect(buttonBox, &QDialogButtonBox::accepted, dlg, &QDialog::accept); connect(buttonBox, &QDialogButtonBox::rejected, dlg, &QDialog::reject); connect(clearButton, &QPushButton::clicked, textEdit, &KTextEdit::clear); dlg->setMinimumWidth( 600 ); if ( dlg->exec() == QDialog::Accepted ) { property->setValue( textEdit->toPlainText() ); dataChanged(); p_itemDocument->setModified(true); } delete dlg; } else { // Is rich text RichTextEditorDlg * dlg = new RichTextEditorDlg( nullptr, property->editorCaption() ); dlg->setText( property->value().toString() ); if ( dlg->exec() == QDialog::Accepted ) { property->setValue( dlg->text() ); dataChanged(); p_itemDocument->setModified(true); } delete dlg; } return true; } void Item::setSelected( bool yes ) { if ( isSelected() == yes ) return; KtlQCanvasPolygon::setSelected(yes); emit selectionChanged(); } void Item::setParentItem( Item *newParentItem ) { // qDebug() << Q_FUNC_INFO << "this = "<addChild(this); } } p_parentItem = newParentItem; (void)level(); reparented( oldParentItem, newParentItem ); p_itemDocument->slotUpdateZOrdering(); } int Item::level() const { return p_parentItem ? p_parentItem->level()+1 : 0; } ItemList Item::children( bool includeGrandChildren ) const { if (!includeGrandChildren) return m_children; ItemList children = m_children; ItemList::const_iterator end = m_children.end(); for ( ItemList::const_iterator it = m_children.begin(); it != end; ++it ) { if (!*it) continue; children += (*it)->children(true); } return children; } void Item::addChild( Item *child ) { if ( !child ) return; if ( child->contains(this) ) { // qCritical() << Q_FUNC_INFO << "Attempting to add a child to this item that is already a parent of this item. Incest results in stack overflow." << endl; return; } if ( contains( child, true ) ) { // qCritical() << Q_FUNC_INFO << "Already have child " << child << endl; return; } m_children.append(child); connect( child, SIGNAL(removed(Item* )), this, SLOT(removeChild(Item* )) ); child->setParentItem(this); childAdded(child); p_itemDocument->slotUpdateZOrdering(); } void Item::removeChild( Item *child ) { if ( !child || !m_children.contains(child) ) return; m_children.removeAll(child); disconnect( child, SIGNAL(removed(Item* )), this, SLOT(removeChild(Item* )) ); childRemoved(child); p_itemDocument->slotUpdateZOrdering(); } bool Item::contains( Item *item, bool direct ) const { const ItemList::const_iterator end = m_children.end(); for ( ItemList::const_iterator it = m_children.begin(); it != end; ++it ) { if ( (Item*)*it == item || ( !direct && (*it)->contains( item, false ) ) ) return true; } return false; } void Item::setRaised( bool isRaised ) { m_bIsRaised = isRaised; // We'll get called later to update our Z } void Item::updateZ( int baseZ ) { m_baseZ = baseZ; double z = ItemDocument::Z::Item + (ItemDocument::Z::DeltaItem)*baseZ; if ( isRaised() ) z += ItemDocument::Z::RaisedItem - ItemDocument::Z::Item; setZ(z); const ItemList::const_iterator end = m_children.end(); for ( ItemList::const_iterator it = m_children.begin(); it != end; ++it ) { if (*it) (*it)->updateZ(baseZ+1); } } int Item::getNumberPre( double num ) { return (int)(num/getMultiplier(num)); } QString Item::getNumberMag( double num ) { if ( num == 0. ) return ""; const double exp_n = std::log10(std::abs(num)); if ( exp_n < minPrefixExp+3 ) return SIprefix[0]; else if ( exp_n >= maxPrefixExp ) return SIprefix[numPrefix-1]; else return SIprefix[(int)std::floor((double)(exp_n/3))-(int)floor(double(minPrefixExp/3))]; } double Item::getMultiplier( double num ) { if ( num == 0. ) return 1.; else return std::pow( 10, 3*std::floor(std::log10(std::abs(num))/3) ); } double Item::getMultiplier( const QString &_mag ) { QString mag; // Allow the user to enter in "u" instead of mu, as unfortunately many keyboards don't have the mu key if ( _mag == "u" ) mag = QChar(0xB5); else mag = _mag; for ( int i=0; ivalue().toDouble() : 0.0; } int Item::dataInt( const QString & id ) const { Variant * variant = property(id); return variant ? variant->value().toInt() : 0; } bool Item::dataBool( const QString & id ) const { Variant * variant = property(id); return variant ? variant->value().toBool() : false; } QString Item::dataString( const QString & id ) const { Variant * variant = property(id); return variant ? variant->value().toString() : QString::null; } QColor Item::dataColor( const QString & id ) const { Variant * variant = property(id); return variant ? variant->value().value() : Qt::black; } Variant * Item::createProperty( const QString & id, Variant::Type::Value type ) { if ( !m_variantData.contains(id) ) { m_variantData[id] = new Variant( id, type ); connect( m_variantData[id], SIGNAL(valueChanged(QVariant,QVariant)), this, SLOT(propertyChangedInitial()) ); } return m_variantData[id]; } Variant * Item::property( const QString & id ) const { if ( m_variantData.contains(id) ) return m_variantData[id]; qCritical() << Q_FUNC_INFO << " No such property with id " << id << endl; return nullptr; } bool Item::hasProperty( const QString & id ) const { return m_variantData.contains(id); } void Item::finishedCreation( ) { m_bDoneCreation = true; dataChanged(); } void Item::propertyChangedInitial() { if ( !m_bDoneCreation ) return; m_pPropertyChangedTimer->setSingleShot(true); m_pPropertyChangedTimer->start( 0 /*, true */ ); } //END Data stuff diff --git a/src/item.h b/src/item.h index a00c0fe7..e1647375 100644 --- a/src/item.h +++ b/src/item.h @@ -1,305 +1,305 @@ /*************************************************************************** * Copyright (C) 2004-2006 by David Saxton * * david@bluehaze.org * * * * 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. * ***************************************************************************/ #ifndef ITEM_H #define ITEM_H #include "variant.h" #include "itemdocument.h" //#include // 2018.10.16 - not needed #include "canvasitems.h" -#include +#include class Document; class EventInfo; class Item; class ItemData; class ItemDocument; class ItemView; class DoubleSpinBox; class Document; class Variant; class QBitArray; class QTimer; typedef Variant Property; typedef Item*(*createItemPtr)( ItemDocument *itemDocument, bool newItem, const char *id ); typedef QPointer GuardedItem; typedef QMap VariantDataMap; typedef QList ItemList; /** @author David Saxton @author Daniel Clarke */ class Item : /* public QObject, */ public KtlQCanvasPolygon { Q_OBJECT public: Item( ItemDocument *itemDocument, bool newItem, const QString &id ); ~Item() override; /** * @return Pointer to the VariantMap used for internal data storage */ VariantDataMap *variantMap() { return &m_variantData; } double dataDouble( const QString & id ) const; int dataInt( const QString & id ) const; bool dataBool( const QString & id ) const; QString dataString( const QString & id ) const; QColor dataColor( const QString & id ) const; virtual Property * createProperty( const QString & id, Variant::Type::Value type ); Property * property( const QString & id ) const; bool hasProperty( const QString & id ) const; /** * Whether or not we can resize the item */ virtual bool canResize() const { return false; } /** * Returns whether the CNItem allows itself to be moved on the canvas. * Most do, but some (such as the PicItem) don't allow this */ virtual bool isMovable() const { return true; } /** * Returns whether or not what the item is displaying has (possibly) changed * since this function was last called. If your item doesn't move, yet still * continously changes what is being displayed (such as a seven segment * display or a lamp), then set m_bDynamicContent to be true in the * constructor or reinherit this to return true when the contents of the * item have changed since this function was last called. */ virtual bool contentChanged() const { return m_bDynamicContent; } /** * Returns a identifier for the CNItem, which is unique on the ICNDocument */ QString id() const { return m_id; } QString type() const { return m_type; } /** * @return the font used for drawing items. This is taken to be the * standard desktop font, limited to a size of 12 pixels. */ QFont font() const; /** * Called from ItemLibrary after this class and subclasses have finished * constructing themselves. */ virtual void finishedCreation(); /** * Sets the selected flag of the item to yes. selected or unselected will be * emitted as appropriate */ void setSelected( bool yes ) override; /** * Convenience function for setting the item bounding area as changed on the * canvas */ void setChanged(); /** * Sets this item as a child of the given item. Calls reparented with the * old and the new parent. */ void setParentItem( Item *parentItem ); /** * The parent item for this item, or nullptr if none */ Item *parentItem() const { return p_parentItem; } ItemDocument *itemDocument() const { return p_itemDocument; } /** * Returns the number of items away from the top item this is * (parent-wise). Returns 0 if has no parent. */ int level() const; /** * If true, then adds ItemDocument::Z::(RaisedItem-Item) to the z value of * the item. */ void setRaised( bool isRaised ); /** * @Returns whether raised or not */ bool isRaised() const { return m_bIsRaised; } /** * Sets this item to the given baseZ level, and calls this function for the * children with baseZ incremented by one. Reinherit this function to set * the Z of attached stuff (such as nodes). */ virtual void updateZ( int baseZ ); /** * Returns the item's position in the overall z-stack of items. */ int baseZ() const { return m_baseZ; } /** * Adds a child. Calls the virtual function childAdded. */ void addChild( Item *child ); /** * Returns the list of children. * @param includeGrandChildren if includeGrandChildren is true then this list will also contain * the children's children, and so on recursively, instead of just the * immediate children. */ ItemList children( bool includeGrandChildren = false ) const; /** * Returns whether we have the given child as either a direct child, or as * either a direct or indirect child */ bool contains( Item *item, bool direct = false ) const; /** * Calls prePresize with the bounds, and if that returns true, sets * m_sizeRect to the given rect, and then calls postResize. The center of * \p sizeRect is taken as the point of rotation. * @param forceItemPoints if true, will set the item points to a rectangle * @of the given size. Pass true if you have already set the size, and want * to update the appearance and bounding of the item. */ void setSize( QRect sizeRect, bool forceItemPoints = false ); /** * Convenience function. * @see setSize( QRect sizeRect, bool forceItemPoints ); */ void setSize( int x, int y, int w, int h, bool forceItemPoints = false ) { setSize( QRect(x,y,w,h), forceItemPoints ); } /** * @returns the m_sizeRect rectangble that contains the item points */ QRect sizeRect() const { return m_sizeRect; } /** * Reinherit this function if you want to determine what the minimum size is * that this item can be resized to. */ virtual QSize minimumSize() const { return QSize(0,0); } int offsetX() const { return m_sizeRect.x(); } int offsetY() const { return m_sizeRect.y(); } int width() const { return m_sizeRect.width(); } int height() const { return m_sizeRect.height(); } virtual bool mousePressEvent( const EventInfo &eventInfo ); virtual bool mouseReleaseEvent( const EventInfo &eventInfo ); virtual bool mouseDoubleClickEvent ( const EventInfo &eventInfo ); virtual bool mouseMoveEvent( const EventInfo &eventInfo ); virtual bool wheelEvent( const EventInfo &eventInfo ); virtual void enterEvent(QEvent *); virtual void leaveEvent(QEvent *); /** * Returns the name of the CNItem, e.g. "Resistor" */ QString name() const { return m_name; } /** * Modifies the exponent of the number so that it appears readable: * eg 10000->10, 174822->175, 0.6->600, etc */ static int getNumberPre( double num ); /** * Returns the SI exponent of the number as a letter: * eg 10000 returns 'k', 0.6 returns 'm', etc */ static QString getNumberMag( double num ); /** * Returns the multiplier required to get the num up to human readable form: * eg 10000 returns 0.001, etc */ static double getMultiplier( double num ); /** * Returns the multiplier required to get the num from human readable form * to its actual value based on the SI exponent: * eg 'm' returns 0.001, etc */ static double getMultiplier( const QString &mag ); virtual ItemData itemData() const; virtual void restoreFromItemData( const ItemData &itemData ); public slots: virtual void removeItem(); /** * Moves item - use this instead of moveBy() so that associated Nodes also get moved */ void moveBy( double dx, double dy ) override; /** * Removes a child. Calls the virtual function childRemoved */ void removeChild( Item *child ); signals: /** * Emitted when the CNItem is removed. Normally, this signal is caught by associated * nodes, who will remove themselves as well. */ void removed( Item *item ); /** * Emitted when the item is selected or unselected. */ void selectionChanged(); /** * Emitted when the item is resized (after calling postResize) */ void resized(); /** * Emitted when the item is moved (by dx, dy). */ void movedBy( double dx, double dy ); protected slots: virtual void propertyChangedInitial(); virtual void dataChanged() {}; protected: /** * Reinherit this function if you want to do anything with children. Called * after the parent is changed, with the old parent and the new parent. */ virtual void reparented( Item */*oldParent*/, Item */*newParent*/ ) {}; /** * Reinherit this function if you want to do anything with children. Called * after a child has been added. */ virtual void childAdded( Item * ) {}; /** * Reinherit this function if you want to do anything with children. Called * after a child has been removed. */ virtual void childRemoved( Item * ) {}; /** * Set the rough bounding points for this item. Calls itemPointsChanged * after setting the points */ void setItemPoints( const QPolygon& pa, bool setSizeFromPoints = true ); /** * Reinherit this function if you want to apply any sort of transformation * to the item points */ virtual void itemPointsChanged(); virtual bool preResize( QRect sizeRect ) { Q_UNUSED(sizeRect); return true; } virtual void postResize() {}; QString m_id; QString m_name; ///< Name (e.g. "Resistor") QString m_type; GuardedItem p_parentItem; // If attached to a parent item ItemList m_children; QPointer p_itemDocument; QPolygon m_itemPoints; // The unorientated and unsized item points QTimer * m_pPropertyChangedTimer; ///< Single show timer for one a property changes friend class ItemLibrary; int m_baseZ; bool m_bIsRaised; bool m_bDoneCreation; bool b_deleted; bool m_bDynamicContent; QRect m_sizeRect; VariantDataMap m_variantData; }; #endif diff --git a/src/itemdocument.cpp b/src/itemdocument.cpp index fb3bb128..e0ab47f8 100644 --- a/src/itemdocument.cpp +++ b/src/itemdocument.cpp @@ -1,1405 +1,1406 @@ /*************************************************************************** * Copyright (C) 2005 by David Saxton * * david@bluehaze.org * * * * 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 "canvasitemparts.h" #include "canvasmanipulator.h" #include "cells.h" #include "circuitdocument.h" #include "connector.h" #include "cnitem.h" #include "drawpart.h" #include "ecnode.h" #include "flowcodedocument.h" #include "icnview.h" #include "itemdocumentdata.h" #include "itemgroup.h" #include "itemselector.h" #include "ktechlab.h" #include "pin.h" #include "resizeoverlay.h" #include "simulator.h" #include "imageexportdlg.h" -#include #include #include // #include //#include #include #include -#include -#include -#include -#include -#include -#include +#include +#include +#include +#include +#include +#include +#include // #include -#include -#include -#include +#include +#include +#include // #include // 2018.08.13 - not needed -#include -#include -#include -#include +#include +#include +#include +#include +#include #include #include #include //BEGIN class ItemDocument int ItemDocument::m_nextActionTicket = 0; ItemDocument::ItemDocument( const QString &caption, const char *name) : Document( caption, name ) { m_queuedEvents = 0; m_nextIdNum = 1; m_savedState = nullptr; m_currentState = nullptr; m_bIsLoading = false; m_canvas = new Canvas( this, "canvas" ); m_canvasTip = new CanvasTip(this,m_canvas); m_cmManager = new CMManager(this); updateBackground(); m_pUpdateItemViewScrollbarsTimer = new QTimer(this); connect( m_pUpdateItemViewScrollbarsTimer, SIGNAL(timeout()), this, SLOT(updateItemViewScrollbars()) ); m_pEventTimer = new QTimer(this); connect( m_pEventTimer, SIGNAL(timeout()), this, SLOT(processItemDocumentEvents()) ); connect( this, SIGNAL(selectionChanged()), this, SLOT(slotInitItemActions()) ); connect( ComponentSelector::self(), SIGNAL(itemClicked(const QString& )), this, SLOT(slotUnsetRepeatedItemId()) ); connect( FlowPartSelector::self(), SIGNAL(itemClicked(const QString& )), this, SLOT(slotUnsetRepeatedItemId()) ); #ifdef MECHANICS connect( MechanicsSelector::self(), SIGNAL(itemClicked(const QString& )), this, SLOT(slotUnsetRepeatedItemId()) ); #endif m_pAlignmentAction = new KActionMenu( i18n("Alignment") /*, "format-justify-right" */ , this ); m_pAlignmentAction->setObjectName("rightjust"); m_pAlignmentAction->setIcon( QIcon::fromTheme("format-justify-right") ); slotUpdateConfiguration(); } ItemDocument::~ItemDocument() { m_bDeleted = true; // ItemMap toDelete = m_itemList; const ItemMap::iterator end = m_itemList.end(); for ( ItemMap::iterator it = m_itemList.begin(); it != end; ++it ) { qDebug() << "ItemDocument::~ItemDocument: deleting [" << it.key() << "] " << it.value(); //delete *it; // 2015.07.31 - this will crash it.value()->deleteLater(); } m_itemList.clear(); cleanClearStack( m_undoStack ); cleanClearStack( m_redoStack ); delete m_cmManager; delete m_currentState; delete m_canvasTip; } void ItemDocument::handleNewView( View * view ) { Document::handleNewView(view); requestEvent( ItemDocument::ItemDocumentEvent::ResizeCanvasToItems ); } bool ItemDocument::registerItem(KtlQCanvasItem *qcanvasItem) { if (!qcanvasItem) return false; requestEvent( ItemDocument::ItemDocumentEvent::ResizeCanvasToItems ); if(Item *item = dynamic_cast(qcanvasItem) ) { m_itemList[ item->id() ] = item; connect( item, SIGNAL(selectionChanged()), this, SIGNAL(selectionChanged()) ); itemAdded(item); return true; } return false; } void ItemDocument::slotSetDrawAction(QAction *selected) { int drawAction = selected->data().toInt(); m_cmManager->setDrawAction(drawAction); } void ItemDocument::cancelCurrentOperation() { m_cmManager->cancelCurrentManipulation(); } void ItemDocument::slotSetRepeatedItemId( const QString &id ) { m_cmManager->setCMState( CMManager::cms_repeated_add, true ); m_cmManager->setRepeatedAddId(id); } void ItemDocument::slotUnsetRepeatedItemId() { m_cmManager->setCMState( CMManager::cms_repeated_add, false ); } void ItemDocument::fileSave() { if ( url().isEmpty() && !getURL(m_fileExtensionInfo) ) return; writeFile(); } void ItemDocument::fileSaveAs() { if ( !getURL(m_fileExtensionInfo) ) return; writeFile(); // Our modified state may not have changed, but we emit this to force the // main window to update our caption. emit modifiedStateChanged(); } void ItemDocument::writeFile() { ItemDocumentData data( type() ); data.saveDocumentState(this); if ( data.saveData(url()) ) { m_savedState = m_currentState; setModified(false); } } bool ItemDocument::openURL( const QUrl &url ) { ItemDocumentData data( type() ); if ( !data.loadData(url) ) return false; // Why do we stop simulating while loading a document? // Crash possible when loading a circuit document, and the Qt event loop is // reentered (such as when a PIC component pops-up a message box), which // will then call the Simulator::step function, which might use components // that have not fully initialized themselves. m_bIsLoading = true; bool wasSimulating = Simulator::self()->isSimulating(); Simulator::self()->slotSetSimulating( false ); data.restoreDocument(this); Simulator::self()->slotSetSimulating( wasSimulating ); m_bIsLoading = false; setURL(url); clearHistory(); m_savedState = m_currentState; setModified(false); if ( FlowCodeDocument *fcd = dynamic_cast(this) ) { // We need to tell all pic-depedent components about what pic type is in use emit fcd->picTypeChanged(); } requestEvent( ItemDocument::ItemDocumentEvent::ResizeCanvasToItems ); // Load Z-position info m_zOrder.clear(); ItemMap::iterator end = m_itemList.end(); for ( ItemMap::iterator it = m_itemList.begin(); it != end; ++it ) { if ( !*it || (*it)->parentItem() ) continue; m_zOrder[(*it)->baseZ()] = *it; } slotUpdateZOrdering(); return true; } void ItemDocument::print() { static QPrinter * printer = new QPrinter; //if ( ! printer->setup( KTechlab::self() ) ) // return; QPrintDialog printDialog(printer, KTechlab::self()); if ( ! printDialog.exec() ) { return; } // setup the printer. with Qt, you always "print" to a // QPainter.. whether the output medium is a pixmap, a screen, // or paper QPainter p; p.begin( printer ); // we let our view do the actual printing //Q3PaintDeviceMetrics metrics( printer ); // 2018.08.13 - replaced with method call QRect pageRect = printer->pageRect(); // Round to 16 so that we cut in the middle of squares int w = pageRect.width(); w = (w & 0xFFFFFFF0) + ((w << 1) & 0x10); int h = pageRect.height(); h = (h & 0xFFFFFFF0) + ((h << 1) & 0x10); p.setClipping( true ); p.setClipRect( 0, 0, w, h, /* QPainter::CoordPainter */ Qt::ReplaceClip ); // TODO is this correct? // Send off the painter for drawing // note: What was this doing?? // set "null" background, so the background horiznotal and vertial lines are not visible m_canvas->setBackgroundPixmap( QPixmap(0,0) /* 0 */ ); QRect bounding = canvasBoundingRect(); unsigned int rows = (unsigned) std::ceil( double( bounding.height() ) / double( h ) ); unsigned int cols = (unsigned) std::ceil( double( bounding.width() ) / double( w ) ); int offset_x = bounding.x(); int offset_y = bounding.y(); for ( unsigned row = 0; row < rows; ++row ) { for ( unsigned col = 0; col < cols; ++col ) { if ( row != 0 || col != 0 ) printer->newPage(); QRect drawArea( offset_x + (col * w), offset_y + (row * h), w, h ); m_canvas->drawArea( drawArea, & p ); p.translate( -w, 0 ); } p.translate( w * cols, -h ); } updateBackground(); // and send the result to the printer p.end(); } void ItemDocument::requestStateSave( int actionTicket ) { if ( m_bIsLoading ) return; cleanClearStack( m_redoStack ); if ( (actionTicket >= 0) && (actionTicket == m_currentActionTicket) ) { delete m_currentState; m_currentState = nullptr; } m_currentActionTicket = actionTicket; //FIXME: it is possible, that we push something here, also nothing has changed, yet. // to reproduce do: // 1. select an item -> something is pushed onto undoStack, but nothing changed // 2. select Undo -> pushed on redoStack, pop from undoStack // 3. deselect item -> there is still something on the redoStack // // this way you can fill up the redoStack, as you like :-/ if (m_currentState) m_undoStack.push(m_currentState); m_currentState = new ItemDocumentData( type() ); m_currentState->saveDocumentState(this); if (!m_savedState) m_savedState = m_currentState; setModified( m_savedState != m_currentState ); emit undoRedoStateChanged(); //FIXME To resize undo queue, have to pop and push everything // In Qt4 QStack is used and QStack inherits QVector, that should // make it a bit more easy int maxUndo = KTLConfig::maxUndo(); if ( maxUndo <= 0 || m_undoStack.count() < maxUndo ) return; IDDStack tempStack; int pushed = 0; while ( !m_undoStack.isEmpty() && pushed < maxUndo ) { tempStack.push( m_undoStack.pop() ); pushed++; } cleanClearStack( m_undoStack ); while ( !tempStack.isEmpty() ) m_undoStack.push( tempStack.pop() ); } void ItemDocument::cleanClearStack( IDDStack &stack ) { while ( !stack.isEmpty() ) { ItemDocumentData * idd = stack.pop(); if ( m_currentState != idd ) delete idd; } } void ItemDocument::clearHistory() { cleanClearStack( m_undoStack ); cleanClearStack( m_redoStack ); delete m_currentState; m_currentState = nullptr; requestStateSave(); } bool ItemDocument::isUndoAvailable() const { return !m_undoStack.isEmpty(); } bool ItemDocument::isRedoAvailable() const { return !m_redoStack.isEmpty(); } void ItemDocument::undo() { if (m_undoStack.empty()) { return; } ItemDocumentData *idd = m_undoStack.pop(); if (!idd) return; if (m_currentState) m_redoStack.push(m_currentState); idd->restoreDocument(this); m_currentState = idd; setModified( m_savedState != m_currentState ); emit undoRedoStateChanged(); } void ItemDocument::redo() { if (m_redoStack.empty()) { return; } ItemDocumentData *idd = m_redoStack.pop(); if (!idd) return; if (m_currentState) m_undoStack.push(m_currentState); idd->restoreDocument(this); m_currentState = idd; setModified( m_savedState != m_currentState ); emit undoRedoStateChanged(); } void ItemDocument::cut() { copy(); deleteSelection(); } void ItemDocument::paste() { QString xml = QApplication::clipboard()->text( QClipboard::Clipboard ); if ( xml.isEmpty() ) return; unselectAll(); ItemDocumentData data( type() ); if ( !data.fromXML(xml) ) return; data.generateUniqueIDs(this); // data.translateContents( 64, 64 ); data.mergeWithDocument( this, true ); // Get rid of any garbage that shouldn't be around / merge connectors / etc flushDeleteList(); requestStateSave(); } Item *ItemDocument::itemWithID( const QString &id ) { if ( m_itemList.contains( id ) ) return m_itemList[id]; else return nullptr; } void ItemDocument::unselectAll() { selectList()->removeAllItems(); } void ItemDocument::select( KtlQCanvasItem * item ) { if (!item) return; item->setSelected( selectList()->contains( item ) || selectList()->addQCanvasItem( item ) ); } void ItemDocument::select( const KtlQCanvasItemList & list ) { const KtlQCanvasItemList::const_iterator end = list.end(); for ( KtlQCanvasItemList::const_iterator it = list.begin(); it != end; ++it ) selectList()->addQCanvasItem(*it); selectList()->setSelected(true); } void ItemDocument::unselect( KtlQCanvasItem *qcanvasItem ) { selectList()->removeQCanvasItem(qcanvasItem); qcanvasItem->setSelected(false); } void ItemDocument::slotUpdateConfiguration() { updateBackground(); m_canvas->setUpdatePeriod( int(1000./KTLConfig::refreshRate()) ); } KtlQCanvasItem* ItemDocument::itemAtTop( const QPoint &pos ) const { KtlQCanvasItemList list = m_canvas->collisions( QRect( pos.x()-1, pos.y()-1, 3, 3 ) ); // note: m_canvas is actually modified here KtlQCanvasItemList::const_iterator it = list.begin(); const KtlQCanvasItemList::const_iterator end = list.end(); while ( it != end ) { KtlQCanvasItem *item = *it; if( !dynamic_cast(item) && !dynamic_cast(item) && !dynamic_cast(item) && !dynamic_cast(item) && !dynamic_cast(item) ) { ++it; } else { if ( ConnectorLine * l = dynamic_cast(item) ) return l->parent(); return item; } } return nullptr; } // these look dangerous., see todo in header file. void ItemDocument::alignHorizontally( ) { selectList()->slotAlignHorizontally(); if ( ICNDocument *icnd = dynamic_cast(this) ) icnd->requestRerouteInvalidatedConnectors(); } void ItemDocument::alignVertically( ) { selectList()->slotAlignVertically(); if ( ICNDocument *icnd = dynamic_cast(this) ) icnd->requestRerouteInvalidatedConnectors(); } void ItemDocument::distributeHorizontally( ) { selectList()->slotDistributeHorizontally(); if ( ICNDocument *icnd = dynamic_cast(this) ) icnd->requestRerouteInvalidatedConnectors(); } void ItemDocument::distributeVertically( ) { selectList()->slotDistributeVertically(); if ( ICNDocument *icnd = dynamic_cast(this) ) icnd->requestRerouteInvalidatedConnectors(); } // ########################### bool ItemDocument::registerUID( const QString &UID ) { return m_idList.insert(UID).second; } void ItemDocument::unregisterUID( const QString & uid ) { m_idList.erase(uid); m_itemList.remove(uid); } QString ItemDocument::generateUID( QString name ) { name.remove( QRegExp("__.*") ); // Change 'node__13' to 'node', for example QString idAttempt = name; while ( !registerUID(idAttempt) ) idAttempt = name + "__" + QString::number(m_nextIdNum++); return idAttempt; } // FIXME: popup menu doesn't seem to work these days. =( void ItemDocument::canvasRightClick( const QPoint &pos, KtlQCanvasItem* item ) { if (item) { if ( dynamic_cast(item) && !item->isSelected() ) { unselectAll(); select(item); } } KTechlab::self()->unplugActionList("alignment_actionlist"); KTechlab::self()->unplugActionList("orientation_actionlist"); fillContextMenu(pos); QMenu *pop = static_cast(KTechlab::self()->factory()->container("item_popup", KTechlab::self() )); if (!pop) return; pop->popup(pos); } void ItemDocument::fillContextMenu( const QPoint & pos ) { Q_UNUSED(pos); ItemView * activeItemView = dynamic_cast(activeView()); if ( !KTechlab::self() || !activeItemView ) return; QAction * align_actions[] = { activeItemView->actionByName("align_horizontally"), activeItemView->actionByName("align_vertically"), activeItemView->actionByName("distribute_horizontally"), activeItemView->actionByName("distribute_vertically") }; bool enableAlignment = selectList()->itemCount() > 1; if ( !enableAlignment ) return; for ( unsigned i = 0; i < 4; ++i ) { align_actions[i]->setEnabled(true); m_pAlignmentAction->removeAction( align_actions[i] ); //m_pAlignmentAction->insert( align_actions[i] ); m_pAlignmentAction->addAction( align_actions[i] ); } QList alignment_actions; alignment_actions.append( m_pAlignmentAction ); KTechlab::self()->plugActionList( "alignment_actionlist", alignment_actions ); } void ItemDocument::slotInitItemActions() { ItemView * activeItemView = dynamic_cast(activeView()); if ( !KTechlab::self() || !activeItemView ) return; QAction * align_actions[] = { activeItemView->actionByName("align_horizontally"), activeItemView->actionByName("align_vertically"), activeItemView->actionByName("distribute_horizontally"), activeItemView->actionByName("distribute_vertically") }; bool enableAlignment = selectList()->itemCount() > 1; for ( unsigned i = 0; i < 4; ++i ) align_actions[i]->setEnabled(enableAlignment); } void ItemDocument::updateBackground() { // Also used in the constructor to make the background initially. // Thoughts. // ~The pixmap could be done somehow with 1bpp. It might save some waste // I expect it won't hurt for now. // ~This is all rather static, only works with square etc... should be no prob. for most uses. IMO. // ~If you want, decide what maximum and minimum spacing should be, then enforce them // in the Config (I suppose you can use tags?) // ~Defaults based on the existing grid background png. It should produce identical results, to your // original png. // **** Below where it says "interval * 10", that decides how big the pixmap will be (always square) // Originally I set this to 32, which give 256x256 with 8 spacing, as that was the size of your pixmap // Are there any good reasons to make the a certain size? (i.e. big or small ?). int interval = 8; int bigness = interval * 10; QPixmap pm( bigness, bigness ); // pm.fill( KTLConfig::bgColor() ); // first fill the background colour in pm.fill( Qt::white ); if( KTLConfig::showGrid() ){ //QPainter p(&pm); // setup painter to draw on pixmap QPainter p; const bool isSuccess = p.begin(&pm); if (!isSuccess) { qWarning() << Q_FUNC_INFO << " painter is not active"; } p.setPen( KTLConfig::gridColor() ); // set forecolour // note: anything other than 8 borks this for( int i = (interval / 2); i < bigness; i+=interval ){ p.drawLine( 0, i, bigness, i ); // horizontal p.drawLine( i, 0, i, bigness ); // vertical } p.end(); // all done } //pm.setDefaultOptimization( QPixmap::BestOptim ); // TODO no longer available? m_canvas->setBackgroundPixmap(pm); // and the finale. } void ItemDocument::requestCanvasResize() { requestEvent( ItemDocumentEvent::ResizeCanvasToItems ); } void ItemDocument::requestEvent( ItemDocumentEvent::type type ) { m_queuedEvents |= type; m_pEventTimer->stop(); m_pEventTimer->setSingleShot(true); m_pEventTimer->start( 0 /*, true */ ); } void ItemDocument::processItemDocumentEvents() { // Copy it incase we have new events requested while doing this... unsigned queuedEvents = m_queuedEvents; m_queuedEvents = 0; if ( queuedEvents & ItemDocumentEvent::ResizeCanvasToItems ) resizeCanvasToItems(); if ( queuedEvents & ItemDocumentEvent::UpdateZOrdering ) slotUpdateZOrdering(); ICNDocument * icnd = dynamic_cast(this); if ( icnd && (queuedEvents & ItemDocumentEvent::UpdateNodeGroups) ) icnd->slotAssignNodeGroups(); if ( icnd && (queuedEvents & ItemDocumentEvent::RerouteInvalidatedConnectors) ) icnd->rerouteInvalidatedConnectors(); } void ItemDocument::resizeCanvasToItems() { QRect bound = canvasBoundingRect(); m_viewList.removeAll((View*)nullptr); const ViewList::iterator end = m_viewList.end(); for ( ViewList::iterator it = m_viewList.begin(); it != end; ++it ) { ItemView * iv = static_cast((View*)*it); CVBEditor * cvbEditor = iv->cvbEditor(); QPoint topLeft = iv->mousePosToCanvasPos( QPoint( 0, 0 ) ); int width = int( cvbEditor->visibleWidth() / iv->zoomLevel() ); int height = int( cvbEditor->visibleHeight() / iv->zoomLevel() ); QRect r( topLeft, QSize( width, height ) ); bound |= r; // qDebug() << "r="<rect(); } } saveArea = m_canvas->rect(); if ( type == "PNG" || type == "BMP" ) outputImage = new QPixmap( saveArea.size() ); else if ( type == "SVG" ) { setSVGExport(true); outputImage = new QPicture(); // svg can't be cropped using the qimage method. saveArea = cropArea; } else { qWarning() << "Unknown type!" << endl; return; } //2018.05.05 - extract to a method // //QPainter p(outputImage); // 2016.05.03 - explicitly initialize painter // QPainter p; // const bool isBeginSuccess = p.begin(outputImage); // if (!isBeginSuccess) { // qWarning() << Q_FUNC_INFO << " painter not active"; // } // // m_canvas->setBackgroundPixmap(QPixmap()); // m_canvas->drawArea( saveArea, &p ); // updateBackground(); // // p.end(); exportToImageDraw(saveArea, *outputImage); bool saveResult; // if cropping we need to convert to an image, // crop, then save. if (crop) { if( type == "SVG" ) saveResult = dynamic_cast(outputImage)->save(filePath, type.toLatin1().data()); else { QImage img = dynamic_cast(outputImage)->toImage(); if ( saveArea.x() < 0 ) { cropArea.translate( - saveArea.x(), 0 ); } if ( saveArea.y() < 0 ) { cropArea.translate( 0, - saveArea.y() ); } qDebug() << Q_FUNC_INFO << " cropArea " << cropArea; QImage imgCropped = img.copy(cropArea); saveResult = imgCropped.save(filePath,type.toLatin1().data()); } } else { if ( type=="SVG" ) saveResult = dynamic_cast(outputImage)->save( filePath, type.toLatin1().data() ); else saveResult = dynamic_cast(outputImage)->save( filePath, type.toLatin1().data() ); } //if(saveResult == true) KMessageBox::information( this, i18n("Sucessfully exported to \"%1\"", url.filename() ), i18n("Image Export") ); //else KMessageBox::information( this, i18n("Export failed"), i18n("Image Export") ); if ( type == "SVG" ) setSVGExport(false); if (saveResult == false) KMessageBox::information( KTechlab::self(), i18n("Export failed"), i18n("Image Export") ); delete outputImage; } void ItemDocument::exportToImageDraw( const QRect &saveArea, QPaintDevice &pDev) { qDebug() << Q_FUNC_INFO << " saveArea " << saveArea; //QPainter p(outputImage); // 2016.05.03 - explicitly initialize painter QPainter p; const bool isBeginSuccess = p.begin(&pDev); if (!isBeginSuccess) { qWarning() << Q_FUNC_INFO << " painter not active"; } QTransform transf; transf.translate( -saveArea.x(), -saveArea.y()); p.setTransform(transf); m_canvas->setBackgroundPixmap(QPixmap()); m_canvas->drawArea( saveArea, &p ); updateBackground(); p.end(); } void ItemDocument::setSVGExport( bool svgExport ) { // Find any items and tell them not to draw buttons or sliders KtlQCanvasItemList items = m_canvas->allItems(); const KtlQCanvasItemList::iterator end = items.end(); for ( KtlQCanvasItemList::Iterator it = items.begin(); it != end; ++it ) { if ( CNItem * cnItem = dynamic_cast(*it) ) cnItem->setDrawWidgets(!svgExport); } } void ItemDocument::raiseZ() { raiseZ( selectList()->items(true) ); } void ItemDocument::raiseZ( const ItemList & itemList ) { if ( m_zOrder.isEmpty() ) slotUpdateZOrdering(); if ( m_zOrder.isEmpty() ) return; IntItemMap::iterator begin = m_zOrder.begin(); IntItemMap::iterator previous = m_zOrder.end(); IntItemMap::iterator it = --m_zOrder.end(); do { Item * previousData = (previous == m_zOrder.end()) ? nullptr : previous.value(); Item * currentData = it.value(); if ( currentData && previousData && itemList.contains(currentData) && !itemList.contains(previousData) ) { previous.value() = currentData; it.value() = previousData; } previous = it; --it; } while ( previous != begin ); slotUpdateZOrdering(); } void ItemDocument::lowerZ() { lowerZ( selectList()->items(true) ); } void ItemDocument::lowerZ( const ItemList &itemList ) { if ( m_zOrder.isEmpty() ) slotUpdateZOrdering(); if ( m_zOrder.isEmpty() ) return; IntItemMap::iterator previous = m_zOrder.begin(); IntItemMap::iterator end = m_zOrder.end(); for ( IntItemMap::iterator it = m_zOrder.begin(); it != end; ++it ) { Item * previousData = previous.value(); Item * currentData = it.value(); if ( currentData && previousData && itemList.contains(currentData) && !itemList.contains(previousData) ) { previous.value() = currentData; it.value() = previousData; } previous = it; } slotUpdateZOrdering(); } void ItemDocument::itemAdded( Item * ) { requestEvent( ItemDocument::ItemDocumentEvent::UpdateZOrdering ); } void ItemDocument::slotUpdateZOrdering() { ItemMap toAdd = m_itemList; IntItemMap newZOrder; int atLevel = 0; IntItemMap::iterator zEnd = m_zOrder.end(); for ( IntItemMap::iterator it = m_zOrder.begin(); it != zEnd; ++it ) { Item * item = it.value(); if (!item) continue; toAdd.remove( item->id() ); if ( !item->parentItem() && item->isMovable() ) newZOrder[atLevel++] = item; } ItemMap::iterator addEnd = toAdd.end(); for ( ItemMap::iterator it = toAdd.begin(); it != addEnd; ++it ) { Item * item = *it; if ( item->parentItem() || !item->isMovable() ) continue; newZOrder[atLevel++] = item; } m_zOrder = newZOrder; zEnd = m_zOrder.end(); for ( IntItemMap::iterator it = m_zOrder.begin(); it != zEnd; ++it ) it.value()->updateZ( it.key() ); } void ItemDocument::update( ) { ItemMap::iterator end = m_itemList.end(); for ( ItemMap::iterator it = m_itemList.begin(); it != end; ++it ) { if ( (*it)->contentChanged() ) (*it)->setChanged(); } } ItemList ItemDocument::itemList( ) const { ItemList l; ItemMap::const_iterator end = m_itemList.end(); for ( ItemMap::const_iterator it = m_itemList.begin(); it != end; ++it ) l << it.value(); return l; } //END class ItemDocument //BEGIN class CanvasTip CanvasTip::CanvasTip( ItemDocument *itemDocument, KtlQCanvas *qcanvas ) : KtlQCanvasRectangle( qcanvas ) { p_itemDocument = itemDocument; setZ( ICNDocument::Z::Tip ); } CanvasTip::~CanvasTip() { } void CanvasTip::displayVI( ECNode *node, const QPoint &pos ) { if ( !node || !updateVI() ) return; unsigned num = node->numPins(); m_v.resize(num); m_i.resize(num); for ( unsigned i = 0; i < num; i++ ) { if ( Pin * pin = node->pin(i) ) { m_v[i] = pin->voltage(); m_i[i] = pin->current(); } } display(pos); } void CanvasTip::displayVI( Connector *connector, const QPoint &pos ) { if ( !connector || !updateVI()) return; unsigned num = connector->numWires(); m_v.resize(num); m_i.resize(num); for ( unsigned i = 0; i < num; i++ ) { if ( Wire * wire = connector->wire(i) ) { m_v[i] = wire->voltage(); m_i[i] = std::abs(wire->current()); } } display(pos); } bool CanvasTip::updateVI() { CircuitDocument *circuitDocument = dynamic_cast(p_itemDocument); if ( !circuitDocument || !Simulator::self()->isSimulating() ) return false; circuitDocument->calculateConnectorCurrents(); return true; } void CanvasTip::display( const QPoint &pos ) { unsigned num = m_v.size(); for ( unsigned i = 0; i < num; i++ ) { if ( !std::isfinite(m_v[i]) || std::abs(m_v[i]) < 1e-9 ) m_v[i] = 0.; if ( !std::isfinite(m_i[i]) || std::abs(m_i[i]) < 1e-9 ) m_i[i] = 0.; } move( pos.x()+20, pos.y()+4 ); if ( num == 0 ) return; if ( num == 1 ) setText( displayText(0) ); else { QString text; for ( unsigned i = 0; i < num; i++ ) text += QString("%1: %2\n").arg( QString::number(i) ).arg( displayText(i) ); setText(text); } } QString CanvasTip::displayText( unsigned num ) const { if ( m_v.size() <= (int)num ) return QString::null; return QString("%1%2V %3%4A") .arg( QString::number( m_v[num] / CNItem::getMultiplier(m_v[num]), 'g', 3 ) ) .arg( CNItem::getNumberMag( m_v[num] ) ) .arg( QString::number( m_i[num] / CNItem::getMultiplier(m_i[num]), 'g', 3 ) ) .arg( CNItem::getNumberMag( m_i[num] ) ); } void CanvasTip::draw( QPainter &p ) { CircuitDocument *circuitDocument = dynamic_cast(p_itemDocument); if ( !circuitDocument || !Simulator::self()->isSimulating() ) return; p.setBrush( QColor( 0xff, 0xff, 0xdc ) ); p.setPen( Qt::black ); p.drawRect( boundingRect() ); QRect textRect = boundingRect(); textRect.setLeft( textRect.left() + 3 ); textRect.setTop( textRect.top() + 1 ); p.drawText( textRect, 0, m_text ); } void CanvasTip::setText( const QString & text ) { m_text = text; canvas()->setChanged( boundingRect() ); QRect r = QFontMetrics( qApp->font() ).boundingRect( 0, 0, 0, 0, 0, m_text ); setSize( r.width() + 4, r.height() - 1 ); } //END class CanvasTip //BEGIN class Canvas Canvas::Canvas( ItemDocument *itemDocument, const char * name ) : KtlQCanvas( itemDocument, name ) { p_itemDocument = itemDocument; m_pMessageTimeout = new QTimer(this); connect( m_pMessageTimeout, SIGNAL(timeout()), this, SLOT(slotSetAllChanged()) ); } void Canvas::resize( const QRect & size ) { if ( rect() == size ) return; QRect oldSize = rect(); KtlQCanvas::resize( size ); emit resized( oldSize, size ); } void Canvas::setMessage( const QString & message ) { m_message = message; if ( message.isEmpty() ) { m_pMessageTimeout->stop(); } else { m_pMessageTimeout->setSingleShot(true); m_pMessageTimeout->start( 2000 /*, true */ ); } setAllChanged(); } void Canvas::drawBackground ( QPainter &p, const QRect & clip ) { KtlQCanvas::drawBackground( p, clip ); #if 0 const int scx = (int)((clip.left()-4)/8); const int ecx = (int)((clip.right()+4)/8); const int scy = (int)((clip.top()-4)/8); const int ecy = (int)((clip.bottom()+4)/8); ICNDocument * icnd = dynamic_cast(p_itemDocument); if ( !icnd ) return; Cells * c = icnd->cells(); if ( !c->haveCell( scx, scy ) || !c->haveCell( ecx, ecy ) ) return; for ( int x=scx; x<=ecx; x++ ) { for ( int y=scy; y<=ecy; y++ ) { const double score = c->cell( x, y ).CIpenalty + c->cell( x, y ).Cpenalty; int value = (int)std::log(score)*20; if ( value>255 ) value=255; else if (value<0 ) value=0; p.setBrush( QColor( 255, (255-value), (255-value) ) ); p.setPen( Qt::NoPen ); p.drawRect( (x*8), (y*8), 8, 8 ); } } #endif } void Canvas::drawForeground ( QPainter &p, const QRect & clip ) { KtlQCanvas::drawForeground( p, clip ); if ( !m_pMessageTimeout->isActive() ) return; // Following code stolen and adapted from amarok/src/playlist.cpp :) // Find out width of smallest view QSize minSize; const ViewList viewList = p_itemDocument->viewList(); ViewList::const_iterator end = viewList.end(); View * firstView = nullptr; for ( ViewList::const_iterator it = viewList.begin(); it != end; ++it ) { if ( !*it ) continue; if ( !firstView ) { firstView = *it; minSize = (*it)->size(); } else minSize = minSize.boundedTo( (*it)->size() ); } if ( !firstView ) return; // Q3SimpleRichText * t = new Q3SimpleRichText( m_message, QApplication::font() ); QTextEdit * t = new QTextEdit( m_message ); { QFont tf = t->document()->defaultFont(); QFontMetrics tfm(tf); QSize textSize = tfm.size(0, m_message); t->resize( textSize ); } int w = t->width(); int h = t->height(); int x = rect().left() + 15; int y = rect().top() + 15; int b = 10; // text padding // if ( w+2*b >= minSize.width() || h+2*b >= minSize.height() ) // { // qWarning() << Q_FUNC_INFO << "size not good w=" << w << " h=" << h << "b=" << b << " minSize=" << minSize; // delete t; // return; // } //p.setBrush( firstView->colorGroup().background() ); // 2018.12.02 p.setBrush( firstView->palette().window() ); p.drawRoundRect( x, y, w+2*b, h+2*b, (8*200)/(w+2*b), (8*200)/(h+2*b) ); // t->draw( &p, x+b, y+b, QRect(), firstView->colorGroup() ); t->resize(w+2*b, h+2*b); t->viewport()->setAutoFillBackground( false ); t->setFrameStyle(QFrame::NoFrame); t->render( &p, QPoint( x, y ) , QRegion(), QWidget::DrawChildren ); delete t; } void Canvas::update() { p_itemDocument->update(); KtlQCanvas::update(); } //END class Canvas diff --git a/src/itemdocument.h b/src/itemdocument.h index ff853820..0933bba9 100644 --- a/src/itemdocument.h +++ b/src/itemdocument.h @@ -1,482 +1,482 @@ /*************************************************************************** * Copyright (C) 2005 by David Saxton * * david@bluehaze.org * * * * 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. * ***************************************************************************/ #ifndef ITEMDOCUMENT_H #define ITEMDOCUMENT_H #include #include #include #include "canvasitems.h" -#include -#include +#include +#include // #include class Canvas; class CanvasTip; class Connector; class CMManager; class ECNode; class Item; class ItemDocumentData; class ItemGroup; class KTechlab; class Operation; class KActionMenu; class KtlQCanvasItem; typedef QStack IDDStack; typedef QPointer GuardedItem; typedef QMap< int, GuardedItem > IntItemMap; typedef QMap< QString, Item* > ItemMap; typedef QList ItemList; typedef QList QPointList; /** @author David Saxton */ class ItemDocument : public Document { Q_OBJECT friend class KtlTestsAppFixture; public: ItemDocument( const QString &caption, const char *name = nullptr ); ~ItemDocument() override; class Z { public: enum { Select = 10000000, Connector = 20000000, Item = 30000000, RaisedItem = 40000000, ResizeHandle = 50000000, Tip = 60000000, ConnectorCreateLine = 70000000, // How much "Z" separates items stacked on each other DeltaItem = 10000 }; }; /** * Some things (such as the canvas getting resized, connectors being * invalidated, need to be done after editing operations have finished, * etc, and they also need to be done in the order given in the * enumeration below. */ class ItemDocumentEvent { public: enum type { ResizeCanvasToItems = 1 << 0, UpdateNodeGroups = 1 << 1, RerouteInvalidatedConnectors = 1 << 2, UpdateZOrdering = 1 << 3 }; }; void fileSave() override; void fileSaveAs() override; void print() override; bool openURL( const QUrl &url ) override; /** * Attempt to register the item, returning true iff successful */ virtual bool registerItem( KtlQCanvasItem *qcanvasItem ); /** * Will attempt to create an item with the given id at position p. Some item * (such as PIC/START) have restrictions, and can only have one instance of * themselves on the canvas, and adds the operation to the undo list */ virtual Item* addItem( const QString &id, const QPoint &p, bool newItem ) = 0; /** * @returns A pointer to the canvas */ Canvas *canvas() const { return m_canvas; } /** * Attemtps to register a unique id for the canvas view of an item on the * canvas. If the id does not already exist, will return true; otherwise * the function will return false. */ bool registerUID( const QString & uid ); /** * Generates a unique id based on a possibly unique component name. */ QString generateUID( QString name ); /** * Unlists the given id as one that is used. * @see registerUID */ virtual void unregisterUID( const QString & uid ); /** * @return Whether or not the item is valid; i.e. is appropriate to the * document being edited, and does not have other special restrictions * on it (such as only allowing one instance of the Start part in * FlowCode). */ virtual bool isValidItem( Item *item ) = 0; /** * @return Whether or not the item is valid; i.e. is appropriate to the * document being edited, and does not have other special restrictions * on it (such as only allowing one instance of the Start part in * FlowCode). */ virtual bool isValidItem( const QString &itemId ) = 0; /** * Increases the "height" of the given list of items by "one". */ void raiseZ( const ItemList & itemList ); /** * Decreases the "height" of the given list of items by "one". */ void lowerZ( const ItemList & itemList ); /** * @return ItemGroup that is used as the select list for this document. */ virtual ItemGroup *selectList() const = 0; /** * Deselects any currently selected items */ void unselectAll(); /** * Select a list of KtlQCanvasItem's */ void select( const KtlQCanvasItemList & list ); /** * Select a KtlQCanvasItem */ void select( KtlQCanvasItem * item ); /** * Unselects the item */ void unselect( KtlQCanvasItem *qcanvasItem ); /** * Deletes anything waiting to be deleted. */ virtual void flushDeleteList() = 0; /** * Returns a rubber-band rectangle that contains all of the items on the * canvas, padded out by a small border. */ QRect canvasBoundingRect() const; /** * Returns a pointer to a Item on the canvas with the given id, * or nullptr if no such Item exists. */ Item* itemWithID( const QString & ); /** * Returns true if the user can perform an undo action * (i.e. the undo stack is not empty) */ bool isUndoAvailable() const override; /** * Returns true if the user can perform an redo action * (i.e. the redo stack is not empty) */ bool isRedoAvailable() const override; /** * Returns the top item at point (x, y), or nullptr if there is no item there */ KtlQCanvasItem* itemAtTop( const QPoint &pos ) const ; /** * Called when the canvas is clicked on with the right mouse button. * Popups up a menu for editing operations */ virtual void canvasRightClick( const QPoint &pos, KtlQCanvasItem* item ); /** * List of items in the ItemDocument */ ItemList itemList() const; /** * Set the given KtlQCanvasItem (which will attempt to be casted to known * items to be deleted. */ virtual void appendDeleteList( KtlQCanvasItem * ) = 0; /** * Save the current state of the document to the undo/redo history. * @param actionTicket if this is non-negative, and the last state save * also had the same actionTicket, then the next state save will * overwrite the previous state save. * @see getActionTicket */ void requestStateSave( int actionTicket = -1 ); /** * Clears the undo / redo history */ void clearHistory(); /** * Requests an event to be done after other stuff (editing, etc) is finished. */ void requestEvent( ItemDocumentEvent::type type ); /** * Called from Canvas (when KtlQCanvas::advance is called). */ virtual void update(); /** * Returns a unique id, for use in requestStateSave */ int getActionTicket() const { return m_nextActionTicket++; } public slots: void undo() override; void redo() override; void cut() override; void paste() override; /** * Ask the canvas to be resized to the current items on the canvas. */ void requestCanvasResize(); /** * Selects everything in the view. */ void selectAll() override = 0; /** * Increases the "height" of the selected items. */ void raiseZ(); /** * Decreases the "height" of the selected items. */ void lowerZ(); /** * Brings up a file dialog requesting the location of the file to export * to, and then exports an image of the canvas. */ void exportToImage(); protected: void exportToImageDraw( const QRect & saveArea, QPaintDevice &pDev); public slots: /** * Deletes whatever is selected. */ virtual void deleteSelection() {}; /** * Called when the user presses Escape (or similar) */ void cancelCurrentOperation(); /** * Sets the y-positions of the selected items to the average of the * initial y-positions. */ // TODO: decide whether these should be moved to ICNdocument... void alignHorizontally(); /** * Sets the x-positions of the selected items to the average of the * initial x-positions. */ void alignVertically(); /** * Averages out the horizontal spacing between the selected items. */ void distributeHorizontally(); /** * Averages out the vertical spacing between the selected items. */ void distributeVertically(); /** * Adds an items not in the Z ordering to the ordering, and removes any * items from the Z ordering if they have parents. Then, calls all items * found in the ordering to tell them their Z position. */ // ################## void slotUpdateZOrdering(); /** * Call this with ItemDocument::DrawAction to start drawing the given thing */ void slotSetDrawAction( QAction *selected ); /** * Sets the editing mode to repeatedly creating a CNItem * with the given id. Usually called when the user double-clicks on * the component box. */ void slotSetRepeatedItemId( const QString &id ); /** * Unsets the editing mode from repeatedly creating a CNItem */ void slotUnsetRepeatedItemId(); /** * Called when the user changes the configuration. * This, for example, will tell the CNItems on the canvas to update * their configuration. */ void slotUpdateConfiguration() override; /** * Enables / disables / selects various actions depending on * what is selected or not. */ virtual void slotInitItemActions(); /** * Process queued events (see ItemDocument::ItemDocumentEvent). */ void processItemDocumentEvents(); signals: /** * Emitted when the selection changes. */ void selectionChanged(); protected slots: /** * Called after the canvas is resized to set the scrollbars of the * ItemViews to either always show or always hidden. */ void updateItemViewScrollbars(); protected: /** * Called from registerItem when a new item is added. */ virtual void itemAdded( Item * item ); void handleNewView( View *view ) override; /** * Set to true to remove buttons and grid and so on from the canvas, set false to put them back */ void setSVGExport( bool svgExport ); void writeFile(); /** * Reinherit this if you want to add any options to the right-click context */ virtual void fillContextMenu( const QPoint & pos ); /** * Reads the background settings (grid-colour, underlying colour) from the Config settings, * and generates the background pixmap from those settings */ void updateBackground(); /** * Sets the canvas size to both (a) containing all items present on the * canvas, and (b) no smaller than the smallest view of the canvas. This * function should only be called by processItemDocumentEvents - a resize * request must be made with requestEvent. */ void resizeCanvasToItems(); Canvas *m_canvas; CMManager *m_cmManager; CanvasTip *m_canvasTip; ItemList m_itemDeleteList; ItemMap m_itemList; QString m_fileExtensionInfo; // For displaying in the save file dialog private: /** * This clears a given stack and deletes all pointers, but the one to m_currentState. */ void cleanClearStack( IDDStack &stack ); static int m_nextActionTicket; unsigned m_queuedEvents; // OR'ed together list of ItemDocumentEvent::type unsigned m_nextIdNum; int m_currentActionTicket; bool m_bIsLoading; ItemDocumentData *m_currentState; ItemDocumentData *m_savedState; // Pointer to the document data that holds the state when it saved KActionMenu *m_pAlignmentAction; IntItemMap m_zOrder; std::set m_idList; // used to ensure unique IDs to try to make sure save files are valid. QTimer *m_pEventTimer; QTimer *m_pUpdateItemViewScrollbarsTimer; IDDStack m_undoStack; IDDStack m_redoStack; friend class ICNView; friend class ItemView; }; /** @author David Saxton */ class Canvas : public KtlQCanvas { Q_OBJECT public: Canvas( ItemDocument *itemDocument, const char * name = nullptr ); /** * Sets a message to be displayed on the canvas for a brief period of * time. If this is called with an empty message, then any existing * message will be removed. */ void setMessage( const QString & message ); void update() override; void resize( const QRect & size ) override; signals: /** * Emitted when the canvas rectangle-size changes. */ void resized( const QRect & oldSize, const QRect & newSize ); public slots: void slotSetAllChanged() { setAllChanged(); } protected: void drawBackground ( QPainter & painter, const QRect & clip ) override; void drawForeground ( QPainter & painter, const QRect & clip ) override; ItemDocument *p_itemDocument; QString m_message; QTimer * m_pMessageTimeout; }; /** @author David Saxton */ class CanvasTip : public KtlQCanvasRectangle { public: CanvasTip( ItemDocument *itemDocument, KtlQCanvas *qcanvas ); ~CanvasTip() override; void displayVI( ECNode *node, const QPoint &pos ); void displayVI( Connector *connector, const QPoint &pos ); protected: void draw( QPainter &p ) override; void setText( const QString & text ); bool updateVI(); void display( const QPoint &pos ); QString displayText( unsigned num ) const; QVector m_v; QVector m_i; ItemDocument *p_itemDocument; QString m_text; }; #endif diff --git a/src/itemdocumentdata.cpp b/src/itemdocumentdata.cpp index 94b9de54..0ed4ce96 100644 --- a/src/itemdocumentdata.cpp +++ b/src/itemdocumentdata.cpp @@ -1,1396 +1,1397 @@ /*************************************************************************** * Copyright (C) 2005 by David Saxton * * david@bluehaze.org * * * * 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 "connector.h" #include "junctionnode.h" #include "ecsubcircuit.h" #include "electronicconnector.h" #include "flowcodedocument.h" #include "flowconnector.h" #include "flowcontainer.h" #include "junctionflownode.h" #include "itemdocumentdata.h" #include "itemlibrary.h" #include "picitem.h" #include "pinmapping.h" -#include #include #include #include #include + +#include #include #include -#include -#include +#include +#include // Converts the QBitArray into a string (e.g. "F289A9E") that can be stored in an xml file static QString toAsciiHex( QBitArray _data ) { QBitArray data = _data; // data = qCompress(data); // Pad out the data to a nice size if ( (data.size() % 4) != 0 ) { data.detach(); data.resize( data.size() + 4 - (data.size()%4) ); } QString text; for ( int i = 0; i < data.size()/4; ++i ) { unsigned val = 0; for ( unsigned j = 0; j < 4; ++j ) val += (data[4*i+j] ? 1:0) << j; text += QString::number( val, 16 ); } return text; } // Converts a string (e.g. "F289A9E") into a QBitArray, the opposite of the above function static QBitArray toQBitArray( QString text ) { unsigned size = text.length(); QBitArray data(size*4); for ( unsigned i = 0; i < size; ++i ) { unsigned val = QString(text[i]).toInt( nullptr, 16 ); for ( unsigned j = 0; j < 4; ++j ) data[4*i+j] = val & (1 << j); } // data = qUncompress(data); return data; } //BEGIN class ItemDocumentData ItemDocumentData::ItemDocumentData( uint documentType ) { reset(); m_documentType = documentType; } ItemDocumentData::~ItemDocumentData() { } void ItemDocumentData::reset() { m_itemDataMap.clear(); m_connectorDataMap.clear(); m_nodeDataMap.clear(); m_microData.reset(); m_documentType = Document::dt_none; } bool ItemDocumentData::loadData( const QUrl &url ) { QScopedPointer file; if (!url.isLocalFile()) { QScopedPointer downloadedFile(new QTemporaryFile()); downloadedFile->open(); KIO::FileCopyJob* job = KIO::file_copy(url, QUrl::fromLocalFile(downloadedFile->fileName())); KJobWidgets::setWindow(job, nullptr); if (!job->exec()) { KMessageBox::error( nullptr, job->errorString() ); return false; } file.reset(downloadedFile.take()); } else { QScopedPointer localFile(new QFile(url.toLocalFile())); if ( !localFile->open( QIODevice::ReadOnly ) ) { KMessageBox::sorry( nullptr, i18n("Could not open %1 for reading", localFile->fileName()) ); return false; } file.reset(localFile.take()); } QString xml; QTextStream textStream( file.data() ); while ( !textStream.atEnd() /* eof() */ ) xml += textStream.readLine() + '\n'; return fromXML(xml); } bool ItemDocumentData::fromXML( const QString &xml ) { reset(); QDomDocument doc( "KTechlab" ); QString errorMessage; if ( !doc.setContent( xml, &errorMessage ) ) { KMessageBox::sorry( nullptr, i18n("Could not parse XML:\n%1", errorMessage) ); return false; } QDomElement root = doc.documentElement(); QDomNode node = root.firstChild(); while ( !node.isNull() ) { QDomElement element = node.toElement(); if ( !element.isNull() ) { const QString tagName = element.tagName(); if ( tagName == "item" ) elementToItemData(element); else if ( tagName == "node" ) elementToNodeData(element); else if ( tagName == "connector" ) elementToConnectorData(element); else if ( tagName == "pic-settings" || tagName == "micro" ) elementToMicroData(element); else if ( tagName == "code" ) ; // do nothing - we no longer use this tag else qWarning() << Q_FUNC_INFO << "Unrecognised element tag name: "<exec()) { KMessageBox::error( nullptr, job->errorString() ); return false; } } return true; } QString ItemDocumentData::toXML() { QDomDocument doc("KTechlab"); //TODO Add revision information to save file QDomElement root = doc.createElement("document"); root.setAttribute( "type", documentTypeString() ); doc.appendChild(root); { const ItemDataMap::iterator end = m_itemDataMap.end(); for ( ItemDataMap::iterator it = m_itemDataMap.begin(); it != end; ++it ) { QDomElement node = itemDataToElement( doc, it.value() ); node.setAttribute( "id", it.key() ); root.appendChild(node); } } { const ConnectorDataMap::iterator end = m_connectorDataMap.end(); for ( ConnectorDataMap::iterator it = m_connectorDataMap.begin(); it != end; ++it ) { QDomElement node = connectorDataToElement( doc, it.value() ); node.setAttribute( "id", it.key() ); root.appendChild(node); } } { const NodeDataMap::iterator end = m_nodeDataMap.end(); for ( NodeDataMap::iterator it = m_nodeDataMap.begin(); it != end; ++it ) { QDomElement node = nodeDataToElement( doc, it.value() ); node.setAttribute( "id", it.key() ); root.appendChild(node); } } if ( m_documentType == Document::dt_flowcode ) { QDomElement node = microDataToElement(doc); root.appendChild(node); } return doc.toString(); } //BEGIN functions for generating / reading QDomElements QDomElement ItemDocumentData::microDataToElement( QDomDocument &doc ) { QDomElement node = doc.createElement("micro"); node.setAttribute( "id", m_microData.id ); { const PinMappingMap::iterator end = m_microData.pinMappings.end(); for ( PinMappingMap::iterator it = m_microData.pinMappings.begin(); it != end; ++it ) { QDomElement pinMapNode = doc.createElement("pinmap"); QString type; switch ( it.value().type() ) { case PinMapping::SevenSegment: type = "sevensegment"; break; case PinMapping::Keypad_4x3: type = "keypad_4x3"; break; case PinMapping::Keypad_4x4: type = "keypad_4x4"; break; case PinMapping::Invalid: break; } pinMapNode.setAttribute( "id", it.key() ); pinMapNode.setAttribute( "type", type ); pinMapNode.setAttribute( "map", it.value().pins().join(" ") ); node.appendChild(pinMapNode); } } { const PinDataMap::iterator end = m_microData.pinMap.end(); for ( PinDataMap::iterator it = m_microData.pinMap.begin(); it != end; ++it ) { QDomElement pinNode = doc.createElement("pin"); pinNode.setAttribute( "id", it.key() ); pinNode.setAttribute( "type", (it.value().type == PinSettings::pt_input) ? "input" : "output" ); pinNode.setAttribute( "state", (it.value().state == PinSettings::ps_off) ? "off" : "on" ); node.appendChild(pinNode); } } { const QStringMap::iterator end = m_microData.variableMap.end(); for ( QStringMap::iterator it = m_microData.variableMap.begin(); it != end; ++it ) { QDomElement variableNode = doc.createElement("variable"); variableNode.setAttribute( "name", it.key() ); variableNode.setAttribute( "value", it.value() ); node.appendChild(variableNode); } } return node; } void ItemDocumentData::elementToMicroData( QDomElement element ) { QString id = element.attribute( "id", QString::null ); if ( id.isNull() ) id = element.attribute( "pic", QString::null ); if ( id.isNull() ) { qCritical() << Q_FUNC_INFO << "Could not find id in element" << endl; return; } m_microData.reset(); m_microData.id = id; QDomNode node = element.firstChild(); while ( !node.isNull() ) { QDomElement childElement = node.toElement(); if ( !childElement.isNull() ) { const QString tagName = childElement.tagName(); if ( tagName == "pinmap" ) { QString id = childElement.attribute( "id", QString::null ); QString typeString = childElement.attribute( "type", QString::null ); if ( !id.isEmpty() && !typeString.isEmpty() ) { PinMapping::Type type = PinMapping::Invalid; if ( typeString == "sevensegment" ) type = PinMapping::SevenSegment; else if ( typeString == "keypad_4x3" ) type = PinMapping::Keypad_4x3; else if ( typeString == "keypad_4x4" ) type = PinMapping::Keypad_4x4; PinMapping pinMapping( type ); //pinMapping.setPins( QStringList::split( " ", childElement.attribute( "map", 0 ) ) ); // 2018.12.01 pinMapping.setPins( childElement.attribute( "map", nullptr ).split( " ", QString::SkipEmptyParts ) ); m_microData.pinMappings[id] = pinMapping; } } else if ( tagName == "pin" ) { QString pinID = childElement.attribute( "id", QString::null ); if ( !pinID.isEmpty() ) { m_microData.pinMap[pinID].type = (childElement.attribute( "type", "input" ) == "input" ) ? PinSettings::pt_input : PinSettings::pt_output; m_microData.pinMap[pinID].state = (childElement.attribute( "state", "off" ) == "off" ) ? PinSettings::ps_off : PinSettings::ps_on; } } else if ( tagName == "variable" ) { QString variableId = childElement.attribute( "name", QString::null ); m_microData.variableMap[variableId] = childElement.attribute( "value", QString::null ); } else qCritical() << Q_FUNC_INFO << "Unrecognised element tag name: "<= 0, then set by a FlowPart, so we don't need to worry about the angle / flip if ( itemData.orientation >= 0 ) { node.setAttribute( "orientation", itemData.orientation ); } else { node.setAttribute( "angle", itemData.angleDegrees ); node.setAttribute( "flip", itemData.flipped ); } if ( !itemData.parentId.isEmpty() ) node.setAttribute( "parent", itemData.parentId ); const QStringMap::const_iterator stringEnd = itemData.dataString.end(); for ( QStringMap::const_iterator it = itemData.dataString.begin(); it != stringEnd; ++it ) { QDomElement e = doc.createElement("data"); node.appendChild(e); e.setAttribute( "id", it.key() ); e.setAttribute( "type", "string" ); e.setAttribute( "value", it.value() ); } const DoubleMap::const_iterator numberEnd = itemData.dataNumber.end(); for ( DoubleMap::const_iterator it = itemData.dataNumber.begin(); it != numberEnd; ++it ) { QDomElement e = doc.createElement("data"); node.appendChild(e); e.setAttribute( "id", it.key() ); e.setAttribute( "type", "number" ); e.setAttribute( "value", QString::number(it.value()) ); } const QColorMap::const_iterator colorEnd = itemData.dataColor.end(); for ( QColorMap::const_iterator it = itemData.dataColor.begin(); it != colorEnd; ++it ) { QDomElement e = doc.createElement("data"); node.appendChild(e); e.setAttribute( "id", it.key() ); e.setAttribute( "type", "color" ); e.setAttribute( "value", it.value().name() ); } const QBitArrayMap::const_iterator rawEnd = itemData.dataRaw.end(); for ( QBitArrayMap::const_iterator it = itemData.dataRaw.begin(); it != rawEnd; ++it ) { QDomElement e = doc.createElement("data"); node.appendChild(e); e.setAttribute( "id", it.key() ); e.setAttribute( "type", "raw" ); e.setAttribute( "value", toAsciiHex(it.value()) ); } const BoolMap::const_iterator boolEnd = itemData.dataBool.end(); for ( BoolMap::const_iterator it = itemData.dataBool.begin(); it != boolEnd; ++it ) { QDomElement e = doc.createElement("data"); node.appendChild(e); e.setAttribute( "id", it.key() ); e.setAttribute( "type", "bool" ); e.setAttribute( "value", QString::number(it.value()) ); } const BoolMap::const_iterator buttonEnd = itemData.buttonMap.end(); for ( BoolMap::const_iterator it = itemData.buttonMap.begin(); it != buttonEnd; ++it ) { QDomElement e = doc.createElement("button"); node.appendChild(e); e.setAttribute( "id", it.key() ); e.setAttribute( "state", QString::number(it.value()) ); } const IntMap::const_iterator sliderEnd = itemData.sliderMap.end(); for ( IntMap::const_iterator it = itemData.sliderMap.begin(); it != sliderEnd; ++it ) { QDomElement e = doc.createElement("slider"); node.appendChild(e); e.setAttribute( "id", it.key() ); e.setAttribute( "value", QString::number(it.value()) ); } return node; } void ItemDocumentData::elementToItemData( QDomElement element ) { QString id = element.attribute( "id", QString::null ); if ( id.isNull() ) { qCritical() << Q_FUNC_INFO << "Could not find id in element" << endl; return; } ItemData itemData; itemData.type = element.attribute( "type", QString::null ); itemData.x = element.attribute( "x", "120" ).toInt(); itemData.y = element.attribute( "y", "120" ).toInt(); itemData.z = element.attribute( "z", "-1" ).toInt(); if ( element.hasAttribute("width") && element.hasAttribute("height") ) { itemData.setSize = true; itemData.size = QRect( element.attribute( "offset-x", "0" ).toInt(), element.attribute( "offset-y", "0" ).toInt(), element.attribute( "width", "120" ).toInt(), element.attribute( "height", "120" ).toInt() ); } else itemData.setSize = false; itemData.angleDegrees = element.attribute( "angle", "0" ).toInt(); itemData.flipped = element.attribute( "flip", "0" ).toInt(); itemData.orientation = element.attribute( "orientation", "-1" ).toInt(); itemData.parentId = element.attribute( "parent", QString::null ); m_itemDataMap[id] = itemData; QDomNode node = element.firstChild(); while ( !node.isNull() ) { QDomElement childElement = node.toElement(); if ( !childElement.isNull() ) { const QString tagName = childElement.tagName(); if ( tagName == "item" ) { // We're reading in a file saved in the older format, with // child items nestled, so we must specify that the new item // has the currently parsed item as its parent. elementToItemData(childElement); QString childId = childElement.attribute( "id", QString::null ); if ( !childId.isNull() ) m_itemDataMap[childId].parentId = id; } else if ( tagName == "data" ) { QString dataId = childElement.attribute( "id", QString::null ); if ( !dataId.isNull() ) { QString dataType = childElement.attribute( "type", QString::null ); QString value = childElement.attribute( "value", QString::null ); if ( dataType == "string" || dataType == "multiline" ) m_itemDataMap[id].dataString[dataId] = value; else if ( dataType == "number" ) m_itemDataMap[id].dataNumber[dataId] = value.toDouble(); else if ( dataType == "color" ) m_itemDataMap[id].dataColor[dataId] = QColor(value); else if ( dataType == "raw" ) m_itemDataMap[id].dataRaw[dataId] = toQBitArray(value); else if ( dataType == "bool" ) m_itemDataMap[id].dataBool[dataId] = bool(value.toInt()); else qCritical() << Q_FUNC_INFO << "Unknown data type of \""<itemList() ); if ( ICNDocument *icnd = dynamic_cast(itemDocument) ) { addConnectors( icnd->connectorList() ); addNodes( icnd->nodeList() ); if ( FlowCodeDocument *fcd = dynamic_cast(itemDocument) ) { if ( fcd->microSettings() ) setMicroData( fcd->microSettings()->microData() ); } } m_documentType = itemDocument->type(); } void ItemDocumentData::generateUniqueIDs( ItemDocument *itemDocument ) { if (!itemDocument) return; QStringMap replaced; replaced[""] = QString::null; replaced[QString::null] = QString::null; ItemDataMap newItemDataMap; ConnectorDataMap newConnectorDataMap; NodeDataMap newNodeDataMap; //BEGIN Go through and replace the old ids { const ItemDataMap::iterator end = m_itemDataMap.end(); for ( ItemDataMap::iterator it = m_itemDataMap.begin(); it != end; ++it ) { if ( !replaced.contains( it.key() ) ) replaced[it.key()] = itemDocument->generateUID(it.key()); newItemDataMap[replaced[it.key()]] = it.value(); } } { const NodeDataMap::iterator end = m_nodeDataMap.end(); for ( NodeDataMap::iterator it = m_nodeDataMap.begin(); it != end; ++it ) { if ( !replaced.contains( it.key() ) ) replaced[it.key()] = itemDocument->generateUID(it.key()); newNodeDataMap[replaced[it.key()]] = it.value(); } } { const ConnectorDataMap::iterator end = m_connectorDataMap.end(); for ( ConnectorDataMap::iterator it = m_connectorDataMap.begin(); it != end; ++it ) { if ( !replaced.contains( it.key() ) ) replaced[it.key()] = itemDocument->generateUID(it.key()); newConnectorDataMap[replaced[it.key()]] = it.value(); } } //END Go through and replace the old ids //BEGIN Go through and replace the internal references to the ids { const ItemDataMap::iterator end = newItemDataMap.end(); for ( ItemDataMap::iterator it = newItemDataMap.begin(); it != end; ++it ) { it.value().parentId = replaced[it.value().parentId]; } } { const ConnectorDataMap::iterator end = newConnectorDataMap.end(); for ( ConnectorDataMap::iterator it = newConnectorDataMap.begin(); it != end; ++it ) { it.value().startNodeParent = replaced[it.value().startNodeParent]; it.value().endNodeParent = replaced[it.value().endNodeParent]; it.value().startNodeId = replaced[it.value().startNodeId]; it.value().endNodeId = replaced[it.value().endNodeId]; } } //END Go through and replace the internal references to the ids m_itemDataMap = newItemDataMap; m_connectorDataMap = newConnectorDataMap; m_nodeDataMap = newNodeDataMap; } void ItemDocumentData::translateContents( int dx, int dy ) { //BEGIN Go through and replace the old ids { const ItemDataMap::iterator end = m_itemDataMap.end(); for ( ItemDataMap::iterator it = m_itemDataMap.begin(); it != end; ++it ) { it.value().x += dx; it.value().y += dx; } } { const NodeDataMap::iterator end = m_nodeDataMap.end(); for ( NodeDataMap::iterator it = m_nodeDataMap.begin(); it != end; ++it ) { it.value().x += dx; it.value().y += dy; } } { const ConnectorDataMap::iterator end = m_connectorDataMap.end(); for ( ConnectorDataMap::iterator it = m_connectorDataMap.begin(); it != end; ++it ) { const QPointList::iterator routeEnd = it.value().route.end(); for ( QPointList::iterator routeIt = it.value().route.begin(); routeIt != routeEnd; ++routeIt ) { *routeIt += QPoint( dx/8, dy/8 ); } } } } void ItemDocumentData::restoreDocument( ItemDocument *itemDocument ) { if ( !itemDocument ) return; ICNDocument *icnd = dynamic_cast(itemDocument); FlowCodeDocument *fcd = dynamic_cast(icnd); if ( fcd && !m_microData.id.isEmpty() ) { fcd->setPicType(m_microData.id); fcd->microSettings()->restoreFromMicroData(m_microData); } mergeWithDocument(itemDocument,false); { ItemList removeItems = itemDocument->itemList(); removeItems.removeAll((Item*)nullptr); const ItemDataMap::iterator end = m_itemDataMap.end(); for ( ItemDataMap::iterator it = m_itemDataMap.begin(); it != end; ++it ) removeItems.removeAll( itemDocument->itemWithID(it.key()) ); const ItemList::iterator removeEnd = removeItems.end(); for ( ItemList::iterator it = removeItems.begin(); it != removeEnd; ++it ) { if ( (*it)->canvas() && (*it)->type() != PicItem::typeString() ) (*it)->removeItem(); } } if (icnd) { { NodeList removeNodes = icnd->nodeList(); removeNodes.removeAll((Node*)nullptr); const NodeDataMap::iterator end = m_nodeDataMap.end(); for ( NodeDataMap::iterator it = m_nodeDataMap.begin(); it != end; ++it ) removeNodes.removeAll( icnd->nodeWithID( it.key() ) ); const NodeList::iterator removeEnd = removeNodes.end(); for ( NodeList::iterator it = removeNodes.begin(); it != removeEnd; ++it ) { if ( (*it)->canvas() && !(*it)->isChildNode() ) (*it)->removeNode(); } } { ConnectorList removeConnectors = icnd->connectorList(); removeConnectors.removeAll((Connector*)nullptr); const ConnectorDataMap::iterator end = m_connectorDataMap.end(); for ( ConnectorDataMap::iterator it = m_connectorDataMap.begin(); it != end; ++it ) removeConnectors.removeAll( icnd->connectorWithID(it.key()) ); const ConnectorList::iterator removeEnd = removeConnectors.end(); for ( ConnectorList::iterator it = removeConnectors.begin(); it != removeEnd; ++it ) { if ( (*it)->canvas() ) (*it)->removeConnector(); } } } itemDocument->flushDeleteList(); } void ItemDocumentData::mergeWithDocument( ItemDocument *itemDocument, bool selectNew ) { if ( !itemDocument ) return; ICNDocument *icnd = dynamic_cast(itemDocument); //BEGIN Restore Nodes if (icnd) { const NodeDataMap::iterator nodeEnd = m_nodeDataMap.end(); for ( NodeDataMap::iterator it = m_nodeDataMap.begin(); it != nodeEnd; ++it ) { if ( !icnd->nodeWithID( it.key() ) ) { QString id = it.key(); if ( itemDocument->type() == Document::dt_circuit ) new JunctionNode( icnd, 270, QPoint( int(it.value().x), int(it.value().y) ), &id ); else if ( itemDocument->type() == Document::dt_flowcode ) new JunctionFlowNode( icnd, 270, QPoint( int(it.value().x), int(it.value().y) ), &id ); } } for ( NodeDataMap::iterator it = m_nodeDataMap.begin(); it != nodeEnd; ++it ) { Node *node = icnd->nodeWithID( it.key() ); if (node) node->move( it.value().x, it.value().y ); } } //END Restore Nodes //BEGIN Restore items const ItemDataMap::iterator itemEnd = m_itemDataMap.end(); for ( ItemDataMap::iterator it = m_itemDataMap.begin(); it != itemEnd; ++it ) { if ( !it.value().type.isEmpty() && !itemDocument->itemWithID( it.key() ) ) { Item *item = itemLibrary()->createItem( it.value().type, itemDocument, false, it.key().toLatin1().data(), false ); if ( item && !itemDocument->isValidItem(item) ) { qWarning() << "Attempted to create invalid item with id: " << it.key() << endl; item->removeItem(); itemDocument->flushDeleteList(); item = nullptr; } if (item) { //HACK We move the item now before restoreFromItemData is called later, in case it is to be parented //(as we don't want to move children)... item->move( it.value().x, it.value().y ); } } } for ( ItemDataMap::iterator it = m_itemDataMap.begin(); it != itemEnd; ++it ) { Item *item = itemDocument->itemWithID(it.key()); if (!item) continue; item->restoreFromItemData( it.value() ); item->finishedCreation(); if (selectNew) itemDocument->select(item); item->show(); } //END Restore Items //BEGIN Restore Connectors if (icnd) { const ConnectorDataMap::iterator connectorEnd = m_connectorDataMap.end(); for ( ConnectorDataMap::iterator it = m_connectorDataMap.begin(); it != connectorEnd; ++it ) { if ( icnd->connectorWithID( it.key() ) ) continue; QString id = it.key(); Node *startNode = nullptr; Node *endNode = nullptr; if ( it.value().startNodeIsChild ) { CNItem *item = icnd->cnItemWithID( it.value().startNodeParent ); if (!item) qCritical() << Q_FUNC_INFO << "Unable to find node parent with id: "<childNode( it.value().startNodeCId ); } else startNode = icnd->nodeWithID( it.value().startNodeId ); if ( it.value().endNodeIsChild ) { CNItem *item = icnd->cnItemWithID( it.value().endNodeParent ); if (!item) qCritical() << Q_FUNC_INFO << "Unable to find node parent with id: "<childNode( it.value().endNodeCId ); } else endNode = icnd->nodeWithID( it.value().endNodeId ); if ( !startNode || !endNode ) { qCritical() << Q_FUNC_INFO << "End and start nodes for the connector do not both exist" << endl; } else { Connector *connector; // HACK // FIXME // TODO // for some strange reason the lists in the ItemDocument class the ID lists for items // get out of sync, so some id's are considered to be registered, but in fact they // have no assiciated items; this causes stange bugs when insterting subcircuits in the circuit. // this is just a temporary fix; someone should get to the real cause of this problem and fix // ItemDocument if ( icnd->connectorWithID( id ) ) { qWarning() << "Unregistering connector with ID: " << id << ". This should not delete any of your connections!" << endl; } icnd->unregisterUID(id); // FIXME ICNDocument->type() used // FIXME tons of dynamic_cast if(( icnd->type() == Document::dt_circuit ) || ( icnd->type() == Document::dt_pinMapEditor )) { connector = new ElectronicConnector( dynamic_cast(startNode), dynamic_cast(endNode), icnd, &id ); (dynamic_cast(startNode))->addConnector(connector); (dynamic_cast(endNode))->addConnector(connector); } else { connector = new FlowConnector( dynamic_cast(startNode), dynamic_cast(endNode), icnd, &id ); (dynamic_cast(startNode))->addOutputConnector(connector); (dynamic_cast(endNode))->addInputConnector(connector); } } } for ( ConnectorDataMap::iterator it = m_connectorDataMap.begin(); it != connectorEnd; ++it ) { Connector *connector = icnd->connectorWithID( it.key() ); if (connector) { connector->restoreFromConnectorData( it.value() ); if (selectNew) icnd->select(connector); } } } //END Restore Connectors // This is kind of hackish, but never mind if ( FlowCodeDocument *fcd = dynamic_cast(itemDocument) ) { const ItemList fcdItems = fcd->itemList(); const ItemList::const_iterator fcdItemsEnd = fcdItems.constEnd(); for ( ItemList::const_iterator it = fcdItems.constBegin(); it != fcdItemsEnd; ++it ) { if ( FlowContainer * fc = dynamic_cast((Item*)*it) ) fc->updateContainedVisibility(); } } } void ItemDocumentData::setMicroData( const MicroData &data ) { m_microData = data; } void ItemDocumentData::addItems( const ItemList &itemList ) { const ItemList::const_iterator end = itemList.constEnd(); for ( ItemList::const_iterator it = itemList.constBegin(); it != end; ++it ) { if ( *it && (*it)->canvas() && (*it)->type() != PicItem::typeString() ) addItemData( (*it)->itemData(), (*it)->id() ); } } void ItemDocumentData::addConnectors( const ConnectorList &connectorList ) { const ConnectorList::const_iterator end = connectorList.constEnd(); for ( ConnectorList::const_iterator it = connectorList.constBegin(); it != end; ++it ) { if ( *it && (*it)->canvas() ) { if ( (*it)->startNode() && (*it)->endNode() ) addConnectorData( (*it)->connectorData(), (*it)->id() ); else qDebug() << Q_FUNC_INFO << " *it="<<*it<<" (*it)->startNode()="<<(*it)->startNode()<<" (*it)->endNode()="<<(*it)->endNode()<canvas() && !(*it)->isChildNode() ) addNodeData( (*it)->nodeData(), (*it)->id() ); } } void ItemDocumentData::addItemData( ItemData itemData, QString id ) { if ( m_itemDataMap.contains( id ) ) { qWarning() << "Overwriting item: " << id << endl; } m_itemDataMap[id] = itemData; } void ItemDocumentData::addConnectorData( ConnectorData connectorData, QString id ) { if ( m_connectorDataMap.contains( id ) ) { qWarning() << "Overwriting connector: " << id << endl; } m_connectorDataMap[id] = connectorData; } void ItemDocumentData::addNodeData( NodeData nodeData, QString id ) { if ( m_nodeDataMap.contains( id ) ) { qWarning() << "Overwriting node: " << id << endl; } m_nodeDataMap[id] = nodeData; } //END class ItemDocumentData //BEGIN class ItemData ItemData::ItemData() { x = 0; y = 0; z = -1; angleDegrees = 0; flipped = false; orientation = -1; setSize = false; } //END class ItemData //BEGIN class ConnectorData ConnectorData::ConnectorData() { manualRoute = false; startNodeIsChild = false; endNodeIsChild = false; } //END class ConnectorData //BEGIN class NodeData NodeData::NodeData() { x = 0; y = 0; } //END class NodeDaata //BEGIN class PinData PinData::PinData() { type = PinSettings::pt_input; state = PinSettings::ps_off; } //END class PinData //BEGIN class MicroData MicroData::MicroData() { } void MicroData::reset() { id = QString::null; pinMap.clear(); } //END class MicroData //BEGIN class SubcircuitData SubcircuitData::SubcircuitData() : ItemDocumentData( Document::dt_circuit ) { } void SubcircuitData::initECSubcircuit( ECSubcircuit * ecSubcircuit ) { if (!ecSubcircuit) return; generateUniqueIDs( ecSubcircuit->itemDocument() ); // Generate a list of the External Connections, sorting by x coordinate std::multimap< double, QString > extCon; ItemDataMap::iterator itemEnd = m_itemDataMap.end(); for ( ItemDataMap::iterator it = m_itemDataMap.begin(); it != itemEnd; ++it ) { if ( it.value().type == "ec/external_connection" ) extCon.insert( std::make_pair( it.value().x, it.key() ) ); } // How many external connections do we have? ecSubcircuit->setNumExtCon(extCon.size()); // Sort the connections into the pins of the subcircuit by y coordinate std::multimap< double, QString > leftPins; std::multimap< double, QString > rightPins; int at = 0; int size = (extCon.size()/2) + (extCon.size()%2); const std::multimap< double, QString >::iterator extConEnd = extCon.end(); for ( std::multimap< double, QString >::iterator it = extCon.begin(); it != extConEnd; ++it ) { if ( at < size ) leftPins.insert( std::make_pair( m_itemDataMap[it->second].y, it->second ) ); else rightPins.insert( std::make_pair( m_itemDataMap[it->second].y, it->second ) ); at++; } // Remove the external connections (recording their names and associated numerical position) int nodeId = 0; typedef QMap IntMap; IntMap nodeMap; const std::multimap< double, QString >::iterator leftPinsEnd = leftPins.end(); for ( std::multimap< double, QString >::iterator it = leftPins.begin(); it != leftPinsEnd; ++it ) { nodeMap[ it->second ] = nodeId; ecSubcircuit->setExtConName( nodeId, m_itemDataMap[ it->second ].dataString["name"] ); nodeId++; m_itemDataMap.remove( it->second ); } nodeId = extCon.size()-1; const std::multimap< double, QString >::iterator rightPinsEnd = rightPins.end(); for ( std::multimap< double, QString >::iterator it = rightPins.begin(); it != rightPinsEnd; ++it ) { nodeMap[ it->second ] = nodeId; ecSubcircuit->setExtConName( nodeId, m_itemDataMap[ it->second ].dataString["name"] ); nodeId--; m_itemDataMap.remove( it->second ); } // Replace connector references to the old External Connectors to the nodes const ConnectorDataMap::iterator connectorEnd = m_connectorDataMap.end(); for ( ConnectorDataMap::iterator it = m_connectorDataMap.begin(); it != connectorEnd; ++it ) { if ( it.value().startNodeIsChild && nodeMap.contains(it.value().startNodeParent ) ) { it.value().startNodeCId = QString::number( nodeMap[it.value().startNodeParent] ); it.value().startNodeParent = ecSubcircuit->id(); } if ( it.value().endNodeIsChild && nodeMap.contains(it.value().endNodeParent ) ) { it.value().endNodeCId = QString::number( nodeMap[it.value().endNodeParent] ); it.value().endNodeParent = ecSubcircuit->id(); } } // Create all the new stuff mergeWithDocument( ecSubcircuit->itemDocument(), false ); // Parent and hide the new stuff itemEnd = m_itemDataMap.end(); for ( ItemDataMap::iterator it = m_itemDataMap.begin(); it != itemEnd; ++it) { Component * component = static_cast(ecSubcircuit->itemDocument()->itemWithID( it.key() )); if (component) { component->setParentItem(ecSubcircuit); component->updateConnectorPoints(false); component->setVisible(false); component->setCanvas(nullptr); ecSubcircuit->connect( ecSubcircuit, SIGNAL(subcircuitDeleted()), component, SLOT(removeItem()) ); } } for ( ConnectorDataMap::iterator it = m_connectorDataMap.begin(); it != connectorEnd; ++it ) { Connector * connector = (static_cast(ecSubcircuit->itemDocument()))->connectorWithID( it.key() ); if (connector) { connector->updateConnectorPoints(false); connector->setVisible(false); connector->setCanvas(nullptr); ecSubcircuit->connect( ecSubcircuit, SIGNAL(subcircuitDeleted()), connector, SLOT(removeConnector()) ); } } const NodeDataMap::iterator nodeEnd = m_nodeDataMap.end(); for ( NodeDataMap::iterator it = m_nodeDataMap.begin(); it != nodeEnd; ++it ) { Node * node = (static_cast(ecSubcircuit->itemDocument()))->nodeWithID( it.key() ); if (node) { node->setVisible(false); node->setCanvas(nullptr); ecSubcircuit->connect( ecSubcircuit, SIGNAL(subcircuitDeleted()), node, SLOT(removeNode()) ); } } ecSubcircuit->doneSCInit(); } //END class SubcircuitData diff --git a/src/itemdocumentdata.h b/src/itemdocumentdata.h index 1f9e3988..caf87f8b 100644 --- a/src/itemdocumentdata.h +++ b/src/itemdocumentdata.h @@ -1,238 +1,239 @@ /*************************************************************************** * Copyright (C) 2005 by David Saxton * * david@bluehaze.org * * * * 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. * ***************************************************************************/ #ifndef ITEMDOCUMENTDATA_H #define ITEMDOCUMENTDATA_H #include "item.h" #include "microsettings.h" -#include +#include +#include class Connector; class ECSubcircuit; class QUrl; class Node; class PinMapping; typedef QList > ConnectorList; typedef QList > ItemList; typedef QList > NodeList; typedef QMap< QString, PinMapping > PinMappingMap; typedef QList QPointList; typedef QMap BoolMap; typedef QMap DoubleMap; typedef QMap IntMap; typedef QMap QColorMap; typedef QMap QStringMap; typedef QMap QBitArrayMap; class ItemData { public: ItemData(); QString type; double x; double y; int z; QRect size; bool setSize; int orientation; // used for flowparts, should be set to -1 if not used. double angleDegrees; bool flipped; BoolMap buttonMap; IntMap sliderMap; QString parentId; BoolMap dataBool; DoubleMap dataNumber; QColorMap dataColor; QStringMap dataString; QBitArrayMap dataRaw; }; typedef QMap< QString, ItemData > ItemDataMap; class ConnectorData { public: ConnectorData(); QPointList route; bool manualRoute; bool startNodeIsChild; bool endNodeIsChild; QString startNodeCId; QString endNodeCId; QString startNodeParent; QString endNodeParent; QString startNodeId; QString endNodeId; }; typedef QMap< QString, ConnectorData > ConnectorDataMap; class NodeData { public: NodeData(); double x; double y; }; typedef QMap< QString, NodeData > NodeDataMap; class PinData { public: PinData(); PinSettings::pin_type type; PinSettings::pin_state state; }; typedef QMap< QString, PinData > PinDataMap; class MicroData { public: MicroData(); void reset(); QString id; PinDataMap pinMap; QStringMap variableMap; PinMappingMap pinMappings; }; /** This class encapsulates all or part of an ItemDocument. It is used for writing the document to file / reading from file, as well as for the clipboard and undo/redo system. @author David Saxton */ class ItemDocumentData { public: ItemDocumentData( uint documentType ); ~ItemDocumentData(); /** * Erases / resets all data to defaults */ void reset(); /** * Read in data from a saved file. Any existing data in this class will * be deleted first. * @returns true iff successful */ bool loadData( const QUrl &url ); /** * Write the data to the given file. * @returns true iff successful */ bool saveData( const QUrl &url ); /** * Returns the xml used for describing the data */ QString toXML(); /** * Restore the document from the given xml * @return true if successful */ bool fromXML( const QString &xml ); /** * Saves the document to the data */ void saveDocumentState( ItemDocument *itemDocument ); /** * Restores a document to the state stored in this class */ void restoreDocument( ItemDocument *itemDocument ); /** * Merges the stuff stored here with the given document. If this is * being used for e.g. pasting, you should call generateUniqueIDs() * @param selectNew if true then the newly created items & connectors will be selected */ void mergeWithDocument( ItemDocument *itemDocument, bool selectNew ); /** * Replaces the IDs of everything with unique ones for the document. * Used in pasting. */ void generateUniqueIDs( ItemDocument *itemDocument ); /** * Move all the items, connectors, nodes, etc by the given amount */ void translateContents( int dx, int dy ); /** * Returns the document type. * @see Document::DocumentType */ uint documentType() const { return m_documentType; } //BEGIN functions for adding data void setMicroData( const MicroData &data ); void addItems( const ItemList &itemList ); void addConnectors( const ConnectorList &connectorList ); void addNodes( const NodeList &nodeList ); /** * Add the given ItemData to the stored data */ void addItemData( ItemData itemData, QString id ); /** * Add the given ConnectorData to the stored data */ void addConnectorData( ConnectorData connectorData, QString id ); /** * Add the given NodeData to the stored data */ void addNodeData( NodeData nodeData, QString id ); //END functions for adding data //BEGIN functions for returning strings for saving to xml QString documentTypeString() const; QString revisionString() const; //END functions for returning strings for saving to xml protected: //BEGIN functions for generating QDomElements QDomElement microDataToElement( QDomDocument &doc ); QDomElement itemDataToElement( QDomDocument &doc, const ItemData &itemData ); QDomElement nodeDataToElement( QDomDocument &doc, const NodeData &nodeData ); QDomElement connectorDataToElement( QDomDocument &doc, const ConnectorData &connectorData ); //END functions for generating QDomElements //BEGIN functions for reading QDomElements to stored data void elementToMicroData( QDomElement element ); void elementToItemData( QDomElement element ); void elementToNodeData( QDomElement element ); void elementToConnectorData( QDomElement element ); //END functions for reading QDomElements to stored data ItemDataMap m_itemDataMap; ConnectorDataMap m_connectorDataMap; NodeDataMap m_nodeDataMap; MicroData m_microData; uint m_documentType; // See Document::DocumentType }; class SubcircuitData : public ItemDocumentData { public: SubcircuitData(); void initECSubcircuit( ECSubcircuit * ecSubcircuit ); }; #endif diff --git a/src/itemgroup.cpp b/src/itemgroup.cpp index 219fd6fd..985b0f9b 100644 --- a/src/itemgroup.cpp +++ b/src/itemgroup.cpp @@ -1,323 +1,323 @@ /*************************************************************************** * Copyright (C) 2005 by David Saxton * * david@bluehaze.org * * * * 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 "icndocument.h" #include "item.h" #include "itemgroup.h" #include "mechanicsdocument.h" #include "utils.h" -#include +#include #include ItemGroup::ItemGroup( ItemDocument *view, const char *name ) : QObject( view /*, name */ ) { setObjectName(name); m_activeItem = nullptr; b_itemsAreSameType = true; p_view = view; p_icnDocument = dynamic_cast(p_view); p_mechanicsDocument = dynamic_cast(p_view); QTimer::singleShot( 0, this, SLOT(getViewPtrs()) ); } ItemGroup::~ItemGroup() { } void ItemGroup::getViewPtrs() { p_icnDocument = dynamic_cast(p_view); p_mechanicsDocument = dynamic_cast(p_view); } ItemList ItemGroup::items( bool excludeParentedItems ) const { if (excludeParentedItems) return m_itemList; ItemList items = m_itemList; ItemList parents = m_itemList; uint oldSize = items.size(); do { oldSize = items.size(); ItemList children; ItemList::iterator end = parents.end(); for ( ItemList::iterator it = parents.begin(); it != end; ++it ) children += (*it)->children(); end = children.end(); for ( ItemList::iterator it = children.begin(); it != end; ++it ) { if ( children.count(*it) > 1 ) *it = nullptr; } children.removeAll((Item*)nullptr); items += children; parents = children; } while ( oldSize != items.size() ); return items; } bool ItemGroup::itemsHaveSameDataValue( const QString &id ) const { if ( m_itemList.size() < 1 ) { return true; } if (!itemsAreSameType()) { return false; } ItemList::const_iterator it = m_itemList.begin(); const ItemList::const_iterator end = m_itemList.end(); QVariant firstData = (*it)->property(id)->value(); for ( ++it; it != end; ++it ) { if ( (*it) && (*it)->property(id) && (*it)->property(id)->value() != firstData ) { return false; } } return true; } bool ItemGroup::itemsHaveSameData() const { if ( m_itemList.size() < 1 ) { return true; } if (!itemsAreSameType()) { return false; } VariantDataMap *variantMap = m_itemList.first()->variantMap(); const VariantDataMap::const_iterator vitEnd = variantMap->end(); for ( VariantDataMap::const_iterator vit = variantMap->begin(); vit != vitEnd; ++vit ) { if ( !itemsHaveSameDataValue(vit.key()) ) { return false; } } return true; } bool ItemGroup::itemsHaveDefaultData() const { if (!itemsHaveSameData()) { return false; } if ( m_itemList.size() < 1 ) { return true; } VariantDataMap *variantMap = (*m_itemList.begin())->variantMap(); const VariantDataMap::const_iterator vitEnd = variantMap->end(); for ( VariantDataMap::const_iterator vit = variantMap->begin(); vit != vitEnd; ++vit ) { if ( !vit.value()->isHidden() && vit.value()->value() != vit.value()->defaultValue() ) return false; } return true; } void ItemGroup::registerItem( Item *item ) { if ( !item || m_itemList.contains(item) ) { return; } m_itemList += item; updateAreSameStatus(); } void ItemGroup::unregisterItem( Item *item ) { if ( m_itemList.removeAll(item) > 0 ) { updateAreSameStatus(); } } void ItemGroup::updateAreSameStatus() { b_itemsAreSameType = true; if ( m_itemList.size() < 2 ) { return; } QString activeId = (*m_itemList.begin())->id(); int discardIndex = activeId.lastIndexOf("__"); if ( discardIndex != -1 ) activeId.truncate(discardIndex); const ItemList::iterator end = m_itemList.end(); for ( ItemList::iterator it = ++m_itemList.begin(); it != end && b_itemsAreSameType; ++it ) { if (*it) { QString id = (*it)->id(); discardIndex = id.lastIndexOf("__"); if ( discardIndex != -1 ) id.truncate(discardIndex); if ( id != activeId ) { b_itemsAreSameType = false; } } } } void ItemGroup::slotAlignHorizontally() { if ( m_itemList.size() < 2 ) return; double avg_y = 0.; const ItemList::iterator end = m_itemList.end(); for ( ItemList::iterator it = m_itemList.begin(); it != end; ++it ) avg_y += (*it)->y(); int new_y = int(avg_y/(8*m_itemList.size()))*8+4; for ( ItemList::iterator it = m_itemList.begin(); it != end; ++it ) (*it)->move( (*it)->x(), new_y ); p_icnDocument->requestStateSave(); } void ItemGroup::slotAlignVertically() { if ( m_itemList.size() < 2 ) return; double avg_x = 0.; const ItemList::iterator end = m_itemList.end(); for ( ItemList::iterator it = m_itemList.begin(); it != end; ++it ) avg_x += (*it)->x(); int new_x = int(avg_x/(8*m_itemList.size()))*8+4; for ( ItemList::iterator it = m_itemList.begin(); it != end; ++it ) (*it)->move( new_x, (*it)->y() ); p_icnDocument->requestStateSave(); } void ItemGroup::slotDistributeHorizontally() { if ( m_itemList.size() < 2 ) return; // We sort the items by their horizontal position so that we can calculate // an average spacing typedef std::multimap< double, Item * > DIMap; DIMap ranked; const ItemList::iterator ilend = m_itemList.end(); for ( ItemList::iterator it = m_itemList.begin(); it != ilend; ++it ) ranked.insert( std::make_pair( (*it)->x(), *it ) ); double avg_spacing = 0; Item * previous = nullptr; const DIMap::iterator rankedEnd = ranked.end(); for ( DIMap::iterator it = ranked.begin(); it != rankedEnd; ++it ) { Item * item = it->second; if (previous) { double spacing = item->x() + item->offsetX() - (previous->x() + previous->width() + previous->offsetX()); avg_spacing += spacing; } previous = item; } avg_spacing /= (m_itemList.size()-1); DIMap::iterator it = ranked.begin(); // Position that we are up to double at = it->second->x() + it->second->width() + it->second->offsetX(); for ( ++it; it != rankedEnd; ++it ) { Item * item = it->second; double new_x = at - item->offsetX() + avg_spacing; item->move( snapToCanvas(new_x), item->y() ); at = new_x + item->width() + item->offsetX(); } p_icnDocument->requestStateSave(); } void ItemGroup::slotDistributeVertically() { if ( m_itemList.size() < 2 ) return; // We sort the items by their horizontal position so that we can calculate // an average spacing typedef std::multimap< double, Item * > DIMap; DIMap ranked; const ItemList::iterator ilend = m_itemList.end(); for ( ItemList::iterator it = m_itemList.begin(); it != ilend; ++it ) ranked.insert( std::make_pair( (*it)->y(), *it ) ); double avg_spacing = 0; Item * previous = nullptr; const DIMap::iterator rankedEnd = ranked.end(); for ( DIMap::iterator it = ranked.begin(); it != rankedEnd; ++it ) { Item * item = it->second; if (previous) { double spacing = item->y() + item->offsetY() - (previous->y() + previous->height() + previous->offsetY()); avg_spacing += spacing; } previous = item; } avg_spacing /= (m_itemList.size()-1); DIMap::iterator it = ranked.begin(); // Position that we are up to double at = it->second->y() + it->second->height() + it->second->offsetY(); for ( ++it; it != rankedEnd; ++it ) { Item * item = it->second; double new_y = at - item->offsetY() + avg_spacing; item->move( item->x(), snapToCanvas(new_y) ); at = new_y + item->height() + item->offsetY(); } p_icnDocument->requestStateSave(); } diff --git a/src/itemgroup.h b/src/itemgroup.h index b92edcb7..ec4ab7d1 100644 --- a/src/itemgroup.h +++ b/src/itemgroup.h @@ -1,140 +1,140 @@ /*************************************************************************** * Copyright (C) 2005 by David Saxton * * david@bluehaze.org * * * * 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. * ***************************************************************************/ #ifndef ITEMGROUP_H #define ITEMGROUP_H -#include +#include class Item; class ICNDocument; class ItemDocument; class DoubleSpinBox; class ItemGroup; class MechanicsDocument; class Variant; typedef QList > ItemList; class KtlQCanvasItem; class KtlQCanvasItemList; /** Generic base class for controlling a selection of Item. Provides some functionality such as for dealing with item data @author David Saxton */ class ItemGroup : public QObject { Q_OBJECT public: ItemGroup( ItemDocument *view, const char *name = nullptr ); ~ItemGroup() override; /** * Returns a pointer to the "active" CNItem - i.e. the last CNItem * to be added to the CNItemGroup. This will always return a pointer to * a single item, unless there are no CNItems in the group */ Item *activeItem() const { return m_activeItem; } uint itemCount() const { return m_itemList.count(); } virtual bool addQCanvasItem( KtlQCanvasItem *qcanvasItem ) = 0; virtual void setItems( KtlQCanvasItemList list ) = 0; virtual void removeQCanvasItem( KtlQCanvasItem *qcanvasItem ) = 0; virtual bool contains( KtlQCanvasItem *qcanvasItem ) const = 0; virtual uint count() const = 0; bool isEmpty() const { return (count() == 0); } virtual void mergeGroup( ItemGroup *group ) = 0; virtual void removeAllItems() = 0; virtual void deleteAllItems() = 0; /** * Returns a list of all the Items in the group. * @param excludeParented whether to return items whose (grand-) parents are * already in the list. */ ItemList items( bool excludeParented = true ) const; /** * Sets the selected state of all items in the group */ virtual void setSelected( bool sel ) = 0; /** * Returns true iff either there are no items, or itemsAreSameType and the * value of each data (excluding hidden data) for each item is the same */ bool itemsHaveSameData() const; /** * Returns truee iff either there are no items, or itemsAreSameType and the * value of the data with the given id is the same for each item */ bool itemsHaveSameDataValue( const QString &id ) const; /** * Returns true iff all the iff itemsHaveSameData() returns true and the * value of the data are the defaults */ bool itemsHaveDefaultData() const; /** * Returns true if all the items in the group are the same (e.g. * resistors). This is checked for by looking at the ids of the items, * and seeing if the string before "__#" is the same Note: if there are zero * items in the group, then this will return true */ bool itemsAreSameType() const { return b_itemsAreSameType; } public slots: /** * Align the selected items horizontally so that their positions have the * same y coordinate. */ void slotAlignHorizontally(); /** * Align the selected items horizontally so that their positions have the * same x coordinate. */ void slotAlignVertically(); /** * Distribute the selected items horizontally so that they have the same * spacing in the horizontal direction. */ void slotDistributeHorizontally(); /** * Distribute the selected items vertically so that they have the same * spacing in the vertical direction. */ void slotDistributeVertically(); signals: void itemAdded( Item *item ); void itemRemoved( Item *item ); protected: /** * Subclasses must call this to register the item with the data interface */ void registerItem( Item *item ); /** * Subclasses must call this to unregister the item with the data interface */ void unregisterItem( Item *item ); void updateAreSameStatus(); ItemList m_itemList; bool b_itemsAreSameType; ItemDocument * p_view; ICNDocument *p_icnDocument; MechanicsDocument *p_mechanicsDocument; Item *m_activeItem; private slots: void getViewPtrs(); }; #endif diff --git a/src/iteminterface.cpp b/src/iteminterface.cpp index 2c66576c..aebda52f 100644 --- a/src/iteminterface.cpp +++ b/src/iteminterface.cpp @@ -1,614 +1,614 @@ /*************************************************************************** * Copyright (C) 2004-2005 by David Saxton * * david@bluehaze.org * * * * 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 "circuitview.h" #include "colorcombo.h" #include "contexthelp.h" #include "cnitem.h" #include "cnitemgroup.h" #include "doublespinbox.h" #include "itemdocument.h" #include "itemeditor.h" #include "iteminterface.h" #include "itemview.h" #include "ktechlab.h" #include "lineedit.h" #include -#include #include #include #include -#include -#include -#include +#include +#include +#include +#include #include #include ItemInterface * ItemInterface::m_pSelf = nullptr; ItemInterface * ItemInterface::self() { if ( !m_pSelf ) m_pSelf = new ItemInterface(); return m_pSelf; } ItemInterface::ItemInterface() : QObject( KTechlab::self() ) , m_isInTbDataChanged(false) { m_pActiveItemEditorToolBar = nullptr; p_cvb = nullptr; p_itemGroup = nullptr; p_lastItem = nullptr; m_currentActionTicket = -1; m_toolBarWidgetID = -1; } ItemInterface::~ItemInterface() { } void ItemInterface::slotGetActionTicket() { m_currentActionTicket = p_cvb ? p_cvb->getActionTicket() : -1; } void ItemInterface::slotItemDocumentChanged( ItemDocument * doc ) { slotClearAll(); if ( ItemDocument * itemDocument = dynamic_cast((Document*)p_cvb) ) { disconnect( itemDocument, SIGNAL(selectionChanged()), this, SLOT(slotUpdateItemInterface()) ); } p_itemGroup = nullptr; p_cvb = doc; slotGetActionTicket(); if (!p_cvb) return; connect( p_cvb, SIGNAL(selectionChanged()), this, SLOT(slotUpdateItemInterface()) ); p_itemGroup = p_cvb->selectList(); slotUpdateItemInterface(); } void ItemInterface::clearItemEditorToolBar() { if ( m_pActiveItemEditorToolBar && m_toolBarWidgetID != -1 ) { //m_pActiveItemEditorToolBar->removeItem(m_toolBarWidgetID); // TODO add proper replacmenet m_pActiveItemEditorToolBar->clear(); } m_toolBarWidgetID = -1; itemEditTBCleared(); } void ItemInterface::slotClearAll() { ContextHelp::self()->slotClear(); ItemEditor::self()->slotClear(); clearItemEditorToolBar(); p_lastItem = nullptr; } void ItemInterface::slotMultipleSelected() { ContextHelp::self()->slotMultipleSelected(); ItemEditor::self()->slotMultipleSelected(); clearItemEditorToolBar(); p_lastItem = nullptr; } void ItemInterface::slotUpdateItemInterface() { if (!p_itemGroup) return; slotGetActionTicket(); updateItemActions(); if (!p_itemGroup->itemsAreSameType() ) { slotMultipleSelected(); return; } if ( p_lastItem && p_itemGroup->activeItem() ) { ItemEditor::self()->itemGroupUpdated( p_itemGroup ); return; } p_lastItem = p_itemGroup->activeItem(); if (!p_lastItem) { slotClearAll(); return; } ContextHelp::self()->slotUpdate(p_lastItem); ItemEditor::self()->slotUpdate(p_itemGroup); if ( CNItem * cnItem = dynamic_cast((Item*)p_lastItem) ) { ItemEditor::self()->slotUpdate(cnItem); } // Update item editor toolbar if ( ItemView * itemView = dynamic_cast(p_cvb->activeView()) ) { if ( KTechlab * ktl = KTechlab::self() ) { if ( (m_pActiveItemEditorToolBar = dynamic_cast(ktl->factory()->container("itemEditorTB",itemView)) ) ) { //m_pActiveItemEditorToolBar->setFullSize( true ); // TODO proper replacement m_pActiveItemEditorToolBar->adjustSize(); QWidget * widget = configWidget(); m_toolBarWidgetID = 1; // m_pActiveItemEditorToolBar->insertWidget( m_toolBarWidgetID, 0, widget ); // TODO properly fix m_pActiveItemEditorToolBar->addWidget( widget ); } } } } void ItemInterface::updateItemActions() { ItemView * itemView = ((ItemDocument*)p_cvb) ? dynamic_cast(p_cvb->activeView()) : nullptr; if ( !itemView ) return; bool itemsSelected = p_itemGroup && p_itemGroup->itemCount(); itemView->actionByName("edit_raise")->setEnabled(itemsSelected); itemView->actionByName("edit_lower")->setEnabled(itemsSelected); if ( KTechlab::self() ) { KTechlab::self()->actionByName("edit_cut")->setEnabled(itemsSelected); KTechlab::self()->actionByName("edit_copy")->setEnabled(itemsSelected); } CNItemGroup * cnItemGroup = dynamic_cast((ItemGroup*)p_itemGroup); CircuitView * circuitView = dynamic_cast(itemView); if ( cnItemGroup && circuitView ) { bool canFlip = cnItemGroup->canFlip(); circuitView->actionByName("edit_flip_horizontally")->setEnabled( canFlip ); circuitView->actionByName("edit_flip_vertically")->setEnabled( canFlip ); bool canRotate = cnItemGroup->canRotate(); circuitView->actionByName("edit_rotate_ccw")->setEnabled( canRotate ); circuitView->actionByName("edit_rotate_cw")->setEnabled( canRotate ); } } void ItemInterface::setFlowPartOrientation( unsigned orientation ) { CNItemGroup *cnItemGroup = dynamic_cast((ItemGroup*)p_itemGroup); if (!cnItemGroup) return; cnItemGroup->setFlowPartOrientation( orientation ); } void ItemInterface::setComponentOrientation( int angleDegrees, bool flipped ) { CNItemGroup *cnItemGroup = dynamic_cast((ItemGroup*)p_itemGroup); if (!cnItemGroup) return; cnItemGroup->setComponentOrientation( angleDegrees, flipped ); } void ItemInterface::itemEditTBCleared() { m_stringLineEditMap.clear(); m_stringComboBoxMap.clear(); m_stringURLReqMap.clear(); m_intSpinBoxMap.clear(); m_doubleSpinBoxMap.clear(); m_colorComboMap.clear(); m_boolCheckMap.clear(); } // The bool specifies whether advanced data should be shown QWidget * ItemInterface::configWidget() { if ( !p_itemGroup || !p_itemGroup->activeItem() || !m_pActiveItemEditorToolBar ) return nullptr; VariantDataMap *variantMap = p_itemGroup->activeItem()->variantMap(); QWidget * parent = m_pActiveItemEditorToolBar; // Create new widget with the toolbar or dialog as the parent QWidget * configWidget = new QWidget( parent /*, "tbConfigWidget" */ ); configWidget->setObjectName("tbConfigWidget"); { // 2018.12.02 //configWidget->setSizePolicy( QSizePolicy( QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding, 1, 1 ) ); QSizePolicy p(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding); p.setHorizontalStretch(1); p.setVerticalStretch(1); configWidget->setSizePolicy(p); } QHBoxLayout * configLayout = new QHBoxLayout( configWidget ); // configLayout->setAutoAdd( true ); configLayout->setSpacing( 6 ); // configLayout->addItem( new QSpacerItem( 0, 0, QSizePolicy::Expanding, QSizePolicy::Fixed ) ); const VariantDataMap::iterator vaEnd = variantMap->end(); for ( VariantDataMap::iterator vait = variantMap->begin(); vait != vaEnd; ++vait ) { if ( vait.value()->isHidden() || vait.value()->isAdvanced() ) continue; const Variant::Type::Value type = vait.value()->type(); // common to all types apart from bool QString toolbarCaption = vait.value()->toolbarCaption(); if ( type != Variant::Type::Bool && !toolbarCaption.isEmpty() ) configLayout->addWidget( new QLabel( toolbarCaption, configWidget ) ); QWidget * editWidget = nullptr; // Should be set to the created widget switch( type ) { case Variant::Type::Port: case Variant::Type::Pin: case Variant::Type::VarName: case Variant::Type::Combo: case Variant::Type::Select: case Variant::Type::KeyPad: case Variant::Type::SevenSegment: { QString value = vait.value()->displayString(); if ( !value.isEmpty() && !vait.value()->allowed().contains(value) ) vait.value()->appendAllowed(value); const QStringList allowed = vait.value()->allowed(); KComboBox * box = new KComboBox(configWidget); box->insertItems(box->count(), allowed); box->setCurrentItem(value); if ( type == Variant::Type::VarName || type == Variant::Type::Combo ) box->setEditable( true ); m_stringComboBoxMap[vait.key()] = box; connectMapWidget( box, SIGNAL(editTextChanged(const QString &))); connectMapWidget( box, SIGNAL(activated(const QString &))); connect( *vait, SIGNAL(valueChangedStrAndTrue(const QString &, bool)), box, SLOT(setCurrentItem(const QString &, bool)) ); editWidget = box; break; } case Variant::Type::FileName: { qDebug() << Q_FUNC_INFO << "create FileName"; QString value = vait.value()->value().toString(); if ( !vait.value()->allowed().contains(value) ) vait.value()->appendAllowed(value); const QStringList allowed = vait.value()->allowed(); KUrlComboRequester * urlreq = new KUrlComboRequester( configWidget ); urlreq->setFilter( vait.value()->filter() ); connectMapWidget( urlreq, SIGNAL(urlSelected(QUrl)) ); m_stringURLReqMap[vait.key()] = urlreq; KComboBox * box = urlreq->comboBox(); box->insertItems(box->count(), allowed); box->setEditable( true ); // Note this has to be called after inserting the allowed list urlreq->setUrl(QUrl::fromLocalFile(vait.value()->value().toString())); // Generally we only want a file name once the user has finished typing out the full file name. connectMapWidget( box, SIGNAL(returnPressed(const QString &))); connectMapWidget( box, SIGNAL(activated(const QString &))); connect( *vait, SIGNAL(valueChanged(const QString &)), box, SLOT(setEditText(const QString &)) ); editWidget = urlreq; break; } case Variant::Type::String: { LineEdit * edit = new LineEdit( configWidget ); edit->setText( vait.value()->value().toString() ); connectMapWidget(edit,SIGNAL(textChanged(const QString &))); m_stringLineEditMap[vait.key()] = edit; editWidget = edit; connect( *vait, SIGNAL(valueChanged(const QString &)), edit, SLOT(setText(const QString &)) ); break; } case Variant::Type::Int: { QSpinBox *spin = new QSpinBox(configWidget); spin->setMinimum((int)vait.value()->minValue()); spin->setMaximum((int)vait.value()->maxValue()); spin->setValue(vait.value()->value().toInt()); connectMapWidget( spin, SIGNAL(valueChanged(int)) ); m_intSpinBoxMap[vait.key()] = spin; editWidget = spin; connect( *vait, SIGNAL(valueChanged(int)), spin, SLOT(setValue(int)) ); break; } case Variant::Type::Double: { DoubleSpinBox *spin = new DoubleSpinBox( vait.value()->minValue(), vait.value()->maxValue(), vait.value()->minAbsValue(), vait.value()->value().toDouble(), vait.value()->unit(), configWidget ); connectMapWidget( spin, SIGNAL(valueChanged(double))); m_doubleSpinBoxMap[vait.key()] = spin; editWidget = spin; connect( *vait, SIGNAL(valueChanged(double)), spin, SLOT(setValue(double)) ); break; } case Variant::Type::Color: { QColor value = vait.value()->value().value(); ColorCombo * colorBox = new ColorCombo( (ColorCombo::ColorScheme)vait.value()->colorScheme(), configWidget ); colorBox->setColor( value ); connectMapWidget( colorBox, SIGNAL(activated(const QColor &))); m_colorComboMap[vait.key()] = colorBox; connect( *vait, SIGNAL(valueChanged(const QColor &)), colorBox, SLOT(setColor(const QColor &)) ); editWidget = colorBox; break; } case Variant::Type::Bool: { const bool value = vait.value()->value().toBool(); QCheckBox * box = new QCheckBox( vait.value()->toolbarCaption(), configWidget ); box->setChecked(value); connectMapWidget( box, SIGNAL(toggled(bool))); m_boolCheckMap[vait.key()] = box; connect( *vait, SIGNAL(valueChanged(bool)), box, SLOT(setChecked(bool)) ); editWidget = box; break; } case Variant::Type::Raw: case Variant::Type::PenStyle: case Variant::Type::PenCapStyle: case Variant::Type::Multiline: case Variant::Type::RichText: case Variant::Type::None: { // Do nothing, as these data types are not handled in the toolbar break; } } if ( !editWidget ) continue; const int widgetH = QFontMetrics( configWidget->font() ).height() + 2; editWidget->setMinimumHeight( widgetH ); // note: this is hack-ish; something is not ok with the layout editWidget->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); // In the case of the toolbar, we don't want it too high if ( editWidget->height() > parent->height()-2 ) editWidget->setMaximumHeight( parent->height()-2 ); switch ( type ) { case Variant::Type::VarName: case Variant::Type::Combo: case Variant::Type::String: { QSizePolicy p( QSizePolicy::MinimumExpanding, QSizePolicy::Fixed /*, 1, 1 */ ); p.setHorizontalStretch(1); p.setVerticalStretch(1); editWidget->setSizePolicy( p ); editWidget->setMaximumWidth( 250 ); break; } case Variant::Type::FileName: case Variant::Type::Port: case Variant::Type::Pin: case Variant::Type::Select: case Variant::Type::KeyPad: case Variant::Type::SevenSegment: case Variant::Type::Int: case Variant::Type::Double: case Variant::Type::Color: case Variant::Type::Bool: case Variant::Type::Raw: case Variant::Type::PenStyle: case Variant::Type::PenCapStyle: case Variant::Type::Multiline: case Variant::Type::RichText: case Variant::Type::None: break; } configLayout->addWidget( editWidget ); } configLayout->addItem( new QSpacerItem( 0, 0, QSizePolicy::Expanding, QSizePolicy::Fixed ) ); return configWidget; } void ItemInterface::connectMapWidget( QWidget *widget, const char *_signal ) { connect( widget, _signal, this, SLOT(tbDataChanged()) ); } // TODO move to separate file struct BoolLock { bool *m_flagPtr; BoolLock(bool *flagPtr) : m_flagPtr(flagPtr) { if (m_flagPtr == nullptr) { qCritical() << Q_FUNC_INFO << "nullptr flagPtr"; return; } if (*m_flagPtr == true) { qWarning() << Q_FUNC_INFO << "flag expected to be false, addr=" << m_flagPtr << " Doing nothing"; m_flagPtr = nullptr; } else { *m_flagPtr = true; } } ~BoolLock() { if (m_flagPtr != nullptr) { *m_flagPtr = false; } } }; void ItemInterface::tbDataChanged() { qDebug() << Q_FUNC_INFO << "begin"; if (m_isInTbDataChanged) { qDebug() << Q_FUNC_INFO << "avoiding recursion, returning"; return; } BoolLock inTbChangedLock(&m_isInTbDataChanged); // Manual string values const LineEditMap::iterator m_stringLineEditMapEnd = m_stringLineEditMap.end(); for ( LineEditMap::iterator leit = m_stringLineEditMap.begin(); leit != m_stringLineEditMapEnd; ++leit ) { slotSetData( leit.key(), leit.value()->text() ); } // String values from comboboxes const KComboBoxMap::iterator m_stringComboBoxMapEnd = m_stringComboBoxMap.end(); for ( KComboBoxMap::iterator cmit = m_stringComboBoxMap.begin(); cmit != m_stringComboBoxMapEnd; ++cmit ) { qDebug() << Q_FUNC_INFO << "set KCombo data for " << cmit.key() << " to " << cmit.value()->currentText(); slotSetData( cmit.key(), cmit.value()->currentText() ); } // Colors values from colorcombos const ColorComboMap::iterator m_colorComboMapEnd = m_colorComboMap.end(); for ( ColorComboMap::iterator ccit = m_colorComboMap.begin(); ccit != m_colorComboMapEnd; ++ccit ) { slotSetData( ccit.key(), ccit.value()->color() ); } // Bool values from checkboxes const QCheckBoxMap::iterator m_boolCheckMapEnd = m_boolCheckMap.end(); for ( QCheckBoxMap::iterator chit = m_boolCheckMap.begin(); chit != m_boolCheckMapEnd; ++chit ) { slotSetData( chit.key(), chit.value()->isChecked() ); } const IntSpinBoxMap::iterator m_intSpinBoxMapEnd = m_intSpinBoxMap.end(); for ( IntSpinBoxMap::iterator it = m_intSpinBoxMap.begin(); it != m_intSpinBoxMapEnd; ++it ) { slotSetData( it.key(), it.value()->value() ); } // (?) Combined values from spin boxes and combo boxes // (?) Get values from all spin boxes const DoubleSpinBoxMap::iterator m_doubleSpinBoxMapEnd = m_doubleSpinBoxMap.end(); for ( DoubleSpinBoxMap::iterator sbit = m_doubleSpinBoxMap.begin(); sbit != m_doubleSpinBoxMapEnd; ++sbit ) { // VariantDataMap::iterator vait = variantData.find(sbit.key()); slotSetData( sbit.key(), sbit.value()->value() ); } // Filenames from KUrlRequesters const KUrlReqMap::iterator m_stringURLReqMapEnd = m_stringURLReqMap.end(); for ( KUrlReqMap::iterator urlit = m_stringURLReqMap.begin(); urlit != m_stringURLReqMapEnd; ++urlit ) { qDebug() << Q_FUNC_INFO << "set kurlrequester data for " << urlit.key() << " to " << urlit.value()->url(); QVariant urlVar( urlit.value()->url().path() ); qDebug() << Q_FUNC_INFO << "urlVar=" << urlVar << " urlVar.toUrl=" << urlVar.toUrl(); slotSetData( urlit.key(), urlVar ); } if (p_cvb) p_cvb->setModified(true); } void ItemInterface::setProperty( Variant * v ) { slotSetData( v->id(), v->value() ); } void ItemInterface::slotSetData( const QString &id, QVariant value ) { if ( !p_itemGroup || (p_itemGroup->itemCount() == 0) ) { qDebug() << Q_FUNC_INFO << "p_itemGroup not valid:" << p_itemGroup; return; } if ( !p_itemGroup->itemsAreSameType() ) { qDebug() << Q_FUNC_INFO << "Items are not the same type!"< VariantDataMap; typedef QMap KComboBoxMap; typedef QMap LineEditMap; typedef QMap DoubleSpinBoxMap; typedef QMap IntSpinBoxMap; typedef QMap ColorComboMap; typedef QMap KUrlReqMap; typedef QMap QCheckBoxMap; /** This acts as an interface between the ItemDocument's and the associated Items's, and the various objects that like to know about them (e.g. ContextHelp, ItemEditor, ItemEditTB) @author David Saxton */ class ItemInterface : public QObject { Q_OBJECT public: ~ItemInterface() override; static ItemInterface * self(); /** * The current item group in use (or null if none). */ ItemGroup * itemGroup() const { return p_itemGroup; } /** * Sets the orientation of all flowparts in the group. */ void setFlowPartOrientation( unsigned orientation ); /** * Sets the orientation of all components in the group. */ void setComponentOrientation( int angleDegrees, bool flipped ); /** * Updates actions based on the items currently selected (e.g. rotate, * flip, etc) */ void updateItemActions(); /** * Returns a configuration widget for the component for insertion into te * ItemEditTB. */ virtual QWidget * configWidget(); public slots: /** * If cnItemsAreSameType() returns true, then set the * data with the given id for all the CNItems in the group. * Else, it only sets the data for the activeCNItem() */ void slotSetData( const QString &id, QVariant value ); /** * Essentially the same as slotSetData. */ void setProperty( Variant * v ); /** * Called when the ItemEditTB is cleared. This clears all widget maps. */ void itemEditTBCleared(); void tbDataChanged(); void slotItemDocumentChanged( ItemDocument *view ); void slotUpdateItemInterface(); void slotClearAll(); void slotMultipleSelected(); void clearItemEditorToolBar(); protected: /** * Connects the specified widget to either tbDataChanged or advDataChanged * as specified by mi. */ void connectMapWidget( QWidget *widget, const char *_signal); // Widget maps. LineEditMap m_stringLineEditMap; KComboBoxMap m_stringComboBoxMap; KUrlReqMap m_stringURLReqMap; DoubleSpinBoxMap m_doubleSpinBoxMap; IntSpinBoxMap m_intSpinBoxMap; ColorComboMap m_colorComboMap; QCheckBoxMap m_boolCheckMap; // Use by item editor toolbar QPointer m_pActiveItemEditorToolBar; int m_toolBarWidgetID; protected slots: void slotGetActionTicket(); private: ItemInterface(); static ItemInterface * m_pSelf; QPointer p_cvb; QPointer p_itemGroup; QPointer p_lastItem; int m_currentActionTicket; bool m_isInTbDataChanged; }; #endif diff --git a/src/itemlibrary.cpp b/src/itemlibrary.cpp index 743a4701..cf8f0a9f 100644 --- a/src/itemlibrary.cpp +++ b/src/itemlibrary.cpp @@ -1,729 +1,729 @@ /*************************************************************************** * Copyright (C) 2003-2006 David Saxton * * * * 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 "config.h" #include "cnitem.h" #include "canvasitemparts.h" #include "electronics/circuitdocument.h" #include "component.h" #include "ecsubcircuit.h" #include "ecnode.h" #include "itemlibrary.h" #include "libraryitem.h" #include "node.h" #include "pinmapping.h" #include "subcircuits.h" #include #include -#include #include #include -#include -#include -#include "qdebug.h" -#include -#include -#include -#include -#include -#include -#include -#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include #include #include //BEGIN Item includes #ifdef MECHANICS #include "chassiscircular2.h" #endif #include "dpimage.h" #include "dpline.h" #include "dptext.h" #include "solidshape.h" #include "callsub.h" #include "count.h" #include "delay.h" #include "embed.h" #include "end.h" #include "inputbutton.h" #include "interrupt.h" #include "forloop.h" #include "keypad.h" #include "pulse.h" #include "readport.h" #include "repeat.h" #include "setpin.h" #include "sevenseg.h" #include "start.h" #include "sub.h" #include "testpin.h" #include "unary.h" #include "varassignment.h" #include "varcomparison.h" #include "while.h" #include "writeport.h" #include "addac.h" #include "bidirled.h" #include "binarycounter.h" #include "bussplitter.h" #include "demultiplexer.h" #include "dependentsource.h" #include "discretelogic.h" #include "externalconnection.h" #include "flipflop.h" #include "fulladder.h" #include "inductor.h" #include "magnitudecomparator.h" #include "matrixdisplay.h" #include "matrixdisplaydriver.h" #include "meter.h" #include "multiinputgate.h" #include "multiplexer.h" #include "parallelportcomponent.h" #include "piccomponent.h" #include "pushswitch.h" #include "probe.h" #include "ram.h" #include "resistordip.h" #include "rotoswitch.h" #include "serialportcomponent.h" #include "toggleswitch.h" #include "ec555.h" #include "ecbcdto7segment.h" #include "ecbjt.h" #include "capacitor.h" #include "ecclockinput.h" #include "eccurrentsignal.h" #include "eccurrentsource.h" #include "ecdiode.h" #include "ecfixedvoltage.h" #include "ecground.h" #include "ecjfet.h" #include "eckeypad.h" #include "led.h" #include "ledbargraphdisplay.h" #include "ecmosfet.h" #include "ecopamp.h" #include "ecpotentiometer.h" #include "resistor.h" #include "ecsevensegment.h" #include "ecsignallamp.h" #include "variablecapacitor.h" #include "variableresistor.h" #include "ecvoltagesignal.h" #include "ecvoltagesource.h" //END Item includes KLocalizedString ItemLibrary::m_emptyItemDescription = ki18n("This help item does not yet exist for the %1 language. Help out with KTechlab by creating one via the \"Edit\" button."); ItemLibrary::ItemLibrary() { addFlowParts(); addComponents(); addMechanics(); addDrawParts(); loadItemDescriptions(); } ItemLibrary::~ItemLibrary() { // qDebug() << "m_itemDescriptions[\"en_US\"].size()="<allIDs().contains( type ) ) return *it; } return nullptr; } Item * ItemLibrary::createItem( const QString &id, ItemDocument *itemDocument, bool newItem, const char *newId, bool finishCreation ) { Item *item = nullptr; if ( id.startsWith(QString::fromLatin1("sc/")) ) { // Is a subcircuit... CircuitDocument *circuitDocument = dynamic_cast(itemDocument); if (!circuitDocument) { qWarning() << "Cannot create subcircuit without a circuit document" << endl; return nullptr; } QString temp = id; int numId = temp.remove(QString::fromLatin1("sc/")).toInt(); item = subcircuits()->createSubcircuit( numId, circuitDocument, newItem, newId ); } else { LibraryItem * li = libraryItem( id ); if ( !li ) qWarning() << "Could not find the item constructor for id " << id << endl; else { item = li->createItemFnPtr()( itemDocument, newItem, newId ); item->m_type = li->activeID(); } } if ( finishCreation && item ) item->finishedCreation(); return item; } QImage ItemLibrary::componentImage( Component * component, const uint maxSize ) { // Default orientation for painting const int angleDegrees = component->angleDegrees(); const bool flipped = component->flipped(); component->setAngleDegrees( 0 ); component->setFlipped( false ); QRect bound = component->boundingRect().normalized(); bound.setLeft( bound.left()-8 ); bound.setRight( bound.right()+8 ); bound.setTop( bound.top()-8 ); bound.setBottom( bound.bottom()+8 ); // We want a nice square bounding rect const int dy = bound.width() - bound.height(); if ( dy > 0 ) { bound.setTop( bound.top()-(dy/2) ); bound.setBottom( bound.bottom()+(dy/2) ); } else if ( dy < 0 ) { bound.setLeft( bound.left()+(dy/2) ); bound.setRight( bound.right()-(dy/2) ); } const bool cache = ((bound.width()*bound.height()) > (int)maxSize); QString type; if ( cache && m_imageMap.contains(component->type()) ) return m_imageMap[component->type()]; // Create pixmap big enough to contain CNItem and surrounding nodes // and copy the button grab to it QPixmap pm( bound.size() ); QBitmap mask( bound.size() ); mask.fill( Qt::color0 ); //QPainter maskPainter(&mask); // 2016.05.03 - initialize painter explicitly QPainter maskPainter; { const bool isSuccess = maskPainter.begin(&mask); if (!isSuccess) { qWarning() << Q_FUNC_INFO << " painter not active at line " << __LINE__; } } maskPainter.translate( -bound.x(), -bound.y() ); maskPainter.setPen( Qt::color1 ); maskPainter.setBrush( Qt::color1 ); //BEGIN painting on the pixmap { //QPainter p(&pm); // 2016.05.03 - initialize painter explicitly QPainter p; const bool isBeginSuccess = p.begin(&pm); if (!isBeginSuccess) { qWarning() << Q_FUNC_INFO << " painter not active at line " << __LINE__; } p.translate( -bound.x(), -bound.y() ); p.setPen( component->pen() ); p.setBrush( component->brush() ); //BEGIN Draw the component const bool sel = component->isSelected(); if (sel) { // We block the signals as we end up in an infinite loop with component emitting a selected signal component->blockSignals(true); component->setSelected(false); component->blockSignals(false); } component->drawShape(p); component->drawShape(maskPainter); if (sel) { component->blockSignals(true); component->setSelected(sel); component->blockSignals(false); } //END Draw the component maskPainter.setPen( Qt::color1 ); maskPainter.setBrush( Qt::color1 ); QMatrix transMatrix; // Matrix to apply to the image NodeInfoMap nodes = component->nodeMap(); const NodeInfoMap::iterator nodesEnd = nodes.end(); for ( NodeInfoMap::iterator it = nodes.begin(); it != nodesEnd; ++it ) { Node *node = it.value().node; const bool sel = node->isSelected(); if (sel) node->setSelected(false); if ( ECNode *ecnode = dynamic_cast(node) ) { bool showVB = ecnode->showVoltageBars(); bool showVC = ecnode->showVoltageColor(); ecnode->setShowVoltageBars( false ); ecnode->setShowVoltageColor( false ); ecnode->drawShape(p); ecnode->drawShape( maskPainter ); ecnode->setShowVoltageBars( showVB ); ecnode->setShowVoltageColor( showVC ); } else { node->drawShape(p); node->drawShape(maskPainter); } if (sel) node->setSelected(sel); } p.setPen(Qt::black); TextMap text = component->textMap(); const TextMap::iterator textEnd = text.end(); for ( TextMap::iterator it = text.begin(); it != textEnd; ++it ) { it.value()->drawShape(p); it.value()->drawShape(maskPainter); } // maskPainter.setPen( Qt::color1 ); // maskPainter.setBrush( Qt::color1 ); component->drawWidgets(p); // component->drawWidgets(maskPainter); } //END painting on the pixmap pm.setMask(mask); // pm needs not to have active painters on it // Now, rotate the image so that it's the right way up, and scale it to size QImage im = pm.toImage(); //im = im.smoothScale( 50, 50, Qt::ScaleMin ); //2018.12.01 im = im.scaled( QSize( 50, 50 ), Qt::KeepAspectRatio, Qt::SmoothTransformation ); if (cache) m_imageMap[component->type()] = im; // Restore original orientation component->setAngleDegrees( angleDegrees ); component->setFlipped( flipped ); return im; } QPixmap ItemLibrary::itemIconFull( const QString &id ) { LibraryItemList::iterator end = m_items.end(); for ( LibraryItemList::iterator it = m_items.begin(); it != end; ++it ) { if ( *it && (*it)->allIDs().contains(id) ) { return (*it)->iconFull(); } } return QPixmap(); } bool ItemLibrary::saveDescriptions( const QString & languageCode ) { QString url = itemDescriptionsFile( languageCode ); QFile file( url ); if ( !file.open( QIODevice::WriteOnly ) ) { KMessageBox::sorry( nullptr, i18n("Could not open item descriptions file \"%1\" for writing.", url) ); return false; } QTextStream stream( & file ); const QStringMap itemDescriptions = m_itemDescriptions[ languageCode ]; for (auto descIt = itemDescriptions.begin(), end = itemDescriptions.end(); descIt != end; ++descIt) { stream << QString::fromLatin1("\n").arg( descIt.key() ); stream << descIt.value() << endl; } file.close(); return true; } bool ItemLibrary::haveDescription( QString type, const QString & languageCode ) const { if ( type.startsWith(QString::fromLatin1("/")) ) { // Possibly change e.g. "/ec/capacitor" to "ec/capacitor" type.remove( 0, 1 ); } if ( !m_itemDescriptions[ languageCode ].contains( type ) ) { return libraryItem( type ); } return ! m_itemDescriptions[ languageCode ][ type ].isEmpty(); } QString ItemLibrary::description( QString type, const QString & languageCode ) const { if ( type.startsWith(QString::fromLatin1("/")) ) { // Possibly change e.g. "/ec/capacitor" to "ec/capacitor" type.remove( 0, 1 ); } QString current = m_itemDescriptions[ languageCode ][ type ]; if ( current.isEmpty() ) { // Try english-language description current = m_itemDescriptions[ QString::fromLatin1("en_US") ][ type ]; if ( current.isEmpty() ) return emptyItemDescription( languageCode ); } return current; } QString ItemLibrary::emptyItemDescription(const QString &languageCode) const { //return m_emptyItemDescription.arg( KGlobal::locale()->twoAlphaToLanguageName( language ) ); return m_emptyItemDescription.subs(QLocale(languageCode).nativeLanguageName()).toString(); } bool ItemLibrary::setDescription(QString type, const QString & description, const QString & languageCode) { if ( type.startsWith(QString::fromLatin1("/")) ) { // Possibly change e.g. "/ec/capacitor" to "ec/capacitor" type.remove( 0, 1 ); } m_itemDescriptions[ languageCode ][ type ] = description; return saveDescriptions( languageCode ); } void ItemLibrary::setItemDescriptionsDirectory( QString dir ) { if ( !dir.isEmpty() && !dir.endsWith(QString::fromLatin1("/")) ) dir += QString::fromLatin1("/"); KSharedConfigPtr conf = KSharedConfig::openConfig(); //QString prevGroup = conf->group(); KConfigGroup grGen = conf->group("General"); grGen.writePathEntry( "ItemDescriptionsDirectory", dir ); //conf->setGroup( prevGroup ); } QString ItemLibrary::itemDescriptionsDirectory() const { KSharedConfigPtr conf = KSharedConfig::openConfig(); //QString prevGroup = conf->group(); KConfigGroup grGen = conf->group("General"); QString dir = grGen.readPathEntry( "ItemDescriptionsDirectory", QStandardPaths::locate( QStandardPaths::AppDataLocation, "contexthelp/", QStandardPaths::LocateDirectory) ); //conf->setGroup( prevGroup ); if ( !dir.isEmpty() && !dir.endsWith(QString::fromLatin1("/")) ) dir += QString::fromLatin1("/"); return dir; } QString ItemLibrary::itemDescriptionsFile( const QString & languageCode) const { QString dir( itemDescriptionsDirectory() ); if ( dir.isEmpty() ) return QString::null; const QString url = dir + QLatin1String("help-") + languageCode; return url; } void ItemLibrary::loadItemDescriptions() { // Create an entry for the default language (American English) // and the current language // KLocale * locale = KLocale::global(); // m_itemDescriptions[ locale->defaultLanguage() ]; // m_itemDescriptions[ locale->language() ]; m_itemDescriptions[QLocale().name()]; const QStringList languages = descriptionLanguages(); QStringList::const_iterator end = languages.end(); for ( QStringList::const_iterator it = languages.begin(); it != end; ++it ) { QString url = itemDescriptionsFile( *it ); QFile file( url ); if ( !file.open( QIODevice::ReadOnly ) ) { qWarning() << Q_FUNC_INFO << "Could not open file \"" << url << "\"" << endl; continue; } QTextStream stream( & file ); QString type; QString description; while ( !stream.atEnd() ) { QString line = stream.readLine(); if ( line.startsWith( QString::fromLatin1("") ); type = line.trimmed(); if ( type.startsWith(QString::fromLatin1("/")) ) { // Possibly change e.g. "/ec/capacitor" to "ec/capacitor" type.remove( 0, 1 ); } description = QString::null; } else description += line + '\n'; } // Save the previous description if ( !type.isEmpty() ) m_itemDescriptions[ *it ][ type ] = description.trimmed(); file.close(); } } diff --git a/src/itemlibrary.h b/src/itemlibrary.h index 70dc595d..e54cff00 100644 --- a/src/itemlibrary.h +++ b/src/itemlibrary.h @@ -1,146 +1,146 @@ /*************************************************************************** * Copyright (C) 2003-2006 David Saxton * * * * 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. * ***************************************************************************/ #ifndef ITEMLIBRARY_H #define ITEMLIBRARY_H #include -#include -#include -#include +#include +#include +#include class Component; class Document; class Item; class ItemDocument; class ItemLibrary; class LibraryItem; inline ItemLibrary* itemLibrary(); typedef QMap< QString, QString > QStringMap; typedef QMap< QString, QStringMap > QStringMapMap; typedef QMap< QString, QImage > ImageMap; typedef QList LibraryItemList; /** While the program is running, only one instance of this class is created. You can get it by calling itemLibrary() @short Holds the list of CNItems @author David Saxton */ class ItemLibrary : public QObject { Q_OBJECT public: ~ItemLibrary() override; /** * Returns a QPixmap of the item icon */ QPixmap itemIconFull( const QString &id ); /** * Append the given item into the library */ void addLibraryItem( LibraryItem *item ); /** * Returns a list of items in the library */ LibraryItemList* items() { return &m_items; } /** * @return the LibraryItem for the item with the given type (id) const. */ LibraryItem * libraryItem( QString type ) const; /** * Creates a new item with the given id, and returns a pointer to it */ Item * createItem( const QString &id, ItemDocument * itemDocument, bool newItem, const char *newId = nullptr, bool finishCreation = true ); /** * Returns an image of the given component. As QPixmap::toImage is * a slow function, this will cache the result and return that for large * images. * @param component A pointer to the Component. * @param maxSize The maximum size (in pixels) before the image is * cached. */ QImage componentImage( Component * component, const uint maxSize = 36000 ); /** * Does similar to that above, but will not be able to return a description * if there is none saved on file (instead of the above, which falls back to * calling item->description()). * @param type the id of the item. * @param language the language code, e.g. "es". */ QString description( QString type, const QString & language ) const; /** * @return if we have a description for the item in language. */ bool haveDescription( QString type, const QString & language ) const; /** * Gives the type item the description. * @param type the type of item this description is for. * @param language the language code, e.g. "es". * @return whether the descriptions file could be saved. */ bool setDescription( QString type, const QString & description, const QString & language ); /** * @return the directory containing the item descriptions. By default, * this is something like "/usr/share/apps/ktechlab/contexthelp/". But * can be changed by calling setDescriptionsDirectory. */ QString itemDescriptionsDirectory() const; /** * Stores the item descriptions directory in the users config. */ void setItemDescriptionsDirectory( QString dir ); /** * @return the item description file for the given language. */ QString itemDescriptionsFile( const QString & language ) const; /** * @return the string used for an empty item description - something like * "The help for English does not yet exist..". This is created by inserting * the current language name into m_emptyItemDescription; */ QString emptyItemDescription( const QString & language ) const; /** * @return the list of language-codes that have item descriptions. */ QStringList descriptionLanguages() const { return m_itemDescriptions.keys(); } protected: /** * Saves the item descriptions to the file specified in the config. * @return whether successful (e.g. if the file could be opened for * writing). */ bool saveDescriptions( const QString & language ); void loadItemDescriptions(); void addComponents(); void addFlowParts(); void addMechanics(); void addDrawParts(); ItemLibrary(); LibraryItemList m_items; ImageMap m_imageMap; QStringMapMap m_itemDescriptions; // (Language, type) <--> description static KLocalizedString m_emptyItemDescription; // Description template for when a description does not yet exist friend ItemLibrary * itemLibrary(); }; inline ItemLibrary* itemLibrary() { // are we really sure we aren't calling new over and over again? static ItemLibrary *_itemLibrary = new ItemLibrary(); return _itemLibrary; } #endif diff --git a/src/itemview.cpp b/src/itemview.cpp index 2e385bbc..c328138f 100644 --- a/src/itemview.cpp +++ b/src/itemview.cpp @@ -1,842 +1,841 @@ /*************************************************************************** * Copyright (C) 2005-2006 David Saxton * * david@bluehaze.org * * * * 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 "canvasitemparts.h" #include "canvasmanipulator.h" #include "cnitem.h" #include "component.h" #include "connector.h" #include "docmanager.h" #include "drawpart.h" #include "ecnode.h" #include "itemdocument.h" #include "itemlibrary.h" #include "itemview.h" #include "ktechlab.h" #include "utils.h" //#include #include -#include #include // #include #include #include -#include -#include -#include -#include -#include -#include - -#include -#include +#include +#include +#include +#include +#include +#include +#include +#include +#include #include #include #include //BEGIN class ItemView ItemView::ItemView( ItemDocument * itemDocument, ViewContainer *viewContainer, uint viewAreaId, const char *name ) : View( itemDocument, viewContainer, viewAreaId, name ) { KActionCollection * ac = actionCollection(); KStandardAction::selectAll( itemDocument, SLOT(selectAll()), ac ); KStandardAction::zoomIn( this, SLOT(zoomIn()), ac ); KStandardAction::zoomOut( this, SLOT(zoomOut()), ac ); KStandardAction::actualSize( this, SLOT(actualSize()), ac )->setEnabled(false); //KAccel *pAccel = new KAccel(this); //pAccel->insert( "Cancel", i18n("Cancel"), i18n("Cancel the current operation"), Qt::Key_Escape, itemDocument, SLOT(cancelCurrentOperation()) ); //pAccel->readSettings(); // TODO what does this do? QAction *pAccel = new QAction( QIcon::fromTheme("process-stop"), i18n("Cancel"), ac); pAccel->setObjectName("cancelCurrentOperation"); pAccel->setShortcut(Qt::Key_Escape); connect(pAccel, SIGNAL(triggered(bool)), itemDocument, SLOT(cancelCurrentOperation())); ac->addAction("cancelCurrentOperation", pAccel); { //new KAction( i18n("Delete"), "edit-delete", Qt::Key_Delete, itemDocument, SLOT(deleteSelection()), ac, "edit_delete" ); QAction *action = new QAction( QIcon::fromTheme("edit-delete"), i18n("Delete"), ac); action->setObjectName("edit_delete"); action->setShortcut( Qt::Key_Delete ); connect(action, SIGNAL(triggered(bool)), itemDocument, SLOT(deleteSelection())); ac->addAction("edit_delete", action); } { //new KAction( i18n("Export as Image..."), 0, 0, itemDocument, SLOT(exportToImage()), ac, "file_export_image"); QAction *action = new QAction( QIcon::fromTheme("document-export"), i18n("Export as Image..."), ac); action->setObjectName("file_export_image"); connect(action, SIGNAL(triggered(bool)), itemDocument, SLOT(exportToImage())); ac->addAction("file_export_image", action); } //BEGIN Item Alignment actions { //new KAction( i18n("Align Horizontally"), 0, 0, itemDocument, SLOT(alignHorizontally()), ac, "align_horizontally" ); QAction *action = new QAction( QIcon::fromTheme("align-horizontal-center"), i18n("Align Horizontally"), ac); action->setObjectName("align_horizontally"); connect(action, SIGNAL(triggered(bool)), itemDocument, SLOT(alignHorizontally())); ac->addAction("align_horizontally", action); } { //new KAction( i18n("Align Vertically"), 0, 0, itemDocument, SLOT(alignVertically()), ac, "align_vertically" ); QAction *action = new QAction( QIcon::fromTheme("align-vertical-center"), i18n("Align Vertically"), ac); action->setObjectName("align_vertically"); connect(action, SIGNAL(triggered(bool)), itemDocument, SLOT(alignVertically())); ac->addAction("align_vertically", action); } { //new KAction( i18n("Distribute Horizontally"), 0, 0, itemDocument, SLOT(distributeHorizontally()), ac, "distribute_horizontally" ); QAction *action = new QAction( QIcon::fromTheme("distribute-horizontal-x"), i18n("Distribute Horizontally"), ac); action->setObjectName("distribute_horizontally"); connect(action, SIGNAL(triggered(bool)), itemDocument, SLOT(distributeHorizontally())); ac->addAction("distribute_horizontally", action); } { //new KAction( i18n("Distribute Vertically"), 0, 0, itemDocument, SLOT(distributeVertically()), ac, "distribute_vertically" ); QAction *action = new QAction( QIcon::fromTheme("distribute-vertical-y"), i18n("Distribute Vertically"), ac); action->setObjectName("distribute_vertically"); connect(action, SIGNAL(triggered(bool)), itemDocument, SLOT(distributeVertically())); ac->addAction("distribute_vertically", action); } //END Item Alignment actions //BEGIN Draw actions //KToolBarPopupAction * pa = new KToolBarPopupAction( i18n("Draw"), "paintbrush", 0, 0, 0, ac, "edit_draw" ); KToolBarPopupAction * pa = new KToolBarPopupAction( QIcon::fromTheme("draw-brush"), i18n("Draw"), ac); pa->setObjectName("edit_draw"); pa->setDelayed(false); ac->addAction("edit_draw", pa); QMenu * m = pa->menu(); m->setTitle( i18n("Draw") ); m->addAction( QIcon::fromTheme( "draw-text" ), i18n("Text"))->setData(DrawPart::da_text ); m->addAction( QIcon::fromTheme( "draw-line" ), i18n("Line"))->setData(DrawPart::da_line ); m->addAction( QIcon::fromTheme( "draw-arrow" ), i18n("Arrow"))->setData(DrawPart::da_arrow ); m->addAction( QIcon::fromTheme( "draw-ellipse" ), i18n("Ellipse"))->setData(DrawPart::da_ellipse ); m->addAction( QIcon::fromTheme( "draw-rectangle" ), i18n("Rectangle"))->setData(DrawPart::da_rectangle ); m->addAction( QIcon::fromTheme( "insert-image" ), i18n("Image"))->setData(DrawPart::da_image ); connect( m, SIGNAL(triggered(QAction*)), itemDocument, SLOT(slotSetDrawAction(QAction*)) ); //END Draw actions //BEGIN Item Control actions { //new KAction( i18n("Raise Selection"), "object-order-raise", Qt::Key_PageUp, itemDocument, SLOT(raiseZ()), ac, "edit_raise" ); QAction * action = new QAction( QIcon::fromTheme("object-order-raise"), i18n("Raise Selection"), ac); action->setObjectName("edit_raise"); action->setShortcut(Qt::Key_PageUp); connect(action, SIGNAL(triggered(bool)), itemDocument, SLOT(raiseZ())); ac->addAction("edit_raise", action); } { //new KAction( i18n("Lower Selection"), "object-order-lower", Qt::Key_PageDown, itemDocument, SLOT(lowerZ()), ac, "edit_lower" ); QAction *action = new QAction( QIcon::fromTheme("object-order-lower"), i18n("Lower Selection"), ac); action->setObjectName("edit_lower"); action->setShortcut(Qt::Key_PageDown); connect(action, SIGNAL(triggered(bool)), itemDocument, SLOT(lowerZ())); ac->addAction("edit_lower", action); } //END Item Control actions { //KAction * na = new KAction( "", 0, 0, 0, 0, ac, "null_action" ); QAction * na = new QAction( "", ac); na->setObjectName("null_action"); na->setEnabled(false); } setXMLFile( "ktechlabitemviewui.rc" ); m_pUpdateStatusTmr = new QTimer(this); connect( m_pUpdateStatusTmr, SIGNAL(timeout()), this, SLOT(updateStatus()) ); connect( this, SIGNAL(unfocused()), this, SLOT(stopUpdatingStatus()) ); m_pDragItem = nullptr; p_itemDocument = itemDocument; m_zoomLevel = 1.; m_CVBEditor = new CVBEditor( p_itemDocument->canvas(), this, "cvbEditor" ); m_CVBEditor->setLineWidth(1); connect( m_CVBEditor, SIGNAL(horizontalSliderReleased()), itemDocument, SLOT(requestCanvasResize()) ); connect( m_CVBEditor, SIGNAL(verticalSliderReleased()), itemDocument, SLOT(requestCanvasResize()) ); m_layout->insertWidget( 0, m_CVBEditor ); setAcceptDrops(true); setFocusWidget( m_CVBEditor->viewport() ); } ItemView::~ItemView() { } bool ItemView::canZoomIn() const { return true; } bool ItemView::canZoomOut() const { return int(std::floor((100*m_zoomLevel)+0.5)) > 25; } QPoint ItemView::mousePosToCanvasPos( const QPoint & contentsClick ) const { QPoint offsetPos = contentsClick + QPoint( cvbEditor()->contentsX(), cvbEditor()->contentsY() ); return (offsetPos / zoomLevel()) + p_itemDocument->canvas()->rect().topLeft(); } void ItemView::zoomIn( const QPoint & center ) { // NOTE The code in this function is nearly the same as that in zoomOut. // Any updates to this code should also be done to zoomOut // Previous position of center in widget coordinates QPoint previous = center * zoomLevel() - QPoint( cvbEditor()->contentsX(), cvbEditor()->contentsY() ); // Don't repaint the view until we've also shifted it cvbEditor()->viewport()->setUpdatesEnabled( false ); zoomIn(); // Adjust the contents' position to ensure that "previous" remains fixed QPoint offset = center * zoomLevel() - previous; cvbEditor()->setContentsPos( offset.x(), offset.y() ); cvbEditor()->viewport()->setUpdatesEnabled( true ); cvbEditor()->viewport()->update(); } void ItemView::zoomOut( const QPoint & center ) { // NOTE The code in this function is nearly the same as that in zoomIn. // Any updates to this code should also be done to zoomIn // Previous position of center in widget coordinates QPoint previous = center * zoomLevel() - QPoint( cvbEditor()->contentsX(), cvbEditor()->contentsY() ); // Don't repaint the view until we've also shifted it cvbEditor()->viewport()->setUpdatesEnabled( false ); zoomOut(); // Adjust the contents' position to ensure that "previous" remains fixed QPoint offset = center * zoomLevel() - previous; cvbEditor()->setContentsPos( offset.x(), offset.y() ); cvbEditor()->viewport()->setUpdatesEnabled( true ); cvbEditor()->viewport()->update(); } void ItemView::zoomIn() { int currentZoomPercent = int(std::floor((100*m_zoomLevel)+0.5)); int newZoom = currentZoomPercent; if ( currentZoomPercent < 100 ) newZoom += 25; else if ( currentZoomPercent < 200 ) newZoom += 50; else newZoom += 100; m_zoomLevel = newZoom/100.0; m_CVBEditor->updateWorldMatrix(); p_itemDocument->requestEvent( ItemDocument::ItemDocumentEvent::ResizeCanvasToItems ); updateZoomActions(); } void ItemView::zoomOut() { int currentZoomPercent = int(std::floor((100*m_zoomLevel)+0.5)); int newZoom = currentZoomPercent; if ( currentZoomPercent <= 25 ) return; if ( currentZoomPercent <= 100 ) newZoom -= 25; else if ( currentZoomPercent <= 200 ) newZoom -= 50; else newZoom -= 100; m_zoomLevel = newZoom/100.0; m_CVBEditor->updateWorldMatrix(); p_itemDocument->requestEvent( ItemDocument::ItemDocumentEvent::ResizeCanvasToItems ); updateZoomActions(); } void ItemView::actualSize() { m_zoomLevel = 1.0; QMatrix m( m_zoomLevel, 0.0, 0.0, m_zoomLevel, 1.0, 1.0 ); m_CVBEditor->setWorldMatrix(m); p_itemDocument->requestEvent( ItemDocument::ItemDocumentEvent::ResizeCanvasToItems ); updateZoomActions(); } void ItemView::updateZoomActions() { actionByName("view_zoom_in")->setEnabled( canZoomIn() ); actionByName("view_zoom_out")->setEnabled( canZoomOut() ); actionByName("view_actual_size")->setEnabled( m_zoomLevel != 1.0 ); } void ItemView::dropEvent( QDropEvent *event ) { removeDragItem(); const QMimeData* mimeData = event->mimeData(); if (mimeData->hasUrls()) { // Then it is URLs that we can decode :) const QList urls = mimeData->urls(); for (const QUrl &u : urls) { DocManager::self()->openURL(u); } return; } QString matchingFormat; for (QString format: mimeData->formats()) { if (format.startsWith("ktechlab/")) { matchingFormat = format; break; } } if (matchingFormat.isEmpty()) { return; } event->acceptProposedAction(); QString text; //QDataStream stream( event->encodedData(event->format()), QIODevice::ReadOnly ); QByteArray byteArray( mimeData->data(matchingFormat) ); QDataStream stream( &byteArray, QIODevice::ReadOnly); stream >> text; // Get a new component item p_itemDocument->addItem( text, mousePosToCanvasPos( event->pos() ), true ); setFocus(); } void ItemView::scrollToMouse( const QPoint & pos ) { QPoint viewPos = pos - p_itemDocument->canvas()->rect().topLeft(); viewPos *= m_zoomLevel; int x = viewPos.x(); int y = viewPos.y(); int left = m_CVBEditor->contentsX(); int top = m_CVBEditor->contentsY(); int width = m_CVBEditor->contentsWidth(); int height = m_CVBEditor->contentsHeight(); int right = left + m_CVBEditor->visibleWidth(); int bottom = top + m_CVBEditor->visibleHeight(); // A magic "snap" region whereby if the mouse is near the edge of the canvas, // then assume that we want to scroll right up to it int snapMargin = 32; if ( x < snapMargin ) x = 0; else if ( x > width - snapMargin ) x = width; if ( y < snapMargin ) y = 0; else if ( y > height - snapMargin ) y = height; if ( x < left ) m_CVBEditor->scrollBy( x - left, 0 ); else if ( x > right ) m_CVBEditor->scrollBy( x - right, 0 ); if ( y < top ) m_CVBEditor->scrollBy( 0, y - top ); else if ( y > bottom ) m_CVBEditor->scrollBy( 0, y - bottom); } void ItemView::contentsMousePressEvent( QMouseEvent *e ) { if (!e) return; e->accept(); if(!p_itemDocument ) return; EventInfo eventInfo( this, e ); if ( eventInfo.isRightClick && m_pDragItem ) { // We are dragging an item, and the user has right clicked. // Therefore, we want to rotate the item. /// @todo we should implement a virtual method in item for "rotating the item by one" /// - whatever that one may be (e.g. by 90 degrees, or changing the pin layout for /// flowparts, or nothing if the item isn't rotatable). if ( Component * c = dynamic_cast( m_pDragItem ) ) c->setAngleDegrees( c->angleDegrees() + 90 ); return; } p_itemDocument->canvas()->setMessage( QString::null ); p_itemDocument->m_cmManager->mousePressEvent( eventInfo ); } void ItemView::contentsMouseDoubleClickEvent( QMouseEvent *e ) { if (!e) return; e->accept(); //HACK: Pass this of as a single press event if widget underneath KtlQCanvasItem * atTop = p_itemDocument->itemAtTop( e->pos()/zoomLevel() ); if ( dynamic_cast(atTop) ) contentsMousePressEvent(e); else p_itemDocument->m_cmManager->mouseDoubleClickEvent( EventInfo( this, e ) ); } void ItemView::contentsMouseMoveEvent( QMouseEvent *e ) { // qDebug() << Q_FUNC_INFO << "state = " << e->state() << endl; if ( !e || !p_itemDocument ) return; e->accept(); EventInfo eventInfo( this, e ); p_itemDocument->m_cmManager->mouseMoveEvent( eventInfo ); if ( !m_pUpdateStatusTmr->isActive() ) startUpdatingStatus(); } void ItemView::contentsMouseReleaseEvent( QMouseEvent *e ) { if (!e) return; e->accept(); p_itemDocument->m_cmManager->mouseReleaseEvent( EventInfo( this, e ) ); } void ItemView::contentsWheelEvent( QWheelEvent *e ) { if (!e) return; e->accept(); EventInfo eventInfo( this, e ); if ( eventInfo.ctrlPressed ) { // Zooming in or out if ( eventInfo.scrollDelta > 0 ) zoomIn( eventInfo.pos ); else zoomOut( eventInfo.pos ); return; } p_itemDocument->m_cmManager->wheelEvent( eventInfo ); } void ItemView::dragEnterEvent( QDragEnterEvent *event ) { startUpdatingStatus(); if (event->mimeData()->hasUrls()) { event->setAccepted(true); // Then it is URLs that we can decode later :) return; } } void ItemView::createDragItem( QDragEnterEvent * e ) { removeDragItem(); const QMimeData* mimeData = e->mimeData(); QString matchingFormat; for (QString format: mimeData->formats()) { if (format.startsWith("ktechlab/")) { matchingFormat = format; break; } } if (matchingFormat.isEmpty()) { qWarning() << Q_FUNC_INFO << "Invalid mime data" << mimeData->formats(); return; } e->accept(); QString text; //QDataStream stream( e->encodedData(e->format()), QIODevice::ReadOnly ); //QByteArray byteArray( e->encodedData(e->format()) ); QByteArray byteArray( mimeData->data(matchingFormat) ); QDataStream stream( &byteArray, QIODevice::ReadOnly ); stream >> text; QPoint p = mousePosToCanvasPos( e->pos() ); m_pDragItem = itemLibrary()->createItem( text, p_itemDocument, true ); if ( CNItem * cnItem = dynamic_cast(m_pDragItem) ) { cnItem->move( snapToCanvas(p.x()), snapToCanvas(p.y()) ); } else { m_pDragItem->move( p.x(), p.y() ); } m_pDragItem->show(); } void ItemView::removeDragItem() { if ( !m_pDragItem ) return; m_pDragItem->removeItem(); p_itemDocument->flushDeleteList(); m_pDragItem = nullptr; } void ItemView::dragMoveEvent( QDragMoveEvent * e ) { if ( !m_pDragItem ) return; QPoint p = mousePosToCanvasPos( e->pos() ); if ( CNItem * cnItem = dynamic_cast(m_pDragItem) ) { cnItem->move( snapToCanvas(p.x()), snapToCanvas(p.y()) ); } else { m_pDragItem->move( p.x(), p.y() ); } } void ItemView::dragLeaveEvent( QDragLeaveEvent * ) { removeDragItem(); } void ItemView::enterEvent( QEvent * e ) { Q_UNUSED(e); startUpdatingStatus(); } void ItemView::leaveEvent( QEvent * e ) { Q_UNUSED(e); stopUpdatingStatus(); // Cleanup setCursor(Qt::ArrowCursor); if ( KTechlab::self() ) KTechlab::self()->slotChangeStatusbar(QString::null); if ( p_itemDocument ) p_itemDocument->m_canvasTip->setVisible(false); } void ItemView::requestDocumentResizeToCanvasItems() { p_itemDocument->requestEvent( ItemDocument::ItemDocumentEvent::ResizeCanvasToItems ); } void ItemView::slotUpdateConfiguration() { // m_CVBEditor->setEraseColor( KTLConfig::bgColor() ); //m_CVBEditor->setEraseColor( Qt::white ); // 2018.12.02 QPalette pe; pe.setColor(m_CVBEditor->backgroundRole(), Qt::white ); m_CVBEditor->setPalette(pe); if ( m_pUpdateStatusTmr->isActive() ) startUpdatingStatus(); } void ItemView::startUpdatingStatus() { m_pUpdateStatusTmr->stop(); m_pUpdateStatusTmr->start( int(1000./KTLConfig::refreshRate()) ); } void ItemView::stopUpdatingStatus() { m_pUpdateStatusTmr->stop(); } void ItemView::updateStatus() { QPoint pos = mousePosToCanvasPos( m_CVBEditor->mapFromGlobal( QCursor::pos() ) ); ItemDocument * itemDocument = static_cast(document()); if ( !itemDocument ) return; CMManager * cmManager = itemDocument->m_cmManager; CanvasTip * canvasTip = itemDocument->m_canvasTip; bool displayTip = false; QCursor cursor = Qt::ArrowCursor; QString statusbar; if ( cmManager->cmState() & CMManager::cms_repeated_add ) { cursor = Qt::CrossCursor; statusbar = i18n("Left click to add. Right click to resume normal editing"); } else if ( cmManager->cmState() & CMManager::cms_draw ) { cursor = Qt::CrossCursor; statusbar = i18n("Click and hold to start drawing."); } else if ( cmManager->currentManipulator()) { switch ( cmManager->currentManipulator()->type() ) { case CanvasManipulator::RepeatedItemAdd: cursor = Qt::CrossCursor; statusbar = i18n("Left click to add. Right click to resume normal editing"); break; case CanvasManipulator::ManualConnector: statusbar = i18n("Right click to cancel the connector"); // no break case CanvasManipulator::AutoConnector: cursor = Qt::CrossCursor; break; case CanvasManipulator::ItemMove: case CanvasManipulator::MechItemMove: cursor = Qt::SizeAllCursor; break; case CanvasManipulator::Draw: cursor = Qt::CrossCursor; break; default: break; } } else if ( KtlQCanvasItem *qcanvasItem = itemDocument->itemAtTop(pos) ) { if ( Connector * con = dynamic_cast(qcanvasItem) ) { cursor = Qt::CrossCursor; if ( itemDocument->type() == Document::dt_circuit ) { canvasTip->displayVI( con, pos ); displayTip = true; } } else if ( Node * node = dynamic_cast(qcanvasItem) ) { cursor = Qt::CrossCursor; if ( ECNode * ecnode = dynamic_cast(node) ) { canvasTip->displayVI( ecnode, pos ); displayTip = true; } } else if ( CNItem * item = dynamic_cast(qcanvasItem) ) { statusbar =item->name(); } } setCursor(cursor); if ( KTechlab::self() ) KTechlab::self()->slotChangeStatusbar(statusbar); canvasTip->setVisible(displayTip); } //END class ItemView //BEGIN class CVBEditor CVBEditor::CVBEditor( Canvas *canvas, ItemView *itemView, const char *name ) : KtlQCanvasView( canvas, itemView, name /*,Qt::WNoAutoErase | Qt::WA_StaticContents */ ) { setAttribute(Qt::WA_StaticContents); m_pCanvas = canvas; b_ignoreEvents = false; b_passEventsToView = true; p_itemView = itemView; setMouseTracking(true); viewport()->setMouseTracking(true); setAcceptDrops(true); setFrameShape(NoFrame); // setEraseColor( KTLConfig::bgColor() ); //setEraseColor( Qt::white ); //setPaletteBackgroundColor( Qt::white ); { QPalette p; p.setColor(backgroundRole(), Qt::white); setPalette(p); } //viewport()->setEraseColor( Qt::white ); //viewport()->setPaletteBackgroundColor( Qt::white ); // 2018.12.02 { QPalette pv; pv.setColor(viewport()->backgroundRole(), Qt::white ); viewport()->setPalette(pv); } connect( canvas, SIGNAL(resized( const QRect&, const QRect& )), this, SLOT(canvasResized( const QRect&, const QRect& )) ); } void CVBEditor::canvasResized( const QRect & oldSize, const QRect & newSize ) { updateWorldMatrix(); return; qDebug() << Q_FUNC_INFO << endl; QPoint delta = oldSize.topLeft() - newSize.topLeft(); delta *= p_itemView->zoomLevel(); scrollBy( delta.x(), delta.y() ); } void CVBEditor::updateWorldMatrix() { double z = p_itemView->zoomLevel(); QRect r = m_pCanvas->rect(); // QMatrix m( z, 0.0, 0.0, z, -r.left(), -r.top() ); // QMatrix m( z, 0.0, 0.0, z, 0.0, 0.0 ); QMatrix m; m.scale( z, z ); m.translate( -r.left(), -r.top() ); setWorldMatrix( m ); } void CVBEditor::contentsWheelEvent( QWheelEvent * e ) { QWheelEvent ce( viewport()->mapFromGlobal( e->globalPos() ), e->globalPos(), e->delta(), //e->state() e->buttons(), e->modifiers()); if ( e->orientation() == Qt::Horizontal && horizontalScrollBar() ) QApplication::sendEvent( horizontalScrollBar(), e); else if (e->orientation() == Qt::Vertical && verticalScrollBar() ) QApplication::sendEvent( verticalScrollBar(), e); #if 0 if ( b_ignoreEvents ) return; b_ignoreEvents = true; KtlQCanvasView::wheelEvent( e ); b_ignoreEvents = false; #endif } bool CVBEditor::event( QEvent * e ) { if ( !b_passEventsToView ) { bool isWheel = e->type() == QEvent::Wheel; if ( isWheel && b_ignoreEvents ) return false; b_ignoreEvents = isWheel; bool accepted = KtlQCanvasView::event( e ); b_ignoreEvents = false; return accepted; } switch ( e->type() ) { case QEvent::MouseButtonPress: p_itemView->contentsMousePressEvent( (QMouseEvent*)e ); return ((QMouseEvent*)e)->isAccepted(); case QEvent::MouseButtonRelease: p_itemView->contentsMouseReleaseEvent( (QMouseEvent*)e ); return ((QMouseEvent*)e)->isAccepted(); case QEvent::MouseButtonDblClick: p_itemView->contentsMouseDoubleClickEvent( (QMouseEvent*)e ); return ((QMouseEvent*)e)->isAccepted(); case QEvent::MouseMove: p_itemView->contentsMouseMoveEvent( (QMouseEvent*)e ); return ((QMouseEvent*)e)->isAccepted(); case QEvent::DragEnter: p_itemView->dragEnterEvent((QDragEnterEvent*)e ); return true; case QEvent::DragMove: p_itemView->dragMoveEvent((QDragMoveEvent*)e ); return true; case QEvent::DragLeave: p_itemView->dragLeaveEvent((QDragLeaveEvent*)e ); return true; case QEvent::Drop: p_itemView->dropEvent( (QDropEvent*)e ); return true; case QEvent::Enter: p_itemView->enterEvent( e ); return true; case QEvent::Leave: p_itemView->leaveEvent(e); return true; case QEvent::Wheel: p_itemView->contentsWheelEvent( (QWheelEvent*)e ); return ((QWheelEvent*)e)->isAccepted(); default: return KtlQCanvasView::event( e ); } } void CVBEditor::viewportResizeEvent( QResizeEvent * e ) { KtlQCanvasView::viewportResizeEvent(e); // 2018.09.26 - move to explicit method //p_itemView->p_itemDocument->requestEvent( ItemDocument::ItemDocumentEvent::ResizeCanvasToItems ); p_itemView->requestDocumentResizeToCanvasItems(); } //END class CVBEditor diff --git a/src/itemview.h b/src/itemview.h index e02842a2..eaf62781 100644 --- a/src/itemview.h +++ b/src/itemview.h @@ -1,150 +1,150 @@ /*************************************************************************** * Copyright (C) 2005-2006 David Saxton * * david@bluehaze.org * * * * 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. * ***************************************************************************/ #ifndef ITEMVIEW_H #define ITEMVIEW_H #include #include -#include +#include class Canvas; class CVBEditor; class Item; class ItemDocument; class QTimer; /** @author David Saxton */ class ItemView : public View { Q_OBJECT public: ItemView( ItemDocument *itemDocument, ViewContainer *viewContainer, uint viewAreaId, const char *name = nullptr ); ~ItemView() override; bool canZoomIn() const override; bool canZoomOut() const override; CVBEditor *cvbEditor() const { return m_CVBEditor; } /** * @returns The zoom level */ double zoomLevel() const { return m_zoomLevel; } /** * When the user drags from an item selector into the item view, the * item will be created and shown to the user. This function returns * that item. */ Item * dragItem() const { return m_pDragItem; } /** * Zoom in. The point center will remain fixed. */ void zoomIn( const QPoint & center ); /** * Zoom in. The point center will remain fixed. */ void zoomOut( const QPoint & center ); /** * Converts a mouse click position (in the contents coordinates) to the * associated position on the canvas. */ QPoint mousePosToCanvasPos( const QPoint & contentsClick ) const; public slots: void actualSize() override; void zoomIn(); void zoomOut(); void scrollToMouse( const QPoint &pos ); virtual void updateStatus(); protected slots: /** * Called when the user changes the configuration. */ void slotUpdateConfiguration() override; void startUpdatingStatus(); void stopUpdatingStatus(); protected: /** * If the user drags an acceptable item in (e.g. a component in a * circuit), then call this function to create the item and have it * moved when the user moves his mouse while dragging. */ void createDragItem( QDragEnterEvent * event ); void removeDragItem(); void updateZoomActions(); public: /** * Attempts to create a new CNItem if one was dragged onto the canvas */ void dropEvent( QDropEvent * event ) override; /** * Reinherit to allow different types of items to be dragged in. */ void dragEnterEvent( QDragEnterEvent * event ) override; void dragLeaveEvent( QDragLeaveEvent * event ) override; void dragMoveEvent( QDragMoveEvent * event ) override; void contentsMousePressEvent( QMouseEvent *e ); void contentsMouseReleaseEvent( QMouseEvent *e ); void contentsMouseDoubleClickEvent( QMouseEvent *e ); void contentsMouseMoveEvent( QMouseEvent *e ); void contentsWheelEvent( QWheelEvent *e ); void enterEvent( QEvent * e ) override; void leaveEvent( QEvent * e ) override; void requestDocumentResizeToCanvasItems(); protected: QPointer p_itemDocument; CVBEditor *m_CVBEditor; double m_zoomLevel; QTimer * m_pUpdateStatusTmr; Item * m_pDragItem; //friend class CVBEditor; // 2018.09.26 }; /** @author David Saxton */ class CVBEditor : public KtlQCanvasView { Q_OBJECT public: CVBEditor( Canvas * canvas, ItemView *itemView, const char *name ); void setPassEventsToView( bool pass ) { b_passEventsToView = pass; } bool event( QEvent * e ) override; void contentsWheelEvent( QWheelEvent * e ) override; /** * Updates the world matrix from ItmeView's zoom level and from Canvas' * offset. */ void updateWorldMatrix(); protected slots: void canvasResized( const QRect & oldSize, const QRect & newSize ); protected: void viewportResizeEvent( QResizeEvent * ) override; ItemView *p_itemView; bool b_passEventsToView; bool b_ignoreEvents; Canvas * m_pCanvas; }; #endif diff --git a/src/katemdi.cpp b/src/katemdi.cpp index 9cb4d570..8b789360 100644 --- a/src/katemdi.cpp +++ b/src/katemdi.cpp @@ -1,938 +1,939 @@ /* This file is part of the KDE libraries Copyright (C) 2005 Christoph Cullmann Copyright (C) 2002, 2003 Joseph Wenninger GUIClient partly based on ktoolbarhandler.cpp: Copyright (C) 2002 Simon Hausmann 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 Steet, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "katemdi.h" #include #include //#include #include #include #include #include // #include -#include #include +#include +#include #include #include #include #include typedef QList IntList; static QString getSpaces(int count) { QString s; for (int i = 0; i < count; ++i ) { s += "| "; } return s; } static void printObjChildren(int startLevel, const QObject *obj) { QObjectList chl = obj->children(); QMutableListIterator itCh( chl ); while (itCh.hasNext()) { itCh.next(); qDebug() << getSpaces(startLevel) << itCh.value(); printObjChildren(startLevel + 1, itCh.value()); } } namespace KateMDI { //BEGIN SPLITTER Splitter::Splitter(Qt::Orientation o, QWidget* parent, const char* name) : QSplitter(o, parent /*, name*/) { setObjectName(name); } Splitter::~Splitter() { } /* bool Splitter::isLastChild(QWidget* w) const { return indexOf(w) == (count() - 1); //return ( idAfter( w ) == 0 ); } int Splitter::idAfter ( QWidget * w ) const { //return QSplitter::idAfter (w); return indexOf(w); }*/ //END SPLITTER //BEGIN GUICLIENT GUIClient::GUIClient ( MainWindow *mw ) : QObject ( mw ) , KXMLGUIClient ( mw ) , m_mw (mw) { connect( m_mw->guiFactory(), SIGNAL( clientAdded( KXMLGUIClient * ) ), this, SLOT( clientAdded( KXMLGUIClient * ) ) ); //if (actionCollection()->kaccel()==0) // TODO what does this do? { actionCollection()->associateWidget(m_mw); } // read shortcuts //actionCollection()->readShortcutSettings( "Shortcuts", kapp->config() ); KConfigGroup grShortcuts = KSharedConfig::openConfig()->group("Shortcuts"); actionCollection()->readSettings(&grShortcuts); } GUIClient::~GUIClient() { } void GUIClient::registerToolView (ToolView *tv) { QString aname = QString("kate_mdi_toolview_") + tv->id; // try to read the action shortcut // KShortcut sc; // KConfig *cfg = kapp->config(); // QString _grp = cfg->group(); // cfg->setGroup("Shortcuts"); // sc = KShortcut( cfg->readEntry( aname, "" ) ); // cfg->setGroup( _grp ); KConfigGroup grSh = KSharedConfig::openConfig()->group("Shortcuts"); // sc = KShortcut( grSh.readEntry(aname, "") ); } void GUIClient::clientAdded( KXMLGUIClient *client ) { if ( client == this ) updateActions(); } void GUIClient::updateActions() { if ( !factory() ) return; } //END GUICLIENT //BEGIN TOOLVIEW ToolView::ToolView (MainWindow *mainwin, Sidebar *sidebar, QWidget *parent) : QWidget (parent) , m_mainWin (mainwin) , m_sidebar (sidebar) , m_visible (false) , persistent (false) { QVBoxLayout *vboxLayout = new QVBoxLayout; setLayout(vboxLayout); } ToolView::~ToolView () { m_mainWin->toolViewDeleted (this); } void ToolView::setVisibleToolView (bool vis) { if (m_visible == vis) return; m_visible = vis; emit visibleChanged (m_visible); } bool ToolView::visible () const { return m_visible; } void ToolView::childEvent ( QChildEvent *ev ) { // set the widget to be focus proxy if possible QWidget *childWidget = qobject_cast< QWidget* >( ev->child() ); if ((ev->type() == QEvent::ChildAdded) && ev->child() && childWidget) { //setFocusProxy ((QWidget *)(ev->child()->qt_cast("QWidget"))); setFocusProxy( childWidget ); } QWidget::childEvent (ev); } //END TOOLVIEW //BEGIN SIDEBAR Sidebar::Sidebar (KMultiTabBar::KMultiTabBarPosition pos, MainWindow *mainwin, QWidget *parent) : KMultiTabBar ( //(pos == KMultiTabBar::Top || pos == KMultiTabBar::Bottom) ? KMultiTabBar::Horizontal : KMultiTabBar::Vertical, parent) pos, parent) , m_mainWin (mainwin) , m_splitter (nullptr) , m_ownSplit (nullptr) , m_lastSize (0) { setObjectName( (QString("Sidebar-%1").arg(pos)).toLatin1().data() ); setSidebarPosition( pos ); setFocusPolicy( Qt::NoFocus ); hide (); } Sidebar::~Sidebar () { } void Sidebar::setSidebarPosition( KMultiTabBarPosition pos ) { m_pos = pos; setPosition(pos); } void Sidebar::setSidebarStyle( KMultiTabBarStyle style ) { m_sidebarTabStyle = style; setStyle(style); } void Sidebar::setSplitter (Splitter *sp) { m_splitter = sp; Qt::Orientation splitOrient = (sidebarPosition() == KMultiTabBar::Top || sidebarPosition() == KMultiTabBar::Bottom) ? Qt::Horizontal : Qt::Vertical; m_ownSplit = new Splitter (splitOrient, m_splitter, "own-Split"); m_ownSplit->setChildrenCollapsible( false ); //m_splitter->setResizeMode( m_ownSplit, QSplitter::KeepSize ); // 2018.11.22 m_splitter->setStretchFactor( m_splitter->indexOf(m_ownSplit), 0); m_ownSplit->hide (); } ToolView *Sidebar::addWidget (const QPixmap &icon, const QString &text, ToolView *widget) { static int id = 0; if (widget) { if (widget->sidebar() == this) return widget; widget->sidebar()->removeWidget (widget); } int newId = ++id; appendTab (icon, newId, text); if (!widget) { widget = new ToolView (m_mainWin, this, m_ownSplit); widget->hide (); widget->icon = icon; widget->text = text; } else { widget->hide (); //widget->reparent (m_ownSplit, 0, QPoint()); // 2018.11.22 widget->setParent(m_ownSplit, nullptr); QPoint p; widget->setGeometry(p.x(),p.y(),width(),height()); widget->m_sidebar = this; } // save it's pos ;) widget->persistent = false; m_idToWidget.insert (newId, widget); m_widgetToId.insert (widget, newId); m_toolviews.push_back (widget); show (); connect(tab(newId),SIGNAL(clicked(int)),this,SLOT(tabClicked(int))); tab(newId)->installEventFilter(this); return widget; } void Sidebar::updateMinimumSize() { // qDebug() << "layout()->margin()="<margin()<::iterator end = m_toolviews.end(); for ( QList::iterator it = m_toolviews.begin(); it != end; ++it ) { QSize s = (*it)->childrenRect().size(); minSize = minSize.expandedTo( s ); // qDebug() << "s="<minimumSize()="<<(*it)->minimumSize()<layout()->margin()="<<(*it)->margin()<::iterator it = m_toolviews.begin(); it != end; ++it ) { (*it)->setMinimumSize( minSize ); } } bool Sidebar::removeWidget (ToolView *widget) { if (!m_widgetToId.contains(widget)) return false; removeTab(m_widgetToId[widget]); m_idToWidget.remove (m_widgetToId[widget]); m_widgetToId.remove (widget); m_toolviews.removeAll(widget); bool anyVis = false; //Q3IntDictIterator it( m_idToWidget ); for ( QMap::iterator it = m_idToWidget.begin(); it != m_idToWidget.end(); ++it ) { if (!anyVis) anyVis = it.value()->isVisible(); } if (m_idToWidget.isEmpty()) { m_ownSplit->hide (); hide (); } else if (!anyVis) m_ownSplit->hide (); return true; } bool Sidebar::showWidget (ToolView *widget) { if (!m_widgetToId.contains(widget)) return false; // hide other non-persistent views //Q3IntDictIterator it( m_idToWidget ); for ( QMap::iterator it = m_idToWidget.begin(); it != m_idToWidget.end(); ++it ) if ((it.value() != widget) && !it.value()->persistent) { it.value()->hide(); setTab (it.key(), false); it.value()->setVisibleToolView(false); } setTab (m_widgetToId[widget], true); m_ownSplit->show (); widget->show (); widget->setVisibleToolView (true); return true; } bool Sidebar::hideWidget (ToolView *widget) { if (!m_widgetToId.contains(widget)) return false; bool anyVis = false; updateLastSize (); //for ( Q3IntDictIterator it( m_idToWidget ); it.current(); ++it ) for ( QMap::iterator it = m_idToWidget.begin(); it != m_idToWidget.end(); ++it ) { if (it.value() == widget) { it.value()->hide(); continue; } if (!anyVis) anyVis = it.value()->isVisible(); } // lower tab setTab (m_widgetToId[widget], false); if (!anyVis) m_ownSplit->hide (); widget->setVisibleToolView (false); return true; } void Sidebar::tabClicked(int i) { ToolView *w = m_idToWidget[i]; if (!w) { qWarning() << Q_FUNC_INFO << " unexpected tab number " << i; return; } if (isTabRaised(i)) { showWidget (w); w->setFocus (); } else { hideWidget (w); m_mainWin->centralWidget()->setFocus (); } } bool Sidebar::eventFilter(QObject *obj, QEvent *ev) { if (ev->type()==QEvent::ContextMenu) { QContextMenuEvent *e = (QContextMenuEvent *) ev; KMultiTabBarTab *bt = dynamic_cast(obj); if (bt) { qDebug()<<"Request for popup"<id(); ToolView *w = m_idToWidget[m_popupButton]; if (w) { QMenu *p = new QMenu (this); p->addSection(QIcon::fromTheme("view_remove"), i18n("Behavior")); //p->insertItem(w->persistent ? SmallIcon("view-restore") : SmallIcon("view-fullscreen"), w->persistent ? i18n("Make Non-Persistent") : i18n("Make Persistent"), 10); // 2018.11.22 p->addAction( w->persistent ? QIcon::fromTheme("view-restore") : QIcon::fromTheme("view-fullscreen"), w->persistent ? i18n("Make Non-Persistent") : i18n("Make Persistent") )->setData(10); p->addSection(QIcon::fromTheme("transform-move"), i18n("Move To")); if (sidebarPosition() != 0) //p->insertItem(SmallIcon("go-previous"), i18n("Left Sidebar"),0); // 2018.11.22 p->addAction(QIcon::fromTheme("go-previous"), i18n("Left Sidebar"))->setData(0); if (sidebarPosition() != 1) //p->insertItem(SmallIcon("go-next"), i18n("Right Sidebar"),1); // 2018.11.22 p->addAction(QIcon::fromTheme("go-next"), i18n("Right Sidebar"))->setData(1); if (sidebarPosition() != 2) //p->insertItem(SmallIcon("go-up"), i18n("Top Sidebar"),2); // 2018.11.22 p->addAction(QIcon::fromTheme("go-up"), i18n("Top Sidebar"))->setData(2); if (sidebarPosition() != 3) { //p->insertItem(SmallIcon("go-down"), i18n("Bottom Sidebar"),3); // 2018.11.22 p->addAction(QIcon::fromTheme("go-down"), i18n("Bottom Sidebar"))->setData(3); } connect(p, SIGNAL(triggered(QAction*)), this, SLOT(buttonPopupActivate(QAction*))); p->exec(e->globalPos()); delete p; return true; } } } return false; } void Sidebar::buttonPopupActivate (QAction* action) { int id = action->data().toInt(); ToolView *w = m_idToWidget[m_popupButton]; if (!w) return; // move ids if (id < 4) { // move + show ;) m_mainWin->moveToolView (w, (KMultiTabBar::KMultiTabBarPosition) id); m_mainWin->showToolView (w); } // toggle persistent if (id == 10) w->persistent = !w->persistent; } void Sidebar::updateLastSize () { QList s = m_splitter->sizes (); int i = 0; if ((sidebarPosition() == KMultiTabBar::Right || sidebarPosition() == KMultiTabBar::Bottom)) i = 2; // little threshold if (s[i] > 2) m_lastSize = s[i]; } class TmpToolViewSorter { public: ToolView *tv; unsigned int pos; }; void Sidebar::restoreSession (KConfigGroup *configGr) { // get the last correct placed toolview unsigned int firstWrong = 0; for ( ; firstWrong < m_toolviews.size(); ++firstWrong ) { ToolView *tv = m_toolviews[firstWrong]; unsigned int pos = configGr->readEntry (QString ("Kate-MDI-ToolView-%1-Sidebar-Position").arg(tv->id), firstWrong); if (pos != firstWrong) break; } // we need to reshuffle, ahhh :( if (firstWrong < m_toolviews.size()) { // first: collect the items to reshuffle QList toSort; for (unsigned int i=firstWrong; i < m_toolviews.size(); ++i) { TmpToolViewSorter s; s.tv = m_toolviews[i]; s.pos = configGr->readEntry (QString ("Kate-MDI-ToolView-%1-Sidebar-Position").arg(m_toolviews[i]->id), i); toSort.push_back (s); } // now: sort the stuff we need to reshuffle for (unsigned int m=0; m < toSort.size(); ++m) for (unsigned int n=m+1; n < toSort.size(); ++n) if (toSort[n].pos < toSort[m].pos) { TmpToolViewSorter tmp = toSort[n]; toSort[n] = toSort[m]; toSort[m] = tmp; } // then: remove this items from the button bar // do this backwards, to minimize the relayout efforts for (int i=m_toolviews.size()-1; i >= (int)firstWrong; --i) { removeTab (m_widgetToId[m_toolviews[i]]); } // insert the reshuffled things in order :) for (unsigned int i=0; i < toSort.size(); ++i) { ToolView *tv = toSort[i].tv; m_toolviews[firstWrong+i] = tv; // readd the button int newId = m_widgetToId[tv]; appendTab (tv->icon, newId, tv->text); connect(tab(newId),SIGNAL(clicked(int)),this,SLOT(tabClicked(int))); tab(newId)->installEventFilter(this); // reshuffle in splitter m_ownSplit->addWidget(tv); } } // update last size if needed updateLastSize (); // restore the own splitter sizes QList s = configGr->readEntry (QString ("Kate-MDI-Sidebar-%1-Splitter").arg(sidebarPosition()), IntList()); m_ownSplit->setSizes (s); // show only correct toolviews, remember persistent values ;) bool anyVis = false; for ( unsigned int i=0; i < m_toolviews.size(); ++i ) { ToolView *tv = m_toolviews[i]; tv->persistent = configGr->readEntry (QString ("Kate-MDI-ToolView-%1-Persistent").arg(tv->id), false); tv->setVisibleToolView (configGr->readEntry (QString ("Kate-MDI-ToolView-%1-Visible").arg(tv->id), false)); if (!anyVis) anyVis = tv->visible(); setTab (m_widgetToId[tv],tv->visible()); if (tv->visible()) tv->show(); else tv->hide (); } if (anyVis) m_ownSplit->show(); else m_ownSplit->hide(); } void Sidebar::saveSession (KConfigGroup *config) { // store the own splitter sizes QList s = m_ownSplit->sizes(); config->writeEntry (QString ("Kate-MDI-Sidebar-%1-Splitter").arg(sidebarPosition()), s); // store the data about all toolviews in this sidebar ;) for ( unsigned int i=0; i < m_toolviews.size(); ++i ) { ToolView *tv = m_toolviews[i]; config->writeEntry (QString ("Kate-MDI-ToolView-%1-Position").arg(tv->id), (int)tv->sidebar()->sidebarPosition()); config->writeEntry (QString ("Kate-MDI-ToolView-%1-Sidebar-Position").arg(tv->id), i); config->writeEntry (QString ("Kate-MDI-ToolView-%1-Visible").arg(tv->id), tv->visible()); config->writeEntry (QString ("Kate-MDI-ToolView-%1-Persistent").arg(tv->id), tv->persistent); } } //END SIDEBAR //BEGIN MAIN WINDOW MainWindow::MainWindow (QWidget* parentWidget, const char* name) : KParts::MainWindow(parentWidget) , m_restoreConfig (nullptr) , m_guiClient (new GUIClient (this)) { setObjectName(name); // init the internal widgets QWidget *hb = new QWidget(this); // Q3HBox (this); QHBoxLayout *hbl = new QHBoxLayout; hb->setLayout(hbl); hb->setObjectName("MainWindow-central-HBox"); setCentralWidget(hb); m_sidebars[KMultiTabBar::Left] = new Sidebar (KMultiTabBar::Left, this, hb); m_sidebars[KMultiTabBar::Left]->setObjectName("Main-Left-Sidebar"); hbl->addWidget( m_sidebars[KMultiTabBar::Left] ); m_hSplitter = new Splitter (Qt::Horizontal, hb); m_hSplitter->setObjectName("Main-Left-Splitter"); hbl->addWidget( m_hSplitter ); m_sidebars[KMultiTabBar::Left]->setSplitter (m_hSplitter); //Q3VBox *vb = new Q3VBox (m_hSplitter); QWidget *vb = new QWidget (m_hSplitter); QVBoxLayout *vbl = new QVBoxLayout; vb->setLayout(vbl); vb->setObjectName("Main-Center-VBox"); m_hSplitter->setCollapsible(m_hSplitter->indexOf(vb), false); m_sidebars[KMultiTabBar::Top] = new Sidebar (KMultiTabBar::Top, this, vb); m_sidebars[KMultiTabBar::Top]->setObjectName("Main-Top-Sidebar"); vbl->addWidget( m_sidebars[KMultiTabBar::Top] ); m_vSplitter = new Splitter (Qt::Vertical, vb); m_vSplitter->setObjectName("Main-Top-Splitter"); vbl->addWidget( m_vSplitter ); m_sidebars[KMultiTabBar::Top]->setSplitter (m_vSplitter); m_centralWidget = new QWidget (m_vSplitter); //m_centralWidget = new Q3VBox (m_vSplitter ); QVBoxLayout *vbCl = new QVBoxLayout; m_centralWidget->setLayout( vbCl ); m_centralWidget->setObjectName("Main-Central-Vbox"); m_vSplitter->setCollapsible(m_vSplitter->indexOf(m_centralWidget), false); //vbl->addLayout( vbCl ); // 2016.05.03 - apparently generates a warning about already having a parent m_sidebars[KMultiTabBar::Bottom] = new Sidebar (KMultiTabBar::Bottom, this, vb); m_sidebars[KMultiTabBar::Bottom]->setObjectName("Main-Bottom-Sidebar"); m_sidebars[KMultiTabBar::Bottom]->setSplitter (m_vSplitter); vbl->addWidget( m_sidebars[KMultiTabBar::Bottom] ); m_sidebars[KMultiTabBar::Right] = new Sidebar (KMultiTabBar::Right, this, hb); m_sidebars[KMultiTabBar::Right]->setObjectName("Main-Right-Sidebar"); m_sidebars[KMultiTabBar::Right]->setSplitter (m_hSplitter); hbl->addWidget( m_sidebars[KMultiTabBar::Right] ); } MainWindow::~MainWindow () { // cu toolviews while (!m_toolviews.isEmpty()) delete m_toolviews[0]; // seems like we really should delete this by hand ;) delete m_centralWidget; for (unsigned int i=0; i < 4; ++i) delete m_sidebars[i]; } QWidget *MainWindow::centralWidget () const { return m_centralWidget; } ToolView *MainWindow::createToolView (const QString &identifier, KMultiTabBar::KMultiTabBarPosition pos, const QPixmap &icon, const QString &text) { if (m_idToWidget[identifier]) return nullptr; // try the restore config to figure out real pos if (m_restoreConfig && m_restoreConfig->hasGroup (m_restoreGroup)) { KConfigGroup grRest = m_restoreConfig->group (m_restoreGroup); pos = (KMultiTabBar::KMultiTabBarPosition) grRest.readEntry (QString ("Kate-MDI-ToolView-%1-Position").arg(identifier), (int) pos); } ToolView *v = m_sidebars[pos]->addWidget (icon, text, nullptr); v->id = identifier; m_idToWidget.insert (identifier, v); m_toolviews.push_back (v); // register for menu stuff m_guiClient->registerToolView (v); return v; } ToolView *MainWindow::toolView (const QString &identifier) const { return m_idToWidget[identifier]; } void MainWindow::toolViewDeleted (ToolView *widget) { if (!widget) return; if (widget->mainWindow() != this) return; // unregister from menu stuff widget->sidebar()->removeWidget (widget); m_idToWidget.remove (widget->id); m_toolviews.removeAll (widget); } void MainWindow::setToolViewStyle (KMultiTabBar::KMultiTabBarStyle style) { m_sidebars[0]->setSidebarStyle(style); m_sidebars[1]->setSidebarStyle(style); m_sidebars[2]->setSidebarStyle(style); m_sidebars[3]->setSidebarStyle(style); } KMultiTabBar::KMultiTabBarStyle MainWindow::toolViewStyle () const { // all sidebars have the same style, so just take Top return m_sidebars[KMultiTabBar::Top]->sidebarTabStyle(); } bool MainWindow::moveToolView (ToolView *widget, KMultiTabBar::KMultiTabBarPosition pos) { if (!widget || widget->mainWindow() != this) return false; // try the restore config to figure out real pos if (m_restoreConfig && m_restoreConfig->hasGroup (m_restoreGroup)) { KConfigGroup grRest = m_restoreConfig->group (m_restoreGroup); pos = (KMultiTabBar::KMultiTabBarPosition) grRest.readEntry (QString ("Kate-MDI-ToolView-%1-Position").arg(widget->id), (int) pos); } m_sidebars[pos]->addWidget (widget->icon, widget->text, widget); return true; } bool MainWindow::showToolView (ToolView *widget) { if (!widget || widget->mainWindow() != this) return false; // skip this if happens during restoring, or we will just see flicker if (m_restoreConfig && m_restoreConfig->hasGroup (m_restoreGroup)) return true; return widget->sidebar()->showWidget (widget); } bool MainWindow::hideToolView (ToolView *widget) { if (!widget || widget->mainWindow() != this) return false; // skip this if happens during restoring, or we will just see flicker if (m_restoreConfig && m_restoreConfig->hasGroup (m_restoreGroup)) return true; return widget->sidebar()->hideWidget (widget); } void MainWindow::startRestore (KConfig *config, const QString &group) { // first save this stuff m_restoreConfig = config; m_restoreGroup = group; if (!m_restoreConfig || !m_restoreConfig->hasGroup (m_restoreGroup)) { //BEGIN Added stuff specifically for ktechlab QList hs; hs << 220 << 100 << 230; QList vs; vs << 0 << 100 << 150; m_sidebars[0]->setLastSize (hs[0]); m_sidebars[1]->setLastSize (hs[2]); m_sidebars[2]->setLastSize (vs[0]); m_sidebars[3]->setLastSize (vs[2]); m_hSplitter->setSizes(hs); m_vSplitter->setSizes(vs); //END Added stuff specifically for ktechlab return; } // apply size once, to get sizes ready ;) KConfigGroup grRestWnd = m_restoreConfig->group (m_restoreGroup); KWindowConfig::restoreWindowSize(windowHandle(), grRestWnd); //m_restoreConfig->group (m_restoreGroup); // get main splitter sizes ;) QList hs = grRestWnd.readEntry ("Kate-MDI-H-Splitter", IntList()); QList vs = grRestWnd.readEntry ("Kate-MDI-V-Splitter", IntList()); m_sidebars[0]->setLastSize (hs[0]); m_sidebars[1]->setLastSize (hs[2]); m_sidebars[2]->setLastSize (vs[0]); m_sidebars[3]->setLastSize (vs[2]); m_hSplitter->setSizes(hs); m_vSplitter->setSizes(vs); setToolViewStyle( (KMultiTabBar::KMultiTabBarStyle)grRestWnd.readEntry ("Kate-MDI-Sidebar-Style", (int)toolViewStyle()) ); } void MainWindow::finishRestore () { if (!m_restoreConfig) return; if (m_restoreConfig->hasGroup (m_restoreGroup)) { // apply all settings, like toolbar pos and more ;) applyMainWindowSettings(m_restoreConfig->group(m_restoreGroup)); // reshuffle toolviews only if needed KConfigGroup grRest = m_restoreConfig->group (m_restoreGroup); for ( unsigned int i=0; i < m_toolviews.size(); ++i ) { KMultiTabBar::KMultiTabBarPosition newPos = (KMultiTabBar::KMultiTabBarPosition)grRest.readEntry( QString ("Kate-MDI-ToolView-%1-Position").arg(m_toolviews[i]->id), (int) m_toolviews[i]->sidebar()->sidebarPosition()); if (m_toolviews[i]->sidebar()->sidebarPosition() != newPos) { moveToolView (m_toolviews[i], newPos); } } // restore the sidebars for (unsigned int i=0; i < 4; ++i) m_sidebars[i]->restoreSession (&grRest); } // clear this stuff, we are done ;) m_restoreConfig = nullptr; m_restoreGroup = ""; } void MainWindow::saveSession (KConfigGroup *grConf) { if (!grConf) return; saveMainWindowSettings (*grConf); // save main splitter sizes ;) QList hs = m_hSplitter->sizes(); QList vs = m_vSplitter->sizes(); if (hs[0] <= 2 && !m_sidebars[0]->splitterVisible ()) hs[0] = m_sidebars[0]->lastSize(); if (hs[2] <= 2 && !m_sidebars[1]->splitterVisible ()) hs[2] = m_sidebars[1]->lastSize(); if (vs[0] <= 2 && !m_sidebars[2]->splitterVisible ()) vs[0] = m_sidebars[2]->lastSize(); if (vs[2] <= 2 && !m_sidebars[3]->splitterVisible ()) vs[2] = m_sidebars[3]->lastSize(); grConf->writeEntry ("Kate-MDI-H-Splitter", hs); grConf->writeEntry ("Kate-MDI-V-Splitter", vs); // save sidebar style grConf->writeEntry ("Kate-MDI-Sidebar-Style", (int)toolViewStyle()); // save the sidebars for (unsigned int i=0; i < 4; ++i) m_sidebars[i]->saveSession (grConf); } void KateMDI::MainWindow::updateSidebarMinimumSizes( ) { // for (unsigned int i=0; i < 4; ++i) // m_sidebars[i]->updateMinimumSize(); m_sidebars[KMultiTabBar::Right]->updateMinimumSize(); } //END MAIN WINDOW } diff --git a/src/katemdi.h b/src/katemdi.h index 486b6995..d21ec668 100644 --- a/src/katemdi.h +++ b/src/katemdi.h @@ -1,405 +1,405 @@ /* This file is part of the KDE libraries Copyright (C) 2005 Christoph Cullmann Copyright (C) 2002, 2003 Joseph Wenninger 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 Steet, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef __KATE_MDI_H__ #define __KATE_MDI_H__ #include #include // #include // #include // #include -#include +#include #include class QAction; namespace KateMDI { class MainWindow; class ToolView; /** This class is needed because QSplitter cant return an index for a widget. */ class Splitter : public QSplitter { Q_OBJECT public: Splitter(Qt::Orientation o, QWidget* parent = nullptr, const char* name = nullptr); ~Splitter() override; /** Since there is supposed to be only 2 childs of a katesplitter, * any child other than the last is the first. * This method uses QSplitter::idAfter(widget) which * returns 0 if there is no widget after this one. * This results in an error if widget is not a child * in this splitter */ //bool isLastChild(QWidget* w) const; //int idAfter ( QWidget * w ) const; }; class GUIClient : public QObject, public KXMLGUIClient { Q_OBJECT public: GUIClient ( MainWindow *mw ); ~GUIClient() override; void registerToolView (ToolView *tv); private slots: void clientAdded( KXMLGUIClient *client ); void updateActions(); private: MainWindow *m_mw; }; class ToolView : public QWidget { Q_OBJECT friend class Sidebar; friend class MainWindow; friend class GUIClient; friend class ToggleToolViewAction; protected: /** * ToolView * Objects of this clas represent a toolview in the mainwindow * you should only add one widget as child to this toolview, it will * be automatically set to be the focus proxy of the toolview * @param mainwin main window for this toolview * @param sidebar sidebar of this toolview * @param parent parent widget, e.g. the splitter of one of the sidebars */ ToolView (MainWindow *mainwin, class Sidebar *sidebar, QWidget *parent); public: /** * destuct me, this is allowed for all, will care itself that the toolview is removed * from the mainwindow and sidebar */ ~ToolView () override; signals: /** * toolview hidden or shown * @param visible is this toolview made visible? */ void visibleChanged (bool visible); /** * some internal methodes needed by the main window and the sidebars */ protected: MainWindow *mainWindow () { return m_mainWin; } Sidebar *sidebar () { return m_sidebar; } void setVisibleToolView (bool vis); public: bool visible () const; protected: void childEvent ( QChildEvent *ev ) override; private: MainWindow *m_mainWin; Sidebar *m_sidebar; /** * unique id */ QString id; /** * is visible in sidebar */ bool m_visible; /** * is this view persistent? */ bool persistent; QPixmap icon; QString text; }; class Sidebar : public KMultiTabBar { Q_OBJECT public: Sidebar (KMultiTabBar::KMultiTabBarPosition pos, MainWindow *mainwin, QWidget *parent); ~Sidebar () override; void setSplitter (Splitter *sp); //HACK use these functions intead of their respective functions in //KMultiTabBar so that we know what they were set to. void setSidebarPosition( KMultiTabBarPosition pos ); KMultiTabBar::KMultiTabBarPosition sidebarPosition() const { return m_pos; } void setSidebarStyle( KMultiTabBarStyle style ); KMultiTabBar::KMultiTabBarStyle sidebarTabStyle() const { return m_sidebarTabStyle; } public: ToolView *addWidget (const QPixmap &icon, const QString &text, ToolView *widget); bool removeWidget (ToolView *widget); bool showWidget (ToolView *widget); bool hideWidget (ToolView *widget); void setLastSize (int s) { m_lastSize = s; } int lastSize () const { return m_lastSize; } void updateLastSize (); bool splitterVisible () const { return m_ownSplit->isVisible(); } void restoreSession (); void updateMinimumSize(); /** * restore the current session config from given object, use current group * @param config config object to use */ void restoreSession (KConfigGroup *config); /** * save the current session config to given object, use current group * @param config config object to use */ void saveSession (KConfigGroup* config); private slots: void tabClicked(int); protected: bool eventFilter(QObject *obj, QEvent *ev) override; private slots: void buttonPopupActivate (QAction* action); private: MainWindow *m_mainWin; KMultiTabBar::KMultiTabBarStyle m_sidebarTabStyle; KMultiTabBar::KMultiTabBarPosition m_pos; Splitter *m_splitter; KMultiTabBar *m_tabBar; Splitter *m_ownSplit; //Q3IntDict m_idToWidget; QMap m_idToWidget; QMap m_widgetToId; /** * list of all toolviews around in this sidebar */ QList m_toolviews; int m_lastSize; int m_popupButton; }; class MainWindow : public KParts::MainWindow { Q_OBJECT friend class ToolView; // // Constructor area // public: /** * Constructor */ MainWindow (QWidget* parentWidget = nullptr, const char* name = nullptr); /** * Destructor */ ~MainWindow () override; // // public interfaces // public: /** * central widget ;) * use this as parent for your content * this widget will get focus if a toolview is hidden * @return central widget */ QWidget *centralWidget () const; /** * add a given widget to the given sidebar if possible, name is very important * @param identifier unique identifier for this toolview * @param pos position for the toolview, if we are in session restore, this is only a preference * @param icon icon to use for the toolview * @param text text to use in addition to icon * @return created toolview on success or 0 */ ToolView *createToolView (const QString &identifier, KMultiTabBar::KMultiTabBarPosition pos, const QPixmap &icon, const QString &text); /** * give you handle to toolview for the given name, 0 if no toolview around * @param identifier toolview name * @return toolview if existing, else 0 */ ToolView *toolView (const QString &identifier) const; /** * set the toolview's tabbar style. * @param style the tabbar style. */ void setToolViewStyle (KMultiTabBar::KMultiTabBarStyle style); /** * get the toolview's tabbar style. Call this before @p startRestore(), * otherwise you overwrite the usersettings. * @return toolview's tabbar style */ KMultiTabBar::KMultiTabBarStyle toolViewStyle () const; protected: /** * called by toolview destructor * @param widget toolview which is destroyed */ void toolViewDeleted (ToolView *widget); /** * modifiers for existing toolviews */ public: /** * move a toolview around * @param widget toolview to move * @param pos position to move too, during session restore, only preference * @return success */ bool moveToolView (ToolView *widget, KMultiTabBar::KMultiTabBarPosition pos); /** * show given toolview, discarded while session restore * @param widget toolview to show * @return success */ bool showToolView (ToolView *widget); /** * hide given toolview, discarded while session restore * @param widget toolview to hide * @return success */ bool hideToolView (ToolView *widget); /** * session saving and restore stuff */ public: /** * start the restore * @param config config object to use * @param group config group to use */ void startRestore (KConfig *config, const QString &group); /** * finish the restore */ void finishRestore (); /** * save the current session config to given object and group * @param config config object to use */ void saveSession (KConfigGroup *config); void updateSidebarMinimumSizes(); /** * internal data ;) */ private: /** * map identifiers to widgets */ //Q3Dict m_idToWidget; QMap m_idToWidget; /** * list of all toolviews around */ QList m_toolviews; /** * widget, which is the central part of the * main window ;) */ QWidget *m_centralWidget; /** * horizontal splitter */ Splitter *m_hSplitter; /** * vertical splitter */ Splitter *m_vSplitter; /** * sidebars for the four sides */ Sidebar *m_sidebars[4]; /** * config object for session restore, only valid between * start and finish restore calls */ KConfig *m_restoreConfig; /** * restore group */ QString m_restoreGroup; /** * out guiclient */ GUIClient *m_guiClient; }; } #endif diff --git a/src/ktlqt3support/ktlfindqobjectchild.cpp b/src/ktlqt3support/ktlfindqobjectchild.cpp index 702ba00b..d7ea5230 100644 --- a/src/ktlqt3support/ktlfindqobjectchild.cpp +++ b/src/ktlqt3support/ktlfindqobjectchild.cpp @@ -1,29 +1,29 @@ /*************************************************************************** * Copyright (C) 2018 by Zoltan Padrah * * zoltan_padrah@users.sf.net * * * * 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 "ktlfindqobjectchild.h" -#include +#include #include QObject *ktlFindQObjectChild(QObject *parent, const char *className) { const QObjectList children = parent->children(); for (QObject *child : children) { if (0 == strcmp( child->metaObject()->className(), className )) { return child; } QObject *ret = ktlFindQObjectChild(child, className); if (ret) { return ret; } } return nullptr; } diff --git a/src/ktlqt3support/ktlq3frame.cpp b/src/ktlqt3support/ktlq3frame.cpp index 8a89647e..cbf56850 100644 --- a/src/ktlqt3support/ktlq3frame.cpp +++ b/src/ktlqt3support/ktlq3frame.cpp @@ -1,200 +1,201 @@ /**************************************************************************** ** ** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the Qt3Support module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "ktlq3frame.h" -#include "qevent.h" -#include "qpainter.h" + +#include +#include QT_BEGIN_NAMESPACE /*! \class KtlQ3Frame \compat */ /*! Creates a new frame with the given \a parent, object \a name, and with widget flags \a f. */ KtlQ3Frame::KtlQ3Frame(QWidget* parent, const char* name, Qt::WindowFlags f) :QFrame(parent, f), marg(0) { if (name) setObjectName(QLatin1String(name)); setAttribute(Qt::WA_LayoutOnEntireRect); } /*! Destructs the frame. */ KtlQ3Frame::~KtlQ3Frame() { } /*! Paints the frame (or part of the frame) that's necessary, depending on the \a event. */ void KtlQ3Frame::paintEvent(QPaintEvent * event) { QPainter paint(this); if (!contentsRect().contains(event->rect())) { paint.save(); paint.setClipRegion(event->region().intersected(frameRect())); drawFrame(&paint); paint.restore(); } if (event->rect().intersects(contentsRect())) { paint.setClipRegion(event->region().intersected(contentsRect())); drawContents(&paint); } } /*! \fn void KtlQ3Frame::drawContents(QPainter *painter) Virtual function that draws the contents of the frame on the given \a painter. The QPainter is already open when you get it, and you must leave it open. Painter \link QPainter::setWorldMatrix() transformations\endlink are switched off on entry. If you transform the painter, remember to take the frame into account and \link QPainter::resetXForm() reset transformation\endlink before returning. This function is reimplemented by subclasses that draw something inside the frame. It should only draw inside contentsRect(). The default function does nothing. \sa contentsRect(), QPainter::setClipRect() */ void KtlQ3Frame::drawContents(QPainter *) { } /*! Draws the frame using the painter \a p and the current frame attributes and color group. The rectangle inside the frame is not affected. This function is virtual, but in general you do not need to reimplement it. If you do, note that the QPainter is already open and must remain open. \sa frameRect(), contentsRect(), drawContents(), frameStyle(), setPalette() */ void KtlQ3Frame::drawFrame(QPainter *p) { QFrame::drawFrame(p); } /*! \fn void KtlQ3Frame::resizeEvent(QResizeEvent *event) This just calls frameChanged(); it does not make use of the \a event itself. */ void KtlQ3Frame::resizeEvent(QResizeEvent *e) { if (e->size() == e->oldSize()) frameChanged(); } /*! Virtual function that is called when the frame style, line width or mid-line width changes. This function can be reimplemented by subclasses that need to know when the frame attributes change. */ void KtlQ3Frame::frameChanged() { } /*! \property KtlQ3Frame::margin \brief the width of the margin The margin is the distance between the innermost pixel of the frame and the outermost pixel of contentsRect(). It is included in frameWidth(). The margin is filled according to backgroundMode(). The default value is 0. \sa lineWidth(), frameWidth() */ void KtlQ3Frame::setMargin(int w) { if (marg == w) return; marg = w; update(); frameChanged(); } /*! \property KtlQ3Frame::contentsRect \brief the frame's contents rectangle (including the margins) */ QRect KtlQ3Frame::contentsRect() const { QRect cr(QFrame::contentsRect()); cr.adjust(marg, marg, -marg, -marg); return cr; } /*! Returns the width of the frame (including the margin). */ int KtlQ3Frame::frameWidth() const { return QFrame::frameWidth() + marg; } QT_END_NAMESPACE diff --git a/src/ktlqt3support/ktlq3frame.h b/src/ktlqt3support/ktlq3frame.h index 3ab2c0db..0510b618 100644 --- a/src/ktlqt3support/ktlq3frame.h +++ b/src/ktlqt3support/ktlq3frame.h @@ -1,90 +1,90 @@ /**************************************************************************** ** ** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the Qt3Support module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef KTL_Q3FRAME_H #define KTL_Q3FRAME_H -#include +#include // QT_BEGIN_HEADER // QT_BEGIN_NAMESPACE // QT_MODULE(Qt3SupportLight) class KtlQ3Frame : public QFrame { Q_OBJECT Q_PROPERTY(int margin READ margin WRITE setMargin) Q_PROPERTY(QRect contentsRect READ contentsRect) public: KtlQ3Frame(QWidget* parent, const char* name = nullptr, Qt::WindowFlags f = {}); ~KtlQ3Frame() override; #ifndef qdoc bool lineShapesOk() const { return true; } #endif int margin() const { return marg; } void setMargin(int); QRect contentsRect() const; int frameWidth() const; protected: void paintEvent(QPaintEvent *) override; void resizeEvent(QResizeEvent *) override; virtual void frameChanged(); virtual void drawFrame(QPainter *); virtual void drawContents(QPainter *); private: Q_DISABLE_COPY(KtlQ3Frame) int marg; }; // QT_END_NAMESPACE // QT_END_HEADER #endif // KTL_Q3FRAME_H diff --git a/src/ktlqt3support/ktlq3polygonscanner.cpp b/src/ktlqt3support/ktlq3polygonscanner.cpp index 1d90f32b..f3a68b88 100644 --- a/src/ktlqt3support/ktlq3polygonscanner.cpp +++ b/src/ktlqt3support/ktlq3polygonscanner.cpp @@ -1,939 +1,940 @@ /**************************************************************************** ** ** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the Qt3Support module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "ktlq3polygonscanner.h" -#include "qpolygon.h" + +#include #include QT_BEGIN_NAMESPACE // Based on Xserver code miFillGeneralPoly... /* * * Written by Brian Kelleher; Oct. 1985 * * Routine to fill a polygon. Two fill rules are * supported: frWINDING and frEVENODD. * * See fillpoly.h for a complete description of the algorithm. */ /* * These are the data structures needed to scan * convert regions. Two different scan conversion * methods are available -- the even-odd method, and * the winding number method. * The even-odd rule states that a point is inside * the polygon if a ray drawn from that point in any * direction will pass through an odd number of * path segments. * By the winding number rule, a point is decided * to be inside the polygon if a ray drawn from that * point in any direction passes through a different * number of clockwise and counterclockwise path * segments. * * These data structures are adapted somewhat from * the algorithm in (Foley/Van Dam) for scan converting * polygons. * The basic algorithm is to start at the top (smallest y) * of the polygon, stepping down to the bottom of * the polygon by incrementing the y coordinate. We * keep a list of edges which the current scanline crosses, * sorted by x. This list is called the Active Edge Table (AET) * As we change the y-coordinate, we update each entry in * in the active edge table to reflect the edges new xcoord. * This list must be sorted at each scanline in case * two edges intersect. * We also keep a data structure known as the Edge Table (ET), * which keeps track of all the edges which the current * scanline has not yet reached. The ET is basically a * list of ScanLineList structures containing a list of * edges which are entered at a given scanline. There is one * ScanLineList per scanline at which an edge is entered. * When we enter a new edge, we move it from the ET to the AET. * * From the AET, we can implement the even-odd rule as in * (Foley/Van Dam). * The winding number rule is a little trickier. We also * keep the EdgeTableEntries in the AET linked by the * nextWETE (winding EdgeTableEntry) link. This allows * the edges to be linked just as before for updating * purposes, but only uses the edges linked by the nextWETE * link as edges representing spans of the polygon to * drawn (as with the even-odd rule). */ /* $XConsortium: miscanfill.h,v 1.5 94/04/17 20:27:50 dpw Exp $ */ /* Copyright (c) 1987 X Consortium Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of the X Consortium shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from the X Consortium. */ /* * scanfill.h * * Written by Brian Kelleher; Jan 1985 * * This file contains a few macros to help track * the edge of a filled object. The object is assumed * to be filled in scanline order, and thus the * algorithm used is an extension of Bresenham's line * drawing algorithm which assumes that y is always the * major axis. * Since these pieces of code are the same for any filled shape, * it is more convenient to gather the library in one * place, but since these pieces of code are also in * the inner loops of output primitives, procedure call * overhead is out of the question. * See the author for a derivation if needed. */ /* * In scan converting polygons, we want to choose those pixels * which are inside the polygon. Thus, we add .5 to the starting * x coordinate for both left and right edges. Now we choose the * first pixel which is inside the pgon for the left edge and the * first pixel which is outside the pgon for the right edge. * Draw the left pixel, but not the right. * * How to add .5 to the starting x coordinate: * If the edge is moving to the right, then subtract dy from the * error term from the general form of the algorithm. * If the edge is moving to the left, then add dy to the error term. * * The reason for the difference between edges moving to the left * and edges moving to the right is simple: If an edge is moving * to the right, then we want the algorithm to flip immediately. * If it is moving to the left, then we don't want it to flip until * we traverse an entire pixel. */ #define BRESINITPGON(dy, x1, x2, xStart, d, m, m1, incr1, incr2) { \ int dx; /* local storage */ \ \ /* \ * if the edge is horizontal, then it is ignored \ * and assumed not to be processed. Otherwise, do this stuff. \ */ \ if ((dy) != 0) { \ xStart = (x1); \ dx = (x2) - xStart; \ if (dx < 0) { \ m = dx / (dy); \ m1 = m - 1; \ incr1 = -2 * dx + 2 * (dy) * m1; \ incr2 = -2 * dx + 2 * (dy) * m; \ d = 2 * m * (dy) - 2 * dx - 2 * (dy); \ } else { \ m = dx / (dy); \ m1 = m + 1; \ incr1 = 2 * dx - 2 * (dy) * m1; \ incr2 = 2 * dx - 2 * (dy) * m; \ d = -2 * m * (dy) + 2 * dx; \ } \ } \ } #define BRESINCRPGON(d, minval, m, m1, incr1, incr2) { \ if (m1 > 0) { \ if (d > 0) { \ minval += m1; \ d += incr1; \ } \ else { \ minval += m; \ d += incr2; \ } \ } else {\ if (d >= 0) { \ minval += m1; \ d += incr1; \ } \ else { \ minval += m; \ d += incr2; \ } \ } \ } /* * This structure contains all of the information needed * to run the bresenham algorithm. * The variables may be hardcoded into the declarations * instead of using this structure to make use of * register declarations. */ typedef struct { int minor; /* minor axis */ int d; /* decision variable */ int m, m1; /* slope and slope+1 */ int incr1, incr2; /* error increments */ } BRESINFO; #define BRESINITPGONSTRUCT(dmaj, min1, min2, bres) \ BRESINITPGON(dmaj, min1, min2, bres.minor, bres.d, \ bres.m, bres.m1, bres.incr1, bres.incr2) #define BRESINCRPGONSTRUCT(bres) \ BRESINCRPGON(bres.d, bres.minor, bres.m, bres.m1, bres.incr1, bres.incr2) typedef struct _EdgeTableEntry { int ymax; /* ycoord at which we exit this edge. */ BRESINFO bres; /* Bresenham info to run the edge */ struct _EdgeTableEntry *next; /* next in the list */ struct _EdgeTableEntry *back; /* for insertion sort */ struct _EdgeTableEntry *nextWETE; /* for winding num rule */ int ClockWise; /* flag for winding number rule */ } EdgeTableEntry; typedef struct _ScanLineList{ int scanline; /* the scanline represented */ EdgeTableEntry *edgelist; /* header node */ struct _ScanLineList *next; /* next in the list */ } ScanLineList; typedef struct { int ymax; /* ymax for the polygon */ int ymin; /* ymin for the polygon */ ScanLineList scanlines; /* header node */ } EdgeTable; /* * Here is a struct to help with storage allocation * so we can allocate a big chunk at a time, and then take * pieces from this heap when we need to. */ #define SLLSPERBLOCK 25 typedef struct _ScanLineListBlock { ScanLineList SLLs[SLLSPERBLOCK]; struct _ScanLineListBlock *next; } ScanLineListBlock; /* * number of points to buffer before sending them off * to scanlines() : Must be an even number */ #define NUMPTSTOBUFFER 200 /* * * a few macros for the inner loops of the fill code where * performance considerations don't allow a procedure call. * * Evaluate the given edge at the given scanline. * If the edge has expired, then we leave it and fix up * the active edge table; otherwise, we increment the * x value to be ready for the next scanline. * The winding number rule is in effect, so we must notify * the caller when the edge has been removed so he * can reorder the Winding Active Edge Table. */ #define EVALUATEEDGEWINDING(pAET, pPrevAET, y, fixWAET) { \ if (pAET->ymax == y) { /* leaving this edge */ \ pPrevAET->next = pAET->next; \ pAET = pPrevAET->next; \ fixWAET = 1; \ if (pAET) \ pAET->back = pPrevAET; \ } \ else { \ BRESINCRPGONSTRUCT(pAET->bres); \ pPrevAET = pAET; \ pAET = pAET->next; \ } \ } /* * Evaluate the given edge at the given scanline. * If the edge has expired, then we leave it and fix up * the active edge table; otherwise, we increment the * x value to be ready for the next scanline. * The even-odd rule is in effect. */ #define EVALUATEEDGEEVENODD(pAET, pPrevAET, y) { \ if (pAET->ymax == y) { /* leaving this edge */ \ pPrevAET->next = pAET->next; \ pAET = pPrevAET->next; \ if (pAET) \ pAET->back = pPrevAET; \ } \ else { \ BRESINCRPGONSTRUCT(pAET->bres) \ pPrevAET = pAET; \ pAET = pAET->next; \ } \ } /*********************************************************** Copyright (c) 1987 X Consortium Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of the X Consortium shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from the X Consortium. Copyright 1987 by Digital Equipment Corporation, Maynard, Massachusetts. All Rights Reserved Permission to use, copy, modify, and distribute this software and its documentation for any purpose and without fee is hereby granted, provided that the above copyright notice appear in all copies and that both that copyright notice and this permission notice appear in supporting documentation, and that the name of Digital not be used in advertising or publicity pertaining to distribution of the software without specific, written prior permission. DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ******************************************************************/ #define MAXINT 0x7fffffff #define MININT -MAXINT /* * fillUtils.c * * Written by Brian Kelleher; Oct. 1985 * * This module contains all of the utility functions * needed to scan convert a polygon. * */ /* * InsertEdgeInET * * Insert the given edge into the edge table. * First we must find the correct bucket in the * Edge table, then find the right slot in the * bucket. Finally, we can insert it. * */ static bool miInsertEdgeInET(EdgeTable *ET, EdgeTableEntry *ETE, int scanline, ScanLineListBlock **SLLBlock, int *iSLLBlock) { register EdgeTableEntry *start, *prev; register ScanLineList *pSLL, *pPrevSLL; ScanLineListBlock *tmpSLLBlock; /* * find the right bucket to put the edge into */ pPrevSLL = &ET->scanlines; pSLL = pPrevSLL->next; while (pSLL && (pSLL->scanline < scanline)) { pPrevSLL = pSLL; pSLL = pSLL->next; } /* * reassign pSLL (pointer to ScanLineList) if necessary */ if ((!pSLL) || (pSLL->scanline > scanline)) { if (*iSLLBlock > SLLSPERBLOCK-1) { tmpSLLBlock = (ScanLineListBlock *)malloc(sizeof(ScanLineListBlock)); if (!tmpSLLBlock) return false; (*SLLBlock)->next = tmpSLLBlock; tmpSLLBlock->next = nullptr; *SLLBlock = tmpSLLBlock; *iSLLBlock = 0; } pSLL = &((*SLLBlock)->SLLs[(*iSLLBlock)++]); pSLL->next = pPrevSLL->next; pSLL->edgelist = nullptr; pPrevSLL->next = pSLL; } pSLL->scanline = scanline; /* * now insert the edge in the right bucket */ prev = nullptr; start = pSLL->edgelist; while (start && (start->bres.minor < ETE->bres.minor)) { prev = start; start = start->next; } ETE->next = start; if (prev) prev->next = ETE; else pSLL->edgelist = ETE; return true; } /* * CreateEdgeTable * * This routine creates the edge table for * scan converting polygons. * The Edge Table (ET) looks like: * * EdgeTable * -------- * | ymax | ScanLineLists * |scanline|-->------------>-------------->... * -------- |scanline| |scanline| * |edgelist| |edgelist| * --------- --------- * | | * | | * V V * list of ETEs list of ETEs * * where ETE is an EdgeTableEntry data structure, * and there is one ScanLineList per scanline at * which an edge is initially entered. * */ typedef struct { #if defined(Q_OS_MAC) int y, x; #else int x, y; #endif } DDXPointRec, *DDXPointPtr; /* * Clean up our act. */ static void miFreeStorage(ScanLineListBlock *pSLLBlock) { register ScanLineListBlock *tmpSLLBlock; while (pSLLBlock) { tmpSLLBlock = pSLLBlock->next; free(pSLLBlock); pSLLBlock = tmpSLLBlock; } } static bool miCreateETandAET(int count, DDXPointPtr pts, EdgeTable *ET, EdgeTableEntry *AET, EdgeTableEntry *pETEs, ScanLineListBlock *pSLLBlock) { register DDXPointPtr top, bottom; register DDXPointPtr PrevPt, CurrPt; int iSLLBlock = 0; int dy; if (count < 2) return true; /* * initialize the Active Edge Table */ AET->next = nullptr; AET->back = nullptr; AET->nextWETE = nullptr; AET->bres.minor = MININT; /* * initialize the Edge Table. */ ET->scanlines.next = nullptr; ET->ymax = MININT; ET->ymin = MAXINT; pSLLBlock->next = nullptr; PrevPt = &pts[count-1]; /* * for each vertex in the array of points. * In this loop we are dealing with two vertices at * a time -- these make up one edge of the polygon. */ while (count--) { CurrPt = pts++; /* * find out which point is above and which is below. */ if (PrevPt->y > CurrPt->y) { bottom = PrevPt, top = CurrPt; pETEs->ClockWise = 0; } else { bottom = CurrPt, top = PrevPt; pETEs->ClockWise = 1; } /* * don't add horizontal edges to the Edge table. */ if (bottom->y != top->y) { pETEs->ymax = bottom->y-1; /* -1 so we don't get last scanline */ /* * initialize integer edge algorithm */ dy = bottom->y - top->y; BRESINITPGONSTRUCT(dy, top->x, bottom->x, pETEs->bres) if (!miInsertEdgeInET(ET, pETEs, top->y, &pSLLBlock, &iSLLBlock)) { miFreeStorage(pSLLBlock->next); return false; } ET->ymax = qMax(ET->ymax, PrevPt->y); ET->ymin = qMin(ET->ymin, PrevPt->y); pETEs++; } PrevPt = CurrPt; } return true; } /* * loadAET * * This routine moves EdgeTableEntries from the * EdgeTable into the Active Edge Table, * leaving them sorted by smaller x coordinate. * */ static void miloadAET(EdgeTableEntry *AET, EdgeTableEntry *ETEs) { register EdgeTableEntry *pPrevAET; register EdgeTableEntry *tmp; pPrevAET = AET; AET = AET->next; while (ETEs) { while (AET && (AET->bres.minor < ETEs->bres.minor)) { pPrevAET = AET; AET = AET->next; } tmp = ETEs->next; ETEs->next = AET; if (AET) AET->back = ETEs; ETEs->back = pPrevAET; pPrevAET->next = ETEs; pPrevAET = ETEs; ETEs = tmp; } } /* * computeWAET * * This routine links the AET by the * nextWETE (winding EdgeTableEntry) link for * use by the winding number rule. The final * Active Edge Table (AET) might look something * like: * * AET * ---------- --------- --------- * |ymax | |ymax | |ymax | * | ... | |... | |... | * |next |->|next |->|next |->... * |nextWETE| |nextWETE| |nextWETE| * --------- --------- ^-------- * | | | * V-------------------> V---> ... * */ static void micomputeWAET(EdgeTableEntry *AET) { register EdgeTableEntry *pWETE; register int inside = 1; register int isInside = 0; AET->nextWETE = nullptr; pWETE = AET; AET = AET->next; while (AET) { if (AET->ClockWise) isInside++; else isInside--; if ((!inside && !isInside) || (inside && isInside)) { pWETE->nextWETE = AET; pWETE = AET; inside = !inside; } AET = AET->next; } pWETE->nextWETE = nullptr; } /* * InsertionSort * * Just a simple insertion sort using * pointers and back pointers to sort the Active * Edge Table. * */ static int miInsertionSort(EdgeTableEntry *AET) { register EdgeTableEntry *pETEchase; register EdgeTableEntry *pETEinsert; register EdgeTableEntry *pETEchaseBackTMP; register int changed = 0; AET = AET->next; while (AET) { pETEinsert = AET; pETEchase = AET; while (pETEchase->back->bres.minor > AET->bres.minor) pETEchase = pETEchase->back; AET = AET->next; if (pETEchase != pETEinsert) { pETEchaseBackTMP = pETEchase->back; pETEinsert->back->next = AET; if (AET) AET->back = pETEinsert->back; pETEinsert->next = pETEchase; pETEchase->back->next = pETEinsert; pETEchase->back = pETEinsert; pETEinsert->back = pETEchaseBackTMP; changed = 1; } } return changed; } /*! \overload */ void KtlQ3PolygonScanner::scan(const QPolygon& pa, bool winding, int index, int npoints) { scan(pa, winding, index, npoints, true); } /*! \overload If \a stitchable is false, the right and bottom edges of the polygon are included. This causes adjacent polygons to overlap. */ void KtlQ3PolygonScanner::scan(const QPolygon& pa, bool winding, int index, int npoints, bool stitchable) { scan(pa, winding, index, npoints, stitchable ? Edge(Left+Top) : Edge(Left+Right+Top+Bottom)); } /*! Calls processSpans() for all scanlines of the polygon defined by \a npoints starting at \a index in \a pa. If \a winding is true, the Winding algorithm rather than the Odd-Even rule is used. The \a edges is any bitwise combination of: \list \i KtlQ3PolygonScanner::Left \i KtlQ3PolygonScanner::Right \i KtlQ3PolygonScanner::Top \i KtlQ3PolygonScanner::Bottom \endlist \a edges determines which edges are included. \warning The edges feature does not work properly. */ void KtlQ3PolygonScanner::scan(const QPolygon& pa, bool winding, int index, int npoints, Edge edges) { DDXPointPtr ptsIn = (DDXPointPtr)pa.data(); ptsIn += index; register EdgeTableEntry *pAET; /* the Active Edge Table */ register int y; /* the current scanline */ register int nPts = 0; /* number of pts in buffer */ register EdgeTableEntry *pWETE; /* Winding Edge Table */ register ScanLineList *pSLL; /* Current ScanLineList */ register DDXPointPtr ptsOut; /* ptr to output buffers */ int *width; DDXPointRec FirstPoint[NUMPTSTOBUFFER]; /* the output buffers */ int FirstWidth[NUMPTSTOBUFFER]; EdgeTableEntry *pPrevAET; /* previous AET entry */ EdgeTable ET; /* Edge Table header node */ EdgeTableEntry AET; /* Active ET header node */ EdgeTableEntry *pETEs; /* Edge Table Entries buff */ ScanLineListBlock SLLBlock; /* header for ScanLineList */ int fixWAET = 0; int edge_l = (edges & Left) ? 1 : 0; int edge_r = (edges & Right) ? 1 : 0; int edge_t = 1; //#### (edges & Top) ? 1 : 0; int edge_b = (edges & Bottom) ? 1 : 0; if (npoints == -1) npoints = pa.size(); if (npoints < 3) return; if(!(pETEs = (EdgeTableEntry *) malloc(sizeof(EdgeTableEntry) * npoints))) return; ptsOut = FirstPoint; width = FirstWidth; if (!miCreateETandAET(npoints, ptsIn, &ET, &AET, pETEs, &SLLBlock)) { free(pETEs); return; } pSLL = ET.scanlines.next; if (!winding) { /* * for each scanline */ for (y = ET.ymin+1-edge_t; y < ET.ymax+edge_b; y++) { /* * Add a new edge to the active edge table when we * get to the next edge. */ if (pSLL && y == pSLL->scanline) { miloadAET(&AET, pSLL->edgelist); pSLL = pSLL->next; } pPrevAET = &AET; pAET = AET.next; /* * for each active edge */ while (pAET) { ptsOut->x = pAET->bres.minor + 1 - edge_l; ptsOut++->y = y; *width++ = pAET->next->bres.minor - pAET->bres.minor - 1 + edge_l + edge_r; nPts++; /* * send out the buffer when its full */ if (nPts == NUMPTSTOBUFFER) { processSpans(nPts, (QPoint*)FirstPoint, FirstWidth); ptsOut = FirstPoint; width = FirstWidth; nPts = 0; } EVALUATEEDGEEVENODD(pAET, pPrevAET, y) EVALUATEEDGEEVENODD(pAET, pPrevAET, y) } miInsertionSort(&AET); } } else /* default to WindingNumber */ { /* * for each scanline */ for (y = ET.ymin+1-edge_t; y < ET.ymax+edge_b; y++) { /* * Add a new edge to the active edge table when we * get to the next edge. */ if (pSLL && y == pSLL->scanline) { miloadAET(&AET, pSLL->edgelist); micomputeWAET(&AET); pSLL = pSLL->next; } pPrevAET = &AET; pAET = AET.next; pWETE = pAET; /* * for each active edge */ while (pAET) { /* * if the next edge in the active edge table is * also the next edge in the winding active edge * table. */ if (pWETE == pAET) { ptsOut->x = pAET->bres.minor + 1 - edge_l; ptsOut++->y = y; *width++ = pAET->nextWETE->bres.minor - pAET->bres.minor - 1 + edge_l + edge_r; nPts++; /* * send out the buffer */ if (nPts == NUMPTSTOBUFFER) { processSpans(nPts, (QPoint*)FirstPoint, FirstWidth); ptsOut = FirstPoint; width = FirstWidth; nPts = 0; } pWETE = pWETE->nextWETE; while (pWETE != pAET) { EVALUATEEDGEWINDING(pAET, pPrevAET, y, fixWAET) } pWETE = pWETE->nextWETE; } EVALUATEEDGEWINDING(pAET, pPrevAET, y, fixWAET) } /* * reevaluate the Winding active edge table if we * just had to resort it or if we just exited an edge. */ if (miInsertionSort(&AET) || fixWAET) { micomputeWAET(&AET); fixWAET = 0; } } } /* * Get any spans that we missed by buffering */ processSpans(nPts, (QPoint*)FirstPoint, FirstWidth); free(pETEs); miFreeStorage(SLLBlock.next); } /***** END OF X11-based CODE *****/ QT_END_NAMESPACE diff --git a/src/ktlqt3support/ktlq3scrollview.cpp b/src/ktlqt3support/ktlq3scrollview.cpp index 4733385d..84bc98fc 100644 --- a/src/ktlqt3support/ktlq3scrollview.cpp +++ b/src/ktlqt3support/ktlq3scrollview.cpp @@ -1,2857 +1,2859 @@ /**************************************************************************** ** ** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the Qt3Support module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "ktlq3scrollview.h" -#include "qwidget.h" -#include "qscrollbar.h" -#include "qpainter.h" -#include "qpixmap.h" -#include "qcursor.h" +#include +#include +#include +#include +#include // #include "q3ptrdict.h" -#include "qhash.h" -#include "qapplication.h" -#include "qtimer.h" -#include "qstyle.h" +#include +#include +#include +#include // #include "q3ptrlist.h" -#include "qevent.h" +#include +#include +#include //#include "q3listview.h" // #ifdef Q_WS_MAC // 2018.10.18 - do not depend on internal headers // # include "private/qt_mac_p.h" // #endif // QT_BEGIN_NAMESPACE // using namespace Qt; static const int coord_limit = 4000; static const int autoscroll_margin = 16; static const int initialScrollTime = 30; static const int initialScrollAccel = 5; struct QSVChildRec { QSVChildRec(QWidget* c, int xx, int yy) : child(c), x(xx), y(yy) { } void hideOrShow(KtlQ3ScrollView* sv, QWidget* clipped_viewport); void moveTo(KtlQ3ScrollView* sv, int xx, int yy, QWidget* clipped_viewport) { if (x != xx || y != yy) { x = xx; y = yy; hideOrShow(sv,clipped_viewport); } } QWidget* child; int x, y; }; void QSVChildRec::hideOrShow(KtlQ3ScrollView* sv, QWidget* clipped_viewport) { if (clipped_viewport) { if (x+child->width() < sv->contentsX()+clipped_viewport->x() || x > sv->contentsX()+clipped_viewport->width() || y+child->height() < sv->contentsY()+clipped_viewport->y() || y > sv->contentsY()+clipped_viewport->height()) { child->move(clipped_viewport->width(), clipped_viewport->height()); } else { child->move(x-sv->contentsX()-clipped_viewport->x(), y-sv->contentsY()-clipped_viewport->y()); } } else { child->move(x-sv->contentsX(), y-sv->contentsY()); } } // QT_BEGIN_INCLUDE_NAMESPACE // QT_END_INCLUDE_NAMESPACE class KtlQ3ScrollViewData { public: KtlQ3ScrollViewData(KtlQ3ScrollView* parent, int vpwflags) : hbar(new QScrollBar(Qt::Horizontal, parent /*, "qt_hbar"*/)), vbar(new QScrollBar(Qt::Vertical, parent /*, "qt_vbar" */)), viewport(new KtlQAbstractScrollAreaWidget(parent, "qt_viewport", QFlag(vpwflags))), clipped_viewport(nullptr), flags(vpwflags), vx(0), vy(0), vwidth(1), vheight(1), #ifndef QT_NO_DRAGANDDROP autoscroll_timer(parent /* , "scrollview autoscroll timer" */), drag_autoscroll(true), #endif scrollbar_timer(parent /*, "scrollview scrollbar timer" */), inresize(false), use_cached_size_hint(true) { hbar->setObjectName("qt_hbar"); vbar->setObjectName("qt_vbar"); #ifndef QT_NO_DRAGANDDROP autoscroll_timer.setObjectName("scrollview autoscroll timer"); #endif scrollbar_timer.setObjectName("scrollview scrollbar timer"); l_marg = r_marg = t_marg = b_marg = 0; viewport->ensurePolished(); vMode = KtlQ3ScrollView::Auto; hMode = KtlQ3ScrollView::Auto; corner = nullptr; // vbar->setSteps(20, 1/*set later*/); vbar->setSingleStep(20); vbar->setPageStep(1); //hbar->setSteps(20, 1/*set later*/); hbar->setSingleStep(20); hbar->setPageStep(1); policy = KtlQ3ScrollView::Default; signal_choke = false; static_bg = false; fake_scroll = false; hbarPressed = false; vbarPressed = false; hbar->setLayoutDirection(Qt::LeftToRight); } ~KtlQ3ScrollViewData(); QSVChildRec* rec(QWidget* w) { return /* childDict.find(w); */ childDict.value(w); } QSVChildRec* ancestorRec(QWidget* w); QSVChildRec* addChildRec(QWidget* w, int x, int y) { QSVChildRec *r = new QSVChildRec(w,x,y); children.append(r); childDict.insert(w, r); return r; } void deleteChildRec(QSVChildRec* r) { childDict.remove(r->child); //children.removeRef(r); children.removeAll(r); delete r; } void hideOrShowAll(KtlQ3ScrollView* sv, bool isScroll = false); void moveAllBy(int dx, int dy); bool anyVisibleChildren(); void autoMove(KtlQ3ScrollView* sv); void autoResize(KtlQ3ScrollView* sv); void autoResizeHint(KtlQ3ScrollView* sv); void viewportResized(int w, int h); QScrollBar* hbar; QScrollBar* vbar; bool hbarPressed; bool vbarPressed; KtlQAbstractScrollAreaWidget* viewport; KtlQClipperWidget* clipped_viewport; int flags; //Q3PtrList children; QList children; //Q3PtrDict childDict; // 2018.10.07 QHash< QWidget*, QSVChildRec* > childDict; QWidget* corner; int vx, vy, vwidth, vheight; // for drawContents-style usage int l_marg, r_marg, t_marg, b_marg; KtlQ3ScrollView::ResizePolicy policy; KtlQ3ScrollView::ScrollBarMode vMode; KtlQ3ScrollView::ScrollBarMode hMode; #ifndef QT_NO_DRAGANDDROP QPoint cpDragStart; QTimer autoscroll_timer; int autoscroll_time; int autoscroll_accel; bool drag_autoscroll; #endif QTimer scrollbar_timer; uint static_bg : 1; uint fake_scroll : 1; // This variable allows ensureVisible to move the contents then // update both the sliders. Otherwise, updating the sliders would // cause two image scrolls, creating ugly flashing. // uint signal_choke : 1; // This variables indicates in updateScrollBars() that we are // in a resizeEvent() and thus don't want to flash scroll bars uint inresize : 1; uint use_cached_size_hint : 1; QSize cachedSizeHint; inline int contentsX() const { return -vx; } inline int contentsY() const { return -vy; } inline int contentsWidth() const { return vwidth; } }; inline KtlQ3ScrollViewData::~KtlQ3ScrollViewData() { //children.setAutoDelete(true); // 2018.12.08 for (QList::iterator it = children.begin(); it != children.end(); ++it) { QSVChildRec *r = *it; delete r; } children.removeAll(nullptr); } QSVChildRec* KtlQ3ScrollViewData::ancestorRec(QWidget* w) { if (clipped_viewport) { while (w->parentWidget() != clipped_viewport) { w = w->parentWidget(); if (!w) return nullptr; } } else { while (w->parentWidget() != viewport) { w = w->parentWidget(); if (!w) return nullptr; } } return rec(w); } void KtlQ3ScrollViewData::hideOrShowAll(KtlQ3ScrollView* sv, bool isScroll) { if (!clipped_viewport) return; if (clipped_viewport->x() <= 0 && clipped_viewport->y() <= 0 && clipped_viewport->width()+clipped_viewport->x() >= viewport->width() && clipped_viewport->height()+clipped_viewport->y() >= viewport->height()) { // clipped_viewport still covers viewport if(static_bg) { //clipped_viewport->repaint(true); // 2018.11.30 clipped_viewport->repaint(); } else if ((!isScroll && !clipped_viewport->testAttribute(Qt::WA_StaticContents)) || static_bg) clipped_viewport->update(); } else { // Re-center int nx = (viewport->width() - clipped_viewport->width()) / 2; int ny = (viewport->height() - clipped_viewport->height()) / 2; clipped_viewport->move(nx,ny); clipped_viewport->update(); } //for (QSVChildRec *r = children.first(); r; r=children.next()) { // 2018.12.08 // r->hideOrShow(sv, clipped_viewport); //} for (QList::iterator it = children.begin(); it != children.end(); ++it) { QSVChildRec *r = *it; r->hideOrShow(sv, clipped_viewport); } } void KtlQ3ScrollViewData::moveAllBy(int dx, int dy) { if (clipped_viewport && !static_bg) { clipped_viewport->move(clipped_viewport->x()+dx, clipped_viewport->y()+dy); } else { //for (QSVChildRec *r = children.first(); r; r=children.next()) { // 2018.12.07 // r->child->move(r->child->x()+dx,r->child->y()+dy); //} for (QList::iterator it = children.begin(); it != children.end(); ++it) { QSVChildRec *r = *it; r->child->move(r->child->x()+dx,r->child->y()+dy); } if (static_bg) { //viewport->repaint(true); // 2018.11.30 viewport->repaint(); } } } bool KtlQ3ScrollViewData::anyVisibleChildren() { //for (QSVChildRec *r = children.first(); r; r=children.next()) { // 2018.12.08 // if (r->child->isVisible()) return true; //} for (QList::iterator it = children.begin(); it != children.end(); ++it) { QSVChildRec *r = *it; if (r->child->isVisible()) return true; } return false; } void KtlQ3ScrollViewData::autoMove(KtlQ3ScrollView* sv) { if (policy == KtlQ3ScrollView::AutoOne) { QSVChildRec* r = children.first(); if (r) sv->setContentsPos(-r->child->x(),-r->child->y()); } } void KtlQ3ScrollViewData::autoResize(KtlQ3ScrollView* sv) { if (policy == KtlQ3ScrollView::AutoOne) { QSVChildRec* r = children.first(); if (r) sv->resizeContents(r->child->width(),r->child->height()); } } void KtlQ3ScrollViewData::autoResizeHint(KtlQ3ScrollView* sv) { if (policy == KtlQ3ScrollView::AutoOne) { QSVChildRec* r = children.first(); if (r) { QSize s = r->child->sizeHint(); if (s.isValid()) r->child->resize(s); } } else if (policy == KtlQ3ScrollView::AutoOneFit) { QSVChildRec* r = children.first(); if (r) { QSize sh = r->child->sizeHint(); sh = sh.boundedTo(r->child->maximumSize()); sv->resizeContents(sh.width(), sh.height()); } } } void KtlQ3ScrollViewData::viewportResized(int w, int h) { if (policy == KtlQ3ScrollView::AutoOneFit) { QSVChildRec* r = children.first(); if (r) { QSize sh = r->child->sizeHint(); sh = sh.boundedTo(r->child->maximumSize()); r->child->resize(qMax(w,sh.width()), qMax(h,sh.height())); } } } /*! \class KtlQ3ScrollView \brief The KtlQ3ScrollView widget provides a scrolling area with on-demand scroll bars. \compat The KtlQ3ScrollView is a large canvas - potentially larger than the coordinate system normally supported by the underlying window system. This is important because it is quite easy to go beyond these limitations (e.g. many web pages are more than 32000 pixels high). Additionally, the KtlQ3ScrollView can have QWidgets positioned on it that scroll around with the drawn content. These sub-widgets can also have positions outside the normal coordinate range (but they are still limited in size). To provide content for the widget, inherit from KtlQ3ScrollView, reimplement drawContents() and use resizeContents() to set the size of the viewed area. Use addChild() and moveChild() to position widgets on the view. To use KtlQ3ScrollView effectively it is important to understand its widget structure in the three styles of use: a single large child widget, a large panning area with some widgets and a large panning area with many widgets. \section1 Using One Big Widget \img qscrollview-vp2.png The first, simplest usage of KtlQ3ScrollView (depicted above), is appropriate for scrolling areas that are never more than about 4000 pixels in either dimension (this is about the maximum reliable size on X11 servers). In this usage, you just make one large child in the KtlQ3ScrollView. The child should be a child of the viewport() of the scrollview and be added with addChild(): \snippet doc/src/snippets/code/src_qt3support_widgets_q3scrollview.cpp 0 You can go on to add arbitrary child widgets to the single child in the scrollview as you would with any widget: \snippet doc/src/snippets/code/src_qt3support_widgets_q3scrollview.cpp 1 Here the KtlQ3ScrollView has four children: the viewport(), the verticalScrollBar(), the horizontalScrollBar() and a small cornerWidget(). The viewport() has one child: the QWidget. The QWidget has the three QLabel objects as child widgets. When the view is scrolled, the QWidget is moved; its children move with it as child widgets normally do. \section1 Using a Very Big View with Some Widgets \img qscrollview-vp.png The second usage of KtlQ3ScrollView (depicted above) is appropriate when few, if any, widgets are on a very large scrolling area that is potentially larger than 4000 pixels in either dimension. In this usage you call resizeContents() to set the size of the area and reimplement drawContents() to paint the contents. You may also add some widgets by making them children of the viewport() and adding them with addChild() (this is the same as the process for the single large widget in the previous example): \snippet doc/src/snippets/code/src_qt3support_widgets_q3scrollview.cpp 2 Here, the KtlQ3ScrollView has the same four children: the viewport(), the verticalScrollBar(), the horizontalScrollBar() and a small cornerWidget(). The viewport() has the three QLabel objects as child widgets. When the view is scrolled, the scrollview moves the child widgets individually. \section1 Using a Very Big View with Many Widgets \img qscrollview-cl.png The final usage of KtlQ3ScrollView (depicted above) is appropriate when many widgets are on a very large scrolling area that is potentially larger than 4000 pixels in either dimension. In this usage you call resizeContents() to set the size of the area and reimplement drawContents() to paint the contents. You then call enableClipper(true) and add widgets, again by making them children of the viewport(), and adding them with addChild(): \snippet doc/src/snippets/code/src_qt3support_widgets_q3scrollview.cpp 3 Here, the KtlQ3ScrollView has four children: the clipper() (not the viewport() this time), the verticalScrollBar(), the horizontalScrollBar() and a small cornerWidget(). The clipper() has one child: the viewport(). The viewport() has the same three labels as child widgets. When the view is scrolled the viewport() is moved; its children move with it as child widgets normally do. \target allviews \section1 Details Relevant for All Views Normally you will use the first or third method if you want any child widgets in the view. Note that the widget you see in the scrolled area is the viewport() widget, not the KtlQ3ScrollView itself. So to turn mouse tracking on, for example, use viewport()->setMouseTracking(true). To enable drag-and-drop, you would setAcceptDrops(true) on the KtlQ3ScrollView (because drag-and-drop events propagate to the parent). But to work out the logical position in the view, you would need to map the drop co-ordinate from being relative to the KtlQ3ScrollView to being relative to the contents; use the function viewportToContents() for this. To handle mouse events on the scrolling area, subclass scrollview as you would subclass other widgets, but rather than reimplementing mousePressEvent(), reimplement contentsMousePressEvent() instead. The contents specific event handlers provide translated events in the coordinate system of the scrollview. If you reimplement mousePressEvent(), you'll get called only when part of the KtlQ3ScrollView is clicked: and the only such part is the "corner" (if you don't set a cornerWidget()) and the frame; everything else is covered up by the viewport, clipper or scroll bars. When you construct a KtlQ3ScrollView, some of the window flags apply to the viewport() instead of being sent to the QWidget constructor for the KtlQ3ScrollView. \list \i An image-manipulation widget would use \c WNoAutoErase|WStaticContents because the widget draws all pixels itself, and when its size increases, it only needs a paint event for the new part because the old part remains unchanged. \i A scrolling game widget in which the background scrolls as the characters move might use \c WNoAutoErase (in addition to \c WStaticContents) so that the window system background does not flash in and out during scrolling. \i A word processing widget might use \c WNoAutoErase and repaint itself line by line to get a less-flickery resizing. If the widget is in a mode in which no text justification can take place, it might use \c WStaticContents too, so that it would only get a repaint for the newly visible parts. \endlist Child widgets may be moved using addChild() or moveChild(). Use childX() and childY() to get the position of a child widget. A widget may be placed in the corner between the vertical and horizontal scroll bars with setCornerWidget(). You can get access to the scroll bars using horizontalScrollBar() and verticalScrollBar(), and to the viewport with viewport(). The scroll view can be scrolled using scrollBy(), ensureVisible(), setContentsPos() or center(). The visible area is given by visibleWidth() and visibleHeight(), and the contents area by contentsWidth() and contentsHeight(). The contents may be repainted using one of the repaintContents() or updateContents() functions. Coordinate conversion is provided by contentsToViewport() and viewportToContents(). The contentsMoving() signal is emitted just before the contents are moved to a new position. \warning KtlQ3ScrollView currently does not erase the background when resized, i.e. you must always clear the background manually in scrollview subclasses. This will change in a future version of Qt and we recommend specifying the \c WNoAutoErase flag explicitly. */ /*! \enum KtlQ3ScrollView::ResizePolicy This enum type is used to control a KtlQ3ScrollView's reaction to resize events. \value Default the KtlQ3ScrollView selects one of the other settings automatically when it has to. In this version of Qt, KtlQ3ScrollView changes to \c Manual if you resize the contents with resizeContents() and to \c AutoOne if a child is added. \value Manual the contents stays the size set by resizeContents(). \value AutoOne if there is only one child widget the contents stays the size of that widget. Otherwise the behavior is undefined. \value AutoOneFit if there is only one child widget the contents stays the size of that widget's sizeHint(). If the scrollview is resized larger than the child's sizeHint(), the child will be resized to fit. If there is more than one child, the behavior is undefined. */ //#### The widget will be resized to its sizeHint() when a LayoutHint event //#### is received /*! Constructs a KtlQ3ScrollView called \a name with parent \a parent and widget flags \a f. The widget flags \c WStaticContents, \c WNoAutoErase and \c WPaintClever are propagated to the viewport() widget. The other widget flags are propagated to the parent constructor as usual. */ KtlQ3ScrollView::KtlQ3ScrollView(QWidget *parent, const char *name, Qt::WindowFlags f) : KtlQ3Frame(parent, name, f & (~Qt::WA_StaticContents) /*& (~Qt::WNoAutoErase) & (~Qt::WResizeNoErase) */ ) { Qt::WindowFlags flags = /* Qt::WResizeNoErase | (f&Qt::WPaintClever) | (f&Qt::WRepaintNoErase) | */ (f&Qt::WA_StaticContents); d = new KtlQ3ScrollViewData(this, flags); #ifndef QT_NO_DRAGANDDROP connect(&d->autoscroll_timer, SIGNAL(timeout()), this, SLOT(doDragAutoScroll())); #endif connect(d->hbar, SIGNAL(valueChanged(int)), this, SLOT(hslide(int))); connect(d->vbar, SIGNAL(valueChanged(int)), this, SLOT(vslide(int))); connect(d->hbar, SIGNAL(sliderPressed()), this, SLOT(hbarIsPressed())); connect(d->hbar, SIGNAL(sliderReleased()), this, SLOT(hbarIsReleased())); connect(d->vbar, SIGNAL(sliderPressed()), this, SLOT(vbarIsPressed())); connect(d->vbar, SIGNAL(sliderReleased()), this, SLOT(vbarIsReleased())); d->viewport->installEventFilter(this); connect(&d->scrollbar_timer, SIGNAL(timeout()), this, SLOT(updateScrollBars())); setFrameStyle(KtlQ3Frame::StyledPanel | KtlQ3Frame::Sunken); setLineWidth(style()->pixelMetric(QStyle::PM_DefaultFrameWidth)); setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding)); } /*! Destroys the KtlQ3ScrollView. Any children added with addChild() will be deleted. */ KtlQ3ScrollView::~KtlQ3ScrollView() { // Be careful not to get all those useless events... if (d->clipped_viewport) d->clipped_viewport->removeEventFilter(this); else d->viewport->removeEventFilter(this); // order is important // ~QWidget may cause a WM_ERASEBKGND on Windows delete d->vbar; d->vbar = nullptr; delete d->hbar; d->hbar = nullptr; delete d->viewport; d->viewport = nullptr; delete d; d = nullptr; } /*! \fn void KtlQ3ScrollView::horizontalSliderPressed() This signal is emitted whenever the user presses the horizontal slider. */ /*! \fn void KtlQ3ScrollView::horizontalSliderReleased() This signal is emitted whenever the user releases the horizontal slider. */ /*! \fn void KtlQ3ScrollView::verticalSliderPressed() This signal is emitted whenever the user presses the vertical slider. */ /*! \fn void KtlQ3ScrollView::verticalSliderReleased() This signal is emitted whenever the user releases the vertical slider. */ void KtlQ3ScrollView::hbarIsPressed() { d->hbarPressed = true; emit(horizontalSliderPressed()); } void KtlQ3ScrollView::hbarIsReleased() { d->hbarPressed = false; emit(horizontalSliderReleased()); } /*! Returns true if horizontal slider is pressed by user; otherwise returns false. */ bool KtlQ3ScrollView::isHorizontalSliderPressed() { return d->hbarPressed; } void KtlQ3ScrollView::vbarIsPressed() { d->vbarPressed = true; emit(verticalSliderPressed()); } void KtlQ3ScrollView::vbarIsReleased() { d->vbarPressed = false; emit(verticalSliderReleased()); } /*! Returns true if vertical slider is pressed by user; otherwise returns false. */ bool KtlQ3ScrollView::isVerticalSliderPressed() { return d->vbarPressed; } /*! \internal */ void KtlQ3ScrollView::styleChange(QStyle& old) { //QWidget::styleChange(old); // 2019.04.14 updateScrollBars(); d->cachedSizeHint = QSize(); } /*! \internal */ void KtlQ3ScrollView::fontChange(const QFont &old) { //QWidget::fontChange(old); // 2019.04.14 updateScrollBars(); d->cachedSizeHint = QSize(); } void KtlQ3ScrollView::hslide(int pos) { if (!d->signal_choke) { moveContents(-pos, -d->contentsY()); QApplication::syncX(); } } void KtlQ3ScrollView::vslide(int pos) { if (!d->signal_choke) { moveContents(-d->contentsX(), -pos); QApplication::syncX(); } } /*! Called when the horizontal scroll bar geometry changes. This is provided as a protected function so that subclasses can do interesting things such as providing extra buttons in some of the space normally used by the scroll bars. The default implementation simply gives all the space to \a hbar. The new geometry is given by \a x, \a y, \a w and \a h. \sa setVBarGeometry() */ void KtlQ3ScrollView::setHBarGeometry(QScrollBar& hbar, int x, int y, int w, int h) { hbar.setGeometry(x, y, w, h); } /*! Called when the vertical scroll bar geometry changes. This is provided as a protected function so that subclasses can do interesting things such as providing extra buttons in some of the space normally used by the scroll bars. The default implementation simply gives all the space to \a vbar. The new geometry is given by \a x, \a y, \a w and \a h. \sa setHBarGeometry() */ void KtlQ3ScrollView::setVBarGeometry(QScrollBar& vbar, int x, int y, int w, int h) { vbar.setGeometry(x, y, w, h); } /*! Returns the viewport size for size (\a x, \a y). The viewport size depends on (\a x, \a y) (the size of the contents), the size of this widget and the modes of the horizontal and vertical scroll bars. This function permits widgets that can trade vertical and horizontal space for each other to control scroll bar appearance better. For example, a word processor or web browser can control the width of the right margin accurately, whether or not there needs to be a vertical scroll bar. */ QSize KtlQ3ScrollView::viewportSize(int x, int y) const { int fw = frameWidth(); int lmarg = fw+d->l_marg; int rmarg = fw+d->r_marg; int tmarg = fw+d->t_marg; int bmarg = fw+d->b_marg; int w = width(); int h = height(); bool needh, needv; bool showh, showv; int hsbExt = horizontalScrollBar()->sizeHint().height(); int vsbExt = verticalScrollBar()->sizeHint().width(); if (d->policy != AutoOne || d->anyVisibleChildren()) { // Do we definitely need the scroll bar? needh = w-lmarg-rmarg < x; needv = h-tmarg-bmarg < y; // Do we intend to show the scroll bar? if (d->hMode == AlwaysOn) showh = true; else if (d->hMode == AlwaysOff) showh = false; else showh = needh; if (d->vMode == AlwaysOn) showv = true; else if (d->vMode == AlwaysOff) showv = false; else showv = needv; // Given other scroll bar will be shown, NOW do we need one? if (showh && h-vsbExt-tmarg-bmarg < y) { if (d->vMode == Auto) showv=true; } if (showv && w-hsbExt-lmarg-rmarg < x) { if (d->hMode == Auto) showh=true; } } else { // Scroll bars not needed, only show scroll bar that are always on. showh = d->hMode == AlwaysOn; showv = d->vMode == AlwaysOn; } return QSize(w-lmarg-rmarg - (showv ? vsbExt : 0), h-tmarg-bmarg - (showh ? hsbExt : 0)); } /*! Updates scroll bars: all possibilities are considered. You should never need to call this in your code. */ void KtlQ3ScrollView::updateScrollBars() { if(!horizontalScrollBar() && !verticalScrollBar()) return; // I support this should use viewportSize()... but it needs // so many of the temporary variables from viewportSize. hm. int fw = frameWidth(); int lmarg = fw+d->l_marg; int rmarg = fw+d->r_marg; int tmarg = fw+d->t_marg; int bmarg = fw+d->b_marg; int w = width(); int h = height(); int portw, porth; bool needh; bool needv; bool showh; bool showv; bool showc = false; int hsbExt = horizontalScrollBar()->sizeHint().height(); int vsbExt = verticalScrollBar()->sizeHint().width(); QSize oldVisibleSize(visibleWidth(), visibleHeight()); if (d->policy != AutoOne || d->anyVisibleChildren()) { // Do we definitely need the scroll bar? needh = w-lmarg-rmarg < d->contentsWidth(); if (d->inresize) needh = !horizontalScrollBar()->isHidden(); needv = h-tmarg-bmarg < contentsHeight(); // Do we intend to show the scroll bar? if (d->hMode == AlwaysOn) showh = true; else if (d->hMode == AlwaysOff) showh = false; else showh = needh; if (d->vMode == AlwaysOn) showv = true; else if (d->vMode == AlwaysOff) showv = false; else showv = needv; #ifdef Q_WS_MAC bool mac_need_scroll = false; if(!parentWidget()) { mac_need_scroll = true; } else { QWidget *tlw = window(); // #ifndef QT_MAC_USE_COCOA // 2018.10.18 - do not depend on internal headers // QPoint tlw_br = QPoint(tlw->width(), tlw->height()), // my_br = qt_mac_posInWindow(this) + QPoint(w, h); // if(my_br.x() >= tlw_br.x() - 3 && my_br.y() >= tlw_br.y() - 3) // #endif mac_need_scroll = true; } if(mac_need_scroll) { // #ifndef QT_MAC_USE_COCOA // 2018.10.18 - do not depend on internal headers // WindowAttributes attr; // GetWindowAttributes((WindowPtr)handle(), &attr); // mac_need_scroll = (attr & kWindowResizableAttribute); // #endif } if(mac_need_scroll) { showc = true; if(d->vMode == Auto) showv = true; if(d->hMode == Auto) showh = true; } #endif // Given other scroll bar will be shown, NOW do we need one? if (showh && h-vsbExt-tmarg-bmarg < contentsHeight()) { needv=true; if (d->vMode == Auto) showv=true; } if (showv && !d->inresize && w-hsbExt-lmarg-rmarg < d->contentsWidth()) { needh=true; if (d->hMode == Auto) showh=true; } } else { // Scrollbars not needed, only show scroll bar that are always on. needh = needv = false; showh = d->hMode == AlwaysOn; showv = d->vMode == AlwaysOn; } bool sc = d->signal_choke; d->signal_choke=true; // Hide unneeded scroll bar, calculate viewport size if (showh) { porth=h-hsbExt-tmarg-bmarg; } else { if (!needh) d->hbar->setValue(0); d->hbar->hide(); porth=h-tmarg-bmarg; } if (showv) { portw=w-vsbExt-lmarg-rmarg; } else { if (!needv) d->vbar->setValue(0); d->vbar->hide(); portw=w-lmarg-rmarg; } // Configure scroll bars that we will show if (needv) { d->vbar->setRange(0, contentsHeight()-porth); //d->vbar->setSteps(KtlQ3ScrollView::d->vbar->lineStep(), porth); // 2018.11.30 d->vbar->setSingleStep(KtlQ3ScrollView::d->vbar->singleStep()); d->vbar->setPageStep(porth); } else { d->vbar->setRange(0, 0); } if (needh) { d->hbar->setRange(0, qMax(0, d->contentsWidth()-portw)); //d->hbar->setSteps(KtlQ3ScrollView::d->hbar->lineStep(), portw); // 2018.11.30 d->hbar->setSingleStep(KtlQ3ScrollView::d->hbar->singleStep()); d->hbar->setPageStep(portw); } else { d->hbar->setRange(0, 0); } // Position the scroll bars, viewport and corner widget. int bottom; bool reverse = QApplication::layoutDirection() == Qt::RightToLeft; int xoffset = (reverse && (showv || cornerWidget())) ? vsbExt : 0; int xpos = reverse ? 0 : w - vsbExt; bool frameContentsOnly = style()->styleHint(QStyle::SH_ScrollView_FrameOnlyAroundContents); if(! frameContentsOnly) { if (reverse) xpos += fw; else xpos -= fw; } if (showh) { int right = (showc || showv || cornerWidget()) ? w-vsbExt : w; if (! frameContentsOnly) setHBarGeometry(*d->hbar, fw + xoffset, h-hsbExt-fw, right-fw-fw, hsbExt); else setHBarGeometry(*d->hbar, 0 + xoffset, h-hsbExt, right, hsbExt); bottom=h-hsbExt; } else { bottom=h; } if (showv) { clipper()->setGeometry(lmarg + xoffset, tmarg, w-vsbExt-lmarg-rmarg, bottom-tmarg-bmarg); d->viewportResized(w-vsbExt-lmarg-rmarg, bottom-tmarg-bmarg); if (! frameContentsOnly) changeFrameRect(QRect(0, 0, w, h)); else changeFrameRect(QRect(xoffset, 0, w-vsbExt, bottom)); if (showc || cornerWidget()) { if (! frameContentsOnly) setVBarGeometry(*d->vbar, xpos, fw, vsbExt, h-hsbExt-fw-fw); else setVBarGeometry(*d->vbar, xpos, 0, vsbExt, h-hsbExt); } else { if (! frameContentsOnly) setVBarGeometry(*d->vbar, xpos, fw, vsbExt, bottom-fw-fw); else setVBarGeometry(*d->vbar, xpos, 0, vsbExt, bottom); } } else { if (! frameContentsOnly) changeFrameRect(QRect(0, 0, w, h)); else changeFrameRect(QRect(0, 0, w, bottom)); clipper()->setGeometry(lmarg, tmarg, w-lmarg-rmarg, bottom-tmarg-bmarg); d->viewportResized(w-lmarg-rmarg, bottom-tmarg-bmarg); } QWidget *corner = d->corner; if (d->corner) { if (! frameContentsOnly) corner->setGeometry(xpos, h-hsbExt-fw, vsbExt, hsbExt); else corner->setGeometry(xpos, h-hsbExt, vsbExt, hsbExt); } d->signal_choke=sc; if (d->contentsX()+visibleWidth() > d->contentsWidth()) { int x; #if 0 if (reverse) x =qMin(0,d->contentsWidth()-visibleWidth()); else #endif x =qMax(0,d->contentsWidth()-visibleWidth()); d->hbar->setValue(x); // Do it even if it is recursive moveContents(-x, -d->contentsY()); } if (d->contentsY()+visibleHeight() > contentsHeight()) { int y=qMax(0,contentsHeight()-visibleHeight()); d->vbar->setValue(y); // Do it even if it is recursive moveContents(-d->contentsX(), -y); } // Finally, show the scroll bars if (showh && (d->hbar->isHidden() || !d->hbar->isVisible())) d->hbar->show(); if (showv && (d->vbar->isHidden() || !d->vbar->isVisible())) d->vbar->show(); d->signal_choke=true; d->vbar->setValue(d->contentsY()); d->hbar->setValue(d->contentsX()); d->signal_choke=false; QSize newVisibleSize(visibleWidth(), visibleHeight()); if (d->clipped_viewport && oldVisibleSize != newVisibleSize) { QResizeEvent e(newVisibleSize, oldVisibleSize); viewportResizeEvent(&e); } } /*! \reimp */ void KtlQ3ScrollView::setVisible(bool visible) { if (visible && !isVisible()) { QWidget::setVisible(visible); updateScrollBars(); d->hideOrShowAll(this); } else { QWidget::setVisible(visible); } } /*! \internal */ void KtlQ3ScrollView::resize(int w, int h) { QWidget::resize(w, h); } /*! \internal */ void KtlQ3ScrollView::resize(const QSize& s) { resize(s.width(), s.height()); } /*! \reimp */ void KtlQ3ScrollView::resizeEvent(QResizeEvent* event) { //KtlQ3Frame::resizeEvent(); #if 0 if (QApplication::reverseLayout()) { d->fake_scroll = true; scrollBy(-event->size().width() + event->oldSize().width(), 0); d->fake_scroll = false; } #endif bool inresize = d->inresize; d->inresize = true; updateScrollBars(); d->inresize = inresize; //d->scrollbar_timer.start(0, true); // 2018.11.30 d->scrollbar_timer.setSingleShot(true); d->scrollbar_timer.start(0); d->hideOrShowAll(this); } /*! \reimp */ void KtlQ3ScrollView::mousePressEvent(QMouseEvent * e) { e->ignore(); } /*! \reimp */ void KtlQ3ScrollView::mouseReleaseEvent(QMouseEvent *e) { e->ignore(); } /*! \reimp */ void KtlQ3ScrollView::mouseDoubleClickEvent(QMouseEvent *e) { e->ignore(); } /*! \reimp */ void KtlQ3ScrollView::mouseMoveEvent(QMouseEvent *e) { e->ignore(); } /*! \reimp */ #ifndef QT_NO_WHEELEVENT void KtlQ3ScrollView::wheelEvent(QWheelEvent *e) { /* QWheelEvent ce(viewport()->mapFromGlobal(e->globalPos()), e->globalPos(), e->delta(), e->state()); - 2018.11.30*/ QWheelEvent ce(viewport()->mapFromGlobal(e->globalPos()), e->globalPos(), e->delta(), e->buttons(), e->modifiers()); viewportWheelEvent(&ce); if (!ce.isAccepted()) { if (e->orientation() == Qt::Horizontal && horizontalScrollBar()) horizontalScrollBar()->event(e); else if (e->orientation() == Qt::Vertical && verticalScrollBar()) verticalScrollBar()->event(e); } else { e->accept(); } } #endif /*! \reimp */ void KtlQ3ScrollView::contextMenuEvent(QContextMenuEvent *e) { if (e->reason() != QContextMenuEvent::Keyboard) { e->ignore(); return; } /* QContextMenuEvent ce(e->reason(), viewport()->mapFromGlobal(e->globalPos()), e->globalPos(), e->state()); - 2018.11.30 */ QContextMenuEvent ce(e->reason(), viewport()->mapFromGlobal(e->globalPos()), e->globalPos(), e->modifiers()); viewportContextMenuEvent(&ce); if (ce.isAccepted()) e->accept(); else e->ignore(); } KtlQ3ScrollView::ScrollBarMode KtlQ3ScrollView::vScrollBarMode() const { return d->vMode; } /*! \enum KtlQ3ScrollView::ScrollBarMode This enum type describes the various modes of KtlQ3ScrollView's scroll bars. \value Auto KtlQ3ScrollView shows a scroll bar when the content is too large to fit and not otherwise. This is the default. \value AlwaysOff KtlQ3ScrollView never shows a scroll bar. \value AlwaysOn KtlQ3ScrollView always shows a scroll bar. (The modes for the horizontal and vertical scroll bars are independent.) */ /*! \property KtlQ3ScrollView::vScrollBarMode \brief the mode for the vertical scroll bar The default mode is KtlQ3ScrollView::Auto. \sa hScrollBarMode */ void KtlQ3ScrollView::setVScrollBarMode(ScrollBarMode mode) { if (d->vMode != mode) { d->vMode = mode; updateScrollBars(); } } /*! \property KtlQ3ScrollView::hScrollBarMode \brief the mode for the horizontal scroll bar The default mode is KtlQ3ScrollView::Auto. \sa vScrollBarMode */ KtlQ3ScrollView::ScrollBarMode KtlQ3ScrollView::hScrollBarMode() const { return d->hMode; } void KtlQ3ScrollView::setHScrollBarMode(ScrollBarMode mode) { if (d->hMode != mode) { d->hMode = mode; updateScrollBars(); } } /*! Returns the widget in the corner between the two scroll bars. By default, no corner widget is present. */ QWidget* KtlQ3ScrollView::cornerWidget() const { return d->corner; } /*! Sets the widget in the \a corner between the two scroll bars. You will probably also want to set at least one of the scroll bar modes to \c AlwaysOn. Passing 0 shows no widget in the corner. Any previous \a corner widget is hidden. You may call setCornerWidget() with the same widget at different times. All widgets set here will be deleted by the KtlQ3ScrollView when it is destroyed unless you separately reparent the widget after setting some other corner widget (or 0). Any \e newly set widget should have no current parent. By default, no corner widget is present. \sa setVScrollBarMode(), setHScrollBarMode() */ void KtlQ3ScrollView::setCornerWidget(QWidget* corner) { QWidget* oldcorner = d->corner; if (oldcorner != corner) { if (oldcorner) oldcorner->hide(); d->corner = corner; if (corner) corner->setParent(this); updateScrollBars(); if (corner) corner->show(); } } void KtlQ3ScrollView::setResizePolicy(ResizePolicy r) { d->policy = r; } /*! \property KtlQ3ScrollView::resizePolicy \brief the resize policy The default is \c Default. \sa ResizePolicy */ KtlQ3ScrollView::ResizePolicy KtlQ3ScrollView::resizePolicy() const { return d->policy; } /*! \internal */ void KtlQ3ScrollView::setEnabled(bool enable) { KtlQ3Frame::setEnabled(enable); } /*! Removes the \a child widget from the scrolled area. Note that this happens automatically if the \a child is deleted. */ void KtlQ3ScrollView::removeChild(QWidget* child) { if (!d || !child) // First check in case we are destructing return; QSVChildRec *r = d->rec(child); if (r) d->deleteChildRec(r); } /*! \internal */ void KtlQ3ScrollView::removeChild(QObject* child) { // KtlQ3Frame::removeChild(child); // 2018.11.30 if (child) { child->setParent(nullptr); } } /*! Inserts the widget, \a child, into the scrolled area positioned at (\a x, \a y). The position defaults to (0, 0). If the child is already in the view, it is just moved. You may want to call enableClipper(true) if you add a large number of widgets. */ void KtlQ3ScrollView::addChild(QWidget* child, int x, int y) { if (!child) { #if defined(QT_CHECK_NULL) qWarning("KtlQ3ScrollView::addChild(): Cannot add null child"); #endif return; } child->ensurePolished(); //child->setBackgroundOrigin(WidgetOrigin); // 2018.11.30 - does nothing in qt4 if (child->parentWidget() == viewport()) { // May already be there QSVChildRec *r = d->rec(child); if (r) { r->moveTo(this,x,y,d->clipped_viewport); if (d->policy > Manual) { d->autoResizeHint(this); d->autoResize(this); // #### better to just deal with this one widget! } return; } } if (d->children.isEmpty() && d->policy != Manual) { if (d->policy == Default) setResizePolicy(AutoOne); child->installEventFilter(this); } else if (d->policy == AutoOne) { child->removeEventFilter(this); //#### ????? setResizePolicy(Manual); } if (child->parentWidget() != viewport()) { //child->reparent(viewport(), 0, QPoint(0,0), false); // 2018.11.30 child->setParent(viewport()); child->setGeometry(0, 0, child->width(), child->height()); } d->addChildRec(child,x,y)->hideOrShow(this, d->clipped_viewport); if (d->policy > Manual) { d->autoResizeHint(this); d->autoResize(this); // #### better to just deal with this one widget! } } /*! Repositions the \a child widget to (\a x, \a y). This function is the same as addChild(). */ void KtlQ3ScrollView::moveChild(QWidget* child, int x, int y) { addChild(child,x,y); } /*! Returns the X position of the given \a child widget. Use this rather than QWidget::x() for widgets added to the view. This function returns 0 if \a child has not been added to the view. */ int KtlQ3ScrollView::childX(QWidget* child) { QSVChildRec *r = d->rec(child); return r ? r->x : 0; } /*! Returns the Y position of the given \a child widget. Use this rather than QWidget::y() for widgets added to the view. This function returns 0 if \a child has not been added to the view. */ int KtlQ3ScrollView::childY(QWidget* child) { QSVChildRec *r = d->rec(child); return r ? r->y : 0; } /*! \fn bool KtlQ3ScrollView::childIsVisible(QWidget*) \obsolete Returns true if \a child is visible. This is equivalent to child->isVisible(). */ /*! \fn void KtlQ3ScrollView::showChild(QWidget* child, bool y) \obsolete Sets the visibility of \a child. Equivalent to QWidget::show() or QWidget::hide(). */ /*! This event filter ensures the scroll bars are updated when a single contents widget is resized, shown, hidden or destroyed; it passes mouse events to the KtlQ3ScrollView. The event is in \a e and the object is in \a obj. */ bool KtlQ3ScrollView::eventFilter(QObject *obj, QEvent *e) { bool disabled = !(qobject_cast(obj)->isEnabled()); if (!d) return false; // we are destructing if (obj == d->viewport || obj == d->clipped_viewport) { switch (e->type()) { /* Forward many events to viewport...() functions */ case QEvent::Paint: viewportPaintEvent((QPaintEvent*)e); break; case QEvent::Resize: if (!d->clipped_viewport) viewportResizeEvent((QResizeEvent *)e); break; case QEvent::MouseButtonPress: if (disabled) return false; viewportMousePressEvent((QMouseEvent*)e); if (((QMouseEvent*)e)->isAccepted()) return true; break; case QEvent::MouseButtonRelease: if (disabled) return false; viewportMouseReleaseEvent((QMouseEvent*)e); if (((QMouseEvent*)e)->isAccepted()) return true; break; case QEvent::MouseButtonDblClick: if (disabled) return false; viewportMouseDoubleClickEvent((QMouseEvent*)e); if (((QMouseEvent*)e)->isAccepted()) return true; break; case QEvent::MouseMove: if (disabled) return false; viewportMouseMoveEvent((QMouseEvent*)e); if (((QMouseEvent*)e)->isAccepted()) return true; break; #ifndef QT_NO_DRAGANDDROP case QEvent::DragEnter: if (disabled) return false; viewportDragEnterEvent((QDragEnterEvent*)e); break; case QEvent::DragMove: { if (disabled) return false; if (d->drag_autoscroll) { QPoint vp = ((QDragMoveEvent*) e)->pos(); QRect inside_margin(autoscroll_margin, autoscroll_margin, visibleWidth() - autoscroll_margin * 2, visibleHeight() - autoscroll_margin * 2); if (!inside_margin.contains(vp)) { startDragAutoScroll(); // Keep sending move events ((QDragMoveEvent*)e)->accept(QRect(0,0,0,0)); } } viewportDragMoveEvent((QDragMoveEvent*)e); } break; case QEvent::DragLeave: if (disabled) return false; stopDragAutoScroll(); viewportDragLeaveEvent((QDragLeaveEvent*)e); break; case QEvent::Drop: if (disabled) return false; stopDragAutoScroll(); viewportDropEvent((QDropEvent*)e); break; #endif // QT_NO_DRAGANDDROP #ifndef QT_NO_WHEELEVENT case QEvent::Wheel: if (disabled) return false; break; #endif case QEvent::ContextMenu: if (disabled) return false; viewportContextMenuEvent((QContextMenuEvent*)e); if (((QContextMenuEvent*)e)->isAccepted()) return true; break; case QEvent::ChildRemoved: removeChild((QWidget*)((QChildEvent*)e)->child()); break; //case QEvent::LayoutHint: // 2018.12.08 - deprecated // d->autoResizeHint(this); // break; default: break; } } else if (d && d->rec((QWidget*)obj)) { // must be a child if (e->type() == QEvent::Resize) d->autoResize(this); else if (e->type() == QEvent::Move) d->autoMove(this); } return KtlQ3Frame::eventFilter(obj, e); // always continue with standard event processing } /*! This event handler is called whenever the KtlQ3ScrollView receives a mousePressEvent(): the press position in \a e is translated to be a point on the contents. */ void KtlQ3ScrollView::contentsMousePressEvent(QMouseEvent* e) { e->ignore(); } /*! This event handler is called whenever the KtlQ3ScrollView receives a mouseReleaseEvent(): the release position in \a e is translated to be a point on the contents. */ void KtlQ3ScrollView::contentsMouseReleaseEvent(QMouseEvent* e) { e->ignore(); } /*! This event handler is called whenever the KtlQ3ScrollView receives a mouseDoubleClickEvent(): the click position in \a e is translated to be a point on the contents. The default implementation generates a normal mouse press event. */ void KtlQ3ScrollView::contentsMouseDoubleClickEvent(QMouseEvent* e) { contentsMousePressEvent(e); // try mouse press event } /*! This event handler is called whenever the KtlQ3ScrollView receives a mouseMoveEvent(): the mouse position in \a e is translated to be a point on the contents. */ void KtlQ3ScrollView::contentsMouseMoveEvent(QMouseEvent* e) { e->ignore(); } #ifndef QT_NO_DRAGANDDROP /*! This event handler is called whenever the KtlQ3ScrollView receives a dragEnterEvent(): the drag position is translated to be a point on the contents. The default implementation does nothing. The \a event parameter is ignored. */ void KtlQ3ScrollView::contentsDragEnterEvent(QDragEnterEvent * /* event */) { } /*! This event handler is called whenever the KtlQ3ScrollView receives a dragMoveEvent(): the drag position is translated to be a point on the contents. The default implementation does nothing. The \a event parameter is ignored. */ void KtlQ3ScrollView::contentsDragMoveEvent(QDragMoveEvent * /* event */) { } /*! This event handler is called whenever the KtlQ3ScrollView receives a dragLeaveEvent(): the drag position is translated to be a point on the contents. The default implementation does nothing. The \a event parameter is ignored. */ void KtlQ3ScrollView::contentsDragLeaveEvent(QDragLeaveEvent * /* event */) { } /*! This event handler is called whenever the KtlQ3ScrollView receives a dropEvent(): the drop position is translated to be a point on the contents. The default implementation does nothing. The \a event parameter is ignored. */ void KtlQ3ScrollView::contentsDropEvent(QDropEvent * /* event */) { } #endif // QT_NO_DRAGANDDROP /*! This event handler is called whenever the KtlQ3ScrollView receives a wheelEvent() in \a{e}: the mouse position is translated to be a point on the contents. */ #ifndef QT_NO_WHEELEVENT void KtlQ3ScrollView::contentsWheelEvent(QWheelEvent * e) { e->ignore(); } #endif /*! This event handler is called whenever the KtlQ3ScrollView receives a contextMenuEvent() in \a{e}: the mouse position is translated to be a point on the contents. */ void KtlQ3ScrollView::contentsContextMenuEvent(QContextMenuEvent *e) { e->ignore(); } /*! This is a low-level painting routine that draws the viewport contents. Reimplement this if drawContents() is too high-level (for example, if you don't want to open a QPainter on the viewport). The paint event is passed in \a pe. */ void KtlQ3ScrollView::viewportPaintEvent(QPaintEvent* pe) { QWidget* vp = viewport(); QPainter p(vp); QRect r = pe->rect(); if (d->clipped_viewport) { QRect rr( -d->clipped_viewport->x(), -d->clipped_viewport->y(), d->viewport->width(), d->viewport->height() ); r &= rr; if (r.isValid()) { int ex = r.x() + d->clipped_viewport->x() + d->contentsX(); int ey = r.y() + d->clipped_viewport->y() + d->contentsY(); int ew = r.width(); int eh = r.height(); drawContentsOffset(&p, d->contentsX()+d->clipped_viewport->x(), d->contentsY()+d->clipped_viewport->y(), ex, ey, ew, eh); } } else { r &= d->viewport->rect(); int ex = r.x() + d->contentsX(); int ey = r.y() + d->contentsY(); int ew = r.width(); int eh = r.height(); drawContentsOffset(&p, d->contentsX(), d->contentsY(), ex, ey, ew, eh); } } /*! To provide simple processing of events on the contents, this function receives all resize events sent to the viewport. The default implementation does nothing. The \a event parameter is ignored. \sa QWidget::resizeEvent() */ void KtlQ3ScrollView::viewportResizeEvent(QResizeEvent * /* event */) { } /*! \internal To provide simple processing of events on the contents, this function receives all mouse press events sent to the viewport, translates the event and calls contentsMousePressEvent(). \sa contentsMousePressEvent(), QWidget::mousePressEvent() */ void KtlQ3ScrollView::viewportMousePressEvent(QMouseEvent* e) { /* QMouseEvent ce(e->type(), viewportToContents(e->pos()), e->globalPos(), e->button(), e->state()); - 2018.11.30 */ QMouseEvent ce(e->type(), viewportToContents(e->pos()), e->globalPos(), e->button(), e->buttons(), e->modifiers()); contentsMousePressEvent(&ce); if (!ce.isAccepted()) e->ignore(); } /*!\internal To provide simple processing of events on the contents, this function receives all mouse release events sent to the viewport, translates the event and calls contentsMouseReleaseEvent(). \sa QWidget::mouseReleaseEvent() */ void KtlQ3ScrollView::viewportMouseReleaseEvent(QMouseEvent* e) { /* QMouseEvent ce(e->type(), viewportToContents(e->pos()), e->globalPos(), e->button(), e->state()); -- 2018.11.30 */ QMouseEvent ce(e->type(), viewportToContents(e->pos()), e->globalPos(), e->button(), e->buttons(), e->modifiers()); contentsMouseReleaseEvent(&ce); if (!ce.isAccepted()) e->ignore(); } /*!\internal To provide simple processing of events on the contents, this function receives all mouse double click events sent to the viewport, translates the event and calls contentsMouseDoubleClickEvent(). \sa QWidget::mouseDoubleClickEvent() */ void KtlQ3ScrollView::viewportMouseDoubleClickEvent(QMouseEvent* e) { /* QMouseEvent ce(e->type(), viewportToContents(e->pos()), e->globalPos(), e->button(), e->state()); - 2018.11.30 */ QMouseEvent ce(e->type(), viewportToContents(e->pos()), e->globalPos(), e->button(), e->buttons(), e->modifiers()); contentsMouseDoubleClickEvent(&ce); if (!ce.isAccepted()) e->ignore(); } /*!\internal To provide simple processing of events on the contents, this function receives all mouse move events sent to the viewport, translates the event and calls contentsMouseMoveEvent(). \sa QWidget::mouseMoveEvent() */ void KtlQ3ScrollView::viewportMouseMoveEvent(QMouseEvent* e) { QMouseEvent ce(e->type(), viewportToContents(e->pos()), e->globalPos(), e->button(), e->buttons(), e->modifiers()); contentsMouseMoveEvent(&ce); if (!ce.isAccepted()) e->ignore(); } #ifndef QT_NO_DRAGANDDROP /*!\internal To provide simple processing of events on the contents, this function receives all drag enter events sent to the viewport, translates the event and calls contentsDragEnterEvent(). \sa QWidget::dragEnterEvent() */ void KtlQ3ScrollView::viewportDragEnterEvent(QDragEnterEvent* e) { //e->setPoint(viewportToContents(e->pos())); // 2018.11.30 QDragEnterEvent ev(viewportToContents(e->pos()), e->possibleActions(), e->mimeData(), e->mouseButtons(), e->keyboardModifiers()); contentsDragEnterEvent(&ev); //e->setPoint(contentsToViewport(e->pos())); // 2018.11.30 } /*!\internal To provide simple processing of events on the contents, this function receives all drag move events sent to the viewport, translates the event and calls contentsDragMoveEvent(). \sa QWidget::dragMoveEvent() */ void KtlQ3ScrollView::viewportDragMoveEvent(QDragMoveEvent* e) { //e->setPoint(viewportToContents(e->pos())); // 2018.11.30 QDragMoveEvent ev(viewportToContents(e->pos()), e->possibleActions(), e->mimeData(), e->mouseButtons(), e->keyboardModifiers()); contentsDragMoveEvent(&ev); //e->setPoint(contentsToViewport(e->pos())); // 2018.11.30 } /*!\internal To provide simple processing of events on the contents, this function receives all drag leave events sent to the viewport and calls contentsDragLeaveEvent(). \sa QWidget::dragLeaveEvent() */ void KtlQ3ScrollView::viewportDragLeaveEvent(QDragLeaveEvent* e) { contentsDragLeaveEvent(e); } /*!\internal To provide simple processing of events on the contents, this function receives all drop events sent to the viewport, translates the event and calls contentsDropEvent(). \sa QWidget::dropEvent() */ void KtlQ3ScrollView::viewportDropEvent(QDropEvent* e) { //e->setPoint(viewportToContents(e->pos())); // 2018.11.30 QDropEvent ev(viewportToContents(e->pos()), e->possibleActions(), e->mimeData(), e->mouseButtons(), e->keyboardModifiers()); contentsDropEvent(&ev); //e->setPoint(contentsToViewport(e->pos())); // 2018.11.30 } #endif // QT_NO_DRAGANDDROP /*!\internal To provide simple processing of events on the contents, this function receives all wheel events sent to the viewport, translates the event and calls contentsWheelEvent(). \sa QWidget::wheelEvent() */ #ifndef QT_NO_WHEELEVENT void KtlQ3ScrollView::viewportWheelEvent(QWheelEvent* e) { /* Different than standard mouse events, because wheel events might be sent to the focus widget if the widget-under-mouse doesn't want the event itself. */ /* QWheelEvent ce(viewportToContents(e->pos()), e->globalPos(), e->delta(), e->state()); */ QWheelEvent ce(viewportToContents(e->pos()), e->globalPos(), e->delta(), e->buttons(), e->modifiers()); contentsWheelEvent(&ce); if (ce.isAccepted()) e->accept(); else e->ignore(); } #endif /*! \internal To provide simple processing of events on the contents, this function receives all context menu events sent to the viewport, translates the event and calls contentsContextMenuEvent(). */ void KtlQ3ScrollView::viewportContextMenuEvent(QContextMenuEvent *e) { //QContextMenuEvent ce(e->reason(), viewportToContents(e->pos()), e->globalPos(), e->state()); QContextMenuEvent ce(e->reason(), viewportToContents(e->pos()), e->globalPos(), e->modifiers()); contentsContextMenuEvent(&ce); if (ce.isAccepted()) e->accept(); else e->ignore(); } /*! Returns the component horizontal scroll bar. It is made available to allow accelerators, autoscrolling, etc. It should not be used for other purposes. This function never returns 0. */ QScrollBar* KtlQ3ScrollView::horizontalScrollBar() const { return d->hbar; } /*! Returns the component vertical scroll bar. It is made available to allow accelerators, autoscrolling, etc. It should not be used for other purposes. This function never returns 0. */ QScrollBar* KtlQ3ScrollView::verticalScrollBar() const { return d->vbar; } /*! Scrolls the content so that the point (\a x, \a y) is visible with at least 50-pixel margins (if possible, otherwise centered). */ void KtlQ3ScrollView::ensureVisible(int x, int y) { ensureVisible(x, y, 50, 50); } /*! \overload Scrolls the content so that the point (\a x, \a y) is visible with at least the \a xmargin and \a ymargin margins (if possible, otherwise centered). */ void KtlQ3ScrollView::ensureVisible(int x, int y, int xmargin, int ymargin) { int pw=visibleWidth(); int ph=visibleHeight(); int cx=-d->contentsX(); int cy=-d->contentsY(); int cw=d->contentsWidth(); int ch=contentsHeight(); if (pw < xmargin*2) xmargin=pw/2; if (ph < ymargin*2) ymargin=ph/2; if (cw <= pw) { xmargin=0; cx=0; } if (ch <= ph) { ymargin=0; cy=0; } if (x < -cx+xmargin) cx = -x+xmargin; else if (x >= -cx+pw-xmargin) cx = -x+pw-xmargin; if (y < -cy+ymargin) cy = -y+ymargin; else if (y >= -cy+ph-ymargin) cy = -y+ph-ymargin; if (cx > 0) cx=0; else if (cx < pw-cw && cw>pw) cx=pw-cw; if (cy > 0) cy=0; else if (cy < ph-ch && ch>ph) cy=ph-ch; setContentsPos(-cx, -cy); } /*! Scrolls the content so that the point (\a x, \a y) is in the top-left corner. */ void KtlQ3ScrollView::setContentsPos(int x, int y) { #if 0 // bounds checking... if (QApplication::reverseLayout()) if (x > d->contentsWidth() - visibleWidth()) x = d->contentsWidth() - visibleWidth(); else #endif if (x < 0) x = 0; if (y < 0) y = 0; // Choke signal handling while we update BOTH sliders. d->signal_choke=true; moveContents(-x, -y); d->vbar->setValue(y); d->hbar->setValue(x); d->signal_choke=false; } /*! Scrolls the content by \a dx to the left and \a dy upwards. */ void KtlQ3ScrollView::scrollBy(int dx, int dy) { setContentsPos(qMax(d->contentsX()+dx, 0), qMax(d->contentsY()+dy, 0)); } /*! Scrolls the content so that the point (\a x, \a y) is in the center of visible area. */ void KtlQ3ScrollView::center(int x, int y) { ensureVisible(x, y, 32000, 32000); } /*! \overload Scrolls the content so that the point (\a x, \a y) is visible with the \a xmargin and \a ymargin margins (as fractions of visible the area). For example: \list \i Margin 0.0 allows (x, y) to be on the edge of the visible area. \i Margin 0.5 ensures that (x, y) is in middle 50% of the visible area. \i Margin 1.0 ensures that (x, y) is in the center of the visible area. \endlist */ void KtlQ3ScrollView::center(int x, int y, float xmargin, float ymargin) { int pw=visibleWidth(); int ph=visibleHeight(); ensureVisible(x, y, int(xmargin/2.0*pw+0.5), int(ymargin/2.0*ph+0.5)); } /*! \fn void KtlQ3ScrollView::contentsMoving(int x, int y) This signal is emitted just before the contents are moved to position (\a x, \a y). \sa contentsX(), contentsY() */ /*! Moves the contents by (\a x, \a y). */ void KtlQ3ScrollView::moveContents(int x, int y) { if (-x+visibleWidth() > d->contentsWidth()) #if 0 if(QApplication::reverseLayout()) x=qMax(0,-d->contentsWidth()+visibleWidth()); else #endif x=qMin(0,-d->contentsWidth()+visibleWidth()); if (-y+visibleHeight() > contentsHeight()) y=qMin(0,-contentsHeight()+visibleHeight()); int dx = x - d->vx; int dy = y - d->vy; if (!dx && !dy) return; // Nothing to do emit contentsMoving(-x, -y); d->vx = x; d->vy = y; if (d->clipped_viewport || d->static_bg) { // Cheap move (usually) d->moveAllBy(dx,dy); } else if (/*dx && dy ||*/ (qAbs(dy) * 5 > visibleHeight() * 4) || (qAbs(dx) * 5 > visibleWidth() * 4) ) { // Big move if (viewport()->updatesEnabled()) viewport()->update(); d->moveAllBy(dx,dy); } else if (!d->fake_scroll || d->contentsWidth() > visibleWidth()) { // Small move clipper()->scroll(dx,dy); } d->hideOrShowAll(this, true); } /*! \property KtlQ3ScrollView::contentsX \brief the X coordinate of the contents that are at the left edge of the viewport. */ int KtlQ3ScrollView::contentsX() const { return d->contentsX(); } /*! \property KtlQ3ScrollView::contentsY \brief the Y coordinate of the contents that are at the top edge of the viewport. */ int KtlQ3ScrollView::contentsY() const { return d->contentsY(); } /*! \property KtlQ3ScrollView::contentsWidth \brief the width of the contents area */ int KtlQ3ScrollView::contentsWidth() const { return d->contentsWidth(); } /*! \property KtlQ3ScrollView::contentsHeight \brief the height of the contents area */ int KtlQ3ScrollView::contentsHeight() const { return d->vheight; } /*! Sets the size of the contents area to \a w pixels wide and \a h pixels high and updates the viewport accordingly. */ void KtlQ3ScrollView::resizeContents(int w, int h) { int ow = d->vwidth; int oh = d->vheight; d->vwidth = w; d->vheight = h; d->scrollbar_timer.setSingleShot(true); d->scrollbar_timer.start(0 /*, true */ ); if (d->children.isEmpty() && d->policy == Default) setResizePolicy(Manual); if (ow > w) { // Swap int t=w; w=ow; ow=t; } // Refresh area ow..w if (ow < visibleWidth() && w >= 0) { if (ow < 0) ow = 0; if (w > visibleWidth()) w = visibleWidth(); clipper()->update(d->contentsX()+ow, 0, w-ow, visibleHeight()); } if (oh > h) { // Swap int t=h; h=oh; oh=t; } // Refresh area oh..h if (oh < visibleHeight() && h >= 0) { if (oh < 0) oh = 0; if (h > visibleHeight()) h = visibleHeight(); clipper()->update(0, d->contentsY()+oh, visibleWidth(), h-oh); } } /*! Calls update() on a rectangle defined by \a x, \a y, \a w, \a h, translated appropriately. If the rectangle is not visible, nothing is repainted. \sa repaintContents() */ void KtlQ3ScrollView::updateContents(int x, int y, int w, int h) { if (!isVisible() || !updatesEnabled()) return; QWidget* vp = viewport(); // Translate x -= d->contentsX(); y -= d->contentsY(); if (x < 0) { w += x; x = 0; } if (y < 0) { h += y; y = 0; } if (w < 0 || h < 0) return; if (x > visibleWidth() || y > visibleHeight()) return; if (w > visibleWidth()) w = visibleWidth(); if (h > visibleHeight()) h = visibleHeight(); if (d->clipped_viewport) { // Translate clipper() to viewport() x -= d->clipped_viewport->x(); y -= d->clipped_viewport->y(); } vp->update(x, y, w, h); } /*! \overload Updates the contents in rectangle \a r */ void KtlQ3ScrollView::updateContents(const QRect& r) { updateContents(r.x(), r.y(), r.width(), r.height()); } /*! \overload */ void KtlQ3ScrollView::updateContents() { updateContents(d->contentsX(), d->contentsY(), visibleWidth(), visibleHeight()); } /*! \overload Repaints the contents of rectangle \a r. If \a erase is true the background is cleared using the background color. */ void KtlQ3ScrollView::repaintContents(const QRect& r, bool erase) { repaintContents(r.x(), r.y(), r.width(), r.height(), erase); } /*! \overload Repaints the contents. If \a erase is true the background is cleared using the background color. */ void KtlQ3ScrollView::repaintContents(bool erase) { repaintContents(d->contentsX(), d->contentsY(), visibleWidth(), visibleHeight(), erase); } /*! Calls repaint() on a rectangle defined by \a x, \a y, \a w, \a h, translated appropriately. If the rectangle is not visible, nothing is repainted. If \a erase is true the background is cleared using the background color. \sa updateContents() */ void KtlQ3ScrollView::repaintContents(int x, int y, int w, int h, bool /*erase*/) { if (!isVisible() || !updatesEnabled()) return; QWidget* vp = viewport(); // Translate logical to clipper() x -= d->contentsX(); y -= d->contentsY(); if (x < 0) { w += x; x = 0; } if (y < 0) { h += y; y = 0; } if (w < 0 || h < 0) return; if (w > visibleWidth()) w = visibleWidth(); if (h > visibleHeight()) h = visibleHeight(); if (d->clipped_viewport) { // Translate clipper() to viewport() x -= d->clipped_viewport->x(); y -= d->clipped_viewport->y(); } vp->update(x, y, w, h); } /*! For backward-compatibility only. It is easier to use drawContents(QPainter*,int,int,int,int). The default implementation translates the painter appropriately and calls drawContents(QPainter*,int,int,int,int). See drawContents() for an explanation of the parameters \a p, \a offsetx, \a offsety, \a clipx, \a clipy, \a clipw and \a cliph. */ void KtlQ3ScrollView::drawContentsOffset(QPainter* p, int offsetx, int offsety, int clipx, int clipy, int clipw, int cliph) { p->translate(-offsetx,-offsety); drawContents(p, clipx, clipy, clipw, cliph); } /*! \fn void KtlQ3ScrollView::drawContents(QPainter* p, int clipx, int clipy, int clipw, int cliph) Reimplement this function if you are viewing a drawing area rather than a widget. The function should draw the rectangle (\a clipx, \a clipy, \a clipw, \a cliph) of the contents using painter \a p. The clip rectangle is in the scrollview's coordinates. For example: \snippet doc/src/snippets/code/src_qt3support_widgets_q3scrollview.cpp 4 The clip rectangle and translation of the painter \a p is already set appropriately. */ void KtlQ3ScrollView::drawContents(QPainter*, int, int, int, int) { } /*! \reimp */ void KtlQ3ScrollView::frameChanged() { // // slight ugle-hack - the listview header needs readjusting when // // changing the frame // if (Q3ListView *lv = qobject_cast(this)) // lv->triggerUpdate(); KtlQ3Frame::frameChanged(); updateScrollBars(); } /*! Returns the viewport widget of the scrollview. This is the widget containing the contents widget or which is the drawing area. */ QWidget* KtlQ3ScrollView::viewport() const { if (d->clipped_viewport) return d->clipped_viewport; return d->viewport; } /*! Returns the clipper widget. Contents in the scrollview are ultimately clipped to be inside the clipper widget. You should not need to use this function. \sa visibleWidth(), visibleHeight() */ QWidget* KtlQ3ScrollView::clipper() const { return d->viewport; } /*! \property KtlQ3ScrollView::visibleWidth \brief the horizontal amount of the content that is visible */ int KtlQ3ScrollView::visibleWidth() const { return clipper()->width(); } /*! \property KtlQ3ScrollView::visibleHeight \brief the vertical amount of the content that is visible */ int KtlQ3ScrollView::visibleHeight() const { return clipper()->height(); } void KtlQ3ScrollView::changeFrameRect(const QRect& r) { QRect oldr = frameRect(); if (oldr != r) { QRect cr = contentsRect(); QRegion fr(frameRect()); fr = fr.subtracted(contentsRect()); setFrameRect(r); if (isVisible()) { cr = cr.intersected(contentsRect()); fr = fr.united(frameRect()); fr = fr.subtracted(cr); if (!fr.isEmpty()) update(fr); } } } /*! Sets the margins around the scrolling area to \a left, \a top, \a right and \a bottom. This is useful for applications such as spreadsheets with "locked" rows and columns. The marginal space is \e inside the frameRect() and is left blank; reimplement drawFrame() or put widgets in the unused area. By default all margins are zero. \sa frameChanged() */ void KtlQ3ScrollView::setMargins(int left, int top, int right, int bottom) { if (left == d->l_marg && top == d->t_marg && right == d->r_marg && bottom == d->b_marg) return; d->l_marg = left; d->t_marg = top; d->r_marg = right; d->b_marg = bottom; updateScrollBars(); } /*! Returns the left margin. \sa setMargins() */ int KtlQ3ScrollView::leftMargin() const { return d->l_marg; } /*! Returns the top margin. \sa setMargins() */ int KtlQ3ScrollView::topMargin() const { return d->t_marg; } /*! Returns the right margin. \sa setMargins() */ int KtlQ3ScrollView::rightMargin() const { return d->r_marg; } /*! Returns the bottom margin. \sa setMargins() */ int KtlQ3ScrollView::bottomMargin() const { return d->b_marg; } /*! \reimp */ bool KtlQ3ScrollView::focusNextPrevChild(bool next) { // Makes sure that the new focus widget is on-screen, if // necessary by scrolling the scroll view. bool retval = KtlQ3Frame::focusNextPrevChild(next); if (retval) { QWidget *w = window()->focusWidget(); if (isAncestorOf(w)) { QSVChildRec *r = d->ancestorRec(w); if (r && (r->child == w || w->isVisibleTo(r->child))) { QPoint cp = r->child->mapToGlobal(QPoint(0, 0)); QPoint cr = w->mapToGlobal(QPoint(0, 0)) - cp; ensureVisible(r->x + cr.x() + w->width()/2, r->y + cr.y() + w->height()/2, w->width()/2, w->height()/2); } } } return retval; } /*! When a large numbers of child widgets are in a scrollview, especially if they are close together, the scrolling performance can suffer greatly. If \a y is true the scrollview will use an extra widget to group child widgets. Note that you may only call enableClipper() prior to adding widgets. */ void KtlQ3ScrollView::enableClipper(bool y) // note: this method as of 2018.11.30 is unused { if (!d->clipped_viewport == !y) return; if (d->children.count()) qFatal("May only call KtlQ3ScrollView::enableClipper() before adding widgets"); if (y) { d->clipped_viewport = new KtlQClipperWidget(clipper(), "qt_clipped_viewport", QFlag(d->flags)); d->clipped_viewport->setGeometry(-coord_limit/2,-coord_limit/2, coord_limit,coord_limit); //d->clipped_viewport->setBackgroundMode(d->viewport->backgroundMode()); d->clipped_viewport->setBackgroundRole(d->viewport->backgroundRole()); //d->viewport->setBackgroundMode(NoBackground); // no exposures for this // 2018.11.30 d->viewport->setAttribute(Qt::WA_NoSystemBackground); // hope this is the correct replacement for above d->viewport->removeEventFilter(this); d->clipped_viewport->installEventFilter(this); d->clipped_viewport->show(); } else { delete d->clipped_viewport; d->clipped_viewport = nullptr; } } /*! Sets the scrollview to have a static background if \a y is true, or a scrolling background if \a y is false. By default, the background is scrolling. Be aware that this mode is quite slow, as a full repaint of the visible area has to be triggered on every contents move. \sa hasStaticBackground() */ void KtlQ3ScrollView::setStaticBackground(bool y) { d->static_bg = y; } /*! Returns true if KtlQ3ScrollView uses a static background; otherwise returns false. \sa setStaticBackground() */ bool KtlQ3ScrollView::hasStaticBackground() const { return d->static_bg; } /*! \overload Returns the point \a p translated to a point on the viewport() widget. */ QPoint KtlQ3ScrollView::contentsToViewport(const QPoint& p) const { if (d->clipped_viewport) { return QPoint(p.x() - d->contentsX() - d->clipped_viewport->x(), p.y() - d->contentsY() - d->clipped_viewport->y()); } else { return QPoint(p.x() - d->contentsX(), p.y() - d->contentsY()); } } /*! \overload Returns the point on the viewport \a vp translated to a point in the contents. */ QPoint KtlQ3ScrollView::viewportToContents(const QPoint& vp) const { if (d->clipped_viewport) { return QPoint(vp.x() + d->contentsX() + d->clipped_viewport->x(), vp.y() + d->contentsY() + d->clipped_viewport->y()); } else { return QPoint(vp.x() + d->contentsX(), vp.y() + d->contentsY()); } } /*! Translates a point (\a x, \a y) in the contents to a point (\a vx, \a vy) on the viewport() widget. */ void KtlQ3ScrollView::contentsToViewport(int x, int y, int& vx, int& vy) const { const QPoint v = contentsToViewport(QPoint(x,y)); vx = v.x(); vy = v.y(); } /*! Translates a point (\a vx, \a vy) on the viewport() widget to a point (\a x, \a y) in the contents. */ void KtlQ3ScrollView::viewportToContents(int vx, int vy, int& x, int& y) const { const QPoint c = viewportToContents(QPoint(vx,vy)); x = c.x(); y = c.y(); } /*! \reimp */ QSize KtlQ3ScrollView::sizeHint() const { if (d->use_cached_size_hint && d->cachedSizeHint.isValid()) return d->cachedSizeHint; //constPolish(); // 2018.11.30 ensurePolished(); int f = 2 * frameWidth(); int h = fontMetrics().height(); QSize sz(f, f); if (d->policy > Manual) { QSVChildRec *r = d->children.first(); if (r) { QSize cs = r->child->sizeHint(); if (cs.isValid()) sz += cs.boundedTo(r->child->maximumSize()); else sz += r->child->size(); } } else { sz += QSize(d->contentsWidth(), contentsHeight()); } if (d->vMode == AlwaysOn) sz.setWidth(sz.width() + d->vbar->sizeHint().width()); if (d->hMode == AlwaysOn) sz.setHeight(sz.height() + d->hbar->sizeHint().height()); return sz.expandedTo(QSize(12 * h, 8 * h)) .boundedTo(QSize(36 * h, 24 * h)); } /*! \reimp */ QSize KtlQ3ScrollView::minimumSizeHint() const { int h = fontMetrics().height(); if (h < 10) h = 10; int f = 2 * frameWidth(); return QSize((6 * h) + f, (4 * h) + f); } /*! \reimp (Implemented to get rid of a compiler warning.) */ void KtlQ3ScrollView::drawContents(QPainter *) { } #ifndef QT_NO_DRAGANDDROP /*! \internal */ void KtlQ3ScrollView::startDragAutoScroll() { if (!d->autoscroll_timer.isActive()) { d->autoscroll_time = initialScrollTime; d->autoscroll_accel = initialScrollAccel; d->autoscroll_timer.start(d->autoscroll_time); } } /*! \internal */ void KtlQ3ScrollView::stopDragAutoScroll() { d->autoscroll_timer.stop(); } /*! \internal */ void KtlQ3ScrollView::doDragAutoScroll() { QPoint p = d->viewport->mapFromGlobal(QCursor::pos()); if (d->autoscroll_accel-- <= 0 && d->autoscroll_time) { d->autoscroll_accel = initialScrollAccel; d->autoscroll_time--; d->autoscroll_timer.start(d->autoscroll_time); } int l = qMax(1, (initialScrollTime- d->autoscroll_time)); int dx = 0, dy = 0; if (p.y() < autoscroll_margin) { dy = -l; } else if (p.y() > visibleHeight() - autoscroll_margin) { dy = +l; } if (p.x() < autoscroll_margin) { dx = -l; } else if (p.x() > visibleWidth() - autoscroll_margin) { dx = +l; } if (dx || dy) { scrollBy(dx,dy); } else { stopDragAutoScroll(); } } /*! \property KtlQ3ScrollView::dragAutoScroll \brief whether autoscrolling in drag move events is enabled If this property is set to true (the default), the KtlQ3ScrollView automatically scrolls the contents in drag move events if the user moves the cursor close to a border of the view. Of course this works only if the viewport accepts drops. Specifying false disables this autoscroll feature. */ void KtlQ3ScrollView::setDragAutoScroll(bool b) { d->drag_autoscroll = b; } bool KtlQ3ScrollView::dragAutoScroll() const { return d->drag_autoscroll; } #endif // QT_NO_DRAGANDDROP /*!\internal */ void KtlQ3ScrollView::setCachedSizeHint(const QSize &sh) const { if (isVisible() && !d->cachedSizeHint.isValid()) d->cachedSizeHint = sh; } /*!\internal */ void KtlQ3ScrollView::disableSizeHintCaching() { d->use_cached_size_hint = false; } /*!\internal */ QSize KtlQ3ScrollView::cachedSizeHint() const { return d->use_cached_size_hint ? d->cachedSizeHint : QSize(); } // QT_END_NAMESPACE diff --git a/src/ktlqt3support/ktlq3scrollview.h b/src/ktlqt3support/ktlq3scrollview.h index e8915b4b..40937920 100644 --- a/src/ktlqt3support/ktlq3scrollview.h +++ b/src/ktlqt3support/ktlq3scrollview.h @@ -1,280 +1,280 @@ /**************************************************************************** ** ** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the Qt3Support module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef KTL_Q3SCROLLVIEW_H #define KTL_Q3SCROLLVIEW_H #include -#include +#include // QT_BEGIN_HEADER //QT_BEGIN_NAMESPACE //QT_MODULE(Qt3SupportLight) class KtlQ3ScrollViewData; class KtlQ3ScrollView : public KtlQ3Frame { Q_OBJECT Q_ENUMS( ResizePolicy ScrollBarMode ) Q_PROPERTY( ResizePolicy resizePolicy READ resizePolicy WRITE setResizePolicy ) Q_PROPERTY( ScrollBarMode vScrollBarMode READ vScrollBarMode WRITE setVScrollBarMode ) Q_PROPERTY( ScrollBarMode hScrollBarMode READ hScrollBarMode WRITE setHScrollBarMode ) Q_PROPERTY( int visibleWidth READ visibleWidth ) Q_PROPERTY( int visibleHeight READ visibleHeight ) Q_PROPERTY( int contentsWidth READ contentsWidth ) Q_PROPERTY( int contentsHeight READ contentsHeight ) Q_PROPERTY( int contentsX READ contentsX ) Q_PROPERTY( int contentsY READ contentsY ) Q_PROPERTY( bool dragAutoScroll READ dragAutoScroll WRITE setDragAutoScroll ) public: KtlQ3ScrollView(QWidget* parent = nullptr, const char* name = nullptr, Qt::WindowFlags f = {}); ~KtlQ3ScrollView() override; enum ResizePolicy { Default, Manual, AutoOne, AutoOneFit }; virtual void setResizePolicy( ResizePolicy ); ResizePolicy resizePolicy() const; void styleChange( QStyle & ); void removeChild(QWidget* child); virtual void addChild( QWidget* child, int x=0, int y=0 ); virtual void moveChild( QWidget* child, int x, int y ); int childX(QWidget* child); int childY(QWidget* child); bool childIsVisible(QWidget* child) { return child->isVisible(); } // obsolete functions void showChild(QWidget* child, bool yes=true) { child->setVisible(yes); } enum ScrollBarMode { Auto, AlwaysOff, AlwaysOn }; ScrollBarMode vScrollBarMode() const; virtual void setVScrollBarMode( ScrollBarMode ); ScrollBarMode hScrollBarMode() const; virtual void setHScrollBarMode( ScrollBarMode ); QWidget* cornerWidget() const; virtual void setCornerWidget(QWidget*); // ### 4.0: Consider providing a factory function for scrollbars // (e.g. make the two following functions virtual) QScrollBar* horizontalScrollBar() const; QScrollBar* verticalScrollBar() const; QWidget* viewport() const; QWidget* clipper() const; int visibleWidth() const; int visibleHeight() const; int contentsWidth() const; int contentsHeight() const; int contentsX() const; int contentsY() const; void resize( int w, int h ); void resize( const QSize& ); void setVisible(bool visible) override; void updateContents( int x, int y, int w, int h ); void updateContents( const QRect& r ); void updateContents(); void repaintContents( int x, int y, int w, int h, bool erase=true ); void repaintContents( const QRect& r, bool erase=true ); void repaintContents( bool erase=true ); void contentsToViewport( int x, int y, int& vx, int& vy ) const; void viewportToContents( int vx, int vy, int& x, int& y ) const; QPoint contentsToViewport( const QPoint& ) const; QPoint viewportToContents( const QPoint& ) const; void enableClipper( bool y ); void setStaticBackground( bool y ); bool hasStaticBackground() const; QSize viewportSize( int, int ) const; QSize sizeHint() const override; QSize minimumSizeHint() const override; void removeChild(QObject* child); bool isHorizontalSliderPressed(); bool isVerticalSliderPressed(); virtual void setDragAutoScroll( bool b ); bool dragAutoScroll() const; Q_SIGNALS: void contentsMoving(int x, int y); void horizontalSliderPressed(); void horizontalSliderReleased(); void verticalSliderPressed(); void verticalSliderReleased(); public Q_SLOTS: virtual void resizeContents( int w, int h ); void scrollBy( int dx, int dy ); virtual void setContentsPos( int x, int y ); void ensureVisible(int x, int y); void ensureVisible(int x, int y, int xmargin, int ymargin); void center(int x, int y); void center(int x, int y, float xmargin, float ymargin); void updateScrollBars(); // ### virtual in 4.0 void setEnabled( bool enable ); protected: virtual void drawContents(QPainter*, int cx, int cy, int cw, int ch); virtual void drawContentsOffset(QPainter*, int ox, int oy, int cx, int cy, int cw, int ch); virtual void contentsMousePressEvent( QMouseEvent* ); virtual void contentsMouseReleaseEvent( QMouseEvent* ); virtual void contentsMouseDoubleClickEvent( QMouseEvent* ); virtual void contentsMouseMoveEvent( QMouseEvent* ); virtual void contentsDragEnterEvent( QDragEnterEvent * ); virtual void contentsDragMoveEvent( QDragMoveEvent * ); virtual void contentsDragLeaveEvent( QDragLeaveEvent * ); virtual void contentsDropEvent( QDropEvent * ); virtual void contentsWheelEvent( QWheelEvent * ); virtual void contentsContextMenuEvent( QContextMenuEvent * ); virtual void viewportPaintEvent( QPaintEvent* ); virtual void viewportResizeEvent( QResizeEvent* ); virtual void viewportMousePressEvent( QMouseEvent* ); virtual void viewportMouseReleaseEvent( QMouseEvent* ); virtual void viewportMouseDoubleClickEvent( QMouseEvent* ); virtual void viewportMouseMoveEvent( QMouseEvent* ); virtual void viewportDragEnterEvent( QDragEnterEvent * ); virtual void viewportDragMoveEvent( QDragMoveEvent * ); virtual void viewportDragLeaveEvent( QDragLeaveEvent * ); virtual void viewportDropEvent( QDropEvent * ); virtual void viewportWheelEvent( QWheelEvent * ); virtual void viewportContextMenuEvent( QContextMenuEvent * ); void frameChanged() override; public: virtual void setMargins(int left, int top, int right, int bottom); int leftMargin() const; int topMargin() const; int rightMargin() const; int bottomMargin() const; protected: bool focusNextPrevChild( bool next ) override; virtual void setHBarGeometry(QScrollBar& hbar, int x, int y, int w, int h); virtual void setVBarGeometry(QScrollBar& vbar, int x, int y, int w, int h); void resizeEvent(QResizeEvent*) override; void mousePressEvent( QMouseEvent * ) override; void mouseReleaseEvent( QMouseEvent * ) override; void mouseDoubleClickEvent( QMouseEvent * ) override; void mouseMoveEvent( QMouseEvent * ) override; void wheelEvent( QWheelEvent * ) override; void contextMenuEvent( QContextMenuEvent * ) override; bool eventFilter( QObject *, QEvent *e ) override; void setCachedSizeHint( const QSize &sh ) const; QSize cachedSizeHint() const; void fontChange( const QFont & ); private: void drawContents( QPainter* ) override; void moveContents(int x, int y); KtlQ3ScrollViewData* d; private Q_SLOTS: void hslide(int); void vslide(int); void hbarIsPressed(); void hbarIsReleased(); void vbarIsPressed(); void vbarIsReleased(); void doDragAutoScroll(); void startDragAutoScroll(); void stopDragAutoScroll(); private: // Disabled copy constructor and operator= Q_DISABLE_COPY(KtlQ3ScrollView) void changeFrameRect(const QRect&); public: void disableSizeHintCaching(); }; class KtlQAbstractScrollAreaWidget : public QWidget { Q_OBJECT public: KtlQAbstractScrollAreaWidget(KtlQ3ScrollView* parent = nullptr, const char* name = nullptr, Qt::WindowFlags f = {}) : QWidget(parent, f) { setObjectName(name); setAutoFillBackground(true); } }; class KtlQClipperWidget : public QWidget { Q_OBJECT public: KtlQClipperWidget(QWidget * parent = nullptr, const char * name = nullptr, Qt::WindowFlags f = {}) : QWidget (parent,f) { setObjectName(name); } }; //QT_END_NAMESPACE // QT_END_HEADER #endif // KTL_Q3SCROLLVIEW_H diff --git a/src/languages/asmparser.cpp b/src/languages/asmparser.cpp index d356c143..beac6d1d 100644 --- a/src/languages/asmparser.cpp +++ b/src/languages/asmparser.cpp @@ -1,147 +1,147 @@ /*************************************************************************** * Copyright (C) 2005 by David Saxton * * david@bluehaze.org * * * * 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 "asmparser.h" #include "config.h" #include "gpsimprocessor.h" -#include -#include -#include -#include +#include +#include +#include +#include AsmParser::AsmParser( const QString &url ) : m_url(url) { m_bContainsRadix = false; m_type = Absolute; } AsmParser::~AsmParser() { } bool AsmParser::parse( GpsimDebugger * debugger ) { QFile file(m_url); if ( !file.open(QIODevice::ReadOnly) ) return false; QTextStream stream( &file ); m_type = Absolute; m_bContainsRadix = false; m_picID = QString::null; //QStringList nonAbsoluteOps = QStringList::split( ",", // "code,.def,.dim,.direct,endw,extern,.file,global,idata,.ident,.line,.type,udata,udata_acs,udata_ovr,udata_shr" ); QStringList nonAbsoluteOps = QString( "code,.def,.dim,.direct,endw,extern,.file,global,idata,.ident,.line,.type,udata,udata_acs,udata_ovr,udata_shr") .split(","); unsigned inputAtLine = 0; while ( !stream.atEnd() ) { const QString line = stream.readLine().trimmed(); if ( m_type != Relocatable ) { QString col0 = line.section( QRegExp("[; ]"), 0, 0 ); col0 = col0.trimmed(); if ( nonAbsoluteOps.contains(col0) ) m_type = Relocatable; } if ( !m_bContainsRadix ) { if ( line.contains( QRegExp("^RADIX[\\s]*") ) || line.contains( QRegExp("^radix[\\s]*") ) ) m_bContainsRadix = true; } if ( m_picID.isEmpty() ) { // We look for "list p = ", and "list p = picid ", and subtract the positions / lengths away from each other to get the picid text position QRegExp fullRegExp("[lL][iI][sS][tT][\\s]+[pP][\\s]*=[\\s]*[\\d\\w]+"); QRegExp halfRegExp("[lL][iI][sS][tT][\\s]+[pP][\\s]*=[\\s]*"); int startPos = fullRegExp.indexIn(line); if ( (startPos != -1) && (startPos == halfRegExp.indexIn(line)) ) { m_picID = line.mid( startPos + halfRegExp.matchedLength(), fullRegExp.matchedLength() - halfRegExp.matchedLength() ); m_picID = m_picID.toUpper(); if ( !m_picID.startsWith("P") ) m_picID.prepend("P"); } } #ifndef NO_GPSIM if ( debugger && line.startsWith(";#CSRC\t") ) { // Assembly file produced (by sdcc) from C, line is in format: // ;#CSRC\t[file-name] [file-line] // The filename can contain spaces. int fileLineAt = line.lastIndexOf(" "); if ( fileLineAt == -1 ) qWarning() << Q_FUNC_INFO << "Syntax error in line \"" << line << "\" while looking for file-line" << endl; else { // 7 = length_of(";#CSRC\t") QString fileName = line.mid( 7, fileLineAt-7 ); QString fileLineString = line.mid( fileLineAt+1, line.length() - fileLineAt - 1 ); if ( fileName.startsWith("\"") ) { // Newer versions of SDCC insert " around the filename fileName.remove( 0, 1 ); // First " fileName.remove( fileName.length()-1, 1 ); // Last " } bool ok; int fileLine = fileLineString.toInt(&ok) - 1; if ( ok && fileLine >= 0 ) debugger->associateLine( fileName, fileLine, m_url, inputAtLine ); else qDebug() << Q_FUNC_INFO << "Not a valid line number: \"" << fileLineString << "\"" << endl; } } if ( debugger && (line.startsWith(".line\t") || line.startsWith(";#MSRC") ) ) { // Assembly file produced by either sdcc or microbe, line is in format: // \t[".line"/"#MSRC"]\t[file-line]; [file-name]\t[c/microbe source code for that line] // We're screwed if the file name contains tabs, but hopefully not many do... //QStringList lineParts = QStringList::split( '\t', line ); // 2018.12.01 QStringList lineParts = line.split( '\t' , QString::SkipEmptyParts ); if ( lineParts.size() < 2 ) qWarning() << Q_FUNC_INFO << "Line is in wrong format for extracing source line and file: \""<= 0 ) debugger->associateLine( fileName, fileLine, m_url, inputAtLine ); else qDebug() << Q_FUNC_INFO << "Not a valid line number: \"" << fileLineString << "\"" << endl; } } } #endif // !NO_GPSIM inputAtLine++; } return true; } diff --git a/src/languages/asmparser.h b/src/languages/asmparser.h index cebeb9c7..d568b370 100644 --- a/src/languages/asmparser.h +++ b/src/languages/asmparser.h @@ -1,62 +1,62 @@ /*************************************************************************** * Copyright (C) 2005 by David Saxton * * david@bluehaze.org * * * * 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. * ***************************************************************************/ #ifndef ASMPARSER_H #define ASMPARSER_H -#include +#include class GpsimDebugger; /** Reads in an assembly file, and extracts useful information from it, such as the PIC ID @author David Saxton */ class AsmParser { public: /// @param url a path to a file in the local filesystem AsmParser( const QString &url ); ~AsmParser(); enum Type { Relocatable, Absolute }; /** * Read in data from file, return success status. * @param debugger if this is non-null, then source-line markers read * from the assembly file (such as those beginning with ";#CSRC" will be * passed to hllDebugger). */ bool parse( GpsimDebugger * debugger = nullptr ); /** * Returns the PIC ID */ QString picID() const { return m_picID; } /** * Returns whether or not the assembly file contained the "set radix" * directive */ bool containsRadix() const { return m_bContainsRadix; } /** * If the assembly file contains any of several key words that identify * it as a relocatable object, then this will return Relocatable. */ Type type() const { return m_type; } protected: const QString m_url; QString m_picID; bool m_bContainsRadix; Type m_type; }; #endif diff --git a/src/languages/externallanguage.cpp b/src/languages/externallanguage.cpp index b7fc9d1a..4f74f8dd 100644 --- a/src/languages/externallanguage.cpp +++ b/src/languages/externallanguage.cpp @@ -1,179 +1,179 @@ /*************************************************************************** * Copyright (C) 2005 by David Saxton * * david@bluehaze.org * * * * 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 "externallanguage.h" #include "languagemanager.h" #include "logview.h" -#include #include #include -#include -#include +#include +#include +#include ExternalLanguage::ExternalLanguage( ProcessChain *processChain, const QString &name ) : Language( processChain, name ) { m_languageProcess = nullptr; } ExternalLanguage::~ExternalLanguage() { deleteLanguageProcess(); } void ExternalLanguage::deleteLanguageProcess() { if (!m_languageProcess) return; // I'm not too sure if this combination of killing the process is the best way.... // m_languageProcess->tryTerminate(); // QTimer::singleShot( 5000, m_languageProcess, SLOT( kill() ) ); // delete m_languageProcess; m_languageProcess->kill(); m_languageProcess->deleteLater(); m_languageProcess = nullptr; } void ExternalLanguage::processStdout() { QString allOut = m_languageProcess->readAllStandardOutput(); QStringList lines = allOut.split('\n', QString::SkipEmptyParts); //QStringList::split( '\n', allOut, false ); // 2018.12.01 QStringList::iterator end = lines.end(); for ( QStringList::iterator it = lines.begin(); it != end; ++it ) { if ( isError( *it ) ) { outputError( *it ); outputtedError( *it ); } else if ( isWarning( *it ) ) { outputWarning( *it ); outputtedWarning( *it ); } else { outputMessage( *it ); outputtedMessage( *it ); } } } void ExternalLanguage::processStderr() { QString allStdErr = m_languageProcess->readAllStandardError(); QStringList lines = allStdErr.split('\n', QString::SkipEmptyParts); // QStringList::split( '\n', allStdErr, false ); QStringList::iterator end = lines.end(); for ( QStringList::iterator it = lines.begin(); it != end; ++it ) { if ( isStderrOutputFatal( *it ) ) { outputError( *it ); outputtedError( *it ); } else { outputWarning( *it ); outputtedWarning( *it ); } } } void ExternalLanguage::processExited( int, QProcess::ExitStatus ) { if ( !m_languageProcess ) { qDebug() << Q_FUNC_INFO << " m_languageProcess == nullptr, returning"; return; } bool allOk = processExited( (m_languageProcess->exitStatus() == QProcess::NormalExit) && (m_errorCount == 0) ); finish(allOk); deleteLanguageProcess(); } void ExternalLanguage::processInitFailed() { finish(false); deleteLanguageProcess(); } bool ExternalLanguage::start() { displayProcessCommand(); m_languageProcess->setOutputChannelMode(KProcess::SeparateChannels); m_languageProcess->start( ); return m_languageProcess->waitForStarted(); } void ExternalLanguage::resetLanguageProcess() { reset(); deleteLanguageProcess(); m_errorCount = 0; m_languageProcess = new KProcess(this); connect( m_languageProcess, SIGNAL(readyReadStandardOutput()), this, SLOT(processStdout()) ); connect( m_languageProcess, SIGNAL(readyReadStandardError()), this, SLOT(processStderr()) ); connect( m_languageProcess, SIGNAL(finished(int, QProcess::ExitStatus)), this, SLOT(processExited(int, QProcess::ExitStatus )) ); } void ExternalLanguage::displayProcessCommand() { QStringList quotedArguments; QList arguments = m_languageProcess->program(); // QList arguments; // for (QList::const_iterator itArgs = m_languageProcess->args().begin(); // itArgs != m_languageProcess->args().end(); // ++itArgs) { // arguments.append(QString(*itArgs)); // } if ( arguments.size() == 1 ) quotedArguments << arguments[0]; else { QList::const_iterator end = arguments.end(); for ( QList::const_iterator it = arguments.begin(); it != end; ++it ) { if ( (*it).isEmpty() || (*it).contains( QRegExp("[\\s]") ) ) quotedArguments << KShell::quoteArg( *it ); else quotedArguments << *it; } } // outputMessage( "" + quotedArguments.join(" ") + "" ); outputMessage( quotedArguments.join(" ") ); // LanguageManager::self()->logView()->addOutput( quotedArguments.join(" "), LogView::ot_info ); } diff --git a/src/languages/externallanguage.h b/src/languages/externallanguage.h index 859abc24..22b10a87 100644 --- a/src/languages/externallanguage.h +++ b/src/languages/externallanguage.h @@ -1,99 +1,99 @@ /*************************************************************************** * Copyright (C) 2005 by David Saxton * * david@bluehaze.org * * * * 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. * ***************************************************************************/ #ifndef EXTERNALLANGUAGE_H #define EXTERNALLANGUAGE_H #include "language.h" -#include +#include class KProcess; /** Base class for Language support that relies on an external program; so this class provides functionality for dealing with external processes. @author Daniel Clarke @author David Saxton */ class ExternalLanguage : public Language { Q_OBJECT public: ExternalLanguage( ProcessChain *processChain, const QString &name ); ~ExternalLanguage() override; protected slots: void processStdout(); void processStderr(); void processExited( int, QProcess::ExitStatus ); protected: /** * Call this to start the language process. ExternalLanguage will ensure * that communication et all is properly set up. * @return true on success, false on error */ bool start(); /** * @returns whether the string outputted to stdout is an error or not */ virtual bool isError( const QString &message ) const = 0; /** * @returns whether the string outputted to stderr is fatal (stopped compilation) */ virtual bool isStderrOutputFatal( const QString & message ) const { Q_UNUSED(message); return true; } /** * @returns whether the string outputted to stdout is a warning or not */ virtual bool isWarning( const QString &message ) const = 0; /** * Called when the process outputs a (non warning/error) message */ virtual void outputtedMessage( const QString &/*message*/ ) {}; /** * Called when the process outputs a warning */ virtual void outputtedWarning( const QString &/*message*/ ) {}; /** * Called when the process outputs an error */ virtual void outputtedError( const QString &/*message*/ ) {}; /** * Called when the process exits (called before any signals are emitted, * etc). If you reinherit this function, you should return whether * everything is OK. */ virtual bool processExited( bool successfully ) { return successfully; } /** * Call this function if your process could not be started - the process * will be deleted, and a failure signal emitted. */ void processInitFailed(); /** * Disconnects and deletes the language's process. */ void deleteLanguageProcess(); /** * Creates process and makes connections, ready for the derived class to * add arguments and start the process. */ void resetLanguageProcess(); /** * Prints out the command used for running the process, with any arguments * that contain spaces put into quotation marks. */ void displayProcessCommand(); KProcess * m_languageProcess; }; #endif diff --git a/src/languages/flowcode.cpp b/src/languages/flowcode.cpp index f0cb4fb4..d8cad3b1 100644 --- a/src/languages/flowcode.cpp +++ b/src/languages/flowcode.cpp @@ -1,503 +1,503 @@ /*************************************************************************** * Copyright (C) 2003-2005 by David Saxton * * david@bluehaze.org * * * * 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 "flowcodedocument.h" #include "flowcode.h" #include "flowcontainer.h" #include "flowpart.h" #include "microsettings.h" #include "microinfo.h" #include "micropackage.h" #include "node.h" #include "outputflownode.h" #include "pinmapping.h" #include // #include -#include -#include +#include +#include FlowCode::FlowCode( ProcessChain *processChain ) : Language( processChain, i18n("FlowCode") ) { m_successfulMessage = i18n("*** Microbe generation successful ***"); m_failedMessage = i18n("*** Microbe generation failed ***"); p_startPart = nullptr; } FlowCode::~FlowCode() { } void FlowCode::processInput( ProcessOptions options ) { m_processOptions = options; if ( !options.p_flowCodeDocument ) { options.p_flowCodeDocument = new FlowCodeDocument( QString::null, nullptr ); options.p_flowCodeDocument->openURL(QUrl::fromLocalFile(options.inputFiles().first())); connect( this, SIGNAL(processSucceeded( Language *)), options.p_flowCodeDocument, SLOT(deleteLater()) ); connect( this, SIGNAL(processFailed( Language *)), options.p_flowCodeDocument, SLOT(deleteLater()) ); } if ( !options.p_flowCodeDocument->microSettings() ) { finish(false); return; } QFile file(options.intermediaryOutput()); if ( file.open(QIODevice::WriteOnly | QIODevice::ReadOnly) == false ) { finish(false); return; } file.close(); if ( file.open(QIODevice::WriteOnly) == false ) { finish(false); return; } const QString code = generateMicrobe( options.p_flowCodeDocument->itemList(), options.p_flowCodeDocument->microSettings() ); if (code.isEmpty()) { finish(false); return; } QTextStream stream(&file); stream << code; file.close(); finish(true); } void FlowCode::setStartPart( FlowPart *startPart ) { p_startPart = startPart; } void FlowCode::addCode( const QString& code ) { m_code += code; if ( !m_code.endsWith("\n") ) m_code += '\n'; } bool FlowCode::isValidBranch( FlowPart *flowPart ) { return flowPart && (flowPart->level() >= m_curLevel) && !m_stopParts.contains(flowPart); } void FlowCode::addCodeBranch( FlowPart * flowPart ) { if (!flowPart) return; if ( !isValidBranch(flowPart) ) return; if ( m_addedParts.contains(flowPart) ) { const QString labelName = genLabel(flowPart->id()); addCode( "goto "+labelName ); m_gotos.append(labelName); return; } else { m_addedParts.append(flowPart); int prevLevel = m_curLevel; m_curLevel = flowPart->level(); const QString labelName = genLabel(flowPart->id()); addCode(labelName+':'); m_labels.append(labelName); flowPart->generateMicrobe(this); m_curLevel = prevLevel; } } QString FlowCode::genLabel( const QString &id ) { return "__label_"+id; } void FlowCode::addStopPart( FlowPart *part ) { if (part) m_stopParts.append(part); } void FlowCode::removeStopPart( FlowPart *part ) { if (!part) return; // We only want to remove one instance of the FlowPart, in case it has been // used as a StopPart for more than one FlowPart //FlowPartList::iterator it = m_stopParts.find(part); // 2018.12.01 //if ( it != m_stopParts.end() ) m_stopParts.remove(it); int foundIndex = m_stopParts.indexOf(part); if ( -1 == foundIndex ) { m_stopParts.removeAll(part); } } QString FlowCode::generateMicrobe( const ItemList &itemList, MicroSettings *settings ) { bool foundStart = false; const ItemList::const_iterator end = itemList.end(); for ( ItemList::const_iterator it = itemList.begin(); it != end; ++it ) { if (!*it) continue; FlowPart * startPart = dynamic_cast((Item*)*it); if (!startPart) continue; // Check to see if we have any floating connections const NodeInfoMap nodeMap = startPart->nodeMap(); NodeInfoMap::const_iterator nodeMapEnd = nodeMap.end(); for ( NodeInfoMap::const_iterator nodeMapIt = nodeMap.begin(); nodeMapIt != nodeMapEnd; ++nodeMapIt ) { Node * node = nodeMapIt.value().node; // FIXME dynamic_cast used if( !node || ( dynamic_cast(node) == nullptr) ) continue; if ( !startPart->outputPart( nodeMapIt.key() ) ) outputWarning( i18n("Warning: Floating connection for %1", startPart->id() ) ); } FlowContainer * fc = dynamic_cast((Item*)*it); if ( (*it)->id().startsWith("START") && startPart ) { foundStart = true; setStartPart(startPart); } else if ( ((*it)->id().startsWith("interrupt") || (*it)->id().startsWith("sub")) && fc ) { addSubroutine(fc); } } if (!foundStart) { outputError( i18n("KTechlab was unable to find the \"Start\" part.\nThis must be included as the starting point for your program.") ); return nullptr; } m_addedParts.clear(); m_stopParts.clear(); m_gotos.clear(); m_labels.clear(); m_code = QString::null; // PIC type { const QString codeString = settings->microInfo()->id() + "\n"; addCode(codeString); } // Initial variables { QStringList vars = settings->variableNames(); // If "inited" is true at the end, we comment at the insertion point bool inited = false; const QString codeString = "// Initial variable values:\n"; addCode(codeString); const QStringList::iterator end = vars.end(); for ( QStringList::iterator it = vars.begin(); it != end; ++it ) { VariableInfo *info = settings->variableInfo(*it); if ( info /*&& info->initAtStart*/ ) { inited = true; addCode(*it+" = "+info->valueAsString()); } } if (!inited) { m_code.remove(codeString); } else { addCode("\n"); } } // Initial pin maps { const PinMappingMap pinMappings = settings->pinMappings(); PinMappingMap::const_iterator end = pinMappings.end(); for ( PinMappingMap::const_iterator it = pinMappings.begin(); it != end; ++it ) { QString type; switch ( it.value().type() ) { case PinMapping::Keypad_4x3: case PinMapping::Keypad_4x4: type = "keypad"; break; case PinMapping::SevenSegment: type = "sevenseg"; break; case PinMapping::Invalid: break; } if ( type.isEmpty() ) continue; addCode( QString("%1 %2 %3").arg( type ).arg( it.key() ).arg( it.value().pins().join(" ") ) ); } } // Initial port settings { QStringList portNames = settings->microInfo()->package()->portNames(); const QStringList::iterator end = portNames.end(); // TRIS registers (remember that this is set to ..11111 on all resets) for ( QStringList::iterator it = portNames.begin(); it != end; ++it ) { const int portType = settings->portType(*it); const int pinCount = settings->microInfo()->package()->pinCount( 0, *it ); // We don't need to reset it if portType == 2^(pinCount-1) if ( portType != (1<level(); addCodeBranch(p_startPart); addCode("end"); { const FlowPartList::iterator end = m_subroutines.end(); for ( FlowPartList::iterator it = m_subroutines.begin(); it != end; ++it ) { m_curLevel = 0; if (*it) { addCode("\n"); addCodeBranch(*it); } } } tidyCode(); return m_code; } void FlowCode::tidyCode() { // First, get rid of the unused labels const QStringList::iterator end = m_labels.end(); for ( QStringList::iterator it = m_labels.begin(); it != end; ++it ) { if ( !m_gotos.contains(*it) ) m_code.remove(*it+':'); } // And now on to handling indentation :-) if ( !m_code.endsWith("\n") ) m_code.append("\n"); QString newCode; bool multiLineComment = false; // For "/*"..."*/" bool comment = false; // For "//" bool asmEmbed = false; bool asmEmbedAllowed = true; bool asmKeyword = false; int asmEmbedLevel = -1; int level = 0; int pos=-1; const int length = m_code.length(); while ( ++posparentItem() || !dynamic_cast(part) ) return; m_subroutines.append(part); } ProcessOptions::ProcessPath::Path FlowCode::outputPath( ProcessOptions::ProcessPath::Path inputPath ) const { switch (inputPath) { case ProcessOptions::ProcessPath::FlowCode_AssemblyAbsolute: return ProcessOptions::ProcessPath::Microbe_AssemblyAbsolute; case ProcessOptions::ProcessPath::FlowCode_Microbe: return ProcessOptions::ProcessPath::None; case ProcessOptions::ProcessPath::FlowCode_PIC: return ProcessOptions::ProcessPath::Microbe_PIC; case ProcessOptions::ProcessPath::FlowCode_Program: return ProcessOptions::ProcessPath::Microbe_Program; case ProcessOptions::ProcessPath::AssemblyAbsolute_PIC: case ProcessOptions::ProcessPath::AssemblyAbsolute_Program: case ProcessOptions::ProcessPath::AssemblyRelocatable_Library: case ProcessOptions::ProcessPath::AssemblyRelocatable_Object: case ProcessOptions::ProcessPath::AssemblyRelocatable_PIC: case ProcessOptions::ProcessPath::AssemblyRelocatable_Program: case ProcessOptions::ProcessPath::C_AssemblyRelocatable: case ProcessOptions::ProcessPath::C_Library: case ProcessOptions::ProcessPath::C_Object: case ProcessOptions::ProcessPath::C_PIC: case ProcessOptions::ProcessPath::C_Program: case ProcessOptions::ProcessPath::Microbe_AssemblyAbsolute: case ProcessOptions::ProcessPath::Microbe_PIC: case ProcessOptions::ProcessPath::Microbe_Program: case ProcessOptions::ProcessPath::Object_Disassembly: case ProcessOptions::ProcessPath::Object_Library: case ProcessOptions::ProcessPath::Object_PIC: case ProcessOptions::ProcessPath::Object_Program: case ProcessOptions::ProcessPath::PIC_AssemblyAbsolute: case ProcessOptions::ProcessPath::Program_Disassembly: case ProcessOptions::ProcessPath::Program_PIC: case ProcessOptions::ProcessPath::Invalid: case ProcessOptions::ProcessPath::None: return ProcessOptions::ProcessPath::Invalid; } return ProcessOptions::ProcessPath::Invalid; } diff --git a/src/languages/flowcode.h b/src/languages/flowcode.h index 267749c4..3517e1b0 100644 --- a/src/languages/flowcode.h +++ b/src/languages/flowcode.h @@ -1,106 +1,106 @@ /*************************************************************************** * Copyright (C) 2003-2004 by David Saxton * * david@bluehaze.org * * * * 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. * ***************************************************************************/ #ifndef FLOWCODE_H #define FLOWCODE_H #include "language.h" -#include -#include -#include -#include -#include +#include +#include +#include +#include +#include class CNItem; class FlowPart; class Item; class MicroSettings; typedef QList FlowPartList; typedef QList > ItemList; /** "FlowCode" can possibly be considered a misnomer, as the output is actually Microbe. However, the function of this class is to take a set of FlowParts, and generate the basic from the code that they create. The 3 simple steps for usage of this function: (1) Create an instance of this class, giving the Start point and setings (2) Add all the subroutines present using addSubroutine() (3) Call generateMicrobe() to get the Microbe code. @author David Saxton */ class FlowCode : public Language { public: FlowCode( ProcessChain *processChain ); void processInput( ProcessOptions options ) override; ProcessOptions::ProcessPath::Path outputPath( ProcessOptions::ProcessPath::Path inputPath ) const override; /** * You must set the start part */ void setStartPart( FlowPart *startPart ); ~FlowCode() override; /** * You must add all top level subroutines using this function */ void addSubroutine( FlowPart *part ); /** * Adds code at the current insertion point */ void addCode( const QString& code ); /** * Adds a code branch to the current insertion point. This will stop when the level gets * below the original starting level (so for insertion of the contents of a for loop, * insertion will stop at the end of that for loop). * @param flowPart The next FlowPart to get code from */ void addCodeBranch( FlowPart *flowPart ); /** * Designates a FlowPart as a stopping part (i.e. will refuse requests to addCodeBranch * for that FlowPart until removeStopPart is called */ void addStopPart( FlowPart *part ); /** * Undesignates a FlowPart as a stopping part */ void removeStopPart( FlowPart *part ); /** * Generates and returns the microbe code */ QString generateMicrobe( const ItemList &itemList, MicroSettings *settings ); /** * Returns true if the FlowPart is a valid one for adding a branch */ bool isValidBranch( FlowPart *flowPart ); /** * Generates a nice label name from the string, e.g. genLabel("callsub") * returns "__label_callsub". */ static QString genLabel( const QString &id ); protected: /** * Performs indenting, removal of unnecessary labels, etc. */ void tidyCode(); QStringList m_gotos; // Gotos used QStringList m_labels; // Labels used FlowPartList m_subroutines; FlowPartList m_addedParts; FlowPartList m_stopParts; FlowPart *p_startPart; QString m_code; int m_curLevel; }; #endif diff --git a/src/languages/gpasm.cpp b/src/languages/gpasm.cpp index 720ead62..fbfbba61 100644 --- a/src/languages/gpasm.cpp +++ b/src/languages/gpasm.cpp @@ -1,188 +1,188 @@ /*************************************************************************** * Copyright (C) 2005 by David Saxton * * david@bluehaze.org * * * * 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 "asmparser.h" #include "docmanager.h" #include "gpasm.h" #include "logview.h" #include "languagemanager.h" #include #include #include -#include +#include #include Gpasm::Gpasm( ProcessChain *processChain ) : ExternalLanguage( processChain, "Gpasm" ) { m_successfulMessage = i18n("*** Assembly successful ***"); m_failedMessage = i18n("*** Assembly failed ***"); } Gpasm::~Gpasm() { } void Gpasm::processInput( ProcessOptions options ) { resetLanguageProcess(); m_processOptions = options; AsmParser p( options.inputFiles().first() ); p.parse(); *m_languageProcess << ("gpasm"); if ( ProcessOptions::ProcessPath::from( options.processPath() ) == ProcessOptions::ProcessPath::AssemblyRelocatable ) *m_languageProcess << ("--object"); // *m_languageProcess << ("--debug-info"); // Debug info // Output filename *m_languageProcess << ("--output"); *m_languageProcess << ( options.intermediaryOutput() ); if ( !options.m_hexFormat.isEmpty() ) { *m_languageProcess << ("--hex-format"); *m_languageProcess << (options.m_hexFormat); } // Radix if ( !p.containsRadix() ) { *m_languageProcess << ("--radix"); switch( KTLConfig::radix() ) { case KTLConfig::EnumRadix::Binary: *m_languageProcess << ("BIN"); break; case KTLConfig::EnumRadix::Octal: *m_languageProcess << ("OCT"); break; case KTLConfig::EnumRadix::Hexadecimal: *m_languageProcess << ("HEX"); break; case KTLConfig::EnumRadix::Decimal: default: *m_languageProcess << ("DEC"); break; } } // Warning Level *m_languageProcess << ("--warning"); switch( KTLConfig::gpasmWarningLevel() ) { case KTLConfig::EnumGpasmWarningLevel::Warnings: *m_languageProcess << ("1"); break; case KTLConfig::EnumGpasmWarningLevel::Errors: *m_languageProcess << ("2"); break; default: case KTLConfig::EnumGpasmWarningLevel::All: *m_languageProcess << ("0"); break; } // Ignore case if ( KTLConfig::ignoreCase() ) *m_languageProcess << ("--ignore-case"); // Dos formatting if ( KTLConfig::dosFormat() ) *m_languageProcess << ("--dos"); // Force list if ( options.b_forceList ) *m_languageProcess << ("--force-list"); // Other options if ( !KTLConfig::miscGpasmOptions().isEmpty() ) *m_languageProcess << ( KTLConfig::miscGpasmOptions() ); // Input Asm file *m_languageProcess << ( options.inputFiles().first() ); if ( !start() ) { KMessageBox::sorry( LanguageManager::self()->logView(), i18n("Assembly failed. Please check you have gputils installed.") ); processInitFailed(); return; } } bool Gpasm::isError( const QString &message ) const { return message.contains( "Error", Qt::CaseInsensitive ); } bool Gpasm::isWarning( const QString &message ) const { return message.contains( "Warning", Qt::CaseInsensitive ); } ProcessOptions::ProcessPath::Path Gpasm::outputPath( ProcessOptions::ProcessPath::Path inputPath ) const { switch (inputPath) { case ProcessOptions::ProcessPath::AssemblyAbsolute_PIC: return ProcessOptions::ProcessPath::Program_PIC; case ProcessOptions::ProcessPath::AssemblyAbsolute_Program: return ProcessOptions::ProcessPath::None; case ProcessOptions::ProcessPath::AssemblyRelocatable_Library: return ProcessOptions::ProcessPath::Object_Library; case ProcessOptions::ProcessPath::AssemblyRelocatable_Object: return ProcessOptions::ProcessPath::None; case ProcessOptions::ProcessPath::AssemblyRelocatable_PIC: return ProcessOptions::ProcessPath::Object_PIC; case ProcessOptions::ProcessPath::AssemblyRelocatable_Program: return ProcessOptions::ProcessPath::Object_Program; case ProcessOptions::ProcessPath::C_AssemblyRelocatable: case ProcessOptions::ProcessPath::C_Library: case ProcessOptions::ProcessPath::C_Object: case ProcessOptions::ProcessPath::C_PIC: case ProcessOptions::ProcessPath::C_Program: case ProcessOptions::ProcessPath::FlowCode_AssemblyAbsolute: case ProcessOptions::ProcessPath::FlowCode_Microbe: case ProcessOptions::ProcessPath::FlowCode_PIC: case ProcessOptions::ProcessPath::FlowCode_Program: case ProcessOptions::ProcessPath::Microbe_AssemblyAbsolute: case ProcessOptions::ProcessPath::Microbe_PIC: case ProcessOptions::ProcessPath::Microbe_Program: case ProcessOptions::ProcessPath::Object_Disassembly: case ProcessOptions::ProcessPath::Object_Library: case ProcessOptions::ProcessPath::Object_PIC: case ProcessOptions::ProcessPath::Object_Program: case ProcessOptions::ProcessPath::PIC_AssemblyAbsolute: case ProcessOptions::ProcessPath::Program_Disassembly: case ProcessOptions::ProcessPath::Program_PIC: case ProcessOptions::ProcessPath::Invalid: case ProcessOptions::ProcessPath::None: return ProcessOptions::ProcessPath::Invalid; } return ProcessOptions::ProcessPath::Invalid; } diff --git a/src/languages/gpdasm.cpp b/src/languages/gpdasm.cpp index 4c140a7b..9ffc7989 100644 --- a/src/languages/gpdasm.cpp +++ b/src/languages/gpdasm.cpp @@ -1,160 +1,160 @@ /*************************************************************************** * Copyright (C) 2005 by David Saxton * * david@bluehaze.org * * * * 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 "docmanager.h" #include "gpdasm.h" #include "logview.h" #include "languagemanager.h" #include #include #include -#include -#include -#include +#include +#include +#include Gpdasm::Gpdasm( ProcessChain *processChain ) : ExternalLanguage( processChain, "Gpdasm" ) { m_successfulMessage = i18n("*** Disassembly successful ***"); m_failedMessage = i18n("*** Disassembly failed ***"); } Gpdasm::~Gpdasm() { } void Gpdasm::processInput( ProcessOptions options ) { resetLanguageProcess(); m_asmOutput = ""; m_processOptions = options;; *m_languageProcess << ("gpdasm"); *m_languageProcess << ("--processor"); *m_languageProcess << ( options.m_picID ); *m_languageProcess << ( options.inputFiles().first() ); if ( !start() ) { KMessageBox::sorry( LanguageManager::self()->logView(), i18n("Disassembly failed. Please check you have gputils installed.") ); processInitFailed(); return; } } void Gpdasm::outputtedMessage( const QString &message ) { m_asmOutput += message + "\n"; } bool Gpdasm::processExited( bool successfully ) { if (!successfully) return false; QFile file(m_processOptions.intermediaryOutput()); if ( file.open(QIODevice::WriteOnly) == false ) return false; QTextStream stream(&file); stream << m_asmOutput; file.close(); return true; } bool Gpdasm::isError( const QString &message ) const { return (message.indexOf( "error", -1, Qt::CaseInsensitive ) != -1); } bool Gpdasm::isWarning( const QString &message ) const { return (message.indexOf( "warning", -1, Qt::CaseInsensitive ) != -1); } MessageInfo Gpdasm::extractMessageInfo( const QString &text ) { if ( text.length()<5 || !text.startsWith("/") ) return MessageInfo(); const int index = text.indexOf( ".asm", 0, Qt::CaseInsensitive )+4; if ( index == -1+4 ) return MessageInfo(); const QString fileName = text.left(index); // Extra line number const QString message = text.right(text.length()-index); const int linePos = message.indexOf( QRegExp(":[\\d]+") ); int line = -1; if ( linePos != -1 ) { const int linePosEnd = message.indexOf( ':', linePos+1 ); if ( linePosEnd != -1 ) { const QString number = message.mid( linePos+1, linePosEnd-linePos-1 ).trimmed(); bool ok; line = number.toInt(&ok)-1; if (!ok) line = -1; } } return MessageInfo( fileName, line ); } ProcessOptions::ProcessPath::Path Gpdasm::outputPath( ProcessOptions::ProcessPath::Path inputPath ) const { switch (inputPath) { case ProcessOptions::ProcessPath::Object_Disassembly: case ProcessOptions::ProcessPath::Program_Disassembly: return ProcessOptions::ProcessPath::None; case ProcessOptions::ProcessPath::AssemblyAbsolute_PIC: case ProcessOptions::ProcessPath::AssemblyAbsolute_Program: case ProcessOptions::ProcessPath::AssemblyRelocatable_Library: case ProcessOptions::ProcessPath::AssemblyRelocatable_Object: case ProcessOptions::ProcessPath::AssemblyRelocatable_PIC: case ProcessOptions::ProcessPath::AssemblyRelocatable_Program: case ProcessOptions::ProcessPath::C_AssemblyRelocatable: case ProcessOptions::ProcessPath::C_Library: case ProcessOptions::ProcessPath::C_Object: case ProcessOptions::ProcessPath::C_PIC: case ProcessOptions::ProcessPath::C_Program: case ProcessOptions::ProcessPath::FlowCode_AssemblyAbsolute: case ProcessOptions::ProcessPath::FlowCode_Microbe: case ProcessOptions::ProcessPath::FlowCode_PIC: case ProcessOptions::ProcessPath::FlowCode_Program: case ProcessOptions::ProcessPath::Microbe_AssemblyAbsolute: case ProcessOptions::ProcessPath::Microbe_PIC: case ProcessOptions::ProcessPath::Microbe_Program: case ProcessOptions::ProcessPath::Object_Library: case ProcessOptions::ProcessPath::Object_PIC: case ProcessOptions::ProcessPath::Object_Program: case ProcessOptions::ProcessPath::PIC_AssemblyAbsolute: case ProcessOptions::ProcessPath::Program_PIC: case ProcessOptions::ProcessPath::Invalid: case ProcessOptions::ProcessPath::None: return ProcessOptions::ProcessPath::Invalid; } return ProcessOptions::ProcessPath::Invalid; } diff --git a/src/languages/gplink.cpp b/src/languages/gplink.cpp index 51291de3..22912279 100644 --- a/src/languages/gplink.cpp +++ b/src/languages/gplink.cpp @@ -1,255 +1,255 @@ /*************************************************************************** * Copyright (C) 2005 by David Saxton * * david@bluehaze.org * * * * 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 "gplink.h" #include "languagemanager.h" #include "logview.h" #include "microinfo.h" #include "microlibrary.h" #include #include #include -#include -#include -#include +#include +#include +#include #include Gplink::Gplink( ProcessChain *processChain ) : ExternalLanguage( processChain, "Gpasm" ) { m_successfulMessage = i18n("*** Linking successful ***"); m_failedMessage = i18n("*** Linking failed ***"); // search for SDCC m_sdccLibDir = ""; #define SEARCH_FOR_SDCC(dir) \ { \ if ( m_sdccLibDir.isEmpty() ) { \ QFile f(dir); \ qDebug() << Q_FUNC_INFO << " SDCC lib testing " << dir ; \ if( f.exists() ) { \ qDebug() << Q_FUNC_INFO << " SDCC lib found " << dir ; \ m_sdccLibDir = dir; \ } \ } \ } // consider adding more paths here, if necessary if (!KTLConfig::sDCC_install_prefix().isEmpty()) { SEARCH_FOR_SDCC( KTLConfig::sDCC_install_prefix().append("/share/sdcc/lib") ); } SEARCH_FOR_SDCC( "/usr/local/share/sdcc/lib" ) SEARCH_FOR_SDCC( "/usr/share/sdcc/lib" ) SEARCH_FOR_SDCC( "/usr/sdcc/lib" ) SEARCH_FOR_SDCC( "/usr/sdcc/share/lib" ) SEARCH_FOR_SDCC( "/opt/sdcc/lib" ) SEARCH_FOR_SDCC( "/opt/sdcc/share/lib" ) SEARCH_FOR_SDCC( QDir::homePath() + "/share/sdcc/lib" ); #undef SEARCH_FOR_SDCC if( m_sdccLibDir == "") { qWarning() << Q_FUNC_INFO << "SDCC lib not found. Expect linking errors"; } { QFile f( m_sdccLibDir + "/../non-free/lib" ); if ( !f.exists() ) { qWarning() << Q_FUNC_INFO << "SDCC non-free lib not found. Expect linking errors"; } } } Gplink::~Gplink() { } void Gplink::processInput( ProcessOptions options ) { resetLanguageProcess(); m_processOptions = options; *m_languageProcess << ("gplink"); // note: needed for newer GPUtils: relocate to shared memory if necessary *m_languageProcess << ("--use-shared"); if ( !options.m_hexFormat.isEmpty() ) { *m_languageProcess << ("--hex-format"); *m_languageProcess << (options.m_hexFormat); } if ( options.m_bOutputMapFile ) *m_languageProcess << ("--map"); if ( !options.m_libraryDir.isEmpty() ) { *m_languageProcess << ("--include"); *m_languageProcess << ( options.m_libraryDir ); } if ( !options.m_linkerScript.isEmpty() ) { *m_languageProcess << ("--script"); *m_languageProcess << ( options.m_linkerScript ); } if ( !options.m_linkOther.isEmpty() ) *m_languageProcess << (options.m_linkOther); // Output hex file *m_languageProcess << ("--output"); *m_languageProcess << ( options.intermediaryOutput() ); // Input object file const QStringList inputFiles = options.inputFiles(); QStringList::const_iterator end = inputFiles.end(); for ( QStringList::const_iterator it = inputFiles.begin(); it != end; ++it ) *m_languageProcess << ( *it ); // Other libraries end = options.m_linkLibraries.end(); for ( QStringList::const_iterator it = options.m_linkLibraries.begin(); it != end; ++it ) *m_languageProcess << ( *it ); // if selected to automatically link to SDCC libraries, add some options. if( KTLConfig::gplink_link_shared() ) { // set up the include directory MicroInfo * info = MicroLibrary::self()->microInfoWithID( options.m_picID ); if ( ! info ) { // be program won't link anyway, but the user can't say that the program didn't try qCritical() << Q_FUNC_INFO << "Couldn't find the requested PIC" << options.m_picID << endl; qWarning() << Q_FUNC_INFO << "Supposing that the pic is pic12 or pic14" << endl; *m_languageProcess << "-I" << m_sdccLibDir + "/pic" ; } else { QString picLibSubdir; switch ( info->instructionSet()->set() ) { // note: is PIC12 supported by SDCC? case AsmInfo::PIC12: picLibSubdir = "pic12"; break; case AsmInfo::PIC14: picLibSubdir = "pic14"; break; case AsmInfo::PIC16: picLibSubdir = "pic16"; break; default: qWarning() << Q_FUNC_INFO << "Inexpected instruction set: " << (int) info->instructionSet()->set(); break; } *m_languageProcess << "-I" << m_sdccLibDir + "/" + picLibSubdir ; // non-free libraries are needed; beware the Debian/Ubuntu/derivatives' packages *m_languageProcess << "-I" << m_sdccLibDir + "/../non-free/lib/" + picLibSubdir ; // to pic to link against *m_languageProcess << options.m_picID.toLower().replace ( "p","pic" ) + ".lib"; } *m_languageProcess << "libsdcc.lib"; } if ( !start() ) { KMessageBox::sorry( LanguageManager::self()->logView(), i18n("Linking failed. Please check you have gputils installed.") ); processInitFailed(); return; } } bool Gplink::isError( const QString &message ) const { return message.contains( "Error", Qt::CaseInsensitive ); } bool Gplink::isWarning( const QString &message ) const { return message.contains( "Warning", Qt::CaseInsensitive ); } MessageInfo Gplink::extractMessageInfo( const QString &text ) { if ( text.length()<5 || !text.startsWith("/") ) return MessageInfo(); #if 0 const int index = text.indexOf( ".asm", 0, Qt::CaseInsensitive )+4; if ( index == -1+4 ) return MessageInfo(); const QString fileName = text.left(index); // Extra line number const QString message = text.right(text.length()-index); const int linePos = message.indexOf( QRegExp(":[\\d]+") ); int line = -1; if ( linePos != -1 ) { const int linePosEnd = message.indexOf( ':', linePos+1 ); if ( linePosEnd != -1 ) { const QString number = message.mid( linePos+1, linePosEnd-linePos-1 ).trimmed(); bool ok; line = number.toInt(&ok)-1; if (!ok) line = -1; } } return MessageInfo( fileName, line ); #endif return MessageInfo(); } ProcessOptions::ProcessPath::Path Gplink::outputPath( ProcessOptions::ProcessPath::Path inputPath ) const { switch (inputPath) { case ProcessOptions::ProcessPath::Object_PIC: return ProcessOptions::ProcessPath::Program_PIC; case ProcessOptions::ProcessPath::Object_Program: return ProcessOptions::ProcessPath::None; case ProcessOptions::ProcessPath::AssemblyAbsolute_PIC: case ProcessOptions::ProcessPath::AssemblyAbsolute_Program: case ProcessOptions::ProcessPath::AssemblyRelocatable_Library: case ProcessOptions::ProcessPath::AssemblyRelocatable_Object: case ProcessOptions::ProcessPath::AssemblyRelocatable_PIC: case ProcessOptions::ProcessPath::AssemblyRelocatable_Program: case ProcessOptions::ProcessPath::C_AssemblyRelocatable: case ProcessOptions::ProcessPath::C_Library: case ProcessOptions::ProcessPath::C_Object: case ProcessOptions::ProcessPath::C_PIC: case ProcessOptions::ProcessPath::C_Program: case ProcessOptions::ProcessPath::FlowCode_AssemblyAbsolute: case ProcessOptions::ProcessPath::FlowCode_Microbe: case ProcessOptions::ProcessPath::FlowCode_PIC: case ProcessOptions::ProcessPath::FlowCode_Program: case ProcessOptions::ProcessPath::Microbe_AssemblyAbsolute: case ProcessOptions::ProcessPath::Microbe_PIC: case ProcessOptions::ProcessPath::Microbe_Program: case ProcessOptions::ProcessPath::Object_Disassembly: case ProcessOptions::ProcessPath::Object_Library: case ProcessOptions::ProcessPath::PIC_AssemblyAbsolute: case ProcessOptions::ProcessPath::Program_Disassembly: case ProcessOptions::ProcessPath::Program_PIC: case ProcessOptions::ProcessPath::Invalid: case ProcessOptions::ProcessPath::None: return ProcessOptions::ProcessPath::Invalid; } return ProcessOptions::ProcessPath::Invalid; } diff --git a/src/languages/language.cpp b/src/languages/language.cpp index 271e57f8..0e0d7f62 100644 --- a/src/languages/language.cpp +++ b/src/languages/language.cpp @@ -1,548 +1,548 @@ /*************************************************************************** * Copyright (C) 2005 by David Saxton * * david@bluehaze.org * * * * 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 "asmparser.h" #include "ktechlab.h" #include "logview.h" #include "language.h" #include "outputmethoddlg.h" #include "processchain.h" #include "projectmanager.h" #include "languagemanager.h" -#include //#include #include #include -#include -#include +#include +#include +#include #include //BEGIN class Language Language::Language( ProcessChain *processChain, const QString &name ) : QObject( KTechlab::self() /*, name */ ) { setObjectName(name); p_processChain = processChain; } Language::~Language() { } void Language::outputMessage( const QString &message ) { LanguageManager::self()->slotMessage( message, extractMessageInfo(message) ); } void Language::outputWarning( const QString &message ) { LanguageManager::self()->slotWarning( message, extractMessageInfo(message) ); } void Language::outputError( const QString &message ) { LanguageManager::self()->slotError( message, extractMessageInfo(message) ); m_errorCount++; } void Language::finish( bool successful ) { if (successful) { outputMessage(m_successfulMessage + "\n"); KTechlab::self()->slotChangeStatusbar(m_successfulMessage); ProcessOptions::ProcessPath::Path newPath = outputPath( m_processOptions.processPath() ); if ( newPath == ProcessOptions::ProcessPath::None ) emit processSucceeded(this); else if (p_processChain) { m_processOptions.setInputFiles( QStringList( m_processOptions.intermediaryOutput() ) ); m_processOptions.setIntermediaryOutput( m_processOptions.targetFile() ); m_processOptions.setProcessPath(newPath); // p_processChain->compile(m_processOptions); p_processChain->setProcessOptions(m_processOptions); p_processChain->compile(); } } else { outputError(m_failedMessage + "\n"); KTechlab::self()->slotChangeStatusbar(m_failedMessage); emit processFailed(this); return; } } void Language::reset() { m_errorCount = 0; } MessageInfo Language::extractMessageInfo( const QString &text ) { if ( !text.startsWith("/") ) return MessageInfo(); const int index = text.indexOf( ":", 0, Qt::CaseInsensitive ); if ( index == -1 ) return MessageInfo(); const QString fileName = text.left(index); // Extra line number const QString message = text.right(text.length()-index); const int linePos = message.indexOf( QRegExp(":[\\d]+") ); int line = -1; if ( linePos != -1 ) { const int linePosEnd = message.indexOf( ':', linePos+1 ); if ( linePosEnd != -1 ) { const QString number = message.mid( linePos+1, linePosEnd-linePos-1 ).trimmed(); bool ok; line = number.toInt(&ok)-1; if (!ok) line = -1; } } return MessageInfo( fileName, line ); } //END class Language //BEGIN class ProcessOptionsSpecial ProcessOptionsSpecial::ProcessOptionsSpecial() { m_bOutputMapFile = true; b_forceList = true; b_addToProject = ProjectManager::self()->currentProject(); p_flowCodeDocument = nullptr; switch ( KTLConfig::hexFormat() ) { case KTLConfig::EnumHexFormat::inhx8m: m_hexFormat = "inhx8m"; break; case KTLConfig::EnumHexFormat::inhx8s: m_hexFormat = "inhx8s"; break; case KTLConfig::EnumHexFormat::inhx16: m_hexFormat = "inhx16"; break; case KTLConfig::EnumHexFormat::inhx32: default: m_hexFormat = "inhx32"; break; } } //END class ProcessOptionsSpecial //BEGIN class ProcessOptions ProcessOptions::ProcessOptions() { m_pHelper = new ProcessOptionsHelper; b_targetFileSet = false; m_pTextOutputTarget = nullptr; } ProcessOptions::ProcessOptions( OutputMethodInfo info ) { m_pHelper = new ProcessOptionsHelper; b_addToProject = info.addToProject(); m_picID = info.picID(); b_targetFileSet = false; setTargetFile( info.outputFile().toLocalFile() ); switch ( info.method() ) { case OutputMethodInfo::Method::Direct: m_method = Method::LoadAsNew; break; case OutputMethodInfo::Method::SaveAndForget: m_method = Method::Forget; break; case OutputMethodInfo::Method::SaveAndLoad: m_method = Method::Load; break; } } void ProcessOptions::setTextOutputTarget( TextDocument * target, QObject * receiver, const char * slot ) { m_pTextOutputTarget = target; QObject::connect( m_pHelper, SIGNAL(textOutputtedTo( TextDocument* )), receiver, slot ); } void ProcessOptions::setTextOutputtedTo( TextDocument * outputtedTo ) { m_pTextOutputTarget = outputtedTo; emit m_pHelper->textOutputtedTo( m_pTextOutputTarget ); } void ProcessOptions::setTargetFile( const QString &file ) { if (b_targetFileSet) { qWarning() << "Trying to reset target file!"< -#include +#include +#include class FlowCodeDocument; class KTechlab; class LogView; class MessageInfo; class MicroSettings; class OutputMethodInfo; class ProcessChain; class ProcessOptions; class TextDocument; class QProcess; typedef QList ProcessOptionsList; class ProcessOptionsSpecial { public: ProcessOptionsSpecial(); bool b_addToProject; bool b_forceList; QString m_picID; FlowCodeDocument * p_flowCodeDocument; // Linking QString m_hexFormat; bool m_bOutputMapFile; QString m_libraryDir; QString m_linkerScript; QStringList m_linkLibraries; QString m_linkOther; // Programming QString m_port; QString m_program; }; class ProcessOptionsHelper : public QObject { Q_OBJECT #define protected public signals: #undef protected void textOutputtedTo( TextDocument * outputtedTo ); }; class ProcessOptions : public ProcessOptionsSpecial { public: ProcessOptions(); ProcessOptions( OutputMethodInfo info ); class ProcessPath { public: enum MediaType { AssemblyAbsolute, AssemblyRelocatable, C, Disassembly, FlowCode, Library, Microbe, Object, Pic, Program, Unknown // Used for guessing the media type }; enum Path // From_To // processor that will be invoked first { AssemblyAbsolute_PIC, // gpasm (indirect) AssemblyAbsolute_Program, // gpasm (direct) AssemblyRelocatable_Library, // gpasm (indirect) AssemblyRelocatable_Object, // gpasm (direct) AssemblyRelocatable_PIC, // gpasm (indirect) AssemblyRelocatable_Program, // gpasm (indirect) C_AssemblyRelocatable, // sdcc (direct) C_Library, // sdcc (indirect) C_Object, // sdcc (indirect) C_PIC, // sdcc (indirect) C_Program, // sdcc (indirect) FlowCode_AssemblyAbsolute, // flowcode (indirect) FlowCode_Microbe, // flowcode (direct) FlowCode_PIC, // flowcode (indirect) FlowCode_Program, // flowcode (indirect) Microbe_AssemblyAbsolute, // microbe (direct) Microbe_PIC, // microbe (indirect) Microbe_Program, // microbe (indirect) Object_Disassembly, // gpdasm (direct) Object_Library, // gplib (direct) Object_PIC, // gplink (indirect) Object_Program, // gplink (direct) PIC_AssemblyAbsolute, // download from pic (direct) Program_Disassembly, // gpdasm (direct) Program_PIC, // upload to pic (direct) Invalid, // From and to types are incompatible None // From and to types are the same }; static Path path( MediaType from, MediaType to ); static MediaType from( Path path ); static MediaType to( Path path ); }; class Method { public: enum type { Forget, // Don't do anything after processing successfully LoadAsNew, // Load the output as a new file Load // Load the output file }; }; /** * Tries to guess the media type from the url (and possible the contents * of the file as well). * @param url a path to a file in the local filesystem */ static ProcessPath::MediaType guessMediaType( const QString & url ); /** * The *final* target file (not any intermediatary ones) */ QString targetFile() const { return m_targetFile; } /** * This sets the final target file, as well as the initial intermediatary one */ void setTargetFile( const QString &file ); void setIntermediaryOutput( const QString &file ) { m_intermediaryFile = file; } QString intermediaryOutput() const { return m_intermediaryFile; } /** * As paths in local filesystem */ void setInputFiles( const QStringList & files ) { m_inputFiles = files; } QStringList inputFiles() const { return m_inputFiles; } void setMethod( Method::type method ) { m_method = method; } Method::type method() const { return m_method; } void setProcessPath( ProcessPath::Path path ) { m_processPath = path; } ProcessPath::Path processPath() const { return m_processPath; } /** * If the output is text; If the user has selected (in config options) * ReuseSameViewForOutput, then the given TextDocument will have its * text set to the output if the TextDocument is not modified and has * an empty url. Otherwise a new TextDocument will be created. Either * way, once the processing has finished, a signal will be emitted * to the given receiver passing a TextDocument * as an argument. This * is not to be confused with setTextOutputtedTo, which is called once * the processing has finished, and will call-back to the slot given. */ void setTextOutputTarget( TextDocument * target, QObject * receiver, const char * slot ); /** * @see setTextOutputTarget */ TextDocument * textOutputTarget() const { return m_pTextOutputTarget; } /** * @see setTextOuputTarget */ void setTextOutputtedTo( TextDocument * outputtedTo ); protected: TextDocument * m_pTextOutputTarget; ProcessOptionsHelper * m_pHelper; bool b_targetFileSet; QStringList m_inputFiles; QString m_targetFile; QString m_intermediaryFile; Method::type m_method; ProcessPath::Path m_processPath; }; /** @author Daniel Clarke @author David Saxton */ class Language : public QObject { Q_OBJECT public: Language( ProcessChain *processChain, const QString &name ); ~Language() override; /** * Compile / assemble / dissassembly / whatever the given input. */ virtual void processInput( ProcessOptions options ) = 0; /** * Return the ProcessOptions object current state */ ProcessOptions processOptions() const { return m_processOptions; } /** * Return the output path from the given input path. Will return None * if we've done processing. */ virtual ProcessOptions::ProcessPath::Path outputPath( ProcessOptions::ProcessPath::Path inputPath ) const = 0; signals: /** * Emitted when the processing was successful. * @param language Pointer to this class */ void processSucceeded( Language *language ); /** * Emitted when the processing failed. * @param language Pointer to this class */ void processFailed( Language *language ); protected: /** * Examines the string for the line number if applicable, and creates a new * MessageInfo for it. */ virtual MessageInfo extractMessageInfo( const QString &text ); /** * Reset the error count */ void reset(); void outputMessage( const QString &message ); void outputWarning( const QString &message ); void outputError( const QString &error ); void finish( bool successful ); int m_errorCount; ProcessOptions m_processOptions; ProcessChain *p_processChain; /** * A message appropriate to the language's success after compilation or similar. */ QString m_successfulMessage; /** * A message appropriate to the language's failure after compilation or similar. */ QString m_failedMessage; }; #endif diff --git a/src/languages/languagemanager.h b/src/languages/languagemanager.h index 988b7603..0d4498bc 100644 --- a/src/languages/languagemanager.h +++ b/src/languages/languagemanager.h @@ -1,90 +1,90 @@ /*************************************************************************** * Copyright (C) 2005 by David Saxton * * david@bluehaze.org * * * * 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. * ***************************************************************************/ #ifndef LANGUAGEMANAGER_H #define LANGUAGEMANAGER_H -#include -#include +#include +#include #include "language.h" #include "logview.h" class FlowCode; class Gpasm; class Gpdasm; class KTechlab; class Language; class LanguageManager; class MessageInfo; class Microbe; class ProcessChain; class ProcessListChain; class ProcessOptions; namespace KateMDI { class ToolView; } /** @author David Saxton */ class LanguageManager : public QObject { Q_OBJECT public: static LanguageManager * self( KateMDI::ToolView * parent = nullptr ); static QString toolViewIdentifier() { return "LanguageManager"; } ~LanguageManager() override; /** * Call to compile a file of one type all the way to another type, this can * also be used in reverse to disassemble code. Connect to the returned * ProcessChain for notification of compile success / failure * @return Pointer to the ProcessChain used to compile */ ProcessChain * compile( ProcessOptions options ); ProcessListChain * compile( ProcessOptionsList pol ); /** * @return Pointer to the LogView that displays the output messages */ LogView * logView() const { return m_logView; } /** * Clear any errors and clear the log view */ void reset(); public slots: /** * Called when the user clicks on any text in the LogView */ void slotParaClicked( const QString& message, MessageInfo messageInfo ); /** * Called by languages to report an error message * @param error Error message to report */ void slotError( const QString &error, MessageInfo messageInfo ); /** * Called by languages to report a warning message * @param warning Warning message to report */ void slotWarning( const QString &warning, MessageInfo messageInfo ); /** * Called by languages to report a general message * @param message General message to report */ void slotMessage( const QString &message, MessageInfo messageInfo ); protected: LanguageManager( KateMDI::ToolView * parent ); private: LogView * m_logView; static LanguageManager * m_pSelf; }; #endif diff --git a/src/languages/microbe.cpp b/src/languages/microbe.cpp index 73bfacad..1fde1b5c 100644 --- a/src/languages/microbe.cpp +++ b/src/languages/microbe.cpp @@ -1,137 +1,137 @@ /*************************************************************************** * Copyright (C) 2005 by David Saxton * * david@bluehaze.org * * * * 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 "contexthelp.h" #include "docmanager.h" #include "logview.h" #include "microbe.h" #include "languagemanager.h" -#include #include #include - -#include #include +#include +#include + Microbe::Microbe( ProcessChain *processChain ) : ExternalLanguage( processChain, "Microbe" ) { m_failedMessage = i18n("*** Compilation failed ***"); m_successfulMessage = i18n("*** Compilation successful ***"); #if 0 // Setup error messages list QFile file( locate("appdata",i1 8n("error_messages_en_gb")) ); if ( file.open( QIODevice::ReadOnly ) ) { QTextStream stream( &file ); QString line; while ( !stream.atEnd() ) { line = stream.readLine(); // line of text excluding '\n' if ( !line.isEmpty() ) { bool ok; const int pos = line.left( line.indexOf("#") ).toInt(&ok); if (ok) { m_errorMessages[pos] = line.right(line.length()-line.indexOf("#")); } else { qCritical() << Q_FUNC_INFO << "Error parsing Microbe error-message file"<logView(), i18n("Assembly failed. Please check you have KTechlab installed properly (\"microbe\" could not be started).") ); processInitFailed(); return; } } bool Microbe::isError( const QString &message ) const { return message.contains( "Error", Qt::CaseInsensitive ); } bool Microbe::isWarning( const QString &message ) const { return message.contains( "Warning", Qt::CaseInsensitive ); } ProcessOptions::ProcessPath::Path Microbe::outputPath( ProcessOptions::ProcessPath::Path inputPath ) const { switch (inputPath) { case ProcessOptions::ProcessPath::Microbe_AssemblyAbsolute: return ProcessOptions::ProcessPath::None; case ProcessOptions::ProcessPath::Microbe_PIC: return ProcessOptions::ProcessPath::AssemblyAbsolute_PIC; case ProcessOptions::ProcessPath::Microbe_Program: return ProcessOptions::ProcessPath::AssemblyAbsolute_Program; case ProcessOptions::ProcessPath::AssemblyAbsolute_PIC: case ProcessOptions::ProcessPath::AssemblyAbsolute_Program: case ProcessOptions::ProcessPath::AssemblyRelocatable_Library: case ProcessOptions::ProcessPath::AssemblyRelocatable_Object: case ProcessOptions::ProcessPath::AssemblyRelocatable_PIC: case ProcessOptions::ProcessPath::AssemblyRelocatable_Program: case ProcessOptions::ProcessPath::C_AssemblyRelocatable: case ProcessOptions::ProcessPath::C_Library: case ProcessOptions::ProcessPath::C_Object: case ProcessOptions::ProcessPath::C_PIC: case ProcessOptions::ProcessPath::C_Program: case ProcessOptions::ProcessPath::FlowCode_AssemblyAbsolute: case ProcessOptions::ProcessPath::FlowCode_Microbe: case ProcessOptions::ProcessPath::FlowCode_PIC: case ProcessOptions::ProcessPath::FlowCode_Program: case ProcessOptions::ProcessPath::Object_Disassembly: case ProcessOptions::ProcessPath::Object_Library: case ProcessOptions::ProcessPath::Object_PIC: case ProcessOptions::ProcessPath::Object_Program: case ProcessOptions::ProcessPath::PIC_AssemblyAbsolute: case ProcessOptions::ProcessPath::Program_Disassembly: case ProcessOptions::ProcessPath::Program_PIC: case ProcessOptions::ProcessPath::Invalid: case ProcessOptions::ProcessPath::None: return ProcessOptions::ProcessPath::Invalid; } return ProcessOptions::ProcessPath::Invalid; } diff --git a/src/languages/microbe.h b/src/languages/microbe.h index 45ed4e4b..3a0ca460 100644 --- a/src/languages/microbe.h +++ b/src/languages/microbe.h @@ -1,41 +1,41 @@ /*************************************************************************** * Copyright (C) 2005 by David Saxton * * david@bluehaze.org * * * * 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. * ***************************************************************************/ #ifndef MICROBE_H #define MICROBE_H #include "externallanguage.h" -#include +#include typedef QMap< int, QString > ErrorMap; /** @author Daniel Clarke @author David Saxton */ class Microbe : public ExternalLanguage { public: Microbe( ProcessChain *processChain ); ~Microbe() override; void processInput( ProcessOptions options ) override; ProcessOptions::ProcessPath::Path outputPath( ProcessOptions::ProcessPath::Path inputPath ) const override; protected: bool isError( const QString &message ) const override; bool isWarning( const QString &message ) const override; ErrorMap m_errorMessages; }; #endif diff --git a/src/languages/picprogrammer.cpp b/src/languages/picprogrammer.cpp index e6e1254c..05f008f5 100644 --- a/src/languages/picprogrammer.cpp +++ b/src/languages/picprogrammer.cpp @@ -1,464 +1,463 @@ /*************************************************************************** * Copyright (C) 2005 by David Saxton * * david@bluehaze.org * * * * 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 "languagemanager.h" #include "picprogrammer.h" #include "logview.h" #include #include #include #include #include -#include -#include -#include -#include -#include -#include +#include +#include +#include +#include +#include #include #include //BEGIN class ProgrammerConfig ProgrammerConfig::ProgrammerConfig() { } void ProgrammerConfig::reset() { initCommand = QString::null; readCommand = QString::null; writeCommand = QString::null; verifyCommand = QString::null; blankCheckCommand = QString::null; eraseCommand = QString::null; } //END class ProgrammerConfig //BEGIN class PicProgrammerSettings bool PicProgrammerSettings::m_bDoneStaticConfigsInit = false; ProgrammerConfigMap PicProgrammerSettings::m_staticConfigs = ProgrammerConfigMap(); PicProgrammerSettings::PicProgrammerSettings() { if ( !m_bDoneStaticConfigsInit ) initStaticConfigs(); } void PicProgrammerSettings::initStaticConfigs() { m_bDoneStaticConfigsInit = true; ProgrammerConfig config; config.description = i18n("Supported programmers: %1", QString("JuPic, PICStart Plus, Warp-13")); config.description += i18n("
Interface: Serial Port"); config.initCommand = ""; config.readCommand = "picp %port %device -rp %file"; config.writeCommand = "picp %port %device -wp %file"; config.verifyCommand = ""; config.blankCheckCommand = "picp %port %device -b"; config.eraseCommand = "picp %port %device -e"; // config.executable = "picp"; m_staticConfigs[ "PICP" ] = config; config.description = i18n("Supported programmers: %1", QString("Epic Plus")); config.description += i18n("
Interface: Parallel Port"); config.initCommand = "odyssey init"; config.readCommand = "odyssey %device read %file"; config.writeCommand = "odyssey %device write %file"; config.verifyCommand = "odyssey %device verify %file"; config.blankCheckCommand = "odyssey %device blankcheck"; config.eraseCommand = "odyssey %device erase"; // config.executable = "odyssey"; m_staticConfigs[ "Odyssey" ] = config; config.description = i18n("Supported programmers: %1", QString("JDM PIC-Programmer 2, PIC-PG2C")); config.description += i18n("
Interface: Serial Port"); config.initCommand = ""; config.readCommand = "picprog --output %file --pic %port"; config.writeCommand = "picprog --burn --input %file --pic %port --device %device"; config.verifyCommand = ""; config.blankCheckCommand = ""; config.eraseCommand = "picprog --erase --pic %device"; m_staticConfigs[ "PICProg" ] = config; config.description = i18n("Supported programmers: %1", QString("Epic Plus")); config.description += i18n("
Interface: Parallel Port"); config.initCommand = ""; config.readCommand = "dump84 --dump-all --output=%file"; config.writeCommand = "prog84 --intel16=%file"; config.verifyCommand = ""; config.blankCheckCommand = ""; config.eraseCommand = "prog84 --clear"; m_staticConfigs[ "prog84" ] = config; config.description = i18n("Supported programmers: %1", QString("Kit 149, Kit 150")); config.description += i18n("
Interface: USB Port"); config.initCommand = ""; config.readCommand = "pp -d %device -r %file"; config.writeCommand = "pp -d %device -w %file"; config.verifyCommand = "pp -d %device -v %file"; config.blankCheckCommand = ""; config.eraseCommand = "pp -d %device -e"; m_staticConfigs[ "PP" ] = config; config.description = i18n("Supported programmers: %1", QString("Wisp628")); config.description += i18n("
Interface: Serial Port"); config.initCommand = ""; config.readCommand = "xwisp ID %device PORT %device DUMP"; config.writeCommand = "xwisp ID %device PORT %device WRITE %file"; config.verifyCommand = ""; config.blankCheckCommand = ""; config.eraseCommand = "xwisp ID %device PORT %device ERASE"; m_staticConfigs[ "XWisp" ] = config; #if 0 config.description = i18n("Supported programmers: %1", QString("Epic Plus, JDM PIC-Programmer 2, PICCOLO, PICCOLO Grande, Trivial HVP Programmer")); config.description += i18n("
Interface: Serial Port and Parallel Port"); config.initCommand = ""; config.readCommand = ""; config.writeCommand = ""; config.verifyCommand = ""; config.blankCheckCommand = ""; config.eraseCommand = ""; config.executable = "pkp"; m_staticConfigs[ "PiKdev" ] = config; config.executable = ""; config.description = i18n("Supported programmers: %1", QString("Trivial LVP programmer, Trivial HVP Programmer")); config.description += i18n("
Interface: Parallel Port"); config.initCommand = ""; config.readCommand = ""; config.writeCommand = ""; config.verifyCommand = ""; config.blankCheckCommand = ""; config.eraseCommand = ""; m_staticConfigs[ "PicPrg2" ] = config; config.description = i18n("Supported programmers: %1", QString("El Cheapo")); config.description += i18n("
Interface: Parallel Port"); config.initCommand = ""; config.readCommand = ""; config.writeCommand = ""; config.verifyCommand = ""; config.blankCheckCommand = ""; config.eraseCommand = ""; m_staticConfigs[ "PP06" ] = config; config.description = i18n("Supported programmers: %1", QString("NOPPP")); config.description += i18n("
Interface: Parallel Port"); config.initCommand = ""; config.readCommand = ""; config.writeCommand = ""; config.verifyCommand = ""; config.blankCheckCommand = ""; config.eraseCommand = ""; m_staticConfigs[ "NOPPP" ] = config; config.description = i18n("Supported programmers: %1", QString("SNOPPP")); config.description += i18n("
Interface: Parallel Port"); config.initCommand = ""; config.readCommand = ""; config.writeCommand = ""; config.verifyCommand = ""; config.blankCheckCommand = ""; config.eraseCommand = ""; m_staticConfigs[ "SNOPPP" ] = config; #endif } void PicProgrammerSettings::load( KConfig * config ) { QStringList oldCustomProgrammers = config->groupList().filter("CustomProgrammer_"); QStringList::iterator ocpEnd = oldCustomProgrammers.end(); for ( QStringList::iterator it = oldCustomProgrammers.begin(); it != ocpEnd; ++it ) { // The CustomProgrammer_ string we searched for might appear half way through... (don't want) if ( (*it).startsWith("CustomProgrammer_") ) { //config->setGroup(*it); KConfigGroup grProg = config->group(*it); ProgrammerConfig pc; pc.initCommand = grProg.readEntry( "InitCommand" ); pc.readCommand = grProg.readEntry( "ReadCommand" ); pc.writeCommand = grProg.readEntry( "WriteCommand" ); pc.verifyCommand = grProg.readEntry( "VerifyCommand" ); pc.blankCheckCommand = grProg.readEntry( "BlankCheckCommand" ); pc.eraseCommand = grProg.readEntry( "EraseCommand" ); QString name = grProg.readEntry( "Name" ); m_customConfigs[name] = pc; } } } void PicProgrammerSettings::save( KConfig * config ) { QStringList oldCustomProgrammers = config->groupList().filter("CustomProgrammer_"); QStringList::iterator ocpEnd = oldCustomProgrammers.end(); for ( QStringList::iterator it = oldCustomProgrammers.begin(); it != ocpEnd; ++it ) { // The CustomProgrammer_ string we searched for might appear half way through... (don't want) if ( (*it).startsWith("CustomProgrammer_") ) config->deleteGroup(*it); } int at = 0; ProgrammerConfigMap::iterator end = m_customConfigs.end(); for ( ProgrammerConfigMap::iterator it = m_customConfigs.begin(); it != end; ++it ) { //config->setGroup( QString("CustomProgrammer_%1").arg(at++) ); QString grName = QString("CustomProgrammer_%1").arg(at++); KConfigGroup gr = config->group(grName); gr.writeEntry( "Name", it.key() ); gr.writeEntry( "InitCommand", it.value().initCommand ); gr.writeEntry( "ReadCommand", it.value().readCommand ); gr.writeEntry( "WriteCommand", it.value().writeCommand ); gr.writeEntry( "VerifyCommand", it.value().verifyCommand ); gr.writeEntry( "BlankCheckCommand", it.value().blankCheckCommand ); gr.writeEntry( "EraseCommand", it.value().eraseCommand ); } } ProgrammerConfig PicProgrammerSettings::config( const QString & name ) { if ( name.isEmpty() ) return ProgrammerConfig(); QString l = name.toLower(); ProgrammerConfigMap::const_iterator end = m_customConfigs.end(); for ( ProgrammerConfigMap::const_iterator it = m_customConfigs.begin(); it != end; ++it ) { if ( it.key().toLower() == l ) return *it; } end = m_staticConfigs.end(); for ( ProgrammerConfigMap::const_iterator it = m_staticConfigs.begin(); it != end; ++it ) { if ( it.key().toLower() == l ) return *it; } return m_customConfigs[ name ]; } void PicProgrammerSettings::removeConfig( const QString & name ) { if ( isPredefined( name ) ) { qWarning() << Q_FUNC_INFO << "Cannot remove a predefined PIC programmer configuration." << endl; return; } QString l = name.toLower(); ProgrammerConfigMap::iterator end = m_customConfigs.end(); for ( ProgrammerConfigMap::iterator it = m_customConfigs.begin(); it != end; ++it ) { if ( it.key().toLower() == l ) { m_customConfigs.erase( it ); return; } } } void PicProgrammerSettings::saveConfig( const QString & name, const ProgrammerConfig & config ) { if ( isPredefined( name ) ) { qWarning() << Q_FUNC_INFO << "Cannot save to a predefined PIC programmer configuration." << endl; return; } QString l = name.toLower(); ProgrammerConfigMap::iterator end = m_customConfigs.end(); for ( ProgrammerConfigMap::iterator it = m_customConfigs.begin(); it != end; ++it ) { if ( it.key().toLower() == l ) { *it = config; return; } } m_customConfigs[ name ] = config; } QStringList PicProgrammerSettings::configNames( bool makeLowercase ) const { if ( !makeLowercase ) return m_customConfigs.keys() + m_staticConfigs.keys(); QStringList names; ProgrammerConfigMap::const_iterator end = m_customConfigs.end(); for ( ProgrammerConfigMap::const_iterator it = m_customConfigs.begin(); it != end; ++it ) names << it.key().toLower(); end = m_staticConfigs.end(); for ( ProgrammerConfigMap::const_iterator it = m_staticConfigs.begin(); it != end; ++it ) names << it.key().toLower(); return names; } bool PicProgrammerSettings::isPredefined( const QString & name ) const { QString l = name.toLower(); ProgrammerConfigMap::const_iterator end = m_staticConfigs.end(); for ( ProgrammerConfigMap::const_iterator it = m_staticConfigs.begin(); it != end; ++it ) { if ( it.key().toLower() == l ) return true; } return false; } //END class PicProgrammerSettings //BEGIN class PicProgrammer PicProgrammer::PicProgrammer( ProcessChain *processChain ) : ExternalLanguage( processChain, "PicProgrammer" ) { m_successfulMessage = i18n("*** Programming successful ***"); m_failedMessage = i18n("*** Programming failed ***"); } PicProgrammer::~PicProgrammer() { } void PicProgrammer::processInput( ProcessOptions options ) { resetLanguageProcess(); m_processOptions = options; PicProgrammerSettings settings; //settings.load( kapp->config() ); KSharedConfigPtr cfgPtr = KSharedConfig::openConfig(); settings.load( cfgPtr.data() ); QString program = options.m_program; if ( !settings.configNames( true ).contains( program.toLower() ) ) { qCritical() << Q_FUNC_INFO << "Invalid program" << endl; finish( false ); return; } ProgrammerConfig config = settings.config( program ); QString command = config.writeCommand; command.replace( "%port", options.m_port ); command.replace( "%device", QString( options.m_picID ).remove("P") ); command.replace( "%file", KShell::quoteArg( options.inputFiles().first() ) ); //m_languageProcess->setUseShell( true ); // 2017.10.08 - port to KProcess //*m_languageProcess << command; m_languageProcess->setShellCommand( command ); if ( !start() ) { // KMessageBox::sorry( LanguageManager::self()->logView(), i18n("Could not program PIC.") ); processInitFailed(); return; } } bool PicProgrammer::isError( const QString &message ) const { return message.contains( "Error", Qt::CaseInsensitive ); } bool PicProgrammer::isWarning( const QString &message ) const { return message.contains( "Warning", Qt::CaseInsensitive ); } ProcessOptions::ProcessPath::Path PicProgrammer::outputPath( ProcessOptions::ProcessPath::Path inputPath ) const { switch (inputPath) { case ProcessOptions::ProcessPath::PIC_AssemblyAbsolute: case ProcessOptions::ProcessPath::Program_PIC: return ProcessOptions::ProcessPath::None; case ProcessOptions::ProcessPath::AssemblyAbsolute_PIC: case ProcessOptions::ProcessPath::AssemblyAbsolute_Program: case ProcessOptions::ProcessPath::AssemblyRelocatable_Library: case ProcessOptions::ProcessPath::AssemblyRelocatable_Object: case ProcessOptions::ProcessPath::AssemblyRelocatable_PIC: case ProcessOptions::ProcessPath::AssemblyRelocatable_Program: case ProcessOptions::ProcessPath::C_AssemblyRelocatable: case ProcessOptions::ProcessPath::C_Library: case ProcessOptions::ProcessPath::C_Object: case ProcessOptions::ProcessPath::C_PIC: case ProcessOptions::ProcessPath::C_Program: case ProcessOptions::ProcessPath::FlowCode_AssemblyAbsolute: case ProcessOptions::ProcessPath::FlowCode_Microbe: case ProcessOptions::ProcessPath::FlowCode_PIC: case ProcessOptions::ProcessPath::FlowCode_Program: case ProcessOptions::ProcessPath::Microbe_AssemblyAbsolute: case ProcessOptions::ProcessPath::Microbe_PIC: case ProcessOptions::ProcessPath::Microbe_Program: case ProcessOptions::ProcessPath::Object_Disassembly: case ProcessOptions::ProcessPath::Object_Library: case ProcessOptions::ProcessPath::Object_PIC: case ProcessOptions::ProcessPath::Object_Program: case ProcessOptions::ProcessPath::Program_Disassembly: case ProcessOptions::ProcessPath::Invalid: case ProcessOptions::ProcessPath::None: return ProcessOptions::ProcessPath::Invalid; } return ProcessOptions::ProcessPath::Invalid; } //END class PicProgrammer diff --git a/src/languages/picprogrammer.h b/src/languages/picprogrammer.h index dd13fdbc..38779e79 100644 --- a/src/languages/picprogrammer.h +++ b/src/languages/picprogrammer.h @@ -1,128 +1,128 @@ /*************************************************************************** * Copyright (C) 2005 by David Saxton * * david@bluehaze.org * * * * 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. * ***************************************************************************/ #ifndef PICPROGRAMMER_H #define PICPROGRAMMER_H #include "externallanguage.h" -#include +#include class KConfig; class KProcess; class ProgrammerConfig { public: ProgrammerConfig(); /** * Clears the type and all commands. */ void reset(); QString initCommand; QString readCommand; QString writeCommand; QString verifyCommand; QString blankCheckCommand; QString eraseCommand; QString description; QString executable; // The name of the program executable }; typedef QMap< QString, ProgrammerConfig > ProgrammerConfigMap; /** This class provides access to the PIC Programmer configurations. Several are predefinied; the rest can be read from and written to, and removed. Names are case insensitive. Each programmer configuration is in the form of the ProgrammerConfig struct. @author David Saxton */ class PicProgrammerSettings { public: PicProgrammerSettings(); /** * Reads in custom ProgrammerConfigs from config. Any previously loaded * configurations stored in this class will removed first. */ void load( KConfig * config ); /** * Saves the custom ProgrammConfigs to config. */ void save( KConfig * config ); /** * @return the ProgrammConfig for the programmer with the given name. If * no such ProgrammerConfigs with the given name exist, then one will be * created. The name is case insensitive (although the full case of the * name will be stored if a new ProgrammerConfig is created). */ ProgrammerConfig config( const QString & name ); /** * Removes the config (if it is custom) with the give name. */ void removeConfig( const QString & name ); /** * Sets the ProgrammerConfig with the given name (or creates one if no * such config exists). The name is case insensitive. */ void saveConfig( const QString & name, const ProgrammerConfig & config ); /** * @param makeLowercase whether the names should be converted to * lowercase before returning. * @return a list of names of the custom and predefined configs. */ QStringList configNames( bool makeLowercase ) const; /** * @return whether the given config is predefined. */ bool isPredefined( const QString & name ) const; protected: /** * Called when a PicProgrammerSettings object is first created. Does * initialization of the predefined configs. */ void initStaticConfigs(); ProgrammerConfigMap m_customConfigs; static bool m_bDoneStaticConfigsInit; static ProgrammerConfigMap m_staticConfigs; }; /** @author David Saxton */ class PicProgrammer : public ExternalLanguage { public: PicProgrammer( ProcessChain *processChain ); ~PicProgrammer() override; void processInput( ProcessOptions options ) override; ProcessOptions::ProcessPath::Path outputPath( ProcessOptions::ProcessPath::Path inputPath ) const override; protected: bool isError( const QString &message ) const override; bool isWarning( const QString &message ) const override; }; #endif diff --git a/src/languages/processchain.cpp b/src/languages/processchain.cpp index 525617cb..ff0d23f6 100644 --- a/src/languages/processchain.cpp +++ b/src/languages/processchain.cpp @@ -1,331 +1,332 @@ /*************************************************************************** * Copyright (C) 2005 by David Saxton * * david@bluehaze.org * * * * 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 "asmparser.h" #include "docmanager.h" #include "gplib.h" #include "language.h" #include "languagemanager.h" #include "logview.h" #include "ktechlab.h" #include "outputmethoddlg.h" #include "processchain.h" #include "projectmanager.h" #include "textdocument.h" #include "flowcode.h" #include "gpasm.h" #include "gpdasm.h" #include "gplink.h" #include "microbe.h" #include "picprogrammer.h" #include "sdcc.h" -#include #include -#include -#include + +#include +#include +#include #include #include #include //BEGIN class ProcessChain ProcessChain::ProcessChain( ProcessOptions options, const char *name ) : QObject( KTechlab::self() /*, name */ ) { setObjectName( name ); m_pFlowCode = nullptr; m_pGpasm = nullptr; m_pGpdasm = nullptr; m_pGplib = nullptr; m_pGplink = nullptr; m_pMicrobe = nullptr; m_pPicProgrammer = nullptr; m_pSDCC = nullptr; m_processOptions = options; QString target; if ( ProcessOptions::ProcessPath::to( options.processPath() ) == ProcessOptions::ProcessPath::Pic ) target = options.m_picID; else target = options.targetFile(); LanguageManager::self()->logView()->addOutput( i18n("Building: %1", target ), LogView::ot_important ); QTimer::singleShot( 0, this, SLOT(compile()) ); } ProcessChain::~ProcessChain() { delete m_pFlowCode; delete m_pGpasm; delete m_pGpdasm; delete m_pGplib; delete m_pGplink; delete m_pMicrobe; delete m_pPicProgrammer; delete m_pSDCC; } // void ProcessChain::compile( ProcessOptions * options ) void ProcessChain::compile() { // If the micro id in the options is empty, then attempt to get it from any // open project (it might not be necessarily...but won't hurt if it isn't). if ( m_processOptions.m_picID.isEmpty() ) { if ( ProjectInfo * projectInfo = ProjectManager::self()->currentProject() ) { ProjectItem * projectItem = projectInfo->findItem(QUrl::fromLocalFile(m_processOptions.inputFiles().first())); if (projectItem) m_processOptions.m_picID = projectItem->microID(); } } switch ( m_processOptions.processPath() ) { #define DIRECT_PROCESS( path, processor ) \ case ProcessOptions::ProcessPath::path: \ { \ processor()->processInput(m_processOptions); break; \ } #define INDIRECT_PROCESS( path, processor, extension ) \ case ProcessOptions::ProcessPath::path: \ { \ QTemporaryFile f(QDir::tempPath() + QLatin1String("/ktechlab_XXXXXX") + QLatin1String(extension)); \ f.open(); \ f.close(); \ m_processOptions.setIntermediaryOutput( f.fileName() ); \ processor()->processInput(m_processOptions); \ break; \ } INDIRECT_PROCESS( AssemblyAbsolute_PIC, gpasm, ".hex" ) DIRECT_PROCESS( AssemblyAbsolute_Program, gpasm ) INDIRECT_PROCESS( AssemblyRelocatable_Library, gpasm, ".o" ) DIRECT_PROCESS( AssemblyRelocatable_Object, gpasm ) INDIRECT_PROCESS( AssemblyRelocatable_PIC, gpasm, ".o" ) INDIRECT_PROCESS( AssemblyRelocatable_Program, gpasm, ".o" ) DIRECT_PROCESS( C_AssemblyRelocatable, sdcc ) INDIRECT_PROCESS( C_Library, sdcc, ".asm" ) INDIRECT_PROCESS( C_Object, sdcc, ".asm" ) INDIRECT_PROCESS( C_PIC, sdcc, ".asm" ) INDIRECT_PROCESS( C_Program, sdcc, ".asm" ) INDIRECT_PROCESS( FlowCode_AssemblyAbsolute, flowCode, ".microbe" ) DIRECT_PROCESS( FlowCode_Microbe, flowCode ) INDIRECT_PROCESS( FlowCode_PIC, flowCode, ".microbe" ) INDIRECT_PROCESS( FlowCode_Program, flowCode, ".microbe" ) DIRECT_PROCESS( Microbe_AssemblyAbsolute, microbe ) INDIRECT_PROCESS( Microbe_PIC, microbe, ".asm" ) INDIRECT_PROCESS( Microbe_Program, microbe, ".asm" ) DIRECT_PROCESS( Object_Disassembly, gpdasm ) DIRECT_PROCESS( Object_Library, gplib ) INDIRECT_PROCESS( Object_PIC, gplink, ".lib" ) DIRECT_PROCESS( Object_Program, gplink ) DIRECT_PROCESS( PIC_AssemblyAbsolute, picProgrammer ) DIRECT_PROCESS( Program_Disassembly, gpdasm ) DIRECT_PROCESS( Program_PIC, picProgrammer ) #undef DIRECT_PROCESS #undef INDIRECT_PROCESS case ProcessOptions::ProcessPath::Invalid: qWarning() << Q_FUNC_INFO << "Process path is invalid" << endl; case ProcessOptions::ProcessPath::None: qWarning() << Q_FUNC_INFO << "Nothing to do" << endl; break; } } void ProcessChain::slotFinishedCompile(Language *language) { ProcessOptions options = language->processOptions(); if ( options.b_addToProject && ProjectManager::self()->currentProject() ) ProjectManager::self()->currentProject()->addFile( QUrl::fromLocalFile(options.targetFile()) ); ProcessOptions::ProcessPath::MediaType typeTo = ProcessOptions::ProcessPath::to( m_processOptions.processPath() ); TextDocument * editor = nullptr; if ( KTLConfig::reuseSameViewForOutput() ) { editor = options.textOutputTarget(); if ( editor && (!editor->url().isEmpty() || editor->isModified()) ) editor = nullptr; } switch (typeTo) { case ProcessOptions::ProcessPath::AssemblyAbsolute: case ProcessOptions::ProcessPath::AssemblyRelocatable: case ProcessOptions::ProcessPath::C: case ProcessOptions::ProcessPath::Disassembly: case ProcessOptions::ProcessPath::Library: case ProcessOptions::ProcessPath::Microbe: case ProcessOptions::ProcessPath::Object: case ProcessOptions::ProcessPath::Program: { switch ( options.method() ) { case ProcessOptions::Method::LoadAsNew: { if ( !editor ) editor = DocManager::self()->createTextDocument(); if ( !editor ) break; QString text; QFile f( options.targetFile() ); if ( !f.open( QIODevice::ReadOnly ) ) { editor->deleteLater(); editor = nullptr; break; } QTextStream stream(&f); while ( !stream.atEnd() ) text += stream.readLine()+'\n'; f.close(); editor->setText( text, true ); break; } case ProcessOptions::Method::Load: { editor = dynamic_cast( DocManager::self()->openURL(QUrl::fromLocalFile(options.targetFile())) ); break; } case ProcessOptions::Method::Forget: break; } } case ProcessOptions::ProcessPath::FlowCode: case ProcessOptions::ProcessPath::Pic: case ProcessOptions::ProcessPath::Unknown: break; } if (editor) { switch (typeTo) { case ProcessOptions::ProcessPath::AssemblyAbsolute: case ProcessOptions::ProcessPath::AssemblyRelocatable: { if ( KTLConfig::autoFormatMBOutput() ) editor->formatAssembly(); editor->slotInitLanguage( TextDocument::ct_asm ); break; } case ProcessOptions::ProcessPath::C: editor->slotInitLanguage( TextDocument::ct_c ); break; case ProcessOptions::ProcessPath::Disassembly: break; case ProcessOptions::ProcessPath::Library: case ProcessOptions::ProcessPath::Object: case ProcessOptions::ProcessPath::Program: editor->slotInitLanguage( TextDocument::ct_hex ); break; case ProcessOptions::ProcessPath::Microbe: editor->slotInitLanguage( TextDocument::ct_microbe ); break; case ProcessOptions::ProcessPath::FlowCode: case ProcessOptions::ProcessPath::Pic: case ProcessOptions::ProcessPath::Unknown: break; } DocManager::self()->giveDocumentFocus( editor ); } options.setTextOutputtedTo( editor ); emit successful(options); emit successful(); } #define LanguageFunction(a,b,c) \ a * ProcessChain::b( ) \ { \ if ( !c ) \ { \ c = new a( this ); \ connect( c, SIGNAL(processSucceeded(Language* )), this, SLOT(slotFinishedCompile(Language* )) ); \ connect( c, SIGNAL(processFailed(Language* )), this, SIGNAL(failed()) ); \ } \ return c; \ } LanguageFunction( FlowCode, flowCode, m_pFlowCode ) LanguageFunction( Gpasm, gpasm, m_pGpasm ) LanguageFunction( Gpdasm, gpdasm, m_pGpdasm ) LanguageFunction( Gplib, gplib, m_pGplib ) LanguageFunction( Gplink, gplink, m_pGplink ) LanguageFunction( Microbe, microbe, m_pMicrobe ) LanguageFunction( PicProgrammer, picProgrammer, m_pPicProgrammer ) LanguageFunction( SDCC, sdcc, m_pSDCC ) //END class ProcessChain //BEGIN class ProcessListChain ProcessListChain::ProcessListChain( ProcessOptionsList pol, const char * name ) : QObject( KTechlab::self() /*, name */ ) { setObjectName( name ); m_processOptionsList = pol; // Start us off... slotProcessChainSuccessful(); } void ProcessListChain::slotProcessChainSuccessful() { if ( m_processOptionsList.isEmpty() ) { emit successful(); return; } ProcessOptionsList::iterator it = m_processOptionsList.begin(); ProcessOptions po = *it; m_processOptionsList.erase(it); ProcessChain * pc = LanguageManager::self()->compile(po); connect( pc, SIGNAL(successful()), this, SLOT(slotProcessChainSuccessful()) ); connect( pc, SIGNAL(failed()), this, SLOT(slotProcessChainFailed()) ); } void ProcessListChain::slotProcessChainFailed() { emit failed(); } //END class ProcessListChain diff --git a/src/languages/processchain.h b/src/languages/processchain.h index 616cad3a..f2cf3eac 100644 --- a/src/languages/processchain.h +++ b/src/languages/processchain.h @@ -1,125 +1,125 @@ /*************************************************************************** * Copyright (C) 2005 by David Saxton * * david@bluehaze.org * * * * 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. * ***************************************************************************/ #ifndef PROCESSCHAIN_H #define PROCESSCHAIN_H #include "language.h" -#include -#include +#include +#include class FlowCode; class Gpasm; class Gpdasm; class Gplib; class Gplink; class KTechlab; class Microbe; class PicProgrammer; class ProcesOptions; class SDCC; typedef QList ProcessOptionsList; /** @author Daniel Clarke @author David Saxton */ class ProcessChain : public QObject { Q_OBJECT public: ProcessChain( ProcessOptions options, const char *name = nullptr ); ~ProcessChain() override; void setProcessOptions( ProcessOptions options ) { m_processOptions = options; } public slots: /** * Adds the output file to project if requested in the options, and opens * the file in a code editor. Called to signal that a language in the last * step of a compile has finished its compiling successfully. */ void slotFinishedCompile( Language * language ); /** * Call to compile a file of one type all the way to another type. This * uses the ProcessOptions given in the constructor of this function, or * later in setProcessOptions. */ void compile(); signals: /** * Emitted when compiling has successfully gone all the way through to the * specified 'typeTo' * @param options The ProcessOptions holding the output filename * @see compile */ void successful(ProcessOptions options); /** * Convenience signal */ void successful(); /** * Emitted if not successful */ void failed(); protected: FlowCode * flowCode(); Gpasm * gpasm(); Gpdasm * gpdasm(); Gplib * gplib(); Gplink * gplink(); Microbe * microbe(); PicProgrammer * picProgrammer(); SDCC * sdcc(); int m_errorCount; ProcessOptions m_processOptions; private: FlowCode * m_pFlowCode; Microbe * m_pMicrobe; Gpasm * m_pGpasm; Gpdasm * m_pGpdasm; Gplib * m_pGplib; Gplink * m_pGplink; PicProgrammer * m_pPicProgrammer; SDCC * m_pSDCC; }; class ProcessListChain : public QObject { Q_OBJECT public: ProcessListChain( ProcessOptionsList pol, const char *name = nullptr ); signals: /** * Emitted if successful */ void successful(); /** * Emitted if not successful */ void failed(); protected slots: void slotProcessChainSuccessful(); void slotProcessChainFailed(); protected: ProcessOptionsList m_processOptionsList; }; #endif diff --git a/src/languages/sourceline.h b/src/languages/sourceline.h index e223e340..1e72ef57 100644 --- a/src/languages/sourceline.h +++ b/src/languages/sourceline.h @@ -1,44 +1,44 @@ /*************************************************************************** * Copyright (C) 2005 by David Saxton * * david@bluehaze.org * * * * 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. * ***************************************************************************/ #ifndef SOURCELINE_H #define SOURCELINE_H -#include +#include /** @author David Saxton */ class SourceLine { public: /** * Creates an invalid source line (line is negative). */ SourceLine(); /// @param fileName the path to a file in the local filesystem SourceLine( const QString & fileName, int line ); /// @returns the path to the source file in the local filesystem QString fileName() const { return m_fileName; } int line() const { return m_line; } bool isValid() const { return m_line >= 0; } bool operator < ( const SourceLine & sourceLine ) const; bool operator == ( const SourceLine & sourceLine ) const; protected: QString m_fileName; int m_line; }; #endif diff --git a/src/libraryitem.cpp b/src/libraryitem.cpp index 12e7c343..99a7c839 100644 --- a/src/libraryitem.cpp +++ b/src/libraryitem.cpp @@ -1,82 +1,82 @@ /*************************************************************************** * Copyright (C) 2003 by David Saxton * * david@bluehaze.org * * * * 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 "libraryitem.h" #include -#include +#include #include LibraryItem::LibraryItem( QStringList idList, const QString &name, const QString &category, QPixmap icon, Type type, createItemPtr _createItem ) { m_idList = idList; m_name = name; m_category = category; m_icon_full = icon; m_type = type; createItem = _createItem; createIcon16(); } LibraryItem::LibraryItem( QStringList idList, const QString &name, const QString &category, const QString &iconName, Type type, createItemPtr _createItem ) { m_idList = idList; m_name = name; m_category = category; m_icon_full.load( QStandardPaths::locate(QStandardPaths::AppDataLocation, "icons/"+iconName ) ); m_type = type; createItem = _createItem; createIcon16(); } LibraryItem::LibraryItem( QStringList idList, const QString &name, const QString &category, Type type, createItemPtr _createItem ) { m_idList = idList; m_name = name; m_category = category; m_type = type; createItem = _createItem; createIcon16(); } LibraryItem::~LibraryItem() { } void LibraryItem::createIcon16() { if ( m_icon_full.isNull() ) m_icon_full = KIconLoader::global()->loadIcon( "null", KIconLoader::Small ); // const int size = KIcon::SizeSmallMedium; // const int size = 22; const int size = 16; if ( m_icon_full.width() == size && m_icon_full.height() == size ) { m_icon_16 = m_icon_full; return; } QImage im = m_icon_full.toImage(); //im = im.smoothScale( size, size, Qt::ScaleMin ); // 2018.11.30 im = im.scaled( QSize( size, size ), Qt::KeepAspectRatio, Qt::SmoothTransformation ); m_icon_16.convertFromImage(im); } QString LibraryItem::activeID( ) const { return m_idList.isEmpty() ? "" : m_idList[0]; } diff --git a/src/math/qvector.cpp b/src/math/qvector.cpp index f095aba7..6d06c219 100644 --- a/src/math/qvector.cpp +++ b/src/math/qvector.cpp @@ -1,176 +1,176 @@ /*************************************************************************** * Copyright (C) 2006 by Alan Grimes * * agrimes@speakeasy.net * * * * 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., * * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * ***************************************************************************/ // #ifndef QVECTOR_H #include "qvector.h" // #endif #include // for null #include #include -#include +#include #include using namespace std; /* #ifndef BADRNG_H #include "badrng.h" #endif */ // ###################################### double QuickVector::at(CUI m_a) const { // if(!m_a || m_a > m) return NAN; return values[m_a]; } // ##################################### bool QuickVector::atPut(CUI m_a, const double val) { if(m_a >= m) return false; values[m_a] = val; changed = true; return true; } // ##################################### bool QuickVector::atAdd(CUI m_a, const double val) { if(m_a >= m) return false; values[m_a] += val; changed = true; return true; } // ##################################### QuickVector::QuickVector(CUI m_in) : m(m_in), changed(true) { assert(m); values = new double[m]; memset(values, 0, sizeof(double) *m); } // ##################################### QuickVector::QuickVector(const QuickVector *old) : m(old->m), changed(old->changed) { assert(m); values = new double[m]; for(unsigned int j = 0; j < m; j++) values[j] = old->values[j]; } // ##################################### QuickVector::~QuickVector() { delete[] values; } // ##################################### /* void QuickVector::fillWithRandom() { for(unsigned int j = 0; j < m; j++) values[j] = drng(); } */ // ##################################### void QuickVector::fillWithZeros() { memset(values, 0, m*sizeof(double)); changed = true; } // ##################################### QuickVector &QuickVector::operator-(const QuickVector &y) const { // if(y.m != m) abort(); QuickVector *ret; ret = new QuickVector(m); for(unsigned int i = 0; i < m; i++) ret->values[i] = values[i] - y.values[i]; return *ret; } // #################################### void QuickVector::dumpToAux() const { for(unsigned int i = 0; i < m; i++) cout << values[i] << ' '; cout << endl; } // #################################### bool QuickVector::swapElements(CUI m_a, CUI m_b) { if(m_a >= m || m_b >= m) return false; double temp = values[m_a]; values[m_a] = values[m_b]; values[m_b] = temp; changed = true; return true; } // ################################### QuickVector &QuickVector::operator=(const QuickVector &y) { assert(y.m == m); for(unsigned int i = 0; i < m; i++) values[i] = y.values[i]; changed = true; return *this; } // ################################### QuickVector &QuickVector::operator *=(const QuickVector &y) { // if(y.m != m) return nullptr; for(unsigned int i = 0; i < m; i++) values[i] *= y.values[i]; changed = true; return *this; } // ################################### QuickVector &QuickVector::operator +=(const QuickVector &y) { // if(y.m != m) return nullptr; for(unsigned int i = 0; i < m; i++) values[i] += y.values[i]; changed = true; return *this; } // ################################### diff --git a/src/mechanics/chassiscircular2.cpp b/src/mechanics/chassiscircular2.cpp index 80bdbd0a..d40d4c36 100644 --- a/src/mechanics/chassiscircular2.cpp +++ b/src/mechanics/chassiscircular2.cpp @@ -1,153 +1,153 @@ /*************************************************************************** * Copyright (C) 2005 by David Saxton * * david@bluehaze.org * * * * 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 "chassiscircular2.h" #include "libraryitem.h" #include -#include -#include +#include +#include #include #include double normalizeAngle( double angle ); Item* ChassisCircular2::construct( ItemDocument *itemDocument, bool newItem, const char *id ) { return new ChassisCircular2( (MechanicsDocument*)itemDocument, newItem, id ); } LibraryItem* ChassisCircular2::libraryItem() { return new LibraryItem( QStringList(QString("mech/chassis_circular_2")), i18n("Circular 2-Wheel Chassis"), i18n("Chassis'"), "chassis.png", LibraryItem::lit_mechanical, ChassisCircular2::construct ); } ChassisCircular2::ChassisCircular2( MechanicsDocument *mechanicsDocument, bool newItem, const char *id ) : MechanicsItem( mechanicsDocument, newItem, id ? id : "chassis_circular_2" ) { m_name = i18n("Circular 2-Wheel Chassis"); m_theta1 = 0.0; m_theta2 = 0.0; //Q3PointArray pa; // 2018.08.14 - ported to PainterPath //pa.makeEllipse( -25, -25, 50, 50 ); QPainterPath path; path.addEllipse( -25, -25, 50, 50); QPolygon pa = path.toFillPolygon().toPolygon(); QMatrix m(4,0,0,4,0,0); // m.setTransformationMode( QMatrix::Areas ); // TODO find a replacement pa = m.map(pa); setItemPoints(pa); itemResized(); } ChassisCircular2::~ChassisCircular2() { } void ChassisCircular2::itemResized() { const double w = sizeRect().width(); const double h = sizeRect().height(); m_wheel1Pos = QRect( int(w/5), int(h/6), int(w/4), int(h/8) ); m_wheel2Pos = QRect( int(w/5), int(5*h/6-h/8), int(w/4), int(h/8) ); } void ChassisCircular2::advance( int phase ) { if ( phase != 1 ) return; double speed1 = 60.; // pixels per second double speed2 = 160.; // pixels per second m_theta1 = normalizeAngle( m_theta1 + (speed1/1000.)/m_wheel1Pos.width() ); m_theta2 = normalizeAngle( m_theta2 + (speed2/1000.)/m_wheel2Pos.width() ); const double d1 = speed1/1000.; const double d2 = speed2/1000.; const double sep = m_wheel2Pos.center().y()-m_wheel1Pos.center().y(); double dtheta = std::atan( (d2-d1)/sep ); // Change in orientation of chassis double moveAngle = absolutePosition().angle()+dtheta/2; rotateBy(dtheta); moveBy( ((d1+d2)/2.)*std::cos(moveAngle), ((d1+d2)/2.)*std::sin(moveAngle) ); } void ChassisCircular2::drawShape( QPainter &p ) { const double _x = int(sizeRect().x() + x()); const double _y = int(sizeRect().y() + y()); const double w = sizeRect().width(); const double h = sizeRect().height(); initPainter(p); p.setBrush( QColor( 255, 246, 210 ) ); QRect circleRect = sizeRect(); circleRect.moveLeft( int(circleRect.left() + x()) ); circleRect.moveTop( int(circleRect.top() + y()) ); p.drawEllipse(circleRect); // Draw wheels // TODO get this info from m_wheel1Pos and m_wheel2Pos const double X = _x + (w / 5); // Wheel's left pos const double H = h / 8; // Wheel's height const double y1 = _y + (h / 6); // Wheel 1 y-pos const double y2 = _y + (5 * h / 6)-H; // Wheel 2 y-pos p.setPen( Qt::NoPen ); const double stripeWidth = 5; const double offset2 = 1 + int(m_theta1*m_wheel1Pos.width())%int(2*stripeWidth); const double offset1 = 1 + int(m_theta2*m_wheel2Pos.width())%int(2*stripeWidth); p.setBrush( QColor( 255, 232, 182 ) ); for ( double i=-1; i #include -#include -#include -#include + +#include +#include +#include +#include #include #define DPR ( 180.0 / M_PI ) /** @returns an angle between 0 and 2 pi */ double normalizeAngle( double angle ) { if ( angle < 0 ) angle += 2 * M_PI *(std::ceil(-angle)); return angle - 2 * M_PI * std::floor(angle/(2 * M_PI)); } MechanicsItem::MechanicsItem( MechanicsDocument *mechanicsDocument, bool newItem, const QString &id ) : Item( mechanicsDocument, newItem, id ) { p_mechanicsDocument = mechanicsDocument; m_selectionMode = MechanicsItem::sm_move; createProperty( "mass", Variant::Type::Double ); property("mass")->setCaption( i18n("Mass") ); property("mass")->setUnit("g"); property("mass")->setValue(10.0); property("mass")->setMinValue(1e-3); property("mass")->setMaxValue(1e12); property("mass")->setAdvanced(true); createProperty( "moi", Variant::Type::Double ); property("moi")->setCaption( i18n("Moment of Inertia") ); property("moi")->setUnit("gm"); property("moi")->setValue(0.01); property("moi")->setMinValue(1e-3); property("moi")->setMaxValue(1e12); property("moi")->setAdvanced(true); setZ(ItemDocument::Z::Item); // setAnimated(true); p_mechanicsDocument->registerItem(this); } MechanicsItem::~MechanicsItem() { } void MechanicsItem::setSelectionMode( SelectionMode sm ) { if ( sm == m_selectionMode ) return; m_selectionMode = sm; } void MechanicsItem::setSelected( bool yes ) { if ( yes == isSelected() ) return; if (!yes) // Reset the selection mode m_selectionMode = MechanicsItem::sm_resize; Item::setSelected(yes); } void MechanicsItem::dataChanged() { Item::dataChanged(); m_mechanicsInfo.mass = dataDouble("mass"); m_mechanicsInfo.momentOfInertia = dataDouble("moi"); updateMechanicsInfoCombined(); } PositionInfo MechanicsItem::absolutePosition() const { MechanicsItem *parentMechItem = dynamic_cast((Item*)(p_parentItem)); if (parentMechItem) return parentMechItem->absolutePosition() + m_relativePosition; return m_relativePosition; } void MechanicsItem::reparented( Item *oldItem, Item *newItem ) { MechanicsItem *oldMechItem = dynamic_cast(oldItem); MechanicsItem *newMechItem = dynamic_cast(newItem); if (oldMechItem) { m_relativePosition = oldMechItem->absolutePosition() + m_relativePosition; disconnect( oldMechItem, SIGNAL(moved()), this, SLOT(parentMoved()) ); } if (newMechItem) { m_relativePosition = m_relativePosition - newMechItem->absolutePosition(); connect( newMechItem, SIGNAL(moved()), this, SLOT(parentMoved()) ); } updateCanvasPoints(); } void MechanicsItem::childAdded( Item *child ) { MechanicsItem *mechItem = dynamic_cast(child); if (!mechItem) return; connect( mechItem, SIGNAL(updateMechanicsInfoCombined()), this, SLOT(childMoved()) ); updateMechanicsInfoCombined(); } void MechanicsItem::childRemoved( Item *child ) { MechanicsItem *mechItem = dynamic_cast(child); if (!mechItem) return; disconnect( mechItem, SIGNAL(updateMechanicsInfoCombined()), this, SLOT(childMoved()) ); updateMechanicsInfoCombined(); } void MechanicsItem::parentMoved() { PositionInfo absPos = absolutePosition(); Item::moveBy( absPos.x() - x(), absPos.y() - y() ); updateCanvasPoints(); emit moved(); } void MechanicsItem::updateCanvasPoints() { const QRect ipbr = m_itemPoints.boundingRect(); double scalex = double(m_sizeRect.width()) / double(ipbr.width()); double scaley = double(m_sizeRect.height()) / double(ipbr.height()); PositionInfo abs = absolutePosition(); QMatrix m; m.rotate(abs.angle() * DPR); m.translate( m_sizeRect.left(), m_sizeRect.top() ); m.scale( scalex, scaley ); m.translate( -int(ipbr.left()), -int(ipbr.top()) ); setPoints( m.map(m_itemPoints) ); //QRect tempt = m.mapRect(ipbr); // 2017.10.01 - comment out unused variable } void MechanicsItem::rotateBy( double dtheta ) { m_relativePosition.rotate(dtheta); updateCanvasPoints(); updateMechanicsInfoCombined(); emit moved(); } void MechanicsItem::moveBy( double dx, double dy ) { m_relativePosition.translate( dx, dy ); Item::moveBy( m_relativePosition.x() - x(), m_relativePosition.y() - y() ); emit moved(); } void MechanicsItem::updateMechanicsInfoCombined() { m_mechanicsInfoCombined = m_mechanicsInfo; double mass_x = 0.; double mass_y = 0.; const ItemList::const_iterator end = m_children.end(); for ( ItemList::const_iterator it = m_children.begin(); it != end; ++it ) { MechanicsItem *child = dynamic_cast((Item*)*it); if (child) { CombinedMechanicsInfo *childInfo = child->mechanicsInfoCombined(); const PositionInfo relativeChildPosition = child->relativePosition(); double mass = childInfo->mass; // double angle = relativeChildPosition.angle(); double dx = relativeChildPosition.x() /*+ cos(angle)*childInfo->m_x - sin(angle)*childInfo->m_y*/; double dy = relativeChildPosition.y() /*+ sin(angle)*childInfo->m_x + cos(angle)*childInfo->m_y*/; m_mechanicsInfoCombined.mass += mass; mass_x += mass * dx; mass_y += mass * dy; double length_squared = dx*dx + dy*dy; m_mechanicsInfoCombined.momentOfInertia += length_squared * childInfo->momentOfInertia; } } m_mechanicsInfoCombined.x = mass_x / m_mechanicsInfoCombined.mass; m_mechanicsInfoCombined.y = mass_y / m_mechanicsInfoCombined.mass; } ItemData MechanicsItem::itemData() const { ItemData itemData = Item::itemData(); itemData.angleDegrees = m_relativePosition.angle() * DPR; return itemData; } bool MechanicsItem::mousePressEvent( const EventInfo &eventInfo ) { Q_UNUSED(eventInfo); return false; } bool MechanicsItem::mouseReleaseEvent( const EventInfo &eventInfo ) { Q_UNUSED(eventInfo); return false; } bool MechanicsItem::mouseDoubleClickEvent( const EventInfo &eventInfo ) { Q_UNUSED(eventInfo); return false; } bool MechanicsItem::mouseMoveEvent( const EventInfo &eventInfo ) { Q_UNUSED(eventInfo); return false; } bool MechanicsItem::wheelEvent( const EventInfo &eventInfo ) { Q_UNUSED(eventInfo); return false; } void MechanicsItem::enterEvent(QEvent *) { } void MechanicsItem::leaveEvent(QEvent *) { } QRect MechanicsItem::maxInnerRectangle( const QRect &outerRect ) const { QRect normalizedOuterRect = outerRect.normalized(); const double LEFT = normalizedOuterRect.left(); const double TOP = normalizedOuterRect.top(); const double X = normalizedOuterRect.width(); const double Y = normalizedOuterRect.height(); const double a = normalizeAngle(absolutePosition().angle()); double left; double top; double width; double height; // if ( can change width/height ratio ) { double x1 = X*std::cos(a) - Y*std::sin(a); double y1 = X*std::sin(a) + Y*std::cos(a); double x2 = X*std::cos(a); double y2 = X*std::sin(a); double x3 = -Y*std::sin(a); double y3 = Y*std::cos(a); double xbig;/* = std::max( std::abs(x2-x3), std::abs(x1) );*/ double ybig;/* = std::max( std::abs(y2-y3), std::abs(y1) );*/ if ( (a - floor(a/6.2832)*6.2832) < M_PI ) { xbig = std::abs(x3-x2); ybig = std::abs(y1); } else { xbig = std::abs(x1); ybig = std::abs(y3-y2); } width = X*(X/xbig); height = Y*(Y/ybig); top = -std::sin(a) * (LEFT + width*std::sin(a)) + std::cos(a)*TOP; left = std::cos(a) * (LEFT + width*std::sin(a)) + std::sin(a)*TOP; } return QRect( int(left), int(top), int(width), int(height) ); } void MechanicsItem::initPainter( QPainter &p ) { PositionInfo absPos = absolutePosition(); p.translate( absPos.x(), absPos.y() ); p.rotate( absPos.angle() * DPR); p.translate( -absPos.x(), -absPos.y() ); } void MechanicsItem::deinitPainter( QPainter &p ) { PositionInfo absPos = absolutePosition(); p.translate( absPos.x(), absPos.y() ); p.rotate( -absPos.angle() * DPR); p.translate( -absPos.x(), -absPos.y() ); } PositionInfo::PositionInfo() { reset(); } const PositionInfo PositionInfo::operator+( const PositionInfo &info ) { // Copy the child to a new position PositionInfo newInfo = info; // Translate the newInfo by our translation amount newInfo.translate( x(), y() ); // Rotate the child about us newInfo.rotateAboutPoint( x(), y(), angle() ); return newInfo; } const PositionInfo PositionInfo::operator-( const PositionInfo &info ) { PositionInfo newInfo = *this; newInfo.translate( -info.x(), -info.y() ); newInfo.rotate( -info.angle() ); return newInfo; } void PositionInfo::rotateAboutPoint( double x, double y, double angle ) { m_angle += angle; double newx = x + (m_x-x)*std::cos(angle) - (m_y-y)*std::sin(angle); double newy = y + (m_x-x)*std::sin(angle) + (m_y-y)*std::cos(angle); m_x = newx; m_y = newy; } void PositionInfo::reset() { m_x = 0.; m_y = 0.; m_angle = 0.; } MechanicsInfo::MechanicsInfo() { mass = 0.; momentOfInertia = 0.; } CombinedMechanicsInfo::CombinedMechanicsInfo() : MechanicsInfo() { x = 0.; y = 0.; } CombinedMechanicsInfo::CombinedMechanicsInfo( const MechanicsInfo &info ) : MechanicsInfo(info) { x = 0.; y = 0.; } diff --git a/src/mechanics/mechanicsitem.h b/src/mechanics/mechanicsitem.h index aa4f3830..cffd892f 100644 --- a/src/mechanics/mechanicsitem.h +++ b/src/mechanics/mechanicsitem.h @@ -1,233 +1,233 @@ /*************************************************************************** * Copyright (C) 2004-2005 by David Saxton * * david@bluehaze.org * * * * 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. * ***************************************************************************/ #ifndef MECHANICSITEM_H #define MECHANICSITEM_H #include -#include +#include class LibraryItem; class MechanicsItem; // class MechanicsItemOverlayItem; class MechanicsDocument; typedef QList MechanicsItemList; /** @short Stores mass, moment of inertia @author David Saxton */ class MechanicsInfo { public: MechanicsInfo(); double mass; // Mass double momentOfInertia; // Moment of inertia }; class CombinedMechanicsInfo : public MechanicsInfo { public: CombinedMechanicsInfo(); CombinedMechanicsInfo( const MechanicsInfo &info ); double x; // X coordinate of center of mass double y; // Y coordinate of center of mass }; /** @short Stores a position and orientation @author David Saxton */ class PositionInfo { public: PositionInfo(); /** * Adds together two positions: for this=PARENT +(CHILD), the new position * is formed by translating this position by that of the CHILDs * translation, and then rotating everything about the center of this item */ const PositionInfo operator+( const PositionInfo &info ); /** * Not quite the inverse of operator+. Subtracts the given position info * as if it was applied before this current info. */ const PositionInfo operator-( const PositionInfo &info ); /** * x position (0 is left) */ double x() const { return m_x; } /** * y position (0 is top) */ double y() const { return m_y; } /** * Angle in radians, positive direction is anticlockwise */ double angle() const { return m_angle; } /** * Sets the x-position */ void setX( double x ) { m_x = x; } /** * Sets the y-position */ void setY( double y ) { m_y = y; } /** * Sets the angle */ void setAngle( double angle ) { m_angle = angle; } /** * Adds (x,y) to the current position */ void translate( double dx, const double dy ) { m_x += dx; m_y += dy; } /** * Rotates anticlockwise by the given amount (in radians) */ void rotate( double angle ) { m_angle += angle; } /** * Resets the position to (0,0), and the orientation to 0 */ void reset(); /** * Rotates the current position about the given point through the given * angle in radians anticlockwise. This will change the position and * orientation. */ void rotateAboutPoint( double x, double y, double angle ); protected: double m_x; double m_y; double m_angle; }; /** @author David Saxton */ class MechanicsItem : public Item { Q_OBJECT public: MechanicsItem( MechanicsDocument *mechanicsDocument, bool newItem, const QString &id ); ~MechanicsItem() override; enum SelectionMode { sm_move, sm_resize, sm_rotate }; /** * Sets the selection mode (sm_resize or sm_rotate). Note that setSelected * also needs to be called to select the item. */ void setSelectionMode( SelectionMode sm ); void setSelected( bool yes ) override; /** * @returns the selection mode */ SelectionMode selectionMode() const { return m_selectionMode; } /** * Move the MechanicsItem by the given amount */ void moveBy( double dx, double dy ) override; /** * Returns the absolute position on the canvas */ PositionInfo absolutePosition() const; /** * Returns the position relative to the parent item (or the absolute * position if there is no parent item) */ PositionInfo relativePosition() const { return m_relativePosition; } /** * Returns the mechanics info for this item (so not taking into account that * of attached children) */ MechanicsInfo *mechanicsInfo() { return &m_mechanicsInfo; } /** * Returns the combined mechanics info for this item (which takes into * account that of attached children). */ CombinedMechanicsInfo *mechanicsInfoCombined() { return &m_mechanicsInfoCombined; } /** * Returns the rectangle that can legitimately fit inside the given bounding * rectangle, given this items current rotation. Legitimately means that * whether this item is allowed to be distorted, inverted, resized, etc. */ QRect maxInnerRectangle( const QRect &outerRect ) const; ItemData itemData() const override; bool mousePressEvent( const EventInfo &eventInfo ) override; bool mouseReleaseEvent( const EventInfo &eventInfo ) override; bool mouseDoubleClickEvent ( const EventInfo &eventInfo ) override; bool mouseMoveEvent( const EventInfo &eventInfo ) override; bool wheelEvent( const EventInfo &eventInfo ) override; void enterEvent(QEvent *) override; void leaveEvent(QEvent *) override; public slots: /** * Rotate the item by the given amount (in radians) */ void rotateBy( double dtheta ); void parentMoved(); signals: /** * Emitted when this item moves (translates or rotates) */ void moved(); protected slots: /** * Recalculate the combined mechanics info (e.g. when mass is changed, or child added) */ void updateMechanicsInfoCombined(); protected: void reparented( Item *oldItem, Item *newItem ) override; void childAdded( Item *child ) override; void childRemoved( Item *child ) override; /** * Called when this item is resized, so that sub classes can do whatever */ virtual void itemResized() {}; /** * Sets the correct orientation on the painter */ void initPainter( QPainter &p ); /** * *Must* be called after calling initPainter, if initPainter was called */ void deinitPainter( QPainter &p ); void dataChanged() override; void itemPointsChanged() override { updateCanvasPoints(); } /** * Calculates the setPoints required from the current m_itemPoints and the * current position / angle */ void updateCanvasPoints(); MechanicsDocument *p_mechanicsDocument; PositionInfo m_relativePosition; // Absolution position if not attached to a parent item, or otherwise relative to parent item MechanicsInfo m_mechanicsInfo; CombinedMechanicsInfo m_mechanicsInfoCombined; private: SelectionMode m_selectionMode; }; #endif diff --git a/src/mechanics/mechanicssimulation.cpp b/src/mechanics/mechanicssimulation.cpp index ed76db88..fa536b46 100644 --- a/src/mechanics/mechanicssimulation.cpp +++ b/src/mechanics/mechanicssimulation.cpp @@ -1,135 +1,135 @@ /*************************************************************************** * Copyright (C) 2005 by David Saxton * * david@bluehaze.org * * * * 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 "mechanicsdocument.h" #include "mechanicsitem.h" #include "mechanicssimulation.h" #include -#include +#include MechanicsSimulation::MechanicsSimulation( MechanicsDocument *mechanicsDocument ) : QObject(mechanicsDocument) { p_mechanicsDocument = mechanicsDocument; m_advanceTmr = new QTimer(this); connect( m_advanceTmr, SIGNAL(timeout()), this, SLOT(slotAdvance()) ); m_advanceTmr->start(20); // it is not implemented anyway, so don't hog the CPU } MechanicsSimulation::~MechanicsSimulation() { } void MechanicsSimulation::slotAdvance() { // if ( p_mechanicsDocument && p_mechanicsDocument->canvas() ) // p_mechanicsDocument->canvas()->advance(); } RigidBody::RigidBody( MechanicsDocument *mechanicsDocument ) { p_mechanicsDocument = mechanicsDocument; p_overallParent = nullptr; } RigidBody::~RigidBody() { } bool RigidBody::addMechanicsItem( MechanicsItem *item ) { if ( !item || m_mechanicsItemList.contains(item) ) return false; m_mechanicsItemList.append(item); findOverallParent(); return true; } void RigidBody::moveBy( double dx, double dy ) { if (overallParent()) overallParent()->moveBy( dx, dy ); } void RigidBody::rotateBy( double dtheta ) { if (overallParent()) overallParent()->rotateBy(dtheta); } bool RigidBody::findOverallParent() { p_overallParent = nullptr; if ( m_mechanicsItemList.isEmpty() ) return false; m_mechanicsItemList.removeAll(nullptr); const MechanicsItemList::iterator end = m_mechanicsItemList.end(); for ( MechanicsItemList::iterator it = m_mechanicsItemList.begin(); it != end; ++it ) { MechanicsItem *parentItem = *it; MechanicsItem *parentCandidate = dynamic_cast((*it)->parentItem()); while (parentCandidate) { parentItem = parentCandidate; parentCandidate = dynamic_cast(parentItem->parentItem()); } if ( !p_overallParent ) // Must be the first item to test p_overallParent = parentItem; if ( p_overallParent != parentItem ) { p_overallParent = nullptr; return false; } } return true; } void RigidBody::updateRigidBodyInfo() { if (!p_overallParent) return; m_mass = p_overallParent->mechanicsInfoCombined()->mass; m_momentOfInertia = p_overallParent->mechanicsInfoCombined()->momentOfInertia; } Vector2D::Vector2D() { x = 0.; y = 0.; } double Vector2D::length() const { return std::sqrt( x*x + y*y ); } RigidBodyState::RigidBodyState() { angularMomentum = 0.; } diff --git a/src/mechanics/mechanicssimulation.h b/src/mechanics/mechanicssimulation.h index 52567ff5..a8be1910 100644 --- a/src/mechanics/mechanicssimulation.h +++ b/src/mechanics/mechanicssimulation.h @@ -1,133 +1,133 @@ /*************************************************************************** * Copyright (C) 2005 by David Saxton * * david@bluehaze.org * * * * 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. * ***************************************************************************/ #ifndef MECHANICSSIMULATION_H #define MECHANICSSIMULATION_H -#include -#include -#include +#include +#include +#include class MechanicsItem; class MechanicsDocument; typedef QList MechanicsItemList; /** @short 2 dimensional vector with associated length functions et al @author David Saxton */ class Vector2D { public: Vector2D(); double length() const; double lengthSquared() const { return x*x + y*y; } double x; double y; }; /** @short State of a rigid body, in an inertial MechanicsDocument frame @author David Saxton */ class RigidBodyState { public: RigidBodyState(); Vector2D linearMomentum; double angularMomentum; Vector2D position; // Position of center of mass }; /** @author David Saxton */ class MechanicsSimulation : public QObject { Q_OBJECT public: MechanicsSimulation( MechanicsDocument *mechanicsDocument ); ~MechanicsSimulation() override; MechanicsDocument* mechanicsDocument() const { return p_mechanicsDocument; } protected slots: void slotAdvance(); protected: QPointer p_mechanicsDocument; QTimer *m_advanceTmr; }; /** Rigid body with mass, inertia, etc. Collection of mechanics items with functionality for moving them about, rotating, etc. Only one mother-parent (has no parent itself, all other items are descendents) allowed. @short Rigid body, handles MechanicsItems @author David Saxton */ class RigidBody { public: RigidBody( MechanicsDocument *mechanicsDocument ); ~RigidBody(); /** * */ void advance( int phase, double delta ); /** * Add the MechanicsItem to the entity. * @returns true iff successful in adding */ bool addMechanicsItem( MechanicsItem *item ); /** * Pointer to the mother MechanicsItem. */ MechanicsItem *overallParent() const { return p_overallParent; } /** * Updates the mass and the moment of inertia info */ void updateRigidBodyInfo(); protected: /** * Attempt to find the overall parent. * @returns false iff unsucessful (including if there are no MechanicsItems present) */ bool findOverallParent(); /** * Move the set of MechanicsItems by the given amount */ void moveBy( double dx, double dy ); /** * Rotate the set of MechanicsItems by the given amount about the center of * mass. * @param dtheta Rotate amount in radians */ void rotateBy( double dtheta ); MechanicsItemList m_mechanicsItemList; MechanicsItem *p_overallParent; MechanicsDocument *p_mechanicsDocument; RigidBodyState m_rigidBodyState; double m_mass; double m_momentOfInertia; }; #endif diff --git a/src/mechanics/mechanicsview.cpp b/src/mechanics/mechanicsview.cpp index 3227820f..d2a65aa5 100644 --- a/src/mechanics/mechanicsview.cpp +++ b/src/mechanics/mechanicsview.cpp @@ -1,39 +1,40 @@ /*************************************************************************** * Copyright (C) 2005 by David Saxton * * david@bluehaze.org * * * * 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 "mechanicsdocument.h" #include "mechanicsview.h" #include "viewiface.h" -#include +#include +#include MechanicsView::MechanicsView( MechanicsDocument *mechanicsDocument, ViewContainer *viewContainer, uint viewAreaId, const char *name ) : ItemView( mechanicsDocument, viewContainer, viewAreaId, name ) { setXMLFile( "ktechlabmechanicsui.rc", true ); m_pViewIface = new MechanicsViewIface(this); } MechanicsView::~MechanicsView() { delete m_pViewIface; } void MechanicsView::dragEnterEvent( QDragEnterEvent * e ) { ItemView::dragEnterEvent(e); if ( e->isAccepted() ) return; //e->setAccepted( e->provides("ktechlab/mechanical") ); // 2019.04.14 e->setAccepted( e->mimeData()->hasFormat("ktechlab/mechanical") ); } diff --git a/src/micro/asminfo.cpp b/src/micro/asminfo.cpp index e1796126..f5aa6dd4 100644 --- a/src/micro/asminfo.cpp +++ b/src/micro/asminfo.cpp @@ -1,68 +1,68 @@ /*************************************************************************** * Copyright (C) 2003,2005 by David Saxton * * david@bluehaze.org * * * * 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 "asminfo.h" -#include +#include AsmInfo::AsmInfo() { } AsmInfo::~AsmInfo() { } void AsmInfo::addInstruction( const QString & operand, const QString & description, const QString & opcode ) { Instruction instruction; instruction.operand = operand; instruction.description = description; instruction.opcode = opcode; m_instructionList.append( instruction ); m_operandList.append( operand ); } QString AsmInfo::setToString( Set set ) { switch (set) { case AsmInfo::PIC12: return QString::fromLatin1("PIC12"); case AsmInfo::PIC14: return QString::fromLatin1("PIC14"); case AsmInfo::PIC16: return QString::fromLatin1("PIC16"); } qWarning() << Q_FUNC_INFO << "Unrecognized set="< +// #include // #include #include "picinfo12bit.h" #include "picinfo14bit.h" #include "picinfo16bit.h" #include "micropackage.h" #include // MicroLibrary * MicroLibrary::m_pSelf = nullptr; // static K3StaticDeleter staticMicroLibraryDeleter; Q_GLOBAL_STATIC( MicroLibrary, globalMicroLibrary); MicroLibrary * MicroLibrary::self() { return globalMicroLibrary; // if ( !m_pSelf ) // staticMicroLibraryDeleter.setObject( m_pSelf, new MicroLibrary() ); // return m_pSelf; } MicroLibrary::MicroLibrary() { addMicroInfo( new PicInfo12C508() ); addMicroInfo( new PicInfo12C509() ); addMicroInfo( new PicInfo16C54 () ); addMicroInfo( new PicInfo16C55() ); addMicroInfo( new PicInfo16C61() ); addMicroInfo( new PicInfo16C62() ); addMicroInfo( new PicInfo16C63() ); addMicroInfo( new PicInfo16C64() ); addMicroInfo( new PicInfo16F627() ); addMicroInfo( new PicInfo16F628() ); addMicroInfo( new PicInfo16C65() ); addMicroInfo( new PicInfo16C71() ); addMicroInfo( new PicInfo16C72() ); addMicroInfo( new PicInfo16C73() ); addMicroInfo( new PicInfo16C712() ); addMicroInfo( new PicInfo16C716() ); addMicroInfo( new PicInfo16C74() ); addMicroInfo( new PicInfo16C84() ); addMicroInfo( new PicInfo16CR83() ); addMicroInfo( new PicInfo16F83() ); addMicroInfo( new PicInfo16CR84() ); addMicroInfo( new PicInfo16F84() ); addMicroInfo( new PicInfo16F873() ); addMicroInfo( new PicInfo16F874() ); addMicroInfo( new PicInfo16F877() ); addMicroInfo( new PicInfo17C752() ); addMicroInfo( new PicInfo17C756() ); addMicroInfo( new PicInfo17C762() ); addMicroInfo( new PicInfo17C766() ); addMicroInfo( new PicInfo18C242() ); addMicroInfo( new PicInfo18C252() ); addMicroInfo( new PicInfo18C442() ); addMicroInfo( new PicInfo18C452() ); addMicroInfo( new PicInfo18F442() ); addMicroInfo( new PicInfo18F452() ); } MicroLibrary::~MicroLibrary() { const MicroInfoList::iterator end = m_microInfoList.end(); for ( MicroInfoList::iterator it = m_microInfoList.begin(); it != end; ++it ) { delete *it; } } MicroInfo *MicroLibrary::microInfoWithID( QString id ) { id = id.toUpper(); const MicroInfoList::iterator end = m_microInfoList.end(); for ( MicroInfoList::iterator it = m_microInfoList.begin(); it != end; ++it ) { if ( (*it)->id() == id ) return *it; } return nullptr; } void MicroLibrary::addMicroInfo( MicroInfo *microInfo ) { if (microInfo) m_microInfoList += microInfo; } QStringList MicroLibrary::microIDs( unsigned asmSet, unsigned gpsimSupport, unsigned flowCodeSupport, unsigned microbeSupport ) { QStringList ids; const MicroInfoList::iterator end = m_microInfoList.end(); for ( MicroInfoList::iterator it = m_microInfoList.begin(); it != end; ++it ) { MicroInfo * info = *it; if ( (info->instructionSet()->set() & asmSet) && (info->gpsimSupport() & gpsimSupport) && (info->flowcodeSupport() & flowCodeSupport) && (info->microbeSupport() & microbeSupport) ) ids << info->id(); } return ids; } diff --git a/src/micro/microlibrary.h b/src/micro/microlibrary.h index 13273d74..39b07626 100644 --- a/src/micro/microlibrary.h +++ b/src/micro/microlibrary.h @@ -1,56 +1,56 @@ /*************************************************************************** * Copyright (C) 2003-2005 by David Saxton * * david@bluehaze.org * * * * 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. * ***************************************************************************/ #ifndef MICROLIBRARY_H #define MICROLIBRARY_H #include "asminfo.h" #include "microinfo.h" -#include -#include -#include +#include +#include +#include class MicroInfo; class MicroLibrary; typedef QList MicroInfoList; inline MicroLibrary *microLibrary(); /** @short Stores all the avaiable PICs (info) @author David Saxton */ class MicroLibrary { public: static MicroLibrary * self(); ~MicroLibrary(); MicroInfo *microInfoWithID( QString id ); void addMicroInfo( MicroInfo *microInfo ); /** * Returns a list of micro ids with the given properties (OR'ed * together). */ QStringList microIDs( unsigned asmSet = AsmInfo::AsmSetAll, unsigned gpsimSupport = MicroInfo::AllSupport, unsigned flowCodeSupport = MicroInfo::AllSupport, unsigned microbeSupport = MicroInfo::AllSupport ); public: MicroLibrary(); private: // static MicroLibrary * m_pSelf; MicroInfoList m_microInfoList; friend MicroLibrary *microLibrary(); }; #endif diff --git a/src/micro/micropackage.cpp b/src/micro/micropackage.cpp index ed002b94..8f877614 100644 --- a/src/micro/micropackage.cpp +++ b/src/micro/micropackage.cpp @@ -1,109 +1,109 @@ /*************************************************************************** * Copyright (C) 2003 by David Saxton * * david@bluehaze.org * * * * 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 "micropackage.h" -#include +#include PicPin::PicPin() { pinID = QString::fromLatin1("INVALID"); type = PicPin::type_bidir; portName = QString::fromLatin1("INVALID"); portPosition = -1; } PicPin::PicPin( const QString &_pinID, PicPin::pin_type _type, const QString &_portName, int _portPosition ) { pinID = _pinID; type = _type; portName = _portName; portPosition = _portPosition; } MicroPackage::MicroPackage( const int pinCount ) { m_numPins = pinCount; } MicroPackage::~MicroPackage() { } void MicroPackage::assignPin( int pinPosition, PicPin::pin_type type, const QString& pinID, const QString& portName, int portPosition ) { if ( m_picPinMap.find(pinPosition) != m_picPinMap.end() ) { qCritical() << "PicDevice::assignBidirPin: Attempting to reset pin "< -#include +#include +#include -#include +#include /** @author David Saxton */ class PicPin { public: enum pin_type { type_input = 0x1, // Input only pin type_bidir = 0x2, // Bi-directional (input/output) type_open = 0x4, // Open collector type_vss = 0x8, // Voltage source type_vdd = 0x10, // Voltage drain type_mclr = 0x20, // Memory clear type_osc = 0x40 // Oscillator }; PicPin(); PicPin( const QString &_pinID, PicPin::pin_type _type, const QString &_portName = "", int _portPosition = -1 ); PicPin::pin_type type; QString pinID; // Id of pin, eg 'MCLR' // For bidir (io) pins QString portName; // Name of port, eg 'PORTA' int portPosition; // Position in port }; typedef QMap PicPinMap; /** @short Describes the PIC package (i.e. pins) @author David Saxton */ class MicroPackage { public: MicroPackage( const int pinCount ); virtual ~MicroPackage(); /** * Assigns a pin to a position in the package. */ void assignPin( int pinPosition, PicPin::pin_type type, const QString& pinID, const QString& portName = "", int portPosition = -1); void assignPin( int pinPosition, PicPin::pin_type type, const char* pinID, const char* portName = "", int portPosition = -1) { assignPin(pinPosition, type, QString::fromLatin1(pinID), QString::fromLatin1(portName), portPosition); } /** * Returns the pins of the given type(s). If portName is not specified, all pins will be returned; * not just those belonging to a given port. pin_type's can be OR'ed together * e.g. pins( PicPin::type_input | PicPin::type_bidir, "PORTA" ) will return all bidirectional or * input pins belonging to PORTA. If pinType is 0, then this will return all pins */ PicPinMap pins( uint pinType = 0, const QString& portName = "" ); /** * Returns just a QStringList of the pin ids. * @see pins( uint pinType, const QString& portName ) */ QStringList pinIDs( uint pinType = 0, const QString& portName = "" ); /** * Returns the number of pins of the given type(s) (which can be OR'ed together), with the given * port name if specified, while avoiding the construction of a new PicPinMap. if pinType is 0, * then all pin types are considered * @see pins */ int pinCount( uint pinType = 0, const QString& portName = "" ); /** * Returns a list of port names, eg 'PORTA', 'PORTB' for the package */ QStringList portNames() const { return m_portNames; } /** * Returns the number of ports */ uint portCount() const { return m_portNames.size(); } private: PicPinMap m_picPinMap; QStringList m_portNames; int m_numPins; }; #endif diff --git a/src/microsettings.h b/src/microsettings.h index 0ca0b53e..4cb89a3e 100644 --- a/src/microsettings.h +++ b/src/microsettings.h @@ -1,211 +1,211 @@ /*************************************************************************** * Copyright (C) 2003-2005 by David Saxton * * david@bluehaze.org * * * * 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. * ***************************************************************************/ #ifndef MICROSETTINGS_H #define MICROSETTINGS_H -#include -#include +#include +#include class QString; class QVariant; class MicroData; class MicroInfo; class VariableInfo { public: VariableInfo(); // Returns the value as a string QString valueAsString() const; // MicroSettings::VariableType (don't rely on this just yet...) int type; // Sets the value void setValue( const QVariant & value ); // If true, the variable will be initialised at the start of the FlowCode // to the given value bool initAtStart; // True if the variable was "created" by the user in the variable dialog, // as opposed to being from a variable name entry box bool permanent; private: QVariant value; }; typedef QMap< QString, VariableInfo > VariableMap; // Variable name, variable info /** @short Stores pic pin settings - type/state @author David Saxton */ class PinSettings : public QObject { Q_OBJECT public: enum pin_type { pt_input, pt_output }; enum pin_state { ps_on, ps_off }; PinSettings(); PinSettings( PinSettings::pin_type _type, PinSettings::pin_state _state, const QString &id, const QString &port ); PinSettings::pin_type type() const { return m_type; } PinSettings::pin_state state() const { return m_state; } QString id() const { return m_id; } QString port() const { return m_port; } void setType( PinSettings::pin_type type ); void setState( PinSettings::pin_state state ); signals: /** * Emitted when either the type or the state is changed. */ void settingsChanged(); private: PinSettings::pin_type m_type; PinSettings::pin_state m_state; QString m_id; QString m_port; }; typedef QList PinSettingsList; class PinMapping; typedef QMap< QString, PinMapping > PinMappingMap; typedef QMap< QString, PinSettingsList > PortList; /** This class stores PIC settings that are specific to the PIC program being devloped. This includes such things as port settings and variable settings. This is different from PIC info, which includes stuff such as PIC pin names @short Stores Pic settings - pin settings @author David Saxton */ class MicroSettings : public QObject { Q_OBJECT public: enum VariableType { vt_signedInteger, vt_unsignedInteger, vt_unknown }; MicroSettings( MicroInfo *microInfo ); ~MicroSettings() override; /** * Returns microdata to describe the microsettings. * This includes ports settins and variable settings */ MicroData microData() const; void restoreFromMicroData( const MicroData µData ); /** * Returns a pointer to the MicroInfo object for the PIC in use */ MicroInfo *microInfo() const { return _microInfo; } /** * Set the pin with the given id to the given initial type (input/output) */ void setPinType( const QString &id, PinSettings::pin_type type ); /** * Set the pin with the given id to the given initial state (on/off) */ void setPinState( const QString &id, PinSettings::pin_state state ); /** * Returns a pointer to the PinSettings for the pin with the given id, * or null if no such pin exists. */ PinSettings* pinWithID( const QString &id ); /** * Returns the initial port state (on/off) for the given port. * Each pin state occupies one bit of the returned integer. */ int portState( const QString &port ); /** * Sets the port with the given name to the given state */ void setPortState( const QString &port, int state ); /** * Sets the port with the given name to the given type */ void setPortType( const QString &port, int type ); /** * Returns the initial port type (intput/output) for the given port. * Each pin type occupies one bit of the returned integer. */ int portType( const QString &port ); /** * Sets the variable "name" to the initial value "value. If the variable * already exists, its value will be changed. Else, the variable will be * created. */ void setVariable( const QString &name, QVariant value, bool permanent = true ); /** * Returns the list of initial variables as a QStringList, just the names * without the values. Generated from the VariableMap m_variables. */ QStringList variableNames(); /** * Returns a pointer to the variable info with the given name, or nullptr * if the variable is not found */ VariableInfo *variableInfo( const QString &name ); /** * Deletes the variable with the given name, returns true if successul * (i.e. a variable with that name existed), or false if not */ bool deleteVariable( const QString &name ); /** * Removes all variables */ void removeAllVariables(); /** * Sets the list of Pin Mappings to that given. */ void setPinMappings( const PinMappingMap & pinMappings ); /** * Returns the pic pin mapping with the given id. */ PinMapping pinMapping( const QString & id ) const; /** * Returns the list of different Pin Mappings; */ PinMappingMap pinMappings() const; signals: void pinMappingsChanged(); private: PinMappingMap m_pinMappings; PinSettingsList m_pinSettingsList; MicroInfo *_microInfo; VariableMap m_variableMap; PortList m_ports; }; #endif diff --git a/src/node.cpp b/src/node.cpp index cf7be02c..bd1fcb17 100644 --- a/src/node.cpp +++ b/src/node.cpp @@ -1,174 +1,173 @@ /*************************************************************************** * Copyright (C) 2003-2005 by David Saxton * * david@bluehaze.org * * * * 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 "cnitem.h" #include "icndocument.h" #include "connector.h" #include "itemdocumentdata.h" #include "node.h" -#include - -#include +#include +#include QColor Node::m_selectedColor = QColor( 101, 134, 192 ); Node::Node( ICNDocument *icnDocument, Node::node_type type, int dir, const QPoint &pos, QString *id ) : //QObject(), KtlQCanvasPolygon( icnDocument ? icnDocument->canvas() : nullptr ) { QString name("Node"); if (id) { name.append(QString("-%1").arg(*id)); } else { name.append("-Unknown"); } setObjectName(name.toLatin1().data()); qDebug() << Q_FUNC_INFO << " this=" << this; m_length = 8; p_nodeGroup = nullptr; p_parentItem = nullptr; b_deleted = false; m_dir = dir; m_type = type; p_icnDocument = icnDocument; m_level = 0; if ( p_icnDocument ) { if (id) { m_id = *id; if ( !p_icnDocument->registerUID(*id) ) qCritical() << Q_FUNC_INFO << "Could not register id " << *id << endl; } else m_id = p_icnDocument->generateUID("node"+QString::number(type)); } initPoints(); move( pos.x(), pos.y() ); setBrush( Qt::black ); setPen( QPen( Qt::black ) ); show(); emit (moved(this)); } Node::~Node() { if ( p_icnDocument ) p_icnDocument->unregisterUID( id() ); } void Node::setLevel( const int level ) { m_level = level; } void Node::setLength( int length ) { if ( m_length == length ) return; m_length = length; initPoints(); } void Node::setOrientation( int dir ) { if ( m_dir == dir ) return; m_dir = dir; initPoints(); } void Node::initPoints() { int l = m_length; // Bounding rectangle, facing right QPolygon pa( QRect( 0, -8, l, 16 ) ); QMatrix m; m.rotate( m_dir ); pa = m.map(pa); setPoints(pa); } void Node::setVisible( bool yes ) { if ( isVisible() == yes ) return; KtlQCanvasPolygon::setVisible(yes); } void Node::setParentItem( CNItem *parentItem ) { if (!parentItem) { qCritical() << Q_FUNC_INFO << "no parent item" << endl; return; } p_parentItem = parentItem; setLevel(p_parentItem->level()); connect( p_parentItem, SIGNAL(movedBy(double, double )), this, SLOT(moveBy(double, double)) ); connect( p_parentItem, SIGNAL(removed(Item*)), this, SLOT(removeNode(Item*)) ); } void Node::removeNode() { if (b_deleted) return; b_deleted = true; emit removed(this); p_icnDocument->appendDeleteList(this); } void Node::setICNDocument(ICNDocument *documentPtr) { p_icnDocument = documentPtr; } void Node::moveBy( double dx, double dy ) { if ( dx == 0 && dy == 0 ) return; KtlQCanvasPolygon::moveBy( dx, dy ); emit moved(this); } NodeData Node::nodeData() const { NodeData data; data.x = x(); data.y = y(); return data; } void Node::setNodeSelected( bool yes ) { if ( isSelected() == yes ) return; KtlQCanvasItem::setSelected(yes); setPen( yes ? m_selectedColor : Qt::black ); setBrush( yes ? m_selectedColor : Qt::black ); } void Node::initPainter( QPainter & p ) { p.translate( int(x()), int(y()) ); p.rotate( m_dir ); } void Node::deinitPainter( QPainter & p ) { p.rotate( -m_dir ); p.translate( -int(x()), -int(y()) ); } diff --git a/src/node.h b/src/node.h index 7ca28809..d804798f 100644 --- a/src/node.h +++ b/src/node.h @@ -1,224 +1,224 @@ /*************************************************************************** * Copyright (C) 2003-2005 by David Saxton * * david@bluehaze.org * * * * 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. * ***************************************************************************/ #ifndef NODE_H #define NODE_H //#include // 2018.10.16 - not needed #include "canvasitems.h" -#include +#include class CNItem; class Item; class ICNDocument; class ICNDocument; class Connector; class Node; class NodeData; class NodeGroup; class QTimer; typedef QList > ConnectorList; typedef QList > NodeList; /** @short A standard node that can be associated with a Connector or a CNItem @author David Saxton */ class Node : /* public QObject, */ public KtlQCanvasPolygon { Q_OBJECT public: // this shall disappear one day /** * Used for run-time identification of the node: * Can be electronic node (so has values of current, voltage, etc) * or a pic part node * this enum will be cleared soon */ enum node_type { ec_pin, ec_junction, fp_in, fp_out, fp_junction }; /** * @param dir the direction of the node; 0 degrees for left, 90 degrees for * up, etc in an anti-clockwise direction. An "up" node has the * wire-connection point at the top and the (component/flowpart)-end at the * bottom. */ Node( ICNDocument *icnDocument, Node::node_type type, int dir, const QPoint &pos, QString *id = nullptr ); ~Node() override; /** * Sets the node's visibility, as well as updating the visibility of the * attached connectors as appropriate */ void setVisible( bool yes ) override; /** * Returns the global id, that is unique to the node * amongst all the nodes on the canvas */ const QString id() const { return m_id; } /** * Returns the id that is internal to the CNItem to which the * node belongs to. Returns a null QString if no parentitem */ const QString childId() const { return m_childId; } /** * Use this function to set the child-id, that is unique to the node * amongst the other nodes associated with its parent CNItem */ void setChildId( const QString &id ) { m_childId = id; } /** * Sets the "level" of the node. By default, the level is 0. The level of * the node tells the node what CNItems it can be connected to through * a connector. * @see level */ virtual void setLevel( const int level ); /** * Returns the level of the nodes * @see setLevel */ int level() const { return m_level; } /** * Sets the orientation of the node. */ void setOrientation( int dir ); /** * Changes the lenght of the node. By default, this is 8. Some node types * (such as junctions) do not make use of this value. */ void setLength( int length ); /** * Associates a CNItem with the node - ie the node belongs to the CNItem, * and hence gets deleted when the CNItem gets deleted.s */ virtual void setParentItem( CNItem *parentItem ); /** * Returns true if the node is part of a CNItem * (i.e. not between multiple connectors) */ bool isChildNode() const { return (p_parentItem != nullptr); } /** * Returns a pointer to the CNItem to which the node belongs, * or Null if it doesn't. */ CNItem *parentItem() const { return p_parentItem; } NodeData nodeData() const; void setNodeGroup( NodeGroup *ng ) { p_nodeGroup = ng; } NodeGroup *nodeGroup() const { return p_nodeGroup; } /* interface common to ecnode and fpnode; these might be required by ItemDocumentData, ICNDocument */ virtual bool isConnected( Node *node, NodeList *checkedNodes = nullptr ) = 0; virtual void removeConnector( Connector *connector ) = 0; /** * Returns the total number of connections to the node. This is the number * of connectors and the parent * item connector if it exists and is requested. * @param includeParentItem Count the parent item as a connector if it exists * @param includeHiddenConnectors hidden connectors are those as e.g. part of a subcircuit */ virtual int numCon( bool includeParentItem, bool includeHiddenConnectors ) const = 0; /** * @return the list of all the connectors attached to the node */ virtual ConnectorList getAllConnectors() const = 0; /** * For a flownode: returns the first input connector, if it exist, or the fist outptut connector, if it exists. * For an electric node: returns the first connector * If the node isn't connected to anyithing, returns null ( 0 ) * @return pointer to the desired connector */ virtual Connector* getAConnector() const = 0; /** * Removes all the NULL connectors */ virtual void removeNullConnectors() = 0; /** * Draw shape. Note that this has to remain public. */ void drawShape( QPainter &p ) override = 0; void setICNDocument(ICNDocument *documentPtr); public slots: void moveBy( double dx, double dy ) override; void removeNode(Item*) { removeNode(); } void removeNode(); void setNodeSelected( bool yes ); signals: void moved( Node *node ); /** * Emitted when the CNItem is removed. Normally, this signal is caught by associated * nodes, who will remove themselves as well. */ void removed( Node* node ); protected: virtual void initPoints(); /** * Moves and rotates (according to m_dir) the painter, so that our current * position is (0,0). */ void initPainter( QPainter & p ); /** * Undoes the effects of initPainter. */ void deinitPainter( QPainter & p ); /** If this node has precisely two connectors emerging from it, then this * function will trace the two connectors until the point where they * diverge; this point is returned. */ virtual QPoint findConnectorDivergePoint( bool * found ) = 0; /** The node's type. This member will be removed! */ node_type m_type; int m_dir; int m_length; int m_level; ICNDocument *p_icnDocument; CNItem *p_parentItem; NodeGroup *p_nodeGroup; static QColor m_selectedColor; private: // these fields are critical to saved circuit documents. QString m_id; QString m_childId; bool b_deleted; }; #endif diff --git a/src/nodegroup.cpp b/src/nodegroup.cpp index 624d9e5a..c059942e 100644 --- a/src/nodegroup.cpp +++ b/src/nodegroup.cpp @@ -1,575 +1,575 @@ /*************************************************************************** * Copyright (C) 2004-2005 by David Saxton * * david@bluehaze.org * * * * 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 "icndocument.h" #include "connector.h" #include "conrouter.h" #include "node.h" #include "nodegroup.h" -#include +#include #include #include NodeGroup::NodeGroup( ICNDocument *icnDocument, const char *name ) : QObject( icnDocument /*, name */ ) { setObjectName(name); p_icnDocument = icnDocument; b_visible = true; } NodeGroup::~NodeGroup() { clearConList(); m_extNodeList.removeAll( (Node*)nullptr ); const NodeList::iterator xnEnd = m_extNodeList.end(); for ( NodeList::iterator it = m_extNodeList.begin(); it != xnEnd; ++it ) (*it)->setNodeGroup(nullptr); m_extNodeList.clear(); m_nodeList.removeAll( (Node*)nullptr ); const NodeList::iterator nEnd = m_nodeList.end(); for ( NodeList::iterator it = m_nodeList.begin(); it != nEnd; ++it ) (*it)->setNodeGroup(nullptr); m_nodeList.clear(); } void NodeGroup::setVisible( bool visible ) { if ( b_visible == visible ) return; b_visible = visible; m_nodeList.removeAll( (Node*)nullptr ); const NodeList::iterator nEnd = m_nodeList.end(); for ( NodeList::iterator it = m_nodeList.begin(); it != nEnd; ++it ) (*it)->setVisible(visible); } void NodeGroup::addNode( Node *node, bool checkSurrouding ) { if ( !node || node->isChildNode() || m_nodeList.contains(node) ) { return; } m_nodeList.append(node); node->setNodeGroup(this); node->setVisible(b_visible); if (checkSurrouding) { ConnectorList con = node->getAllConnectors(); ConnectorList::iterator end = con.end(); for ( ConnectorList::iterator it = con.begin(); it != end; ++it ) { if (*it) { // maybe we can put here a check, because only 1 of there checks should pass if( (*it)->startNode() != node ) addNode( (*it)->startNode(), true ); if( (*it)->endNode() != node ) addNode( (*it)->endNode(), true ); } } } } void NodeGroup::translate( int dx, int dy ) { if ( (dx == 0) && (dy == 0) ) return; m_conList.removeAll((Connector*)nullptr); m_nodeList.removeAll((Node*)nullptr); const ConnectorList::iterator conEnd = m_conList.end(); for ( ConnectorList::iterator it = m_conList.begin(); it != conEnd; ++it ) { (*it)->updateConnectorPoints(false); (*it)->translateRoute( dx, dy ); } const NodeList::iterator nodeEnd = m_nodeList.end(); for ( NodeList::iterator it = m_nodeList.begin(); it != nodeEnd; ++it ) (*it)->moveBy( dx, dy ); } void NodeGroup::updateRoutes() { resetRoutedMap(); // Basic algorithm used here: starting with the external nodes, find the // pair with the shortest distance between them. Route the connectors // between the two nodes appropriately. Remove that pair of nodes from the // list, and add the nodes along the connector route (which have been spaced // equally along the route). Repeat until all the nodes are connected. const ConnectorList::iterator conEnd = m_conList.end(); for ( ConnectorList::iterator it = m_conList.begin(); it != conEnd; ++it ) { if (*it) (*it)->updateConnectorPoints(false); } Node *n1, *n2; NodeList currentList = m_extNodeList; while ( !currentList.isEmpty() ) { findBestPair( ¤tList, &n1, &n2 ); if ( n1 == nullptr || n2 == nullptr ) { return; } NodeList route = findRoute( n1, n2 ); currentList += route; ConRouter cr(p_icnDocument); cr.mapRoute( (int)n1->x(), (int)n1->y(), (int)n2->x(), (int)n2->y() ); if (cr.pointList(false).size() <= 0) { qDebug() << Q_FUNC_INFO << "no ConRouter points, giving up"; return; // continue might get to an infinite loop } QPointListList pl = cr.dividePoints( route.size()+1 ); const NodeList::iterator routeEnd = route.end(); const QPointListList::iterator plEnd = pl.end(); Node *prev = n1; NodeList::iterator routeIt = route.begin(); for ( QPointListList::iterator it = pl.begin(); it != plEnd; ++it ) { Node *next = (routeIt == routeEnd) ? n2 : (Node*)*(routeIt++); removeRoutedNodes( ¤tList, prev, next ); QPointList pointList = *it; if ( prev != n1 ) { QPoint first = pointList.first(); prev->moveBy( first.x() - prev->x(), first.y() - prev->y() ); } Connector *con = findCommonConnector( prev, next ); if (con) { con->updateConnectorPoints(false); con->setRoutePoints( pointList, false, false ); con->updateConnectorPoints(true); // con->conRouter()->setPoints( &pointList, con->startNode() != prev ); // con->conRouter()->setPoints( &pointList, con->pointsAreReverse( &pointList ) ); // con->calcBoundingPoints(); } prev = next; } } } NodeList NodeGroup::findRoute( Node *startNode, Node *endNode ) { NodeList nl; if ( !startNode || !endNode || startNode == endNode ) { return nl; } IntList temp; IntList il = findRoute( temp, getNodePos(startNode), getNodePos(endNode) ); const IntList::iterator end = il.end(); for ( IntList::iterator it = il.begin(); it != end; ++it ) { Node *node = getNodePtr(*it); if (node) { nl += node; } } nl.removeAll(startNode); nl.removeAll(endNode); return nl; } IntList NodeGroup::findRoute( IntList used, int currentNode, int endNode, bool *success ) { bool temp; if (!success) { success = &temp; } *success = false; if ( !used.contains(currentNode) ) { used.append(currentNode); } if ( currentNode == endNode ) { *success = true; return used; } const uint n = m_nodeList.size()+m_extNodeList.size(); for ( uint i=0; igetAllConnectors(); ConnectorList n2Con = n2->getAllConnectors(); const ConnectorList::iterator end = n1Con.end(); for ( ConnectorList::iterator it = n1Con.begin(); it != end; ++it ) { if ( n2Con.contains(*it) ) { return *it; } } return nullptr; } void NodeGroup::findBestPair( NodeList *list, Node **n1, Node **n2 ) { *n1 = nullptr; *n2 = nullptr; if ( list->size() < 2 ) { return; } const NodeList::iterator end = list->end(); int shortest = 1<<30; // Try and find any that are aligned horizontally for ( NodeList::iterator it1 = list->begin(); it1 != end; ++it1 ) { NodeList::iterator it2 = it1; for ( ++it2; it2 != end; ++it2 ) { if ( *it1 != *it2 && (*it1)->y() == (*it2)->y() && canRoute( *it1, *it2 ) ) { const int distance = std::abs(int( (*it1)->x()-(*it2)->x() )); if ( distance < shortest ) { shortest = distance; *n1 = *it1; *n2 = *it2; } } } } if (*n1) { return; } // Try and find any that are aligned vertically for ( NodeList::iterator it1 = list->begin(); it1 != end; ++it1 ) { NodeList::iterator it2 = it1; for ( ++it2; it2 != end; ++it2 ) { if ( *it1 != *it2 && (*it1)->x() == (*it2)->x() && canRoute( *it1, *it2 ) ) { const int distance = std::abs(int( (*it1)->y()-(*it2)->y() )); if ( distance < shortest ) { shortest = distance; *n1 = *it1; *n2 = *it2; } } } } if (*n1) { return; } // Now, lets just find the two closest nodes for ( NodeList::iterator it1 = list->begin(); it1 != end; ++it1 ) { NodeList::iterator it2 = it1; for ( ++it2; it2 != end; ++it2 ) { if ( *it1 != *it2 && canRoute( *it1, *it2 ) ) { const int dx = (int)((*it1)->x()-(*it2)->x()); const int dy = (int)((*it1)->y()-(*it2)->y()); const int distance = std::abs(dx) + std::abs(dy); if ( distance < shortest ) { shortest = distance; *n1 = *it1; *n2 = *it2; } } } } if (!*n1) { qCritical() << "NodeGroup::findBestPair: Could not find a routable pair of nodes!"<contains(node) ) { return; } reachable->append(node); const uint n = m_nodeList.size() + m_extNodeList.size(); assert( node < int(n) ); for ( uint i=0; istartNode()); int n2 = getNodePos(con->endNode()); if ( n1 != -1 && n2 != -1 ) { b_routedMap[n1*n + n2] = b_routedMap[n2*n + n1] = true; } } } } void NodeGroup::removeRoutedNodes( NodeList *nodes, Node *n1, Node *n2 ) { if (!nodes) { return; } // Lets get rid of any duplicate nodes in nodes (as a general cleaning operation) const NodeList::iterator end = nodes->end(); for ( NodeList::iterator it = nodes->begin(); it != end; ++it ) { //if ( nodes->contains(*it) > 1 ) { if ( nodes->count(*it) > 1 ) { *it = nullptr; } } nodes->removeAll((Node*)nullptr); const int n1pos = getNodePos(n1); const int n2pos = getNodePos(n2); if ( n1pos == -1 || n2pos == -1 ) { return; } const uint n = m_nodeList.size() + m_extNodeList.size(); b_routedMap[n1pos*n + n2pos] = b_routedMap[n2pos*n + n1pos] = false; bool n1HasCon = false; bool n2HasCon = false; for ( uint i=0; iremoveAll(n1); } if (!n2HasCon) { nodes->removeAll(n2); } } int NodeGroup::getNodePos( Node *n ) { if (!n) { return -1; } int pos = m_nodeList.indexOf(n); if ( pos != -1 ) { return pos; } pos = m_extNodeList.indexOf(n); if ( pos != -1 ) { return pos+m_nodeList.size(); } return -1; } Node* NodeGroup::getNodePtr( int n ) { if ( n<0 ) { return nullptr; } const int a = m_nodeList.size(); if (nsetNodeGroup(nullptr); disconnect( con, SIGNAL(removed(Connector*)), this, SLOT(connectorRemoved(Connector*)) ); } } m_conList.clear(); } void NodeGroup::init() { NodeList::iterator xnEnd = m_extNodeList.end(); for ( NodeList::iterator it = m_extNodeList.begin(); it != xnEnd; ++it ) { (*it)->setNodeGroup(nullptr); } m_extNodeList.clear(); clearConList(); // First, lets get all of our external nodes and internal connectors const NodeList::iterator nlEnd = m_nodeList.end(); for ( NodeList::iterator nodeIt = m_nodeList.begin(); nodeIt != nlEnd; ++nodeIt ) { // 2. rewrite ConnectorList conList = ( *nodeIt )->getAllConnectors(); ConnectorList::iterator conIt, conEnd = conList.end(); for ( conIt = conList.begin(); conIt != conEnd; ++conIt ) { Connector *con = *conIt; // possible check: only 1 of these ifs should be true if ( con->startNode() != *nodeIt ) { addExtNode ( con->startNode() ); if ( !m_conList.contains ( con ) ) { m_conList += con; con->setNodeGroup ( this ); } } if ( con->endNode() != *nodeIt ) { addExtNode ( con->endNode() ); if ( !m_conList.contains ( con ) ) { m_conList += con; con->setNodeGroup ( this ); } } connect ( con, SIGNAL ( removed ( Connector* ) ), this, SLOT ( connectorRemoved ( Connector* ) ) ); } // Connect the node up to us connect ( *nodeIt, SIGNAL ( removed ( Node* ) ), this, SLOT ( nodeRemoved ( Node* ) ) ); } // And connect up our external nodes xnEnd = m_extNodeList.end(); for ( NodeList::iterator it = m_extNodeList.begin(); it != xnEnd; ++it ) { // connect( *it, SIGNAL(moved(Node*)), this, SLOT(extNodeMoved()) ); connect( *it, SIGNAL(removed(Node*)), this, SLOT(nodeRemoved(Node*)) ); } } void NodeGroup::nodeRemoved( Node *node ) { // We are probably about to get deleted by ICNDocument anyway...so no point in doing anything m_nodeList.removeAll(node); node->setNodeGroup(nullptr); node->setVisible(true); m_extNodeList.removeAll(node); } void NodeGroup::connectorRemoved( Connector *connector ) { m_conList.removeAll(connector); } void NodeGroup::addExtNode( Node *node ) { if ( !m_extNodeList.contains(node) && !m_nodeList.contains(node) ) { m_extNodeList.append(node); node->setNodeGroup(this); } } diff --git a/src/nodegroup.h b/src/nodegroup.h index 1cb1e6db..9185ac08 100644 --- a/src/nodegroup.h +++ b/src/nodegroup.h @@ -1,145 +1,145 @@ /*************************************************************************** * Copyright (C) 2003-2004 by David Saxton * * david@bluehaze.org * * * * 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. * ***************************************************************************/ #ifndef NODEGROUP_H #define NODEGROUP_H -#include -#include -#include +#include +#include +#include #include class ICNDocument; class Connector; class ConRouter; class Node; class NodeGroup; class QTimer; typedef QList IntList; typedef QList NodeGroupList; typedef QList > NodeList; /** Controls a group of nodes who are not attached to any CNItems (poor things!) along with their associated connectors. @author David Saxton */ class NodeGroup : public QObject { Q_OBJECT public: NodeGroup( ICNDocument *icnDocument, const char *name = nullptr); ~NodeGroup() override; /** * Adds a node to the group (this checks to make sure that the node is not * a child node). If checkSurrouding is true, then surrounding nodes will be * checked to see if they are valid for inclusion - and if so, include them. */ void addNode( Node *node, bool checkSurrouding ); /** * Returns the list of internal nodes */ NodeList internalNodeList() const { return m_nodeList; } /** * Returns the list of external nodes */ NodeList externalNodeList() const { return m_extNodeList; } /** * Returns the list of connectors */ ConnectorList connectorList() const { return m_conList; } /** * Translates the routes by the given amount */ void translate( int dx, int dy ); void init(); /** * @returns true if node is an internal node to this group */ bool contains( Node *node ) const { return m_nodeList.contains(node); } /** * Reroute the NodeGroup. This function should only ever be called by * ICNDocument::rerouteInvalidatedConnectors(), as it is important that * there is only ever one entity controlling the routing of connectors. */ void updateRoutes(); /** * Sets the visibility of all nodes in the group. */ void setVisible( bool visible ); public slots: /** * Called when an internal or external node is deleted */ void nodeRemoved( Node *node ); /** * Called when a connector is removed */ void connectorRemoved( Connector *connector ); protected: void clearConList(); /** * Finds the common connector between two nodes */ Connector* findCommonConnector( Node *n1, Node *n2 ); /** * Find the best pair of nodes in the given list to route between. These * will be nodes that give a ncie path (e.g. if they're aligned horizontally * or vertically), or otherwise the closest such pair. The two nodes will be * returned in n1 and n2. */ void findBestPair( NodeList *list, Node **n1, Node **n2 ); /** * Finds the nodes along the route with the given start and end nodes (which * will be unique). The end nodes are not included in the returned list. */ NodeList findRoute( Node *startNode, Node *endNode ); ConnectorList m_conList; NodeList m_nodeList; NodeList m_extNodeList; ICNDocument *p_icnDocument; QBitArray b_routedMap; // Routes between different nodes bool b_visible; private: IntList findRoute( IntList used, int currentNode, int endNode, bool *success = nullptr ); void resetRoutedMap(); /** * Looks at b_routedMap as well as the connectors coming out of nodes, and * removes the nodes from the given list that have all of their connectors * routed. */ void removeRoutedNodes( NodeList *nodes, Node *n1, Node *n2 ); void addExtNode( Node *node ); /** * Looks at b_mappedRoute to see if there is a completely unrouted set of * connectors between the two given nodes; */ bool canRoute( Node *n1, Node *n2 ); void getReachable( IntList *reachable, int node ); /** * Either: position of node in m_nodeList, * or: (position of node in m_extNodeList) + m_nodeList.size() * or: -1 */ int getNodePos( Node *n ); /** * Essentially the inverse of getNodePos */ Node* getNodePtr( int n ); }; #endif diff --git a/src/oscilloscopedata.h b/src/oscilloscopedata.h index 78e2dc2f..c86a31d9 100644 --- a/src/oscilloscopedata.h +++ b/src/oscilloscopedata.h @@ -1,205 +1,205 @@ /*************************************************************************** * Copyright (C) 2005 by David Saxton * * david@bluehaze.org * * * * 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. * ***************************************************************************/ #ifndef OSCILLOSCOPEDATA_H #define OSCILLOSCOPEDATA_H -#include -#include +#include +#include #include #include #define DATA_CHUNK_SIZE (8192/sizeof(T)) /* #define DATA_CHUNK_ARRAY_SIZE ((8192-sizeof(uint32_t))/sizeof(DataChunk*)) // Allow a minimum of 64 megabytes of stored data (67108864 bytes) /// \todo The maximum allowed amount of stored data should be configurable or /// more intelligent (e.g. taking into account the number of probes or the /// amount of physical memory on the system). #define DCARRAY_ARRAY_SIZE ((67108864/(8192*DATA_CHUNK_ARRAY_SIZE))+1) */ /** max number of samples that a probe can hold */ #define MAX_PROBE_DATA_SIZE ( 1 * 1024 * 1024 ) // TODO ^ should be configurable /** For use in LogicProbe: Every time the input changes state, the new input state is recorded in value, along with the simulator time that it occurs at. */ class LogicDataPoint { public: LogicDataPoint() : value(0), time(0) { } LogicDataPoint( bool v, uint64_t t) : value(v), time(t) {} bool value : 1; uint64_t time : 63; }; /** @author David Saxton */ class ProbeData : public QObject { Q_OBJECT public: ProbeData( int id); ~ProbeData() override; /** * @returns unique id for oscilloscope, set on construction */ int id() const { return m_id; } /** * Set the proportion (0 = top, 1 = bottom) of the way down the * oscilloscope view that the probe output is drawn. If the proportion * is out of range ( <0, or >1), then the drawPosition is set to 0/1 */ void setDrawPosition( float drawPosition) { m_drawPosition = drawPosition; } /** * Returns the draw position. Default is 0.5. * @see setDrawPosition */ float drawPosition() const { return m_drawPosition; } /** * Set the colour that is used to display the probe in the oscilloscope. * Default is black. */ void setColor( QColor color); /** * @returns the colour that is used to display the probe in the oscilloscope */ QColor color() const { return m_color; } // /** // * Will not record any data when paused // */ // void setPaused( bool isPaused) { b_isPaused = isPaused; } /** * Returns the time (in Simulator time) that this probe was created at, * or last reset. */ uint64_t resetTime() const { return m_resetTime; } /** * Erases all recorded data, and sets m_resetTime to the current * simulator time. */ virtual void eraseData() = 0; /** * Searches for and returns the position of the last DataPoint that was * added before or at the given Simulator time. If no DataPoints were * were recorded before the given time, then will return the one closest * to the given time. Will return 0 if no DataPoints have been recorded * yet. */ virtual uint64_t findPos( uint64_t time) const = 0; signals: /** * Emitted when an attribute that affects how the probe is drawn in the * oscilloscope is changed. */ void displayAttributeChanged(); protected: const int m_id; float m_drawPosition; uint64_t m_resetTime; QColor m_color; }; /** @author David Saxton */ class LogicProbeData : public ProbeData { public: LogicProbeData( int id); ~LogicProbeData() override { delete m_data; } /** * Appends the data point to the set of data. */ void addDataPoint( LogicDataPoint data); /* { m_data->push_back(data); } */ // 2016.05.06 - moved to cpp void eraseData() override; uint64_t findPos( uint64_t time) const override; bool isEmpty() const { return m_data->size() == 0; } protected: std::vector *m_data; friend class OscilloscopeView; }; /** @author David Saxton */ class FloatingProbeData : public ProbeData { public: enum Scaling { Linear, Logarithmic }; FloatingProbeData( int id); /** * Appends the data point to the set of data. */ void addDataPoint( float data) ; // { m_data->push_back(data); } // 2016.05.06 - moved to cpp /** * Converts the insert position to a Simulator time. */ uint64_t toTime( uint64_t at) const; /** * Sets the scaling to use in the oscilloscope display. */ void setScaling( Scaling scaling); /** * @return the scaling used for the oscilloscope display. */ Scaling scaling() const { return m_scaling; } /** * Sets the value to use as the upper absolute value in the display. */ void setUpperAbsValue( double upperAbsValue); /** * @return the upper absolute value to use in the display. */ double upperAbsValue() const { return m_upperAbsValue; } /** * Sets the value to use as the lower absolute value in the display * (this is only used with logarithmic scaling). */ void setLowerAbsValue( double lowerAbsValue); /** * @return the lower absolute value to use in the display (this is * only used with logarithmic scaling). */ double lowerAbsValue() const { return m_lowerAbsValue; } void eraseData() override; uint64_t findPos( uint64_t time) const override; bool isEmpty() const { return m_data->size() == 0; } protected: Scaling m_scaling; double m_upperAbsValue; double m_lowerAbsValue; std::vector *m_data; friend class OscilloscopeView; }; #endif diff --git a/src/picitem.cpp b/src/picitem.cpp index e253d58a..38c6a57b 100644 --- a/src/picitem.cpp +++ b/src/picitem.cpp @@ -1,470 +1,471 @@ /*************************************************************************** * Copyright (C) 2003-2005 by David Saxton * * david@bluehaze.org * * * * 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 "canvasitemparts.h" #include "flowcodedocument.h" #include "microinfo.h" #include "microsettings.h" #include "microsettingsdlg.h" #include "micropackage.h" #include "picitem.h" #include "eventinfo.h" -#include #include -#include -#include +#include +#include +#include +#include static const int InnerWidth = 88; static const int SidePadding = 24; static const int TopPadding = 36; static const int BottomPadding = 42; static const int PinWidth = 12; static const int PinSeparation = 16 - PinWidth; static const int PinLength = 8; static const int ArcWidth = 22; static const int PinDirArrow = 3; //BEGIN class PinItem PinItem::PinItem( FlowCodeDocument* _view, QPoint position, bool _onLeft, PinSettings * pinSettings ) : KtlQCanvasRectangle(nullptr) { m_pinSettings = pinSettings; view = _view; onLeft = _onLeft; connect( m_pinSettings, SIGNAL(settingsChanged()), this, SLOT(updateDrawing()) ); if ( QFontInfo(m_font).pixelSize() > 11 ) // It has to be > 11, not > 12, as (I think) pixelSize() rounds off the actual size m_font.setPixelSize(12); setCanvas( view->canvas() ); move ( position.x(), position.y() ); initItem(); setZ( (ICNDocument::Z::RaisedItem + ICNDocument::Z::ResizeHandle)/2 + 1 ); // Hackish, but whatever } void PinItem::updateDrawing() { update(); } void PinItem::initItem() { setSize( PinLength, PinWidth ); setSelected(false); setPen( QPen( Qt::black ) ); calcTextRect(); show(); } void PinItem::drawShape( QPainter& p ) { if (!m_pinSettings) return; if ( m_pinSettings->state() == PinSettings::ps_on ) { if ( m_pinSettings->type() == PinSettings::pt_output ) setBrush( QColor( 255, 127, 127 ) ); else setBrush( QColor( 255, 191, 191 ) ); } else setBrush( Qt::white ); p.drawRect(rect()); p.setFont(m_font); p.setBrush( Qt::NoBrush ); QRect r = m_textRect; if ( onLeft ) p.drawText( r, Qt::AlignLeft, m_pinSettings->id() ); else p.drawText( r, Qt::AlignRight, m_pinSettings->id() ); //QRect br = p.boundingRect( r, Qt::AlignLeft, m_pinSettings->id() ); // 2017.10.01 - comment out unused variable int left; int right; if ( onLeft ) { right = (int)x(); left = right - 8; } else { left = (int)x() + PinLength; right = left + 8; } int midY = (int)y() + PinWidth/2; QPolygon pa(3); int midLeft = left + (8-PinDirArrow)/2; int midRight = left + (8+PinDirArrow)/2; if ( onLeft ) { midLeft--; midRight--; } else { midLeft++; midRight++; } p.setBrush( Qt::black ); // Right facing arrow if ( (m_pinSettings->type() == PinSettings::pt_input && onLeft) || (m_pinSettings->type() == PinSettings::pt_output && !onLeft) ) { pa[0] = QPoint( midRight, midY ); pa[1] = QPoint( midLeft, midY - PinDirArrow ); pa[2] = QPoint( midLeft, midY + PinDirArrow ); p.drawPolygon(pa); p.drawLine ( left, midY, right, midY ); } else // Left facing arrow { pa[0] = QPoint( midLeft, midY ); pa[1] = QPoint( midRight, midY - PinDirArrow ); pa[2] = QPoint( midRight, midY + PinDirArrow ); p.drawPolygon(pa); p.drawLine ( left, midY, right, midY ); } } QRect PinItem::boundingRect () const { QRect r = m_textRect; if ( onLeft ) r.setLeft( (int)x() - 10 ); else r.setRight( (int)x() + PinLength + 10 ); return r; } QString PinItem::id() { return m_pinSettings->id(); } void PinItem::switchState() { if ( m_pinSettings->state() == PinSettings::ps_on ) m_pinSettings->setState(PinSettings::ps_off); else m_pinSettings->setState(PinSettings::ps_on); update(); } void PinItem::dragged( int dx ) { if ( (onLeft && dx > 0) || (!onLeft && dx < 0) ) { m_pinSettings->setType(PinSettings::pt_input); } else m_pinSettings->setType(PinSettings::pt_output); update(); } void PinItem::moveBy ( double dx, double dy ) { KtlQCanvasRectangle::moveBy( dx, dy ); calcTextRect(); } void PinItem::calcTextRect() { m_textRect = rect(); m_textRect.moveTop( m_textRect.top()-2 ); QRect br; // QWidget tmpWidget; // //tmpWidget.setAttribute(Qt::WA_PaintOutsidePaintEvent, true); //note: add this if needed // //QPainter p(&tmpWidget); // 2016.05.03 - initialize painter explicitly // QPainter p; // const bool isBeginSuccess = p.begin(&tmpWidget); // { // qWarning() << Q_FUNC_INFO << " painter not active"; // } // // p.setFont(m_font); QFontMetrics fontMetrics( m_font ); if (!m_pinSettings) { qDebug() << "PinItem::textRect: No pinSettings!"<id() ); // 2016.05.03 - do not create dummy widget br = fontMetrics.boundingRect( m_textRect, Qt::AlignLeft, m_pinSettings->id() ); } else { m_textRect.setLeft( m_textRect.right() - InnerWidth/2 ); m_textRect.setRight( (int)x() - 2 ); //br = p.boundingRect( m_textRect, Qt::AlignRight, m_pinSettings->id() ); // 2016.05.03 - do not create dummy widget br = fontMetrics.boundingRect( m_textRect, Qt::AlignRight, m_pinSettings->id() ); } } //END class PinItem //BEGIN class PicItem PicItem::PicItem( ICNDocument *icnDocument, bool newItem, const char *id, MicroSettings *_microSettings ) : CNItem( icnDocument, newItem, id ? id : "picitem" ) { m_name = "PIC"; m_type = typeString(); p_icnDocument = icnDocument; icnDocument->registerItem(this); microSettings = _microSettings; const int numPins = microSettings->microInfo()->package()->pinCount( PicPin::type_bidir | PicPin::type_input | PicPin::type_open ); const int numSide = (numPins/2) + (numPins%2); m_bExpanded = true; m_innerHeight = (numSide+2)*PinWidth + (numSide-1)*PinSeparation; updateVisibility(); addButton( "settings", QRect( SidePadding-8, m_innerHeight+TopPadding+(BottomPadding-24)/2-1, InnerWidth+16, 24 ), i18n("Advanced...") ); addButton( "expandBtn", QRect( (TopPadding-22)/2, (TopPadding-22)/2, 22, 22 ), QIcon::fromTheme( "go-down" ), true ); button("expandBtn")->setState(true); move( 12, 12 ); QStringList pinIDs = microSettings->microInfo()->package()->pinIDs( PicPin::type_bidir | PicPin::type_input | PicPin::type_open ); QStringList::iterator it = pinIDs.begin(); for ( int i=0; i < numSide; ++i, ++it ) { QPoint position( int(this->x()) + SidePadding - PinLength+1, int(y()) + TopPadding + (i+1)*PinWidth + i*PinSeparation ); const QString id = *it; PinSettings *settings = microSettings->pinWithID(id); m_pinItemList.append( new PinItem( dynamic_cast(icnDocument), position, true, settings ) ); } for ( int i=0; i < numPins/2; ++i, ++it ) { QPoint position( int(this->x()) + SidePadding + InnerWidth-1, int(y()) + TopPadding + m_innerHeight - ( (i+2)*PinWidth + i*PinSeparation ) ); const QString id = *it; PinSettings *settings = microSettings->pinWithID(id); m_pinItemList.append( new PinItem( dynamic_cast(icnDocument), position, false, settings ) ); } setSelected(false); setPen( QPen( Qt::black ) ); updateZ(-1); update(); show(); } PicItem::~PicItem() { const PinItemList::iterator end = m_pinItemList.end(); for ( PinItemList::iterator it = m_pinItemList.begin(); it != end; ++it ) delete *it; m_pinItemList.clear(); } void PicItem::updateZ( int baseZ ) { (void)baseZ; setZ( (ICNDocument::Z::RaisedItem + ICNDocument::Z::ResizeHandle)/2 ); // Hackish, but whatever button("settings")->setZ( z()+1 ); button("expandBtn")->setZ( z()+1 ); } void PicItem::drawShape( QPainter & p ) { int _x = int(x()); int _y = int(y()); p.setBrush( QColor( 0xef, 0xff, 0xef ) ); p.setFont( font() ); p.drawRoundRect( _x, _y, width(), height(), 2000/width(), 2000/height() ); p.drawText( _x+TopPadding-2, _y, width()-TopPadding+2, TopPadding, Qt::AlignVCenter, i18n("PIC Settings") ); if ( !m_bExpanded ) return; // Draw rectangle to cut off pins p.setBrush( QColor( 239, 255, 255 ) ); QRect r( _x+SidePadding, _y+TopPadding, InnerWidth, m_innerHeight ); p.drawRect(r); // Draw dimple thingy at end of pic p.drawArc( r.x()+(r.width()-ArcWidth)/2, r.y()+1-ArcWidth/2, ArcWidth, ArcWidth, 180*16, 180*16 ); // Draw vertical text centered in PIC p.translate( r.width()/2 + r.x(), r.height()/2 + r.y() ); p.rotate(90); QRect textRect( r.width()/-2, r.height()/-2, r.width(), r.height() ); p.drawText( textRect, Qt::AlignCenter, microSettings->microInfo()->id() ); p.rotate(-90); p.translate( r.width()/-2 - r.x(), r.height()/-2 - r.y() ); } void PicItem::buttonStateChanged( const QString &id, bool state ) { if ( id == "expandBtn" ) { m_bExpanded = state; updateVisibility(); } else if ( id == "settings" ) { if (!state) return; // Redraw button button("settings")->setState(false); update(); MicroSettingsDlg *dlg = new MicroSettingsDlg( microSettings, nullptr, "microSettingsDlg" ); connect( dlg, &MicroSettingsDlg::accepted, this, &PicItem::slotMicroSettingsDlgAccepted); connect( dlg, &MicroSettingsDlg::applyClicked, this, &PicItem::slotMicroSettingsDlgAccepted); dlg->show(); // At this point the PIC is selected but this does not appear to the // user so we must deselect it when done. p_icnDocument->unselectAll(); } } void PicItem::updateVisibility() { if (m_bExpanded) setSize( 0, 0, InnerWidth+(2*SidePadding), m_innerHeight+TopPadding+BottomPadding, true ); else setSize( 0, 0, InnerWidth+(2*SidePadding), TopPadding, true ); const PinItemList::iterator end = m_pinItemList.end(); for ( PinItemList::iterator it = m_pinItemList.begin(); it != end; ++it ) (*it)->setVisible(m_bExpanded); if ( Button * btn = button("settings") ) btn->setVisible(m_bExpanded); } void PicItem::slotMicroSettingsDlgAccepted() { const PinItemList::iterator end = m_pinItemList.end(); for ( PinItemList::iterator it = m_pinItemList.begin(); it != end; ++it ) canvas()->setChanged( (*it)->boundingRect() ); p_icnDocument->requestStateSave(); } bool PicItem::mousePressEvent( const EventInfo &info ) { QMouseEvent *e = info.mousePressEvent( 0, 0 ); const PinItemList::iterator end = m_pinItemList.end(); for ( PinItemList::iterator it = m_pinItemList.begin(); it != end; ++it ) if ( e->isAccepted() && (*it)->boundingRect().contains( info.pos ) ) { //reset mouse-gesture state m_pressed = true; m_pos = info.pos; m_dragged = false; m_dx = 0; delete e; return true; } m_pressed = false; delete e; return CNItem::mousePressEvent( info ); } bool PicItem::mouseReleaseEvent( const EventInfo &info ) { QMouseEvent *e = info.mouseReleaseEvent( 0, 0 ); if ( !m_pressed ) { delete e; return CNItem::mouseReleaseEvent( info ); } const PinItemList::iterator end = m_pinItemList.end(); for ( PinItemList::iterator it = m_pinItemList.begin(); it != end; ++it ) if ( !e->isAccepted() ) { continue; } else if ( (*it)->boundingRect().contains(m_pos) && (*it)->boundingRect().contains( info.pos ) ) { if ( m_dragged ) { (*it)->dragged( m_dx ); } else { (*it)->switchState(); } m_pressed = false; m_dragged = false; m_pos = QPoint(); m_dx = 0; delete e; return true; } delete e; return CNItem::mouseReleaseEvent( info ); } bool PicItem::mouseMoveEvent( const EventInfo &info ) { QMouseEvent *e = info.mouseMoveEvent( 0, 0 ); const PinItemList::iterator end = m_pinItemList.end(); for ( PinItemList::iterator it = m_pinItemList.begin(); it != end; ++it ) if ( e->isAccepted() && (*it)->boundingRect().contains( info.pos ) ) { QPoint vec = info.pos - m_pos; if ( m_pressed && vec.manhattanLength() > 4 ) { m_dragged = true; m_dx = vec.x(); } delete e; return true; } delete e; return CNItem::mouseMoveEvent( info ); } //END class PicItem diff --git a/src/picitem.h b/src/picitem.h index 364f710d..cb0ad7ae 100644 --- a/src/picitem.h +++ b/src/picitem.h @@ -1,103 +1,103 @@ /*************************************************************************** * Copyright (C) 2003,2005 by David Saxton * * david@bluehaze.org * * * * 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. * ***************************************************************************/ #ifndef PICITEM_H #define PICITEM_H #include "cnitem.h" -#include +#include class MicroSettings; class FlowCodeDocument; class PinSettings; /** @short Allows visual setting of pin type/state @author David Saxton */ class PinItem : /*public QObject, */ public KtlQCanvasRectangle { Q_OBJECT public: PinItem( FlowCodeDocument* _view, QPoint position, bool _onLeft, PinSettings *_pinSettings ); QRect boundingRect () const override; void switchState(); QString id(); /** * Called from ICNDocument when the pin item was dragged */ void dragged( int dx ); void moveBy ( double dx, double dy ) override; public slots: void updateDrawing(); private: void initItem(); void drawShape( QPainter& p ) override; void calcTextRect(); FlowCodeDocument *view; // Pointer to canvas view that the component item is currently on bool onLeft; PinSettings * m_pinSettings; QRect m_textRect; QFont m_font; }; typedef QList PinItemList; /** Allows visual editing of inital PIC settings @author David Saxton */ class PicItem : public CNItem { Q_OBJECT public: PicItem( ICNDocument *icnDocument, bool newItem, const char *id, MicroSettings *_microSettings ); ~PicItem() override; void drawShape( QPainter &p ) override; void buttonStateChanged( const QString &id, bool state ) override; bool isMovable() const override { return false; } static QString typeString() { return "microitem"; } void updateZ( int baseZ ) override; bool mousePressEvent( const EventInfo &info ) override; bool mouseReleaseEvent( const EventInfo &info ) override; bool mouseMoveEvent( const EventInfo &info ) override; protected slots: void slotMicroSettingsDlgAccepted(); protected: void updateVisibility(); MicroSettings *microSettings; PinItemList m_pinItemList; ICNDocument *p_icnDocument; bool m_bExpanded; int m_innerHeight; private: QPoint m_pos; int m_dx; bool m_pressed; bool m_dragged; }; #endif diff --git a/src/projectmanager.cpp b/src/projectmanager.cpp index 86214860..d51ef591 100644 --- a/src/projectmanager.cpp +++ b/src/projectmanager.cpp @@ -1,1304 +1,1305 @@ /*************************************************************************** * Copyright (C) 2003-2005 by David Saxton * * david@bluehaze.org * * * * 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 "docmanager.h" #include "document.h" #include "language.h" #include "languagemanager.h" #include "ktechlab.h" #include "microselectwidget.h" #include "programmerdlg.h" #include "projectdlgs.h" #include "projectmanager.h" #include "recentfilesaction.h" -#include #include #include #include #include #include +#include #include -#include +#include +#include // #include -#include +#include #include #include #include #include #include #include #include #include static QString relativeUrl(const QUrl& baseDirUrl, const QUrl& url) { if (baseDirUrl.scheme() == url.scheme() && baseDirUrl.host() == url.host() && baseDirUrl.port() == url.port() && baseDirUrl.userInfo() == url.userInfo()) { return QDir(baseDirUrl.path()).relativeFilePath(url.path()); } return url.toDisplayString(QUrl::PreferLocalFile); } static QString resolvedLocalFile(const QString& baseDir, const QString& path) { Q_ASSERT(baseDir.endsWith(QLatin1Char('/'))); if (QDir::isAbsolutePath(path)) return path; return QDir::cleanPath(baseDir + path); } //BEGIN class LinkerOptions LinkerOptions::LinkerOptions() { m_hexFormat = HexFormat::inhx32; m_bOutputMapFile = false; } QDomElement LinkerOptions::toDomElement( QDomDocument & doc, const QUrl & baseDirUrl ) const { QDomElement node = doc.createElement("linker"); node.setAttribute( "hex-format", hexFormatToString(hexFormat()) ); node.setAttribute( "output-map-file", outputMapFile() ); node.setAttribute( "library-dir", libraryDir() ); node.setAttribute( "linker-script", linkerScript() ); node.setAttribute( "other", linkerOther() ); // internal are always local files, like the project base dir // so can always get relative path from a QDir const QDir baseDir(baseDirUrl.toLocalFile()); QStringList::const_iterator end = m_linkedInternal.end(); for ( QStringList::const_iterator it = m_linkedInternal.begin(); it != end; ++it ) { QDomElement child = doc.createElement("linked-internal"); node.appendChild(child); child.setAttribute( "url", baseDir.relativeFilePath(*it)); } end = m_linkedExternal.end(); for ( QStringList::const_iterator it = m_linkedExternal.begin(); it != end; ++it ) { QDomElement child = doc.createElement("linked-external"); node.appendChild(child); child.setAttribute( "url", *it ); } return node; } void LinkerOptions::domElementToLinkerOptions( const QDomElement & element, const QUrl & baseDirUrl ) { setHexFormat( stringToHexFormat( element.attribute( "hex-format", QString::null ) ) ); setOutputMapFile( element.attribute( "output-map-file", "0" ).toInt() ); setLibraryDir( element.attribute( "library-dir", QString::null ) ); setLinkerScript( element.attribute( "linker-script", QString::null ) ); setLinkerOther( element.attribute( "other", QString::null ) ); m_linkedInternal.clear(); m_linkedExternal.clear(); QString baseDir = baseDirUrl.toLocalFile(); if (!baseDir.endsWith(QLatin1Char('/'))) { baseDir.append(QLatin1Char('/')); } QDomNode node = element.firstChild(); while ( !node.isNull() ) { QDomElement childElement = node.toElement(); if ( !childElement.isNull() ) { const QString tagName = childElement.tagName(); if ( tagName == "linked-internal" ) m_linkedInternal << ::resolvedLocalFile(baseDir, childElement.attribute("url", QString())); else if ( tagName == "linked-external" ) m_linkedExternal << childElement.attribute( "url", QString::null ); else qCritical() << Q_FUNC_INFO << "Unrecognised element tag name: "<deleteLater(); m_children.clear(); delete m_pILVItem; } void ProjectItem::setILVItem( ILVItem * ilvItem ) { m_pILVItem = ilvItem; ilvItem->setExpanded(true); ilvItem->setText( 0, name() ); ilvItem->setProjectItem(this); updateILVItemPixmap(); } void ProjectItem::updateILVItemPixmap() { if ( !m_pILVItem ) return; switch ( type() ) { case ProjectType: { // ?! - We shouldn't have an ilvitem for this. break; } case ProgramType: { QPixmap pm; pm.load( QStandardPaths::locate( QStandardPaths::AppDataLocation, "icons/project_program.png" ) ); m_pILVItem->setIcon( 0, QIcon( pm ) ); break; } case LibraryType: { QPixmap pm; pm.load( QStandardPaths::locate( QStandardPaths::AppDataLocation, "icons/project_library.png" ) ); m_pILVItem->setIcon( 0, QIcon( pm ) ); break; } case FileType: { QMimeType m = QMimeDatabase().mimeTypeForFile( url().path() ); //m_pILVItem->setPixmap( 0, m->pixmap( KIconLoader::Small ) ); m_pILVItem->setIcon(0, QIcon::fromTheme(m.iconName())); break; } } } void ProjectItem::addChild( ProjectItem * child ) { if ( !child || m_children.contains(child) ) return; m_children << child; child->setILVItem( m_pILVItem ? new ILVItem( m_pILVItem, child->name() ) : new ILVItem( m_pProjectManager, name() ) ); updateControlChildMicroIDs(); } void ProjectItem::updateControlChildMicroIDs() { bool control = false; switch ( type() ) { case ProjectItem::ProjectType: case ProjectItem::LibraryType: case ProjectItem::ProgramType: control = !microID().isEmpty(); break; case ProjectItem::FileType: control = true; break; } m_children.removeAll( (ProjectItem*)nullptr ); ProjectItemList::iterator end = m_children.end(); for ( ProjectItemList::iterator it = m_children.begin(); it != end; ++it ) (*it)->setUseParentMicroID( control ); } void ProjectItem::setObjectName( const QString & name ) { m_name = name; if (m_pILVItem) m_pILVItem->setText( 0, name ); } void ProjectItem::setURL( const QUrl & url ) { m_url = url; if ( m_name.isEmpty() ) setObjectName( url.fileName() ); if ( type() != FileType ) { // The output url *is* our url setOutputURL(url); } else if ( outputURL().isEmpty() ) { // Try and guess what the output url should be... QString newExtension; switch ( outputType() ) { case ProgramOutput: newExtension = ".hex"; break; case ObjectOutput: newExtension = ".o"; break; case LibraryOutput: newExtension = ".o"; break; case UnknownOutput: break; } if ( !newExtension.isEmpty() ) { QString fileName = url.path(); fileName.chop(fileName.length() - fileName.lastIndexOf('.')); fileName.append(newExtension ); QUrl newUrl(url); newUrl.setPath(fileName); setOutputURL(newUrl); } } updateILVItemPixmap(); } QString ProjectItem::microID() const { if ( !m_bUseParentMicroID ) return m_microID; return m_pParent ? m_pParent->microID() : QString::null; } void ProjectItem::setMicroID( const QString & id ) { ProcessingOptions::setMicroID(id); updateControlChildMicroIDs(); } ProjectItem::OutputType ProjectItem::outputType() const { if ( !m_pParent ) return UnknownOutput; switch ( m_pParent->type() ) { case ProjectItem::ProjectType: { // We're a top level build target, so look at our own type switch ( type() ) { case ProjectItem::ProjectType: qWarning() << Q_FUNC_INFO << "Parent item and this item are both project items" << endl; return UnknownOutput; case ProjectItem::FileType: case ProjectItem::ProgramType: return ProgramOutput; case ProjectItem::LibraryType: return LibraryOutput; } return UnknownOutput; } case ProjectItem::FileType: { qWarning() << Q_FUNC_INFO << "Don't know how to handle parent item being a file" << endl; return UnknownOutput; } case ProjectItem::ProgramType: case ProjectItem::LibraryType: return ObjectOutput; } return UnknownOutput; } bool ProjectItem::build( ProcessOptionsList * pol ) { if ( !pol ) return false; // Check to see that we aren't already in the ProcessOptionstList; ProcessOptionsList::iterator polEnd = pol->end(); for ( ProcessOptionsList::iterator it = pol->begin(); it != polEnd; ++it ) { if ( (*it).targetFile() == outputURL().toLocalFile() ) return true; } ProjectInfo * projectInfo = ProjectManager::self()->currentProject(); assert(projectInfo); if ( outputURL().isEmpty() ) { KMessageBox::sorry( nullptr, i18n("Do not know how to build \"%1\" (output URL is empty).", name()) ); return false; } // Build all internal libraries that we depend on QStringList::iterator send = m_linkedInternal.end(); for ( QStringList::iterator it = m_linkedInternal.begin(); it != send; ++it ) { ProjectItem * lib = projectInfo->findItem(QUrl::fromLocalFile(projectInfo->directory() + *it)); if ( !lib ) { KMessageBox::sorry( nullptr, i18n("Do not know how to build \"%1\" (library does not exist in project).", *it) ); return false; } if ( !lib->build(pol) ) return false; } // Build all children m_children.removeAll( (ProjectItem*)nullptr ); ProjectItemList::iterator cend = m_children.end(); for ( ProjectItemList::iterator it = m_children.begin(); it != cend; ++it ) { if ( ! (*it)->build(pol) ) return false; } // Now build ourself ProcessOptions po; po.b_addToProject = false; po.setTargetFile( outputURL().toLocalFile() ); po.m_picID = microID(); ProcessOptions::ProcessPath::MediaType typeTo = ProcessOptions::ProcessPath::Unknown; switch ( outputType() ) { case UnknownOutput: KMessageBox::sorry( nullptr, i18n("Do not know how to build \"%1\" (unknown output type).", name()) ); return false; case ProgramOutput: typeTo = ProcessOptions::ProcessPath::Program; break; case ObjectOutput: typeTo = ProcessOptions::ProcessPath::Object; break; case LibraryOutput: typeTo = ProcessOptions::ProcessPath::Library; break; } switch ( type() ) { case ProjectType: // Nothing to do return true; case FileType: { const QString fileName = url().toLocalFile(); po.setInputFiles( QStringList( fileName ) ); po.setProcessPath( ProcessOptions::ProcessPath::path( ProcessOptions::guessMediaType( fileName ), typeTo ) ); break; } case ProgramType: case LibraryType: // Build up a list of input urls QStringList inputFiles; // Link child objects m_children.removeAll( (ProjectItem*)nullptr ); ProjectItemList::iterator cend = m_children.end(); for ( ProjectItemList::iterator it = m_children.begin(); it != cend; ++it ) inputFiles << (*it)->outputURL().toLocalFile(); po.setInputFiles(inputFiles); po.setProcessPath( ProcessOptions::ProcessPath::path( ProcessOptions::ProcessPath::Object, typeTo ) ); break; } po.m_hexFormat = hexFormatToString( hexFormat() ); po.m_bOutputMapFile = outputMapFile(); po.m_libraryDir = libraryDir(); po.m_linkerScript = linkerScript(); po.m_linkOther = linkerOther(); // Link against libraries QStringList::iterator lend = m_linkedInternal.end(); for ( QStringList::iterator it = m_linkedInternal.begin(); it != lend; ++it ) po.m_linkLibraries += projectInfo->directory() + *it; lend = m_linkedExternal.end(); for ( QStringList::iterator it = m_linkedExternal.begin(); it != lend; ++it ) po.m_linkLibraries += *it; // Save our working file (if open) and append to the build list Document * currentDoc = DocManager::self()->findDocument( url() ); if (currentDoc) currentDoc->fileSave(); pol->append(po); return true; } void ProjectItem::upload( ProcessOptionsList * pol ) { build( pol ); ProgrammerDlg * dlg = new ProgrammerDlg( microID(), (QWidget*)KTechlab::self(), "Programmer Dlg" ); const int accepted = dlg->exec(); if (accepted != QDialog::Accepted) { dlg->deleteLater(); return; } ProcessOptions po; dlg->initOptions( & po ); po.b_addToProject = false; po.setInputFiles( QStringList( outputURL().toLocalFile() ) ); po.setProcessPath( ProcessOptions::ProcessPath::Program_PIC ); pol->append( po ); dlg->deleteLater(); } QDomElement ProjectItem::toDomElement( QDomDocument & doc, const QUrl & baseDirUrl ) const { QDomElement node = doc.createElement("item"); node.setAttribute( "type", typeToString() ); node.setAttribute( "name", m_name ); node.setAttribute( "url", ::relativeUrl(baseDirUrl, m_url) ); node.appendChild( LinkerOptions::toDomElement( doc, baseDirUrl ) ); node.appendChild( ProcessingOptions::toDomElement( doc, baseDirUrl ) ); ProjectItemList::const_iterator end = m_children.end(); for ( ProjectItemList::const_iterator it = m_children.begin(); it != end; ++it ) { if (*it) node.appendChild( (*it)->toDomElement( doc, baseDirUrl ) ); } return node; } QList ProjectItem::childOutputURLs( unsigned types, unsigned outputTypes ) const { QList urls; ProjectItemList::const_iterator end = m_children.end(); for ( ProjectItemList::const_iterator it = m_children.begin(); it != end; ++it ) { if (!*it) continue; if ( ((*it)->type() & types) && ((*it)->outputType() & outputTypes) ) urls += (*it)->outputURL(); urls += (*it)->childOutputURLs(types); } return urls; } ProjectItem * ProjectItem::findItem( const QUrl & url ) { if ( this->url() == url ) return this; ProjectItemList::const_iterator end = m_children.end(); for ( ProjectItemList::const_iterator it = m_children.begin(); it != end; ++it ) { if (!*it) continue; ProjectItem * found = (*it)->findItem(url); if (found) return found; } return nullptr; } bool ProjectItem::closeOpenFiles() { Document * doc = DocManager::self()->findDocument(m_url); if ( doc && !doc->fileClose() ) return false; m_children.removeAll( (ProjectItem*)nullptr ); ProjectItemList::iterator end = m_children.end(); for ( ProjectItemList::iterator it = m_children.begin(); it != end; ++it ) { if ( !(*it)->closeOpenFiles() ) return false; } return true; } void ProjectItem::addFiles() { const QList urls = KTechlab::self()->getFileURLs(); for (const QUrl &url : urls) addFile(url); } void ProjectItem::addCurrentFile() { Document *document = DocManager::self()->getFocusedDocument(); if (!document) return; // If the file isn't saved yet, we must do that // before it is added to the project. if( document->url().isEmpty() ) { document->fileSaveAs(); // If the user pressed cancel then just give up, // otherwise the file can now be added. } if( !document->url().isEmpty() ) addFile( document->url() ); } void ProjectItem::addFile( const QUrl & url ) { if ( url.isEmpty() ) return; m_children.removeAll( (ProjectItem*)nullptr ); ProjectItemList::iterator end = m_children.end(); for ( ProjectItemList::iterator it = m_children.begin(); it != end; ++it ) { if ( (*it)->type() == FileType && (*it)->url() == url ) return; } ProjectItem * item = new ProjectItem( this, FileType, m_pProjectManager ); item->setURL(url); addChild(item); } QString ProjectItem::typeToString() const { switch (m_type) { case ProjectType: return "Project"; case FileType: return "File"; case ProgramType: return "Program"; case LibraryType: return "Library"; } return QString::null; } ProjectItem::Type ProjectItem::stringToType( const QString & type ) { if ( type == "Project" ) return ProjectType; if ( type == "File" ) return FileType; if ( type == "Program" ) return ProgramType; if ( type == "Library" ) return LibraryType; return FileType; } void ProjectItem::domElementToItem( const QDomElement & element, const QUrl & baseDirUrl ) { Type type = stringToType( element.attribute( "type", QString::null ) ); QString name = element.attribute( "name", QString::null ); QUrl url = baseDirUrl.resolved(QUrl(element.attribute("url", QString()))); ProjectItem * createdItem = new ProjectItem( this, type, m_pProjectManager ); createdItem->setObjectName( name ); createdItem->setURL( url ); addChild( createdItem ); QDomNode node = element.firstChild(); while ( !node.isNull() ) { QDomElement childElement = node.toElement(); if ( !childElement.isNull() ) { const QString tagName = childElement.tagName(); if ( tagName == "linker" ) createdItem->domElementToLinkerOptions( childElement, baseDirUrl ); else if ( tagName == "processing" ) createdItem->domElementToProcessingOptions( childElement, baseDirUrl ); else if ( tagName == "item" ) createdItem->domElementToItem( childElement, baseDirUrl ); else qCritical() << Q_FUNC_INFO << "Unrecognised element tag name: "< file; if (!url.isLocalFile()) { QScopedPointer downloadedFile(new QTemporaryFile()); downloadedFile->open(); KIO::FileCopyJob* job = KIO::file_copy(url, QUrl::fromLocalFile(downloadedFile->fileName())); KJobWidgets::setWindow(job, nullptr); if (!job->exec()) { KMessageBox::error( nullptr, job->errorString() ); return false; } file.reset(downloadedFile.take()); } else { QScopedPointer localFile(new QFile(url.toLocalFile())); if ( !localFile->open( QIODevice::ReadOnly ) ) { KMessageBox::sorry( nullptr, i18n("Could not open %1 for reading", localFile->fileName()) ); return false; } file.reset(localFile.take()); } m_url = url; QString xml; QTextStream textStream( file.data() ); while ( !textStream.atEnd() ) //was: eof() xml += textStream.readLine() + '\n'; QDomDocument doc( "KTechlab" ); QString errorMessage; if ( !doc.setContent( xml, &errorMessage ) ) { KMessageBox::sorry( nullptr, i18n("Could not parse XML:\n%1", errorMessage) ); return false; } QDomElement root = doc.documentElement(); QDomNode node = root.firstChild(); while ( !node.isNull() ) { QDomElement element = node.toElement(); if ( !element.isNull() ) { const QString tagName = element.tagName(); if ( tagName == "linker" ) domElementToLinkerOptions( element, m_url ); else if ( tagName == "processing" ) domElementToProcessingOptions( element, m_url ); else if ( tagName == "file" || tagName == "item" ) domElementToItem( element, m_url ); else qWarning() << Q_FUNC_INFO << "Unrecognised element tag name: "<toDomElement( doc, baseDirUrl ) ); QTextStream stream(&file); stream << doc.toString(); file.close(); { QAction *recentfilesaction = KTechlab::self()->actionByName("project_open_recent"); if (recentfilesaction) { (static_cast(recentfilesaction))->addUrl(m_url); } else { qWarning() << "there is no project_open_recent action in KTechLab!"; } } return true; } bool ProjectInfo::saveAndClose() { if (!save()) return false; if (!closeOpenFiles()) return false; return true; } //END class ProjectInfo //BEGIN class ProjectManager ProjectManager * ProjectManager::m_pSelf = nullptr; ProjectManager * ProjectManager::self( KateMDI::ToolView * parent ) { if ( !m_pSelf ) { assert(parent); m_pSelf = new ProjectManager( parent ); } return m_pSelf; } ProjectManager::ProjectManager( KateMDI::ToolView * parent ) : ItemSelector( parent, "Project Manager" ), m_pCurrentProject(nullptr) { setWhatsThis( i18n("Displays the list of files in the project.\nTo open or close a project, use the \"Project\" menu. Right click on a file to remove it from the project") ); setListCaption( i18n("File") ); setWindowTitle( i18n("Project Manager") ); connect( this, SIGNAL(itemClicked(QTreeWidgetItem*,int)), this, SLOT(slotItemClicked(QTreeWidgetItem*,int)) ); } ProjectManager::~ProjectManager() { } void ProjectManager::slotNewProject() { if ( !slotCloseProject() ) return; NewProjectDlg *newProjectDlg = new NewProjectDlg(this); const int accepted = newProjectDlg->exec(); if (accepted == QDialog::Accepted) { m_pCurrentProject = new ProjectInfo( this ); m_pCurrentProject->setObjectName( newProjectDlg->projectName() ); m_pCurrentProject->setURL(QUrl::fromLocalFile(newProjectDlg->location() + m_pCurrentProject->name().toLower() + ".ktechlab")); QDir dir; if ( !dir.mkdir( m_pCurrentProject->directory() ) ) qDebug() << "Error in creating directory " << m_pCurrentProject->directory() << endl; m_pCurrentProject->save(); updateActions(); emit projectCreated(); } delete newProjectDlg; } void ProjectManager::slotProjectOptions() { } void ProjectManager::slotOpenProject() { QString filter; filter = QString("*.ktechlab|%1 (*.ktechlab)\n*|%2").arg( i18n("KTechlab Project") ).arg( i18n("All Files") ); QUrl url = QFileDialog::getOpenFileUrl(this, i18n("Open Location"), QUrl(), filter); if ( url.isEmpty() ) return; slotOpenProject(url); } void ProjectManager::slotOpenProject( const QUrl & url ) { if ( m_pCurrentProject && m_pCurrentProject->url() == url ) return; if ( !slotCloseProject() ) return; m_pCurrentProject = new ProjectInfo( this ); if ( !m_pCurrentProject->open(url) ) { m_pCurrentProject->deleteLater(); m_pCurrentProject = nullptr; return; } { RecentFilesAction * rfa = static_cast(KTechlab::self()->actionByName("project_open_recent")); if (rfa) { rfa->addUrl( m_pCurrentProject->url() ); } else { qWarning() << "there is no project_open_recent action in application"; } } if ( KTLConfig::raiseItemSelectors() ) KTechlab::self()->showToolView( KTechlab::self()->toolView( toolViewIdentifier() ) ); updateActions(); emit projectOpened(); } bool ProjectManager::slotCloseProject() { if ( !m_pCurrentProject ) return true; if ( !m_pCurrentProject->saveAndClose() ) return false; m_pCurrentProject->deleteLater(); m_pCurrentProject = nullptr; updateActions(); emit projectClosed(); return true; } void ProjectManager::slotCreateSubproject() { if ( !currentProject() ) return; CreateSubprojectDlg * dlg = new CreateSubprojectDlg(this); const int accepted = dlg->exec(); if (accepted == QDialog::Accepted) { ProjectItem::Type type = ProjectItem::ProgramType; switch ( dlg->type() ) { case CreateSubprojectDlg::ProgramType: type = ProjectItem::ProgramType; break; case CreateSubprojectDlg::LibraryType: type = ProjectItem::LibraryType; break; } ProjectItem * subproject = new ProjectItem( currentProject(), type, this ); subproject->setURL( QUrl::fromLocalFile(dlg->targetFile()) ); currentProject()->addChild(subproject); currentProject()->save(); emit subprojectCreated(); } delete dlg; } void ProjectManager::updateActions() { bool projectIsOpen = m_pCurrentProject; KTechlab::self()->actionByName("project_create_subproject")->setEnabled( projectIsOpen ); KTechlab::self()->actionByName("project_export_makefile")->setEnabled( projectIsOpen ); KTechlab::self()->actionByName("subproject_add_existing_file")->setEnabled( projectIsOpen ); KTechlab::self()->actionByName("subproject_add_current_file")->setEnabled( projectIsOpen ); // KTechlab::self()->actionByName("project_options")->setEnabled( projectIsOpen ); KTechlab::self()->actionByName("project_close")->setEnabled( projectIsOpen ); KTechlab::self()->actionByName("project_add_existing_file")->setEnabled( projectIsOpen ); KTechlab::self()->actionByName("project_add_current_file")->setEnabled( projectIsOpen ); } void ProjectManager::slotAddFile() { if ( !currentProject() ) return; currentProject()->addFiles(); emit filesAdded(); } void ProjectManager::slotAddCurrentFile() { if ( !currentProject() ) return; currentProject()->addCurrentFile(); emit filesAdded(); } void ProjectManager::slotSubprojectAddExistingFile() { ILVItem * currentItem = dynamic_cast(selectedItem()); if ( !currentItem || !currentItem->projectItem() ) return; currentItem->projectItem()->addFiles(); emit filesAdded(); } void ProjectManager::slotSubprojectAddCurrentFile() { ILVItem * currentItem = dynamic_cast(selectedItem()); if ( !currentItem || !currentItem->projectItem() ) return; currentItem->projectItem()->addCurrentFile(); emit filesAdded(); } void ProjectManager::slotItemBuild() { ILVItem * currentItem = dynamic_cast(selectedItem()); if ( !currentItem || !currentItem->projectItem() ) return; ProcessOptionsList pol; currentItem->projectItem()->build(&pol); LanguageManager::self()->compile(pol); } void ProjectManager::slotItemUpload() { ILVItem * currentItem = dynamic_cast(selectedItem()); if ( !currentItem || !currentItem->projectItem() ) return; ProcessOptionsList pol; currentItem->projectItem()->upload(&pol); LanguageManager::self()->compile(pol); } void ProjectManager::slotRemoveSelected() { ILVItem *currentItem = dynamic_cast(selectedItem()); if ( !currentItem ) return; int choice = KMessageBox::questionYesNo( this, i18n("Do you really want to remove \"%1\"?", currentItem->text(0) ), i18n("Remove Project File?"), KGuiItem(i18n("Remove")), KGuiItem(i18n("Cancel")) ); if ( choice == KMessageBox::No ) return; currentItem->projectItem()->deleteLater(); emit filesRemoved(); } void ProjectManager::slotExportToMakefile() { } void ProjectManager::slotSubprojectLinkerOptions() { ILVItem * currentItem = dynamic_cast(selectedItem()); if ( !currentItem || !currentItem->projectItem() ) return; LinkerOptionsDlg * dlg = new LinkerOptionsDlg( currentItem->projectItem(), this ); dlg->exec(); currentProject()->save(); // The dialog sets the options for us if it was accepted, so we don't need to do anything delete dlg; } void ProjectManager::slotItemProcessingOptions() { ILVItem * currentItem = dynamic_cast(selectedItem()); if ( !currentItem || !currentItem->projectItem() ) return; ProcessingOptionsDlg * dlg = new ProcessingOptionsDlg( currentItem->projectItem(), this ); dlg->exec(); currentProject()->save(); // The dialog sets the options for us if it was accepted, so we don't need to do anything delete dlg; } void ProjectManager::slotItemClicked( QTreeWidgetItem* item, int ) { ILVItem * ilvItem = dynamic_cast(item); if ( !ilvItem ) return; ProjectItem * projectItem = ilvItem->projectItem(); if ( !projectItem || projectItem->type() != ProjectItem::FileType ) return; DocManager::self()->openURL( projectItem->url() ); } void ProjectManager::slotContextMenuRequested( const QPoint& pos ) { QTreeWidgetItem* item = itemAt(pos); QString popupName; ILVItem * ilvItem = dynamic_cast(item); QAction * linkerOptionsAct = KTechlab::self()->actionByName("project_item_linker_options"); linkerOptionsAct->setEnabled(false); if ( !m_pCurrentProject ) { popupName = "project_none_popup"; } else if ( !ilvItem ) { popupName = "project_blank_popup"; } else { ProcessOptions::ProcessPath::MediaType mediaType = ProcessOptions::guessMediaType( ilvItem->projectItem()->url().toLocalFile() ); switch ( ilvItem->projectItem()->type() ) { case ProjectItem::FileType: if ( mediaType == ProcessOptions::ProcessPath::Unknown ) popupName = "project_file_other_popup"; else popupName = "project_file_popup"; break; case ProjectItem::ProgramType: popupName = "project_program_popup"; break; case ProjectItem::LibraryType: popupName = "project_library_popup"; break; case ProjectItem::ProjectType: return; } switch ( ilvItem->projectItem()->outputType() ) { case ProjectItem::ProgramOutput: linkerOptionsAct->setEnabled(true); break; case ProjectItem::ObjectOutput: case ProjectItem::LibraryOutput: case ProjectItem::UnknownOutput: linkerOptionsAct->setEnabled(false); break; } // Only have linking options for SDCC files linkerOptionsAct->setEnabled( mediaType == ProcessOptions::ProcessPath::C ); } bool haveFocusedDocument = DocManager::self()->getFocusedDocument(); KTechlab::self()->actionByName("subproject_add_current_file")->setEnabled( haveFocusedDocument ); KTechlab::self()->actionByName("project_add_current_file")->setEnabled( haveFocusedDocument ); QMenu *pop = static_cast(KTechlab::self()->factory()->container( popupName, KTechlab::self() )); if (pop) { QPoint globalPos = mapToGlobal(pos); pop->popup(globalPos); } } //END class ProjectManager diff --git a/src/projectmanager.h b/src/projectmanager.h index c4333052..ceb8bbd7 100644 --- a/src/projectmanager.h +++ b/src/projectmanager.h @@ -1,362 +1,362 @@ /*************************************************************************** * Copyright (C) 2003-2005 by David Saxton * * david@bluehaze.org * * * * 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. * ***************************************************************************/ #ifndef PROJECTMANAGER_H #define PROJECTMANAGER_H #include "itemselector.h" #include -#include -#include +#include +#include class Document; class ILVItem; class KTechlab; class ProcessOptions; class ProjectInfo; class ProjectItem; class ProjectManager; class QDomDocument; class QDomElement; class QStringList; namespace KateMDI { class ToolView; } typedef QList ProcessOptionsList; typedef QList< QPointer > ProjectItemList; class LinkerOptions { public: LinkerOptions(); class HexFormat { public: enum type { inhx32, inhx8m, inhx8s, inhx16 }; }; HexFormat::type hexFormat() const { return m_hexFormat; } void setHexFormat( HexFormat::type hexFormat ) { m_hexFormat = hexFormat; } bool outputMapFile() const { return m_bOutputMapFile; } void setOutputMapFile( bool outputMapFile ) { m_bOutputMapFile = outputMapFile; } QString libraryDir() const { return m_libraryDir; } void setLibraryDir( const QString & libraryDir ) { m_libraryDir = libraryDir; } QString linkerScript() const { return m_linkerScript; } void setLinkerScript( const QString & linkerScript ) { m_linkerScript = linkerScript; } QString linkerOther() const { return m_other; } void setLinkerOther( const QString & other ) { m_other = other; } /** * Used for linkable ProjectItems. Returns a list of urls of files * inside the project to link against. Each url is relative to the * project directory. */ QStringList linkedInternal() const { return m_linkedInternal; } void setLinkedInternal( const QStringList & linkedInternal ) { m_linkedInternal = linkedInternal; } /** * Used for linkable ProjectItems. Returns a list of urls of files * outside the project to link against. Each url is absolute. */ QStringList linkedExternal() const { return m_linkedExternal; } void setLinkedExternal( const QStringList & linkedExternal ) { m_linkedExternal = linkedExternal; } /** * @param baseDirUrl url of the directory of the project */ QDomElement toDomElement( QDomDocument & doc, const QUrl & baseDirUrl ) const; static QString hexFormatToString( HexFormat::type format ); static HexFormat::type stringToHexFormat( const QString & hexFormat ); protected: /** * @param baseDirUrl url of the directory of the project */ void domElementToLinkerOptions( const QDomElement & element, const QUrl & baseDirUrl ); QStringList m_linkedInternal; QStringList m_linkedExternal; HexFormat::type m_hexFormat; bool m_bOutputMapFile; QString m_libraryDir; QString m_linkerScript; QString m_other; }; class ProcessingOptions { public: ProcessingOptions(); virtual ~ProcessingOptions(); /** * Sets the output url that this item will be built into (if this is a * buildable item). * Only urls of local filesystems supported. */ void setOutputURL( const QUrl & url ) { m_outputURL = url; } QUrl outputURL() const { return m_outputURL; } /** * Set the microprocessor id that this project item is being built for * (when applicable). */ virtual void setMicroID( const QString & id ) { m_microID = id; } virtual QString microID() const { return m_microID; } /** * @param baseDirUrl url of the directory of the project */ QDomElement toDomElement( QDomDocument & doc, const QUrl & baseDirUrl ) const; void setUseParentMicroID( bool useParentMicroID ) { m_bUseParentMicroID = useParentMicroID; } bool useParentMicroID() const { return m_bUseParentMicroID; } protected: /** * @param baseDirUrl url of the directory of the project */ void domElementToProcessingOptions( const QDomElement & element, const QUrl & baseDirUrl ); QUrl m_outputURL; QString m_microID; bool m_bUseParentMicroID; }; /** @author David Saxton */ class ProjectItem : public QObject, public LinkerOptions, public ProcessingOptions { public: enum Type { ProjectType = 1 << 0, FileType = 1 << 1, ProgramType = 1 << 2, LibraryType = 1 << 3 }; enum { AllTypes = ProjectType | FileType | ProgramType | LibraryType }; enum OutputType { ProgramOutput = 1 << 0, ObjectOutput = 1 << 1, LibraryOutput = 1 << 2, UnknownOutput = 1 << 3 }; enum { AllOutputs = ProgramOutput | ObjectOutput | LibraryOutput | UnknownOutput }; ProjectItem( ProjectItem * parent, Type type, ProjectManager * projectManager ); ~ProjectItem() override; Type type() const { return m_type; } QString typeToString() const; static Type stringToType( const QString & type ); void setILVItem( ILVItem * ilvItem ); /** * Adds the child to the list of children, and creates an ILVItem for it * in the project tree view. */ void addChild( ProjectItem * child ); ProjectItemList children() const { return m_children; } void setObjectName( const QString & name ); QString name() const { return m_name; } /** * Sets the (input) url that this project item refers to. If the output * url has not yet been set, then this project item will set the output * url based on this (input) url. */ void setURL( const QUrl & url ); QUrl url() const { return m_url; } OutputType outputType() const; /** * Returns a list of output urls of the children and their recursively * contained children (does not include the url for this project item). * @param types An OR'ed list of ProjectItem::Type values for the * children. * @param outputTypes An OR'ed list of ProjectItem::OutputType values * for the children. */ QList childOutputURLs( unsigned types = AllTypes, unsigned outputTypes = AllOutputs ) const; /** * Creates a new ProjectItem for the given url and adds it as a child. */ void addFile( const QUrl & url ); /** * Queries the user for a list of urls to add, and then calls addFile * for each url. */ void addFiles(); void addCurrentFile(); bool closeOpenFiles(); /** * @param baseDirUrl url of the directory of the project */ QDomElement toDomElement( QDomDocument & doc, const QUrl & baseDirUrl ) const; bool build( ProcessOptionsList * pol ); void upload( ProcessOptionsList * pol ); void setMicroID( const QString & id ) override; QString microID() const override; /** * Searches this item and the children for an item for the given url, * return null if no such item could be found. */ ProjectItem * findItem( const QUrl & url ); protected: /** * @param baseDirUrl url of the directory of the project */ void domElementToItem( const QDomElement & element, const QUrl & baseDirUrl ); void updateILVItemPixmap(); void updateControlChildMicroIDs(); QUrl m_url; QString m_name; ProjectItemList m_children; Type m_type; QPointer m_pILVItem; ProjectManager * m_pProjectManager; ProjectItem * m_pParent; }; /** @author David Saxton */ class ProjectInfo : public ProjectItem { Q_OBJECT public: ProjectInfo( ProjectManager * projectManager ); ~ProjectInfo() override; /** * Returns the directory that the project is saved in */ QString directory() const { return m_url.adjusted(QUrl::StripTrailingSlash).adjusted(QUrl::RemoveFilename).path(); } /** * Saves the project information to file, and attempts to close all * open project files. * @return true iff succesful */ bool saveAndClose(); bool save(); bool open( const QUrl & url ); }; /** @short Project Management @author David Saxton */ class ProjectManager : public ItemSelector { Q_OBJECT public: ~ProjectManager() override; static ProjectManager * self( KateMDI::ToolView * parent = nullptr ); static QString toolViewIdentifier() { return "ProjectManager"; } /** * @return the currently open project, or nullptr if no project is open. */ ProjectInfo * currentProject() const { return m_pCurrentProject; } void updateActions(); signals: /** * Emitted when an existing project is opened. */ void projectOpened(); /** * Emitted when the current project is closed. */ void projectClosed(); /** * Emitted when a new project is created. */ void projectCreated(); /** * Emitted when a subproject is created. */ void subprojectCreated(); /** * Emitted when file(s) are added to the project or a subproject. */ void filesAdded(); /** * Emitted when file(s) are removed from the project or a subproject. */ void filesRemoved(); public slots: void slotNewProject(); void slotOpenProject(); void slotOpenProject( const QUrl &url ); bool slotCloseProject(); void slotCreateSubproject(); void slotAddFile(); void slotAddCurrentFile(); void slotSubprojectAddExistingFile(); void slotSubprojectAddCurrentFile(); void slotItemBuild(); void slotItemUpload(); void slotItemProcessingOptions(); void slotRemoveSelected(); void slotExportToMakefile(); void slotSubprojectLinkerOptions(); /** * Pops ups a project configuration dialog */ void slotProjectOptions(); private slots: void slotContextMenuRequested( const QPoint &pos ) override; /** * Called when a user clicks on any item in the project view */ void slotItemClicked( QTreeWidgetItem* item, int ); protected: ProjectInfo * m_pCurrentProject; private: ProjectManager( KateMDI::ToolView * parent ); static ProjectManager * m_pSelf; }; #endif diff --git a/src/resizeoverlay.cpp b/src/resizeoverlay.cpp index c444a17c..cc730fef 100644 --- a/src/resizeoverlay.cpp +++ b/src/resizeoverlay.cpp @@ -1,743 +1,743 @@ /*************************************************************************** * Copyright (C) 2005 by David Saxton * * david@bluehaze.org * * * * 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 "itemdocument.h" #include "mechanicsitem.h" #include "resizeoverlay.h" -#include -#include +#include +#include #include #define DPR ( 180.0 / M_PI ) //BEGIN class ResizeOverlay ResizeOverlay::ResizeOverlay( Item *parent ) : QObject(parent) { b_showResizeHandles = false; b_visible = true; p_item = parent; } ResizeOverlay::~ResizeOverlay() { const ResizeHandleMap::iterator end = m_resizeHandleMap.end(); for ( ResizeHandleMap::iterator it = m_resizeHandleMap.begin(); it != end; ++it ) { if (it.value()) it.value()->setCanvas(nullptr); delete (ResizeHandle*)it.value(); } m_resizeHandleMap.clear(); } void ResizeOverlay::showResizeHandles( bool show ) { b_showResizeHandles = show; const ResizeHandleMap::iterator end = m_resizeHandleMap.end(); for ( ResizeHandleMap::iterator it = m_resizeHandleMap.begin(); it != end; ++it ) { it.value()->setVisible(b_showResizeHandles && b_visible); } } void ResizeOverlay::setVisible( bool visible ) { b_visible = visible; const ResizeHandleMap::iterator end = m_resizeHandleMap.end(); for ( ResizeHandleMap::iterator it = m_resizeHandleMap.begin(); it != end; ++it ) { it.value()->setVisible(b_showResizeHandles && b_visible); } } ResizeHandle *ResizeOverlay::createResizeHandle( int id, ResizeHandle::DrawType drawType, int xsnap, int ysnap ) { ResizeHandleMap::iterator it = m_resizeHandleMap.find(id); if ( it != m_resizeHandleMap.end() ) return it.value(); ResizeHandle *newResizeHandle = new ResizeHandle( this, id, drawType, xsnap, ysnap ); m_resizeHandleMap[id] = newResizeHandle; connect( newResizeHandle, SIGNAL(rhMovedBy(int, double, double )), this, SLOT(slotResizeHandleMoved(int, double, double )) ); return newResizeHandle; } void ResizeOverlay::removeResizeHandle( int id ) { ResizeHandleMap::iterator it = m_resizeHandleMap.find(id); if ( it == m_resizeHandleMap.end() ) return; ResizeHandle *rh = it.value(); disconnect( rh, SIGNAL(rhMovedBy(int, double, double )), this, SLOT(slotResizeHandleMoved(int, double, double )) ); delete rh; m_resizeHandleMap.erase(it); } ResizeHandle *ResizeOverlay::resizeHandle( int id ) { ResizeHandleMap::iterator it = m_resizeHandleMap.find(id); if ( it != m_resizeHandleMap.end() ) return it.value(); return nullptr; } void ResizeOverlay::slotMoveAllResizeHandles( double dx, double dy ) { const ResizeHandleMap::iterator end = m_resizeHandleMap.end(); for ( ResizeHandleMap::iterator it = m_resizeHandleMap.begin(); it != end; ++it ) { it.value()->moveBy( dx, dy ); } } void ResizeOverlay::syncX( ResizeHandle *rh1, ResizeHandle *rh2, ResizeHandle *rh3 ) { syncX( rh1, rh2 ); syncX( rh1, rh3 ); syncX( rh2, rh3 ); } void ResizeOverlay::syncY( ResizeHandle *rh1, ResizeHandle *rh2, ResizeHandle *rh3 ) { syncY( rh1, rh2 ); syncY( rh1, rh3 ); syncY( rh2, rh3 ); } void ResizeOverlay::syncX( ResizeHandle *rh1, ResizeHandle *rh2 ) { if ( !rh1 || !rh2 ) return; connect( rh1, SIGNAL(rhMovedByX(double )), rh2, SLOT(slotMoveByX(double )) ); connect( rh2, SIGNAL(rhMovedByX(double )), rh1, SLOT(slotMoveByX(double )) ); } void ResizeOverlay::syncY( ResizeHandle *rh1, ResizeHandle *rh2 ) { if ( !rh1 || !rh2 ) return; connect( rh1, SIGNAL(rhMovedByY(double )), rh2, SLOT(slotMoveByY(double )) ); connect( rh2, SIGNAL(rhMovedByY(double )), rh1, SLOT(slotMoveByY(double )) ); } //END class ResizeOverlay //BEGIN class MechanicsItemOverlay MechanicsItemOverlay::MechanicsItemOverlay( MechanicsItem *parent ) : ResizeOverlay(parent) { p_mechanicsItem = parent; connect( parent, SIGNAL(moved()), this, SLOT(slotUpdateResizeHandles()) ); connect( parent, SIGNAL(resized()), this, SLOT(slotUpdateResizeHandles()) ); m_tl = createResizeHandle( ResizeHandle::rhp_topLeft, ResizeHandle::dt_resize_backwardsDiagonal ); m_tm = createResizeHandle( ResizeHandle::rhp_topMiddle, ResizeHandle::dt_resize_vertical ); m_tr = createResizeHandle( ResizeHandle::rhp_topRight, ResizeHandle::dt_resize_forwardsDiagonal ); m_mr = createResizeHandle( ResizeHandle::rhp_middleRight, ResizeHandle::dt_resize_horizontal ); m_br = createResizeHandle( ResizeHandle::rhp_bottomRight, ResizeHandle::dt_resize_backwardsDiagonal ); m_bm = createResizeHandle( ResizeHandle::rhp_bottomMiddle, ResizeHandle::dt_resize_vertical ); m_bl = createResizeHandle( ResizeHandle::rhp_bottomLeft, ResizeHandle::dt_resize_forwardsDiagonal ); m_ml = createResizeHandle( ResizeHandle::rhp_middleLeft, ResizeHandle::dt_resize_horizontal ); m_mm = createResizeHandle( ResizeHandle::rhp_center, ResizeHandle::dt_point_crosshair ); slotUpdateResizeHandles(); } MechanicsItemOverlay::~MechanicsItemOverlay() { } void MechanicsItemOverlay::slotUpdateResizeHandles() { const PositionInfo absPos = p_mechanicsItem->absolutePosition(); const QRect sizeRect = p_mechanicsItem->sizeRect(); QPolygon pa(9); pa[0] = sizeRect.topLeft(); pa[2] = sizeRect.topRight(); pa[1] = (pa[0]+pa[2])/2; pa[4] = sizeRect.bottomRight(); pa[3] = (pa[2]+pa[4])/2; pa[6] = sizeRect.bottomLeft(); pa[5] = (pa[4]+pa[6])/2; pa[7] = (pa[6]+pa[0])/2; pa[8] = QPoint(0,0); QMatrix m; m.rotate(absPos.angle() * DPR); pa = m.map(pa); m_tl->move( absPos.x()+pa[0].x(), absPos.y()+pa[0].y() ); m_tm->move( absPos.x()+pa[1].x(), absPos.y()+pa[1].y() ); m_tr->move( absPos.x()+pa[2].x(), absPos.y()+pa[2].y() ); m_mr->move( absPos.x()+pa[3].x(), absPos.y()+pa[3].y() ); m_br->move( absPos.x()+pa[4].x(), absPos.y()+pa[4].y() ); m_bm->move( absPos.x()+pa[5].x(), absPos.y()+pa[5].y() ); m_bl->move( absPos.x()+pa[6].x(), absPos.y()+pa[6].y() ); m_ml->move( absPos.x()+pa[7].x(), absPos.y()+pa[7].y() ); m_mm->move( absPos.x()+pa[8].x(), absPos.y()+pa[8].y() ); } void MechanicsItemOverlay::slotResizeHandleMoved( int id, double dx, double dy ) { Q_UNUSED(id); Q_UNUSED(dx); Q_UNUSED(dy); switch (id) { case ResizeHandle::rhp_topLeft: break; case ResizeHandle::rhp_topMiddle: break; case ResizeHandle::rhp_topRight: break; case ResizeHandle::rhp_middleRight: break; case ResizeHandle::rhp_bottomRight: break; case ResizeHandle::rhp_bottomMiddle: break; case ResizeHandle::rhp_bottomLeft: break; case ResizeHandle::rhp_middleLeft: break; case ResizeHandle::rhp_center: break; default: qCritical() << Q_FUNC_INFO << "Unknown resize handle id " << id << endl; break; } } //END class MechanicsItemOverlay //BEGIN class RectangularOverlay RectangularOverlay::RectangularOverlay( Item *parent, int xsnap, int ysnap ) : ResizeOverlay(parent) { connect( parent, SIGNAL(resized()), this, SLOT(slotUpdateResizeHandles()) ); connect( parent, SIGNAL(movedBy(double, double )), this, SLOT(slotMoveAllResizeHandles(double, double )) ); m_tl = createResizeHandle( ResizeHandle::rhp_topLeft, ResizeHandle::dt_resize_backwardsDiagonal, xsnap, ysnap ); m_tm = createResizeHandle( ResizeHandle::rhp_topMiddle, ResizeHandle::dt_resize_vertical, xsnap, ysnap ); m_tr = createResizeHandle( ResizeHandle::rhp_topRight, ResizeHandle::dt_resize_forwardsDiagonal, xsnap, ysnap ); m_mr = createResizeHandle( ResizeHandle::rhp_middleRight, ResizeHandle::dt_resize_horizontal, xsnap, ysnap ); m_br = createResizeHandle( ResizeHandle::rhp_bottomRight, ResizeHandle::dt_resize_backwardsDiagonal, xsnap, ysnap ); m_bm = createResizeHandle( ResizeHandle::rhp_bottomMiddle, ResizeHandle::dt_resize_vertical, xsnap, ysnap ); m_bl = createResizeHandle( ResizeHandle::rhp_bottomLeft, ResizeHandle::dt_resize_forwardsDiagonal, xsnap, ysnap ); m_ml = createResizeHandle( ResizeHandle::rhp_middleLeft, ResizeHandle::dt_resize_horizontal, xsnap, ysnap ); syncX( m_tl, m_ml, m_bl ); syncX( m_tr, m_mr, m_br ); syncY( m_tl, m_tm, m_tr ); syncY( m_bl, m_bm, m_br ); slotUpdateResizeHandles(); } void RectangularOverlay::removeTopMiddle() { if (!m_tm) return; removeResizeHandle( m_tm->id() ); m_tm = nullptr; } void RectangularOverlay::removeBotMiddle() { if (!m_bm) return; removeResizeHandle( m_bm->id() ); m_bm = nullptr; } void RectangularOverlay::slotUpdateResizeHandles() { const QRect sizeRect = p_item->sizeRect(); int x1 = sizeRect.left() + int(p_item->x()); int x2 = x1 + sizeRect.width(); int y1 = sizeRect.top() + int(p_item->y()); int y2 = y1 + sizeRect.height(); m_tl->move( x1, y1 ); if (m_tm) m_tm->move( (x1+x2)/2, y1 ); m_tr->move( x2, y1 ); m_mr->move( x2, (y1+y2)/2 ); m_br->move( x2, y2 ); if (m_bm) m_bm->move( (x1+x2)/2, y2 ); m_bl->move( x1, y2 ); m_ml->move( x1, (y1+y2)/2 ); } bool RectangularOverlay::isValidXPos( ResizeHandle *rh ) { Q_UNUSED(rh); bool ok; getSizeRect( nullptr, &ok, nullptr ); return ok; } bool RectangularOverlay::isValidYPos( ResizeHandle *rh ) { Q_UNUSED(rh); bool ok; getSizeRect( nullptr, nullptr, &ok ); return ok; } void RectangularOverlay::slotResizeHandleMoved( int id, double dx, double dy ) { Q_UNUSED(id); Q_UNUSED(dx); Q_UNUSED(dy); bool ok; QRect sizeRect = getSizeRect(&ok); if (!ok) return; p_item->setSize(sizeRect); slotUpdateResizeHandles(); } QRect RectangularOverlay::getSizeRect( bool *ok, bool *widthOk, bool *heightOk ) const { bool t1,t2,t3; if (!ok) ok = &t1; if (!widthOk) widthOk = &t2; if (!heightOk) heightOk = &t3; int width = int(m_br->x() - m_tl->x()); int height = int(m_br->y() - m_tl->y()); QRect sizeRect( int(m_tl->x() - p_item->x()), int(m_tl->y() - p_item->y()), width, height ); *widthOk = sizeRect.width() >= p_item->minimumSize().width(); *heightOk = sizeRect.height() >= p_item->minimumSize().height(); *ok = *widthOk && *heightOk; return sizeRect; } //END class RectangularOverlay //BEGIN class LineOverlay LineOverlay::LineOverlay( Item * parent ) : ResizeOverlay(parent) { connect( parent, SIGNAL(resized()), this, SLOT(slotUpdateResizeHandles()) ); connect( parent, SIGNAL(movedBy(double, double )), this, SLOT(slotMoveAllResizeHandles(double, double )) ); m_pStart = createResizeHandle( ResizeHandle::rhp_start, ResizeHandle::dt_point_rect ); m_pEnd = createResizeHandle( ResizeHandle::rhp_end, ResizeHandle::dt_point_rect ); slotUpdateResizeHandles(); } QPoint LineOverlay::startPoint() const { return QPoint( int(m_pStart->x()), int(m_pStart->y()) ); } QPoint LineOverlay::endPoint() const { return QPoint( int(m_pEnd->x()), int(m_pEnd->y()) ); } void LineOverlay::slotUpdateResizeHandles() { int _x = int(p_item->x() + p_item->offsetX()); int _y = int(p_item->y() + p_item->offsetY()); m_pStart->move( _x, _y ); m_pEnd->move( _x+p_item->width(), _y+p_item->height() ); } void LineOverlay::slotResizeHandleMoved( int id, double dx, double dy ) { Q_UNUSED(id); Q_UNUSED(dx); Q_UNUSED(dy); p_item->setSize( int(m_pStart->x()-p_item->x()), int(m_pStart->y()-p_item->y()), int(m_pEnd->x()-m_pStart->x()), int(m_pEnd->y()-m_pStart->y()) ); } //END class LineOverlay //BEGIN class ResizeHandle ResizeHandle::ResizeHandle( ResizeOverlay *resizeOverlay, int id, DrawType drawType, int xsnap, int ysnap ) : //QObject(), KtlQCanvasRectangle( 0, 0, 13, 13, resizeOverlay->parentItem()->canvas() ) { p_resizeOverlay = resizeOverlay; m_drawType = drawType; m_id = id; b_hover = false; m_xsnap = xsnap; m_ysnap = ysnap; setZ( ItemDocument::Z::ResizeHandle ); } ResizeHandle::~ResizeHandle() { hide(); } void ResizeHandle::setHover( bool hover ) { if ( b_hover == hover ) return; b_hover = hover; canvas()->setChanged( QRect( int(x())-8, int(y())-8, 15, 15 ) ); } QPolygon ResizeHandle::areaPoints() const { // QPolygon pa = KtlQCanvasRectangle::areaPoints(); // pa.translate( -7, -7 ); // return pa; return QPolygon( QRect( int(x())-8, int(y())-8, 15, 15 ) ); } void ResizeHandle::moveRH( double _x, double _y ) { double dx = int((_x-4)/m_xsnap)*m_xsnap+4 - x(); double dy = int((_y-4)/m_ysnap)*m_ysnap+4 - y(); if ( (dx == 0) && (dy == 0) ) return; //BEGIN Move and check moveBy( dx, dy ); if ( dx != 0 ) emit rhMovedByX(dx); if ( dy != 0 ) emit rhMovedByY(dy); bool xOk = p_resizeOverlay->isValidXPos(this); bool yOk = p_resizeOverlay->isValidYPos(this); if (!xOk) { moveBy( -dx, 0 ); emit rhMovedByX(-dx); dx = 0; } if (!yOk) { moveBy( 0, -dy ); emit rhMovedByY(-dy); dy = 0; } if ( !xOk && !yOk ) return; //END Move and check emit rhMovedBy( id(), dx, dy ); } void ResizeHandle::setDrawType( DrawType drawType ) { m_drawType = drawType; canvas()->setChanged(boundingRect()); } void ResizeHandle::drawShape( QPainter &p ) { p.drawPixmap( rect().topLeft()-QPoint( 7, 7 ), handlePixmap( m_drawType, b_hover ) ); } const QPixmap& ResizeHandle::handlePixmap( DrawType drawType, bool hover ) { const char * resize_forwardsDiagonal_hover_xpm[] = { "13 13 3 1", " c None", ". c #000000", "+ c #8EA5D0", " ", " ....... ", " ..+++. ", " .++++. ", " .+++++. ", " . .+++++.. ", " ...+++++... ", " ..+++++. . ", " .+++++. ", " .++++. ", " .+++.. ", " ....... ", " "}; static QPixmap pixmap_forwardsDiagonal_hover(resize_forwardsDiagonal_hover_xpm); const char * resize_forwardsDiagonal_nohover_xpm[] = { "13 13 2 1", " c None", ". c #000000", " ", " ....... ", " ...... ", " ...... ", " ....... ", " . ........ ", " ........... ", " ........ . ", " ....... ", " ...... ", " ...... ", " ....... ", " "}; static QPixmap pixmap_forwardsDiagonal_nohover(resize_forwardsDiagonal_nohover_xpm); const char * resize_backwardsDiagonal_hover_xpm[] = { "13 13 3 1", " c None", ". c #000000", "+ c #8EA5D0", " ", " ....... ", " .+++.. ", " .++++. ", " .+++++. ", " ..+++++. . ", " ...+++++... ", " . .+++++.. ", " .+++++. ", " .++++. ", " ..+++. ", " ....... ", " "}; static QPixmap pixmap_backwardsDiagonal_hover(resize_backwardsDiagonal_hover_xpm); const char * resize_backwardsDiagonal_nohover_xpm[] = { "13 13 2 1", " c None", ". c #000000", " ", " ....... ", " ...... ", " ...... ", " ....... ", " ........ . ", " ........... ", " . ........ ", " ....... ", " ...... ", " ...... ", " ....... ", " "}; static QPixmap pixmap_backwardsDiagonal_nohover(resize_backwardsDiagonal_nohover_xpm); const char * resize_vertical_hover_xpm[] = { "13 13 3 1", " c None", ". c #000000", "+ c #8EA5D0", " . ", " ... ", " ..+.. ", " ..+++.. ", " ..+++++.. ", " .+++. ", " .+++. ", " .+++. ", " ..+++++.. ", " ..+++.. ", " ..+.. ", " ... ", " . "}; static QPixmap pixmap_vertical_hover(resize_vertical_hover_xpm); const char * resize_vertical_nohover_xpm[] = { "13 13 2 1", " c None", ". c #000000", " . ", " ... ", " ..... ", " ....... ", " ......... ", " ..... ", " ..... ", " ..... ", " ......... ", " ....... ", " ..... ", " ... ", " . "}; static QPixmap pixmap_vertical_nohover(resize_vertical_nohover_xpm); const char * resize_horizontal_hover_xpm[] = { "13 13 3 1", " c None", ". c #000000", "+ c #8EA5D0", " ", " ", " . . ", " .. .. ", " ..+...+.. ", " ..+++++++.. ", "..+++++++++..", " ..+++++++.. ", " ..+...+.. ", " .. .. ", " . . ", " ", " "}; static QPixmap pixmap_horizontal_hover(resize_horizontal_hover_xpm); const char * resize_horizontal_nohover_xpm[] = { "13 13 2 1", " c None", ". c #000000", " ", " ", " . . ", " .. .. ", " ......... ", " ........... ", ".............", " ........... ", " ......... ", " .. .. ", " . . ", " ", " "}; static QPixmap pixmap_horizontal_nohover(resize_horizontal_nohover_xpm); const char * point_rect_hover_xpm[] = { "13 13 3 1", " c None", ". c #000000", "+ c #8EA5D0", " ", " ", " ", " ", " ", " ..... ", " .+++. ", " .+++. ", " .+++. ", " ..... ", " ", " ", " "}; static QPixmap pixmap_point_rect_hover(point_rect_hover_xpm); const char * point_rect_nohover_xpm[] = { "13 13 3 1", " c None", ". c #000000", "+ c #FFFFFF", " ", " ", " ", " ", " ", " ..... ", " .+++. ", " .+++. ", " .+++. ", " ..... ", " ", " ", " "}; static QPixmap pixmap_point_rect_nohover(point_rect_nohover_xpm); if (hover) { switch(drawType) { case ResizeHandle::dt_resize_forwardsDiagonal: return pixmap_forwardsDiagonal_hover; case ResizeHandle::dt_resize_backwardsDiagonal: return pixmap_backwardsDiagonal_hover; case ResizeHandle::dt_resize_vertical: return pixmap_vertical_hover; case ResizeHandle::dt_resize_horizontal: return pixmap_horizontal_hover; case ResizeHandle::dt_point_rect: return pixmap_point_rect_hover; case ResizeHandle::dt_point_crosshair: case ResizeHandle::dt_rotate_topLeft: case ResizeHandle::dt_rotate_topRight: case ResizeHandle::dt_rotate_bottomRight: case ResizeHandle::dt_rotate_bottomLeft: qWarning() << Q_FUNC_INFO << "ResizeHandle of type " << drawType << " does not have an image." << endl; } } else { switch(drawType) { case ResizeHandle::dt_resize_forwardsDiagonal: return pixmap_forwardsDiagonal_nohover; case ResizeHandle::dt_resize_backwardsDiagonal: return pixmap_backwardsDiagonal_nohover; case ResizeHandle::dt_resize_vertical: return pixmap_vertical_nohover; case ResizeHandle::dt_resize_horizontal: return pixmap_horizontal_nohover; case ResizeHandle::dt_point_rect: return pixmap_point_rect_nohover; case ResizeHandle::dt_point_crosshair: case ResizeHandle::dt_rotate_topLeft: case ResizeHandle::dt_rotate_topRight: case ResizeHandle::dt_rotate_bottomRight: case ResizeHandle::dt_rotate_bottomLeft: qWarning() << Q_FUNC_INFO << "ResizeHandle of type " << drawType << " does not have an image." << endl; } } static QPixmap blank; return blank; } //END class ResizeHandle diff --git a/src/resizeoverlay.h b/src/resizeoverlay.h index 8114e078..cff24deb 100644 --- a/src/resizeoverlay.h +++ b/src/resizeoverlay.h @@ -1,282 +1,282 @@ /*************************************************************************** * Copyright (C) 2005 by David Saxton * * david@bluehaze.org * * * * 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. * ***************************************************************************/ #ifndef RESIZEOVERLAY_H #define RESIZEOVERLAY_H // This file contains class definitions for different types of resizing and rotating //#include // 2018.10.16 - not needed #include "canvasitems.h" -#include -#include -#include -#include +#include +#include +#include +#include class MechanicsItem; class ResizeHandle; class ResizeOverlay; class QEvent; typedef QMap< int, QPointer > ResizeHandleMap; /** @author David Saxton */ class ResizeHandle : /* public QObject, */ public KtlQCanvasRectangle { Q_OBJECT public: /** * Convenience enumeration for resize handle positioning. Note: this class * does not make use of the values in this enumeration - it is just * provided here for use by other classes. */ enum ResizeHandlePosition { rhp_none, rhp_topLeft, rhp_topMiddle, rhp_topRight, rhp_middleRight, rhp_bottomRight, rhp_bottomMiddle, rhp_bottomLeft, rhp_middleLeft, rhp_center, rhp_start, rhp_end }; enum DrawType { // Draws a simple rectangle dt_point_rect, // Crosshair dt_point_crosshair, // Straight arrows in various directions dt_resize_forwardsDiagonal, dt_resize_backwardsDiagonal, dt_resize_vertical, dt_resize_horizontal, // Arrows as part of an arc dt_rotate_topLeft, dt_rotate_topRight, dt_rotate_bottomRight, dt_rotate_bottomLeft }; ResizeHandle( ResizeOverlay *resizeOverlay, int id, DrawType drawType, int xsnap, int ysnap ); ~ResizeHandle() override; int id() const { return m_id; } void setDrawType( DrawType drawType ); void moveRH( double x, double y ); void setHover( bool hover ); static const QPixmap& handlePixmap( DrawType drawType, bool hover ); QPolygon areaPoints () const override; public slots: void slotMoveByX( double dx ) { moveBy( dx, 0 ); } void slotMoveByY( double dy ) { moveBy( 0, dy ); } signals: void rhMovedBy( int id, double dx, double dy ); void rhMovedByX( double dx ); void rhMovedByY( double dy ); protected: void drawShape( QPainter &p ) override; DrawType m_drawType; bool b_hover; // If true, then paint resize handle for mouse hovering over int m_id; int m_xsnap; int m_ysnap; ResizeOverlay *p_resizeOverlay; }; typedef QList ResizeHandleList; /** @author David Saxton */ class ResizeOverlay : public QObject { Q_OBJECT public: ResizeOverlay( Item *parent ); ~ResizeOverlay() override; Item *parentItem() const { return p_item; } /** * Shows / hides the resize handles. They are hidden by default. */ void showResizeHandles( bool show ); /** * Sets the visibility. Visibility is true by default. */ void setVisible( bool visible ); /** * Reinherit this function to determine whether the X coordinate of the spot * that the resize handle has moved into is valid or not */ virtual bool isValidXPos( ResizeHandle *rh ) { Q_UNUSED(rh); return true; } /** * Reinherit this function to determine whether the Y coordinate of the spot * that the resize handle has moved into is valid or not */ virtual bool isValidYPos( ResizeHandle *rh ) { Q_UNUSED(rh); return true; } public slots: void slotMoveAllResizeHandles( double dx, double dy ); protected slots: virtual void slotResizeHandleMoved( int id, double dx, double dy ) = 0; protected: /** * Connects up the given resize handles so that they are always kept at the * same horizontal coordinate */ void syncX( ResizeHandle *rh1, ResizeHandle *rh2 ); void syncX( ResizeHandle *rh1, ResizeHandle *rh2, ResizeHandle *rh3 ); /** * Connects up the given resize handles so that they are always kept at the * same vertical coordinate */ void syncY( ResizeHandle *rh1, ResizeHandle *rh2 ); void syncY( ResizeHandle *rh1, ResizeHandle *rh2, ResizeHandle *rh3 ); /** * Returns a pointer to the ResizeHandle with the given id, or 0 if no such * handle exists */ ResizeHandle *resizeHandle( int id ); /** * Creates and attaches the resize handle with the given DrawType. If a * ResizeHandle with the given id exists, will return a pointer to that * instead */ ResizeHandle *createResizeHandle( int id, ResizeHandle::DrawType drawType, int xsnap = 1, int ysnap = 1 ); /** * Removes the resize handle with the given id */ void removeResizeHandle( int id ); Item *p_item; ResizeHandleMap m_resizeHandleMap; bool b_showResizeHandles; bool b_visible; }; /** @author David Saxton */ class MechanicsItemOverlay : public ResizeOverlay { Q_OBJECT public: MechanicsItemOverlay( MechanicsItem *parent ); ~MechanicsItemOverlay() override; public slots: void slotUpdateResizeHandles(); protected slots: void slotResizeHandleMoved( int id, double dx, double dy ) override; protected: ResizeHandle *m_tl; ResizeHandle *m_tm; ResizeHandle *m_tr; ResizeHandle *m_mr; ResizeHandle *m_br; ResizeHandle *m_bm; ResizeHandle *m_bl; ResizeHandle *m_ml; ResizeHandle *m_mm; MechanicsItem *p_mechanicsItem; }; /** @author David Saxton */ class RectangularOverlay : public ResizeOverlay { Q_OBJECT public: RectangularOverlay( Item *item, int xsnap = 1, int ysnap = 1 ); void removeTopMiddle(); void removeBotMiddle(); /** * Get the size rectangle from the position of the handles. If the size * is invalid (e.g. the parent Item does not consider it a valid size, * then *ok is set to false; otherwise to true. * @returns the sizerect, regardless of whether or not it is valid */ QRect getSizeRect( bool *ok = nullptr, bool *widthOk = nullptr, bool *heightOk = nullptr ) const; bool isValidXPos( ResizeHandle *rh ) override; bool isValidYPos( ResizeHandle *rh ) override; public slots: void slotUpdateResizeHandles(); protected slots: void slotResizeHandleMoved( int id, double dx, double dy ) override; protected: ResizeHandle *m_tl; ResizeHandle *m_tm; ResizeHandle *m_tr; ResizeHandle *m_mr; ResizeHandle *m_br; ResizeHandle *m_bm; ResizeHandle *m_bl; ResizeHandle *m_ml; }; /** @author David Saxton */ class LineOverlay : public ResizeOverlay { Q_OBJECT public: LineOverlay( Item * parent ); QPoint startPoint() const; QPoint endPoint() const; public slots: void slotUpdateResizeHandles(); protected slots: void slotResizeHandleMoved( int id, double dx, double dy ) override; protected: ResizeHandle * m_pStart; ResizeHandle * m_pEnd; }; #endif diff --git a/src/simulator.cpp b/src/simulator.cpp index 87e86eed..56267dc7 100644 --- a/src/simulator.cpp +++ b/src/simulator.cpp @@ -1,389 +1,389 @@ /*************************************************************************** * Copyright (C) 2005 by David Saxton * * david@bluehaze.org * * * * 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 "component.h" #include "gpsimprocessor.h" #include "pin.h" #include "simulator.h" #include "switch.h" // #include -#include -#include +#include +#include #include #include using namespace std; //BEGIN class Simulator // Simulator *Simulator::m_pSelf = 0; // static K3StaticDeleter staticSimulatorDeleter; Q_GLOBAL_STATIC(Simulator, globalSimulator); bool Simulator::isDestroyedSim() { return globalSimulator.isDestroyed(); } Simulator *Simulator::self() { // if (!m_pSelf) // staticSimulatorDeleter.setObject(m_pSelf, new Simulator()); // // return m_pSelf; return globalSimulator; } Simulator::Simulator() : m_bIsSimulating(false), m_llNumber(0), m_stepNumber(0), m_currentChain(0) { m_gpsimProcessors = new list; m_componentCallbacks = new list; m_components = new list; m_ordinaryCircuits = new list; // use integer math for these, update period is double. unsigned max = unsigned(LOGIC_UPDATE_RATE / LINEAR_UPDATE_RATE); for (unsigned i = 0; i < max; i++) { m_pStartStepCallback[i] = nullptr; } LogicConfig lc; m_pChangedLogicStart = new LogicOut(lc, false); m_pChangedLogicLast = m_pChangedLogicStart; m_pChangedCircuitStart = new Circuit; m_pChangedCircuitLast = m_pChangedCircuitStart; m_stepTimer = new QTimer(this); connect(m_stepTimer, SIGNAL(timeout()), this, SLOT(step())); slotSetSimulating(true); // start the timer } Simulator::~Simulator() { delete m_pChangedLogicStart; delete m_pChangedCircuitStart; delete m_gpsimProcessors; delete m_components; delete m_componentCallbacks; delete m_ordinaryCircuits; } long long Simulator::time() const { return m_stepNumber * LOGIC_UPDATE_PER_STEP + m_llNumber; } void Simulator::step() { if (!m_bIsSimulating) return; // We are called a thousand times a second (the maximum allowed by QTimer), // so divide the LINEAR_UPDATE_RATE by 1e3 for the number of loops we need // to do. const unsigned maxSteps = unsigned(LINEAR_UPDATE_RATE / SIMULATOR_STEP_INTERVAL_MS); for (unsigned i = 0; i < maxSteps; ++i) { // here starts 1 linear step m_stepNumber++; // Update the non-logic parts of the simulation { list::iterator components_end = m_components->end(); for (list::iterator component = m_components->begin(); component != components_end; component++) { (*component)->stepNonLogic(); } } { list::iterator circuits_end = m_ordinaryCircuits->end(); for (list::iterator circuit = m_ordinaryCircuits->begin(); circuit != circuits_end; circuit++) { (*circuit)->doNonLogic(); } } // Update the logic parts of our simulation //const unsigned max = unsigned(LOGIC_UPDATE_RATE / LINEAR_UPDATE_RATE); // 2015.09.27 - use contants for logic updates for (m_llNumber = 0; m_llNumber < LOGIC_UPDATE_PER_STEP; ++m_llNumber) { // here starts 1 logic update // Update the logic components { list::iterator callbacks_end = m_componentCallbacks->end(); for (list::iterator callback = m_componentCallbacks->begin(); callback != callbacks_end; callback++) { callback->callback(); } } if (m_pStartStepCallback[m_llNumber]) { list::iterator callbacks_end = m_pStartStepCallback[m_llNumber]->end(); for (list::iterator callback = m_pStartStepCallback[m_llNumber]->begin(); callback != callbacks_end; callback++) { (*callback)->callback(); // should we delete the list entry? no } } delete m_pStartStepCallback[m_llNumber]; m_pStartStepCallback[m_llNumber] = nullptr; #ifndef NO_GPSIM // Update the gpsim processors { list::iterator processors_end = m_gpsimProcessors->end(); for (list::iterator processor = m_gpsimProcessors->begin(); processor != processors_end; processor++) { (*processor)->executeNext(); } } #endif // why do we change this here instead of later? int prevChain = m_currentChain; m_currentChain ^= 1; // Update the non-logic circuits if (Circuit *changed = m_pChangedCircuitStart->nextChanged(prevChain)) { QSet canAddChangedSet; for ( Circuit *circuit = changed; circuit && (!canAddChangedSet.contains(circuit)); circuit = circuit->nextChanged(prevChain)) { circuit->setCanAddChanged(true); canAddChangedSet.insert(circuit); } m_pChangedCircuitStart->setNextChanged(nullptr, prevChain); m_pChangedCircuitLast = m_pChangedCircuitStart; do { Circuit *next = changed->nextChanged(prevChain); changed->setNextChanged(nullptr, prevChain); changed->doLogic(); changed = next; } while (changed); } // Call the logic callbacks if (LogicOut *changed = m_pChangedLogicStart->nextChanged(prevChain)) { for (LogicOut *out = changed; out; out = out->nextChanged(prevChain)) out->setCanAddChanged(true); m_pChangedLogicStart->setNextChanged(nullptr, prevChain); m_pChangedLogicLast = m_pChangedLogicStart; do { LogicOut *next = changed->nextChanged(prevChain); changed->setNextChanged(nullptr, prevChain); double v = changed->isHigh() ? changed->outputHighVoltage() : 0.0; for (PinList::iterator it = changed->pinListBegin; it != changed->pinListEnd; ++it) { if (Pin *pin = *it) pin->setVoltage(v); } LogicIn *logicCallback = changed; while (logicCallback) { logicCallback->callCallback(); logicCallback = logicCallback->nextLogic(); } changed = next; } while (changed); } } } } void Simulator::slotSetSimulating(bool simulate) { if (m_bIsSimulating == simulate) return; if (simulate) { m_stepTimer->start(SIMULATOR_STEP_INTERVAL_MS); } else { m_stepTimer->stop(); } m_bIsSimulating = simulate; emit simulatingStateChanged(simulate); } void Simulator::createLogicChain(LogicOut *logicOut, const LogicInList &logicInList, const PinList &pinList) { if (!logicOut) return; bool state = logicOut->outputState(); logicOut->setUseLogicChain(true); logicOut->pinList = pinList; logicOut->pinListBegin = logicOut->pinList.begin(); logicOut->pinListEnd = logicOut->pinList.end(); LogicIn *last = logicOut; const LogicInList::const_iterator end = logicInList.end(); for (LogicInList::const_iterator it = logicInList.begin(); it != end; ++it) { LogicIn *next = *it; last->setNextLogic(next); last->setLastState(state); last = next; } last->setNextLogic(nullptr); last->setLastState(state); // Mark it as changed, if it isn't already changed... LogicOut *changed = m_pChangedLogicStart->nextChanged(m_currentChain); while (changed) { if (changed == logicOut) return; changed = changed->nextChanged(m_currentChain); } addChangedLogic(logicOut); logicOut->setCanAddChanged(false); if (!m_logicChainStarts.contains(logicOut)) m_logicChainStarts << logicOut; } void Simulator::attachGpsimProcessor(GpsimProcessor *cpu) { m_gpsimProcessors->push_back(cpu); } void Simulator::detachGpsimProcessor(GpsimProcessor *cpu) { m_gpsimProcessors->remove(cpu); } void Simulator::attachComponentCallback(Component *component, VoidCallbackPtr function) { m_componentCallbacks->push_back(ComponentCallback(component, function)); } void Simulator::attachComponent(Component *component) { if (!component || !component->doesStepNonLogic()) return; m_components->push_back(component); } void Simulator::detachComponent(Component *component) { m_components->remove(component); detachComponentCallbacks(*component); } static Component *compx; bool pred1(ComponentCallback &x) { return x.component() == compx; } void Simulator::detachComponentCallbacks(Component &component) { compx = &component; m_componentCallbacks->remove_if(pred1); } void Simulator::attachCircuit(Circuit *circuit) { if (!circuit) return; m_ordinaryCircuits->push_back(circuit); // if ( circuit->canAddChanged() ) { addChangedCircuit(circuit); circuit->setCanAddChanged(false); // } } void Simulator::removeLogicInReferences(LogicIn *logicIn) { if (!logicIn) return; QList::iterator end = m_logicChainStarts.end(); for (QList::iterator it = m_logicChainStarts.begin(); it != end; ++it) { LogicIn *logicCallback = *it; while (logicCallback) { if (logicCallback->nextLogic() == logicIn) logicCallback->setNextLogic(logicCallback->nextLogic()->nextLogic()); logicCallback = logicCallback->nextLogic(); } } } void Simulator::removeLogicOutReferences(LogicOut *logic) { m_logicChainStarts.removeAll(logic); // Any changes to the code below will probably also apply to Simulator::detachCircuit if (m_pChangedLogicLast == logic) { LogicOut *previous_1 = nullptr; LogicOut *previous_2 = nullptr; for (LogicOut *logic = m_pChangedLogicStart; logic;) { if (previous_1) previous_2 = previous_1; previous_1 = logic; logic = logic->nextChanged(m_currentChain); } m_pChangedLogicLast = previous_2; } for (unsigned chain = 0; chain < 2; ++chain) { for (LogicOut *prevChanged = m_pChangedLogicStart; prevChanged; prevChanged = prevChanged->nextChanged(chain)) { LogicOut *nextChanged = prevChanged->nextChanged(chain); if (nextChanged == logic) prevChanged->setNextChanged(nextChanged->nextChanged(chain), chain); } } } void Simulator::detachCircuit(Circuit *circuit) { if (!circuit) return; m_ordinaryCircuits->remove(circuit); // Any changes to the code below will probably also apply to Simulator::removeLogicOutReferences if (m_pChangedCircuitLast == circuit) { Circuit *previous_1 = nullptr; Circuit *previous_2 = nullptr; for (Circuit * circuit = m_pChangedCircuitStart; circuit;) { if (previous_1) previous_2 = previous_1; previous_1 = circuit; circuit = circuit->nextChanged(m_currentChain); } m_pChangedCircuitLast = previous_2; } for (unsigned chain = 0; chain < 2; ++chain) { for (Circuit *prevChanged = m_pChangedCircuitStart; prevChanged; prevChanged = prevChanged->nextChanged(chain)) { Circuit *nextChanged = prevChanged->nextChanged(chain); if (nextChanged == circuit) prevChanged->setNextChanged(nextChanged->nextChanged(chain), chain); } } } //END class Simulator diff --git a/src/textdocument.cpp b/src/textdocument.cpp index e9158676..6d80719a 100644 --- a/src/textdocument.cpp +++ b/src/textdocument.cpp @@ -1,1250 +1,1250 @@ /*************************************************************************** * Copyright (C) 2005 by David Saxton * * david@bluehaze.org * * * * 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 "asmformatter.h" #include "asminfo.h" #include "asmparser.h" #include "debugmanager.h" #include "docmanager.h" #include "documentiface.h" #include "filemetainfo.h" #include "gpsimprocessor.h" #include "ktechlab.h" #include "language.h" #include "languagemanager.h" #include "microselectwidget.h" #include "programmerdlg.h" #include "symbolviewer.h" #include "textdocument.h" #include "textview.h" #include "gpsimprocessor.h" // #include -#include -#include +#include +#include #include #include #include #include #include #include #include bool TextDocument::isUndoAvailable() const { //return (m_doc->undoCount() != 0); return true; // TODO FIXME fix undo/redo } bool TextDocument::isRedoAvailable() const { //return (m_doc->redoCount() != 0); return true; // TODO FIXME fix undo/redo } TextDocument *TextDocument::constructTextDocument( const QString& caption, const char *name ) { TextDocument *textDocument = new TextDocument( caption, name); if( textDocument->m_constructorSuccessful ) return textDocument; delete textDocument; return nullptr; } TextDocument::TextDocument( const QString &caption, const char *name ) : Document( caption, name ), m_doc(nullptr) { m_constructorSuccessful = false; #ifndef NO_GPSIM m_bOwnDebugger = false; b_lockSyncBreakpoints = false; m_lastDebugLineAt = -1; m_pDebugger = nullptr; #endif m_pLastTextOutputTarget = nullptr; m_guessedCodeType = TextDocument::ct_unknown; m_type = Document::dt_text; //m_bookmarkActions.setAutoDelete(true); // TODO see if this genereates memory leaks m_pDocumentIface = new TextDocumentIface(this); KTextEditor::Editor* editor = KTextEditor::Editor::instance(); m_doc = editor->createDocument(this); if(!m_doc) { KMessageBox::sorry( KTechlab::self(), i18n("Failed to create editor") ); return; } guessScheme(); connect( m_doc, SIGNAL(undoChanged()), this, SIGNAL(undoRedoStateChanged()) ); connect( m_doc, SIGNAL(undoChanged()), this, SLOT(slotSyncModifiedStates()) ); connect( m_doc, SIGNAL(textChanged(KTextEditor::Document *)), this, SLOT(slotSyncModifiedStates()) ); // connect( m_doc, SIGNAL(selectionChanged()), this, SLOT(slotSelectionmChanged()) ); // 2016.09.08 - moved to TextView connect( m_doc, SIGNAL(marksChanged(KTextEditor::Document*)), this, SLOT(slotUpdateMarksInfo()) ); KTextEditor::MarkInterface *markIface = qobject_cast( m_doc ); if (!markIface) { KMessageBox::sorry( KTechlab::self(), i18n("Failed to create MarkInterface") ); return; } markIface->setMarkDescription((KTextEditor::MarkInterface::MarkTypes)Breakpoint, i18n("Breakpoint")); markIface->setMarkPixmap((KTextEditor::MarkInterface::MarkTypes)Breakpoint, *inactiveBreakpointPixmap()); markIface->setMarkPixmap((KTextEditor::MarkInterface::MarkTypes)ActiveBreakpoint, *activeBreakpointPixmap()); markIface->setMarkPixmap((KTextEditor::MarkInterface::MarkTypes)ReachedBreakpoint, *reachedBreakpointPixmap()); markIface->setMarkPixmap((KTextEditor::MarkInterface::MarkTypes)DisabledBreakpoint, *disabledBreakpointPixmap()); markIface->setMarkPixmap((KTextEditor::MarkInterface::MarkTypes)ExecutionPoint, *executionPointPixmap()); markIface->setEditableMarks( Bookmark | Breakpoint ); m_constructorSuccessful = true; } TextDocument::~TextDocument() { if( !m_constructorSuccessful ) return; debugStop(); if ( KTechlab::self() ) { ViewList::iterator end = m_viewList.end(); for ( ViewList::iterator it = m_viewList.begin(); it != end; ++it ) { if ( TextView * tv = dynamic_cast( (View*)*it) ) { KTextEditor::View * kv = tv->kateView(); KTechlab::self()->factory()->removeClient( kv ); } } } delete m_doc; delete m_pDocumentIface; } bool TextDocument::fileClose() { const QUrl u = url(); if ( !u.isEmpty() ) fileMetaInfo()->grabMetaInfo( u, this ); return Document::fileClose(); } TextView* TextDocument::textView() const { return static_cast(activeView()); } View * TextDocument::createView( ViewContainer *viewContainer, uint viewAreaId, const char *name ) { TextView * textView = new TextView( this, viewContainer, viewAreaId, name ); fileMetaInfo()->initializeFromMetaInfo( url(), textView ); handleNewView(textView); return textView; } KTextEditor::View* TextDocument::createKateView( QWidget *parent, const char * /*name*/ ) { //return static_cast((m_doc->createView( parent, name ))->qt_cast("Kate::View")); return m_doc->createView( parent /*, name */); } void TextDocument::cut() { if (textView()) textView()->cut(); } void TextDocument::copy() { if (textView()) textView()->copy(); } void TextDocument::paste() { if (textView()) textView()->paste(); } void TextDocument::setText( const QString & text, bool asInitial ) { if ( asInitial ) { disconnect( m_doc, SIGNAL(undoChanged()), this, SIGNAL(undoRedoStateChanged()) ); disconnect( m_doc, SIGNAL(undoChanged()), this, SLOT(slotSyncModifiedStates()) ); disconnect( m_doc, SIGNAL(textChanged(KTextEditor::Document *)), this, SLOT(slotSyncModifiedStates()) ); } const ViewList::iterator end = m_viewList.end(); for ( ViewList::iterator it = m_viewList.begin(); it != end; ++it ) (static_cast((View*)*it))->saveCursorPosition(); m_doc->setText(text); for ( ViewList::iterator it = m_viewList.begin(); it != end; ++it ) (static_cast((View*)*it))->restoreCursorPosition(); if ( asInitial ) { //m_doc->clearUndo(); // TODO FIXME //m_doc->clearRedo(); // TODO FIXME qWarning() << "TextDocument::clearUndo TODO"; setModified(false); connect( m_doc, SIGNAL(undoChanged()), this, SIGNAL(undoRedoStateChanged()) ); connect( m_doc, SIGNAL(undoChanged()), this, SLOT(slotSyncModifiedStates()) ); connect( m_doc, SIGNAL(textChanged(KTextEditor::Document *)), this, SLOT(slotSyncModifiedStates()) ); } } void TextDocument::undo() { textView()->undo(); slotSyncModifiedStates(); } void TextDocument::redo() { textView()->redo(); slotSyncModifiedStates(); } void TextDocument::slotSyncModifiedStates() { setModified( m_doc->isModified() ); } void TextDocument::setModified( bool modified ) { if ( (modified == b_modified) && (modified == isModified()) ) { return; } m_doc->setModified(modified); b_modified = modified; emit modifiedStateChanged(); } void TextDocument::guessScheme( bool allowDisable ) { // And specific file actions depending on the current type of file QString fileName = url().fileName(); QString extension = fileName.right( fileName.length() - fileName.lastIndexOf('.') - 1 ); if ( extension == "asm" || extension == "src" || extension == "inc" ) slotInitLanguage(ct_asm); else if ( extension == "hex" ) slotInitLanguage(ct_hex); else if ( extension == "basic" || extension == "microbe" ) slotInitLanguage(ct_microbe); else if ( extension == "c" ) slotInitLanguage(ct_c); else if ( m_guessedCodeType != TextDocument::ct_unknown ) slotInitLanguage(m_guessedCodeType); else if ( allowDisable && activeView() ) textView()->disableActions(); } void TextDocument::slotInitLanguage( CodeType type ) { QString hlName; switch (type) { case ct_asm: hlName = "PicAsm"; break; case ct_c: hlName = "C"; break; case ct_hex: break; case ct_microbe: hlName = "Microbe"; break; case ct_unknown: break; } if ( !hlName.isEmpty() ) { //int i = 0; // 2017.10.01 - comment out unused variable //int hlModeCount = m_doc->hlModeCount(); QStringList hlModes = m_doc->highlightingModes(); //while ( ihlModeName(i) != hlName ) // i++; while (!hlModes.isEmpty()) { if (hlModes.first() == hlName) { break; } hlModes.removeFirst(); } //m_doc->setHlMode(i); if (!hlModes.isEmpty()) { m_doc->setHighlightingMode(hlModes.first()); } } m_guessedCodeType = type; ViewList::iterator end = m_viewList.end(); for ( ViewList::iterator it = m_viewList.begin(); it != end; ++it ) { if ( TextView * tv = dynamic_cast( (View*)*it ) ) tv->initCodeActions(); } } void TextDocument::formatAssembly() { AsmFormatter formatter; //QStringList lines = QStringList::split( "\n", m_doc->text(), true ); // 2018.12.01 QStringList lines = m_doc->text().split("\n", QString::KeepEmptyParts); setText( formatter.tidyAsm(lines), false ); setModified(true); } void TextDocument::fileSave( const QUrl& url ) { if ( m_doc->url() != url ) { qCritical() << Q_FUNC_INFO << "Error: Kate::View url and passed url do not match; cannot save." << endl; return; } if ( activeView() && (textView()->save()) ) saveDone(); } void TextDocument::fileSaveAs() { if ( activeView() && (textView()->saveAs()) ) saveDone(); // Our modified state may not have changed, but we emit this to force the // main window to update our caption. emit modifiedStateChanged(); } void TextDocument::saveDone() { setURL( m_doc->url() ); guessScheme(false); setModified(false); emit modifiedStateChanged(); } bool TextDocument::openURL( const QUrl& url ) { m_doc->openUrl(url); setURL(url); fileMetaInfo()->initializeFromMetaInfo( url, this ); guessScheme(); #ifndef NO_GPSIM DebugManager::self()->urlOpened( this ); #endif return true; } void TextDocument::setLastTextOutputTarget( TextDocument * target ) { m_pLastTextOutputTarget = target; } QString TextDocument::outputFilePath( const QString &ext ) { QString filePath = url().path(); if ( filePath.isEmpty() ) { QTemporaryFile f(QDir::tempPath() + QLatin1String("/ktechlab_XXXXXX") + ext); f.setAutoRemove(false); if (!f.open()) { qWarning() << Q_FUNC_INFO << " Failed to open temporary file"; return QString(); } QTextStream out(&f); out << m_doc->text(); f.close(); DocManager::self()->associateDocument(QUrl::fromLocalFile(f.fileName()), this ); return f.fileName(); } if ( isModified() ) { fileSave(); } return filePath; } void TextDocument::slotConvertTo( QAction *action ) { int target = action->data().toInt(); switch ( (ConvertToTarget)target ) { case TextDocument::MicrobeOutput: break; case TextDocument::AssemblyOutput: convertToAssembly(); break; case TextDocument::HexOutput: convertToHex(); break; case TextDocument::PICOutput: convertToPIC(); break; } } void TextDocument::convertToAssembly() { QString filePath; bool showPICSelect = false; ProcessOptions::ProcessPath::MediaType toType; if ( m_guessedCodeType == TextDocument::ct_microbe ) { toType = ProcessOptions::ProcessPath::AssemblyAbsolute; filePath = outputFilePath(".microbe"); } else if ( m_guessedCodeType == TextDocument::ct_hex ) { toType = ProcessOptions::ProcessPath::Disassembly; filePath = outputFilePath(".hex"); } else if ( m_guessedCodeType == TextDocument::ct_c ) { toType = ProcessOptions::ProcessPath::AssemblyRelocatable; filePath = outputFilePath(".c"); showPICSelect = true; } else { qCritical() << "Could not get file type for converting to assembly!"<setAllowedAsmSet( AsmInfo::PIC14 | AsmInfo::PIC16 ); dlg.setOutputExtension(".asm"); dlg.setFilter( QString("*.asm *.src *.inc|%1 (*.asm, *.src, *.inc)\n*|%2").arg(i18n("Assembly Code")).arg(i18n("All Files")) ); const int accepted = dlg.exec(); if (accepted != QDialog::Accepted) return; ProcessOptions o( dlg.info() ); o.setTextOutputTarget( m_pLastTextOutputTarget, this, SLOT(setLastTextOutputTarget( TextDocument* )) ); o.setInputFiles(QStringList( filePath) ); o.setProcessPath( ProcessOptions::ProcessPath::path( ProcessOptions::guessMediaType(filePath), toType ) ); LanguageManager::self()->compile(o); } void TextDocument::convertToHex() { QString filePath; bool showPICSelect = false; if ( m_guessedCodeType == TextDocument::ct_microbe ) filePath = outputFilePath(".microbe"); else if ( m_guessedCodeType == TextDocument::ct_asm ) { filePath = outputFilePath(".asm"); showPICSelect = true; // FIXME if we use shared libs, then we need the pic type } else if ( m_guessedCodeType == TextDocument::ct_c ) { filePath = outputFilePath(".c"); showPICSelect = true; } else { qCritical() << "Could not get file type for converting to hex!"<setAllowedAsmSet( AsmInfo::PIC14 | AsmInfo::PIC16 ); const int accepted = dlg.exec(); if (accepted != QDialog::Accepted) return; ProcessOptions o( dlg.info() ); o.setTextOutputTarget( m_pLastTextOutputTarget, this, SLOT(setLastTextOutputTarget( TextDocument* )) ); o.setInputFiles(QStringList(filePath)); o.setProcessPath( ProcessOptions::ProcessPath::path( ProcessOptions::guessMediaType(filePath), ProcessOptions::ProcessPath::Program ) ); LanguageManager::self()->compile(o); } void TextDocument::convertToPIC() { QString filePath; QString picID; switch ( m_guessedCodeType ) { case ct_microbe: filePath = outputFilePath(".microbe"); break; case ct_asm: { filePath = outputFilePath(".asm"); AsmParser p( filePath ); p.parse(); picID = p.picID(); break; } case ct_c: filePath = outputFilePath(".c"); break; case ct_hex: filePath = outputFilePath(".hex"); break; case ct_unknown: qCritical() << "Could not get file type for converting to hex!"<microSelect()->setAllowedAsmSet( AsmInfo::PIC14 | AsmInfo::PIC16 ); const int accepted = dlg->exec(); if (accepted != QDialog::Accepted) { dlg->deleteLater(); return; } ProcessOptions o; dlg->initOptions( & o ); o.setInputFiles( QStringList(filePath )); o.setProcessPath( ProcessOptions::ProcessPath::path( ProcessOptions::guessMediaType(filePath), ProcessOptions::ProcessPath::Pic ) ); LanguageManager::self()->compile( o ); dlg->deleteLater(); } void TextDocument::print() { //KTextEditor::printInterface(m_doc)->print (); // TODO FIXME textView()->print(); } // 2016.09.08 - moved to TextView // void TextDocument::slotSelectionmChanged() // { // KTechlab::self()->actionByName( "edit_cut" )->setEnabled( /* m_doc->hasSelection () */ m_doc->activeView()->selection() ); // KTechlab::self()->actionByName( "edit_copy" )->setEnabled( /*m_doc->hasSelection () */ m_doc->activeView()->selection() ); // } IntList TextDocument::bookmarkList() const { IntList bookmarkList; typedef QHash MarkList; KTextEditor::MarkInterface *iface = qobject_cast( m_doc ); if (!iface) return IntList(); //MarkList markList = m_doc->marks(); const MarkList &markList = iface->marks(); // Find out what marks need adding to our internal lists //for ( KTextEditor::Mark * mark = markList.first(); mark; mark = markList.next() ) for (MarkList::const_iterator itMark = markList.begin(); itMark != markList.end(); ++itMark) { const KTextEditor::Mark * mark = itMark.value(); if ( mark->type & Bookmark ) bookmarkList += mark->line; } return bookmarkList; } void TextDocument::slotUpdateMarksInfo() { if ( !KTechlab::self() ) return; if ( activeView() ) textView()->slotUpdateMarksInfo(); #ifndef NO_GPSIM syncBreakpoints(); #endif // Update our list of bookmarks in the menu KTechlab::self()->unplugActionList("bookmark_actionlist"); m_bookmarkActions.clear(); //QPtrList markList = m_doc->marks(); typedef QHash MarkList; KTextEditor::MarkInterface *iface = qobject_cast( m_doc ); if (!iface) return ; //MarkList markList = m_doc->marks(); MarkList markList = iface->marks(); // Find out what marks need adding to our internal lists //for ( KTextEditor::Mark * mark = markList.first(); mark; mark = markList.next() ) //{ for (MarkList::iterator itMark = markList.begin(); itMark != markList.end(); ++itMark) { KTextEditor::Mark * mark = itMark.value(); if ( mark->type & Bookmark ) { QString actionCaption = i18n("%1 - %2", QString::number( mark->line+1 ), m_doc->text(KTextEditor::Range( mark->line, 0, mark->line, 100 /* FIXME arbitrary */)) ); QString actionName = QString("bookmark_%1").arg(QString::number(mark->line)); /* QAction * a = new QAction( actionCaption, 0, this, SLOT(slotBookmarkRequested()), this, actionName ); */ QAction * a = new QAction( actionCaption, this); a->setObjectName(actionName.toLatin1().data()); connect(a, SIGNAL(triggered(bool)), this, SLOT(slotBookmarkRequested())); m_bookmarkActions.append(a); } } KTechlab::self()->plugActionList( "bookmark_actionlist", m_bookmarkActions ); } void TextDocument::slotBookmarkRequested() { const QObject * s = sender(); if (!s) return; QString name = s->objectName(); if ( !name.startsWith("bookmark_") ) return; name.remove("bookmark_"); int line = -1; bool ok; line = name.toInt(&ok); if ( ok && line >= 0 && activeView() ) (static_cast(activeView()))->gotoLine(line); } void TextDocument::setBookmarks( const IntList &lines ) { clearBookmarks(); const IntList::const_iterator end = lines.end(); for ( IntList::const_iterator it = lines.begin(); it != end; ++it ) setBookmark( *it, true ); } void TextDocument::clearBookmarks() { //QPtrList markList = m_doc->marks(); typedef QHash MarkList; KTextEditor::MarkInterface *iface = qobject_cast( m_doc ); if (!iface) return; //MarkList markList = m_doc->marks(); MarkList markList = iface->marks(); // Find out what marks need adding to our internal lists //for ( KTextEditor::Mark * mark = markList.first(); mark; mark = markList.next() ) //{ for (MarkList::iterator itMark = markList.begin(); itMark != markList.end(); ++itMark) { KTextEditor::Mark * mark = itMark.value(); if ( mark->type & Bookmark ) iface->removeMark( mark->line, Bookmark ); } slotUpdateMarksInfo(); } void TextDocument::setBookmark( uint line, bool isBookmark ) { KTextEditor::MarkInterface *iface = qobject_cast( m_doc ); if (!iface) return; if (isBookmark) iface->addMark( line, Bookmark ); else iface->removeMark( line, Bookmark ); } void TextDocument::setBreakpoints( const IntList &lines ) { #ifndef NO_GPSIM clearBreakpoints(); const IntList::const_iterator end = lines.end(); for ( IntList::const_iterator it = lines.begin(); it != end; ++it ) setBreakpoint( *it, true ); #endif // !NO_GPSIM } IntList TextDocument::breakpointList() const { IntList breakpointList; #ifndef NO_GPSIM //typedef QPtrList MarkList; typedef QHash MarkList; KTextEditor::MarkInterface *iface = qobject_cast( m_doc ); if (!iface) return IntList(); //MarkList markList = m_doc->marks(); MarkList markList = iface->marks(); // note: this will copy // Find out what marks need adding to our internal lists //for ( KTextEditor::Mark * mark = markList.first(); mark; mark = markList.next() ) for (MarkList::iterator itMark = markList.begin(); itMark != markList.end(); ++itMark) { KTextEditor::Mark * mark = itMark.value(); if ( mark->type & Breakpoint ) breakpointList += mark->line; } #endif // !NO_GPSIM return breakpointList; } void TextDocument::setBreakpoint( uint line, bool isBreakpoint ) { #ifndef NO_GPSIM KTextEditor::MarkInterface *iface = qobject_cast( m_doc ); if (!iface) return ; if (isBreakpoint) { iface->addMark( line, Breakpoint ); if (m_pDebugger) m_pDebugger->setBreakpoint( m_debugFile, line, true ); } else { iface->removeMark( line, Breakpoint ); if (m_pDebugger) m_pDebugger->setBreakpoint( m_debugFile, line, false ); } #endif // !NO_GPSIM } void TextDocument::debugRun() { #ifndef NO_GPSIM if (m_pDebugger) { m_pDebugger->gpsim()->setRunning(true); slotInitDebugActions(); return; } switch ( guessedCodeType() ) { case ct_unknown: KMessageBox::sorry( nullptr, i18n("Unknown code type."), i18n("Cannot debug") ); return; case ct_hex: KMessageBox::sorry( nullptr, i18n("Cannot debug hex."), i18n("Cannot debug") ); return; case ct_microbe: m_bLoadDebuggerAsHLL = true; m_debugFile = outputFilePath(".microbe"); break; case ct_asm: m_bLoadDebuggerAsHLL = false; m_debugFile = outputFilePath(".asm"); break; case ct_c: m_bLoadDebuggerAsHLL = true; m_debugFile = outputFilePath(".c"); break; } m_symbolFile = GpsimProcessor::generateSymbolFile( m_debugFile, this, SLOT(slotCODCreationSucceeded()), SLOT(slotCODCreationFailed()) ); #endif // !NO_GPSIM } void TextDocument::debugInterrupt() { #ifndef NO_GPSIM if (!m_pDebugger) return; m_pDebugger->gpsim()->setRunning(false); slotInitDebugActions(); #endif // !NO_GPSIM } void TextDocument::debugStop() { #ifndef NO_GPSIM if ( !m_pDebugger || !m_bOwnDebugger ) return; m_pDebugger->gpsim()->deleteLater(); m_pDebugger = nullptr; slotDebugSetCurrentLine( SourceLine() ); slotInitDebugActions(); #endif // !NO_GPSIM } void TextDocument::debugStep() { #ifndef NO_GPSIM if (!m_pDebugger) return; m_pDebugger->stepInto(); #endif // !NO_GPSIM } void TextDocument::debugStepOver() { #ifndef NO_GPSIM if (!m_pDebugger) return; m_pDebugger->stepOver(); #endif // !NO_GPSIM } void TextDocument::debugStepOut() { #ifndef NO_GPSIM if (!m_pDebugger) return; m_pDebugger->stepOut(); #endif // !NO_GPSIM } void TextDocument::slotDebugSetCurrentLine( const SourceLine & line ) { #ifndef NO_GPSIM KTextEditor::MarkInterface *iface = qobject_cast( m_doc ); if (!iface) return; int textLine = line.line(); if ( DocManager::self()->findDocument(QUrl::fromLocalFile(line.fileName())) != this ) textLine = -1; iface->removeMark( m_lastDebugLineAt, ExecutionPoint ); iface->addMark( textLine, ExecutionPoint ); if ( activeView() ) textView()->setCursorPosition( textLine, 0 ); m_lastDebugLineAt = textLine; #endif // !NO_GPSIM } void TextDocument::slotInitDebugActions() { #ifndef NO_GPSIM if ( m_pDebugger ) { if ( m_pDebugger->gpsim()->isRunning() ) slotDebugSetCurrentLine( SourceLine() ); else slotDebugSetCurrentLine( m_pDebugger->currentLine() ); } if ( activeView() ) textView()->slotInitDebugActions(); #endif // !NO_GPSIM } void TextDocument::slotCODCreationSucceeded() { #ifndef NO_GPSIM GpsimProcessor * gpsim = new GpsimProcessor( m_symbolFile, this ); if (m_bLoadDebuggerAsHLL) gpsim->setDebugMode( GpsimDebugger::HLLDebugger ); else gpsim->setDebugMode( GpsimDebugger::AsmDebugger ); setDebugger( gpsim->currentDebugger(), true ); #endif // !NO_GPSIM } void TextDocument::slotCODCreationFailed() { #ifndef NO_GPSIM m_debugFile = QString::null; m_symbolFile = QString::null; #endif // !NO_GPSIM } void TextDocument::slotDebuggerDestroyed() { #ifndef NO_GPSIM slotDebugSetCurrentLine( SourceLine() ); m_pDebugger = nullptr; m_debugFile = QString::null; slotInitDebugActions(); #endif // !NO_GPSIM } #ifndef NO_GPSIM void TextDocument::clearBreakpoints() { //QPtrList markList = m_doc->marks(); //typedef QPtrList MarkList; typedef QHash MarkList; KTextEditor::MarkInterface *iface = qobject_cast( m_doc ); if (!iface) return; //MarkList markList = m_doc->marks(); MarkList markList = iface->marks(); // note: this will copy // Find out what marks need adding to our internal lists //for ( KTextEditor::Mark * mark = markList.first(); mark; mark = markList.next() ) for (MarkList::iterator itMark = markList.begin(); itMark != markList.end(); ++itMark) { KTextEditor::Mark * mark = itMark.value(); if ( mark->type & Bookmark ) iface->removeMark( mark->line, Breakpoint ); } slotUpdateMarksInfo(); } void TextDocument::syncBreakpoints() { if (b_lockSyncBreakpoints) return; // We don't really care about synching marks if we aren't debugging / aren't able to take use of the marks if (!m_pDebugger) return; b_lockSyncBreakpoints = true; //QPtrList markList = m_doc->marks(); //typedef QPtrList MarkList; typedef QHash MarkList; KTextEditor::MarkInterface *iface = qobject_cast( m_doc ); if (!iface) return; //MarkList markList = m_doc->marks(); MarkList markList = iface->marks(); // note: this will copy IntList bpList; // Find out what marks need adding to our internal lists //for ( KTextEditor::Mark * mark = markList.first(); mark; mark = markList.next() ) for (MarkList::iterator itMark = markList.begin(); itMark != markList.end(); ++itMark) { KTextEditor::Mark *mark = itMark.value(); const int line = mark->line; if ( mark->type & Breakpoint ) bpList.append(line); if ( mark->type == ExecutionPoint ) m_lastDebugLineAt = line; } m_pDebugger->setBreakpoints( m_debugFile, bpList ); b_lockSyncBreakpoints = false; } bool TextDocument::debuggerIsRunning() const { return m_pDebugger; } bool TextDocument::debuggerIsStepping() const { return m_pDebugger && !m_pDebugger->gpsim()->isRunning(); } void TextDocument::setDebugger( GpsimDebugger * debugger, bool ownDebugger ) { if ( debugger == m_pDebugger ) return; // If we create a gpsim, then we may get called by DebugManager, which will // try to claim we don't own it. So if we have a symbol file waiting, thne // wait until we are called from its successful creation if ( !m_symbolFile.isEmpty() && !ownDebugger ) return; // Reset it for use next time m_symbolFile = QString::null; if (m_bOwnDebugger) delete m_pDebugger; m_pDebugger = debugger; m_bOwnDebugger = ownDebugger; if (!m_pDebugger) return; if ( m_debugFile.isEmpty() ) m_debugFile = url().path(); connect( m_pDebugger, SIGNAL(destroyed()), this, SLOT(slotDebuggerDestroyed()) ); connect( m_pDebugger->gpsim(), SIGNAL(runningStatusChanged(bool )), this, SLOT(slotInitDebugActions()) ); connect( m_pDebugger, SIGNAL(lineReached(const SourceLine &)), this, SLOT(slotDebugSetCurrentLine(const SourceLine &)) ); m_pDebugger->setBreakpoints( m_debugFile, breakpointList() ); slotInitDebugActions(); if ( !m_pDebugger->gpsim()->isRunning() ) slotDebugSetCurrentLine( m_pDebugger->currentLine() ); if ( this == dynamic_cast(DocManager::self()->getFocusedDocument()) ) SymbolViewer::self()->setContext( m_pDebugger->gpsim() ); } #endif // !NO_GPSIM const QPixmap* TextDocument::inactiveBreakpointPixmap() { const char*breakpoint_gr_xpm[]={ "11 16 6 1", "c c #c6c6c6", "d c #2c2c2c", "# c #000000", ". c None", "a c #ffffff", "b c #555555", "...........", "...........", "...#####...", "..#aaaaa#..", ".#abbbbbb#.", "#abbbbbbbb#", "#abcacacbd#", "#abbbbbbbb#", "#abcacacbd#", "#abbbbbbbb#", ".#bbbbbbb#.", "..#bdbdb#..", "...#####...", "...........", "...........", "..........."}; static QPixmap pixmap( breakpoint_gr_xpm ); return &pixmap; } const QPixmap* TextDocument::activeBreakpointPixmap() { const char* breakpoint_xpm[]={ "11 16 6 1", "c c #c6c6c6", ". c None", "# c #000000", "d c #840000", "a c #ffffff", "b c #ff0000", "...........", "...........", "...#####...", "..#aaaaa#..", ".#abbbbbb#.", "#abbbbbbbb#", "#abcacacbd#", "#abbbbbbbb#", "#abcacacbd#", "#abbbbbbbb#", ".#bbbbbbb#.", "..#bdbdb#..", "...#####...", "...........", "...........", "..........."}; static QPixmap pixmap( breakpoint_xpm ); return &pixmap; } const QPixmap* TextDocument::reachedBreakpointPixmap() { const char*breakpoint_bl_xpm[]={ "11 16 7 1", "a c #c0c0ff", "# c #000000", "c c #0000c0", "e c #0000ff", "b c #dcdcdc", "d c #ffffff", ". c None", "...........", "...........", "...#####...", "..#ababa#..", ".#bcccccc#.", "#acccccccc#", "#bcadadace#", "#acccccccc#", "#bcadadace#", "#acccccccc#", ".#ccccccc#.", "..#cecec#..", "...#####...", "...........", "...........", "..........."}; static QPixmap pixmap( breakpoint_bl_xpm ); return &pixmap; } const QPixmap* TextDocument::disabledBreakpointPixmap() { const char*breakpoint_wh_xpm[]={ "11 16 7 1", "a c #c0c0ff", "# c #000000", "c c #0000c0", "e c #0000ff", "b c #dcdcdc", "d c #ffffff", ". c None", "...........", "...........", "...#####...", "..#ddddd#..", ".#ddddddd#.", "#ddddddddd#", "#ddddddddd#", "#ddddddddd#", "#ddddddddd#", "#ddddddddd#", ".#ddddddd#.", "..#ddddd#..", "...#####...", "...........", "...........", "..........."}; static QPixmap pixmap( breakpoint_wh_xpm ); return &pixmap; } const QPixmap* TextDocument::executionPointPixmap() { const char*exec_xpm[]={ "11 16 4 1", "a c #00ff00", "b c #000000", ". c None", "# c #00c000", "...........", "...........", "...........", "#a.........", "#aaa.......", "#aaaaa.....", "#aaaaaaa...", "#aaaaaaaaa.", "#aaaaaaa#b.", "#aaaaa#b...", "#aaa#b.....", "#a#b.......", "#b.........", "...........", "...........", "..........."}; static QPixmap pixmap( exec_xpm ); return &pixmap; } diff --git a/src/textdocument.h b/src/textdocument.h index c61d5ed8..04d88159 100644 --- a/src/textdocument.h +++ b/src/textdocument.h @@ -1,257 +1,257 @@ /*************************************************************************** * Copyright (C) 2004-2005 by David Saxton * * david@bluehaze.org * * * * 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. * ***************************************************************************/ #ifndef TEXTDOCUMENT_H #define TEXTDOCUMENT_H #include "config.h" #include "document.h" #include "gpsimprocessor.h" -#include +#include // #include //#include #include #include class GpsimDebugger; class SourceLine; class TextView; namespace KTextEditor { class View; class Document; } typedef QList IntList; /** @author David Saxton */ class TextDocument : public Document { Q_OBJECT public: ~TextDocument() override; enum CodeType { ct_unknown, ct_asm, ct_c, ct_hex, ct_microbe }; enum MarkType { Bookmark = KTextEditor::MarkInterface::markType01, Breakpoint = KTextEditor::MarkInterface::markType02, ActiveBreakpoint = KTextEditor::MarkInterface::markType03, ReachedBreakpoint = KTextEditor::MarkInterface::markType04, DisabledBreakpoint = KTextEditor::MarkInterface::markType05, ExecutionPoint = KTextEditor::MarkInterface::markType06 }; View *createView( ViewContainer *viewContainer, uint viewAreaId, const char *name = nullptr ) override; /** * Attempts to construct a new TextDocument object and returns a pointer to * it if successful, or 0 if it failed. * @returns pointer to constructed object, or 0 if there was a problem */ static TextDocument *constructTextDocument( const QString& caption, const char *name = nullptr ); /** * @returns the guessed code type that this file is */ CodeType guessedCodeType() const { return m_guessedCodeType; } /** * Set the given lines as all bookmarks */ void setBookmarks( const IntList &lines ); /** * Set the given line to a bookmark (or not) */ void setBookmark( uint line, bool isBookmark ); /** * @return List of bookmarks */ IntList bookmarkList() const; /** * Set the given lines as all breakpoints */ void setBreakpoints( const IntList &lines ); /** * Set the given line to a breakpoint (or not ) */ void setBreakpoint( uint line, bool isBreakpoint ); /** * @return List of breakpoints */ IntList breakpointList() const; #ifndef NO_GPSIM /** * Attach ourselves to the given debugger. * @param ownDebugger whether we have permission to delete it. */ void setDebugger( GpsimDebugger * debugger, bool ownDebugger ); GpsimDebugger * debugger() const { return m_pDebugger; } bool ownDebugger() const { return m_bOwnDebugger; } /** * Returns true if the debugger is running (this includes when we are in * stepping mode) */ bool debuggerIsRunning() const; /** * Returns true if we are in stepping more */ bool debuggerIsStepping() const; QString debugFile() const { return m_debugFile; } virtual void clearBreakpoints(); #endif bool openURL(const QUrl& url) override; void fileSave(const QUrl& url); /** * Set the document to the given text, making the document unmodified, and * reseting the undo/redo history/ * @param asInitial whether the next should be treated as if we had just * opened the file (no undo/redo history, and unmodified). */ void setText( const QString & text, bool asInitial ); /** * Attempts to guess the filetype from the file extension, and load an * appropriate highlighting/etc * @param allowDisable If false, will simply keep the old scheme if nothing * appropriate is found */ void guessScheme( bool allowDisable = true ); void fileSave() override { fileSave(url()); } void fileSaveAs() override; void print() override; void setModified( bool modified ) override; KTextEditor::View* createKateView( QWidget *parent, const char *name = nullptr ); void undo() override; void redo() override; void cut() override; void copy() override; void paste() override; bool isModified() const override { return m_doc->isModified(); } bool isUndoAvailable() const override ; // { return (m_doc->undoCount() != 0); } bool isRedoAvailable() const override ; // { return (m_doc->redoCount() != 0); } void clearBookmarks(); bool fileClose() override; static const QPixmap* inactiveBreakpointPixmap(); static const QPixmap* activeBreakpointPixmap(); static const QPixmap* reachedBreakpointPixmap(); static const QPixmap* disabledBreakpointPixmap(); static const QPixmap* executionPointPixmap(); /** * Returns a TextView pointer to the active TextView (if there is one) */ TextView *textView() const; enum ConvertToTarget { MicrobeOutput, // (not used) AssemblyOutput, HexOutput, PICOutput }; KTextEditor::Document * kateDocument() const { return m_doc; } public slots: /** * @param target as ConvertToTarget */ void slotConvertTo( QAction *action ); void convertToAssembly() override; void convertToHex() override; void convertToPIC() override; void formatAssembly(); void debugRun() override; void debugInterrupt() override; void debugStep() override; void debugStepOver(); void debugStepOut(); void debugStop() override; void slotInitLanguage( CodeType type ); /** * Called when change line / toggle marks */ void slotUpdateMarksInfo(); void slotDebugSetCurrentLine( const SourceLine & line ); /** * Initialize the actions appropriate for when the debugger is running * or stepping */ void slotInitDebugActions(); protected: /** * Returns a filepath with the editor's contents in. If the url of this file * is non-empty (i.e. the user has already saved the file), then that url is * returned. Otherwise, a temporary file with the given extension (ext) is * created, and the location of this file is returned. */ QString outputFilePath( const QString &ext ); void saveDone(); #ifndef NO_GPSIM /** * Looks at the list of marks returned by Kate, and syncs them with the * marks that we know about */ void syncBreakpoints(); int m_lastDebugLineAt; // Last line with a debug point reached mark bool m_bLoadDebuggerAsHLL; #endif KTextEditor::Document *m_doc; QPointer m_pLastTextOutputTarget; private slots: void setLastTextOutputTarget( TextDocument * target ); void slotSyncModifiedStates(); void slotCODCreationSucceeded(); void slotCODCreationFailed(); void slotDebuggerDestroyed(); void slotBookmarkRequested(); // void slotSelectionmChanged(); // 2016.09.08 - moved to TextView private: TextDocument( const QString& caption, const char *name = nullptr ); bool m_constructorSuccessful; CodeType m_guessedCodeType; QList m_bookmarkActions; #ifndef NO_GPSIM bool b_lockSyncBreakpoints; // Used to avoid calling syncMarks() when we are currently doing so bool m_bOwnDebugger; QPointer m_pDebugger; QString m_symbolFile; QString m_debugFile; #endif }; #endif diff --git a/src/textview.cpp b/src/textview.cpp index d1806d12..0f94f172 100644 --- a/src/textview.cpp +++ b/src/textview.cpp @@ -1,723 +1,723 @@ /*************************************************************************** * Copyright (C) 2005 by David Saxton * * david@bluehaze.org * * * * 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. * ***************************************************************************/ #define protected public #include #undef protected #include "asmformatter.h" #include "config.h" #include "filemetainfo.h" #include "gpsimprocessor.h" #include "ktechlab.h" #include "symbolviewer.h" #include "textdocument.h" #include "textview.h" #include "variablelabel.h" #include "viewiface.h" #include "ktlfindqobjectchild.h" //#include // ? #include // #include "kateview.h" -#include #include #include // #include #include #include -#include -#include -#include -#include +#include +#include +#include +#include +#include //#include -#include -#include -#include +#include +#include #include +#include //BEGIN class TextView TextView::TextView( TextDocument * textDocument, ViewContainer *viewContainer, uint viewAreaId, const char *name ) : View( textDocument, viewContainer, viewAreaId, name ) { m_view = textDocument->createKateView(this); m_view->insertChildClient(this); KActionCollection * ac = actionCollection(); //BEGIN Convert To * Actions //KToolBarPopupAction * pa = new KToolBarPopupAction( i18n("Convert to"), "fork", 0, 0, 0, ac, "program_convert" ); KToolBarPopupAction * pa = new KToolBarPopupAction( QIcon::fromTheme("fork"), i18n("Convert To"), ac); pa->setObjectName("program_convert"); pa->setDelayed(false); ac->addAction(pa->objectName(), pa); QMenu * m = pa->menu(); m->setTitle( i18n("Convert To") ); QAction *actToMicrobe = m->addAction( QIcon::fromTheme( "convert_to_microbe" ), i18n("Microbe")); actToMicrobe->setData( TextDocument::MicrobeOutput ); m->addAction( QIcon::fromTheme( "convert_to_assembly" ), i18n("Assembly"))->setData( TextDocument::AssemblyOutput ); m->addAction( QIcon::fromTheme( "convert_to_hex" ), i18n("Hex"))->setData( TextDocument::HexOutput ); m->addAction( QIcon::fromTheme( "convert_to_pic" ), i18n("PIC (upload)"))->setData( TextDocument::PICOutput ); connect( m, SIGNAL(triggered(QAction*)), textDocument, SLOT(slotConvertTo(QAction*)) ); //m->setItemEnabled( TextDocument::MicrobeOutput, false ); // 2018.12.02 actToMicrobe->setEnabled(false); ac->addAction(pa->objectName(), pa); //END Convert To * Actions { //new QAction( i18n("Format Assembly Code"), "", Qt::Key_F12, textDocument, SLOT(formatAssembly()), ac, "format_asm" ); QAction *action = new QAction( i18n("Format Assembly Code"), ac); action->setObjectName("format_asm"); action->setShortcut(Qt::Key_F12); connect(action, SIGNAL(triggered(bool)), textDocument, SLOT(formatAssembly())); ac->addAction(action->objectName(), action); } #ifndef NO_GPSIM //BEGIN Debug Actions { //new QAction( i18n("Set &Breakpoint"), 0, 0, this, SLOT(toggleBreakpoint()), ac, "debug_toggle_breakpoint" ); QAction *action = new QAction(i18n("Set &Breakpoint"), ac); action->setObjectName("debug_toggle_breakpoint"); connect(action, SIGNAL(triggered(bool)), this, SLOT(toggleBreakpoint())); ac->addAction(action->objectName(), action); } { //new QAction( i18n("Run"), "debug-run", 0, textDocument, SLOT(debugRun()), ac, "debug_run" ); QAction *action = new QAction( QIcon::fromTheme("debug-run"), i18n("Run"), ac); action->setObjectName("debug_run"); connect(action, SIGNAL(triggered(bool)), textDocument, SLOT(debugRun())); ac->addAction(action->objectName(), action); } { //new QAction( i18n("Interrupt"), "media-playback-pause", 0, textDocument, SLOT(debugInterrupt()), ac, "debug_interrupt" ); QAction *action = new QAction( QIcon::fromTheme("media-playback-pause"), i18n("Interrupt"), ac); action->setObjectName("debug_interrupt"); connect(action, SIGNAL(triggered(bool)), textDocument, SLOT(debugInterrupt())); ac->addAction(action->objectName(), action); } { //new QAction( i18n("Stop"), "process-stop", 0, textDocument, SLOT(debugStop()), ac, "debug_stop" ); QAction *action = new QAction( QIcon::fromTheme("process-stop"), i18n("Stop"), ac); action->setObjectName("debug_stop"); connect(action, SIGNAL(triggered(bool)), textDocument, SLOT(debugStop())); ac->addAction(action->objectName(), action); } { //new QAction( i18n("Step"), "debug-step-instruction", Qt::CTRL|Qt::ALT|Qt::Key_Right, textDocument, SLOT(debugStep()), ac, "debug_step" ); QAction *action = new QAction( QIcon::fromTheme("debug-step-instruction"), i18n("Step"), ac); action->setObjectName("debug_step"); action->setShortcut(Qt::CTRL|Qt::ALT|Qt::Key_Right); connect(action, SIGNAL(triggered(bool)), textDocument, SLOT(debugStep())); ac->addAction(action->objectName(), action); } { //new QAction( i18n("Step Over"), "debug-step-over", 0, textDocument, SLOT(debugStepOver()), ac, "debug_step_over" ); QAction *action = new QAction( QIcon::fromTheme("debug-step-over"), i18n("Step Over"), ac); action->setObjectName("debug_step_over"); connect(action, SIGNAL(triggered(bool)), textDocument, SLOT(debugStepOver())); ac->addAction(action->objectName(), action); } { //new QAction( i18n("Step Out"), "debug-step-out", 0, textDocument, SLOT(debugStepOut()), ac, "debug_step_out" ); QAction *action = new QAction( QIcon::fromTheme("debug-step-out"), i18n("Step Out"), ac); action->setObjectName("debug_step_out"); connect(action, SIGNAL(triggered(bool)), textDocument, SLOT(debugStepOut())); ac->addAction(action->objectName(), action); } //END Debug Actions #endif setXMLFile( "ktechlabtextui.rc" ); m_view->setXMLFile( "ktechlabkateui.rc" ); m_savedCursorLine = 0; m_savedCursorColumn = 0; m_pViewIface = new TextViewIface(this); setAcceptDrops(true); //m_view->installPopup( static_cast( KTechlab::self()->factory()->container( "ktexteditor_popup", KTechlab::self() ) ) ); m_view->setContextMenu( static_cast( KTechlab::self()->factory()->container( "ktexteditor_popup", KTechlab::self() ) ) ); //QWidget * internalView = static_cast( m_view->child( 0, "KateViewInternal" ) ); // 2018.12.02 QWidget * internalView = static_cast( ktlFindQObjectChild( m_view, "KateViewInternal" ) ); connect( m_view, SIGNAL(cursorPositionChanged(KTextEditor::View *, const KTextEditor::Cursor &)), this, SLOT(slotCursorPositionChanged()) ); connect( m_view, SIGNAL(selectionChanged(KTextEditor::View *)), this, SLOT(slotSelectionmChanged()) ); setFocusWidget( internalView ); connect( this, SIGNAL(focused( View* )), this, SLOT(gotFocus()) ); m_layout->insertWidget( 0, m_view ); slotCursorPositionChanged(); slotInitDebugActions(); initCodeActions(); #ifndef NO_GPSIM m_pTextViewLabel = new VariableLabel( this ); m_pTextViewLabel->hide(); TextViewEventFilter * eventFilter = new TextViewEventFilter( this ); connect( eventFilter, SIGNAL(wordHoveredOver( const QString&, int, int )), this, SLOT(slotWordHoveredOver( const QString&, int, int )) ); connect( eventFilter, SIGNAL(wordUnhovered()), this, SLOT(slotWordUnhovered()) ); internalView->installEventFilter( eventFilter ); #endif // TODO HACK disable some actions which collide with ktechlab's actions. // the proper solution would be to move the actions from KTechLab object level to document level for // all types of documents for (QAction *act: actionCollection()->actions()) { qDebug() << Q_FUNC_INFO << "act: " << act->text() << " shortcut " << act->shortcut() << ":" << act ; if ( ((act->objectName()) == QLatin1String("file_save")) || ((act->objectName()) == QLatin1String("file_save_as")) || ((act->objectName()) == QLatin1String("file_print")) || ((act->objectName()) == QLatin1String("edit_undo")) || ((act->objectName()) == QLatin1String("edit_redo")) || ((act->objectName()) == QLatin1String("edit_cut")) || ((act->objectName()) == QLatin1String("edit_copy")) || ((act->objectName()) == QLatin1String("edit_paste")) ) { act->setShortcutContext(Qt::WidgetWithChildrenShortcut); //act->setShortcutConfigurable(true); act->setShortcut(Qt::Key_unknown); qDebug() << Q_FUNC_INFO << "action " << act << " disabled"; } } } TextView::~TextView() { if ( KTechlab::self() ) { // 2017.01.09: do not crash on document close. factory has its clients removed in TextDocument::~TextDocument() //if ( KXMLGUIFactory * f = m_view->factory() ) // f->removeClient( m_view ); KTechlab::self()->addNoRemoveGUIClient( m_view ); } delete m_pViewIface; } bool TextView::closeView() { if ( textDocument() ) { const QUrl url = textDocument()->url(); if ( !url.isEmpty() ) fileMetaInfo()->grabMetaInfo(url, this ); } bool doClose = View::closeView(); if ( doClose ) KTechlab::self()->factory()->removeClient(m_view); return View::closeView(); } bool TextView::gotoLine( const int line ) { //return m_view->setCursorPosition( line, 0/*m_view->cursorColumn()*/ ); return m_view->setCursorPosition( KTextEditor::Cursor( line, 0/*m_view->cursorColumn()*/ ) ); } TextDocument *TextView::textDocument() const { return static_cast(document()); } void TextView::undo() { qDebug() << Q_FUNC_INFO; // note: quite a hack, but could not find any more decent way of getting to undo/redo interface // note: quite a hack, but could not find any more decent way of getting to undo/redo interface QAction* action = actionByName("edit_undo"); if (action) { action->trigger(); return; } qWarning() << Q_FUNC_INFO << "no edit_undo action in text view! no action taken"; } void TextView::redo() { qDebug() << Q_FUNC_INFO; // note: quite a hack, but could not find any more decent way of getting to undo/redo interface QAction* action = actionByName("edit_redo"); if (action) { action->trigger(); return; } qWarning() << Q_FUNC_INFO << "no edit_redo action in text view! no action taken"; } void TextView::cut() { //m_view-> cut(); if (!m_view->selection()) return; QClipboard *clipboard = QApplication::clipboard(); clipboard->setText( m_view->document()->text( m_view->selectionRange() ) ); m_view->document()->removeText(m_view->selectionRange()); } void TextView::copy() { //m_view->copy(); if (!m_view->selection()) return; QClipboard *clipboard = QApplication::clipboard(); clipboard->setText( m_view->document()->text( m_view->selectionRange() ) ); } void TextView::paste() { //m_view->paste(); QClipboard *clipboard = QApplication::clipboard(); m_view->document()->insertText( m_view->cursorPosition(), clipboard->text()); } void TextView::disableActions() { QMenu * tb = (dynamic_cast(actionByName("program_convert")))->menu(); const QList actions = tb->actions(); for(QAction *a : actions) { switch (a->data().toInt()) { case TextDocument::AssemblyOutput: case TextDocument::HexOutput: case TextDocument::PICOutput: a->setEnabled( false ); break; default: qDebug() << Q_FUNC_INFO << " skip action: " << a; } } //tb->setItemEnabled( TextDocument::AssemblyOutput, false ); // 2018.12.02 //tb->setItemEnabled( TextDocument::HexOutput, false ); //tb->setItemEnabled( TextDocument::PICOutput, false ); actionByName("format_asm")->setEnabled(false); #ifndef NO_GPSIM actionByName("debug_toggle_breakpoint")->setEnabled(false); #endif } //KTextEditor::View::saveResult TextView::save() { return m_view->save(); } bool TextView::save() { return (m_view->document()->documentSave()); } //KTextEditor::View::saveResult TextView::saveAs() { return m_view->saveAs(); } bool TextView::saveAs() { return m_view->document()->documentSaveAs(); } void TextView::print() { qDebug() << Q_FUNC_INFO; // note: quite a hack, but could not find any more decent way of getting to undo/redo interface QAction* action = actionByName("file_print"); if (action) { action->trigger(); return; } qWarning() << Q_FUNC_INFO << "no file_print action in text view! no action taken"; } void TextView::gotFocus() { #ifndef NO_GPSIM GpsimDebugger * debugger = textDocument()->debugger(); if ( !debugger || !debugger->gpsim() ) return; SymbolViewer::self()->setContext( debugger->gpsim() ); #endif } void TextView::slotSelectionmChanged() { KTechlab::self()->actionByName( "edit_cut" )->setEnabled( m_view->selection() ); KTechlab::self()->actionByName( "edit_copy" )->setEnabled( m_view->selection() ); } void TextView::initCodeActions() { disableActions(); QMenu * tb = (dynamic_cast(actionByName("program_convert")))->menu(); QAction *actHexOut = nullptr; QAction *actPicOut = nullptr; QAction *actAsmOut = nullptr; const QList actions = tb->actions(); for (QAction *a : actions) { switch (a->data().toInt()) { case TextDocument::AssemblyOutput: actAsmOut = a; break; case TextDocument::HexOutput: actHexOut = a; break; case TextDocument::PICOutput: actPicOut = a; break; default: qDebug() << Q_FUNC_INFO << " skip action: " << a; } } switch ( textDocument()->guessedCodeType() ) { case TextDocument::ct_asm: { //tb->setItemEnabled( TextDocument::HexOutput, true ); // 2018.12.02 //tb->setItemEnabled( TextDocument::PICOutput, true ); actHexOut->setEnabled( true ); actPicOut->setEnabled( true ); actionByName("format_asm")->setEnabled(true); #ifndef NO_GPSIM actionByName("debug_toggle_breakpoint")->setEnabled(true); slotInitDebugActions(); #endif break; } case TextDocument::ct_c: { //tb->setItemEnabled( TextDocument::AssemblyOutput, true ); //tb->setItemEnabled( TextDocument::HexOutput, true ); //tb->setItemEnabled( TextDocument::PICOutput, true ); actAsmOut->setEnabled( true ); actHexOut->setEnabled( true ); actPicOut->setEnabled( true ); break; } case TextDocument::ct_hex: { //tb->setItemEnabled( TextDocument::AssemblyOutput, true ); //tb->setItemEnabled( TextDocument::PICOutput, true ); actAsmOut->setEnabled( true ); actPicOut->setEnabled( true ); break; } case TextDocument::ct_microbe: { //tb->setItemEnabled( TextDocument::AssemblyOutput, true ); //tb->setItemEnabled( TextDocument::HexOutput, true ); //tb->setItemEnabled( TextDocument::PICOutput, true ); actAsmOut->setEnabled( true ); actHexOut->setEnabled( true ); actPicOut->setEnabled( true ); break; } case TextDocument::ct_unknown: { break; } } } void TextView::setCursorPosition( uint line, uint col ) { //m_view->setCursorPosition( line, col ); m_view->setCursorPosition( KTextEditor::Cursor( line, col ) ); } unsigned TextView::currentLine() { //unsigned l,c ; KTextEditor::Cursor curs = m_view->cursorPosition(); return curs.line(); } unsigned TextView::currentColumn() { //unsigned l,c ; KTextEditor::Cursor curs = m_view->cursorPosition(); // &l, &c ); return curs.column(); } void TextView::saveCursorPosition() { KTextEditor::Cursor curs = m_view->cursorPosition(); // &m_savedCursorLine, &m_savedCursorColumn ); m_savedCursorLine = curs.line(); m_savedCursorColumn = curs.column(); } void TextView::restoreCursorPosition() { m_view->setCursorPosition( KTextEditor::Cursor( m_savedCursorLine, m_savedCursorColumn ) ); } void TextView::slotCursorPositionChanged() { uint line, column; KTextEditor::Cursor curs = m_view->cursorPosition(); //&line, &column ); line = curs.line(); column = curs.column(); m_statusBar->setStatusText(i18n(" Line: %1 Col: %2 ", QString::number(line+1), QString::number(column+1))); slotUpdateMarksInfo(); } void TextView::slotUpdateMarksInfo() { #ifndef NO_GPSIM uint l,c ; KTextEditor::Cursor curs = m_view->cursorPosition(); // &l, &c ); l = curs.line(); c = curs.column(); KTextEditor::MarkInterface *iface = qobject_cast( m_view->document() ); // if ( m_view->getDoc()->mark(l) & TextDocument::Breakpoint ) if (iface->mark(l) & TextDocument::Breakpoint) actionByName("debug_toggle_breakpoint")->setText( i18n("Clear &Breakpoint") ); else actionByName("debug_toggle_breakpoint")->setText( i18n("Set &Breakpoint") ); #endif } void TextView::slotInitDebugActions() { #ifndef NO_GPSIM bool isRunning = textDocument()->debuggerIsRunning(); bool isStepping = textDocument()->debuggerIsStepping(); bool ownDebugger = textDocument()->ownDebugger(); actionByName("debug_run")->setEnabled( !isRunning || isStepping ); actionByName("debug_interrupt")->setEnabled(isRunning && !isStepping); actionByName("debug_stop")->setEnabled(isRunning && ownDebugger); actionByName("debug_step")->setEnabled(isRunning && isStepping); actionByName("debug_step_over")->setEnabled(isRunning && isStepping); actionByName("debug_step_out")->setEnabled(isRunning && isStepping); #endif // !NO_GPSIM } void TextView::toggleBreakpoint() { #ifndef NO_GPSIM uint l,c ; KTextEditor::Cursor curs = m_view->cursorPosition(); // &l, &c ); l = curs.line(); c = curs.column(); //const bool isBreakpoint = m_view->getDoc()->mark(l) & TextDocument::Breakpoint; KTextEditor::MarkInterface *iface = qobject_cast(m_view->document()); if (!iface) return; const bool isBreakpoint = iface->mark(l) & TextDocument::Breakpoint; //textDocument()->setBreakpoint( l, !(m_view->getDoc()->mark(l) & TextDocument::Breakpoint) ); textDocument()->setBreakpoint(l, !isBreakpoint); #endif // !NO_GPSIM } void TextView::slotWordHoveredOver( const QString & word, int line, int /*col*/ ) { #ifndef NO_GPSIM // We're only interested in popping something up if we currently have a debugger running GpsimProcessor * gpsim = textDocument()->debugger() ? textDocument()->debugger()->gpsim() : nullptr; if ( !gpsim ) { m_pTextViewLabel->hide(); return; } // Find out if the word that we are hovering over is the operand data //KTextEditor::EditInterface * e = (KTextEditor::EditInterface*)textDocument()->kateDocument()->qt_cast("KTextEditor::EditInterface"); //InstructionParts parts( e->textLine( unsigned(line) ) ); InstructionParts parts(textDocument()->kateDocument()->line(line)); if ( !parts.operandData().contains( word ) ) return; if ( RegisterInfo * info = gpsim->registerMemory()->fromName( word ) ) m_pTextViewLabel->setRegister( info, info->name() ); else { int operandAddress = textDocument()->debugger()->programAddress( textDocument()->debugFile(), line ); if ( operandAddress == -1 ) { m_pTextViewLabel->hide(); return; } int regAddress = gpsim->operandRegister( operandAddress ); if ( regAddress != -1 ) m_pTextViewLabel->setRegister( gpsim->registerMemory()->fromAddress( regAddress ), word ); else { m_pTextViewLabel->hide(); return; } } m_pTextViewLabel->move( mapFromGlobal( QCursor::pos() ) + QPoint( 0, 20 ) ); m_pTextViewLabel->show(); #endif // !NO_GPSIM } void TextView::slotWordUnhovered() { #ifndef NO_GPSIM m_pTextViewLabel->hide(); #endif // !NO_GPSIM } //END class TextView //BEGIN class TextViewEventFilter TextViewEventFilter::TextViewEventFilter( TextView * textView ) { m_hoverStatus = Sleeping; m_pTextView = textView; m_lastLine = m_lastCol = -1; //((KTextEditor::TextHintInterface*)textView->kateView()->qt_cast("KTextEditor::TextHintInterface"))->enableTextHints(0); { KTextEditor::View * view = textView->kateView(); KTextEditor::TextHintInterface *iface = qobject_cast(view); if (iface) { #warning "TextHintProvider" //iface->enableTextHints(0); //iface->registerTextHintProvider(); // TODO //connect( textView->kateView(), SIGNAL(needTextHint(int, int, QString &)), this, SLOT(slotNeedTextHint( int, int, QString& )) ); connect( view, SIGNAL(needTextHint(const KTextEditor::Cursor &, QString &)), this, SLOT(slotNeedTextHint(const KTextEditor::Cursor &, QString &)) ); } else { qWarning() << "KTextEditor::View does not implement TextHintInterface for " << view; } } m_pHoverTimer = new QTimer( this ); connect( m_pHoverTimer, SIGNAL(timeout()), this, SLOT(hoverTimeout()) ); m_pSleepTimer = new QTimer( this ); connect( m_pSleepTimer, SIGNAL(timeout()), this, SLOT(gotoSleep()) ); m_pNoWordTimer = new QTimer( this ); connect( m_pNoWordTimer, SIGNAL(timeout()), this, SLOT(slotNoWordTimeout()) ); } bool TextViewEventFilter::eventFilter( QObject *, QEvent * e ) { // qDebug() << Q_FUNC_INFO << "e->type() = " << e->type() << endl; if ( e->type() == QEvent::MouseMove ) { if ( !m_pNoWordTimer->isActive() ) m_pNoWordTimer->start( 10 ); return false; } if ( e->type() == QEvent::FocusOut || e->type() == QEvent::FocusIn || e->type() == QEvent::MouseButtonPress || e->type() == QEvent::Leave || e->type() == QEvent::Wheel ) { // user moved focus somewhere - hide the tip and sleep if ( ((QFocusEvent*)e)->reason() != Qt::PopupFocusReason ) updateHovering( nullptr, -1, -1 ); } return false; } void TextViewEventFilter::hoverTimeout() { m_pSleepTimer->stop(); m_hoverStatus = Active; emit wordHoveredOver( m_lastWord, m_lastLine, m_lastCol ); } void TextViewEventFilter::gotoSleep() { m_hoverStatus = Sleeping; m_lastWord = QString::null; emit wordUnhovered(); m_pHoverTimer->stop(); } void TextViewEventFilter::slotNoWordTimeout() { updateHovering( nullptr, -1, -1 ); } void TextViewEventFilter::updateHovering( const QString & currentWord, int line, int col ) { if ( (currentWord == m_lastWord) && (line == m_lastLine) ) return; m_lastWord = currentWord; m_lastLine = line; m_lastCol = col; if ( currentWord.isEmpty() ) { if ( m_hoverStatus == Active ) m_hoverStatus = Hidden; emit wordUnhovered(); if ( !m_pSleepTimer->isActive() ) { m_pSleepTimer->setSingleShot( true ); m_pSleepTimer->start( 2000 /*, true */ ); } return; } if ( m_hoverStatus != Sleeping ) { emit wordHoveredOver( currentWord, line, col ); } else { m_pHoverTimer->setSingleShot( true ); m_pHoverTimer->start( 700 /*, true */ ); } } static inline bool isWordLetter( const QString & s ) { return (s.length() == 1) && (s[0].isLetterOrNumber() || s[0] == '_'); } void TextViewEventFilter::slotNeedTextHint(const KTextEditor::Cursor &position, QString & /*text*/) { int line = position.line(); int col = position.column(); m_pNoWordTimer->stop(); //KTextEditor::EditInterface * e = (KTextEditor::EditInterface*)m_pTextView->textDocument()->kateDocument()->qt_cast("KTextEditor::EditInterface"); KTextEditor::Document *d = m_pTextView->textDocument()->kateDocument(); // Return if we aren't currently in a word if ( !isWordLetter( d->text( KTextEditor::Range( line, col, line, col+1 ) ) ) ) { updateHovering( QString::null, line, col ); return; } // Find the start of the word int wordStart = col; do wordStart--; while ( wordStart > 0 && isWordLetter( d->text( KTextEditor::Range( line, wordStart, line, wordStart+1 ) ) ) ); wordStart++; // Find the end of the word int wordEnd = col; do wordEnd++; while ( isWordLetter( d->text( KTextEditor::Range( line, wordEnd, line, wordEnd+1 ) ) ) ); QString t = d->text( KTextEditor::Range( line, wordStart, line, wordEnd ) ); updateHovering( t, line, col ); } //END class TextViewEventFilter diff --git a/src/textview.h b/src/textview.h index 0e63887d..6e9eb435 100644 --- a/src/textview.h +++ b/src/textview.h @@ -1,200 +1,200 @@ /*************************************************************************** * Copyright (C) 2005 by David Saxton * * david@bluehaze.org * * * * 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. * ***************************************************************************/ #ifndef TEXTVIEW_H #define TEXTVIEW_H #include "config.h" #include "view.h" // #include //#include #include -#include -#include +#include +#include class QMouseEvent; class RegisterInfo; class TextDocument; class TextView; class VariableLabel; /** @author David Saxton */ class TextView : public View { Q_OBJECT public: TextView( TextDocument *textDocument, ViewContainer *viewContainer, uint viewAreaId, const char *name = nullptr ); ~TextView() override; bool closeView() override; /** * Brings up the goto line dialog. */ bool gotoLine( const int line ) ; // { return m_view->setCursorPosition( line, 0/*m_view->cursorColumn()*/ ); } /** * Returns a pointer to the document as a text document */ TextDocument *textDocument() const; void undo(); void redo(); void cut(); // { m_view->cut(); } void copy(); // { m_view->copy(); } void paste(); // { m_view->paste(); } void saveCursorPosition(); void restoreCursorPosition(); /** * Enable code actions depending on the type of code being edited */ void initCodeActions(); void setCursorPosition( uint line, uint col ); // { m_view->setCursorPosition( line, col ); } unsigned currentLine(); unsigned currentColumn(); void disableActions(); KTextEditor::View * kateView() const { return m_view; } //KTextEditor::View::saveResult save(); // { return m_view->save(); } bool save(); //KTextEditor::View::saveResult saveAs(); // { return m_view->saveAs(); } bool saveAs(); void print(); public slots: /** * Called when change line / toggle marks */ void slotUpdateMarksInfo(); void slotCursorPositionChanged(); void toggleBreakpoint() override; /** * Initialize the actions appropriate for when the debugger is running * or stepping */ void slotInitDebugActions(); protected slots: void slotWordHoveredOver( const QString & word, int line, int col ); void slotWordUnhovered(); void gotFocus(); void slotSelectionmChanged(); protected: KTextEditor::View * m_view; #ifndef NO_GPSIM VariableLabel * m_pTextViewLabel; ///< Pops up when the user hovers his mouse over a word #endif private: uint m_savedCursorLine; uint m_savedCursorColumn; }; /** This class is an event filter to be installed in the kate view, and is used to do stuff like poping up menus and telling TextView that a word is being hovered over (used in the debugger). @author David Saxton */ class TextViewEventFilter : public QObject { Q_OBJECT public: TextViewEventFilter( TextView * textView ); bool eventFilter( QObject * watched, QEvent * e ) override; signals: /** * When the user hovers the mouse for more than 700 milliseconds over a * word, "hover mode" is entered. When the user presses a key, clicks * mouse, etc, this mode is left. During the mode, any word that is * under the mouse cursor will be emitted as hoveredOver( word ). */ void wordHoveredOver( const QString & word, int line, int col ); /** * Emitted when focus is lost, the mouse moves to a different word, etc. */ void wordUnhovered(); protected slots: void slotNeedTextHint( const KTextEditor::Cursor& position, QString& text ); /** * Called when we are not in hover mode, but the user has had his mouse * in the same position for some time. */ void hoverTimeout(); /** * Called (from m_pSleepTimer) when we are in hover mode, but no word * has been hovered over for some time. */ void gotoSleep(); /** * @see m_pNoWordTimer */ void slotNoWordTimeout(); protected: enum HoverStatus { /** * We are currently hovering - wordHoveredOver was emitted, and * wordUnhovered hasn't been emitted yet. */ Active, /** * A word was unhovered recently. We will go straight to PoppedUp * mode if a word is hovered over again. */ Hidden, /** * A word was not unhovered recentely. There will be a short display * before going to PoppedUp mode if a word is hovered over. */ Sleeping }; /** * Starts / stops timers, emits signals, etc. See other functions for an * idea of what this does. */ void updateHovering( const QString & currentWord, int line, int col ); /** * Started when the user moves his mouse over a word, and we are in * Sleeping mode. Reset when the user moves his mouse, etc. */ QTimer * m_pHoverTimer; /** * Started when a word is unhovered. When this timeouts, we will go to * Sleeping mode. */ QTimer * m_pSleepTimer; /** * Activated by the user moving the mouse. Reset by a call to * slotNeedTextHint. This timer is needed as KateViewInternal doesn't * bother updating us if the mouse cursor isn't over text. */ QTimer * m_pNoWordTimer; TextView * m_pTextView; QString m_lastWord; int m_lastLine; int m_lastCol; HoverStatus m_hoverStatus; }; #endif diff --git a/src/utils.h b/src/utils.h index 34b94d0b..ff99c0b0 100644 --- a/src/utils.h +++ b/src/utils.h @@ -1,75 +1,75 @@ #ifndef UTILS_H #define UTILS_H -#include +#include #include inline int roundDown( int x, int roundness ) { if ( x < 0 ) return (x-roundness+1) / roundness; else return (x / roundness); } inline int roundDown( double x, int roundness ) { return roundDown( int(x), roundness ); } inline QPoint roundDown( const QPoint & p, int roundness ) { return QPoint( roundDown( p.x(), roundness ), roundDown( p.y(), roundness ) ); } inline int toCanvas( int pos ) { return pos*8+4; } inline int fromCanvas( int pos ) { return roundDown( pos-4, 8 ); } inline QPoint toCanvas( const QPoint * pos ) { return QPoint( toCanvas(pos->x()), toCanvas(pos->y()) ); } inline QPoint fromCanvas( const QPoint * pos ) { return QPoint( fromCanvas(pos->x()), fromCanvas(pos->y()) ); } inline QPoint toCanvas( const QPoint & pos ) { return QPoint( toCanvas(pos.x()), toCanvas(pos.y()) ); } inline QPoint fromCanvas( const QPoint & pos ) { return QPoint( fromCanvas(pos.x()), fromCanvas(pos.y()) ); } inline int roundDouble( double x ) { return int(std::floor(x+0.5)); } inline double qpoint_distance( const QPoint & p1, const QPoint & p2 ) { double dx = p1.x() - p2.x(); double dy = p1.y() - p2.y(); return std::sqrt( dx*dx + dy*dy ); } inline int snapToCanvas( int x ) { return roundDown( x, 8 )*8 + 4; } inline int snapToCanvas( double x ) { return snapToCanvas( int(x) ); } #endif diff --git a/src/variablelabel.h b/src/variablelabel.h index 9baff59a..385fa033 100644 --- a/src/variablelabel.h +++ b/src/variablelabel.h @@ -1,62 +1,62 @@ /*************************************************************************** * Copyright (C) 2005 by David Saxton * * david@bluehaze.org * * * * 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 "config.h" #ifndef NO_GPSIM #ifndef VARIABLELABEL_H #define VARIABLELABEL_H -#include -#include +#include +#include class TextView; /** Used for displaying the value of a variable when the user hovers his mouse over a variable while debugging. @author David Saxton */ class VariableLabel : public QLabel { Q_OBJECT public: VariableLabel( TextView * parent ); /** * Sets the register that this label is displaying the value of. */ void setRegister( RegisterInfo * info, const QString & name ); /** * Sets the value that this label is displaying. This is an alternative * to setRegister. */ void setValue( unsigned value ); protected slots: /** * Updates what is displayed from m_pRegisterInfo. */ void updateText(); protected: void disconnectRegisterInfo(); QPointer m_pRegisterInfo; QString m_registerName; int m_value; }; #endif #endif // !NO_GPSIM diff --git a/src/variant.cpp b/src/variant.cpp index 20493c2b..2e469beb 100644 --- a/src/variant.cpp +++ b/src/variant.cpp @@ -1,220 +1,220 @@ /*************************************************************************** * Copyright (C) 2003-2005 by David Saxton * * david@bluehaze.org * * * * 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 "colorcombo.h" #include "cnitem.h" #include -#include +#include #include using namespace std; Variant::Variant( const QString & id, Type::Value type ) : QObject(), m_id( id ) { m_type = type; m_bSetDefault = false; m_bHidden = false; m_bAdvanced = false; m_minValue = 1e-6; m_maxValue = 1e9; m_minAbsValue = 1e-6; m_colorScheme = ColorCombo::QtStandard; if ( type == Type::Color ) { // this value is taken from ColorCombo and should ideally be put somewhere... m_defaultValue = "#f62a2a"; m_value = "#f62a2a"; } } Variant::~Variant() { } void Variant::setType( Type::Value type ) { m_type = type; } void Variant::appendAllowed( const QString & id, const QString & i18nName ) { m_allowed[id] = i18nName; } void Variant::setAllowed( const QStringList & allowed ) { m_allowed.clear(); QStringList::const_iterator end = allowed.end(); for ( QStringList::const_iterator it = allowed.begin(); it != end; ++it ) m_allowed[ *it ] = *it; } void Variant::appendAllowed( const QString & allowed ) { m_allowed[ allowed ] = allowed; } void Variant::setMinValue( double value ) { m_minValue = value; if ( std::abs(value) < m_minAbsValue && value != 0. ) { m_minAbsValue = std::abs(value); } } void Variant::setMaxValue( double value ) { m_maxValue = value; if ( std::abs(value) < m_minAbsValue && value != 0. ) { m_minAbsValue = std::abs(value); } } QString Variant::displayString() const { switch(type()) { case Variant::Type::Double: { double numValue = m_value.toDouble(); return QString::number( numValue / CNItem::getMultiplier(numValue) ) + " " + CNItem::getNumberMag(numValue) + m_unit; } case Variant::Type::Int: return m_value.toString()+" "+m_unit; case Variant::Type::Bool: return m_value.toBool() ? i18n("True") : i18n("False"); case Variant::Type::Select: return m_allowed[ m_value.toString() ]; default: return m_value.toString(); } } void Variant::setValue( QVariant val ) { qDebug() << Q_FUNC_INFO << "val=" << val << " old=" << m_value; if ( type() == Variant::Type::Select && !m_allowed.contains( val.toString() ) ) { // Our value is being set to an i18n name, not the actual string id. // So change val to the id (if it exists) QString i18nName = val.toString(); QStringMap::iterator end = m_allowed.end(); for ( QStringMap::iterator it = m_allowed.begin(); it != end; ++it ) { if ( it.value() == i18nName ) { val = it.key(); break; } } } if ( !m_bSetDefault ) { m_defaultValue = val; m_bSetDefault = true; } if ( m_value == val ) return; const QVariant old = m_value; m_value = val; emit( valueChanged( val, old ) ); switch ( type() ) { case Variant::Type::String: case Variant::Type::FileName: case Variant::Type::PenCapStyle: case Variant::Type::PenStyle: case Variant::Type::Port: case Variant::Type::Pin: case Variant::Type::VarName: case Variant::Type::Combo: case Variant::Type::Select: case Variant::Type::SevenSegment: case Variant::Type::KeyPad: case Variant::Type::Multiline: case Variant::Type::RichText: { QString dispString = displayString(); qDebug() << Q_FUNC_INFO << "dispString=" << dispString << " value=" << m_value; emit valueChanged( dispString ); emit valueChangedStrAndTrue( dispString, true ); } break; case Variant::Type::Int: emit valueChanged( value().toInt() ); break; case Variant::Type::Double: emit valueChanged( value().toDouble() ); break; case Variant::Type::Color: emit valueChanged( value().value() ); break; case Variant::Type::Bool: emit valueChanged( value().toBool() ); break; case Variant::Type::Raw: case Variant::Type::None: break; } qDebug() << Q_FUNC_INFO << "result m_value=" << m_value; } void Variant::setMinAbsValue( double val ) { m_minAbsValue = val; } bool Variant::changed() const { // Have to handle double slightly differently due inperfect storage of real // numbers if ( type() == Type::Double ) { double cur = value().toDouble(); double def = defaultValue().toDouble(); double diff = abs( cur - def ); if ( diff == 0 ) return false; // denom cannot be zero double denom = max( abs( cur ), abs( def ) ); // not changed if within 1e-4% of each other's value return ( (diff / denom) > 1e-6 ); } return value() != defaultValue(); } diff --git a/src/variant.h b/src/variant.h index 24b85a17..0eb7765a 100644 --- a/src/variant.h +++ b/src/variant.h @@ -1,219 +1,219 @@ /*************************************************************************** * Copyright (C) 2003-2004 by David Saxton * * david@bluehaze.org * * * * 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. * ***************************************************************************/ #ifndef VARIANT_H #define VARIANT_H -#include -#include -#include +#include +#include +#include /// \todo Replace "Variant" with "Property" class Variant; typedef Variant Property; class QColor; class QString; typedef QMap< QString, QString > QStringMap; /** For information: QVariant::type() returns an enum for the current data type contained. e.g. returns QVariant::Color or QVariant::Rect @author Daniel Clarke @author David Saxton */ class Variant : public QObject { Q_OBJECT public: class Type { public: enum Value { None, Int, // Integer Raw, // QByteArray Double, // Real number String, // Editable string Multiline, // String that may contain linebreaks RichText, // HTML formatted text Select, // Selection of strings Combo, // Editable combination of strings FileName, // Filename on local filesystem Color, // Color Bool, // Boolean VarName, // Variable name Port, // Port name Pin, // Pin name PenStyle, // Pen Style PenCapStyle, // Pen Cap Style SevenSegment, // Pin Map for Seven Segment Display KeyPad // Pin Map for Keypad }; }; Variant( const QString & id, Type::Value type ); ~Variant() override; QString id() const { return m_id; } /** * Returns the type of Variant (see Variant::Type::Value) */ Variant::Type::Value type() const { return m_type; } /** * Sets the variant type */ void setType( Type::Value type ); /** * Returns the filter used for file dialogs (if this is of type Type::FileName) */ QString filter() const { return m_filter; } void setFilter( const QString & filter ) { m_filter = filter; } /** * The selection of colours to be used in the combo box - e.g. * ColorCombo::LED. * @see ColorCombo::ColorScheme */ int colorScheme() const { return m_colorScheme; } void setColorScheme( int colorScheme ) { m_colorScheme = colorScheme; } /** * This function is for convenience; it sets both the toolbar and editor * caption. */ void setCaption( const QString & caption ) { setToolbarCaption(caption); setEditorCaption(caption); } /** * This text is displayed to the left of the entry widget in the toolbar */ QString toolbarCaption() const { return m_toolbarCaption; } void setToolbarCaption( const QString & caption ) { m_toolbarCaption = caption; } /** * This text is displayed to the left of the entry widget in the item editor */ QString editorCaption() const { return m_editorCaption; } void setEditorCaption( const QString & caption ) { m_editorCaption = caption; } /** * Unit of number, (e.g. V (volts) / F (farads)) */ QString unit() const { return m_unit; } void setUnit( const QString & unit ) { m_unit = unit; } /** * The smallest (as in negative, not absoluteness) value that the user can * set this to. */ double minValue() const { return m_minValue; } void setMinValue( double value ); /** * The largest (as in positive, not absoluteness) value that the user can * set this to. */ double maxValue() const { return m_maxValue; } void setMaxValue( double value ); /** * The smallest absolute value that the user can set this to, before the * value is considered zero. */ double minAbsValue() const { return m_minAbsValue; } void setMinAbsValue( double val ); QVariant defaultValue() const { return m_defaultValue; } /** * If this data is marked as advanced, it will only display in the item * editor (and not in the toolbar) */ void setAdvanced( bool advanced ) { m_bAdvanced = advanced; } bool isAdvanced() const { return m_bAdvanced; } /** * If this data is marked as hidden, it will not be editable from anywhere * in the user interface */ void setHidden( bool hidden ) { m_bHidden = hidden; } bool isHidden() const { return m_bHidden; } /** * Returns the best possible attempt at representing the data in a string * for display. Used by the properties list view. */ QString displayString() const; /** * The list of values that the data is allowed to take (if it is string) * that is displayed to the user. */ QStringList allowed() const { return m_allowed.values(); } /** * @param allowed A list of pairs of (id, i18n-name) of allowed values. */ void setAllowed( const QStringMap & allowed ) { m_allowed = allowed; } void setAllowed( const QStringList & allowed ); void appendAllowed( const QString & id, const QString & i18nName ); void appendAllowed( const QString & allowed ); /** * @return whether the current value is different to the default value. */ bool changed() const; QVariant value() const { return m_value; } void setValue( QVariant val ); signals: /** * Emitted when the value changes. * NOTE: The order of data given is the new value, and then the old value * This is done so that slots that don't care about the old value don't * have to accept it */ void valueChanged( QVariant newValue, QVariant oldValue ); /** * Emitted for variants of string-like type. */ void valueChanged( const QString & newValue ); /** * Emitted for variants of string-like type. * This signal is needed for updating values in KComboBox-es, see KComboBox::setCurrentItem(), * second bool parameter, insert. */ void valueChangedStrAndTrue( const QString &newValue, bool trueBool); /** * Emitted for variants of int-like type. */ void valueChanged( int newValue ); /** * Emitted for variants of double-like type. */ void valueChanged( double newValue ); /** * Emitted for variants of color-like type. */ void valueChanged( const QColor & newValue ); /** * Emitted for variants of bool-like type. */ void valueChanged( bool newValue ); private: QVariant m_value; // the actual data QVariant m_defaultValue; QString m_unit; const QString m_id; double m_minAbsValue; double m_minValue; double m_maxValue; QString m_toolbarCaption; // Short description shown in e.g. properties dialog QString m_editorCaption; // Text displayed before the data entry widget in the toolbar bool m_bAdvanced; // If advanced, only display data in item editor bool m_bHidden; // If hidden, do not allow user to change data QString m_filter; // If type() == Type::FileName this is the filter used in file dialogs. bool m_bSetDefault; // If false, then the default will be set to the first thing this variant is set to Type::Value m_type; QStringMap m_allowed; int m_colorScheme; }; #endif diff --git a/src/view.cpp b/src/view.cpp index 7e5c23c7..72466a93 100644 --- a/src/view.cpp +++ b/src/view.cpp @@ -1,299 +1,300 @@ /*************************************************************************** * Copyright (C) 2005 by David Saxton * * david@bluehaze.org * * * * 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 "document.h" #include "iteminterface.h" #include "ktechlab.h" #include "view.h" #include "viewiface.h" #include "viewcontainer.h" -#include #include #include #include #include #include -#include -#include +#include +#include +#include +#include #include //BEGIN class View View::View( Document *document, ViewContainer *viewContainer, uint viewAreaId, const char *name ) : QWidget( viewContainer->viewArea(viewAreaId) /*, name ? name : ("view_" + QString::number(viewAreaId)).toLatin1().data() */ ), KXMLGUIClient() { setObjectName(name ? name : ("view_" + QString::number(viewAreaId)).toLatin1().data()); m_pFocusWidget = nullptr; m_dcopID = 0; m_viewAreaId = viewAreaId; m_pDocument = document; p_viewContainer = viewContainer; m_pViewIface = nullptr; setFocusPolicy( Qt::ClickFocus ); if ( ViewArea * viewArea = viewContainer->viewArea(viewAreaId) ) viewArea->setView(this); else qDebug() << Q_FUNC_INFO << " viewArea = " << viewArea <addWidget( new KVSSBSep(this) ); m_layout->addWidget( m_statusBar ); connect( KTechlab::self(), SIGNAL(configurationChanged()), this, SLOT(slotUpdateConfiguration()) ); } } View::~View() { //if ( KTechlab::self() ) // 2015.09.13 - remove the XMLGUIClient from the factory, even at program close // KTechlab::self()->factory()->removeClient(this); // 2017.01.09: do not crash on document close. factory has its clients removed in TextDocument::~TextDocument() //if ( factory() ) { // factory()->removeClient( this ); //} else { // qWarning() << Q_FUNC_INFO << "Null factory"; //} } QAction* View::actionByName( const QString& name ) const { QAction * action = actionCollection()->action(name); if ( !action ) qCritical() << Q_FUNC_INFO << "No such action: " << name << endl; return action; } DCOPObject * View::dcopObject( ) const { return m_pViewIface; } bool View::closeView() { return p_viewContainer->closeViewArea( viewAreaId() ); } void View::setFocusWidget( QWidget * focusWidget ) { assert( focusWidget ); assert( !m_pFocusWidget ); if ( hasFocus() ) focusWidget->setFocus(); m_pFocusWidget = focusWidget; setFocusProxy( m_pFocusWidget ); m_pFocusWidget->installEventFilter( this ); m_pFocusWidget->setFocusPolicy( Qt::ClickFocus ); } bool View::eventFilter( QObject * watched, QEvent * e ) { // qDebug() << Q_FUNC_INFO << e->type() << endl; if ( watched != m_pFocusWidget ) return false; switch ( e->type() ) { case QEvent::FocusIn: { p_viewContainer->setActiveViewArea( viewAreaId() ); if ( KTechlab * ktl = KTechlab::self() ) { ktl->actionByName("file_save")->setEnabled(true); ktl->actionByName("file_save_as")->setEnabled(true); ktl->actionByName("file_close")->setEnabled(true); ktl->actionByName("file_print")->setEnabled(true); ktl->actionByName("edit_paste")->setEnabled(true); ktl->actionByName("view_split_leftright")->setEnabled(true); ktl->actionByName("view_split_topbottom")->setEnabled(true); ItemInterface::self()->updateItemActions(); } // qDebug() << Q_FUNC_INFO << "Focused In\n"; emit focused(this); break; } case QEvent::FocusOut: { // qDebug() << Q_FUNC_INFO << "Focused Out.\n"; QFocusEvent *fe = static_cast(e); if ( QWidget * fw = qApp->focusWidget() ) { QString fwClassName( fw->metaObject()->className() ); // qDebug() << "New focus widget is \""<name()<<"\" of type " << fwClassName << endl; if ( (fwClassName != "KateViewInternal") && (fwClassName != "QViewportWidget") ) { // qDebug() << "Returning as a non-view widget has focus.\n"; break; } } else { // qDebug() << "No widget currently has focus.\n"; } if ( fe->reason() == Qt::PopupFocusReason ) { // qDebug() << Q_FUNC_INFO << "Ignoring focus-out event as was a popup.\n"; break; } if ( fe->reason() == Qt::ActiveWindowFocusReason ) { // qDebug() << Q_FUNC_INFO << "Ignoring focus-out event as main window lost focus.\n"; break; } emit unfocused(); break; } default: break; } return false; } void View::setDCOPID( unsigned id ) { if ( m_dcopID == id ) return; m_dcopID = id; if ( m_pViewIface ) { QString docID; docID.setNum( document()->dcopID() ); QString viewID; viewID.setNum( dcopID() ); m_pViewIface->setObjId( "View#" + docID + "." + viewID ); } } //END class View //BEGIN class ViewStatusBar ViewStatusBar::ViewStatusBar( View *view ) : QStatusBar(view) { p_view = view; m_modifiedLabel = new QLabel(this); addWidget( m_modifiedLabel, 0 /*, false */ ); m_fileNameLabel = new KSqueezedTextLabel(this); addWidget( m_fileNameLabel, 1 /*, false */ ); m_statusLabel = new QLabel(this); m_statusLabel->setAlignment(Qt::AlignHCenter | Qt::AlignVCenter); addPermanentWidget(m_statusLabel, 0); m_modifiedPixmap = KIconLoader::global()->loadIcon( "document-save", KIconLoader::Small ); m_unmodifiedPixmap = KIconLoader::global()->loadIcon( "null", KIconLoader::Small ); connect( view->document(), SIGNAL(modifiedStateChanged()), this, SLOT(slotModifiedStateChanged()) ); connect( view->document(), &Document::fileNameChanged, this, &ViewStatusBar::slotFileNameChanged); connect( view, SIGNAL(focused(View* )), this, SLOT(slotViewFocused(View* )) ); connect( view, SIGNAL(unfocused()), this, SLOT(slotViewUnfocused()) ); slotModifiedStateChanged(); slotFileNameChanged( view->document()->url() ); slotViewUnfocused(); } void ViewStatusBar::setStatusText(const QString &statusText) { m_statusLabel->setText(statusText); } void ViewStatusBar::slotModifiedStateChanged() { m_modifiedLabel->setPixmap( p_view->document()->isModified() ? m_modifiedPixmap : m_unmodifiedPixmap ); } void ViewStatusBar::slotFileNameChanged( const QUrl &url ) { m_fileNameLabel->setText( url.isEmpty() ? i18n("Untitled") : url.adjusted(QUrl::StripTrailingSlash).fileName() ); } void ViewStatusBar::slotViewFocused( View * ) { setPalette(p_view->palette()); } void ViewStatusBar::slotViewUnfocused() { QPalette pal( p_view->palette() ); pal.setColor( QPalette::Window /*QColorGroup::Background */ , pal.mid().color() ); pal.setColor( QPalette::Light, pal.midlight().color() ); setPalette(pal); } //END class ViewStatusBar //BEGIN class KVSSBSep void KVSSBSep::paintEvent( QPaintEvent *e ) { //QPainter p( this ); QPainter p; const bool beginSuccess = p.begin( this ); if (!beginSuccess) { qWarning() << Q_FUNC_INFO << " painter is not active"; } //p.setPen( colorGroup().shadow() ); //QColorGroup colorGroup(palette()); // 2018.12.02 p.setPen( palette().shadow().color() /* colorGroup.shadow() */ ); p.drawLine( e->rect().left(), 0, e->rect().right(), 0 ); //p.setPen( ((View*)parentWidget())->hasFocus() ? colorGroup.light() : colorGroup.midlight() ); View * parentView = dynamic_cast(parentWidget()); if (!parentView) { qWarning() << "parent not View for this=" << this << ", parent=" << parentWidget(); return; } p.setPen( parentView->hasFocus() ? palette().light().color() : palette().midlight().color() ); p.drawLine( e->rect().left(), 1, e->rect().right(), 1 ); } //END class KVSSBSep diff --git a/src/view.h b/src/view.h index b7edea97..599feb8e 100644 --- a/src/view.h +++ b/src/view.h @@ -1,195 +1,194 @@ /*************************************************************************** * Copyright (C) 2005 by David Saxton * * david@bluehaze.org * * * * 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. * ***************************************************************************/ #ifndef VIEW_H #define VIEW_H #include "viewcontainer.h" #include "document.h" #include #include #include -#include -#include -#include -#include -#include +#include +#include +#include +#include class DCOPObject; class Document; class KSqueezedTextLabel; class KTechlab; class View; class ViewContainer; class ViewIface; class QVBoxLayout; class QLabel; class ViewStatusBar : public QStatusBar { Q_OBJECT public: ViewStatusBar( View *view ); public: void setStatusText(const QString &statusText); public slots: void slotModifiedStateChanged(); void slotFileNameChanged( const QUrl &url ); void slotViewFocused( View * ); void slotViewUnfocused(); protected: View *p_view; QLabel* m_statusLabel; QLabel* m_modifiedLabel; KSqueezedTextLabel* m_fileNameLabel; QPixmap m_modifiedPixmap; QPixmap m_unmodifiedPixmap; }; /** @author David Saxton */ class View : public QWidget, public KXMLGUIClient { Q_OBJECT public: View( Document *document, ViewContainer *viewContainer, uint viewAreaId, const char *name = nullptr ); ~View() override; QAction * actionByName( const QString & name ) const; /** * Pointer to the parent document */ Document * document() const { return m_pDocument; } /** * Returns the DCOP object from this view */ DCOPObject * dcopObject() const; /** * Returns the dcop suffix for this view - a unique ID for the current the * view within all views associated with the parent document. DCOP name * will become "View#docID#viewID". */ unsigned dcopID() const { return m_dcopID; } /** * Sets the dcop suffix. The DCOP object for this view will be renamed. * @see dcopID */ void setDCOPID( unsigned id ); /** * Pointer to the ViewContainer that we're in */ ViewContainer *viewContainer() const { return p_viewContainer; } /** * Tells the view container which contains this view to close this view, * returning true if successful (i.e. not both last view and unsaved, etc) */ virtual bool closeView(); /** * Returns the unique (for the view container) view area id associated with this view */ uint viewAreaId() const { return m_viewAreaId; } /** * Zoom in */ virtual void viewZoomIn() {}; /** * Zoom out */ virtual void viewZoomOut() {}; virtual bool canZoomIn() const { return true; } virtual bool canZoomOut() const { return true; } /** * Restore view to actual size */ virtual void actualSize() {}; virtual void toggleBreakpoint() {}; bool eventFilter( QObject * watched, QEvent * e ) override; protected slots: /** * Called when the user changes the configuration. */ virtual void slotUpdateConfiguration() {}; signals: /** * Emitted when the view receives focus. @p view is a pointer to this class. */ void focused( View * view ); /** * Emitted when the view looses focus. */ void unfocused(); protected: /** * This function should be called in the constructor of the child class * (e.g. in ItemView or TextView) to set the widget which receives focus * events. */ void setFocusWidget( QWidget * focusWidget ); QPointer m_pDocument; QPointer p_viewContainer; uint m_viewAreaId; ViewStatusBar * m_statusBar; QVBoxLayout * m_layout; ViewIface * m_pViewIface; unsigned m_dcopID; QWidget * m_pFocusWidget; }; /* "KateViewSpaceStatusBarSeparator" A 2 px line to separate the statusbar from the view. It is here to compensate for the lack of a frame in the view, I think Kate looks very nice this way, as QScrollView with frame looks slightly clumsy... Slight 3D effect. I looked for suitable QStyle props or methods, but found none, though maybe it should use QStyle::PM_DefaultFrameWidth for height (TRY!). It does look a bit funny with flat styles (Light, .Net) as is, but there are on methods to paint panel lines separately. And, those styles tends to look funny on their own, as a light line in a 3D frame next to a light contents widget is not functional. Also, QStatusBar is up to now completely ignorant to style. -anders */ class KVSSBSep : public QWidget { public: KVSSBSep( View * parent = nullptr) : QWidget(parent) { setFixedHeight( 2 ); } protected: void paintEvent( QPaintEvent *e ) override; // { // QPainter p( this ); // //p.setPen( colorGroup().shadow() ); // QColorGroup colorGroup(palette()); // p.setPen( colorGroup.shadow() ); // p.drawLine( e->rect().left(), 0, e->rect().right(), 0 ); // p.setPen( ((View*)parentWidget())->hasFocus() ? colorGroup.light() : colorGroup.midlight() ); // p.drawLine( e->rect().left(), 1, e->rect().right(), 1 ); // } }; #endif diff --git a/src/viewcontainer.cpp b/src/viewcontainer.cpp index 5201f241..55a849aa 100644 --- a/src/viewcontainer.cpp +++ b/src/viewcontainer.cpp @@ -1,599 +1,599 @@ /*************************************************************************** * Copyright (C) 2005-2006 David Saxton * * * * 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 "docmanager.h" #include "document.h" #include "itemview.h" #include "ktechlab.h" #include "view.h" #include "viewcontainer.h" -#include -#include #include #include +#include +#include #include #include //#include //BEGIN class ViewContainer ViewContainer::ViewContainer( const QString & caption, QWidget * parent ) : QWidget( parent ? parent : KTechlab::self()->tabWidget() ) { b_deleted = false; connect( KTechlab::self(), SIGNAL(needUpdateCaptions()), this, SLOT(updateCaption()) ); QHBoxLayout *layout = new QHBoxLayout(this); m_baseViewArea = new ViewArea( this, this, 0, false, "viewarea_0" ); connect( m_baseViewArea, SIGNAL(destroyed(QObject* )), this, SLOT(baseViewAreaDestroyed(QObject* )) ); layout->addWidget(m_baseViewArea); m_activeViewArea = 0; setFocusProxy( m_baseViewArea ); if ( !parent ) { KTechlab::self()->tabWidget()->addTab( this, caption ); QTabWidget * tabWidget = KTechlab::self()->tabWidget(); tabWidget->setCurrentIndex( tabWidget->indexOf(this) ); } show(); } ViewContainer::~ViewContainer() { b_deleted = true; } void ViewContainer::setActiveViewArea( uint id ) { if ( m_activeViewArea == int(id) ) return; m_activeViewArea = id; View * newView = view(id); setFocusProxy( newView ); if ( newView ) { setWindowTitle( newView->windowTitle() ); if ( !DocManager::self()->getFocusedView() && newView->isVisible() ) newView->setFocus(); } } View *ViewContainer::view( uint id ) const { ViewArea *va = viewArea(id); if (!va) return nullptr; // We do not want a recursive search as ViewAreas also hold other ViewAreas //QObjectList l = va->queryList( "View", 0, false, false ); // 2018.12.02 QList l = va->findChildren(); View *view = nullptr; if ( !l.isEmpty() ) view = dynamic_cast(l.first()); //delete l; return view; } ViewArea *ViewContainer::viewArea( uint id ) const { if ( !m_viewAreaMap.contains(id) ) return nullptr; return m_viewAreaMap[id]; } bool ViewContainer::closeViewContainer() { bool didClose = true; while ( didClose && !m_viewAreaMap.isEmpty() ) { didClose = closeViewArea( m_viewAreaMap.begin().key() ); } return m_viewAreaMap.isEmpty(); } bool ViewContainer::closeViewArea( uint id ) { ViewArea *va = viewArea(id); if ( !va ) return true; bool doClose = false; View *v = view(id); if ( v && v->document() ) { doClose = v->document()->numberOfViews() > 1; if (!doClose) doClose = v->document()->fileClose(); } else doClose = true; if (!doClose) return false; m_viewAreaMap.remove(id); va->deleteLater(); if ( m_activeViewArea == int(id) ) { m_activeViewArea = -1; findActiveViewArea(); } return true; } int ViewContainer::createViewArea( int relativeViewArea, ViewArea::Position position, bool showOpenButton ) { if ( relativeViewArea == -1 ) relativeViewArea = activeViewArea(); ViewArea *relative = viewArea(relativeViewArea); if (!relative) { qCritical() << Q_FUNC_INFO << "Could not find relative view area" << endl; return -1; } uint id = uniqueNewId(); // setActiveViewArea(id); ViewArea *viewArea = relative->createViewArea( position, id, showOpenButton ); // ViewArea *viewArea = new ViewArea( m_splitter, id, (const char*)("viewarea_"+QString::number(id)) ); viewArea->show(); // remove? return id; } void ViewContainer::setViewAreaId( ViewArea *viewArea, uint id ) { m_viewAreaMap[id] = viewArea; m_usedIDs.append(id); } void ViewContainer::setViewAreaRemoved( uint id ) { if (b_deleted) return; ViewAreaMap::iterator it = m_viewAreaMap.find(id); if ( it == m_viewAreaMap.end() ) return; m_viewAreaMap.erase(it); if ( m_activeViewArea == int(id) ) findActiveViewArea(); } void ViewContainer::findActiveViewArea() { if ( m_viewAreaMap.isEmpty() ) return; setActiveViewArea( (--m_viewAreaMap.end()).key() ); } void ViewContainer::baseViewAreaDestroyed( QObject *obj ) { if (!obj) return; if (!b_deleted) { b_deleted = true; close(); deleteLater(); } } bool ViewContainer::canSaveUsefulStateInfo() const { return m_baseViewArea && m_baseViewArea->canSaveUsefulStateInfo(); } void ViewContainer::saveState( KConfigGroup *config ) { if (!m_baseViewArea) return; config->writeEntry( "BaseViewArea", m_baseViewArea->id() ); m_baseViewArea->saveState(config); } void ViewContainer::restoreState( KConfigGroup* config, const QString& groupName ) { //config->setGroup(groupName); int baseAreaId = config->readEntry("BaseViewArea", 0); m_baseViewArea->restoreState( config, baseAreaId, groupName ); } int ViewContainer::uniqueParentId() { int lowest = -1; const IntList::iterator end = m_usedIDs.end(); for ( IntList::iterator it = m_usedIDs.begin(); it != end; ++it ) { if ( *it < lowest ) lowest = *it; } int newId = lowest-1; m_usedIDs.append(newId); return newId; } int ViewContainer::uniqueNewId() { int highest = 0; const IntList::iterator end = m_usedIDs.end(); for ( IntList::iterator it = m_usedIDs.begin(); it != end; ++it ) { if ( *it > highest ) highest = *it; } int newId = highest+1; m_usedIDs.append(newId); return newId; } void ViewContainer::setIdUsed( int id ) { m_usedIDs.append(id); } void ViewContainer::updateCaption() { QString caption; if ( !activeView() || !activeView()->document() ) caption = i18n("(empty)"); else { Document * doc = activeView()->document(); caption = doc->url().isEmpty() ? doc->caption() : doc->url().fileName(); if ( viewCount() > 1 ) caption += " ..."; } setWindowTitle(caption); //KTechlab::self()->tabWidget()->setTabLabel( this, caption ); // 2018.12.02 KTechlab::self()->tabWidget()->setTabText( KTechlab::self()->tabWidget()->indexOf(this), caption ); } //END class ViewContainer //BEGIN class ViewArea ViewArea::ViewArea( QWidget *parent, ViewContainer *viewContainer, int id, bool showOpenButton, const char *name ) : QSplitter( parent /*, name */ ) { setObjectName(name); p_viewContainer = viewContainer; m_id = id; p_view = nullptr; p_viewArea1 = nullptr; p_viewArea2 = nullptr; if (id >= 0) p_viewContainer->setViewAreaId( this, uint(id) ); p_viewContainer->setIdUsed(id); m_pEmptyViewArea = nullptr; if ( showOpenButton ) m_pEmptyViewArea = new EmptyViewArea( this ); } ViewArea::~ViewArea() { if ( m_id >= 0 ) p_viewContainer->setViewAreaRemoved( uint(m_id) ); } ViewArea *ViewArea::createViewArea( Position position, uint id, bool showOpenButton ) { if (p_viewArea1 || p_viewArea2) { qCritical() << Q_FUNC_INFO << "Attempting to create ViewArea when already containing ViewAreas!" << endl; return nullptr; } if (!p_view) { qCritical() << Q_FUNC_INFO << "We don't have a view yet, so creating a new ViewArea is redundant" << endl; return nullptr; } setOrientation( ( position == Right ) ? Qt::Horizontal : Qt::Vertical ); p_viewArea1 = new ViewArea( this, p_viewContainer, m_id, false, ("viewarea_"+QString::number(m_id)).toLatin1().data() ); p_viewArea2 = new ViewArea( this, p_viewContainer, id, showOpenButton, ("viewarea_"+QString::number(id)).toLatin1().data() ); connect( p_viewArea1, SIGNAL(destroyed(QObject* )), this, SLOT(viewAreaDestroyed(QObject* )) ); connect( p_viewArea2, SIGNAL(destroyed(QObject* )), this, SLOT(viewAreaDestroyed(QObject* )) ); p_view->clearFocus(); //p_view->reparent( p_viewArea1, QPoint(), true ); // 2018.12.02 p_view->setParent( p_viewArea1 ); p_view->move(QPoint()); p_view->show(); p_viewArea1->setView(p_view); setView( nullptr ); m_id = p_viewContainer->uniqueParentId(); QList splitPos; int pos = ((orientation() == Qt::Horizontal) ? width()/2 : height()/2); splitPos << pos << pos; setSizes(splitPos); p_viewArea1->show(); p_viewArea2->show(); return p_viewArea2; } void ViewArea::viewAreaDestroyed( QObject *obj ) { ViewArea *viewArea = static_cast(obj); if ( viewArea == p_viewArea1 ) p_viewArea1 = nullptr; if ( viewArea == p_viewArea2 ) p_viewArea2 = nullptr; if ( !p_viewArea1 && !p_viewArea2 ) deleteLater(); } void ViewArea::setView( View *view ) { if ( !view ) { p_view = nullptr; setFocusProxy( nullptr ); return; } delete m_pEmptyViewArea; if ( p_view ) { qCritical() << Q_FUNC_INFO << "Attempting to set already contained view!" << endl; return; } p_view = view; // qDebug() << Q_FUNC_INFO << "p_view->isFocusEnabled()="<isFocusEnabled()<<" p_view->isHidden()="<isHidden()<isHidden() ) p_view->setFocus(); // The ViewContainer by default has a view area as its focus proxy. // This is because there is no view when it is constructed. So give // it our view as the focus proxy if it doesn't have one. if ( !dynamic_cast(p_viewContainer->focusProxy()) ) p_viewContainer->setFocusProxy( p_view ); } void ViewArea::viewDestroyed() { if ( !p_view && !p_viewArea1 && !p_viewArea2 ) deleteLater(); } bool ViewArea::canSaveUsefulStateInfo() const { if ( p_viewArea1 && p_viewArea1->canSaveUsefulStateInfo() ) return true; if ( p_viewArea2 && p_viewArea2->canSaveUsefulStateInfo() ) return true; if ( p_view && p_view->document() && !p_view->document()->url().isEmpty() ) return true; return false; } void ViewArea::saveState( KConfigGroup* config ) { bool va1Ok = p_viewArea1 && p_viewArea1->canSaveUsefulStateInfo(); bool va2Ok = p_viewArea2 && p_viewArea2->canSaveUsefulStateInfo(); if ( va1Ok || va2Ok ) { config->writeEntry( orientationKey(m_id), (orientation() == Qt::Horizontal) ? "LeftRight" : "TopBottom" ); QList contains; if (va1Ok) contains << p_viewArea1->id(); if (va2Ok) contains << p_viewArea2->id(); config->writeEntry( containsKey(m_id), contains ); if (va1Ok) p_viewArea1->saveState(config); if (va2Ok) p_viewArea2->saveState(config); } else if ( p_view && !p_view->document()->url().isEmpty() ) { config->writePathEntry( fileKey(m_id), p_view->document()->url().toDisplayString(QUrl::PreferLocalFile) ); } } void ViewArea::restoreState( KConfigGroup* config, int id, const QString& groupName ) { if (!config) return; if ( id != m_id ) { if ( m_id >= 0 ) p_viewContainer->setViewAreaRemoved( uint(m_id) ); m_id = id; if ( m_id >= 0 ) p_viewContainer->setViewAreaId( this, uint(m_id) ); p_viewContainer->setIdUsed(id); } //config->setGroup(groupName); if ( config->hasKey( orientationKey(id) ) ) { QString orientation = config->readEntry( orientationKey(m_id) ); setOrientation( (orientation == "LeftRight") ? Qt::Horizontal : Qt::Vertical ); } //config->setGroup(groupName); if ( config->hasKey( containsKey(m_id) ) ) { typedef QList IntList; IntList contains = config->readEntry( containsKey(m_id), IntList()); if ( contains.isEmpty() || contains.size() > 2 ) qCritical() << Q_FUNC_INFO << "Contained list has wrong size of " << contains.size() << endl; else { if ( contains.size() >= 1 ) { int viewArea1Id = contains[0]; p_viewArea1 = new ViewArea( this, p_viewContainer, viewArea1Id, false, ("viewarea_"+QString::number(viewArea1Id)).toLatin1().data() ); connect( p_viewArea1, SIGNAL(destroyed(QObject* )), this, SLOT(viewAreaDestroyed(QObject* )) ); p_viewArea1->restoreState( config, viewArea1Id, groupName ); p_viewArea1->show(); } if ( contains.size() >= 2 ) { int viewArea2Id = contains[1]; p_viewArea2 = new ViewArea( this, p_viewContainer, viewArea2Id, false, ("viewarea_"+QString::number(viewArea2Id)).toLatin1().data() ); connect( p_viewArea2, SIGNAL(destroyed(QObject* )), this, SLOT(viewAreaDestroyed(QObject* )) ); p_viewArea2->restoreState( config, viewArea2Id, groupName ); p_viewArea2->show(); } } } //config->setGroup(groupName); if ( config->hasKey( fileKey(m_id) ) ) { const QUrl url = QUrl::fromUserInput(config->readPathEntry(fileKey(m_id), QString())); bool openedOk = DocManager::self()->openURL( url, this ); if (!openedOk) deleteLater(); } } QString ViewArea::fileKey( int id ) { return QString("ViewArea ") + QString::number(id) + QString(" file"); } QString ViewArea::containsKey( int id ) { return QString("ViewArea ") + QString::number(id) + QString(" contains"); } QString ViewArea::orientationKey( int id ) { return QString("ViewArea ") + QString::number(id) + QString(" orientation"); } //END class ViewArea //BEGIN class EmptyViewArea EmptyViewArea::EmptyViewArea( ViewArea * parent ) : QWidget( parent ) { m_pViewArea = parent; QGridLayout * layout = new QGridLayout( this /*, 5, 3, 0, 6 */ ); layout->setMargin(0); layout->setSpacing(6); layout->setRowStretch( 0, 20 ); layout->setRowStretch( 2, 1 ); layout->setRowStretch( 4, 20 ); layout->setColumnStretch( 0, 1 ); layout->setColumnStretch( 2, 1 ); QPushButton * newDocButton = new QPushButton(QIcon::fromTheme(QStringLiteral("document-open")), i18n("Open Document"), this ); layout->addWidget( newDocButton, 1, 1 ); connect( newDocButton, SIGNAL(clicked()), this, SLOT(openDocument()) ); QPushButton * cancelButton = new QPushButton(QIcon::fromTheme(QStringLiteral("dialog-cancel")), i18n("Cancel"), this ); layout->addWidget( cancelButton, 3, 1 ); connect( cancelButton, SIGNAL(clicked()), m_pViewArea, SLOT(deleteLater()) ); } EmptyViewArea::~EmptyViewArea() { } void EmptyViewArea::openDocument() { KTechlab::self()->openFile( m_pViewArea ); } //END class EmptyViewArea diff --git a/src/viewcontainer.h b/src/viewcontainer.h index 348bb609..d2f8cd7f 100644 --- a/src/viewcontainer.h +++ b/src/viewcontainer.h @@ -1,237 +1,237 @@ /*************************************************************************** * Copyright (C) 2005-2006 David Saxton * * * * 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. * ***************************************************************************/ #ifndef VIEWCONTAINER_H #define VIEWCONTAINER_H // #include -#include -#include -#include -#include +#include +#include +#include +#include class KConfigGroup; class KTechlab; class View; class ViewArea; class ViewContainer; class KConfig; class QHBoxLayout; class QLayout; class QSplitter; typedef QMap< uint, ViewArea* > ViewAreaMap; typedef QList IntList; /** Before a ViewAre has been given a view, this is shown. \author David Saxton */ class EmptyViewArea : public QWidget { Q_OBJECT public: EmptyViewArea( ViewArea * parent ); ~EmptyViewArea() override; protected slots: void openDocument(); protected: ViewArea * m_pViewArea; }; /** Contains either exactly one View, or two ViewAreas, separated by a QSplitter. If it contains one view, then the value returned in id() is that of the view. If it contains two ViewAreas, then the value returned by id() is -1. @author David Saxton */ class ViewArea : public QSplitter { Q_OBJECT public: enum Position { Right, Bottom }; ViewArea( QWidget *parent, ViewContainer *viewContainer, int id, bool showOpenButton, const char * name = nullptr ); ~ViewArea() override; ViewContainer *viewContainer() const { return p_viewContainer; } int id() const { return m_id; } /** * Splits this ViewArea into two, and returns a pointer to the new ViewArea * @param showOpenButton Whether to present the user with the EmptyViewArea * widget (i.e. the new ViewArea is not destined to be immediately filled * with a view). */ ViewArea *createViewArea( Position position, uint id, bool showOpenButton ); /** * Adds the given View to the main part of the layout */ void setView( View *view ); /** * Saves the state of this ViewArea and any contained ViewAreas */ void saveState( KConfigGroup *config ); /** * Restores the state of this ViewArea and any contained ViewAreas * @param groupName e.g. "ViewContainer 1" */ void restoreState( KConfigGroup *config, int id, const QString &groupName ); /** * Returns true if this ViewArea can save useful information as to its state * (i.e. it's children can save useful information about their state, or has * a view with a url in it) */ bool canSaveUsefulStateInfo() const; static QString fileKey( int id ); static QString containsKey( int id ); static QString orientationKey( int id ); View * view() const { return p_view; } protected slots: void viewAreaDestroyed( QObject *obj ); void viewDestroyed(); protected: int m_id; EmptyViewArea * m_pEmptyViewArea; QPointer p_view; ViewArea *p_viewArea1; ViewArea *p_viewArea2; ViewContainer *p_viewContainer; }; /** @author David Saxton */ class ViewContainer : public QWidget { Q_OBJECT public: /** * Constructs a new ViewContainer, along with a default ViewArea ready for * parenting a View, with an id of 0. parent is only used if ktechlab is * null; otherwise the parent widget is ktechlab's tabWidget() */ ViewContainer( const QString & caption, QWidget * parent = nullptr ); ~ViewContainer() override; /** * Returns the view in the ViewArea with the given id */ View *view( uint id ) const; /** * Returns the ViewArea with the given id */ ViewArea *viewArea( uint id ) const; /** * The active view area is the one that is focused. */ void setActiveViewArea( uint id ); /** * Returns the id of the active ViewArea */ uint activeViewArea() const { return m_activeViewArea; } /** * Returns a pointer to the view of the active view area */ View *activeView() const { return view( activeViewArea() ); } /** * Attempts to close the given viewarea, returning true if successful (i.e * if the user did not object to the close request ) */ bool closeViewArea( uint id ); /** * Creates a view area (parent QWidget, splitter et al) ready for inclusion * of a view. * @param relativeViewArea the viewarea to position the new viewarea next to, if -1 then is taken to be the active view area * @param position Top, Right, Bottom or Left of given relativeViewArea * @returns id of the view area, or -1 if unsucessful * @param showOpenButton Whether to present the user with the EmptyViewArea * widget (i.e. the new ViewArea is not destined to be immediately filled * with a view). */ int createViewArea( int relativeViewArea, ViewArea::Position position, bool showOpenButton ); /** * Attempts to close each view area, returning false if any fail to be * closed */ bool closeViewContainer(); /** * @returns number of views in this view container */ uint viewCount() const { return m_viewAreaMap.size(); } /** * Sets the pointer to the view area with the given id */ void setViewAreaId( ViewArea *viewArea, uint id ); /** * Removes a ViewArea from internal lists */ void setViewAreaRemoved( uint id ); /** * Sets the id to be "used" */ void setIdUsed( int id ); /** * Writes the state of the View Container (layout of views and view URLs) * to the given KConfig. Doesn't change the group - so preset it if * needed! */ void saveState( KConfigGroup *config ); /** * Reads in the saved config state (as written by saveState), and restores * the ViewContainer with all appropriate views open * @param groupName e.g. "ViewContainer 1" */ void restoreState( KConfigGroup *config, const QString &groupName ); /** * Returns a unique id (negative) for a ViewArea that is now a Parent of other ViewAreas */ int uniqueParentId(); /** * Returns a unique id (positive) for a new ViewArea */ int uniqueNewId(); /** * Returns true if this ViewArea can save useful information as to its state * (i.e. it's children can save useful information about their state, or has * a view with a url in it) */ bool canSaveUsefulStateInfo() const; public slots: /** * Sets the tab caption et al from the contents of this ViewContainer */ void updateCaption(); protected slots: void baseViewAreaDestroyed( QObject *obj ); protected: void restoreViewArea( KConfigGroup *config, int id, ViewArea *viewArea ); void findActiveViewArea(); int m_activeViewArea; ViewArea *m_baseViewArea; ViewAreaMap m_viewAreaMap; IntList m_usedIDs; bool b_deleted; }; #endif diff --git a/tests/tests_app/tests_app.cpp b/tests/tests_app/tests_app.cpp index 65233c07..e0899275 100644 --- a/tests/tests_app/tests_app.cpp +++ b/tests/tests_app/tests_app.cpp @@ -1,119 +1,119 @@ /* * KTechLab: An IDE for microcontrollers and electronics * Copyright 2018 Zoltan Padrah * * 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) version 3 or any later version * accepted by the membership of KDE e.V. (or its successor approved * by the membership of KDE e.V.), which shall act as a proxy * defined in Section 14 of version 3 of the license. * * 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, see . * */ #include "../src/ktechlab.h" #include "config.h" #include "docmanager.h" #include "electronics/circuitdocument.h" #include #include -#include -#include -#include +#include +#include +#include #include #include class KtlTestsAppFixture : public QObject { Q_OBJECT public: QApplication *app; KTechlab *ktechlab; private slots: void initTestCase() { int argc = 1; char argv0[] = "tests_app"; char *argv[] = { argv0, nullptr }; app = new QApplication(argc, argv); KAboutData aboutData("ktechlab", i18n("KTechLab"), VERSION, i18n("An IDE for microcontrollers and electronics"), KAboutLicense::GPL_V2, i18n("(C) 2003-2017, The KTechLab developers"), "", "https://userbase.kde.org/KTechlab", "ktechlab-devel@kde.org" ); KAboutData::setApplicationData(aboutData); QCommandLineParser parser; aboutData.setupCommandLine(&parser); parser.process(*app); aboutData.processCommandLine(&parser); ktechlab = new KTechlab; } void cleanupTestCase() { delete ktechlab; ktechlab = nullptr; //delete app; // this crashes apparently app = nullptr; } void testDocumentOpen() { DocManager::self()->closeAll(); QCOMPARE( DocManager::self()->m_documentList.size(), 0); QFile exFile(SRC_TESTS_DATA_DIR "test-document-draw-1.circuit"); QUrl exUrl = QUrl::fromLocalFile(exFile.fileName()); qDebug() << "open example: " << exUrl; DocManager::self()->openURL(exUrl, nullptr); QCOMPARE( DocManager::self()->m_documentList.size(), 1); Document *doc = DocManager::self()->m_documentList.first(); QVERIFY( doc != nullptr ); QCOMPARE( doc->type(), Document::dt_circuit ); CircuitDocument *circDoc = static_cast( doc ); QVERIFY( circDoc != nullptr ); QVERIFY( circDoc->m_canvas ); qDebug() << "item list size " << circDoc->m_itemList.size(); //QRect saveArea = circDoc->m_canvas->rect(); // is empty QRect resizeArea(0, -500, 400, 1080); qDebug() << " resizeArea " << resizeArea; circDoc->m_canvas->resize(resizeArea); QRect saveArea(-500, -500, 1040, 1080); qDebug() << "save area " << saveArea; QPixmap *outputImage = new QPixmap( saveArea.size() ); outputImage->fill(Qt::green); circDoc->exportToImageDraw(saveArea, *outputImage); QImage img = dynamic_cast(outputImage)->toImage(); img = img.copy(); QTemporaryFile imgFile("testDocumentOpen_output_XXXXXX.png"); imgFile.setAutoRemove(false); imgFile.open(); qDebug() << "imgFile.fileName() = " << imgFile.fileName(); bool saveResult = img.save(imgFile.fileName()); QCOMPARE( saveResult, true ); delete outputImage; DocManager::self()->closeAll(); QCOMPARE( DocManager::self()->m_documentList.size(), 0); } }; QTEST_MAIN(KtlTestsAppFixture) #include "tests_app.moc"