/**********************************************************************
**
**
** Implementation of QComboView widget class
**
** Copyright (C) 1992-2000 Trolltech AS.  All rights reserved.
** Copyright (C) 2003 Alexander Dymo <cloudtemple@mksat.net>
**
** This file may be distributed and/or modified under the terms of the
** GNU General Public License version 2 as published by the Free Software
** Foundation and appearing in the file LICENSE.GPL included in the
** packaging of this file.
**
**********************************************************************/

#include "qcomboview.h"
#include <tdeversion.h>
#ifndef TQT_NO_COMBOBOX
#include "tqpopupmenu.h"
#include "tqlistview.h"
#include "tqpainter.h"
#include "tqdrawutil.h"
#include "tqstrlist.h"
#include "tqpixmap.h"
#include "tqtimer.h"
#include "tqapplication.h"
#include "tqlineedit.h"
#include "tqbitmap.h"
#include "tqeffects_p.h"
#include "tqstringlist.h"
#include "tqcombobox.h"
#include "tqstyle.h"
#include "tqheader.h"
#include <limits.h>

class QComboViewData
{
public:
    QComboViewData( QComboView *cb ): current(0), lView( 0 ), combo( cb )
    {
        duplicatesEnabled = TRUE;
        cb->setSizePolicy( TQSizePolicy( TQSizePolicy::Minimum, TQSizePolicy::Fixed ) );
    }

    inline TQListView * listView() { return lView; }
    void updateLinedGeometry();

    void setListView( TQListView *l ) { lView = l ;
        l->setMouseTracking( TRUE );}

    TQListViewItem *current;
    int maxCount;
    int sizeLimit;
    QComboView::Policy p;
    bool autoresize;
    bool poppedUp;
    bool mouseWasInsidePopup;
    bool arrowPressed;
    bool arrowDown;
    bool discardNextMousePress;
    bool shortClick;
    bool useCompletion;
    bool completeNow;
    int completeAt;
    bool duplicatesEnabled;
    int fullHeight, currHeight;

    TQLineEdit * ed;  // /bin/ed rules!
    TQTimer *completionTimer;

    TQSize sizeHint;

private:
    bool	usinglView;
    TQListView   *lView;
    QComboView *combo;

};

void QComboViewData::updateLinedGeometry()
{
    if ( !ed || !combo )
        return;
    TQRect r = TQStyle::visualRect( combo->style().querySubControlMetrics(TQStyle::CC_ComboBox, combo,
                                        TQStyle::SC_ComboBoxEditField), combo );

//    tqWarning("updateLinedGeometry(): currentItem is %d", combo->currentItem() == 0 ? 0 : 1);
    const TQPixmap *pix = combo->currentItem() ? combo->currentItem()->pixmap(0) : 0;
    if ( pix && pix->width() < r.width() )
        r.setLeft( r.left() + pix->width() + 4 );
    if ( r != ed->geometry() )
        ed->setGeometry( r );
}

static inline bool checkInsertIndex( const char *method, const char * name,
				     int count, int *index)
{
    bool range_err = (*index > count);
#if defined(TQT_CHECK_RANGE)
    if ( range_err )
	tqWarning( "QComboView::%s: (%s) Index %d out of range",
		 method, name ? name : "<no name>", *index );
#else
    Q_UNUSED( method )
    Q_UNUSED( name )
#endif
    if ( *index < 0 )				// append
	*index = count;
    return !range_err;
}


static inline bool checkIndex( const char *method, const char * name,
			       int count, int index )
{
    bool range_err = (index >= count);
#if defined(TQT_CHECK_RANGE)
    if ( range_err )
	tqWarning( "QComboView::%s: (%s) Index %i out of range",
		 method, name ? name : "<no name>", index );
#else
    Q_UNUSED( method )
    Q_UNUSED( name )
#endif
    return !range_err;
}


/*!
    Constructs a combobox with a maximum size and either Motif 2.0 or
    Windows look and feel.

    The input field can be edited if \a rw is TRUE, otherwise the user
    may only choose one of the items in the combobox.

    The \a parent and \a name arguments are passed on to the TQWidget
    constructor.
*/


QComboView::QComboView( bool rw, TQWidget *parent, const char *name )
    : TQWidget( parent, name, WResizeNoErase )
{
    d = new QComboViewData( this );
    setUpListView();

    d->current = 0;
    d->maxCount = INT_MAX;
    setSizeLimit(10);
    d->p = AtBottom;
    d->autoresize = FALSE;
    d->poppedUp = FALSE;
    d->arrowDown = FALSE;
    d->discardNextMousePress = FALSE;
    d->shortClick = FALSE;
    d->useCompletion = FALSE;
    d->completeAt = 0;
    d->completeNow = FALSE;
    d->completionTimer = new TQTimer( this );

    setFocusPolicy( TQ_StrongFocus );

    d->ed = 0;
    if ( rw )
	setUpLineEdit();
    setBackgroundMode( PaletteButton, PaletteBase );
}



/*!
    Destroys the combobox.
*/

QComboView::~QComboView()
{
    delete d;
}

void QComboView::setDuplicatesEnabled( bool enable )
{
   d->duplicatesEnabled = enable;
}

bool QComboView::duplicatesEnabled() const
{
    return d->duplicatesEnabled;
}

