src/hbwidgets/editors/hblineedit_p.cpp
author Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
Fri, 17 Sep 2010 08:32:10 +0300
changeset 28 b7da29130b0e
parent 21 4633027730f5
permissions -rw-r--r--
Revision: 201035 Kit: 201037

/****************************************************************************
**
** Copyright (C) 2008-2010 Nokia Corporation and/or its subsidiary(-ies).
** All rights reserved.
** Contact: Nokia Corporation (developer.feedback@nokia.com)
**
** This file is part of the HbWidgets module of the UI Extensions for Mobile.
**
** GNU Lesser General Public License Usage
** 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, Nokia gives you certain additional
** rights.  These rights are described in the Nokia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** If you have questions regarding the use of this file, please contact
** Nokia at developer.feedback@nokia.com.
**
****************************************************************************/

//
//  W A R N I N G
//  -------------
//
// This file is not part of the Hb API.  It exists purely as an
// implementation detail.  This file may change from version to
// version without notice, or even be removed.
//
// We mean it.
//

#include "hblineedit_p.h"
#include "hblineedit.h"
#include "hbcolorscheme.h"

#include <QTextDocument>
#include <QTextBlock>
#include <math.h>

// value used if no maximum lines is set
const int DefaulMaximumVisibleLines = 30;

// minimum font size when using stretch font mode
const qreal KMinimumLineHeight = 4.0;

HbLineEditPrivate::HbLineEditPrivate () :
    HbAbstractEditPrivate(),
    maxLength(-1),
    minimumRows(1),
    maximumRows(1),
    echoMode(HbLineEdit::Normal),
    clearOnEdit(false),
    emitTextChanged(true),
    adjustFontSizeToFitHeight(false),
    stretchedToLineCount(-1),
    mCustomAutoCompContent(0),
    mCustomAutoCompPopup(0)
{
}

HbLineEditPrivate::~HbLineEditPrivate ()
{
}

void HbLineEditPrivate::init()
{
    Q_Q(HbLineEdit);

    QObject::connect(q, SIGNAL(selectionChanged(const QTextCursor &, const QTextCursor &)),
                     q, SIGNAL(selectionChanged()));
    QObject::connect(doc, SIGNAL(contentsChange(int,int,int)), q, SLOT(_q_textChange(int,int,int)));
    QObject::connect(q, SIGNAL(contentsChanged()), q, SLOT(_q_textChanged()));

    q->setScrollable(true);
    scrollArea->setHorizontalScrollBarPolicy(HbScrollArea::ScrollBarAlwaysOff); 
    defaultWrapMode = doc->defaultTextOption().wrapMode(); // cannot be changed.
    q->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
    setBackgroundItem(HbStylePrivate::P_LineEdit_frame_normal);
    q->setFocusHighlight((HbStyle::Primitive)HbStylePrivate::P_LineEdit_frame_highlight,HbWidget::FocusHighlightActive);
    updateWrappingMode();

    Q_ASSERT(scrollArea);
    scrollArea->installEventFilter(q); // needed for resize event processing

    // createCustomAutoCompPopup();
}

void HbLineEditPrivate::createCustomAutoCompPopup()
{
    Q_Q(HbLineEdit);

    mCustomAutoCompPopup = new HbPopup(q);
    mCustomAutoCompPopup->setVisible(false);
    mCustomAutoCompPopup->setFlag(QGraphicsItem::ItemIsPanel, true);
    mCustomAutoCompPopup->setActive(false);
    mCustomAutoCompPopup->setFocusPolicy(Qt::NoFocus);
    mCustomAutoCompPopup->setBackgroundFaded(false);
    mCustomAutoCompPopup->setDismissPolicy(HbPopup::NoDismiss);
    mCustomAutoCompPopup->setTimeout(HbPopup::NoTimeout);
    HbStyle::setItemName(mCustomAutoCompPopup, QString("autoCompletePopup"));
}

void HbLineEditPrivate::updatePaletteFromTheme()
{
    HbAbstractEditPrivate::updatePaletteFromTheme();
    Q_Q(HbLineEdit);

    QColor textColor = HbColorScheme::color("qtc_lineedit_normal");
    QColor selectedColor = HbColorScheme::color("qtc_lineedit_selected");
    QColor selectedBackground = HbColorScheme::color("qtc_lineedit_marker_normal");
    QColor hintText = HbColorScheme::color("qtc_lineedit_hint_normal");
    QPalette pal = q->palette();

    if (textColor.isValid()) {
        pal.setColor(QPalette::Text, textColor);
    }

    if (selectedColor.isValid()) {
        pal.setColor(QPalette::HighlightedText, selectedColor);
    }

    if (selectedBackground.isValid()) {
        pal.setColor(QPalette::Highlight, selectedBackground);
    }

    if (hintText.isValid()) {
        pal.setColor(QPalette::NoRole, hintText);
    }

    q->setPalette(pal);
}
bool HbLineEditPrivate::forwardKeyEvent (QKeyEvent *event)
{
    Q_Q(HbLineEdit);

    bool result = true;

    // do not allow key events if at maximum length
    if (maxLength != -1 && q->document()->begin().length() - 1 >= maxLength) {
        if (event->key() != Qt::Key_Left
                && event->key() != Qt::Key_Right
                && event->key() != Qt::Key_Up
                && event->key() != Qt::Key_Down
                && event->key() != Qt::Key_Backspace
                && event->key() != Qt::Key_Delete) {
            result = false;
        }
    }

    // do not allow line feed
    if (event->key() == Qt::Key_Enter || event->key() == Qt::Key_Return) {
        result = false;
    }

    if (result) {
        if (isPasswordMode()) {
            result = false;
        }
    }

    return result;
}

