diff -r 000000000000 -r 16d8024aca5e src/hbwidgets/widgets/hbcombobox_p.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/hbwidgets/widgets/hbcombobox_p.cpp Mon Apr 19 14:02:13 2010 +0300 @@ -0,0 +1,607 @@ +/**************************************************************************** +** +** 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 "hbcombobox_p.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#ifdef HB_EFFECTS +#include +#include "hbeffectinternal_p.h" +#define HB_DROPD0WN_ITEM_TYPE "HB_DROPDOWN" +#endif + +HbComboBoxPrivate::HbComboBoxPrivate( ): + HbWidgetPrivate ( ), + mLineEdit ( 0 ), + mTextItem ( 0 ), + mButton ( 0 ), + mDropDown ( 0 ), + mModel ( 0 ), + mProxyModel ( 0 ), + mCompleter ( 0 ), + insertPolicy ( HbComboBox::InsertAtBottom ), + mBackgroundItem ( 0 ), + mButtonTouchAreaItem ( 0 ), + mIsDown ( false ), + mEditable ( false ), + mIsDorpdownCreated(false), + mIsDropwnToSceneAdded(false), + mHasDownEffect ( false ), + mHasUpEffect (false ), + mListItemHeight(-1) +{ +} + +HbComboBoxPrivate::~HbComboBoxPrivate( ) +{ + Q_Q(HbComboBox); + if( mButtonTouchAreaItem ) { + static_cast(mButtonTouchAreaItem)->removeEventFilter( q ); + } + if (!q->scene() || !q->scene()->property("destructed").isValid()) { + if( mDropDown ) { + delete mDropDown; + mDropDown = 0; + } + } +} + +void HbComboBoxPrivate::init( ) +{ + createPrimitives( ); +} + +void HbComboBoxPrivate::createPrimitives( ) +{ + Q_Q( HbComboBox ); + + mTextItem = q->style()->createPrimitive( HbStyle::P_ComboBox_text, q ); + HbStyle::setItemName( mTextItem, "combobox_labelfield" ); + + mBackgroundItem = q->style( )->createPrimitive( HbStyle::P_ComboBox_background, q ); + HbStyle::setItemName( mBackgroundItem, "text_background" ); + + mButton = q->style( )->createPrimitive( HbStyle::P_ComboBox_button, q ); + HbStyle::setItemName( mButton, "combobox_button" ); + + mButtonTouchAreaItem = q->style( )->createPrimitive( + HbStyle::P_ComboBoxButton_toucharea, q ); + static_cast(mButtonTouchAreaItem)->installEventFilter( q ); + q->setHandlesChildEvents(true); +} + +void HbComboBoxPrivate::touchAreaPressEvent( ) +{ + Q_Q( HbComboBox ); + if (q->count() > 0) { + HbWidgetFeedback::triggered(q, Hb::InstantPressed); + } + mIsDown = true; + q->updatePrimitives( ); + q->setProperty("state", "pressed"); +} + +void HbComboBoxPrivate::touchAreaReleaseEvent( ) +{ + Q_Q( HbComboBox ); + mIsDown = false; + touchAreaClicked(); + q->updatePrimitives( ); + if (q->count() > 0) { + HbWidgetFeedback::triggered(q, Hb::InstantReleased); + } + + q->setProperty("state", "normal"); +} + +void HbComboBoxPrivate::touchAreaClicked( ) +{ + Q_Q( HbComboBox ); + if ( mModel && mModel->rowCount( ) ) { + addDropDownToScene(); + if( !mDropDown->mList ) { + mDropDown->createList( ); + mDropDown->mList->setModel( mModel ); + q->connect( mDropDown->mList, SIGNAL( activated( QModelIndex ) ), q, + SLOT( _q_textChanged( QModelIndex ) ) ); + } + if ( mCurrentIndex.isValid( ) ) { + if( mDropDown->mList->model( ) != mModel ) { + mDropDown->mList->setModel( mModel ); + } + mDropDown->mList->scrollTo( mCurrentIndex, HbAbstractItemView::PositionAtTop ); + mDropDown->mList->setCurrentIndex( mCurrentIndex, QItemSelectionModel::Select ); + } else { + if( mDropDown->mList->model( ) != mModel ) { + mDropDown->mList->setModel( mModel ); + } + mDropDown->mList->scrollTo( mModel->index( 0, 0 ) ); + mDropDown->mList->setCurrentIndex(mModel->index( 0, 0 ), QItemSelectionModel::Select); + } + #ifdef HB_EFFECTS + HbEffect::start(mDropDown, HB_DROPD0WN_ITEM_TYPE, "appear"); + #endif + positionDropDown( ); + mDropDown->setVisible( true ); + } +} + +void HbComboBoxPrivate::vkbOpened( ) +{ + +} + +void HbComboBoxPrivate::vkbClosed() +{ + if( mDropDown->isVisible()) { + positionDropDown(); + } +} + +void HbComboBoxPrivate::showPopup( QAbstractItemModel* aModel, QModelIndex aIndex ) +{ + Q_UNUSED( aModel ); + Q_UNUSED( aIndex ); + Q_Q( HbComboBox ); + if ( aModel && aModel->rowCount( ) ) { + addDropDownToScene(); + if( !mDropDown->mList ) { + mDropDown->createList(); + q->connect( mDropDown->mList, SIGNAL( activated( QModelIndex ) ), q, + SLOT( _q_textChanged( QModelIndex ) ) ); + } + mDropDown->mList->setModel( aModel ); + if ( aIndex.isValid( ) ) { + mDropDown->mList->scrollTo( aIndex, HbAbstractItemView::PositionAtTop ); + mDropDown->mList->setCurrentIndex(mCurrentIndex, QItemSelectionModel::Select); + } else { + mDropDown->mList->scrollTo( aModel->index( 0, 0 ) ); + } + positionDropDown( ); + mDropDown->setVisible( true ); + } +} + +void HbComboBoxPrivate::createDropDown() +{ + if( !mIsDorpdownCreated ) { + mDropDown = new HbComboDropDown( this ); + mIsDorpdownCreated = true; + mDropDown->setVisible(false); + } +} + +void HbComboBoxPrivate::calculateListItemHeight() +{ + if( mListItemHeight == -1 ) { + QAbstractItemModel *model = mDropDown->mList->model( ); + if( mCurrentIndex.isValid( ) && mDropDown->mList->itemByIndex( mCurrentIndex ) ) { + mListItemHeight = mDropDown->mList->itemByIndex( mCurrentIndex )->geometry( ).height( ); + } else if( model->index( 0, 0 ).isValid() && mDropDown->mList->itemByIndex( model->index( 0, 0 ) ) ) { + mListItemHeight = mDropDown->mList->itemByIndex( model->index( 0, 0 ) )->geometry( ).height( ); + } else { + HbListViewItem *proto = mDropDown->mList->listItemPrototype(); + HbListViewItem *temp = static_cast(proto->createItem()); + mListItemHeight = temp->effectiveSizeHint(Qt::PreferredSize).height(); + delete temp; + temp = 0; + } + } +} + +void HbComboBoxPrivate::positionDropDown( ) +{ + Q_Q( HbComboBox ); + QRectF popupRect; + QRectF sceneRect( QPointF( ), HbDeviceProfile::profile( q ).logicalSize( ) ); + QPointF widgetPos = q->scenePos( ); + QAbstractItemModel *model = mDropDown->mList->model( ); + calculateListItemHeight(); + qreal totalHeightRequd = model->rowCount( ) * mListItemHeight; + qreal maxPopupHeight = 0.0; + if(q->mainWindow()->orientation() == Qt::Horizontal ) { + maxPopupHeight = 5 * mListItemHeight; + } else if(q->mainWindow()->orientation() == Qt::Vertical ) { + maxPopupHeight = 8 * mListItemHeight; + } + if ( totalHeightRequd < maxPopupHeight ) { + maxPopupHeight = totalHeightRequd; + } + QSizeF popupSize = QSizeF( q->rect( ).width( ), maxPopupHeight ); + QPointF popupPos; + if( !mDropDown->vkbOpened ) { + //position of drop down in both editable and non-editable combobox depends upon + //the available space above and below combobox + if( (widgetPos.y( ) + q->rect( ).height( ) + maxPopupHeight) < sceneRect.height( ) ) { + popupPos = QPointF( widgetPos.x(), widgetPos.y( )+ q->rect( ).height( ) ); + #ifdef HB_EFFECTS + if ( !mHasDownEffect ) { + mHasDownEffect = true; + mHasUpEffect = false; + // this is temporary until proper effect theming comes. + //this Effect will be shown when there is space in the view bottom. + HbEffectInternal::add( mDropDown, "combo_appear_down", "appear" ); + HbEffectInternal::add( mDropDown, "combo_disappear_downl", "disappear" ); + } + #endif + } else if( widgetPos.y() - maxPopupHeight > 0.0 ) { + popupPos = QPointF( widgetPos.x(), widgetPos.y()-maxPopupHeight ); + #ifdef HB_EFFECTS + if ( !mHasUpEffect ) { + // this is temporary until proper effect theming comes. + //this Effect will be shown when there is no space in the view bottom + mHasUpEffect = true; + mHasDownEffect = false; + HbEffectInternal::add( mDropDown, "combo_appear_up", "appear" ); + HbEffectInternal::add( mDropDown, "combo_disappear_up", "disappear" ); + } + #endif + } else { + qreal topScreenHeight = sceneRect.height( ) - maxPopupHeight; + if( topScreenHeight > sceneRect.height( ) - topScreenHeight ) { + popupPos = QPointF( widgetPos.x( ), 0.0 ); + #ifdef HB_EFFECTS + if ( !mHasDownEffect ) { + mHasDownEffect = true; + mHasUpEffect = false; + // this is temporary until proper effect theming comes. + //this Effect will be shown when there is more space in the view bottom. + HbEffectInternal::add( mDropDown, "combo_appear_down", "appear" ); + HbEffectInternal::add( mDropDown, "combo_disappear_down", "disappear" ); + } + #endif + } else { + popupPos = QPointF( widgetPos.x( ), sceneRect.height( ) - maxPopupHeight ); + #ifdef HB_EFFECTS + if ( !mHasUpEffect ) { + mHasUpEffect = true; + mHasDownEffect = false; + // this is temporary until proper effect theming comes. + //this Effect will be shown when there is more space in the view bottom. + HbEffectInternal::add( mDropDown, "combo_appear_up", "appear" ); + HbEffectInternal::add( mDropDown, "combo_disappear_up", "disappear" ); + } + #endif + } + } + } else { + // positioning drop down when vkb is positioned + // drop down will come on top/below of combo based upon which side has more space + // available + + HbEditorInterface editorInterface(q); + HbVkbHost *host = editorInterface.vkbHost(); + if ( host ) { + QSizeF keyBoardArea = host->keyboardArea(); + QSize screenSize = HbDeviceProfile::profile(q).logicalSize(); + + qreal heightDifference = screenSize.height() - keyBoardArea.height(); + qreal topSpace = widgetPos.y(); + qreal bottomSpace = heightDifference - topSpace - q->boundingRect().height(); + + if( topSpace > bottomSpace ) { + //display drop down at top + if( widgetPos.y() - maxPopupHeight > 0.0 ) { + popupPos = QPointF( widgetPos.x(), widgetPos.y() - maxPopupHeight ); + } else { + popupPos = QPointF( widgetPos.x(), 0.0 ); + popupSize.setHeight( topSpace ); + } + #ifdef HB_EFFECTS + if ( !mHasUpEffect ) { + mHasUpEffect = true; + mHasDownEffect = false; + // this is temporary until proper effect theming comes. + //this Effect will be shown when there is more space in the view bottom. + HbEffectInternal::add( mDropDown, "combo_appear_up", "appear" ); + HbEffectInternal::add( mDropDown, "combo_disappear_up", "disappear" ); + } + #endif + + } else { + //display drop down at bottom + popupPos = QPointF( widgetPos.x(), widgetPos.y( ) + q->rect( ).height( ) ); + if( bottomSpace < maxPopupHeight ) { + popupSize.setHeight( bottomSpace ); + } + #ifdef HB_EFFECTS + if ( !mHasDownEffect ) { + mHasDownEffect = true; + mHasUpEffect = false; + // this is temporary until proper effect theming comes. + //this Effect will be shown when there is more space in the view bottom. + HbEffectInternal::add( mDropDown, "combo_appear_down", "appear" ); + HbEffectInternal::add( mDropDown, "combo_disappear_down", "disappear" ); + } + #endif + } + } + } + mDropDown->setPreferredSize( popupSize ); + mDropDown->setMinimumSize( popupSize ); + mDropDown->setMaximumSize( popupSize ); + mDropDown->setPos(popupPos); + QGraphicsWidget* p = q; + while(p->parentWidget()) { + p = p->parentWidget(); + } + mDropDown->setZValue( p->zValue( ) + 1 ); +} + +void HbComboBoxPrivate::_q_textChanged( const QModelIndex & aIndex ) +{ + Q_Q( HbComboBox ); + QVariant data = mDropDown->mList->model( )->data( aIndex ); + mText = data.toString( ); + if( !mEditable ) { + if( mLineEdit ) { + mLineEdit->setText( mText ); + } else { + HbStyleOptionComboBox comboBoxOption; + q->initStyleOption(&comboBoxOption); + q->style()->updatePrimitive( mTextItem, HbStyle::P_ComboBox_text, &comboBoxOption); + } + mCurrentIndex = aIndex; + } else { + q->disconnect( mLineEdit, SIGNAL( textChanged ( QString ) ), q, + SLOT( _q_textChanged( QString ) ) ); + mLineEdit->setText( mText ); + mCurrentIndex = findData( mText ); + q->connect( mLineEdit, SIGNAL( textChanged ( QString ) ), q, + SLOT( _q_textChanged( QString ) ) ); + } + if ( mDropDown->isVisible( ) ) { + mDropDown->setVisible( false ); + } + currentIndexChanged( mCurrentIndex ); +} + +void HbComboBoxPrivate::_q_textCompleted( const QModelIndex & aIndex ) +{ + if( aIndex.isValid( ) ) { + showPopup( mCompleter->completionModel( ) ); + } +} + +void HbComboBoxPrivate::_q_textChanged( const QString & aString ) +{ + Q_Q(HbComboBox); + + if( !aString.isEmpty( ) ) { + if ( mCompleter ) { + mCompleter->setCompletionPrefix( aString ); + mCompleter->complete( ); + if( mCompleter->currentRow() == -1 ) + { + if (( mDropDown ) && ( mDropDown->isVisible() )) { + mDropDown->setVisible(false); + } + } + } + } else { + showPopup( mModel, mCurrentIndex); + } + emit q->editTextChanged( aString ); +} + +void HbComboBoxPrivate::setModel( QAbstractItemModel * model ) +{ + Q_Q ( HbComboBox ); + createDropDown( ); + if ( mDropDown->isVisible( ) ) { + mDropDown->setVisible( false ); + } + q->clear( ); + delete mModel; + mModel = model; + mModel->setParent( q ); + setCompletion( mEditable ); + if( mModel->rowCount( ) ) { + q->setCurrentIndex( 0 ); + } +} + +void HbComboBoxPrivate::setCompletion( bool completion ) +{ + Q_Q( HbComboBox ); + if ( completion ) { + if ( mCompleter ) { + mProxyModel->setSourceModel( mModel ); + mProxyModel->sort( Qt::AscendingOrder ); + mCompleter->setModel( mProxyModel ); + } else { + mProxyModel = new QSortFilterProxyModel( q ); + mProxyModel->setDynamicSortFilter( true ); + mProxyModel->setSortCaseSensitivity( Qt::CaseInsensitive ); + mProxyModel->setSourceModel( mModel ); + mProxyModel->sort( Qt::AscendingOrder ); + mCompleter = new QCompleter( mProxyModel, q ); + mCompleter->setCaseSensitivity( Qt::CaseInsensitive ); + mCompleter->setCompletionRole( Qt::DisplayRole ); + mCompleter->setCompletionMode( QCompleter::InlineCompletion ); + q->connect( mCompleter, SIGNAL( highlighted( QModelIndex ) ), q, + SLOT( _q_textCompleted( QModelIndex ) ) ); + } + } else { + if ( mCompleter ) { + delete mCompleter; + mCompleter = 0; + } + if ( mProxyModel ) { + delete mProxyModel; + mProxyModel = 0; + } + } +} + +void HbComboBoxPrivate::setEditable( bool editable ) +{ + Q_Q(HbComboBox); + if( mEditable == editable ) { + return; + } + mEditable = editable; + if( editable ) + { + if( mTextItem ) { + HbStyle::setItemName( mTextItem, "" ); + delete mTextItem; + mTextItem = 0; + mLineEdit = new HbCustomLineEdit( q, this ); + HbStyle::setItemName( mLineEdit, "combobox_labelfield" ); + mLineEdit->backgroundItem()->setVisible(false); + } + q->setHandlesChildEvents( false ); + mLineEdit->setReadOnly( false ); + mLineEdit->setCursorVisibility( Hb::TextCursorVisible ); + mLineEdit->setLongPressEnabled( ); + q->repolish( ); + q->connect( mLineEdit, SIGNAL( textChanged ( QString ) ), + q, SLOT( _q_textChanged( QString ) ) ); + setCompletion( true ); + } else { + q->disconnect( mLineEdit, SIGNAL( textChanged ( QString ) ), + q, SLOT( _q_textChanged( QString ) ) ); + q->setHandlesChildEvents( true ); + mLineEdit->setReadOnly( true ); + mLineEdit->setLongPressEnabled( false ); + setCompletion( false ); + mLineEdit->setCursorVisibility( Hb::TextCursorHidden ); + if( mModel && mModel->rowCount( ) ) { + QModelIndex mi = mModel->index( 0, 0 ); + if( mi.isValid( ) ) { + mCurrentIndex = QModelIndex( mi ); + mText = q->itemText( mCurrentIndex.row( ) ); + mLineEdit->setText( mText ); + } + } + q->repolish( ); + } +} + +QString HbComboBoxPrivate::itemText( const QModelIndex &index ) const +{ + return mModel->data( index, itemRole( ) ).toString( ); +} + +QIcon HbComboBoxPrivate::itemIcon( const QModelIndex &index ) const +{ + QVariant decoration = mModel->data( index, Qt::DecorationRole ); + if ( decoration.type() == QVariant::Icon ) { + return QIcon( qvariant_cast( decoration ) ); + } + return qvariant_cast( decoration ); +} + +int HbComboBoxPrivate::itemRole( ) const +{ + return q_func( )->isEditable( ) ? Qt::EditRole : Qt::DisplayRole; +} + +void HbComboBoxPrivate::addDropDownToScene( ) +{ + Q_Q( HbComboBox ); + if( !mIsDropwnToSceneAdded ) { + if ( q->scene( ) ) { + q->scene( )->addItem( mDropDown ); + } + QGraphicsScene *scene1 = mDropDown->scene( ); + if( scene1 ) + { + scene1->installEventFilter( mDropDown ); + } + mIsDropwnToSceneAdded = true; + } +} +void HbComboBoxPrivate::setCurrentIndex( const QModelIndex &mi ) +{ + Q_Q( HbComboBox ); + bool indexChanged = ( mi != mCurrentIndex ); + if ( indexChanged ) { + mCurrentIndex = QModelIndex( mi ); + mText = q->itemText( mCurrentIndex.row( ) ); + if( mEditable ) { + q->disconnect( mLineEdit, SIGNAL( textChanged ( QString ) ), q, + SLOT( _q_textChanged( QString ) ) ); + mLineEdit->setText( mText ); + q->connect( mLineEdit, SIGNAL( textChanged ( QString ) ), + q, SLOT( _q_textChanged( QString ) ) ); + } else { + if( mLineEdit ) { + mLineEdit->setText( mText ); + } else { + HbStyleOptionComboBox comboBoxOption; + q->initStyleOption(&comboBoxOption); + q->style()->updatePrimitive( mTextItem, HbStyle::P_ComboBox_text, &comboBoxOption); + } + } + currentIndexChanged( mCurrentIndex ); + } +} + +void HbComboBoxPrivate::currentIndexChanged( const QModelIndex &index ) +{ + Q_Q( HbComboBox ); + emit q->currentIndexChanged( index.row( ) ); + emit q->currentIndexChanged( q->itemText ( mCurrentIndex.row( ) ) ); +} + +/* + Returns the index of the item containing the given \a data for the + given \a role; otherwise returns QModelIndex. + + The \a flags specify how the items in the combobox are searched. + */ +QModelIndex HbComboBoxPrivate::findData( const QVariant &data ) const +{ + QModelIndexList result; + if ( mModel ) { + QModelIndex start = mModel->index( 0, 0 ); + result = mModel->match( + start, Qt::DisplayRole, data, 1, Qt::MatchExactly|Qt::MatchCaseSensitive ); + } + if ( result.isEmpty( ) ) { + return QModelIndex( ); + } else { + return result.first( ); + } +} + +#include "moc_hbcombobox.cpp" + +