int QComboView::childCount() const
{
    return d->listView()->childCount();
}


/*!
    Removes all comboview items.
*/

void QComboView::clear()
{
    d->listView()->resize( 0, 0 );
    d->listView()->clear();

    d->current = 0;
    if ( d->ed ) {
        d->ed->setText( TQString::fromLatin1("") );
        d->updateLinedGeometry();
    }
    currentChanged();
}

TQListViewItem *QComboView::currentItem() const
{
    return d->current;
}

void QComboView::setCurrentItem( TQListViewItem *item )
{
    if ( item == d->current && !d->ed ) {
        return;
    }

    if (!item)
    {
        d->current = 0;
        if ( d->ed ) {
//            d->ed->setText( "" );
            d->updateLinedGeometry();
        }
        return;
    }

    d->current = item;
    d->completeAt = 0;
    if ( d->ed ) {
        d->ed->setText( item->text(0) );
//        tqWarning("setCurrentItem( %s )", item->text(0).latin1());
        d->updateLinedGeometry();
    }
    if ( d->listView() ) {
        d->listView()->setCurrentItem( item );
    } else {
        internalHighlight( item );
    // internalActivate( item ); ### this leads to weird behavior, as in 3.0.1
    }

    currentChanged();

    d->listView()->ensureItemVisible(item);
}

bool QComboView::autoResize() const
{
    return d->autoresize;
}

void QComboView::setAutoResize( bool enable )
{
    if ( (bool)d->autoresize != enable ) {
        d->autoresize = enable;
    if ( enable )
        adjustSize();
    }
}


/*!
    reimp

    This implementation caches the size hint to avoid resizing when
    the contents change dynamically. To invalidate the cached value
    call setFont().
*/
TQSize QComboView::sizeHint() const
{
    if ( isVisible() && d->sizeHint.isValid() )
        return d->sizeHint;

    constPolish();
//    int i, w;
    TQFontMetrics fm = fontMetrics();

    int maxW = childCount() ? 18 : 7 * fm.width(TQChar('x')) + 18;
    int maxH = TQMAX( fm.lineSpacing(), 14 ) + 2;

/*    for( i = 0; i < count(); i++ ) {
        w = d->listView()->item( i )->width( d->listView() );
        if ( w > maxW )
            maxW = w;
    }
*/
    d->sizeHint = (style().tqsizeFromContents(TQStyle::CT_ComboBox, this,
        TQSize(maxW, maxH)).expandedTo(TQApplication::globalStrut()));

    return d->sizeHint;
}


/*!
  \internal
  Receives activated signals from an internal popup list and emits
  the activated() signal.
*/

void QComboView::internalActivate( TQListViewItem * item )
{
    if (!item)
    {
        d->current = 0;
        if ( d->ed ) {
//            d->ed->setText( "" );
            d->updateLinedGeometry();
        }
        return;
    }
    popDownListView();
    d->poppedUp = FALSE;

    d->current = item;

    TQString t( item->text(0) );
    if ( d->ed ) {
        d->ed->setText( t );
//        tqWarning("internalActivate( %s )", item->text(0).latin1());
        d->updateLinedGeometry();
    }
    emit activated( item );
    emit activated( t );

//    item->setOpen(true);
}

/*!
  \internal
  Receives highlighted signals from an internal popup list and emits
  the highlighted() signal.
*/

void QComboView::internalHighlight( TQListViewItem * item )
{
    if (!item)
    {
        d->current = 0;
        if ( d->ed ) {
//            d->ed->setText( "" );
            d->updateLinedGeometry();
        }
        return;
    }
    emit highlighted( item );
    TQString t = item->text(0);
    if ( !t.isNull() )
        emit highlighted( t );
}

/*!
  \internal
  Receives timeouts after a click. Used to decide if a Motif style
  popup should stay up or not after a click.
*/
void QComboView::internalClickTimeout()
{
    d->shortClick = FALSE;
}

/*!
    Sets the palette for both the combobox button and the combobox
    popup list to \a palette.
*/

void QComboView::setPalette( const TQPalette &palette )
{
    TQWidget::setPalette( palette );
    if( d ) {
        if(d->listView())
            d->listView()->setPalette( palette );
    }
}

/*!
    Sets the font for both the combobox button and the combobox popup
    list to \a font.
*/

void QComboView::setFont( const TQFont &font )
{
    d->sizeHint = TQSize();      // invalidate size hint
    TQWidget::setFont( font );
    d->listView()->setFont( font );
    if (d->ed)
        d->ed->setFont( font );
    if ( d->autoresize )
        adjustSize();
}


/*!reimp
*/

void QComboView::resizeEvent( TQResizeEvent * e )
{
    if ( d->ed )
        d->updateLinedGeometry();
    d->listView()->resize( width(), d->listView()->height() );
    TQWidget::resizeEvent( e );
}

/*!reimp
*/