void HbLineEditPrivate::validateMaxLength()
{
    Q_Q(HbLineEdit);
    QString text(q->text());

    if (maxLength >= 0 && (text.length()) > maxLength) {
        text.resize(maxLength);
        QTextCursor cursor(doc);
        cursor.setPosition(maxLength);
        cursor.movePosition(QTextCursor::EndOfBlock, QTextCursor::KeepAnchor);
        cursor.removeSelectedText();
        if(isPasswordMode()) {
            passwordText.resize(maxLength);
        }
    }
}

void HbLineEditPrivate::_q_textChanged()
{
    Q_Q(HbLineEdit);

    if(emitTextChanged && !isPasswordMode()) {
        emit q->textChanged(q->text());
    }

    if(adjustFontSizeToFitHeight) {
        readjustStretchFont();
    }

    if (doc->isEmpty()) {
        hideCustomAutoCompPopup();
    } else {
        showCustomAutoCompPopup();
    }
}

void HbLineEditPrivate::_q_textChange(int position, int charsRemoved,int charsAdded)
{
    Q_UNUSED(position)
    Q_UNUSED(charsRemoved)

    if(charsAdded == 0) return;

    emitTextChanged = false;
    validateMaxLength();
    emitTextChanged = true;
}

void HbLineEditPrivate::updateEditingSize()
{
    Q_Q(HbLineEdit);
    if (scrollArea) {
        if(!adjustFontSizeToFitHeight) {
            const int fontHeight = QFontMetrics(q->font()).height();
            scrollArea->setMinimumHeight(fontHeight * minimumRows + 2 * doc->documentMargin());
            if (maximumRows == 1) {
                scrollArea->setPreferredHeight(fontHeight);
                scrollArea->setScrollDirections(Qt::Horizontal);
            } else {
                scrollArea->setPreferredHeight(doc->documentLayout()->documentSize().height());
                scrollArea->setScrollDirections(Qt::Vertical);
            }
            scrollArea->setMaximumHeight(fontHeight * maximumRows + 2 * doc->documentMargin());
        } else {
            qreal prefLineHeight = q->fontSpec().textHeight();
            if (prefLineHeight<0) {
                QFontMetricsF metrics(q->font());
                prefLineHeight = metrics.lineSpacing();
            }

            const qreal marginBonus = 2*doc->documentMargin();
            scrollArea->setMinimumHeight(qMax(minimumRows,1)*KMinimumLineHeight
                                         +marginBonus);

            // minimumRows is used here because it is expected that
            // text will be short in most use cases
            scrollArea->setPreferredHeight(prefLineHeight*minimumRows+marginBonus);

            scrollArea->setMaximumHeight(QWIDGETSIZE_MAX);
        }
    }
}

bool HbLineEditPrivate::canCopy() const
{
    bool retVal = HbAbstractEditPrivate::canCopy();
    if(echoMode != HbLineEdit::Normal) {
        retVal = false;
    }
    return retVal;
}

bool HbLineEditPrivate::isCursorVisible() const
{
    return HbAbstractEditPrivate::isCursorVisible() &&
            (echoMode == HbLineEdit::Normal || echoMode == HbLineEdit::PasswordEchoOnEdit);
}

void HbLineEditPrivate::prepDocForPaste()
{
    if(echoMode != HbLineEdit::Normal) {
        doc->clear();
        passwordText.clear();
    }
}

void HbLineEditPrivate::setCapitalization ( QFont::Capitalization caps )
{    
    QTextCursor newCursor = QTextCursor(doc);
    newCursor.select(QTextCursor::Document);
    QTextCharFormat textFormat = newCursor.charFormat();
    textFormat.setFontCapitalization(caps);
    newCursor.setCharFormat(textFormat);

    //Merge capitalization to cursor's charformat
    cursor.mergeCharFormat(textFormat);
}

QFont::Capitalization HbLineEditPrivate::capitalization() const
{
    return cursor.charFormat().fontCapitalization();
}

