src/hbwidgets/widgets/hbcombobox_p.cpp
changeset 0 16d8024aca5e
child 1 f7ac710697a9
--- /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 <hblistview.h>
+#include <hbabstractviewitem.h>
+#include <hbdeviceprofile.h>
+#include <hbtoucharea.h>
+#include <hbmainwindow.h>
+#include <hbview.h>
+#include <hbtextitem.h>
+#include <hbstyleoptioncombobox.h>
+
+#include <QSortFilterProxyModel>
+#include <QGraphicsScene>
+#include <QCompleter>
+#include <QItemSelectionModel>
+
+#ifdef HB_EFFECTS
+#include <hbeffect.h>
+#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<HbTouchArea*>(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<HbTouchArea*>(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<HbListViewItem*>(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<QIcon>( decoration ) );
+    }
+    return qvariant_cast<QIcon>( 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"
+
+