void QComboView::paintEvent( TQPaintEvent * )
{
    TQPainter p( this );
    const TQColorGroup & g = colorGroup();
    p.setPen(g.text());

    TQStyle::SFlags flags = TQStyle::Style_Default;
    if (isEnabled())
        flags |= TQStyle::Style_Enabled;
    if (hasFocus())
        flags |= TQStyle::Style_HasFocus;

    if ( width() < 5 || height() < 5 ) {
        qDrawShadePanel( &p, rect(), g, FALSE, 2,
                &g.brush( TQColorGroup::Button ) );
        return;
    }

//    bool reverse = TQApplication::reverseLayout();
    style().drawComplexControl( TQStyle::CC_ComboBox, &p, this, rect(), g,
                    flags, TQStyle::SC_All,
                    (d->arrowDown ?
                    TQStyle::SC_ComboBoxArrow :
                    TQStyle::SC_None ));

    TQRect re = style().querySubControlMetrics( TQStyle::CC_ComboBox, this,
                        TQStyle::SC_ComboBoxEditField );
    re = TQStyle::visualRect(re, this);
    p.setClipRect( re );

    if ( !d->ed ) {
        TQListViewItem * item = d->current;
        if ( item ) {
            // we calculate the TQListBoxTexts height (ignoring strut)
            int itemh = d->listView()->fontMetrics().lineSpacing() + 2;
            p.translate( re.x(), re.y() + (re.height() - itemh)/2  );
            item->paintCell( &p, d->listView()->colorGroup(), 0, width(), AlignLeft | AlignVCenter );
        }
    } else if ( d->listView() && d->listView()->currentItem( ) && d->current ) {
        TQListViewItem * item = d->current ;
        const TQPixmap *pix = item->pixmap(0);
        if ( pix ) {
            p.fillRect( re.x(), re.y(), pix->width() + 4, re.height(),
                    colorGroup().brush( TQColorGroup::Base ) );
            p.drawPixmap( re.x() + 2, re.y() +
                    ( re.height() - pix->height() ) / 2, *pix );
        }
    }
    p.setClipping( FALSE );
}


/*!reimp
*/

void QComboView::mousePressEvent( TQMouseEvent *e )
{
    if ( e->button() != Qt::LeftButton )
        return;
    if ( d->discardNextMousePress ) {
        d->discardNextMousePress = FALSE;
        return;
    }
    TQRect arrowRect = style().querySubControlMetrics( TQStyle::CC_ComboBox, this,
                                TQStyle::SC_ComboBoxArrow);
    arrowRect = TQStyle::visualRect(arrowRect, this);

    // Correction for motif style, where arrow is smaller
    // and thus has a rect that doesn't fit the button.
    arrowRect.setHeight( TQMAX(  height() - (2 * arrowRect.y()), arrowRect.height() ) );

    if ( childCount() && ( !editable() || arrowRect.contains( e->pos() ) ) ) {
        d->arrowPressed = FALSE;
        listView()->blockSignals( TRUE );
        tqApp->sendEvent( listView(), e ); // trigger the listbox's autoscroll
        listView()->blockSignals( FALSE );
        popup();
        if ( arrowRect.contains( e->pos() ) ) {
            d->arrowPressed = TRUE;
            d->arrowDown    = TRUE;
            repaint( FALSE );
        }
        TQTimer::singleShot( 200, this, TQT_SLOT(internalClickTimeout()));
        d->shortClick = TRUE;
    }
}

/*!reimp
*/

void QComboView::mouseMoveEvent( TQMouseEvent * )
{
}

/*!reimp
*/

void QComboView::mouseReleaseEvent( TQMouseEvent * )
{
}

/*!reimp
*/

void QComboView::mouseDoubleClickEvent( TQMouseEvent *e )
{
    mousePressEvent( e );
}


/*!reimp
*/

void QComboView::keyPressEvent( TQKeyEvent *e )
{
    TQListViewItem *c = currentItem();
    if ( ( e->key() == Key_F4 && e->state() == 0 ) ||
        ( e->key() == Key_Down && (e->state() & AltButton) ) ||
        ( !d->ed && e->key() == Key_Space ) ) {
        if ( childCount() ) {
            popup();
        }
        return;
    } else if ( e->key() == Key_Up ) {
/*        if ((!c) && (listView()->firstChild()))
            setCurrentItem(listView()->firstChild());*/
        if (c && c->itemAbove() )
            setCurrentItem( c->itemAbove() );
        else
            return;
    } else if ( e->key() == Key_Down ) {
        if ((!c) && (listView()->firstChild()))
        {
            setCurrentItem(listView()->firstChild());
            return;
        }
        if ( c && c->itemBelow() )
            setCurrentItem( c->itemBelow() );
        else
            return;
    } else if ( e->key() == Key_Home && ( !d->ed || !d->ed->hasFocus() ) ) {
        if (listView()->firstChild())
            setCurrentItem( listView()->firstChild() );
        else
            return;
    } else if ( e->key() == Key_End && ( !d->ed || !d->ed->hasFocus() ) ) {
        if (listView()->lastItem())
            setCurrentItem( listView()->lastItem() );
        else
            return;
    } else if ( !d->ed && e->ascii() >= 32 && !e->text().isEmpty() ) {
        if ( !d->completionTimer->isActive() ) {
            d->completeAt = 0;
            c = completionIndex( e->text(), c->itemBelow() );
            if ( c ) {
                setCurrentItem( c );
                d->completeAt = e->text().length();
            }
            else
                return;
        } else {
            d->completionTimer->stop();
            TQString ct = currentText().left( d->completeAt ) + e->text();
            c = completionIndex( ct, c );
            if ( c == 0 && d->completeAt > 0 ) {
                c = completionIndex( e->text(), listView()->firstChild() );
                ct = e->text();
            }
            d->completeAt = 0;
            if ( c ) {
                setCurrentItem( c );
                d->completeAt = ct.length();
            }
            else
                return;
        }
        d->completionTimer->start( 400, TRUE );
    } else {
        e->ignore();
        return;
    }

    c = currentItem();
    if ( childCount() && c && !c->text(0).isNull() )
        emit activated( c->text(0) );
    emit activated( c );
}

