示例#1
0
文件: editor.cpp 项目: KDE/abakus
void Editor::autoComplete( const QString& item )
{
    if( !d->autoCompleteEnabled || item.isEmpty() )
        return;

    int para = 0, curPos = 0;
    getCursorPosition( &para, &curPos );

    QString subtext = text().left( curPos );
    Tokens tokens = Evaluator::scan( subtext );

    if( !tokens.valid() || tokens.count() < 1 )
        return;

    Token lastToken = tokens[ tokens.count()-1 ];
    if( !lastToken.isIdentifier() )
        return;

    QStringList str = QStringList::split( ':', item );

    blockSignals( true );
    setSelection( 0, lastToken.pos(), 0, lastToken.pos()+lastToken.text().length() );
    insert( str[0] );
    blockSignals( false );
}
示例#2
0
void Editor::doMatchingRight()
{
    // Tokenize the expression.
    const int currentPosition = textCursor().position();

    // Check for left par.
    QString subtext = text().right(text().length() - currentPosition);
    Tokens tokens = m_evaluator->scan(subtext);
    if (!tokens.valid() || tokens.count() < 1)
        return;
    Token firstToken = tokens.at(0);

    // Left par?
    if (firstToken.type() == Token::stxOpenPar && firstToken.pos() == 0) {
        // Find the matching right par.
        unsigned par = 1;
        int k = 0;
        Token matchToken;
        int matchPosition = -1;

        for (k = 1; k < tokens.count() && par > 0; ++k) {
            const Token matchToken = tokens.at(k);
            switch (matchToken.type()) {
                case Token::stxOpenPar : ++par; break;
                case Token::stxClosePar: --par; break;
                default:;
            }
            matchPosition = matchToken.pos();
        }

        if (par == 0) {
            QTextEdit::ExtraSelection hilite1;
            hilite1.cursor = textCursor();
            hilite1.cursor.setPosition(currentPosition+matchPosition);
            hilite1.cursor.setPosition(currentPosition+matchPosition + 1, QTextCursor::KeepAnchor);
            hilite1.format.setBackground(m_highlighter->colorForRole(SyntaxHighlighter::Matched));

            QTextEdit::ExtraSelection hilite2;
            hilite2.cursor = textCursor();
            hilite2.cursor.setPosition(currentPosition+firstToken.pos());
            hilite2.cursor.setPosition(currentPosition+firstToken.pos() + 1, QTextCursor::KeepAnchor);
            hilite2.format.setBackground(m_highlighter->colorForRole(SyntaxHighlighter::Matched));

            QList<QTextEdit::ExtraSelection> extras;
            extras << hilite1;
            extras << hilite2;
            setExtraSelections(extras);
        }
    }
}
示例#3
0
文件: editor.cpp 项目: KDE/abakus
void Editor::doMatchingRight()
{
    if( !d->syntaxHighlightEnabled ) return;

    // tokenize the expression
    int para = 0, curPos = 0;
    getCursorPosition( &para, &curPos );

    // check for left par
    QString subtext = text().right( text().length() - curPos );
    Tokens tokens = Evaluator::scan( subtext );
    if( !tokens.valid() ) return;
    if( tokens.count()<1 ) return;
    Token firstToken = tokens[ 0 ];

    // left par ?
    if( firstToken.isOperator() )
    if( firstToken.asOperator() == Token::LeftPar )
    if( firstToken.pos() == 0 )
    {
        // find the matching right par
        unsigned par = 1;
        unsigned int k = 0;
        Token matchToken;
        int matchPos = -1;

        for( k = 1; k < tokens.count(); k++ )
        {
            if( par < 1 ) break;
            Token matchToken = tokens[k];
            if( matchToken.isOperator() )
            {
    if( matchToken.asOperator() == Token::LeftPar )
        par++;
    if( matchToken.asOperator() == Token::RightPar )
        par--;
    if( par == 0 ) matchPos = matchToken.pos();
            }
        }

        if( matchPos >= 0 )
        {
            setSelection( 0, curPos+matchPos, 0, curPos+matchPos+1, 2 );
            setSelection( 0, curPos+firstToken.pos(), 0, curPos+firstToken.pos()+1, 1 );
            setCursorPosition( para, curPos );
        }
    }

}
示例#4
0
void Editor::doMatchingLeft()
{
    // Tokenize the expression.
    const int currentPosition = textCursor().position();

    // Check for right par.
    QString subtext = text().left(currentPosition);
    Tokens tokens = m_evaluator->scan(subtext, Evaluator::NoAutoFix);
    if (!tokens.valid() || tokens.count() < 1)
        return;
    Token lastToken = tokens.at(tokens.count() - 1);

    // Right par?
    if (lastToken.type() == Token::stxClosePar && lastToken.pos() == currentPosition - 1) {
        // Find the matching left par.
        unsigned par = 1;
        int matchPosition = -1;

        for (int i = tokens.count() - 2; i >= 0 && par > 0; --i) {
            Token matchToken = tokens.at(i);
            switch (matchToken.type()) {
                case Token::stxOpenPar : --par; break;
                case Token::stxClosePar: ++par; break;
                default:;
            }
            matchPosition = matchToken.pos();
        }

        if (par == 0) {
            QTextEdit::ExtraSelection hilite1;
            hilite1.cursor = textCursor();
            hilite1.cursor.setPosition(matchPosition);
            hilite1.cursor.setPosition(matchPosition + 1, QTextCursor::KeepAnchor);
            hilite1.format.setBackground(m_highlighter->colorForRole(SyntaxHighlighter::Matched));

            QTextEdit::ExtraSelection hilite2;
            hilite2.cursor = textCursor();
            hilite2.cursor.setPosition(lastToken.pos());
            hilite2.cursor.setPosition(lastToken.pos() + 1, QTextCursor::KeepAnchor);
            hilite2.format.setBackground(m_highlighter->colorForRole(SyntaxHighlighter::Matched));

            QList<QTextEdit::ExtraSelection> extras;
            extras << hilite1;
            extras << hilite2;
            setExtraSelections(extras);
        }
    }
}
示例#5
0
// Called by the cell tool when setting the active element with a cell location.
void CellEditor::setActiveSubRegion(int index)
{
    index = qBound(0, index, (int)d->highlighter->rangeCount());
    int counter = 0;
    bool subRegion = false;
    const Tokens tokens = d->highlighter->formulaTokens();
    for (int i = 0; i < tokens.count(); ++i) {
        const Token token = tokens[i];
        switch (token.type()) {
        case Token::Cell:
        case Token::Range:
            if (!subRegion) {
                d->currentToken = i;
                subRegion = true;
            }
            if (counter == index) {
                setCursorPosition(token.pos() + token.text().length() + 1);
                return;
            }
            ++counter;
            continue;
        case Token::Operator:
            if (token.asOperator() == Token::Semicolon) {
                if (subRegion) {
                    continue;
                }
            }
            subRegion = false;
            continue;
        default:
            subRegion = false;
            continue;
        }
    }
}
int FormulaEditorHighlighter::findMatchingBrace(int pos)
{
    int depth = 0;
    int step = 0;

    Tokens tokens = d->tokens;

    //If this is a left brace we need to step forwards through the text to find the matching right brace,
    //otherwise, it is a right brace so we need to step backwards through the text to find the matching left
    //brace.
    if (tokens.at(pos).asOperator() == Token::LeftPar)
        step = 1;
    else
        step = -1;

    for (int index = pos ; (index >= 0) && (index < (int) tokens.count()) ; index += step) {
        if (tokens.at(index).asOperator() == Token::LeftPar)
            depth++;
        if (tokens.at(index).asOperator() == Token::RightPar)
            depth--;

        if (depth == 0) {
            return index;
        }
    }

    return -1;
}
示例#7
0
void SyntaxHighlighter::highlightBlock(const QString& text)
{
    if (!Settings::instance()->syntaxHighlighting) {
        setFormat(0, text.length(), colorForRole(Number));
        return;
    }

    if (text.startsWith(QLatin1String("="))) {
        setFormat(0, 1, colorForRole(Operator));
        setFormat(1, text.length(), colorForRole(Result));
        groupDigits(text, 1, text.length() - 1);
        return;
    }

    Tokens tokens = Evaluator::instance()->scan(text);

    for (int i = 0; i < tokens.count(); ++i) {
        const Token& token = tokens.at(i);
        const QString tokenText = token.text().toLower();
        QStringList functionNames = FunctionRepo::instance()->getIdentifiers();
        QColor color;

        switch (token.type()) {
        case Token::stxNumber:
        case Token::stxUnknown:
            color = colorForRole(Number);
            break;

        case Token::stxOperator:
            color = colorForRole(Operator);
            break;

        case Token::stxSep:
            color = colorForRole(Separator);
            break;

        case Token::stxOpenPar:
        case Token::stxClosePar:
            color = colorForRole(Parens);
            break;

        case Token::stxIdentifier:
            color = colorForRole(Variable);
            for (int i = 0; i < functionNames.count(); ++i)
                if (functionNames.at(i).toLower() == tokenText)
                    color = colorForRole(Function);
            break;

        default:
            break;
        };

        if (token.pos() >= 0) {
            setFormat(token.pos(), token.text().length(), color);
            if (token.type() == Token::stxNumber)
                groupDigits(text, token.pos(), token.text().length());
        }
    }
}
示例#8
0
void Editor::autoComplete(const QString& item)
{
    if (!m_isAutoCompletionEnabled || item.isEmpty())
        return;

    const int currentPosition = textCursor().position();
    const QString subtext = text().left(currentPosition);
    const Tokens tokens = m_evaluator->scan(subtext);
    if (!tokens.valid() || tokens.count() < 1)
        return;

    const Token lastToken = tokens.at(tokens.count() - 1);
    if (!lastToken.isIdentifier())
        return;

    const QStringList str = item.split(':');

    blockSignals(true);
    QTextCursor cursor = textCursor();
    cursor.setPosition(lastToken.pos());
    cursor.setPosition(lastToken.pos() + lastToken.text().length(), QTextCursor::KeepAnchor);
    setTextCursor(cursor);
    insert(str.at(0));
    blockSignals(false);

    cursor = textCursor();
    bool hasParensAlready;
    if ((hasParensAlready = cursor.movePosition(QTextCursor::NextCharacter, QTextCursor::KeepAnchor))) {
        QString nextChar = cursor.selectedText();
        hasParensAlready = (nextChar == "(");
    }
    bool shouldAutoInsertParens = (FunctionRepo::instance()->find(str.at(0))
                                   || m_evaluator->hasUserFunction(str.at(0)))
                                  && !hasParensAlready;
    if (shouldAutoInsertParens) {
        insert(QString::fromLatin1("()"));
        cursor = textCursor();
        cursor.movePosition(QTextCursor::PreviousCharacter);
        setTextCursor(cursor);
    }
}
示例#9
0
void CellEditor::Private::rebuildSelection()
{
    // Do not react on selection changes, that update the formula's expression,
    // because the selection gets already build based on the current formula.
    selectionChangedLocked = true;

    Sheet *const originSheet = selection->originSheet();
    Map *const map = originSheet->map();

    // Rebuild the reference selection by using the formula tokens.
    Tokens tokens = highlighter->formulaTokens();
    selection->update(); // set the old cursor dirty; updates the editors
    selection->clear();

    //A list of regions which have already been highlighted on the spreadsheet.
    //This is so that we don't end up highlighting the same region twice in two different
    //colors.
    QSet<QString> alreadyUsedRegions;

    int counter = 0;
    for (int i = 0; i < tokens.count(); ++i) {
        const Token token = tokens[i];
        const Token::Type type = token.type();

        if (type == Token::Cell || type == Token::Range) {
            const Region region(token.text(), map, originSheet);

            if (!region.isValid() || region.isEmpty()) {
                continue;
            }
            if (alreadyUsedRegions.contains(region.name())) {
                continue;
            }
            alreadyUsedRegions.insert(region.name());

            const QRect range = region.firstRange();
            Sheet *const sheet = region.firstSheet();

            selection->initialize(range, sheet);
            // Always append the next range by pointing behind the last item.
            selection->setActiveSubRegion(++counter, 0);
        }
    }

    // Set the active sub-region.
    // Needs up-to-date tokens; QSyntaxHighlighter::rehighlight() gets called
    // automatically on text changes, which does the update.
    updateActiveSubRegion(highlighter->formulaTokens());

    selectionChangedLocked = false;
}
示例#10
0
void Editor::autoCalcSelection()
{
    if (!m_isAutoCalcEnabled)
        return;

    const QString str = m_evaluator->autoFix(textCursor().selectedText());
    if (str.isEmpty())
        return;

    // Very short (just one token) and still no calculation, then skip.
    if (!m_isAnsAvailable) {
        const Tokens tokens = m_evaluator->scan(text());
        if (tokens.count() < 2)
            return;
    }

    // Too short even after autofix? Don't bother either.
    const Tokens tokens = m_evaluator->scan(str);
    if (tokens.count() < 2)
        return;

    // Same reason as above, do not update "ans".
    m_evaluator->setExpression(str);
    const HNumber num = m_evaluator->evalNoAssign();

    if (m_evaluator->error().isEmpty()) {
        if (num.isNan() && m_evaluator->isUserFunctionAssign()) {
            // Result is not always available when assigning a user function.
            const QString message = tr("Selection result: n/a");
            emit autoCalcEnabled(message);
        } else {
            const QString message = tr("Selection result: <b>%1</b>").arg(NumberFormatter::format(num));
            emit autoCalcEnabled(message);
        }
    } else
        emit autoCalcEnabled(m_evaluator->error());
}
示例#11
0
文件: editor.cpp 项目: KDE/abakus
int EditorHighlighter::highlightParagraph ( const QString & text, int )
{
    if( !editor->isSyntaxHighlightEnabled() )
    {
        setFormat( 0, text.length(), editor->colorGroup().text() );
        return 0;
    }

    QStringList fnames = FunctionManager::instance()->functionList(FunctionManager::All);
    fnames.sort(); // Sort list so we can bin search it.

    Tokens tokens = Evaluator::scan( text );
    for( unsigned i = 0; i < tokens.count(); i++ )
    {
        Token& token = tokens[i];
        QString text = token.text().lower();
        QFont font = editor->font();
        QColor color = Qt::black;
        switch( token.type() )
        {
            case Token::Number:
            color = editor->highlightColor( Editor::Number );
            break;

            case Token::Identifier:
            {
                color = editor->highlightColor( Editor::Variable );

                // XXX: QT 4: Replace this with qBinaryFind().

                if( fnames.contains( text ) ) {
                    color = editor->highlightColor( Editor::FunctionName );
                }
            }
            break;

            case Token::Operator:
            break;

            default: break;
        }

        if( token.pos() >= 0 ) {
            setFormat( token.pos(), token.text().length(), font, color );
        }
    }

    return 0;
}
示例#12
0
static QString tokenizeFormula(const QString& formula)
{
  Formula f;
  QString expr = formula;
  expr.prepend( '=' );
  f.setExpression( expr );
  Tokens tokens = f.tokens();

  QString resultCodes;
  if( tokens.valid() )
    for( int i = 0; i < tokens.count(); i++ )
      resultCodes.append( encodeTokenType( tokens[i] ) );

  return resultCodes;
}
int ClangFunctionHintModel::activeArgument(const QString &prefix) const
{
    int argnr = 0;
    int parcount = 0;
    SimpleLexer tokenize;
    Tokens tokens = tokenize(prefix);
    for (int i = 0; i < tokens.count(); ++i) {
        const CPlusPlus::Token &tk = tokens.at(i);
        if (tk.is(T_LPAREN))
            ++parcount;
        else if (tk.is(T_RPAREN))
            --parcount;
        else if (! parcount && tk.is(T_COMMA))
            ++argnr;
    }

    if (parcount < 0)
        return -1;

    if (argnr != m_currentArg)
        m_currentArg = argnr;

    return argnr;
}
void FormulaEditorHighlighter::highlightBlock(const QString& text)
{
    // reset syntax highlighting
    setFormat(0, text.length(), QApplication::palette().text().color());

    // save the old ones to identify range changes
    Tokens oldTokens = d->tokens;

    // interpret the text as formula
    // we accept invalid/incomplete formulas
    Formula f;
    d->tokens = f.scan(text);

    QFont editorFont = document()->defaultFont();
    QFont font;

    uint oldRangeCount = d->rangeCount;

    d->rangeCount = 0;
    QList<QColor> colors = d->selection->colors();
    QList<QString> alreadyFoundRanges;

    Sheet *const originSheet = d->selection->originSheet();
    Map *const map = originSheet->map();

    for (int i = 0; i < d->tokens.count(); ++i) {
        Token token = d->tokens[i];
        Token::Type type = token.type();

        switch (type) {
        case Token::Cell:
        case Token::Range: {
            // don't compare, if we have already found a change
            if (!d->rangeChanged && i < oldTokens.count() && token.text() != oldTokens[i].text()) {
                d->rangeChanged = true;
            }

            const Region newRange(token.text(), map, originSheet);
            if (!newRange.isValid()) {
                continue;
            }

            int index = alreadyFoundRanges.indexOf(newRange.name());
            if (index == -1) { /* not found */
                alreadyFoundRanges.append(newRange.name());
                index = alreadyFoundRanges.count() - 1;
            }
            const QColor color(colors[index % colors.size()]);
            setFormat(token.pos() + 1, token.text().length(), color);
            ++d->rangeCount;
        }
        break;
        case Token::Boolean:     // True, False (also i18n-ized)
            /*        font = QFont(editorFont);
                    font.setBold(true);
                    setFormat(token.pos() + 1, token.text().length(), font);*/
            break;
        case Token::Identifier:   // function name or named area*/
            /*        font = QFont(editorFont);
                    font.setBold(true);
                    setFormat(token.pos() + 1, token.text().length(), font);*/
            break;

        case Token::Unknown:
        case Token::Integer:     // 14, 3, 1977
        case Token::Float:       // 3.141592, 1e10, 5.9e-7
        case Token::String:      // "Calligra", "The quick brown fox..."
        case Token::Error:
            break;
        case Token::Operator: {  // +, *, /, -
            switch (token.asOperator()) {
            case Token::LeftPar:
            case Token::RightPar:
                //Check where this brace is in relation to the cursor and highlight it if necessary.
                handleBrace(i);
                break;
            default:
                break;
            }
        }
        break;
        }
    }

    if (oldRangeCount != d->rangeCount)
        d->rangeChanged = true;
}
示例#15
0
// Called on selection (and sheet) changes.
void CellEditor::selectionChanged()
{
    if (d->selectionChangedLocked) {
        return;
    }

    Selection* choice = selection();

    if (choice->isEmpty())
        return;

    const QString text = toPlainText();
    const int textLength = text.length();

    // Find the start text cursor position for the active sub-region within
    // the formula's expression and determine the length of the sub-region.
    Tokens tokens = d->highlighter->formulaTokens();
    uint start = 1;
    uint length = 0;
    if (!tokens.empty()) {
        if (d->currentToken < tokens.count()) {
            Token token = tokens[d->currentToken];
            Token::Type type = token.type();
            if (type == Token::Cell || type == Token::Range) {
                start = token.pos() + 1; // don't forget the '='!
                length = token.text().length();
                // Iterate to the end of the sub-region.
                for (int i = d->currentToken + 1; i < tokens.count(); ++i) {
                    token = tokens[i];
                    type = token.type();
                    switch (type) {
                    case Token::Cell:
                    case Token::Range:
                        length += token.text().length();
                        continue;
                    case Token::Operator:
                        if (token.asOperator() == Token::Semicolon) {
                            ++length;
                            continue;
                        }
                    default:
                        break;
                    }
                    break;
                }
            } else {
                start = token.pos() + 1; // don't forget the '='!
                length = token.text().length();
            }
        } else {
            // sanitize
            d->currentToken = tokens.count();
            start = textLength;
        }
    }

    // Replace the formula's active sub-region with the selection's one.
    const QString address = choice->activeSubRegionName();
    const QString newExpression = QString(text).replace(start, length, address);
    // The expression highlighting gets updated automatically by the next call,
    // even though signals are blocked (must be connected to QTextDocument).
    blockSignals(true);
    setText(newExpression, start + address.length());
    blockSignals(false);

    // Ranges have changed.
    // Reset the flag, that indicates range changes after text changes.
    d->highlighter->resetRangeChanged();
    // Mirror the behaviour of slotCursorPositionChanged(), but here the tokens
    // are already up-to-date.
    d->globalCursorPos = mapToGlobal(cursorRect().bottomLeft());
    // Set the active sub-region.
    // Needs up-to-date tokens; QSyntaxHighlighter::rehighlight() gets called
    // automatically on text changes, which does the update.
    d->updateActiveSubRegion(d->highlighter->formulaTokens());

    // Always emit, because this editor may be hidden or does not have focus,
    // but the external one needs an update.
    emit textChanged(toPlainText());
}
示例#16
0
bool Conditions::isTrueFormula(const Cell &cell, const QString &formula, const QString &baseCellAddress) const
{
    Map* const map = cell.sheet()->map();
    ValueCalc *const calc = map->calc();
    Formula f(cell.sheet(), cell);
    f.setExpression('=' + formula);
    Region r(baseCellAddress, map, cell.sheet());
    if (r.isValid() && r.isSingular()) {
        QPoint basePoint = static_cast<Region::Point*>(*r.constBegin())->pos();
        QString newFormula('=');
        const Tokens tokens = f.tokens();
        for (int t = 0; t < tokens.count(); ++t) {
            const Token token = tokens[t];
            if (token.type() == Token::Cell || token.type() == Token::Range) {
                if (map->namedAreaManager()->contains(token.text())) {
                    newFormula.append(token.text());
                    continue;
                }
                const Region region(token.text(), map, cell.sheet());
                if (!region.isValid() || !region.isContiguous()) {
                    newFormula.append(token.text());
                    continue;
                }
                if (region.firstSheet() != r.firstSheet()) {
                    newFormula.append(token.text());
                    continue;
                }
                Region::Element* element = *region.constBegin();
                if (element->type() == Region::Element::Point) {
                    Region::Point* point = static_cast<Region::Point*>(element);
                    QPoint pos = point->pos();
                    if (!point->isRowFixed()) {
                        int delta = pos.y() - basePoint.y();
                        pos.setY(cell.row() + delta);
                    }
                    if (!point->isColumnFixed()) {
                        int delta = pos.x() - basePoint.x();
                        pos.setX(cell.column() + delta);
                    }
                    newFormula.append(Region(pos, cell.sheet()).name());
                } else {
                    Region::Range* range = static_cast<Region::Range*>(element);
                    QRect r = range->rect();
                    if (!range->isTopFixed()) {
                        int delta = r.top() - basePoint.y();
                        r.setTop(cell.row() + delta);
                    }
                    if (!range->isBottomFixed()) {
                        int delta = r.bottom() - basePoint.y();
                        r.setBottom(cell.row() + delta);
                    }
                    if (!range->isLeftFixed()) {
                        int delta = r.left() - basePoint.x();
                        r.setLeft(cell.column() + delta);
                    }
                    if (!range->isRightFixed()) {
                        int delta = r.right() - basePoint.x();
                        r.setRight(cell.column() + delta);
                    }
                    newFormula.append(Region(r, cell.sheet()).name());
                }
            } else {
                newFormula.append(token.text());
            }
        }
        f.setExpression(newFormula);
    }
    Value val = f.eval();
    return calc->conv()->asBoolean(val).asBoolean();
}
示例#17
0
QString Calligra::Sheets::Odf::encodeFormula(const QString& expr, const KLocale* locale)
{
    // use locale settings
    const QString decimal = locale ? locale->decimalSymbol() : ".";

    QString result('=');

    Formula formula;
    Tokens tokens = formula.scan(expr, locale);

    if (!tokens.valid() || tokens.count() == 0)
        return expr; // no altering on error

    for (int i = 0; i < tokens.count(); ++i) {
        const QString tokenText = tokens[i].text();
        const Token::Type type = tokens[i].type();

        switch (type) {
        case Token::Cell:
        case Token::Range: {
            result.append('[');
            // FIXME Stefan: Hack to get the apostrophes right. Fix and remove!
            const int pos = tokenText.lastIndexOf('!');
            if (pos != -1 && tokenText.left(pos).contains(' '))
                result.append(Region::saveOdf('\'' + tokenText.left(pos) + '\'' + tokenText.mid(pos)));
            else
                result.append(Region::saveOdf(tokenText));
            result.append(']');
            break;
        }
        case Token::Float: {
            QString tmp(tokenText);
            result.append(tmp.replace(decimal, "."));
            break;
        }
        case Token::Operator: {
            if (tokens[i].asOperator() == Token::Equal)
                result.append('=');
            else
                result.append(tokenText);
            break;
        }
        case Token::Identifier: {
            if (tokenText == "ERRORTYPE") {
                // need to replace this
                result.append("ERROR.TYPE");
            } else if (tokenText == "LEGACYNORMSDIST") {
                result.append("LEGACY.NORMSDIST");
            } else if (tokenText == "LEGACYNORMSINV") {
                result.append("LEGACY.NORMSINV");
            } else {
                // dump it out unchanged
                result.append(tokenText);
            }
            break;

        }
        case Token::Boolean:
        case Token::Integer:
        case Token::String:
        default:
            result.append(tokenText);
            break;
        }
    }
    return result;
}
示例#18
0
文件: editor.cpp 项目: KDE/abakus
void Editor::autoCalc()
{
    if( !d->autoCalcEnabled )
        return;

    QString str = Evaluator::autoFix( text() );
    if( str.isEmpty() )
        return;

    // too short? do not bother...
    Tokens tokens = Evaluator::scan( str );
    if( tokens.count() < 2 )
        return;

    // If we're using set for a function don't try.
    QRegExp setFn("\\s*set.*\\(.*=");
    if( str.find(setFn) != -1 )
        return;

    // strip off assignment operator, e.g. "x=1+2" becomes "1+2" only
    // the reason is that we want only to evaluate (on the fly) the expression,
    // not to update (put the result in) the variable
    if( tokens.count() > 2 && tokens[0].isIdentifier() &&
            tokens[1].asOperator() == Token::Equal )
    {
        Tokens::const_iterator it = tokens.begin();
        ++it;
        ++it; // Skip first two tokens.

        // Reconstruct string to evaluate using the tokens.
        str = "";
        while(it != tokens.end())
        {
            str += (*it).text();
            str += ' ';
            ++it;
        }
    }

    Abakus::number_t result = parseString(str.latin1());
    if( Result::lastResult()->type() == Result::Value )
    {
        QString ss = i18n("Result: <b>%2</b>", result.toString());
        d->autoCalcLabel->setText( ss );
        d->autoCalcLabel->adjustSize();

        // reposition nicely
        QPoint pos = mapToGlobal( QPoint( 0, 0 ) );
        pos.setY( pos.y() - d->autoCalcLabel->height() - 1 );
        d->autoCalcLabel->move( pos );
        d->autoCalcLabel->show();
        d->autoCalcLabel->raise();

        // do not show it forever
        QTimer::singleShot( 5000, d->autoCalcLabel, SLOT( hide()) );
    }
    else
    {
        // invalid expression
        d->autoCalcLabel->hide();
    }
}
示例#19
0
void CellEditor::Private::updateActiveSubRegion(const Tokens &tokens)
{
    // Index of the token, at which the text cursor is positioned.
    // For sub-regions it is the start range.
    currentToken = 0;

    if (tokens.isEmpty()) {
        selection->setActiveSubRegion(0, 0); // also set the active element
        return;
    }

    const int cursorPosition = textEdit->textCursor().position() - 1; // without '='
    kDebug() << "cursorPosition:" << cursorPosition << "textLength:" << textEdit->toPlainText().length() - 1;

    uint rangeCounter = 0; // counts the ranges in the sub-region
    uint currentRange = 0; // range index denoting the current range
    int regionStart = 0; // range index denoting the sub-region start
    uint regionEnd = 0; // range index denoting the sub-region end
    enum { Anywhere, InRegion, BeyondCursor } state = Anywhere;

    Token token;
    Token::Type type;
    // Search the current range the text cursor is positioned to.
    // Determine the subregion start and end, in which the range is located.
    for (int i = 0; i < tokens.count(); ++i) {
        token = tokens[i];
        type = token.type();

        // If not in a subregion, we may already quit the loop here.
        if (state == Anywhere) {
            // Already beyond the cursor position?
            if (token.pos() > cursorPosition) {
                state = BeyondCursor;
                break; // for loop
            }
        } else if (state == InRegion) {
            // Loop to the end of the subregion.
            if (type == Token::Cell || type == Token::Range) {
                regionEnd = rangeCounter++;
                continue; // keep going until the referenced region ends
            }
            if (type == Token::Operator) {
                if (tokens[i].asOperator() == Token::Semicolon) {
                    continue; // keep going until the referenced region ends
                }
            }
            state = Anywhere;
            continue;
        }

        // Can the token be replaced by a reference?
        switch (type) {
        case Token::Cell:
        case Token::Range:
            if (state == Anywhere) {
                currentToken = i;
                regionStart = rangeCounter;
                state = InRegion;
            }
            regionEnd = rangeCounter; // length = 1
            currentRange = ++rangeCounter; // point behind the last
            continue;
        case Token::Unknown:
        case Token::Boolean:
        case Token::Integer:
        case Token::Float:
        case Token::String:
        case Token::Error:
            // Set the active sub-region start to the next range but
            // with a length of 0, which results in inserting a new range
            // to the selection on calling Selection::initialize() or
            // Selection::update().
            currentToken = i;
            regionStart = rangeCounter; // position of the next range
            regionEnd = rangeCounter - 1; // length = 0
            currentRange = rangeCounter;
            continue;
        case Token::Operator:
        case Token::Identifier:
            continue;
        }
    }

    // Cursor not reached? I.e. the cursor is placed at the last token's end.
    if (state == Anywhere) {
        token = tokens.last();
        type = token.type();
        // Check the last token.
        // It was processed, but maybe a reference can be placed behind it.
        // Check, if the token can be replaced by a reference.
        switch (type) {
        case Token::Operator:
            // Possible to place a reference behind the operator?
            switch (token.asOperator()) {
            case Token::Plus:
            case Token::Minus:
            case Token::Asterisk:
            case Token::Slash:
            case Token::Caret:
            case Token::LeftPar:
            case Token::Semicolon:
            case Token::Equal:
            case Token::NotEqual:
            case Token::Less:
            case Token::Greater:
            case Token::LessEqual:
            case Token::GreaterEqual:
            case Token::Intersect:
            case Token::Union:
                // Append new references by pointing behind the last.
                currentToken = tokens.count();
                regionStart = rangeCounter;
                regionEnd = rangeCounter - 1; // length = 0
                currentRange = rangeCounter;
                break;
            case Token::InvalidOp:
            case Token::RightPar:
            case Token::Comma:
            case Token::Ampersand:
            case Token::Percent:
            case Token::CurlyBra:
            case Token::CurlyKet:
            case Token::Pipe:
                // reference cannot be placed behind
                break;
            }
            break;
        case Token::Unknown:
        case Token::Boolean:
        case Token::Integer:
        case Token::Float:
        case Token::String:
        case Token::Identifier:
        case Token::Error:
            // currentToken = tokens.count() - 1; // already set
            // Set the active sub-region start to the end of the selection
            // with a length of 0, which results in appending a new range
            // to the selection on calling Selection::initialize() or
            // Selection::update().
            regionStart = rangeCounter;
            regionEnd = rangeCounter - 1; // length = 0
            currentRange = rangeCounter;
            break;
        case Token::Cell:
        case Token::Range:
            // currentToken = tokens.count() - 1; // already set
            // Set the last range as active one. It is not a sub-region,
            // otherwise the state would have been InRegion.
            regionStart = rangeCounter - 1;
            regionEnd = rangeCounter - 1; // length = 1
            currentRange = rangeCounter; // point behind the last
            break;
        }
    }

    const int regionLength = regionEnd - regionStart + 1;
    kDebug() << "currentRange:" << currentRange << "regionStart:" << regionStart
    << "regionEnd:" << regionEnd << "regionLength:" << regionLength;

    selection->setActiveSubRegion(regionStart, regionLength, currentRange);
}
示例#20
0
文件: editor.cpp 项目: KDE/abakus
void Editor::triggerAutoComplete()
{
    if( !d->autoCompleteEnabled ) return;

    // tokenize the expression (don't worry, this is very fast)
    // faster now that it uses flex. ;)
    int para = 0, curPos = 0;
    getCursorPosition( &para, &curPos );
    QString subtext = text().left( curPos );
    Tokens tokens = Evaluator::scan( subtext );
    if(!tokens.valid())
    {
        kdWarning() << "invalid tokens.\n";
        return;
    }

    if(tokens.isEmpty() || subtext.endsWith(" "))
        return;

    Token lastToken = tokens[ tokens.count()-1 ];

    // last token must be an identifier
    if( !lastToken.isIdentifier() )
        return;

    QString id = lastToken.text();
    if( id.isEmpty() )
        return;

    // find matches in function names
    QStringList fnames = FunctionManager::instance()->functionList(FunctionManager::All);
    QStringList choices;

    for( unsigned i=0; i<fnames.count(); i++ )
        if( fnames[i].startsWith( id, false ) )
        {
            QString str = fnames[i];

            ::Function* f = FunctionManager::instance()->function( str );
            if( f && !f->description.isEmpty() )
    str.append( ':' ).append( f->description );

            choices.append( str );
        }

    choices.sort();

    // find matches in variables names
    QStringList vchoices;
    QStringList values = NumeralModel::instance()->valueNames();

    for(QStringList::ConstIterator it = values.begin(); it != values.end(); ++it)
        if( (*it).startsWith( id, false ) )
        {
            QString choice = NumeralModel::description(*it);
            if(choice.isEmpty())
    choice = NumeralModel::instance()->value(*it).toString();

            vchoices.append( QString("%1:%2").arg( *it, choice ) );
        }

    vchoices.sort();
    choices += vchoices;

    // no match, don't bother with completion
    if( !choices.count() ) return;

    // one match, complete it for the user
    if( choices.count()==1 )
    {
        QString str = QStringList::split( ':', choices[0] )[0];

        // single perfect match, no need to give choices.
        if(str == id.lower())
            return;

        str = str.remove( 0, id.length() );
        int para = 0, curPos = 0;
        getCursorPosition( &para, &curPos );
        blockSignals( true );
        insert( str );
        setSelection( 0, curPos, 0, curPos+str.length() );
        blockSignals( false );
        return;
    }

    // present the user with completion choices
    d->completion->showCompletion( choices );
}
示例#21
0
void CellEditor::permuteFixation()
{
    // Nothing to do, if not in reference selection mode.
    if (!d->selection->referenceSelection()) {
        return;
    }

    // Search for the last range before or the range at the cursor.
    int index = -1;
    const int cursorPosition = textCursor().position() - 1; // - '='
    const Tokens tokens = d->highlighter->formulaTokens();
    for (int i = 0; i < tokens.count(); ++i) {
        const Token token = tokens[i];
        if (token.pos() > cursorPosition) {
            break; // for loop
        }
        if (token.type() == Token::Cell || token.type() == Token::Range) {
            index = i;
        }
    }
    // Quit, if no range was found.
    if (index == -1) {
        return;
    }

    const Token token = tokens[index];
    Map *const map = d->selection->activeSheet()->map();
    QString regionName = token.text();
    // Filter sheet; truncates regionName; range without sheet name resides.
    Sheet *const sheet = Region(QString(), map).filterSheetName(regionName);
    const Region region(regionName, map, 0);
    // TODO Stefan: Skip named areas.
    if (!region.isValid()) {
        return;
    }
    // FIXME Stefan: need access to fixation, thus to Region::Range; must use iterator
    Region::Element *range = (*region.constBegin());
    QString result(sheet ? (sheet->sheetName() + '!') : QString());
    // Permute fixation.
    if (region.isSingular()) {
        char fixation = 0x00;
        if (range->isRowFixed()) {
            fixation += 0x01;
        }
        if (range->isColumnFixed()) {
            fixation += 0x02;
        }
        fixation += 0x01;

        int i = 0;
        result += permuteLocationFixation(regionName, i, fixation & 0x02, fixation & 0x01);
    } else {
        char fixation = 0x00;
        if (range->isBottomFixed()) {
            fixation += 0x01;
        }
        if (range->isRightFixed()) {
            fixation += 0x02;
        }
        if (range->isTopFixed()) {
            fixation += 0x04;
        }
        if (range->isLeftFixed()) {
            fixation += 0x08;
        }
        fixation += 0x01;

        int i = 0;
        result += permuteLocationFixation(regionName, i, fixation & 0x08, fixation & 0x04);
        Q_ASSERT(regionName[i] == ':');
        ++i;
        result += ':';
        result += permuteLocationFixation(regionName, i, fixation & 0x02, fixation & 0x01);
    }
    // Replace the range in the formula's expression.
    QString text = toPlainText();
    const int start = token.pos() + 1; // + '='
    const int length = token.text().length();
    setPlainText(text.replace(start, length, result));
    // Set the cursor to the end of the range.
    QTextCursor textCursor = this->textCursor();
    textCursor.setPosition(start + result.length());
    setTextCursor(textCursor);
}
示例#22
0
void Editor::triggerAutoComplete()
{
    if (!m_isAutoCompletionEnabled)
        return;

    // Tokenize the expression (this is very fast).
    const int currentPosition = textCursor().position();
    QString subtext = text().left(currentPosition);
    const Tokens tokens = m_evaluator->scan(subtext, Evaluator::NoAutoFix);
    if (!tokens.valid() || tokens.count() < 1)
        return;

    Token lastToken = tokens.at(tokens.count()-1);

    // Last token must be an identifier.
    if (!lastToken.isIdentifier())
        return;
    const QString id = lastToken.text();
    if (id.length() < 1)
        return;

    // No space after identifier.
    if (lastToken.pos() + id.length() < subtext.length())
        return;

    // Find matches in function names.
    const QStringList fnames = FunctionRepo::instance()->getIdentifiers();
    QStringList choices;
    for (int i = 0; i < fnames.count(); ++i) {
        if (fnames.at(i).startsWith(id, Qt::CaseInsensitive)) {
            QString str = fnames.at(i);
            Function* f = FunctionRepo::instance()->find(str);
            if (f)
                str.append(':').append(f->name());
            choices.append(str);
        }
    }
    choices.sort();

    // Find matches in variables names.
    QStringList vchoices;
    QList<Evaluator::Variable> variables = m_evaluator->getVariables();
    for (int i = 0; i < variables.count(); ++i)
        if (variables.at(i).name.startsWith(id, Qt::CaseInsensitive))
            vchoices.append(QString("%1:%2").arg(variables.at(i).name)
                .arg(NumberFormatter::format(variables.at(i).value)));
    vchoices.sort();
    choices += vchoices;

    // Find matches in user functions.
    QStringList ufchoices;
    QList<Evaluator::UserFunctionDescr> userFunctions = m_evaluator->getUserFunctions();
    for (int i = 0; i < userFunctions.count(); ++i)
        if (userFunctions.at(i).name.startsWith(id, Qt::CaseInsensitive))
            ufchoices.append(QString("%1:User function").arg(userFunctions.at(i).name));
    ufchoices.sort();
    choices += ufchoices;

    // TODO: if we are assigning a user function, find matches in its arguments names and
    //       replace variables names that collide. But we cannot know if we are assigning
    //       a user function without evaluating the expression (a token scan will not be enough).

    // No match, don't bother with completion.
    if (!choices.count())
        return;

    // Single perfect match, no need to give choices.
    if (choices.count() == 1)
        if (choices.at(0).toLower() == id.toLower())
            return;

    // Present the user with completion choices.
    m_completion->showCompletion(choices);
}