summaryrefslogtreecommitdiffstats
path: root/konqueror/konq_combo.cc
diff options
context:
space:
mode:
Diffstat (limited to 'konqueror/konq_combo.cc')
-rw-r--r--konqueror/konq_combo.cc892
1 files changed, 892 insertions, 0 deletions
diff --git a/konqueror/konq_combo.cc b/konqueror/konq_combo.cc
new file mode 100644
index 000000000..2e5279474
--- /dev/null
+++ b/konqueror/konq_combo.cc
@@ -0,0 +1,892 @@
+/* This file is part of the KDE project
+ Copyright (C) 2001 Carsten Pfeiffer <pfeiffer@kde.org>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; see the file COPYING. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#include <qpainter.h>
+#include <qstyle.h>
+#include <kapplication.h>
+#include <kconfig.h>
+#include <kcompletionbox.h>
+#include <kdebug.h>
+#include <kiconloader.h>
+#include <kicontheme.h>
+#include <klineedit.h>
+#include <konq_pixmapprovider.h>
+#include <kstdaccel.h>
+#include <kurldrag.h>
+#include <konq_mainwindow.h>
+#include <kstringhandler.h>
+
+#include <dcopclient.h>
+
+#include "konq_combo.h"
+
+KConfig * KonqCombo::s_config = 0L;
+const int KonqCombo::temporary = 0;
+
+static QString titleOfURL( const QString& urlStr )
+{
+ KURL url = KURL::fromPathOrURL( urlStr );
+ KonqHistoryList& historylist = const_cast<KonqHistoryList&>( KonqHistoryManager::kself()->entries() );
+ KonqHistoryEntry *historyentry = historylist.findEntry( url );
+ if ( !historyentry && !url.url().endsWith( "/" ) ) {
+ url.setPath( url.path()+'/' );
+ historyentry = historylist.findEntry( url );
+ }
+ return ( historyentry ? historyentry->title : QString::null );
+}
+
+class Q_EXPORT KonqComboListBoxPixmap : public QListBoxItem
+{
+public:
+ KonqComboListBoxPixmap( const QString& text );
+ KonqComboListBoxPixmap( const QPixmap &, const QString& text, const QString& title );
+
+ const QPixmap *pixmap() const { return &pm; }
+
+ int height( const QListBox * ) const;
+ int width( const QListBox * ) const;
+
+ int rtti() const;
+ static int RTTI;
+
+ bool reuse( const QString& newText );
+
+protected:
+ void paint( QPainter * );
+
+private:
+ bool lookup_pending;
+ QPixmap pm;
+ QString title;
+};
+
+class KonqComboLineEdit : public KLineEdit
+{
+public:
+ KonqComboLineEdit( QWidget *parent=0, const char *name=0 );
+ void setCompletedItems( const QStringList& items );
+};
+
+class KonqComboCompletionBox : public KCompletionBox
+{
+public:
+ KonqComboCompletionBox( QWidget *parent, const char *name = 0 );
+ void setItems( const QStringList& items );
+ void insertStringList( const QStringList & list, int index = -1 );
+};
+
+KonqCombo::KonqCombo( QWidget *parent, const char *name )
+ : KHistoryCombo( parent, name ),
+ m_returnPressed( false ),
+ m_permanent( false ),
+ m_modifier( NoButton ),
+ m_pageSecurity( KonqMainWindow::NotCrypted )
+{
+ setInsertionPolicy( NoInsertion );
+ setSizePolicy( QSizePolicy( QSizePolicy::Expanding, QSizePolicy::Fixed ));
+
+ Q_ASSERT( s_config );
+
+ KConfigGroupSaver cs( s_config, "Location Bar" );
+ setMaxCount( s_config->readNumEntry("Maximum of URLs in combo", 20 ));
+
+ // We should also connect the completionBox' highlighted signal to
+ // our setEditText() slot, because we're handling the signals ourselves.
+ // But we're lazy and let KCompletionBox do this and simply switch off
+ // handling of signals later.
+ setHandleSignals( true );
+
+ KonqComboLineEdit *edit = new KonqComboLineEdit( this, "combo lineedit" );
+ edit->setHandleSignals( true );
+ edit->setCompletionBox( new KonqComboCompletionBox( edit, "completion box" ) );
+ setLineEdit( edit );
+
+ completionBox()->setTabHandling( true );
+
+ // Make the lineedit consume the Key_Enter event...
+ setTrapReturnKey( true );
+
+ connect( KonqHistoryManager::kself(), SIGNAL(cleared()), SLOT(slotCleared()) );
+ connect( this, SIGNAL(cleared() ), SLOT(slotCleared()) );
+ connect( this, SIGNAL(highlighted( int )), SLOT(slotSetIcon( int )) );
+ connect( this, SIGNAL(activated( const QString& )),
+ SLOT(slotActivated( const QString& )) );
+
+ if ( !kapp->dcopClient()->isAttached() )
+ kapp->dcopClient()->attach();
+}
+
+KonqCombo::~KonqCombo()
+{
+}
+
+void KonqCombo::init( KCompletion *completion )
+{
+ setCompletionObject( completion, false ); //KonqMainWindow handles signals
+ setAutoDeleteCompletionObject( false );
+ setCompletionMode( completion->completionMode() );
+
+ loadItems();
+}
+
+void KonqCombo::setURL( const QString& url )
+{
+ //kdDebug(1202) << "KonqCombo::setURL: " << url << ", returnPressed ? " << m_returnPressed << endl;
+ setTemporary( url );
+
+ if ( m_returnPressed ) { // Really insert...
+ m_returnPressed = false;
+ QByteArray data;
+ QDataStream s( data, IO_WriteOnly );
+ s << url << kapp->dcopClient()->defaultObject();
+ kapp->dcopClient()->send( "konqueror*", "KonquerorIface",
+ "addToCombo(QString,QCString)", data);
+ }
+ // important security consideration: always display the beginning
+ // of the url rather than its end to prevent spoofing attempts.
+ lineEdit()->setCursorPosition( 0 );
+}
+
+void KonqCombo::setTemporary( const QString& text )
+{
+ setTemporary( text, KonqPixmapProvider::self()->pixmapFor(text) );
+}
+
+void KonqCombo::setTemporary( const QString& url, const QPixmap& pix )
+{
+ //kdDebug(1202) << "KonqCombo::setTemporary: " << url << ", temporary = " << temporary << endl;
+
+ // Insert a temporary item when we don't have one yet
+ if ( count() == 0 )
+ insertItem( pix, url, temporary, titleOfURL( url ) );
+ else
+ {
+ if (url != temporaryItem())
+ applyPermanent();
+
+ updateItem( pix, url, temporary, titleOfURL( url ) );
+ }
+
+ setCurrentItem( temporary );
+}
+
+void KonqCombo::removeDuplicates( int index )
+{
+ //kdDebug(1202) << "KonqCombo::removeDuplicates: Starting index = " << index << endl;
+
+ QString url (temporaryItem());
+ if (url.endsWith("/"))
+ url.truncate(url.length()-1);
+
+ // Remove all dupes, if available...
+ for ( int i = index; i < count(); i++ )
+ {
+ QString item (text(i));
+ if (item.endsWith("/"))
+ item.truncate(item.length()-1);
+
+ if ( item == url )
+ removeItem( i );
+ }
+}
+
+// called via DCOP in all instances
+void KonqCombo::insertPermanent( const QString& url )
+{
+ //kdDebug(1202) << "KonqCombo::insertPermanent: URL = " << url << endl;
+ saveState();
+ setTemporary( url );
+ m_permanent = true;
+ restoreState();
+}
+
+// called right before a new (different!) temporary item will be set. So we
+// insert an item that was marked permanent properly at position 1.
+void KonqCombo::applyPermanent()
+{
+ if ( m_permanent && !temporaryItem().isEmpty() ) {
+
+ // Remove as many items as needed to honour maxCount()
+ int index = count();
+ while ( count() >= maxCount() )
+ removeItem( --index );
+
+ QString url (temporaryItem());
+ insertItem( KonqPixmapProvider::self()->pixmapFor( url ), url, 1, titleOfURL( url ) );
+ //kdDebug(1202) << "KonqCombo::applyPermanent: " << url << endl;
+
+ // Remove all duplicates starting from index = 2
+ removeDuplicates( 2 );
+ m_permanent = false;
+ }
+}
+
+void KonqCombo::insertItem( const QString &text, int index, const QString& title )
+{
+ KonqComboListBoxPixmap* item = new KonqComboListBoxPixmap( 0, text, title );
+ listBox()->insertItem( item, index );
+}
+
+void KonqCombo::insertItem( const QPixmap &pixmap, const QString& text, int index, const QString& title )
+{
+ KonqComboListBoxPixmap* item = new KonqComboListBoxPixmap( pixmap, text, title );
+ listBox()->insertItem( item, index );
+}
+
+void KonqCombo::updateItem( const QPixmap& pix, const QString& t, int index, const QString& title )
+{
+ // No need to flicker
+ if (text( index ) == t &&
+ (pixmap(index) && pixmap(index)->serialNumber() == pix.serialNumber()))
+ return;
+
+ // kdDebug(1202) << "KonqCombo::updateItem: item='" << t << "', index='"
+ // << index << "'" << endl;
+
+ // QComboBox::changeItem() doesn't honour the pixmap when
+ // using an editable combobox, so we just remove and insert
+ // ### use QComboBox::changeItem(), once that finally works
+ // Well lets try it now as it seems to work fine for me. We
+ // can always revert :)
+ KonqComboListBoxPixmap* item = new KonqComboListBoxPixmap( pix, t, title );
+ listBox()->changeItem( item, index );
+
+ /*
+ setUpdatesEnabled( false );
+ lineEdit()->setUpdatesEnabled( false );
+
+ removeItem( index );
+ insertItem( pix, t, index );
+
+ setUpdatesEnabled( true );
+ lineEdit()->setUpdatesEnabled( true );
+ update();
+ */
+}
+
+void KonqCombo::saveState()
+{
+ m_cursorPos = cursorPosition();
+ m_currentText = currentText();
+ m_currentIndex = currentItem();
+}
+
+void KonqCombo::restoreState()
+{
+ setTemporary( m_currentText );
+ lineEdit()->setCursorPosition( m_cursorPos );
+}
+
+void KonqCombo::updatePixmaps()
+{
+ saveState();
+
+ setUpdatesEnabled( false );
+ KonqPixmapProvider *prov = KonqPixmapProvider::self();
+ for ( int i = 1; i < count(); i++ ) {
+ updateItem( prov->pixmapFor( text( i ) ), text( i ), i, titleOfURL( text( i ) ) );
+ }
+ setUpdatesEnabled( true );
+ repaint();
+
+ restoreState();
+}
+
+void KonqCombo::loadItems()
+{
+ clear();
+ int i = 0;
+
+ s_config->setGroup( "History" ); // delete the old 2.0.x completion
+ s_config->writeEntry( "CompletionItems", "unused" );
+
+ s_config->setGroup( "Location Bar" );
+ QStringList items = s_config->readPathListEntry( "ComboContents" );
+ QStringList::ConstIterator it = items.begin();
+ QString item;
+ bool first = true;
+ while ( it != items.end() ) {
+ item = *it;
+ if ( !item.isEmpty() ) { // only insert non-empty items
+ if( first ) {
+ insertItem( KonqPixmapProvider::self()->pixmapFor( item, KIcon::SizeSmall ),
+ item, i++, titleOfURL( item ) );
+ }
+ else
+ // icons will be loaded on-demand
+ insertItem( item, i++, titleOfURL( item ) );
+ first = false;
+ }
+ ++it;
+ }
+
+ if ( count() > 0 )
+ m_permanent = true; // we want the first loaded item to stay
+}
+
+void KonqCombo::slotSetIcon( int index )
+{
+ if( pixmap( index ) == NULL )
+ // on-demand icon loading
+ updateItem( KonqPixmapProvider::self()->pixmapFor( text( index ),
+ KIcon::SizeSmall ), text( index ), index,
+ titleOfURL( text( index ) ) );
+ update();
+}
+
+void KonqCombo::popup()
+{
+ for( int i = 0; i < count(); ++i )
+ {
+ if( pixmap( i ) == NULL || pixmap( i )->isNull() )
+ {
+ // on-demand icon loading
+ updateItem( KonqPixmapProvider::self()->pixmapFor( text( i ),
+ KIcon::SizeSmall), text( i ), i, titleOfURL( text( i ) ) );
+ }
+ }
+ KHistoryCombo::popup();
+}
+
+void KonqCombo::saveItems()
+{
+ QStringList items;
+ int i = m_permanent ? 0 : 1;
+
+ for ( ; i < count(); i++ )
+ items.append( text( i ) );
+
+ s_config->setGroup( "Location Bar" );
+ s_config->writePathEntry( "ComboContents", items );
+ KonqPixmapProvider::self()->save( s_config, "ComboIconCache", items );
+
+ s_config->sync();
+}
+
+void KonqCombo::clearTemporary( bool makeCurrent )
+{
+ applyPermanent();
+ changeItem( QString::null, temporary ); // ### default pixmap?
+ if ( makeCurrent )
+ setCurrentItem( temporary );
+}
+
+bool KonqCombo::eventFilter( QObject *o, QEvent *ev )
+{
+ // Handle Ctrl+Del/Backspace etc better than the Qt widget, which always
+ // jumps to the next whitespace.
+ QLineEdit *edit = lineEdit();
+ if ( o == edit ) {
+ int type = ev->type();
+ if ( type == QEvent::KeyPress ) {
+ QKeyEvent *e = static_cast<QKeyEvent *>( ev );
+
+ if ( e->key() == Key_Return || e->key() == Key_Enter ) {
+ m_modifier = e->state();
+ return false;
+ }
+
+ if ( KKey( e ) == KKey( int( KStdAccel::deleteWordBack() ) ) ||
+ KKey( e ) == KKey( int( KStdAccel::deleteWordForward() ) ) ||
+ ((e->state() & ControlButton) &&
+ (e->key() == Key_Left || e->key() == Key_Right) ) ) {
+ selectWord(e);
+ e->accept();
+ return true;
+ }
+ }
+
+ else if ( type == QEvent::MouseButtonDblClick ) {
+ edit->selectAll();
+ return true;
+ }
+ }
+ return KComboBox::eventFilter( o, ev );
+}
+
+void KonqCombo::keyPressEvent( QKeyEvent *e )
+{
+ KHistoryCombo::keyPressEvent( e );
+ // we have to set it as temporary, otherwise we wouldn't get our nice
+ // pixmap. Yes, QComboBox still sucks.
+ if ( KKey( e ) == KKey( int( KStdAccel::rotateUp() ) ) ||
+ KKey( e ) == KKey( int( KStdAccel::rotateDown() ) ) )
+ setTemporary( currentText() );
+}
+
+/*
+ Handle Ctrl+Cursor etc better than the Qt widget, which always
+ jumps to the next whitespace. This code additionally jumps to
+ the next [/#?:], which makes more sense for URLs. The list of
+ chars that will stop the cursor are '/', '.', '?', '#', ':'.
+*/
+void KonqCombo::selectWord(QKeyEvent *e)
+{
+ QLineEdit* edit = lineEdit();
+ QString text = edit->text();
+ int pos = edit->cursorPosition();
+ int pos_old = pos;
+ int count = 0;
+
+ // TODO: make these a parameter when in kdelibs/kdeui...
+ QValueList<QChar> chars;
+ chars << QChar('/') << QChar('.') << QChar('?') << QChar('#') << QChar(':');
+ bool allow_space_break = true;
+
+ if( e->key() == Key_Left || e->key() == Key_Backspace ) {
+ do {
+ pos--;
+ count++;
+ if( allow_space_break && text[pos].isSpace() && count > 1 )
+ break;
+ } while( pos >= 0 && (chars.findIndex(text[pos]) == -1 || count <= 1) );
+
+ if( e->state() & ShiftButton ) {
+ edit->cursorForward(true, 1-count);
+ }
+ else if( e->key() == Key_Backspace ) {
+ edit->cursorForward(false, 1-count);
+ QString text = edit->text();
+ int pos_to_right = edit->text().length() - pos_old;
+ QString cut = text.left(edit->cursorPosition()) + text.right(pos_to_right);
+ edit->setText(cut);
+ edit->setCursorPosition(pos_old-count+1);
+ }
+ else {
+ edit->cursorForward(false, 1-count);
+ }
+ }
+ else if( e->key() == Key_Right || e->key() == Key_Delete ){
+ do {
+ pos++;
+ count++;
+ if( allow_space_break && text[pos].isSpace() )
+ break;
+ } while( pos < (int) text.length() && chars.findIndex(text[pos]) == -1 );
+
+ if( e->state() & ShiftButton ) {
+ edit->cursorForward(true, count+1);
+ }
+ else if( e->key() == Key_Delete ) {
+ edit->cursorForward(false, -count-1);
+ QString text = edit->text();
+ int pos_to_right = text.length() - pos - 1;
+ QString cut = text.left(pos_old) +
+ (pos_to_right > 0 ? text.right(pos_to_right) : QString::null );
+ edit->setText(cut);
+ edit->setCursorPosition(pos_old);
+ }
+ else {
+ edit->cursorForward(false, count+1);
+ }
+ }
+}
+
+void KonqCombo::slotCleared()
+{
+ QByteArray data;
+ QDataStream s( data, IO_WriteOnly );
+ s << kapp->dcopClient()->defaultObject();
+ kapp->dcopClient()->send( "konqueror*", "KonquerorIface", "comboCleared(QCString)", data);
+}
+
+void KonqCombo::removeURL( const QString& url )
+{
+ setUpdatesEnabled( false );
+ lineEdit()->setUpdatesEnabled( false );
+
+ removeFromHistory( url );
+ applyPermanent();
+ setTemporary( currentText() );
+
+ setUpdatesEnabled( true );
+ lineEdit()->setUpdatesEnabled( true );
+ update();
+}
+
+void KonqCombo::mousePressEvent( QMouseEvent *e )
+{
+ m_dragStart = QPoint(); // null QPoint
+
+ if ( e->button() == LeftButton && pixmap( currentItem()) ) {
+ // check if the pixmap was clicked
+ int x = e->pos().x();
+ int x0 = QStyle::visualRect( style().querySubControlMetrics( QStyle::CC_ComboBox, this, QStyle::SC_ComboBoxEditField ), this ).x();
+
+ if ( x > x0 + 2 && x < lineEdit()->x() ) {
+ m_dragStart = e->pos();
+ return; // don't call KComboBox::mousePressEvent!
+ }
+ }
+
+ if ( e->button() == LeftButton && m_pageSecurity!=KonqMainWindow::NotCrypted ) {
+ // check if the lock icon was clicked
+ int x = e->pos().x();
+ int x0 = QStyle::visualRect( style().querySubControlMetrics( QStyle::CC_ComboBox, this, QStyle::SC_ComboBoxArrow ), this ).x();
+ if ( x < x0 )
+ emit showPageSecurity();
+
+ }
+
+ KComboBox::mousePressEvent( e );
+}
+
+void KonqCombo::mouseMoveEvent( QMouseEvent *e )
+{
+ KComboBox::mouseMoveEvent( e );
+ if ( m_dragStart.isNull() || currentText().isEmpty() )
+ return;
+
+ if ( e->state() & LeftButton &&
+ (e->pos() - m_dragStart).manhattanLength() >
+ KGlobalSettings::dndEventDelay() )
+ {
+ KURL url = KURL::fromPathOrURL( currentText() );
+ if ( url.isValid() )
+ {
+ KURL::List list;
+ list.append( url );
+ KURLDrag *drag = new KURLDrag( list, this );
+ QPixmap pix = KonqPixmapProvider::self()->pixmapFor( currentText(),
+ KIcon::SizeMedium );
+ if ( !pix.isNull() )
+ drag->setPixmap( pix );
+ drag->dragCopy();
+ }
+ }
+}
+
+void KonqCombo::slotActivated( const QString& text )
+{
+ //kdDebug(1202) << "KonqCombo::slotActivated: " << text << endl;
+ applyPermanent();
+ m_returnPressed = true;
+ emit activated( text, m_modifier );
+ m_modifier = NoButton;
+}
+
+void KonqCombo::setConfig( KConfig *kc )
+{
+ s_config = kc;
+}
+
+void KonqCombo::paintEvent( QPaintEvent *pe )
+{
+ QComboBox::paintEvent( pe );
+
+ QLineEdit *edit = lineEdit();
+ QRect re = style().querySubControlMetrics( QStyle::CC_ComboBox, this, QStyle::SC_ComboBoxEditField );
+ re = QStyle::visualRect(re, this);
+
+ if ( m_pageSecurity!=KonqMainWindow::NotCrypted ) {
+ QColor color(245, 246, 190);
+ bool useColor = hasSufficientContrast(color,edit->paletteForegroundColor());
+
+ QPainter p( this );
+ p.setClipRect( re );
+
+ QPixmap pix = KonqPixmapProvider::self()->pixmapFor( currentText() );
+ if ( useColor ) {
+ p.fillRect( re.x(), re.y(), pix.width() + 4, re.height(), QBrush( color ));
+ p.drawPixmap( re.x() + 2, re.y() + ( re.height() - pix.height() ) / 2, pix );
+ }
+
+ QRect r = edit->geometry();
+ r.setRight( re.right() - pix.width() - 4 );
+ if ( r != edit->geometry() )
+ edit->setGeometry( r );
+
+ if ( useColor)
+ edit->setPaletteBackgroundColor( color );
+
+ pix = SmallIcon( m_pageSecurity==KonqMainWindow::Encrypted ? "encrypted" : "halfencrypted" );
+ p.fillRect( re.right() - pix.width() - 3 , re.y(), pix.width() + 4, re.height(),
+ QBrush( useColor ? color : edit->paletteBackgroundColor() ));
+ p.drawPixmap( re.right() - pix.width() -1 , re.y() + ( re.height() - pix.height() ) / 2, pix );
+ p.setClipping( FALSE );
+ }
+ else {
+ QRect r = edit->geometry();
+ r.setRight( re.right() );
+ if ( r != edit->geometry() )
+ edit->setGeometry( r );
+ edit->setPaletteBackgroundColor( QApplication::palette( edit ).color( QPalette::Active, QColorGroup::Base ) );
+ }
+}
+
+void KonqCombo::setPageSecurity( int pageSecurity )
+{
+ m_pageSecurity = pageSecurity;
+ repaint();
+}
+
+bool KonqCombo::hasSufficientContrast(const QColor &c1, const QColor &c2)
+{
+ // Taken from khtml/misc/helper.cc
+#define HUE_DISTANCE 40
+#define CONTRAST_DISTANCE 10
+
+ int h1, s1, v1, h2, s2, v2;
+ int hdist = -CONTRAST_DISTANCE;
+ c1.hsv(&h1,&s1,&v1);
+ c2.hsv(&h2,&s2,&v2);
+ if(h1!=-1 && h2!=-1) { // grey values have no hue
+ hdist = kAbs(h1-h2);
+ if (hdist > 180) hdist = 360-hdist;
+ if (hdist < HUE_DISTANCE) {
+ hdist -= HUE_DISTANCE;
+ // see if they are high key or low key colours
+ bool hk1 = h1>=45 && h1<=225;
+ bool hk2 = h2>=45 && h2<=225;
+ if (hk1 && hk2)
+ hdist = (5*hdist)/3;
+ else if (!hk1 && !hk2)
+ hdist = (7*hdist)/4;
+ }
+ hdist = kMin(hdist, HUE_DISTANCE*2);
+ }
+ return hdist + (kAbs(s1-s2)*128)/(160+kMin(s1,s2)) + kAbs(v1-v2) > CONTRAST_DISTANCE;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+KonqComboListBoxPixmap::KonqComboListBoxPixmap( const QString& text )
+ : QListBoxItem()
+{
+ setText( text );
+ lookup_pending = true;
+}
+
+KonqComboListBoxPixmap::KonqComboListBoxPixmap( const QPixmap & pix, const QString& text, const QString& _title )
+ : QListBoxItem()
+{
+ pm = pix;
+ title = _title;
+ setText( text );
+ lookup_pending = false;
+}
+
+void KonqComboListBoxPixmap::paint( QPainter *painter )
+{
+ if ( lookup_pending ) {
+ title = titleOfURL( text() );
+ if ( !title.isEmpty() )
+ pm = KonqPixmapProvider::self()->pixmapFor( text(), KIcon::SizeSmall );
+ else if ( text().find( "://" ) == -1 ) {
+ title = titleOfURL( "http://"+text() );
+ if ( !title.isEmpty() )
+ pm = KonqPixmapProvider::self()->pixmapFor( "http://"+text(), KIcon::SizeSmall );
+ else
+ pm = KonqPixmapProvider::self()->pixmapFor( text(), KIcon::SizeSmall );
+ }
+ else
+ pm = QPixmap();
+ lookup_pending = false;
+ }
+
+ int itemHeight = height( listBox() );
+ int yPos, pmWidth = 0;
+ const QPixmap *pm = pixmap();
+
+ if ( pm && ! pm->isNull() ) {
+ yPos = ( itemHeight - pm->height() ) / 2;
+ painter->drawPixmap( 3, yPos, *pm );
+ pmWidth = pm->width() + 5;
+ }
+
+ int entryWidth = listBox()->width() - listBox()->style().pixelMetric( QStyle::PM_ScrollBarExtent ) -
+ 2 * listBox()->style().pixelMetric( QStyle::PM_DefaultFrameWidth );
+ int titleWidth = ( entryWidth / 3 ) - 1;
+ int urlWidth = entryWidth - titleWidth - pmWidth - 2;
+
+ if ( !text().isEmpty() ) {
+ QString squeezedText = KStringHandler::rPixelSqueeze( text(), listBox()->fontMetrics(), urlWidth );
+ painter->drawText( pmWidth, 0, urlWidth + pmWidth, itemHeight,
+ Qt::AlignLeft | Qt::AlignTop, squeezedText );
+
+ //painter->setPen( KGlobalSettings::inactiveTextColor() );
+ squeezedText = KStringHandler::rPixelSqueeze( title, listBox()->fontMetrics(), titleWidth );
+ QFont font = painter->font();
+ font.setItalic( true );
+ painter->setFont( font );
+ painter->drawText( entryWidth - titleWidth, 0, titleWidth,
+ itemHeight, Qt::AlignLeft | Qt::AlignTop, squeezedText );
+ }
+}
+
+int KonqComboListBoxPixmap::height( const QListBox* lb ) const
+{
+ int h;
+ if ( text().isEmpty() )
+ h = pm.height();
+ else
+ h = QMAX( pm.height(), lb->fontMetrics().lineSpacing() + 2 );
+ return QMAX( h, QApplication::globalStrut().height() );
+}
+
+int KonqComboListBoxPixmap::width( const QListBox* lb ) const
+{
+ if ( text().isEmpty() )
+ return QMAX( pm.width() + 6, QApplication::globalStrut().width() );
+ return QMAX( pm.width() + lb->fontMetrics().width( text() ) + 6,
+ QApplication::globalStrut().width() );
+}
+
+int KonqComboListBoxPixmap::RTTI = 1003;
+
+int KonqComboListBoxPixmap::rtti() const
+{
+ return RTTI;
+}
+
+bool KonqComboListBoxPixmap::reuse( const QString& newText )
+{
+ if ( text() == newText )
+ return false;
+
+ lookup_pending = true;
+ setText( newText );
+ return true;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+KonqComboLineEdit::KonqComboLineEdit( QWidget *parent, const char *name )
+ :KLineEdit( parent, name ) {}
+
+void KonqComboLineEdit::setCompletedItems( const QStringList& items )
+{
+ QString txt;
+ KonqComboCompletionBox *completionbox = static_cast<KonqComboCompletionBox*>( completionBox() );
+
+ if ( completionbox && completionbox->isVisible() )
+ // The popup is visible already - do the matching on the initial string,
+ // not on the currently selected one.
+ txt = completionbox->cancelledText();
+ else
+ txt = text();
+
+ if ( !items.isEmpty() && !(items.count() == 1 && txt == items.first()) ) {
+ if ( !completionBox( false ) )
+ setCompletionBox( new KonqComboCompletionBox( this, "completion box" ) );
+
+ if ( completionbox->isVisible() ) {
+ bool wasSelected = completionbox->isSelected( completionbox->currentItem() );
+ const QString currentSelection = completionbox->currentText();
+ completionbox->setItems( items );
+ QListBoxItem* item = completionbox->findItem( currentSelection, Qt::ExactMatch );
+ if( !item || !wasSelected )
+ {
+ wasSelected = false;
+ item = completionbox->item( 0 );
+ }
+ if ( item ) {
+ completionbox->blockSignals( true );
+ completionbox->setCurrentItem( item );
+ completionbox->setSelected( item, wasSelected );
+ completionbox->blockSignals( false );
+ }
+ }
+ else { // completion box not visible yet -> show it
+ if ( !txt.isEmpty() )
+ completionbox->setCancelledText( txt );
+ completionbox->setItems( items );
+ completionbox->popup();
+ }
+
+ if ( autoSuggest() ) {
+ int index = items.first().find( txt );
+ QString newText = items.first().mid( index );
+ setUserSelection( false );
+ setCompletedText( newText, true );
+ }
+ }
+ else
+ if ( completionbox && completionbox->isVisible() )
+ completionbox->hide();
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+KonqComboCompletionBox::KonqComboCompletionBox( QWidget *parent, const char *name )
+ :KCompletionBox( parent, name ) {}
+
+void KonqComboCompletionBox::setItems( const QStringList& items )
+{
+ bool block = signalsBlocked();
+ blockSignals( true );
+
+ QListBoxItem* item = firstItem();
+ if ( !item )
+ insertStringList( items );
+ else {
+ //Keep track of whether we need to change anything,
+ //so we can avoid a repaint for identical updates,
+ //to reduce flicker
+ bool dirty = false;
+
+ QStringList::ConstIterator it = items.constBegin();
+ const QStringList::ConstIterator itEnd = items.constEnd();
+
+ for ( ; it != itEnd; ++it) {
+ if ( item ) {
+ const bool changed = ((KonqComboListBoxPixmap*)item)->reuse( *it );
+ dirty = dirty || changed;
+ item = item->next();
+ }
+ else {
+ dirty = true;
+ //Inserting an item is a way of making this dirty
+ insertItem( new KonqComboListBoxPixmap( *it ) );
+ }
+ }
+
+ //If there is an unused item, mark as dirty -> less items now
+ if ( item )
+ dirty = true;
+
+ QListBoxItem* tmp = item;
+ while ( (item = tmp ) ) {
+ tmp = item->next();
+ delete item;
+ }
+
+ if ( dirty )
+ triggerUpdate( false );
+ }
+
+ if ( isVisible() && size().height() != sizeHint().height() )
+ sizeAndPosition();
+
+ blockSignals( block );
+
+ // Trigger d->down_workaround = true within KCompletionBox
+ QStringList dummy;
+ KCompletionBox::insertItems( dummy, 1 );
+}
+
+void KonqComboCompletionBox::insertStringList( const QStringList & list, int index )
+{
+ if ( index < 0 )
+ index = count();
+ for ( QStringList::ConstIterator it = list.begin(); it != list.end(); ++it )
+ insertItem( new KonqComboListBoxPixmap( *it ), index++ );
+}
+
+#include "konq_combo.moc"