TQString QComboView::currentText() const
{
    if ( d->ed )
        return d->ed->text();
    else if ( d->current )
        return currentItem()->text(0);
    else
        return TQString();
}

/*!reimp
*/

void QComboView::focusInEvent( TQFocusEvent * e )
{
    TQWidget::focusInEvent( e );
    d->completeNow = FALSE;
    d->completeAt = 0;

    emit focusGranted();
}

/*!reimp
*/

void QComboView::focusOutEvent( TQFocusEvent * e )
{
    TQWidget::focusOutEvent( e );
    d->completeNow = FALSE;
    d->completeAt = 0;

    emit focusLost();
}

/*!reimp
*/

void QComboView::wheelEvent( TQWheelEvent *e )
{
    if ( d->poppedUp ) {
        TQApplication::sendEvent( d->listView(), e );
    } else {
        if ( e->delta() > 0 ) {
            TQListViewItem *c = currentItem();
            if ( c && c->itemAbove() ) {
                setCurrentItem( c->itemAbove() );
                emit activated( currentItem() );
                emit activated( currentText() );
            }
        } else {
            TQListViewItem *c = currentItem();
            if ( c && c->itemBelow() ) {
                setCurrentItem( c->itemBelow() );
                emit activated( currentItem() );
                emit activated( currentText() );
            }
        }
        e->accept();
    }
}

int childCount(TQListViewItem *it)
{
    int count = 1;
    TQListViewItem * myChild = it->firstChild();
    while( myChild ) {
        count += childCount(myChild);
        myChild = myChild->nextSibling();
    }
    return count;
}

int childCount(TQListView *lv)
{
    int count = 0;
    TQListViewItem * myChild = lv->firstChild();
    while( myChild ) {
        count += childCount(myChild);
//        count += 1;
        myChild = myChild->nextSibling();
    }
    return count;
}

/*!
  \internal
   Calculates the listbox height needed to contain all items, or as
   many as the list box is supposed to contain.
*/
static int listHeight( TQListView *l, int /*sl*/ )
{
/*    if ( l->childCount() > 0 )
        return TQMIN( l->childCount(), (uint)sl) * l->firstChild()->height();
    else*/

    int prefH = 0;
    int ch = childCount(l);
    ch = TQMIN(ch, 10);
    if (l->firstChild())
    {
        prefH = ch * l->firstChild()->height();
    }
    else
        prefH = l->sizeHint().height();

    if (l->header()->isVisible())
        prefH += l->header()->sizeHint().height();

//    return prefH < l->sizeHint().height() ? prefH : l->sizeHint().height();

    return prefH+2;
}

/*!
    Pops up the combobox popup list.

    If the list is empty, no items appear.
*/

void QComboView::popup()
{
    if ( !childCount() )
        return;

    // Send all listbox events to eventFilter():
    TQListView* lb = d->listView();
    lb->triggerUpdate( );
    lb->installEventFilter( this );
    lb->viewport()->installEventFilter( this );
    d->mouseWasInsidePopup = FALSE;
//    int w = lb->variableWidth() ? lb->sizeHint().width() : width();
    int w = width();
    int h = listHeight( lb, d->sizeLimit );
    TQRect screen = TQApplication::desktop()->availableGeometry( const_cast<QComboView*>(this) );

    int sx = screen.x();        // screen pos
    int sy = screen.y();
    int sw = screen.width();    // screen width
    int sh = screen.height();   // screen height
    TQPoint pos = mapToGlobal( TQPoint(0,height()) );
    // ## Similar code is in TQPopupMenu
    int x = pos.x();
    int y = pos.y();

    // the complete widget must be visible
    if ( x + w > sx + sw )
        x = sx+sw - w;
    if ( x < sx )
        x = sx;
    if (y + h > sy+sh && y - h - height() >= 0 )
        y = y - h - height();
    TQRect rect =
    style().querySubControlMetrics( TQStyle::CC_ComboBox, this,
                    TQStyle::SC_ComboBoxListBoxPopup,
                    TQStyleOption( x, y, w, h ) );
    if ( rect.isNull() )
        rect.setRect( x, y, w, h );
    lb->setGeometry( rect );

    lb->raise();
    bool block = lb->signalsBlocked();
    lb->blockSignals( TRUE );
    TQListViewItem *currentLBItem = d->current ;
    lb->setCurrentItem( currentLBItem );
    // set the current item to also be the selected item if it isn't already
    if ( currentLBItem && currentLBItem->isSelectable() && !currentLBItem->isSelected() )
        lb->setSelected( currentLBItem, TRUE );
    lb->blockSignals( block );
    lb->setVScrollBarMode(TQScrollView::Auto);

//#ifndef TQT_NO_EFFECTS
/*    if ( TQApplication::isEffectEnabled( UI_AnimateCombo ) ) {
        if ( lb->y() < mapToGlobal(TQPoint(0,0)).y() )
        qScrollEffect( lb, TQEffects::UpScroll );
        else
        qScrollEffect( lb );
    } else*/
//#endif
        lb->show();
    d->poppedUp = TRUE;
}


