1 /* This file is part of the KDE project
2 Copyright (C) 2001 Carsten Pfeiffer <pfeiffer@kde.org>
3 Copyright (C) 2007 Fredrik Höglund <fredrik@kde.org>
5 This program is free software; you can redistribute it and/or
6 modify it under the terms of the GNU General Public
7 License as published by the Free Software Foundation; either
8 version 2 of the License, or (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program; see the file COPYING. If not, write to
17 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18 Boston, MA 02110-1301, USA.
22 #include "konqcombo.h"
25 #include <QtGui/QPainter>
26 #include <QtGui/QStyle>
27 #include <QtGui/QPixmap>
28 #include <QtGui/QKeyEvent>
29 #include <QtGui/QItemDelegate>
30 #include <QtGui/QListWidgetItem>
31 #include <QtCore/QEvent>
34 #include <kapplication.h>
36 #include <kconfiggroup.h>
37 #include <kcompletionbox.h>
39 #include <kiconloader.h>
40 #include <kicontheme.h>
41 #include <klineedit.h>
42 #include <konqpixmapprovider.h>
43 #include <kstandardshortcut.h>
44 #include <konqmainwindow.h>
45 #include <kstringhandler.h>
49 #include "KonquerorAdaptor.h"
50 #include "konqueror_interface.h"
51 #include "konqhistorymanager.h"
53 KConfig
* KonqCombo::s_config
= 0L;
54 const int KonqCombo::temporary
= 0;
56 static QString
titleOfURL( const QString
& urlStr
)
59 KonqHistoryList historylist
= KonqHistoryManager::kself()->entries();
60 KonqHistoryList::iterator historyentry
= historylist
.findEntry( url
);
61 if ( historyentry
== historylist
.end() && !url
.url().endsWith('/') ) {
62 url
.adjustPath(KUrl::AddTrailingSlash
);
63 historyentry
= historylist
.findEntry( url
);
65 return ( historyentry
!= historylist
.end() ? (*historyentry
).title
: QString() );
68 ///////////////////////////////////////////////////////////////////////////////
70 class KonqListWidgetItem
: public QListWidgetItem
73 enum ItemType
{ KonqItemType
= 0x1845D5CC };
75 KonqListWidgetItem( QListWidget
*parent
= 0 );
76 KonqListWidgetItem( const QString
&text
, QListWidget
*parent
= 0 );
78 QVariant
data( int role
) const;
80 bool reuse( const QString
&newText
);
83 mutable bool lookupPending
;
86 ///////////////////////////////////////////////////////////////////////////////
88 class KonqComboItemDelegate
: public QItemDelegate
91 KonqComboItemDelegate( QObject
*parent
) : QItemDelegate( parent
) {}
92 QSize
sizeHint( const QStyleOptionViewItem
&option
, const QModelIndex
&index
) const;
93 void paint( QPainter
*painter
, const QStyleOptionViewItem
&option
, const QModelIndex
&index
) const;
96 ///////////////////////////////////////////////////////////////////////////////
98 class KonqComboLineEdit
: public KLineEdit
101 KonqComboLineEdit( QWidget
*parent
=0 );
102 void setCompletedItems( const QStringList
& items
, bool );
105 void mouseDoubleClickEvent( QMouseEvent
*e
);
108 class KonqComboCompletionBox
: public KCompletionBox
111 KonqComboCompletionBox( QWidget
*parent
);
112 void setItems( const QStringList
& items
);
113 void insertStringList( const QStringList
& list
, int index
= -1 );
116 KonqCombo::KonqCombo( QWidget
*parent
)
117 : KHistoryComboBox( parent
),
118 m_returnPressed( false ),
119 m_permanent( false ),
120 m_pageSecurity( KonqMainWindow::NotCrypted
)
122 setLayoutDirection(Qt::LeftToRight
);
123 setInsertPolicy( NoInsert
);
124 setSizePolicy( QSizePolicy( QSizePolicy::Expanding
, QSizePolicy::Fixed
));
125 setSizeAdjustPolicy( QComboBox::AdjustToMinimumContentsLength
);
127 Q_ASSERT( s_config
);
129 KConfigGroup
locationBarGroup( s_config
, "Location Bar" );
130 setMaxCount( locationBarGroup
.readEntry("Maximum of URLs in combo", 20 ));
132 // We should also connect the completionBox' highlighted signal to
133 // our setEditText() slot, because we're handling the signals ourselves.
134 // But we're lazy and let KCompletionBox do this and simply switch off
135 // handling of signals later.
136 setHandleSignals( true );
138 KonqComboLineEdit
*edit
= new KonqComboLineEdit( this );
139 edit
->setHandleSignals( true );
140 edit
->setCompletionBox( new KonqComboCompletionBox( edit
) );
142 setItemDelegate( new KonqComboItemDelegate( this ) );
144 // we use KUrlCompletion, so let KLineEdit handle Key_Tab so that subdir<tab>/subdir<tab> works (#65877).
145 // KCompletionBox's handling of Key_Tab is just the same as Up/Down keys, not very useful.
146 completionBox()->setTabHandling(false);
147 completionBox()->setItemDelegate( new KonqComboItemDelegate( this ) );
149 // Make the lineedit consume the Qt::Key_Enter event...
150 setTrapReturnKey( true );
152 connect( KonqHistoryManager::kself(), SIGNAL(cleared()), SLOT(slotCleared()) );
153 connect( this, SIGNAL(cleared() ), SLOT(slotCleared()) );
154 connect( this, SIGNAL(highlighted( int )), SLOT(slotSetIcon( int )) );
155 connect( this, SIGNAL(activated( const QString
& )),
156 SLOT(slotActivated( const QString
& )) );
159 KonqCombo::~KonqCombo()
163 void KonqCombo::init( KCompletion
*completion
)
165 setCompletionObject( completion
, false ); //KonqMainWindow handles signals
166 setAutoDeleteCompletionObject( false );
167 setCompletionMode( completion
->completionMode() );
172 void KonqCombo::setURL( const QString
& url
)
174 //kDebug(1202) << url << "returnPressed=" << m_returnPressed;
177 if ( m_returnPressed
) { // Really insert...
178 m_returnPressed
= false;
179 QDBusMessage message
= QDBusMessage::createSignal( KONQ_MAIN_PATH
, "org.kde.Konqueror.Main", "addToCombo" );
181 QDBusConnection::sessionBus().send( message
);
183 // important security consideration: always display the beginning
184 // of the url rather than its end to prevent spoofing attempts.
185 lineEdit()->setCursorPosition( 0 );
188 void KonqCombo::setTemporary( const QString
& text
)
190 setTemporary( text
, KonqPixmapProvider::self()->pixmapFor(text
) );
193 void KonqCombo::setTemporary( const QString
& url
, const QPixmap
& pix
)
195 //kDebug(1202) << url << "temporary=" << temporary;
197 // Insert a temporary item when we don't have one yet
199 insertItem( pix
, url
, temporary
, titleOfURL( url
) );
202 if (url
!= temporaryItem())
205 updateItem( pix
, url
, temporary
, titleOfURL( url
) );
208 setCurrentIndex( temporary
);
211 void KonqCombo::removeDuplicates( int index
)
213 //kDebug(1202) << "starting index= " << index;
215 QString
url(temporaryItem());
216 if (url
.endsWith('/'))
217 url
.truncate(url
.length()-1);
219 // Remove all dupes, if available...
220 for ( int i
= index
; i
< count(); i
++ )
222 QString
item(itemText(i
));
223 if (item
.endsWith('/'))
224 item
.truncate(item
.length()-1);
231 // called via DBUS in all instances
232 void KonqCombo::insertPermanent( const QString
& url
)
234 //kDebug(1202) << "url=" << url;
241 // called right before a new (different!) temporary item will be set. So we
242 // insert an item that was marked permanent properly at position 1.
243 void KonqCombo::applyPermanent()
245 if ( m_permanent
&& !temporaryItem().isEmpty() ) {
247 // Remove as many items as needed to honor maxCount()
249 while ( count() >= maxCount() )
250 removeItem( --index
);
252 QString item
= temporaryItem();
253 insertItem( KonqPixmapProvider::self()->pixmapFor( item
), item
, 1, titleOfURL( item
) );
254 //kDebug(1202) << url;
256 // Remove all duplicates starting from index = 2
257 removeDuplicates( 2 );
262 void KonqCombo::insertItem( const QString
&text
, int index
, const QString
& title
)
264 KHistoryComboBox::insertItem( index
, text
, title
);
267 void KonqCombo::insertItem( const QPixmap
&pixmap
, const QString
& text
, int index
, const QString
& title
)
269 KHistoryComboBox::insertItem( index
, pixmap
, text
, title
);
272 void KonqCombo::updateItem( const QPixmap
& pix
, const QString
& t
, int index
, const QString
& title
)
274 // No need to flicker
275 if (itemText( index
) == t
&&
276 (!itemIcon(index
).isNull() && itemIcon(index
).pixmap(iconSize()).serialNumber() == pix
.serialNumber()))
279 // kDebug(1202) << "item=" << t << "index=" << index;
281 setItemText( index
, t
);
282 setItemIcon( index
, pix
);
283 setItemData( index
, title
);
288 void KonqCombo::saveState()
290 m_cursorPos
= cursorPosition();
291 m_currentText
= currentText();
292 m_currentIndex
= currentIndex();
295 void KonqCombo::restoreState()
297 setTemporary( m_currentText
);
298 lineEdit()->setCursorPosition( m_cursorPos
);
301 void KonqCombo::updatePixmaps()
305 setUpdatesEnabled( false );
306 KonqPixmapProvider
*prov
= KonqPixmapProvider::self();
307 for ( int i
= 1; i
< count(); i
++ ) {
308 setItemIcon( i
, prov
->pixmapFor( itemText( i
) ) );
310 setUpdatesEnabled( true );
316 void KonqCombo::loadItems()
321 KConfigGroup
historyConfigGroup( s_config
, "History" ); // delete the old 2.0.x completion
322 historyConfigGroup
.writeEntry( "CompletionItems", "unused" );
324 KConfigGroup
locationBarGroup( s_config
, "Location Bar" );
325 const QStringList items
= locationBarGroup
.readPathEntry( "ComboContents", QStringList() );
326 QStringList::ConstIterator it
= items
.begin();
328 while ( it
!= items
.end() ) {
330 if ( !item
.isEmpty() ) { // only insert non-empty items
331 insertItem( KonqPixmapProvider::self()->pixmapFor( item
, KIconLoader::SizeSmall
),
332 item
, i
++, titleOfURL( item
) );
338 m_permanent
= true; // we want the first loaded item to stay
341 void KonqCombo::slotSetIcon( int index
)
343 if( itemIcon( index
).isNull())
344 // on-demand icon loading
345 setItemIcon( index
, KonqPixmapProvider::self()->pixmapFor( itemText( index
),
346 KIconLoader::SizeSmall
) );
350 void KonqCombo::getStyleOption(QStyleOptionComboBox
* comboOpt
)
352 //We only use this for querying metrics,so it's rough..
353 comboOpt
->init(this);
354 comboOpt
->editable
= isEditable();
355 comboOpt
->frame
= hasFrame();
356 comboOpt
->iconSize
= iconSize();
357 comboOpt
->currentIcon
= itemIcon(currentIndex());
358 comboOpt
->currentText
= currentText();
361 void KonqCombo::popup()
363 for( int i
= 0; i
< count(); ++i
)
365 if( itemIcon( i
).isNull() )
367 // on-demand icon loading
368 setItemIcon( i
, KonqPixmapProvider::self()->pixmapFor( itemText( i
),
369 KIconLoader::SizeSmall
) );
372 KHistoryComboBox::showPopup();
375 void KonqCombo::saveItems()
378 int i
= m_permanent
? 0 : 1;
380 for ( ; i
< count(); i
++ )
381 items
.append( itemText( i
) );
383 KConfigGroup
locationBarGroup( s_config
, "Location Bar" );
384 locationBarGroup
.writePathEntry( "ComboContents", items
);
385 KonqPixmapProvider::self()->save( locationBarGroup
, "ComboIconCache", items
);
390 void KonqCombo::clearTemporary( bool makeCurrent
)
393 setItemText( temporary
, QString() ); // ### default pixmap?
395 setCurrentIndex( temporary
);
398 bool KonqCombo::eventFilter( QObject
*o
, QEvent
*ev
)
400 // Handle Ctrl+Del/Backspace etc better than the Qt widget, which always
401 // jumps to the next whitespace.
402 QLineEdit
*edit
= lineEdit();
404 const int type
= ev
->type();
405 if (type
== QEvent::KeyPress
) {
406 QKeyEvent
*e
= static_cast<QKeyEvent
*>( ev
);
408 KShortcut
key( e
->key() | e
->modifiers() );
410 if ( key
== KStandardShortcut::deleteWordBack() ||
411 key
== KStandardShortcut::deleteWordForward() ||
412 ((e
->modifiers() & Qt::ControlModifier
) &&
413 (e
->key() == Qt::Key_Left
|| e
->key() == Qt::Key_Right
) ) ) {
420 else if ( type
== QEvent::MouseButtonDblClick
) {
425 return KComboBox::eventFilter( o
, ev
);
428 void KonqCombo::keyPressEvent( QKeyEvent
*e
)
430 KHistoryComboBox::keyPressEvent( e
);
431 // we have to set it as temporary, otherwise we wouldn't get our nice
432 // pixmap. Yes, QComboBox still sucks.
433 KShortcut
key( e
->key() | e
->modifiers() );
434 if ( key
== KStandardShortcut::rotateUp() ||
435 key
== KStandardShortcut::rotateDown() )
436 setTemporary( currentText() );
440 Handle Ctrl+Cursor etc better than the Qt widget, which always
441 jumps to the next whitespace. This code additionally jumps to
442 the next [/#?:], which makes more sense for URLs. The list of
443 chars that will stop the cursor are '/', '.', '?', '#', ':'.
445 void KonqCombo::selectWord(QKeyEvent
*e
)
447 QLineEdit
* edit
= lineEdit();
448 QString text
= edit
->text();
449 int pos
= edit
->cursorPosition();
453 // TODO: make these a parameter when in kdelibs/kdeui...
455 chars
<< QChar('/') << QChar('.') << QChar('?') << QChar('#') << QChar(':');
456 bool allow_space_break
= true;
458 if( e
->key() == Qt::Key_Left
|| e
->key() == Qt::Key_Backspace
) {
462 if( allow_space_break
&& text
[pos
].isSpace() && count
> 1 )
464 } while( pos
>= 0 && (chars
.indexOf(text
[pos
]) == -1 || count
<= 1) );
466 if( e
->modifiers() & Qt::ShiftModifier
) {
467 edit
->cursorForward(true, 1-count
);
469 else if( e
->key() == Qt::Key_Backspace
) {
470 edit
->cursorForward(false, 1-count
);
471 QString text
= edit
->text();
472 int pos_to_right
= edit
->text().length() - pos_old
;
473 QString cut
= text
.left(edit
->cursorPosition()) + text
.right(pos_to_right
);
475 edit
->setCursorPosition(pos_old
-count
+1);
478 edit
->cursorForward(false, 1-count
);
481 else if( e
->key() == Qt::Key_Right
|| e
->key() == Qt::Key_Delete
){
485 if( allow_space_break
&& text
[pos
].isSpace() )
487 } while( pos
< (int) text
.length() && chars
.indexOf(text
[pos
]) == -1 );
489 if( e
->modifiers() & Qt::ShiftModifier
) {
490 edit
->cursorForward(true, count
+1);
492 else if( e
->key() == Qt::Key_Delete
) {
493 edit
->cursorForward(false, -count
-1);
494 QString text
= edit
->text();
495 int pos_to_right
= text
.length() - pos
- 1;
496 QString cut
= text
.left(pos_old
) +
497 (pos_to_right
> 0 ? text
.right(pos_to_right
) : QString() );
499 edit
->setCursorPosition(pos_old
);
502 edit
->cursorForward(false, count
+1);
507 void KonqCombo::slotCleared()
509 QDBusMessage message
= QDBusMessage::createSignal( KONQ_MAIN_PATH
, "org.kde.Konqueror.Main", "comboCleared" );
510 QDBusConnection::sessionBus().send( message
);
513 void KonqCombo::removeURL( const QString
& url
)
515 setUpdatesEnabled( false );
516 lineEdit()->setUpdatesEnabled( false );
518 removeFromHistory( url
);
520 setTemporary( currentText() );
522 setUpdatesEnabled( true );
523 lineEdit()->setUpdatesEnabled( true );
527 void KonqCombo::mousePressEvent( QMouseEvent
*e
)
529 m_dragStart
= QPoint(); // null QPoint
531 if ( e
->button() == Qt::LeftButton
&& !itemIcon( currentIndex()).isNull() ) {
532 // check if the pixmap was clicked
533 int x
= e
->pos().x();
534 QStyleOptionComboBox comboOpt
;
535 getStyleOption(&comboOpt
);
536 int x0
= QStyle::visualRect( layoutDirection(), rect(),
537 style()->subControlRect( QStyle::CC_ComboBox
, &comboOpt
, QStyle::SC_ComboBoxEditField
,
540 if ( x
> x0
+ 2 && x
< lineEdit()->x() ) {
541 m_dragStart
= e
->pos();
542 return; // don't call KComboBox::mousePressEvent!
546 QStyleOptionComboBox optCombo
;
547 optCombo
.initFrom(this);
548 if ( e
->button() == Qt::LeftButton
&& m_pageSecurity
!= KonqMainWindow::NotCrypted
&&
549 style()->subElementRect( QStyle::SE_ComboBoxFocusRect
, &optCombo
, this ).contains( e
->pos() ) )
550 emit
showPageSecurity();
552 KComboBox::mousePressEvent( e
);
555 void KonqCombo::mouseMoveEvent( QMouseEvent
*e
)
557 KComboBox::mouseMoveEvent( e
);
558 if ( m_dragStart
.isNull() || currentText().isEmpty() )
561 if ( e
->buttons() & Qt::LeftButton
&&
562 (e
->pos() - m_dragStart
).manhattanLength() >
563 KGlobalSettings::dndEventDelay() )
565 KUrl
url( currentText() );
568 QDrag
* drag
= new QDrag(this);
569 QMimeData
* mime
= new QMimeData();
570 url
.populateMimeData(mime
);
571 drag
->setMimeData(mime
);
572 QPixmap pix
= KonqPixmapProvider::self()->pixmapFor( currentText(),
573 KIconLoader::SizeMedium
);
575 drag
->setPixmap( pix
);
581 void KonqCombo::slotActivated( const QString
& text
)
584 m_returnPressed
= true;
585 emit
activated( text
, qApp
->keyboardModifiers() );
588 void KonqCombo::setConfig( KConfig
*kc
)
593 void KonqCombo::paintEvent( QPaintEvent
*pe
)
595 QComboBox::paintEvent( pe
);
597 QLineEdit
*edit
= lineEdit();
599 QStyleOptionComboBox comboOpt
;
600 getStyleOption(&comboOpt
);
601 QRect re
= style()->subControlRect( QStyle::CC_ComboBox
, &comboOpt
,
602 QStyle::SC_ComboBoxEditField
, this );
603 re
= QStyle::visualRect(layoutDirection(), rect(), re
);
605 if ( m_pageSecurity
!=KonqMainWindow::NotCrypted
) {
606 QColor
color(245, 246, 190);
607 // From George: disabled for now. We're going to match IE behavior in
609 // 1) No color, but padlock for SSL verified
610 // 2) No padlock for non-verified SSL
611 // 3) Green fill, padlock, and site info for high-assurance verified
612 // 4) If available, red fill for known phishing site
613 bool useColor
= false; //hasSufficientContrast(color,edit->paletteForegroundColor());
618 QPixmap pix
= KonqPixmapProvider::self()->pixmapFor( currentText() );
620 p
.fillRect( re
.x(), re
.y(), pix
.width() + 4, re
.height(), QBrush( color
));
621 p
.drawPixmap( re
.x() + 2, re
.y() + ( re
.height() - pix
.height() ) / 2, pix
);
624 QRect r
= edit
->geometry();
625 r
.setRight( re
.right() - pix
.width() - 4 );
626 if ( r
!= edit
->geometry() )
627 edit
->setGeometry( r
);
631 palette
.setColor(edit
->backgroundRole(), color
);
632 edit
->setPalette(palette
);
635 pix
= SmallIcon( m_pageSecurity
==KonqMainWindow::Encrypted
? "security-high" : "security-medium" );
637 p
.fillRect( re
.right() - pix
.width() - 3 , re
.y(), pix
.width() + 4, re
.height(),
640 p
.drawPixmap( re
.right() - pix
.width() -1 , re
.y() + ( re
.height() - pix
.height() ) / 2, pix
);
641 p
.setClipping( false );
643 QRect r
= edit
->geometry();
644 r
.setRight( re
.right() );
645 if ( r
!= edit
->geometry() )
646 edit
->setGeometry( r
);
647 QPalette palette
= edit
->palette();
648 palette
.setColor(edit
->backgroundRole(), palette
.color(QPalette::Active
, QPalette::Base
));
649 edit
->setPalette(palette
);
653 void KonqCombo::setPageSecurity( int pageSecurity
)
655 int ops
= m_pageSecurity
;
656 m_pageSecurity
= pageSecurity
;
657 if (ops
!= pageSecurity
)
661 bool KonqCombo::hasSufficientContrast(const QColor
&c1
, const QColor
&c2
)
663 // Taken from khtml/misc/helper.cc
664 #define HUE_DISTANCE 40
665 #define CONTRAST_DISTANCE 10
667 int h1
, s1
, v1
, h2
, s2
, v2
;
668 int hdist
= -CONTRAST_DISTANCE
;
669 c1
.getHsv(&h1
,&s1
,&v1
);
670 c2
.getHsv(&h2
,&s2
,&v2
);
671 if(h1
!=-1 && h2
!=-1) { // grey values have no hue
673 if (hdist
> 180) hdist
= 360-hdist
;
674 if (hdist
< HUE_DISTANCE
) {
675 hdist
-= HUE_DISTANCE
;
676 // see if they are high key or low key colours
677 bool hk1
= h1
>=45 && h1
<=225;
678 bool hk2
= h2
>=45 && h2
<=225;
681 else if (!hk1
&& !hk2
)
684 hdist
= qMin(hdist
, HUE_DISTANCE
*2);
686 return hdist
+ (qAbs(s1
-s2
)*128)/(160+qMin(s1
,s2
)) + qAbs(v1
-v2
) > CONTRAST_DISTANCE
;
689 ///////////////////////////////////////////////////////////////////////////////
691 KonqListWidgetItem::KonqListWidgetItem( QListWidget
*parent
)
692 : QListWidgetItem( parent
, KonqItemType
), lookupPending( true )
696 KonqListWidgetItem::KonqListWidgetItem( const QString
&text
, QListWidget
*parent
)
697 : QListWidgetItem( text
, parent
, KonqItemType
), lookupPending( true )
701 QVariant
KonqListWidgetItem::data( int role
) const
703 if ( lookupPending
&& role
!= Qt::DisplayRole
)
705 QString title
= titleOfURL( text() );
708 KonqPixmapProvider
*provider
= KonqPixmapProvider::self();
710 if ( !title
.isEmpty() )
711 pixmap
= provider
->pixmapFor( text(), KIconLoader::SizeSmall
);
712 else if ( text().indexOf( "://" ) == -1 ) {
713 title
= titleOfURL( "http://"+text() );
714 if ( !title
.isEmpty() )
715 pixmap
= provider
->pixmapFor( "http://"+text(), KIconLoader::SizeSmall
);
717 pixmap
= provider
->pixmapFor( text(), KIconLoader::SizeSmall
);
720 const_cast<KonqListWidgetItem
*>( this )->setIcon( pixmap
);
721 const_cast<KonqListWidgetItem
*>( this )->setData( Qt::UserRole
, title
);
723 lookupPending
= false;
726 return QListWidgetItem::data( role
);
729 bool KonqListWidgetItem::reuse(const QString
&newText
)
731 if ( text() == newText
)
734 lookupPending
= true;
739 ///////////////////////////////////////////////////////////////////////////////
741 QSize
KonqComboItemDelegate::sizeHint( const QStyleOptionViewItem
&option
,
742 const QModelIndex
&index
) const
745 int vMargin
= QApplication::style()->pixelMetric( QStyle::PM_FocusFrameVMargin
);
747 QSize
size( 1, qMax( option
.fontMetrics
.lineSpacing(), option
.decorationSize
.height() ) );
748 size
.rheight() += vMargin
* 2;
750 return size
.expandedTo( QApplication::globalStrut() );
753 void KonqComboItemDelegate::paint( QPainter
*painter
, const QStyleOptionViewItem
&option
,
754 const QModelIndex
&index
) const
756 QIcon icon
= qvariant_cast
<QIcon
>( index
.data( Qt::DecorationRole
) );
757 QString url
= index
.data( Qt::DisplayRole
).toString();
758 QString title
= index
.data( Qt::UserRole
).toString();
760 QIcon::Mode mode
= option
.state
& QStyle::State_Enabled
? QIcon::Normal
: QIcon::Disabled
;
761 const QSize size
= icon
.actualSize( option
.decorationSize
, mode
);
762 QPixmap pixmap
= icon
.pixmap( size
, mode
);
764 QStyleOptionViewItemV3
opt( option
);
768 // Draw the item background
770 // ### When Qt 4.4 is released we need to change this code to draw the background
771 // by calling QStyle::drawPrimitive() with PE_PanelItemViewRow.
772 if ( opt
.state
& QStyle::State_Selected
) {
773 painter
->fillRect( option
.rect
, option
.palette
.brush( QPalette::Highlight
) );
774 painter
->setPen( QPen( option
.palette
.brush( QPalette::HighlightedText
), 0 ) );
777 int hMargin
= QApplication::style()->pixelMetric( QStyle::PM_FocusFrameHMargin
);
778 int vMargin
= QApplication::style()->pixelMetric( QStyle::PM_FocusFrameVMargin
);
780 const QRect bounding
= option
.rect
.adjusted( hMargin
, vMargin
, -hMargin
, -vMargin
);
781 const QSize
textSize( bounding
.width() - pixmap
.width() - 2, bounding
.height() );
782 const QRect pixmapRect
= QStyle::alignedRect( option
.direction
, Qt::AlignLeft
| Qt::AlignVCenter
,
783 pixmap
.size(), bounding
);
784 const QRect textRect
= QStyle::alignedRect( option
.direction
, Qt::AlignRight
, textSize
, bounding
);
786 if ( !pixmap
.isNull() )
787 painter
->drawPixmap( pixmapRect
.topLeft(), pixmap
);
789 const QSize
titleSize( ( bounding
.width() / 3 ) - 1, textRect
.height() );
790 const QSize
urlSize( textRect
.width() - titleSize
.width() - 2, textRect
.height() );
791 const QRect titleRect
= QStyle::alignedRect( option
.direction
, Qt::AlignRight
, titleSize
, textRect
);
792 const QRect urlRect
= QStyle::alignedRect( option
.direction
, Qt::AlignLeft
, urlSize
, textRect
);
794 if ( !url
.isEmpty() ) {
795 QString squeezedText
= option
.fontMetrics
.elidedText( url
, Qt::ElideRight
, urlRect
.width() );
796 painter
->drawText( urlRect
, Qt::AlignLeft
| Qt::AlignVCenter
, squeezedText
);
799 if ( !title
.isEmpty() ) {
800 QString squeezedText
= option
.fontMetrics
.elidedText( title
, Qt::ElideRight
, titleRect
.width() );
801 QFont font
= painter
->font();
802 font
.setItalic( true );
803 painter
->setFont( font
);
804 QColor color
= painter
->pen().color();
805 color
.setAlphaF(.75);
806 painter
->setPen(color
);
807 painter
->drawText( titleRect
, Qt::AlignLeft
| Qt::AlignVCenter
, squeezedText
);
813 ///////////////////////////////////////////////////////////////////////////////
815 KonqComboLineEdit::KonqComboLineEdit( QWidget
*parent
)
818 setClearButtonShown( true );
821 void KonqComboLineEdit::mouseDoubleClickEvent( QMouseEvent
*e
)
823 if ( e
->button() == Qt::LeftButton
) {
827 KLineEdit::mouseDoubleClickEvent( e
);
830 void KonqComboLineEdit::setCompletedItems( const QStringList
& items
, bool )
833 KonqComboCompletionBox
*completionbox
= static_cast<KonqComboCompletionBox
*>( completionBox() );
835 if ( completionbox
&& completionbox
->isVisible() )
836 // The popup is visible already - do the matching on the initial string,
837 // not on the currently selected one.
838 txt
= completionbox
->cancelledText();
842 if ( !items
.isEmpty() && !(items
.count() == 1 && txt
== items
.first()) ) {
843 if ( !completionBox( false ) ) {
844 setCompletionBox( new KonqComboCompletionBox( this ) );
845 completionbox
= static_cast<KonqComboCompletionBox
*>( completionBox() );
848 if ( completionbox
->isVisible() ) {
850 QListWidgetItem
* currentItem
= completionbox
->currentItem();
851 bool wasSelected
= false;
852 QString currentSelection
;
854 if ( currentItem
!= 0 ) {
855 wasSelected
= currentItem
->isSelected();
856 currentSelection
= currentItem
->text();
859 completionbox
->setItems( items
);
860 QList
<QListWidgetItem
*> matchedItems
= completionbox
->findItems
861 ( currentSelection
, Qt::MatchExactly
);
863 QListWidgetItem
* item
= matchedItems
.isEmpty() ? 0 : matchedItems
.first();
865 if( !item
|| !wasSelected
)
868 item
= completionbox
->item( 0 );
871 completionbox
->blockSignals( true );
872 completionbox
->setCurrentItem( item
);
873 item
->setSelected(wasSelected
);
874 completionbox
->blockSignals( false );
877 else { // completion box not visible yet -> show it
878 if ( !txt
.isEmpty() )
879 completionbox
->setCancelledText( txt
);
880 completionbox
->setItems( items
);
881 completionbox
->popup();
884 if ( autoSuggest() ) {
885 int index
= items
.first().indexOf( txt
);
886 QString newText
= items
.first().mid( index
);
887 setUserSelection( false );
888 setCompletedText( newText
, true );
892 if ( completionbox
&& completionbox
->isVisible() )
893 completionbox
->hide();
896 ///////////////////////////////////////////////////////////////////////////////
898 KonqComboCompletionBox::KonqComboCompletionBox( QWidget
*parent
)
899 :KCompletionBox( parent
)
901 setLayoutDirection(Qt::LeftToRight
);
904 void KonqComboCompletionBox::setItems( const QStringList
& items
)
906 bool block
= signalsBlocked();
907 blockSignals( true );
912 insertStringList( items
);
914 //Keep track of whether we need to change anything,
915 //so we can avoid a repaint for identical updates,
919 QStringList::ConstIterator it
= items
.constBegin();
920 const QStringList::ConstIterator itEnd
= items
.constEnd();
922 for ( ; it
!= itEnd
; ++it
) {
923 if ( rowIndex
< count() ) {
924 const bool changed
= ((KonqListWidgetItem
*)item(rowIndex
))->reuse( *it
);
925 dirty
= dirty
|| changed
;
929 //Inserting an item is a way of making this dirty
930 addItem( new KonqListWidgetItem( *it
) );
935 //If there is an unused item, mark as dirty -> less items now
936 if ( rowIndex
< count() )
939 while ( rowIndex
< count() ) {
940 delete item(rowIndex
);
943 //TODO KDE 4 - Port this
945 // triggerUpdate( false );
948 if ( isVisible() && size().height() != sizeHint().height() )
951 blockSignals( block
);
953 // Trigger d->down_workaround = true within KCompletionBox
955 KCompletionBox::insertItems( dummy
, 1 );
958 void KonqComboCompletionBox::insertStringList( const QStringList
& list
, int index
)
963 foreach ( const QString
&text
, list
)
964 insertItem( index
++, new KonqListWidgetItem( text
) );
966 #include "konqcombo.moc"