diff -r 594d59766373 -r 7d48bed6ce0c telutils/dialpad/src/dialpadkeypad.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/telutils/dialpad/src/dialpadkeypad.cpp Tue Aug 31 15:45:17 2010 +0300 @@ -0,0 +1,577 @@ +/*! +* Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +* All rights reserved. +* This component and the accompanying materials are made available +* under the terms of "Eclipse Public License v1.0" +* which accompanies this distribution, and is available +* at the URL "http://www.eclipse.org/legal/epl-v10.html". +* +* Initial Contributors: +* Nokia Corporation - initial contribution. +* +* Contributors: +* +* Description: Dialpad keypad +* +*/ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "dialpadnumericbutton.h" +#include "dialpadkeypad.h" +#include "dialpadbutton.h" +#include "dialpadinputfield.h" + +static const int DialpadRowCount = 4; +static const int DialpadColumnCount = 3; +static const QLatin1String handsetIcon("qtg_mono_call"); +static const QLatin1String vmbxIcon("qtg_mono_voice_mailbox"); +// layout values in units +static const qreal DialpadPrimaryTextSize = 5.5; +static const qreal DialpadSecondaryTextSize = 4.0; +static const qreal DialpadIconSize = 4.0; +static const qreal DialpadPrimaryTextLeftMargin = 1.5; +static const qreal DialpadPrimarySecondaryMargin = 0.75; + +static const int DialpadKeyCodeTable[DialpadRowCount*DialpadColumnCount] = +{ + Qt::Key_1, Qt::Key_2, Qt::Key_3, + Qt::Key_4, Qt::Key_5, Qt::Key_6, + Qt::Key_7, Qt::Key_8, Qt::Key_9, + Qt::Key_Asterisk, Qt::Key_0, Qt::Key_NumberSign + // Qt::Key_Yes and Qt::Key_BackSpace are handled separately +}; + +DialpadKeypad::DialpadKeypad( + const HbMainWindow& mainWindow, + DialpadInputField& inputField, + QGraphicsItem* parent) : + HbInputButtonGroup(parent), + mMainWindow(mainWindow), + mInputField(inputField), + mMaxPrimaryLineWidth(0) +{ + setObjectName("keypad"); + + // create clicked signal mapper + mKeyClickedSignalMapper = new QSignalMapper(this); + connect(mKeyClickedSignalMapper,SIGNAL(mapped(int)), + SLOT(handleKeyClicked(int))); + + // connect backspace signals + connect(&mInputField.backspaceButton(),SIGNAL(clicked()), + mKeyClickedSignalMapper,SLOT(map())); + mKeyClickedSignalMapper->setMapping(&mInputField.backspaceButton(), + Qt::Key_Backspace); + + // create keypad + setGridSize(QSize(DialpadColumnCount, DialpadRowCount)); + setButtonBorderSize(0); + + QList buttons; + + for (int i = 0; i < DialpadRowCount * DialpadColumnCount; ++i) { + DialpadNumericButton *item = new DialpadNumericButton( + DialpadKeyCodeTable[i], + QPoint(i % DialpadColumnCount, i / DialpadColumnCount)); + buttons.append(item); + + item->setType(HbInputButton::ButtonTypeNormal); + } + + setButtons(buttons); + + // connect keypad signals + QObject::connect(this, SIGNAL(buttonPressed(const QKeyEvent&)), + this, SLOT(sendKeyPressEvent(const QKeyEvent&))); + QObject::connect(this, SIGNAL(buttonReleased(const QKeyEvent&)), + this, SLOT(sendKeyReleaseEvent(const QKeyEvent&))); + QObject::connect(this, SIGNAL(buttonLongPressed(const QKeyEvent&)), + this, SLOT(sendLongPressEvent(const QKeyEvent&))); + QObject::connect(this, SIGNAL(pressedButtonChanged(const QKeyEvent&, + const QKeyEvent&)), + this, SLOT(handleKeyChangeEvent(const QKeyEvent&, + const QKeyEvent&))); + + // create call button (parent layouts this) + mCallButton = new DialpadButton(parent); + mCallButton->setButtonType(DialpadButton::CallButton); + mCallButton->setIcon(HbIcon(handsetIcon)); + QString buttonName; + buttonName.setNum(Qt::Key_Yes); + mCallButton->setObjectName(buttonName); + connect(mCallButton,SIGNAL(clicked()), + mKeyClickedSignalMapper,SLOT(map())); + connect(mCallButton,SIGNAL(longPress(QPointF)), + mKeyClickedSignalMapper,SLOT(map())); + mKeyClickedSignalMapper->setMapping(mCallButton, + Qt::Key_Yes); + + // set button texts + setButtonTexts(); + // set button icons + button(0)->setIcon(HbIcon(vmbxIcon)); + + // update button texts when input language is changed + connect(HbInputSettingProxy::instance(), + SIGNAL(globalInputLanguageChanged(HbInputLanguage)), + this,SLOT(setButtonTexts())); + + updateColorArray(); + + mUnit = HbDeviceProfile::profile(this).unitValue(); +} + +DialpadKeypad::~DialpadKeypad() +{ +} + +void DialpadKeypad::setButtonTexts() +{ + HbInputLanguage inputLanguage = + HbInputSettingProxy::instance()->globalInputLanguage(); + const HbKeymap *keymap = + HbKeymapFactory::instance()->keymap(inputLanguage.language()); + + mGeneratedChar.clear(); + + if (keymap) { + int buttonCount = (DialpadRowCount*DialpadColumnCount); + for (int i = 0; i < buttonCount; i++) { + int keyCode = DialpadKeyCodeTable[i]; + + if (keyCode == Qt::Key_Asterisk) { + // asterisk is not localized + QChar asterisk('*'); + button(i)->setText(asterisk); + button(i)->setSecondaryText(QLatin1String("+")); + mGeneratedChar.insert(Qt::Key_Asterisk, asterisk); + continue; + } + + if (keyCode == Qt::Key_NumberSign) { + // number sign is not localized + QChar numberSign('#'); + button(i)->setText(numberSign); + mGeneratedChar.insert(Qt::Key_NumberSign, numberSign); + continue; + } + + int index = i; + if (keyCode==Qt::Key_0) { + index = i-1; + } + + const HbMappedKey *key = + keymap->keyForIndex(HbKeyboardVirtual12Key, index); + + if (key) { + QChar numberChar = + HbInputUtils::findFirstNumberCharacterBoundToKey( + key, + inputLanguage.language(), + HbInputUtils::inputDigitType(inputLanguage.language())); + + // button text + button(i)->setText(numberChar); + mGeneratedChar.insert(keyCode,numberChar); + + // additional text (letters) + int numberOfCharacters; + if (keyCode==Qt::Key_7 || keyCode == Qt::Key_9) { + numberOfCharacters = 4; + } else if (keyCode==Qt::Key_0||keyCode==Qt::Key_1) { + numberOfCharacters = 0; + } else { + numberOfCharacters = 3; + } + + QString characters = key->characters(HbModifierNone); + + if (numberOfCharacters!=0 && keyCode!=Qt::Key_1) { + button(i)->setSecondaryText(characters.left(numberOfCharacters)); + } + } + } + } +} + +void DialpadKeypad::handleKeyClicked(int key) +{ + // concerns only yes and backspace keys + postKeyEvent(QEvent::KeyPress, key); + postKeyEvent(QEvent::KeyRelease, key); +} + +void DialpadKeypad::postKeyEvent(QEvent::Type type, int key) +{ + // send simulated key to application + QKeyEvent *keyEvent = new QKeyEvent(type, key, Qt::NoModifier); + HbApplication::postEvent(const_cast(&mMainWindow),keyEvent); +} + +void DialpadKeypad::sendKeyEventToEditor(QEvent::Type type, int key) +{ + // send key event to editor + QKeyEvent keyEvent(type, key, Qt::NoModifier, mGeneratedChar.value(key)); + HbApplication::sendEvent(&mInputField.editor(), &keyEvent); +} + +void DialpadKeypad::sendKeyPressEvent(const QKeyEvent& event) +{ + updateButtonLabels(); + mPressedNumericKey = event.key(); + postKeyEvent(QEvent::KeyPress, event.key()); +} + +void DialpadKeypad::sendKeyReleaseEvent(const QKeyEvent& event) +{ + updateButtonLabels(); + + if (mPressedNumericKey) { + // short press, update editor here + sendKeyEventToEditor(QEvent::KeyPress, event.key()); + } + + postKeyEvent(QEvent::KeyRelease, event.key()); +} + +void DialpadKeypad::sendLongPressEvent(const QKeyEvent& event) +{ + sendKeyEventToEditor(QEvent::KeyPress, event.key()); + mPressedNumericKey = 0; +} + +void DialpadKeypad::handleKeyChangeEvent( + const QKeyEvent& releaseEvent, + const QKeyEvent& pressEvent) +{ + Q_UNUSED(pressEvent) + + postKeyEvent(QEvent::KeyRelease, releaseEvent.key()); + cancelButtonPress(); +} + +void DialpadKeypad::setCallButtonEnabled(bool enabled) +{ + mCallButton->setEnabled(enabled); +} + +void DialpadKeypad::resetButtons() +{ + cancelButtonPress(); + mCallButton->setDown(false); +} + +DialpadButton& DialpadKeypad::callButton() const +{ + return *mCallButton; +} + +DialpadNumericButton* DialpadKeypad::button(int i) const +{ + return static_cast(HbInputButtonGroup::button(i)); +} + +void DialpadKeypad::updateButtonLabels() +{ + // update numeric buttons according to button state (pressed/released) + updateIconColor(); + updateTextLayouts(rect().size()); +} + +void DialpadKeypad::paint( + QPainter* painter, + const QStyleOptionGraphicsItem* option, + QWidget* widget) +{ + Q_UNUSED(option); + Q_UNUSED(widget); + + // Paints empty buttons + HbInputButtonGroup::paint(painter,option,widget); + + qreal cellWidth = boundingRect().width() / gridSize().width(); + qreal cellHeight = boundingRect().height() / gridSize().height(); + + // Draw icons + for (int i = 0; i < DialpadRowCount * DialpadColumnCount; i++) { + DialpadNumericButton *item = button(i); + + if (!item->icon().isNull()) { + // icon is centered to button + qreal x = (item->position().x() * cellWidth) + (cellWidth / 2) - + ((DialpadIconSize * mUnit) / 2); + qreal y = (item->position().y() * cellHeight) + (cellHeight / 2) - + ((DialpadIconSize * mUnit) / 2); + + qreal width = DialpadIconSize * mUnit; + qreal height = DialpadIconSize * mUnit; + + Qt::Alignment alignment = + static_cast(Qt::AlignVCenter | Qt::AlignHCenter); + item->icon().paint(painter, + QRectF(x,y,width,height), + Qt::KeepAspectRatio, + alignment); + } + } + + // Draw texts + QPen origPen = painter->pen(); + for (int i = 0; i < mTextLayouts.count(); ++i) { + if (i==SecondaryText) { + // dimmed in normal state + painter->setPen(mColors.at(Pressed+1)); + } else { + // otherwise normal or pressed color + painter->setPen(mColors.at(i/TextTypeCount)); + } + mTextLayouts.at(i)->draw(painter, QPointF(0, 0)); + } + painter->setPen(origPen); +} + +void DialpadKeypad::updateColorArray() +{ + mColors.clear(); + + QColor normalColor = HbColorScheme::color("qtc_input_button_normal"); + mColors.insert(Normal, normalColor); + + QColor pressedColor = HbColorScheme::color("qtc_input_button_pressed"); + mColors.insert(Pressed, pressedColor); + + // this is used for alphabets shown dimmed, use alpha until exact color + // is specified + QColor disabledColor = HbColorScheme::color("qtc_input_button_normal"); + disabledColor.setAlpha(128); + mColors.insert(Pressed+1, disabledColor); +} + +void DialpadKeypad::updateIconColor() +{ + for (int i = 0; i < (DialpadRowCount * DialpadColumnCount); i++) { + DialpadNumericButton *item = button(i); + + if (item->state()==HbInputButton::ButtonStatePressed) { + item->icon().setColor(mColors.at(Pressed)); + } else { + item->icon().setColor(mColors.at(Normal)); + } + } +} + +void DialpadKeypad::cancelButtonPress() +{ + HbInputButtonGroup::cancelButtonPress(); + updateButtonLabels(); +} + +void DialpadKeypad::setGeometry(const QRectF &rect) +{ + HbInputButtonGroup::setGeometry(rect); + updateTextLayouts(rect.size()); +} + +void DialpadKeypad::changeEvent(QEvent *event) +{ + HbInputButtonGroup::changeEvent(event); + + if (event->type() == HbEvent::ThemeChanged) { + updateColorArray(); + updateIconColor(); + } +} + +void DialpadKeypad::updateTextLayouts(const QSizeF &size) +{ + if (!size.width() && !size.height()) { + return; + } + + // get normal and pressed state texts + QList textContent; + resolveTextContent(textContent); + + // layout the texts + createTextLayouts(size, textContent); +} + +void DialpadKeypad::resolveTextContent(QList &content) +{ + QString normalState; + QString normalStateSecondary; + QString pressedState; + QString pressedStateSecondary; + + for (int i = 0; i < (DialpadRowCount*DialpadColumnCount); i++) { + DialpadNumericButton *item = button(i); + if (item->state()==HbInputButton::ButtonStatePressed) { + if (item->text().length()) { + pressedState.append(item->text()); + pressedState.append(QChar(QChar::LineSeparator)); + } + + if (item->secondaryText().length()) { + pressedStateSecondary.append(item->secondaryText()); + pressedStateSecondary.append(QChar(QChar::LineSeparator)); + } + } else { // ButtonStateNormal + if (item->text().length()) { + normalState.append(item->text()); + normalState.append(QChar(QChar::LineSeparator)); + } + + if (item->secondaryText().length()) { + normalStateSecondary.append(item->secondaryText()); + normalStateSecondary.append(QChar(QChar::LineSeparator)); + } + } + } + + content.insert(PrimaryText, normalState); + content.insert(SecondaryText, normalStateSecondary); + content.insert(TextTypeCount + Pressed, pressedState); + content.insert(StateCount + SecondaryText, pressedStateSecondary); +} + +void DialpadKeypad::createTextLayouts( + const QSizeF &size, const QList &content) +{ + // clear old layouts + qDeleteAll(mTextLayouts); + mTextLayouts.clear(); + + if (content.count()==2) { + // line width is measured only when all buttons are in normal state + mMaxPrimaryLineWidth = 0; + } + + QFont primaryfFont = HbFontSpec(HbFontSpec::Primary).font(); + primaryfFont.setPixelSize(DialpadPrimaryTextSize * mUnit); + + QFont secondaryFont = HbFontSpec(HbFontSpec::Secondary).font(); + secondaryFont.setPixelSize(DialpadSecondaryTextSize * mUnit); + + for (int i=0; i < (StateCount*TextTypeCount); i++ ) { + QString text = content.at(i); + + if (!text.isNull()) { + QTextLayout* textLayout; + int type; + + if (i%TextTypeCount) { + textLayout = new QTextLayout(text,secondaryFont); + type = SecondaryText; + } else { + textLayout = new QTextLayout(text,primaryfFont); + type = PrimaryText; + } + + mTextLayouts.append(textLayout); + + textLayout->beginLayout(); + + int state = (i>=TextTypeCount) ? Pressed : Normal; + + layoutTextLines(size,*textLayout,state,type); + + textLayout->endLayout(); + + textLayout->setCacheEnabled(true); + } + } +} + +void DialpadKeypad::layoutTextLines( + const QSizeF &size, + QTextLayout &textLayout, + int state, + int type) +{ + QFontMetricsF fontMetrics(textLayout.font()); + qreal textHeight = fontMetrics.height(); + + qreal cellWidth = size.width() / gridSize().width(); + qreal cellHeight = size.height() / gridSize().height(); + qreal maxLineWidth = 0; + + for (int j = 0; j < (DialpadRowCount*DialpadColumnCount); j++) { + DialpadNumericButton *item = button(j); + + if ((type==PrimaryText && item->text().isNull()) || + (type==SecondaryText && item->secondaryText().isNull())) { + continue; // no text for this button -> next button + } + + if ( ( state==Normal && + item->state()==HbInputButton::ButtonStateReleased ) || + ( state==Pressed && + item->state()==HbInputButton::ButtonStatePressed ) ) { + + QTextLine line = textLayout.createLine(); + + qreal textPositionX = 0; + qreal textPositionY = 0; + + if (line.isValid()) { + line.setNumColumns(item->text().length()); + // layout text line + if (type==SecondaryText) { + if (j==9) { + // + is centered to button + qreal lineWidth = fontMetrics.width(item->text()); + textPositionX = (item->position().x() * cellWidth) + + (cellWidth / 2) - + (lineWidth / 2); + textPositionY = (item->position().y() + + (0.5 * item->size().height())) * + cellHeight - (0.5 * textHeight); + + } else { + textPositionX = (item->position().x() * cellWidth) + + (DialpadPrimaryTextLeftMargin * mUnit) + + mMaxPrimaryLineWidth + + (DialpadPrimarySecondaryMargin * mUnit) + + buttonBorderSize(); + textPositionY = (item->position().y() + + (0.5 * item->size().height())) * + cellHeight - (0.5 * textHeight); + } + } else { + textPositionX = (item->position().x() * cellWidth) + + (DialpadPrimaryTextLeftMargin * mUnit) + + buttonBorderSize(); + textPositionY = (item->position().y() + + (0.5 * item->size().height())) * + cellHeight - (0.5 * textHeight); + + // store line width, for drawing secondary text + qreal lineWidth = fontMetrics.width(item->text()); + if (mMaxPrimaryLineWidth == 0 && (j>0 && j<10) && + lineWidth > maxLineWidth) { + maxLineWidth = lineWidth; + } + } + } + + line.setPosition(QPointF(textPositionX, textPositionY)); + } + } + + mMaxPrimaryLineWidth = maxLineWidth; +}