/*!
  reimp
*/
void QComboView::updateMask()
{
    TQBitmap bm( size() );
    bm.fill( color0 );

    {
        TQPainter p( &bm, this );
        style().drawComplexControlMask(TQStyle::CC_ComboBox, &p, this, rect());
    }

    setMask( bm );
}

/*!
  \internal
  Pops down (removes) the combobox popup list box.
*/
void QComboView::popDownListView()
{
    d->listView()->removeEventFilter( this );
    d->listView()->viewport()->removeEventFilter( this );
    d->listView()->hide();
    d->listView()->setCurrentItem( d->current );
    if ( d->arrowDown ) {
        d->arrowDown = FALSE;
        repaint( FALSE );
    }
    d->poppedUp = FALSE;
}


/*!
  \internal
  Re-indexes the identifiers in the popup list.
*/

void QComboView::reIndex()
{
}

/*!
  \internal
  Repaints the combobox.
*/

void QComboView::currentChanged()
{
    if ( d->autoresize )
        adjustSize();
    update();
}

/*! reimp

  \internal

  The event filter steals events from the popup or listbox when they
  are popped up. It makes the popup stay up after a short click in
  motif style. In windows style it toggles the arrow button of the
  combobox field, and activates an item and takes down the listbox
  when the mouse button is released.
*/

bool QComboView::eventFilter( TQObject *object, TQEvent *event )
{
    if ( !event )
        return TRUE;
    else if ( TQT_BASE_OBJECT(object) == TQT_BASE_OBJECT(d->ed) ) {
        if ( event->type() == TQEvent::KeyPress ) {
            bool isAccepted = ( (TQKeyEvent*)event )->isAccepted();
            keyPressEvent( (TQKeyEvent *)event );
            if ( ((TQKeyEvent *)event)->isAccepted() ) {
                d->completeNow = FALSE;
                return TRUE;
            } else if ( ((TQKeyEvent *)event)->key() != Key_End ) {
                d->completeNow = TRUE;
                d->completeAt = d->ed->cursorPosition();
            }
            if ( isAccepted )
                ( (TQKeyEvent*)event )->accept();
            else
                ( (TQKeyEvent*)event )->ignore();
        } else if ( event->type() == TQEvent::KeyRelease ) {
            d->completeNow = FALSE;
            keyReleaseEvent( (TQKeyEvent *)event );
            return ((TQKeyEvent *)event)->isAccepted();
        } else if ( event->type() == TQEvent::FocusIn ) {
            focusInEvent( (TQFocusEvent *)event );
        } else if ( event->type() == TQEvent::FocusOut ) {
            focusOutEvent( (TQFocusEvent *)event );
        } else if ( d->useCompletion && d->completeNow ) {
            if ( !d->ed->text().isNull() &&
            d->ed->cursorPosition() > d->completeAt &&
            d->ed->cursorPosition() == (int)d->ed->text().length() ) {
                d->completeNow = FALSE;
                TQString ct( d->ed->text() );
                TQListViewItem *i = completionIndex( ct, currentItem() );
                if ( i ) {
                    TQString it = i->text(0);
                    d->ed->validateAndSet( it, ct.length(),
                            ct.length(), it.length() );
                }
            }
        }
    } else if ( ( TQT_BASE_OBJECT(object) == TQT_BASE_OBJECT(d->listView()) ||
                        TQT_BASE_OBJECT(object) == TQT_BASE_OBJECT(d->listView()->viewport()) )) {
        TQMouseEvent *e = (TQMouseEvent*)event;
        switch( event->type() ) {
        case TQEvent::MouseMove:
            if ( !d->mouseWasInsidePopup  ) {
//                tqWarning("!d->mouseWasInsidePopup");
                TQPoint pos = e->pos();
                if ( TQT_TQRECT_OBJECT(d->listView()->rect()).contains( pos ) )
                    d->mouseWasInsidePopup = TRUE;
                // Check if arrow button should toggle
                if ( d->arrowPressed ) {
                    TQPoint comboPos;
                    comboPos = mapFromGlobal( d->listView()->mapToGlobal(pos) );
                    TQRect arrowRect =
                    style().querySubControlMetrics( TQStyle::CC_ComboBox, this,
                                    TQStyle::SC_ComboBoxArrow);
                    arrowRect = TQStyle::visualRect(arrowRect, this);
                    if ( arrowRect.contains( comboPos ) ) {
                        if ( !d->arrowDown  ) {
                            d->arrowDown = TRUE;
                            repaint( FALSE );
                        }
                        } else {
                        if ( d->arrowDown  ) {
                            d->arrowDown = FALSE;
                            repaint( FALSE );
                        }
                    }
                }
            } else if ((e->state() & ( Qt::RightButton | Qt::LeftButton | Qt::MidButton ) ) == 0 &&
                style().styleHint(TQStyle::SH_ComboBox_ListMouseTracking, this)) {
//                tqWarning("event filter:: emu");
                TQWidget *mouseW = TQApplication::widgetAt( e->globalPos(), TRUE );
//                if ( mouseW == d->listView()->viewport() ) { //###
                if ( mouseW == d->listView()->viewport() ) {
                    TQListViewItem *sel = d->listView()->itemAt(e->pos());
                    if (sel)
                    {
                        d->listView()->setCurrentItem(sel);
                        d->listView()->setSelected(sel, true);
                    }
                    return TRUE;
                }
            }

            break;
        case TQEvent::MouseButtonRelease:
            if ( TQT_TQRECT_OBJECT(d->listView()->rect()).contains( e->pos() ) ) {
                TQMouseEvent tmp( TQEvent::MouseButtonDblClick,
                        e->pos(), e->button(), e->state() ) ;
                // will hide popup
                TQApplication::sendEvent( object, &tmp );
                return TRUE;
            } else {
                if ( d->mouseWasInsidePopup ) {
                    popDownListView();
                } else {
                    d->arrowPressed = FALSE;
                    if ( d->arrowDown  ) {
                        d->arrowDown = FALSE;
                        repaint( FALSE );
                    }
                }
            }
            break;
        case TQEvent::MouseButtonDblClick:
        case TQEvent::MouseButtonPress:
            if ( !TQT_TQRECT_OBJECT(d->listView()->rect()).contains( e->pos() ) ) {
                TQPoint globalPos = d->listView()->mapToGlobal(e->pos());
                if ( TQApplication::widgetAt( globalPos, TRUE ) == this ) {
                    d->discardNextMousePress = TRUE;
                    // avoid popping up again
                }
                popDownListView();
                return TRUE;
            }
            break;
        case TQEvent::KeyPress:
            switch( ((TQKeyEvent *)event)->key() ) {
                case Key_Up:
                case Key_Down:
                    if ( !(((TQKeyEvent *)event)->state() & AltButton) )
                        break;
                case Key_F4:
                case Key_Escape:
                    if ( d->poppedUp ) {
                        popDownListView();
                        return TRUE;
                    }
                break;
                case Key_Enter:
                case Key_Return:
                    // work around TQDialog's enter handling
                    return FALSE;
                default:
                break;
            }
            break;
        case TQEvent::Hide:
            popDownListView();
            break;
        default:
            break;
        }
    }
    return TQWidget::eventFilter( object, event );
}