bool HbLineEditPrivate::isPasswordMode() const
{
    return ((echoMode == HbLineEdit::Password) ||
           (echoMode == HbLineEdit::PasswordEchoOnEdit) ||
           (echoMode == HbLineEdit::NoEcho));
}

void HbLineEditPrivate::setEchoMode(HbLineEdit::EchoMode echoMode)
{
    Q_Q(HbLineEdit);

    if(this->echoMode == HbLineEdit::Normal) {
        passwordText = q->toPlainText();
    }

    this->echoMode = echoMode;
    q->setPlainText(echoString(passwordText));
    if(isPasswordMode()){
        q->setCursorPosition(passwordText.length());
    }
}

QString HbLineEditPrivate::passwordString(const QString &text) const
{
    Q_Q(const HbLineEdit);
    int passChar = q->QGraphicsWidget::style()->styleHint(QStyle::SH_LineEdit_PasswordCharacter);
    return QString(text.length(), passChar);
}

QString HbLineEditPrivate::echoString(const QString &text)
{

    QString retText(text);

    if(echoMode == HbLineEdit::Password || (echoMode == HbLineEdit::PasswordEchoOnEdit && !hasInputFocus())) {
        retText = passwordString(text);
    } else if (echoMode == HbLineEdit::NoEcho) {
        retText.clear();
    } // else Normal

    return retText;
}

void HbLineEditPrivate::updateWrappingMode()
{
    QTextOption option = doc->defaultTextOption();
    if (maximumRows == 1) {
        option.setWrapMode(QTextOption::NoWrap);
    } else {
        option.setWrapMode(defaultWrapMode);
    }
    doc->setDefaultTextOption(option);
}

int HbLineEditPrivate::linesToBeVisible() const
{
    int docLineCount = 0;
    QTextBlock block = doc->firstBlock();
    for (int i=0; i<doc->blockCount(); ++i) {
        docLineCount += block.layout()->lineCount();
        block.next();
    }
    return qBound(minimumRows, docLineCount, maximumRows);
}

void HbLineEditPrivate::readjustStretchFont()
{
    int desiredLineCount=linesToBeVisible();

    if (desiredLineCount==stretchedToLineCount) {
        return;
    }

    setVisibleRows(desiredLineCount);

    if (desiredLineCount<stretchedToLineCount
        && desiredLineCount<linesToBeVisible()) {
        do {
            ++desiredLineCount;
            setVisibleRows(desiredLineCount);
        } while(desiredLineCount<linesToBeVisible());
    }
    stretchedToLineCount = desiredLineCount;
}

void HbLineEditPrivate::onResizeFontChange()
{
    if (!adjustFontSizeToFitHeight) {
        return;
    }

    const int maxRows = (maximumRows>0)?maximumRows:DefaulMaximumVisibleLines;
    for (int i=minimumRows; i<=maxRows; ++i) {
        setVisibleRows(i);
        if(linesToBeVisible()<=i) {
            stretchedToLineCount = i;
            break;
        }
    }
}

void HbLineEditPrivate::setVisibleRows(int rowCount)
{
    Q_Q(HbLineEdit);

    qreal singleLineHeight = scrollArea->size().height()
                             -2*doc->documentMargin();
    if(singleLineHeight<=0) {
        return;
    }
    singleLineHeight/=rowCount;

    HbFontSpec fSpec(q->fontSpec());
    if (fSpec.role()!=HbFontSpec::Undefined) {
        fSpec.setTextHeight(singleLineHeight);
        canvas->setFontSpec(fSpec);
    } else {
        QFont deltaFont;
        deltaFont.setPixelSize(static_cast<int>(singleLineHeight+0.5));
        QFont oldFont = q->font();
        QFontMetricsF metrics(deltaFont.resolve(oldFont));
        //recalculate pixels size to line height
        singleLineHeight = singleLineHeight * singleLineHeight / metrics.lineSpacing();
        deltaFont.setPixelSize(static_cast<int>(singleLineHeight));
        canvas->setFont(deltaFont);
    }
}

void HbLineEditPrivate::showCustomAutoCompPopup()
{
    if (mCustomAutoCompContent) {
        Q_ASSERT(mCustomAutoCompPopup);

        if (!mCustomAutoCompPopup->isVisible() && !doc->isEmpty()) {
            mCustomAutoCompPopup->show();
        }
    }
}

void HbLineEditPrivate::hideCustomAutoCompPopup()
{
    if (mCustomAutoCompContent) {
        Q_ASSERT(mCustomAutoCompPopup);

        if (mCustomAutoCompPopup->isVisible()) {
            mCustomAutoCompPopup->hide();
        }
    }
}

void HbLineEditPrivate::editingFinished()
{
    Q_Q(HbLineEdit);

    if(q->echoMode() == HbLineEdit::PasswordEchoOnEdit) {
        q->setPlainText(passwordString(passwordText));
     }
    emit q->editingFinished();
}

#include "moc_hblineedit.cpp"