1 /* This file is part of the KDE libraries
2 Copyright (C) 1999 Torben Weis <weis@kde.org>
4 This library is free software; you can redistribute it and/or
5 modify it under the terms of the GNU Library General Public
6 License version 2 as published by the Free Software Foundation.
8 This library is distributed in the hope that it will be useful,
9 but WITHOUT ANY WARRANTY; without even the implied warranty of
10 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11 Library General Public License for more details.
13 You should have received a copy of the GNU Library General Public License
14 along with this library; see the file COPYING.LIB. If not, write to
15 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
16 Boston, MA 02110-1301, USA.
19 #include "k3iconview.h"
23 #include <QtCore/QTimer>
24 #include <QtGui/QPainter>
25 #include <QtGui/QPixmapCache>
26 #include <QtGui/QActionEvent>
28 #include "kwordwrap.h"
32 #include <kiconeffect.h>
33 #include <kglobalsettings.h>
36 #include <QApplication>
38 class K3IconView::K3IconViewPrivate
42 mode
= K3IconView::Execute
;
48 K3IconView::Mode mode
;
51 QPixmapCache maskCache
;
53 Q3IconViewItem
*dragHoldItem
;
55 QTimer doubleClickIgnoreTimer
;
58 K3IconView::K3IconView( QWidget
*parent
, const char *name
, Qt::WFlags f
)
59 : Q3IconView( parent
, name
, f
)
61 d
= new K3IconViewPrivate
;
63 connect( this, SIGNAL( onViewport() ),
64 this, SLOT( slotOnViewport() ) );
65 connect( this, SIGNAL( onItem( Q3IconViewItem
* ) ),
66 this, SLOT( slotOnItem( Q3IconViewItem
* ) ) );
67 slotSettingsChanged( KGlobalSettings::SETTINGS_MOUSE
);
68 connect( KGlobalSettings::self(), SIGNAL( settingsChanged(int) ), SLOT( slotSettingsChanged(int) ) );
72 m_pAutoSelect
= new QTimer( this );
73 connect( m_pAutoSelect
, SIGNAL( timeout() ),
74 this, SLOT( slotAutoSelect() ) );
76 connect( &d
->dragHoldTimer
, SIGNAL(timeout()), this, SLOT(slotDragHoldTimeout()) );
79 K3IconView::~K3IconView()
86 void K3IconView::setMode( K3IconView::Mode mode
)
91 K3IconView::Mode
K3IconView::mode() const
96 void K3IconView::slotOnItem( Q3IconViewItem
*item
)
100 if ( m_bChangeCursorOverItem
)
101 viewport()->setCursor(QCursor(Qt::PointingHandCursor
));
103 if ( (m_autoSelectDelay
> -1) ) {
104 m_pAutoSelect
->setSingleShot( true );
105 m_pAutoSelect
->start( m_autoSelectDelay
);
108 m_pCurrentItem
= item
;
112 void K3IconView::slotOnViewport()
114 if ( m_bUseSingle
&& m_bChangeCursorOverItem
)
115 viewport()->unsetCursor();
117 m_pAutoSelect
->stop();
121 void K3IconView::slotSettingsChanged(int category
)
123 if ( category
!= KGlobalSettings::SETTINGS_MOUSE
)
125 m_bUseSingle
= KGlobalSettings::singleClick();
126 //kDebug() << "K3IconView::slotSettingsChanged for mouse, usesingle=" << m_bUseSingle;
128 disconnect( this, SIGNAL( mouseButtonClicked( int, Q3IconViewItem
*,
130 this, SLOT( slotMouseButtonClicked( int, Q3IconViewItem
*,
131 const QPoint
& ) ) );
132 // disconnect( this, SIGNAL( doubleClicked( QIconViewItem *,
133 // const QPoint & ) ),
134 // this, SLOT( slotExecute( QIconViewItem *,
135 // const QPoint & ) ) );
138 connect( this, SIGNAL( mouseButtonClicked( int, Q3IconViewItem
*,
140 this, SLOT( slotMouseButtonClicked( int, Q3IconViewItem
*,
141 const QPoint
& ) ) );
144 // connect( this, SIGNAL( doubleClicked( QIconViewItem *,
145 // const QPoint & ) ),
146 // this, SLOT( slotExecute( QIconViewItem *,
147 // const QPoint & ) ) );
150 m_bChangeCursorOverItem
= KGlobalSettings::changeCursorOverIcon();
151 m_autoSelectDelay
= m_bUseSingle
? KGlobalSettings::autoSelectDelay() : -1;
153 if( !m_bUseSingle
|| !m_bChangeCursorOverItem
)
154 viewport()->unsetCursor();
157 void K3IconView::slotAutoSelect()
159 // check that the item still exists
160 if( index( m_pCurrentItem
) == -1 || !d
->doAutoSelect
)
163 //Give this widget the keyboard focus.
167 Qt::KeyboardModifiers keybstate
= QApplication::keyboardModifiers();
168 Q3IconViewItem
* previousItem
= currentItem();
169 setCurrentItem( m_pCurrentItem
);
171 if( m_pCurrentItem
) {
173 if( (keybstate
& Qt::ShiftModifier
) ) {
174 //Temporary implementation of the selection until QIconView supports it
175 bool block
= signalsBlocked();
176 blockSignals( true );
178 //No Ctrl? Then clear before!
179 if( !(keybstate
& Qt::ControlModifier
) )
182 bool select
= !m_pCurrentItem
->isSelected();
183 bool update
= viewport()->updatesEnabled();
184 viewport()->setUpdatesEnabled( false );
186 //Calculate the smallest rectangle that contains the current Item
187 //and the one that got the autoselect event
190 if ( previousItem
) {
191 r
= QRect( qMin( previousItem
->x(), m_pCurrentItem
->x() ),
192 qMin( previousItem
->y(), m_pCurrentItem
->y() ),
194 if ( previousItem
->x() < m_pCurrentItem
->x() )
195 r
.setWidth( m_pCurrentItem
->x() - previousItem
->x() + m_pCurrentItem
->width() );
197 r
.setWidth( previousItem
->x() - m_pCurrentItem
->x() + previousItem
->width() );
198 if ( previousItem
->y() < m_pCurrentItem
->y() )
199 r
.setHeight( m_pCurrentItem
->y() - previousItem
->y() + m_pCurrentItem
->height() );
201 r
.setHeight( previousItem
->y() - m_pCurrentItem
->y() + previousItem
->height() );
205 r
= QRect( 0, 0, 0, 0 );
207 //Check for each item whether it is within the rectangle.
209 for( Q3IconViewItem
* i
= firstItem(); i
; i
= i
->nextItem() ) {
210 if( i
->intersects( r
) ) {
211 redraw
= redraw
.unite( i
->rect() );
212 setSelected( i
, select
, true );
216 blockSignals( block
);
217 viewport()->setUpdatesEnabled( update
);
218 repaintContents( redraw
, false );
220 emit
selectionChanged();
222 if( selectionMode() == Q3IconView::Single
)
223 emit
selectionChanged( m_pCurrentItem
);
225 //setSelected( m_pCurrentItem, true, (keybstate & ControlButton), (keybstate & ShiftButton) );
227 else if( (keybstate
& Qt::ControlModifier
) )
228 setSelected( m_pCurrentItem
, !m_pCurrentItem
->isSelected(), true );
230 setSelected( m_pCurrentItem
, true );
233 kDebug() << "K3IconView: That's not supposed to happen!!!!";
236 void K3IconView::emitExecute( Q3IconViewItem
*item
, const QPoint
&pos
)
238 if ( d
->mode
!= Execute
)
240 // kDebug() << "K3IconView::emitExecute : not in execute mode !";
244 Qt::KeyboardModifiers keybstate
= QApplication::keyboardModifiers();
246 m_pAutoSelect
->stop();
248 //Don't emit executed if in SC mode and Shift or Ctrl are pressed
249 if( !( m_bUseSingle
&& ((keybstate
& Qt::ShiftModifier
) || (keybstate
& Qt::ControlModifier
)) ) ) {
250 setSelected( item
, false );
251 viewport()->unsetCursor();
252 emit
executed( item
);
253 emit
executed( item
, pos
);
257 void K3IconView::updateDragHoldItem( QDropEvent
*e
)
259 Q3IconViewItem
*item
= findItem( e
->pos() );
261 if ( d
->dragHoldItem
!= item
)
263 d
->dragHoldItem
= item
;
266 d
->dragHoldTimer
.setSingleShot( true );
267 d
->dragHoldTimer
.start( 1000 );
271 d
->dragHoldTimer
.stop();
276 void K3IconView::focusOutEvent( QFocusEvent
*fe
)
278 m_pAutoSelect
->stop();
280 Q3IconView::focusOutEvent( fe
);
283 void K3IconView::leaveEvent( QEvent
*e
)
285 m_pAutoSelect
->stop();
287 Q3IconView::leaveEvent( e
);
290 void K3IconView::contentsMousePressEvent( QMouseEvent
*e
)
292 if( (selectionMode() == Extended
) && (e
->modifiers() & Qt::ShiftModifier
) && !(e
->modifiers() & Qt::ControlModifier
) ) {
293 bool block
= signalsBlocked();
294 blockSignals( true );
298 blockSignals( block
);
301 Q3IconView::contentsMousePressEvent( e
);
302 d
->doAutoSelect
= false;
305 void K3IconView::contentsMouseDoubleClickEvent ( QMouseEvent
* e
)
307 Q3IconView::contentsMouseDoubleClickEvent( e
);
309 Q3IconViewItem
* item
= findItem( e
->pos() );
312 if( (e
->button() == Qt::LeftButton
) && !m_bUseSingle
)
313 emitExecute( item
, e
->globalPos() );
315 emit
doubleClicked( item
, e
->globalPos() );
317 d
->doubleClickIgnoreTimer
.setSingleShot(true);
318 d
->doubleClickIgnoreTimer
.start(0);
321 void K3IconView::slotMouseButtonClicked( int btn
, Q3IconViewItem
*item
, const QPoint
&pos
)
323 //kDebug() << " K3IconView::slotMouseButtonClicked() item=" << item;
324 if( d
->doubleClickIgnoreTimer
.isActive() )
325 return; // Ignore double click
327 if( (btn
== Qt::LeftButton
) && item
)
328 emitExecute( item
, pos
);
331 void K3IconView::contentsMouseReleaseEvent( QMouseEvent
*e
)
333 d
->doAutoSelect
= true;
334 Q3IconView::contentsMouseReleaseEvent( e
);
337 void K3IconView::contentsDragEnterEvent( QDragEnterEvent
*e
)
339 updateDragHoldItem( e
);
340 Q3IconView::contentsDragEnterEvent( e
);
343 void K3IconView::contentsDragLeaveEvent( QDragLeaveEvent
*e
)
345 d
->dragHoldTimer
.stop();
346 d
->dragHoldItem
= 0L;
347 Q3IconView::contentsDragLeaveEvent( e
);
351 void K3IconView::contentsDragMoveEvent( QDragMoveEvent
*e
)
353 updateDragHoldItem( e
);
354 Q3IconView::contentsDragMoveEvent( e
);
357 void K3IconView::contentsDropEvent( QDropEvent
* e
)
359 d
->dragHoldTimer
.stop();
360 Q3IconView::contentsDropEvent( e
);
363 void K3IconView::slotDragHoldTimeout()
365 Q3IconViewItem
*tmp
= d
->dragHoldItem
;
366 d
->dragHoldItem
= 0L;
371 void K3IconView::takeItem( Q3IconViewItem
* item
)
373 if ( item
== d
->dragHoldItem
)
375 d
->dragHoldTimer
.stop();
376 d
->dragHoldItem
= 0L;
379 Q3IconView::takeItem( item
);
382 void K3IconView::cancelPendingHeldSignal()
384 d
->dragHoldTimer
.stop();
385 d
->dragHoldItem
= 0L;
388 void K3IconView::wheelEvent( QWheelEvent
*e
)
390 if (horizontalScrollBar() && (arrangement() == Q3IconView::TopToBottom
)) {
391 QWheelEvent
ce(e
->pos(), e
->delta(), e
->buttons(), e
->modifiers(), Qt::Horizontal
);
392 QApplication::sendEvent( horizontalScrollBar(), &ce
);
393 if (ce
.isAccepted()) {
398 Q3IconView::wheelEvent(e
);
401 void K3IconView::setFont( const QFont
&font
)
405 Q3IconView::setFont( font
);
408 QFontMetrics
*K3IconView::itemFontMetrics() const
411 // QIconView creates one too, but we can't access it
412 d
->fm
= new QFontMetrics( font() );
417 QPixmap
K3IconView::selectedIconPixmap( QPixmap
*pix
, const QColor
&col
) const
420 if ( d
->maskCache
.find( QString::number( pix
->serialNumber() ), m
) )
427 p
.setCompositionMode(QPainter::CompositionMode_SourceAtop
);
428 p
.fillRect(m
.rect(), h
);
431 d
->maskCache
.insert( QString::number( pix
->serialNumber() ), m
);
435 int K3IconView::iconTextHeight() const
437 return d
->textHeight
> 0 ? d
->textHeight
: ( wordWrapIconText() ? 99 : 1 );
440 void K3IconView::setIconTextHeight( int n
)
442 int oldHeight
= iconTextHeight();
448 // so that Qt still shows the tooltip when even a wrapped text is too long
449 setWordWrapIconText( false );
451 // update view if needed
452 if ( iconTextHeight() != oldHeight
)
453 setFont( font() ); // hack to recalc items
458 struct K3IconViewItem::K3IconViewItemPrivate
463 void K3IconViewItem::init()
470 K3IconViewItem::~K3IconViewItem()
476 void K3IconViewItem::calcRect( const QString
& text_
)
478 Q_ASSERT( iconView() );
483 #ifndef NDEBUG // be faster for the end-user, such a bug will have been fixed before hand :)
484 if ( !qobject_cast
<K3IconView
*>(iconView()) )
486 kWarning() << "K3IconViewItem used in a " << iconView()->metaObject()->className() << " !!";
490 //kDebug() << "K3IconViewItem::calcRect - " << text();
491 K3IconView
*view
= static_cast<K3IconView
*>(iconView());
492 QRect itemIconRect
= pixmapRect();
493 QRect itemTextRect
= textRect();
494 QRect itemRect
= rect();
499 #ifndef QT_NO_PICTURE
501 QRect br
= picture()->boundingRect();
503 ph
= br
.height() + 2;
507 // Qt uses unknown_icon if no pixmap. Let's see if we need that - I doubt it
510 pw
= pixmap()->width() + 2;
511 ph
= pixmap()->height() + 2;
513 itemIconRect
.setWidth( pw
);
515 // There is a bug in Qt which prevents the item from being placed
516 // properly when the pixmapRect is not at the top of the itemRect, so we
517 // have to increase the height of the pixmapRect and leave it at the top
518 // of the itemRect...
519 if ( d
&& !d
->m_pixmapSize
.isNull() )
520 itemIconRect
.setHeight( d
->m_pixmapSize
.height() + 2 );
523 itemIconRect
.setHeight( ph
);
526 if ( d
&& !d
->m_pixmapSize
.isNull() )
527 tw
= view
->maxItemWidth() - ( view
->itemTextPos() == Q3IconView::Bottom
? 0 :
528 d
->m_pixmapSize
.width() + 2 );
530 tw
= view
->maxItemWidth() - ( view
->itemTextPos() == Q3IconView::Bottom
? 0 :
531 itemIconRect
.width() );
533 QFontMetrics
*fm
= view
->itemFontMetrics();
537 // When is text_ set ? Doesn't look like it's ever set.
538 t
= text_
.isEmpty() ? text() : text_
;
541 int nbLines
= static_cast<K3IconView
*>( iconView() )->iconTextHeight();
542 int height
= nbLines
> 0 ? fm
->height() * nbLines
: 0xFFFFFFFF;
544 // Should not be higher than pixmap if text is alongside icons
545 if ( view
->itemTextPos() != Q3IconView::Bottom
) {
546 if ( d
&& !d
->m_pixmapSize
.isNull() )
547 height
= qMin( d
->m_pixmapSize
.height() + 2, height
);
549 height
= qMin( itemIconRect
.height(), height
);
550 height
= qMax( height
, fm
->height() );
553 // Calculate the word-wrap
554 QRect
outerRect( 0, 0, tw
- 6, height
);
555 m_wordWrap
= KWordWrap::formatText( *fm
, outerRect
, 0, t
);
556 r
= m_wordWrap
->boundingRect();
558 int realWidth
= qMax( qMin( r
.width() + 4, tw
), fm
->width( "X" ) );
559 itemTextRect
.setWidth( realWidth
);
560 itemTextRect
.setHeight( r
.height() );
562 int w
= 0; int h
= 0; int y
= 0;
563 if ( view
->itemTextPos() == Q3IconView::Bottom
) {
564 // If the pixmap size has been specified, use it
565 if ( d
&& !d
->m_pixmapSize
.isNull() )
567 w
= qMax( itemTextRect
.width(), d
->m_pixmapSize
.width() + 2 );
568 h
= itemTextRect
.height() + d
->m_pixmapSize
.height() + 2 + 1;
570 // Waiting for the qt bug to be solved, the pixmapRect must
571 // stay on the top...
572 y
= d
->m_pixmapSize
.height() + 2 - itemIconRect
.height();
576 w
= qMax( itemTextRect
.width(), itemIconRect
.width() );
577 h
= itemTextRect
.height() + itemIconRect
.height() + 1;
580 itemRect
.setWidth( w
);
581 itemRect
.setHeight( h
);
582 int width
= qMax( w
, QApplication::globalStrut().width() ); // see QIconViewItem::width()
583 int height
= qMax( h
, QApplication::globalStrut().height() ); // see QIconViewItem::height()
584 itemTextRect
= QRect( ( width
- itemTextRect
.width() ) / 2, height
- itemTextRect
.height(),
585 itemTextRect
.width(), itemTextRect
.height() );
586 itemIconRect
= QRect( ( width
- itemIconRect
.width() ) / 2, y
,
587 itemIconRect
.width(), itemIconRect
.height() );
589 // If the pixmap size has been specified, use it
590 if ( d
&& !d
->m_pixmapSize
.isNull() )
592 h
= qMax( itemTextRect
.height(), d
->m_pixmapSize
.height() + 2 );
594 // Waiting for the qt bug to be solved, the pixmapRect must
595 // stay on the top...
596 y
= ( d
->m_pixmapSize
.height() + 2 - itemIconRect
.height() ) / 2;
600 h
= qMax( itemTextRect
.height(), itemIconRect
.height() );
601 w
= itemTextRect
.width() + itemIconRect
.width() + 1;
603 itemRect
.setWidth( w
);
604 itemRect
.setHeight( h
);
605 int width
= qMax( w
, QApplication::globalStrut().width() ); // see QIconViewItem::width()
606 int height
= qMax( h
, QApplication::globalStrut().height() ); // see QIconViewItem::height()
608 itemTextRect
= QRect( width
- itemTextRect
.width(), ( height
- itemTextRect
.height() ) / 2,
609 itemTextRect
.width(), itemTextRect
.height() );
610 if ( itemIconRect
.height() > itemTextRect
.height() ) // icon bigger than text -> center vertically
611 itemIconRect
= QRect( 0, ( height
- itemIconRect
.height() ) / 2,
612 itemIconRect
.width(), itemIconRect
.height() );
613 else // icon smaller than text -> place in top or center with first line
614 itemIconRect
= QRect( 0, qMax(( fm
->height() - itemIconRect
.height() ) / 2 + y
, 0),
615 itemIconRect
.width(), itemIconRect
.height() );
616 if ( ( itemIconRect
.height() <= 20 ) && ( itemTextRect
.height() < itemIconRect
.height() ) )
618 itemTextRect
.setHeight( itemIconRect
.height() - 2 );
619 itemTextRect
.setY( itemIconRect
.y() );
623 if ( itemIconRect
!= pixmapRect() )
624 setPixmapRect( itemIconRect
);
625 if ( itemTextRect
!= textRect() )
626 setTextRect( itemTextRect
);
627 if ( itemRect
!= rect() )
628 setItemRect( itemRect
);
630 // Done by setPixmapRect, setTextRect and setItemRect ! [and useless if no rect changed]
631 //view->updateItemContainer( this );
635 void K3IconViewItem::paintItem( QPainter
*p
, const QColorGroup
&cg
)
637 Q3IconView
* view
= iconView();
641 #ifndef NDEBUG // be faster for the end-user, such a bug will have been fixed before hand :)
642 if ( !qobject_cast
<K3IconView
*>(view
) )
644 kWarning() << "K3IconViewItem used in a " << view
->metaObject()->className() << " !!";
657 KWordWrap
* K3IconViewItem::wordWrap()
662 void K3IconViewItem::paintPixmap( QPainter
*p
, const QColorGroup
&cg
)
664 K3IconView
*kview
= static_cast<K3IconView
*>(iconView());
666 #ifndef QT_NO_PICTURE
668 QPicture
*pic
= picture();
669 if ( isSelected() ) {
670 // TODO something as nice as selectedIconPixmap if possible ;)
671 p
->fillRect( pixmapRect( false ), QBrush( cg
.color(QPalette::Highlight
), Qt::Dense4Pattern
) );
673 p
->drawPicture( x()-pic
->boundingRect().x(), y()-pic
->boundingRect().y(), *pic
);
677 int iconX
= pixmapRect( false ).x();
678 int iconY
= pixmapRect( false ).y();
680 QPixmap
*pix
= pixmap();
681 if ( !pix
|| pix
->isNull() )
685 // Move the pixmap manually because the pixmapRect is at the
686 // top of the itemRect
687 // (won't be needed anymore in future versions of qt)
688 if ( d
&& !d
->m_pixmapSize
.isNull() )
691 if ( kview
->itemTextPos() == Q3IconView::Bottom
)
692 offset
= d
->m_pixmapSize
.height() - pix
->height();
694 offset
= ( d
->m_pixmapSize
.height() - pix
->height() ) / 2;
699 if ( isSelected() ) {
700 QPixmap selectedPix
= kview
->selectedIconPixmap( pix
, cg
.color( QPalette::Highlight
) );
701 p
->drawPixmap( iconX
, iconY
, selectedPix
);
703 p
->drawPixmap( iconX
, iconY
, *pix
);
708 void K3IconViewItem::paintText( QPainter
*p
, const QColorGroup
&cg
)
710 int textX
= textRect( false ).x() + 2;
711 int textY
= textRect( false ).y();
713 if ( isSelected() ) {
714 p
->fillRect( textRect( false ), cg
.color( QPalette::Highlight
) );
715 p
->setPen( QPen( cg
.color( QPalette::HighlightedText
) ) );
717 if ( iconView()->itemTextBackground() != Qt::NoBrush
)
718 p
->fillRect( textRect( false ), iconView()->itemTextBackground() );
719 p
->setPen( cg
.color( QPalette::Text
) );
722 int align
= iconView()->itemTextPos() == Q3IconView::Bottom
? Qt::AlignHCenter
: Qt::AlignLeft
;
723 m_wordWrap
->drawText( p
, textX
, textY
, align
| KWordWrap::Truncate
);
726 QSize
K3IconViewItem::pixmapSize() const
728 return d
? d
->m_pixmapSize
: QSize( 0, 0 );
731 void K3IconViewItem::setPixmapSize( const QSize
& size
)
734 d
= new K3IconViewItemPrivate
;
736 d
->m_pixmapSize
= size
;
739 #include "k3iconview.moc"