/*!
    Returns the index of the first item \e after \a startingAt of
    which \a prefix is a case-insensitive prefix. Returns -1 if no
    items start with \a prefix.
*/

TQListViewItem *QComboView::completionIndex( const TQString & prefix,
                    TQListViewItem *startingAt ) const
{
    TQListViewItem *start = startingAt;
/*    if ( start < 0 || start >= count() )
        start = 0;
    if ( start >= count() )
        return -1;*/
    if (!start)
        start = listView()->firstChild();
    if (!start)
        return 0;
/*    if (!start->itemBelow())
        return 0;*/
    TQString match = prefix.lower();
    if ( match.length() < 1 )
        return start;

    TQString current;
    TQListViewItem *i = start;
    do {
        current = i->text(0).lower();
        if ( current.startsWith( match ) )
            return i;
        i = i->itemBelow();
        if ( i )
            i = listView()->firstChild();
    } while ( i != start );
    return 0;
}


int QComboView::sizeLimit() const
{
    return d ? d->sizeLimit : INT_MAX;
}

void QComboView::setSizeLimit( int lines )
{
    d->sizeLimit = lines;
}


/*int QComboView::maxCount() const
{
    return d ? d->maxCount : INT_MAX;
}

void QComboView::setMaxCount( int count )
{
    int l = this->count();
    while( --l > count )
        removeItem( l );
    d->maxCount = count;
}
*/
QComboView::Policy QComboView::insertionPolicy() const
{
    return d->p;
}

void QComboView::setInsertionPolicy( Policy policy )
{
    d->p = policy;
}



/*!
  Internal slot to keep the line editor up to date.
*/

void QComboView::returnPressed()
{
    TQString s( d->ed->text() );

    if ( s.isEmpty() )
        return;

    TQListViewItem *c = 0;
    bool doInsert = TRUE;
    if ( !d->duplicatesEnabled ) {
        c = listView()->findItem(s, 0);
        if ( c )
            doInsert = FALSE;
    }

    if ( doInsert ) {
        if ( insertionPolicy() != NoInsertion ) {
/*            int cnt = count();
            while ( cnt >= d->maxCount ) {
                removeItem( --cnt );
            }*/
        }

        switch ( insertionPolicy() ) {
            case AtCurrent:
                if ( s != currentItem()->text(0) )
                    currentItem()->setText(0, s);
                emit activated( currentItem() );
                emit activated( s );
                return;
            case NoInsertion:
                emit activated( s );
                return;
            case AtTop:
                c = 0;
                return;
//                break;
            case AtBottom:
                c = new TQListViewItem(listView(), listView()->lastItem(), s);
                break;
            case BeforeCurrent:
                if (currentItem() && currentItem()->itemAbove())
                    c = new TQListViewItem(listView(), currentItem()->itemAbove(), s);
                else
                {
                    c = 0;
                    return;
                }
                break;
            case AfterCurrent:
                if (currentItem() && currentItem()->itemBelow())
                    c = new TQListViewItem(listView(), currentItem()->itemBelow(), s);
                else
                {
                    c = 0;
                    return;
                }
                break;
        }
    }

    if (c)
    {
        setCurrentItem( c );
        emit activated( c );
        emit activated( s );
    }
}


