src/hbwidgets/editors/hblineedit.cpp
changeset 0 16d8024aca5e
child 1 f7ac710697a9
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/hbwidgets/editors/hblineedit.cpp	Mon Apr 19 14:02:13 2010 +0300
@@ -0,0 +1,648 @@
+/****************************************************************************
+**
+** 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.
+**
+****************************************************************************/
+
+#include "hblineedit.h"
+#include "hblineedit_p.h"
+
+#include "hbstyleoption.h"
+#include "hbscrollarea.h"
+#ifdef HB_TEXT_MEASUREMENT_UTILITY
+#include "hbtextmeasurementutility_p.h"
+#include "hbfeaturemanager_p.h"
+#endif //HB_TEXT_MEASUREMENT_UTILITY
+
+#include <QFontMetrics>
+#include <QGraphicsSceneMouseEvent>
+#include <QPainter>
+#include <QTextBlock>
+#include <QTextDocument>
+
+/*!
+ \class HbLineEdit
+ \brief HbLineEdit is a one line text editor widget
+ @alpha
+ @hbwidgets
+
+ A line edit allows the user to enter and edit a single paragraph of plain text.
+ The length of the text can be constrained to maxLength().
+ You can change the text with setText(). The text is retrieved with text().
+
+ The content in \ref HbLineEdit can be split visually into several lines. 
+ In other words the content of the editor can be seen in several lines but it 
+ does not contain any linefeeds (e.g. a number of email recipients separated with semicolon). 
+ In the API there is a group of methods to support this. 
+ 
+ If maxRows() is greater than minRows() the
+ editor is expandable and it changes its size based on the content between min and max values. 
+ Editor expandability can be checked with isExpandable().
+
+ When the text changes the textChanged() signal is emitted.
+ When the cursor is moved the cursorPositionChanged() signal is emitted.
+ When editing is finished, either because the line edit lost focus or Return/Enter is pressed
+ the editingFinished() signal is emitted.
+
+  Echo mode controls how input to editors is shown in the screen. It is by default \b Normal. 
+  Other possible modes are \b NoEcho, \b Password, and \b PasswordEchoOnEdit. 
+
+ The frame can be styled using "P_LineEdit_frame" primitive in HbStyle. Text item is not
+  replaceable but it can be styled.
+ */
+
+/*!
+ \fn void HbLineEdit::editingFinished()
+
+ This signal is emitted when the line edit loses focus or Return/Enter is pressed.
+ */
+
+/*!
+    \fn void HbLineEdit::textChanged(const QString &text)
+
+    This signal is emitted whenever the text changes. The text argument is the new text.
+    This signal is also emitted when the text is changed programmatically, for example,
+    by calling setText().
+
+    Note: this signal is not emitted when the editor is in any of password modes, \sa echoMode()
+*/
+
+/*!
+    \fn void void HbLineEdit::selectionChanged()
+    This signal is emitted when selection changes.
+*/
+
+/*!
+ Constructs an empty HbLineEdit with parent \a parent.
+ */
+HbLineEdit::HbLineEdit (QGraphicsItem *parent) :
+    HbAbstractEdit(*new HbLineEditPrivate, parent)
+{
+    Q_D(HbLineEdit);
+    d->q_ptr = this;
+
+    d->init();
+}
+
+/*!
+ Constructs a HbLineEdit with parent \a parent.
+ The text edit will display the text \a text.
+ The text is interpreted as plain text.
+ */
+HbLineEdit::HbLineEdit (const QString &text, QGraphicsItem *parent) :
+    HbAbstractEdit(*new HbLineEditPrivate, parent)
+{
+    Q_D(HbLineEdit);
+    d->q_ptr = this;
+
+    d->init();
+
+    if ( !text.isEmpty() ) {
+        setText(text);
+    }
+}
+
+/*!
+ Constructs a HbLineEdit with parent \a parent.
+ The text edit will use given private implementation \a dd.
+ */
+HbLineEdit::HbLineEdit (HbLineEditPrivate &dd, QGraphicsItem *parent) :
+    HbAbstractEdit(dd, parent)
+{
+    Q_D(HbLineEdit);
+    d->q_ptr = this;
+
+    d->init();
+}
+
+/*!
+ Destructor.
+ */
+HbLineEdit::~HbLineEdit ()
+{
+}
+
+/*!
+ Returns current maximum character length in edit.
+
+ \sa setMaxLength
+ */
+int HbLineEdit::maxLength () const
+{
+    Q_D(const HbLineEdit);
+    return d->maxLength;
+}
+
+/*!
+ Sets edit maximum character length to \a length.
+
+ If the text is too long, it is truncated at the limit.
+ If truncation occurs any selected text will be unselected.
+ By default, this property contains a value of -1 (unlimited).
+ */
+void HbLineEdit::setMaxLength (int length)
+{
+    Q_D(HbLineEdit);
+    if (length < -1) return; // not allowed.
+
+    if (d->maxLength != length) {
+        d->maxLength = length;
+        d->validateMaxLength();
+    }
+}
+
+/*!
+ Sets the minimum number of editor rows.
+
+ \sa minRows
+ */
+void HbLineEdit::setMinRows (int rows)
+{
+    Q_D(HbLineEdit);
+    d->setApiProtectionFlag(HbWidgetBasePrivate::AC_TextLinesMin, true);
+
+    if (rows > 0) {
+        d->minimumRows = rows;
+
+        if (d->minimumRows > d->maximumRows) {
+            d->maximumRows = d->minimumRows;
+        }
+        d->expandable = isExpandable();
+
+        d->updateWrappingMode();
+
+        updateGeometry();
+    }
+}
+
+/*!
+ Returns the minimum number of editor rows.
+
+ \sa setMinRows
+ */
+int HbLineEdit::minRows () const
+{
+    Q_D(const HbLineEdit);
+
+    return d->minimumRows;
+}
+
+/*!
+ .Tells whether the editor is expandable or fixed.
+ */
+bool HbLineEdit::isExpandable () const
+{
+    Q_D(const HbLineEdit);
+
+    return d->maximumRows > d->minimumRows;
+}
+
+/*!
+ Sets the maximum number of editor rows to \a rows.
+
+ \sa maxRows
+*/
+void HbLineEdit::setMaxRows (int rows)
+{
+    Q_D(HbLineEdit);
+    d->setApiProtectionFlag(HbWidgetBasePrivate::AC_TextLinesMax, true);
+
+    if (rows > 0) {
+        d->maximumRows = rows;
+
+        if (d->maximumRows  < d->minimumRows) {
+            d->minimumRows = d->maximumRows;
+        }
+
+        d->expandable = isExpandable();
+
+        d->updateWrappingMode();
+
+        updateGeometry();
+
+#ifdef HB_TEXT_MEASUREMENT_UTILITY
+        if ( HbFeatureManager::instance()->featureStatus( HbFeatureManager::TextMeasurement ) ) {
+            setProperty( HbTextMeasurementUtilityNameSpace::textMaxLines, d->maximumRows );
+        }
+#endif
+    }
+}
+
+/*!
+ Returns the maximum number of editor rows.
+
+ \sa setMaxRows
+ */
+int HbLineEdit::maxRows () const
+{
+    Q_D(const HbLineEdit);
+
+    return d->maximumRows;
+}
+
+/*!
+ \reimp
+ */
+void HbLineEdit::inputMethodEvent(QInputMethodEvent *e)
+{
+    Q_D(HbLineEdit);
+    
+    if(d->echoMode == HbLineEdit::PasswordEchoOnEdit && d->clearOnEdit) {
+        d->doc->clear();
+        d->passwordText.clear();
+        d->clearOnEdit = false;
+    }
+
+    if (e->commitString().contains("\n")) {
+        QString str = e->commitString();
+        str.replace("\n", " ");
+        e->setCommitString(str, e->replacementStart(), e->replacementLength());
+    }
+    
+    if (!e->commitString().isEmpty() || e->replacementLength()) {
+        // change the commit string and update the password string in case we are in password mode
+        if(d->echoMode == HbLineEdit::Password) {
+            d->passwordText.replace(cursorPosition()+e->replacementStart(), e->replacementLength(), e->commitString());
+            e->setCommitString(d->passwordString(e->commitString()), e->replacementStart(), e->replacementLength());
+        } else if(d->echoMode == HbLineEdit::NoEcho) {
+            d->passwordText.append(e->commitString());
+            e->setCommitString("");
+        } else if(d->echoMode == HbLineEdit::PasswordEchoOnEdit) {
+            // in PasswordEchoOnEdit we still can see what we are typing
+            d->passwordText.replace(cursorPosition()+e->replacementStart(), e->replacementLength(), e->commitString());
+        }
+    }
+    HbAbstractEdit::inputMethodEvent(e);
+}
+
+/*!
+ \reimp
+*/
+
+void HbLineEdit::keyPressEvent (QKeyEvent *event)
+{
+    Q_D(HbLineEdit);
+
+    if(d->echoMode == HbLineEdit::PasswordEchoOnEdit && d->clearOnEdit) {
+        d->doc->clear();
+        d->passwordText.clear();
+        d->clearOnEdit = false;
+    }
+
+    if(d->forwardKeyEvent(event)) {
+        HbAbstractEdit::keyPressEvent(event);
+    } else if (d->echoMode == HbLineEdit::Password || d->echoMode == HbLineEdit::NoEcho){
+        // Keep doc and passwordText in sync
+        bool update = false;
+        if (event->key() == Qt::Key_Backspace && !(event->modifiers() & ~Qt::ShiftModifier)) {
+            update = true;
+            d->passwordText.remove(d->passwordText.length()-1,1);
+        } else if (!event->text().isEmpty() && event->text().at(0).isPrint()) {
+            update = true;
+            d->passwordText.append(event->text());
+        }
+
+        if (update) {
+            setPlainText(((d->echoMode == HbLineEdit::Password)?d->passwordString(d->passwordText):""));
+        }
+        setCursorPosition(d->passwordText.length());
+    }
+    if (d->echoMode == HbLineEdit::PasswordEchoOnEdit) {
+        // Keep doc and passwordText in sync
+        d->passwordText = toPlainText();
+    }    
+}
+
+/*!
+ \reimp
+ */
+void HbLineEdit::keyReleaseEvent (QKeyEvent *event)
+{
+    Q_D(HbLineEdit);
+
+    if (event->key() == Qt::Key_Enter || event->key() == Qt::Key_Return) {
+        emit editingFinished();
+    }
+
+    if(d->forwardKeyEvent(event)) {
+        HbAbstractEdit::keyReleaseEvent(event);
+    }
+}
+
+/*!
+ \reimp
+ */
+int HbLineEdit::type () const
+{
+    return Type;
+}
+
+/*!
+    Returns the edit's contents as plain text. If smiley recognition is enabled
+    all the smiley images will be replaced by their textual representations.
+
+    \sa setText() HbAbstractEdit::setSmileysEnabled()
+*/
+QString HbLineEdit::text() const
+{
+    Q_D(const HbLineEdit);
+    if(d->isPasswordMode()) {
+        return d->passwordText;
+    }
+    return toPlainText();
+}
+
+/*!
+    This property holds the displayed text.
+    If echoMode is Normal this returns the same as text(); if EchoMode is Password or PasswordEchoOnEdit it
+    returns a string of asterisks text().length() characters long, e.g. "******";
+    if EchoMode is NoEcho returns an empty string, "".
+
+    By default, this property contains an empty string.
+
+    \sa text()
+*/
+QString HbLineEdit::displayText() const
+{
+    return toPlainText();
+}
+
+/*!
+  Returns true if there is any text section selected in the line edit.
+  Otherwise returns false.
+
+  \sa setSelection
+ */
+bool HbLineEdit::hasSelectedText() const
+{
+    return textCursor().hasSelection();
+}
+
+/*!
+ Returns the text that is selected in the line edit.
+ If no text section is selected, returns an empty string.
+ */
+QString HbLineEdit::selectedText() const
+{
+    return textCursor().selectedText();
+}
+
+/*!
+  Returns the index of the first selected character in the line
+  edit or -1 if no text is selected.
+ */
+int HbLineEdit::selectionStart () const
+{
+    if (hasSelectedText()) {
+        return textCursor().selectionStart();
+    } else {
+        return -1;
+    }
+}
+
+/*!
+ Selects text from position \a start and for \a length characters.
+ Negative lengths are allowed. Setting the selection fails if
+ start position is negative or out of the range of line edit text.
+
+ \sa hasSelectedText
+ */
+void HbLineEdit::setSelection(int start, int length)
+{
+    int textLength = text().count();
+    QString test = text();
+    Q_UNUSED(test)
+    if (start < 0 || start > textLength) {
+        return;
+    }
+
+    int end(start+length);
+    if (end > textLength) {
+        end = textLength;
+    } else if (end < 0) {
+        end = 0;
+    }
+
+    QTextCursor cursor = textCursor();
+    cursor.setPosition(start, QTextCursor::MoveAnchor);
+    cursor.setPosition(end, QTextCursor::KeepAnchor);
+    setTextCursor(cursor);
+
+}
+
+/*!
+    Sets cursor position to \a pos.
+
+    \sa cursorPosition
+ */
+void HbLineEdit::setCursorPosition (int pos)
+{
+    int length = document()->begin().length();
+
+    if (pos >= length) {
+        pos = length-1;
+    } else if (pos < 0) {
+        pos = 0;
+    }
+    HbAbstractEdit::setCursorPosition(pos);
+}
+
+
+/*!
+    \property HbLineEdit::echoMode
+    \brief the line edit's echo mode
+
+    The echo mode determines how the text entered in the line edit is
+    displayed (or echoed) to the user.
+
+    The most common setting is \l Normal, in which the text entered by the
+    user is displayed verbatim, but HbLineEdit also supports modes that allow
+    the entered text to be suppressed or obscured: these include \l NoEcho,
+    \l Password and \l PasswordEchoOnEdit.
+
+    The widget's display and the ability to copy or drag the text is
+    affected by this setting.
+
+    By default, this property is set to \l Normal.
+
+    \sa setEchoMode()
+*/
+
+HbLineEdit::EchoMode HbLineEdit::echoMode() const
+{
+    Q_D(const HbLineEdit);
+    return (EchoMode) d->echoMode;
+}
+
+void HbLineEdit::setEchoMode(EchoMode mode)
+{
+    Q_D(HbLineEdit);
+    if (mode == (EchoMode)d->echoMode)
+        return;
+    d->setEchoMode(mode);
+#if QT_VERSION >= 0x040600
+    Qt::InputMethodHints hints = inputMethodHints();
+    if(mode != Normal) {
+        hints |= Qt::ImhNoPredictiveText|Qt::ImhHiddenText;
+    } else {
+        hints &= ~Qt::ImhNoPredictiveText & ~Qt::ImhHiddenText;
+    }
+    setInputMethodHints(hints);
+#endif // QT_VERSION
+    // Clear the selection if any
+    d->cursor.clearSelection();
+    d->selectionChanged();
+    update();
+}
+
+
+/*!
+ Sets the current capitalization type of editor text.
+
+ \sa capitalization QTextCharFormat::fontCapitalization
+ */
+void HbLineEdit::setCapitalization ( QFont::Capitalization caps )
+{
+    Q_D(HbLineEdit);
+
+    d->setCapitalization(caps);
+}
+
+/*!
+ Returns the current capitalization type of editor text.
+
+ \sa setCapitalization QTextCharFormat::setFontCapitalization
+ */
+QFont::Capitalization HbLineEdit::capitalization () const
+{
+    Q_D(const HbLineEdit);
+    return d->capitalization();
+}
+
+
+/*!
+ Sets the text edit's text. The text is interpreted as plain text.
+ If smiley recognition is enabled the textual smiley patterns are replaced
+ with the corresponding image from the active smiley theme.
+
+ \sa text HbAbstractEdit::setSmileysEnabled()
+ */
+void HbLineEdit::setText(const QString &text)
+{
+    Q_D(HbLineEdit);
+
+    QString txt( text );
+
+#ifdef HB_TEXT_MEASUREMENT_UTILITY
+    if ( HbFeatureManager::instance()->featureStatus( HbFeatureManager::TextMeasurement ) ) {
+        if (text.endsWith(QChar(LOC_TEST_END))) {
+            int index = text.indexOf(QChar(LOC_TEST_START));
+            setProperty( HbTextMeasurementUtilityNameSpace::textIdPropertyName,  text.mid(index + 1, text.indexOf(QChar(LOC_TEST_END)) - index - 1) );
+            setProperty( HbTextMeasurementUtilityNameSpace::textMaxLines, d->maximumRows );
+            txt = text.left(index);
+        } else {
+            setProperty( HbTextMeasurementUtilityNameSpace::textIdPropertyName,  QVariant::Invalid );
+        }
+    }
+#endif //HB_TEXT_MEASUREMENT_UTILITY
+
+    if(d->isPasswordMode()) {
+        d->passwordText = txt;
+    }
+    setPlainText(d->echoString(txt));
+    moveCursor(QTextCursor::EndOfBlock);
+}
+
+/*!
+    \reimp
+ */
+void HbLineEdit::resizeEvent(QGraphicsSceneResizeEvent *event)
+{
+    HbAbstractEdit::resizeEvent(event);
+
+    document()->setTextWidth(primitive(HbStyle::P_Edit_text)->boundingRect().width());
+}
+
+/*!
+    \reimp
+ */
+bool HbLineEdit::canInsertFromMimeData(const QMimeData *source) const
+{
+    return source->hasText() && !source->text().isEmpty();
+}
+
+/*!
+    \reimp
+ */
+void HbLineEdit::insertFromMimeData(const QMimeData *source)
+{
+    Q_D(HbLineEdit);
+    if (!(d->interactionFlags & Qt::TextEditable) || !source)
+        return;
+
+    QString txt = source->text().replace(QString("\n"),QString(" "));
+
+    if (!txt.isNull()) {
+
+        if(d->isPasswordMode()) {
+            d->passwordText = txt;
+        }
+
+        QTextDocumentFragment fragment = QTextDocumentFragment::fromPlainText(d->echoString(txt));
+        d->cursor.insertFragment(fragment);
+    }
+
+    d->ensureCursorVisible();
+}
+
+/*!
+    \reimp
+ */
+void HbLineEdit::focusOutEvent(QFocusEvent * event)
+{
+    Q_D(HbLineEdit);
+    setBackgroundItem(HbStyle::P_LineEdit_frame_normal);
+
+    if(echoMode() == HbLineEdit::PasswordEchoOnEdit) {
+        setPlainText(d->passwordString(d->passwordText));
+    }
+
+    HbAbstractEdit::focusOutEvent(event);
+
+    emit editingFinished();
+}
+
+/*!
+    \reimp
+ */
+void HbLineEdit::focusInEvent(QFocusEvent * event)
+{
+    Q_D(HbLineEdit);
+    setBackgroundItem(HbStyle::P_LineEdit_frame_highlight);
+
+    if(echoMode() == HbLineEdit::PasswordEchoOnEdit) {
+        // we need to clear the editor when typing starts
+        d->clearOnEdit = true;
+    }
+
+    HbAbstractEdit::focusInEvent(event);
+}