/*! reimp
*/

void QComboView::setEnabled( bool enable )
{
    TQWidget::setEnabled( enable );
}



/*!
    Applies the validator \a v to the combobox so that only text which
    is valid according to \a v is accepted.

    This function does nothing if the combobox is not editable.

    \sa validator() clearValidator() TQValidator
*/

void QComboView::setValidator( const TQValidator * v )
{
    if ( d && d->ed )
        d->ed->setValidator( v );
}


/*!
    Returns the validator which constrains editing for this combobox
    if there is one; otherwise returns 0.

    \sa setValidator() clearValidator() TQValidator
*/

const TQValidator * QComboView::validator() const
{
    return d && d->ed ? d->ed->validator() : 0;
}


/*!
    This slot is equivalent to setValidator( 0 ).
*/

void QComboView::clearValidator()
{
    if ( d && d->ed )
        d->ed->setValidator( 0 );
}


/*!
    Sets the combobox to use \a newListBox instead of the current list
    box or popup. As a side effect, it clears the combobox of its
    current contents.

    \warning QComboView assumes that newListBox->text(n) returns
    non-null for 0 \<= n \< newListbox->count(). This assumption is
    necessary because of the line edit in QComboView.
*/

void QComboView::setListView( TQListView * newListView )
{
    clear();

    delete d->listView();

    newListView->reparent( this, WType_Popup, TQPoint(0,0), FALSE );
    d->setListView( newListView );
    d->listView()->setFont( font() );
    d->listView()->setPalette( palette() );
/*    d->listView()->setVScrollBarMode(TQScrollView::AlwaysOff);
    d->listView()->setHScrollBarMode(TQScrollView::AlwaysOff);*/
    d->listView()->setFrameStyle( TQFrame::Box | TQFrame::Plain );
    d->listView()->setLineWidth( 1 );
/*    d->listView()->setRootIsDecorated( true );
    d->listView()->setAllColumnsShowFocus(true);*/
    d->listView()->resize( 100, 10 );

    if (d->listView()->firstChild())
        d->current = d->listView()->firstChild();

//    d->listView()->header()->hide();


/*    d->listView()->setFont( font() );
    d->listView()->setPalette( palette() );
    d->listView()->setVScrollBarMode( TQScrollView::AlwaysOff );
    d->listView()->setHScrollBarMode( TQScrollView::AlwaysOff );
    d->listView()->setFrameStyle( TQFrame::Box | TQFrame::Plain );
    d->listView()->setLineWidth( 1 );
    d->listView()->setRootIsDecorated( true );
    d->listView()->setAllColumnsShowFocus(true);
    d->listView()->addColumn("");
    d->listView()->resize( 100, 10 );
*/

    connect( d->listView(), TQT_SIGNAL(returnPressed(TQListViewItem*)),
            TQT_SLOT(internalActivate(TQListViewItem*)));
    connect( d->listView(), TQT_SIGNAL(doubleClicked(TQListViewItem*)),
            TQT_SLOT(internalActivate(TQListViewItem*)));
    connect( d->listView(), TQT_SIGNAL(doubleClicked(TQListViewItem*)),
            TQT_SLOT(checkState(TQListViewItem*)));
    connect( d->listView(), TQT_SIGNAL(currentChanged(TQListViewItem*)),
            TQT_SLOT(internalHighlight(TQListViewItem*)));
    connect( d->listView(), TQT_SIGNAL(selectionChanged(TQListViewItem*)),
            TQT_SLOT(internalHighlight(TQListViewItem*)));
}


/*!
    Returns the current list box, or 0 if there is no list box.
    (QComboView can use TQPopupMenu instead of TQListBox.) Provided to
    match setlistView().

    \sa setlistView()
*/

TQListView * QComboView::listView() const
{
    return d ? d->listView() : 0;
}

/*!
    Returns the line edit, or 0 if there is no line edit.

    Only editable listboxes have a line editor.
*/
TQLineEdit* QComboView::lineEdit() const
{
    return d->ed;
}



/*!
    Clears the line edit without changing the combobox's contents.
    Does nothing if the combobox isn't editable.

    This is particularly useful when using a combobox as a line edit
    with history. For example you can connect the combobox's
    activated() signal to clearEdit() in order to present the user
    with a new, empty line as soon as Enter is pressed.

    \sa setEditText()
*/

void QComboView::clearEdit()
{
    if ( d && d->ed )
        d->ed->clear();
}


/*!
    Sets the text in the line edit to \a newText without changing the
    combobox's contents. Does nothing if the combobox isn't editable.

    This is useful e.g. for providing a good starting point for the
    user's editing and entering the change in the combobox only when
    the user presses Enter.

    \sa clearEdit() insertItem()
*/

void QComboView::setEditText( const TQString &newText )
{
    if ( d && d->ed ) {
        d->updateLinedGeometry();
        d->ed->setText( newText );
    }
}

void QComboView::setAutoCompletion( bool enable )
{
    d->useCompletion = enable;
    d->completeNow = FALSE;
}


bool QComboView::autoCompletion() const
{
    return d->useCompletion;
}

/*!reimp
 */
void QComboView::styleChange( TQStyle& s )
{
    d->sizeHint = TQSize();		// invalidate size hint...
    if ( d->ed )
        d->updateLinedGeometry();
    TQWidget::styleChange( s );
}

bool QComboView::editable() const
{
    return d->ed != 0;
}

void QComboView::setEditable( bool y )
{
    if ( y == editable() )
        return;
    if ( y ) {
        setUpListView();
        setUpLineEdit();
        d->ed->show();
        if ( currentItem() )
            setEditText( currentText() );
        } else {
            delete d->ed;
        d->ed = 0;
    }

    setFocusPolicy( TQ_StrongFocus );
    updateGeometry();
    update();
}


void QComboView::setUpListView()
{
    d->setListView( new TQListView( this, "in-combo", WType_Popup ) );

    d->listView()->setFont( font() );
    d->listView()->setPalette( palette() );
/*    d->listView()->setVScrollBarMode( TQScrollView::AlwaysOff );
    d->listView()->setHScrollBarMode( TQScrollView::AlwaysOff );*/
    d->listView()->setFrameStyle( TQFrame::Box | TQFrame::Plain );
    d->listView()->setLineWidth( 1 );
    d->listView()->setRootIsDecorated( false );
    d->listView()->setAllColumnsShowFocus(true);
    d->listView()->addColumn("");
    d->listView()->resize( 100, 10 );
    d->listView()->setResizeMode(TQListView::LastColumn);

    if (d->listView()->firstChild())
        d->current = d->listView()->firstChild();

    d->listView()->header()->hide();

    connect( d->listView(), TQT_SIGNAL(returnPressed(TQListViewItem*)),
            TQT_SLOT(internalActivate(TQListViewItem*)));
    connect( d->listView(), TQT_SIGNAL(doubleClicked(TQListViewItem*)),
            TQT_SLOT(internalActivate(TQListViewItem*)));
    connect( d->listView(), TQT_SIGNAL(doubleClicked(TQListViewItem*)),
            TQT_SLOT(checkState(TQListViewItem*)));
    connect( d->listView(), TQT_SIGNAL(currentChanged(TQListViewItem*)),
            TQT_SLOT(internalHighlight(TQListViewItem*)));
    connect( d->listView(), TQT_SIGNAL(selectionChanged(TQListViewItem*)),
            TQT_SLOT(internalHighlight(TQListViewItem*)));
}


void QComboView::setUpLineEdit()
{
    if ( !d->ed )
        setLineEdit( new TQLineEdit( this, "combo edit" ) );
}

/*!
    Sets the line edit to use \a edit instead of the current line edit.
*/

void QComboView::setLineEdit( TQLineEdit *edit )
{
    if ( !edit ) {
#if defined(TQT_CHECK_NULL)
        Q_ASSERT( edit != 0 );
#endif
        return;
    }

    edit->setText( currentText() );
    if ( d->ed ) {
        int start = 0, end = 0;
        d->ed->getSelection( &start, &end );
        edit->setSelection( start, end );
        edit->setCursorPosition( d->ed->cursorPosition() );
        edit->setEdited( d->ed->edited() );
        delete d->ed;
    }

    d->ed = edit;

    if ( TQT_BASE_OBJECT(edit->parent()) != TQT_BASE_OBJECT(this) ) {
        edit->reparent( this, TQPoint(0,0), FALSE );
        edit->setFont( font() );
    }

    connect (edit, TQT_SIGNAL( textChanged( const TQString& ) ),
            this, TQT_SIGNAL( textChanged( const TQString& ) ) );
    connect( edit, TQT_SIGNAL(returnPressed()), TQT_SLOT(returnPressed()) );

    edit->setFrame( FALSE );
    d->updateLinedGeometry();
    edit->installEventFilter( this );
    setFocusProxy( edit );
    setFocusPolicy( TQ_StrongFocus );

    setUpListView();

    if ( isVisible() )
        edit->show();

    updateGeometry();
    update();
}

void QComboView::setCurrentText( const TQString& txt )
{
    TQListViewItem *i;
    i = listView()->findItem(txt, 0);
    if ( i )
        setCurrentItem( i );
    else if ( d->ed )
        d->ed->setText( txt );
    else if (currentItem())
        currentItem()->setText(0, txt);
}

void QComboView::checkState( TQListViewItem * item)
{
    item->setOpen(!item->isOpen());
}

void QComboView::setCurrentActiveItem( TQListViewItem * item )
{
    if ( item == d->current && !d->ed ) {
        return;
    }

    d->current = item;
    d->completeAt = 0;
    if ( d->ed ) {
        d->ed->setText( item->text(0) );
	d->ed->setCursorPosition(0);
//        tqWarning("setCurrentActiveItem( %s )", item->text(0).latin1());
        d->updateLinedGeometry();
    }
    if ( d->listView() ) {
        d->listView()->setCurrentItem( item );
        emit activated( item );
        emit activated( item->text(0) );
    } else {
        internalHighlight( item );
        internalActivate( item );
    }

    currentChanged();

    d->listView()->ensureItemVisible(item);
}

#include "qcomboview.moc"

#endif // TQT_NO_COMBOBOX

