Send correct mouse wheel events when scaling is enabled.
[kdenetwork.git] / knewsticker / itemviews.cpp
blob1e1d3697775bf52832c35cb5b5eafc5aa7d4766a
1 /*
2 * itemviews.cpp
4 * Copyright (c) 2007 Frerich Raabe <raabe@kde.org>
6 * This program is distributed in the hope that it will be useful, but WITHOUT
7 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
8 * FOR A PARTICULAR PURPOSE. For licensing and distribution details, check the
9 * accompanying file 'COPYING'.
11 #include "itemviews.h"
12 #include "knewsticker.h"
14 #include "settings.h"
16 #include <QCursor>
17 #include <QFontMetrics>
18 #include <QTimeLine>
19 #include <QTimer>
21 #include <KIconLoader>
22 #include <KLocale>
23 #include <KMimeType>
25 NewsItemView::NewsItemView( KNewsTicker *parent )
26 : QObject( 0 ),
27 QGraphicsRectItem( parent ),
28 m_newsTicker( parent )
32 const QList<NewsItem> &NewsItemView::items() const
34 return m_items;
37 void NewsItemView::setItems( const QList<NewsItem> &items )
39 m_items = items;
42 void NewsItemView::reloadSettings()
46 HyperlinkItem::HyperlinkItem( const QString &text, const QUrl &url,
47 QGraphicsItem *parentItem )
48 : QObject(), QGraphicsSimpleTextItem( text, parentItem ),
49 m_url( url )
51 setFont( Settings::font() );
52 setBrush( Settings::color() );
53 setCursor( Qt::PointingHandCursor );
54 setAcceptedMouseButtons( Qt::LeftButton );
55 setAcceptsHoverEvents( true );
58 void HyperlinkItem::hoverEnterEvent( QGraphicsSceneHoverEvent * )
60 if ( m_url.isEmpty() ) {
61 return;
64 QFont f = font();
65 f.setUnderline( true );
66 setFont( f );
67 setBrush( Qt::red );
70 void HyperlinkItem::hoverLeaveEvent( QGraphicsSceneHoverEvent * )
72 if ( m_url.isEmpty() ) {
73 return;
76 QFont f = font();
77 f.setUnderline( false );
78 setFont( f );
79 setBrush( Settings::color() );
82 void HyperlinkItem::mouseReleaseEvent( QGraphicsSceneMouseEvent * )
84 if ( m_url.isEmpty() ) {
85 return;
88 emit activated( m_url.toString() );
91 SeparatorItem::SeparatorItem( QGraphicsItem *parentItem )
92 : QGraphicsSimpleTextItem( " +++ ", parentItem )
94 setBrush( Settings::color() );
95 setFont( Settings::font() );
98 NewsTickerItem::NewsTickerItem( const QString &text, const QUrl &url,
99 const QString &description,
100 QGraphicsItem *parentItem )
101 : QGraphicsItemGroup( parentItem )
103 setHandlesChildEvents( false );
105 qreal xpos = 0;
107 QGraphicsItem *pi = 0;
108 const QString favIcon = KMimeType::favIconForUrl( url );
109 if ( !favIcon.isEmpty() ) {
110 pi = new QGraphicsPixmapItem( SmallIcon( favIcon ), this );
111 addToGroup( pi );
112 xpos += boundingRect().width() + 8;
115 m_headlineItem = new HyperlinkItem( text, url, this );
116 m_headlineItem->setPos( xpos, 0 );
117 m_headlineItem->setToolTip( description );
118 addToGroup( m_headlineItem );
120 m_separatorItem = new SeparatorItem( this );
121 m_separatorItem->setPos( boundingRect().width(), 0 );
122 addToGroup( m_separatorItem );
124 if ( pi ) {
125 pi->setPos( 0, ( boundingRect().height() - pi->boundingRect().height() ) / 2 );
129 ScrollingItemView::ScrollingItemView( KNewsTicker *parent )
130 : NewsItemView( parent ),
131 m_steppingWidth( 1.0 )
133 m_scrollTimer = new QTimer( this );
134 connect( m_scrollTimer, SIGNAL( timeout() ),
135 this, SLOT( advance() ) );
136 configureScrollTimer();
137 m_scrollTimer->start();
140 void ScrollingItemView::setItems( const QList<NewsItem> &items )
142 NewsItemView::setItems( items );
143 relayoutItems();
146 void ScrollingItemView::relayoutItems()
148 qDeleteAll( m_graphicsItems );
149 m_graphicsItems.clear();
151 if ( items().empty() ) {
152 return;
155 qreal ypos = ( boundingRect().height() - QFontMetrics( Settings::font() ).height() ) / 2;
156 qreal xpos = 0;
157 QList<NewsItem>::ConstIterator it, end = items().end();
158 for ( it = items().begin(); it != end; ++it ) {
159 addItemToLayout( *it, &xpos, &ypos );
162 /* In case none of the available items were added to the scroll text (this
163 * can happen if all of them have been read), add a filler item which tells
164 * the user that no unread news are available.
166 if ( m_graphicsItems.isEmpty() ) {
167 NewsItem item;
168 item.text = i18n( "No unread news available" );
170 addItemToLayout( item, &xpos, &ypos );
171 const qreal firstItemWidth = m_graphicsItems.first()->boundingRect().width();
172 while ( xpos < boundingRect().width() + firstItemWidth ) { // XXX
173 addItemToLayout( item, &xpos, &ypos );
175 return;
178 const qreal firstItemWidth = m_graphicsItems.first()->boundingRect().width();
180 it = items().begin();
181 while ( xpos < boundingRect().width() + firstItemWidth ) { // XXX
182 addItemToLayout( *it, &xpos, &ypos );
184 if ( ++it == items().end() ) {
185 it = items().begin();
190 void ScrollingItemView::addItemToLayout( const NewsItem &item, qreal *xpos, qreal *ypos )
192 if ( newsTicker()->hideArticle( item.url ) ) {
193 return;
196 NewsTickerItem *i = new NewsTickerItem( item.text, item.url, item.description,
197 this );
198 connect( i->headlineItem(), SIGNAL( activated( const QString & ) ),
199 this, SIGNAL( itemActivated( const QString & ) ) );
200 i->setPos( *xpos, *ypos );
201 m_graphicsItems.append( i );
202 *xpos += i->boundingRect().width();
205 void ScrollingItemView::advance()
207 if ( m_graphicsItems.isEmpty() ) {
208 return;
211 const qreal ypos = ( boundingRect().height() - QFontMetrics( Settings::font() ).height() ) / 2; // XXX
213 /* In case an item scrolled out to the left, take it out of the m_graphicsItems list
214 * and insert it at the end again. Also, move it's position to the very end (determine
215 * the end by adding up the widths of the remaining items).
217 NewsTickerItem *firstItem = m_graphicsItems.first();
218 while ( firstItem->x() + firstItem->boundingRect().width() < 0 ) {
219 m_graphicsItems.erase( m_graphicsItems.begin() );
221 /* If we just scrolled an item out of view which we read already,
222 * don't show it again in case the user configured this.
224 if ( newsTicker()->hideArticle( firstItem->headlineItem()->url() ) ) {
225 delete firstItem;
226 } else {
227 qreal xpos = 0;
228 foreach ( QGraphicsItem *item, m_graphicsItems ) {
229 xpos += item->boundingRect().width();
231 firstItem->setPos( xpos, ypos );
232 m_graphicsItems.append( firstItem );
234 firstItem = m_graphicsItems.first();
237 foreach ( NewsTickerItem *item, m_graphicsItems ) {
238 item->moveBy( m_steppingWidth * -1, 0 );
242 void ScrollingItemView::configureScrollTimer()
244 if ( Settings::scrollingSpeed() < 25 ) {
245 m_scrollTimer->setInterval( 25 );
246 m_steppingWidth = 25.0 / Settings::scrollingSpeed();
247 } else {
248 m_scrollTimer->setInterval( Settings::scrollingSpeed() );
249 m_steppingWidth = 1.0;
253 void ScrollingItemView::reloadSettings()
255 foreach ( NewsTickerItem *item, m_graphicsItems ) {
256 item->headlineItem()->setBrush( Settings::color() );
257 item->separatorItem()->setBrush( Settings::color() );
259 configureScrollTimer();
262 PagingItemView::PagingItemView( KNewsTicker *parent )
263 : NewsItemView( parent ),
264 m_needToReloadSettings( false )
266 m_linkItem = new HyperlinkItem( QString(), QUrl(), this );
268 QTimeLine *moveInTimer = new QTimeLine( 800,this );
269 connect( moveInTimer, SIGNAL( finished() ),
270 this, SLOT( itemMovedIn() ) );
271 moveInTimer->setCurveShape( QTimeLine::EaseOutCurve );
272 moveInTimer->setFrameRange( 0, 100 );
274 m_moveInAnimation = new QGraphicsItemAnimation;
275 m_moveInAnimation->setItem( m_linkItem );
276 m_moveInAnimation->setTimeLine( moveInTimer );
278 QTimeLine *moveOutTimer = new QTimeLine( 800,this );
279 connect( moveOutTimer, SIGNAL( finished() ),
280 this, SLOT( itemMovedOut() ) );
281 moveOutTimer->setCurveShape( QTimeLine::EaseInCurve );
282 moveOutTimer->setFrameRange( 0, 100 );
284 m_moveOutAnimation = new QGraphicsItemAnimation;
285 m_moveOutAnimation->setItem( m_linkItem );
286 m_moveOutAnimation->setTimeLine( moveOutTimer );
289 PagingItemView::~PagingItemView()
291 m_moveInAnimation->timeLine()->stop();
292 m_moveOutAnimation->timeLine()->stop();
295 void PagingItemView::updateMoveAnimations()
297 m_moveInAnimation->clear();
298 m_moveOutAnimation->clear();
299 switch ( Settings::pagingDirection() ) {
300 case Settings::EnumPagingDirection::Upwards:
301 case Settings::EnumPagingDirection::Downwards: {
302 const qreal itemHeight = m_linkItem->boundingRect().height();
303 const qreal height = boundingRect().height();
304 const qreal xpos = ( boundingRect().width() - m_linkItem->boundingRect().width() ) / 2;
306 const qreal topY = -itemHeight;
307 const qreal centerY = ( height - itemHeight ) / 2;
308 const qreal bottomY = height;
310 if ( Settings::pagingDirection() == Settings::EnumPagingDirection::Upwards ) {
311 for ( qreal y = bottomY; y > centerY; --y ) {
312 const qreal step = double( bottomY - y ) / ( bottomY - centerY );
313 m_moveInAnimation->setPosAt( step, QPointF( xpos, y ) );
316 for ( qreal y = centerY; y > topY; --y ) {
317 const qreal step = double( centerY - y ) / ( centerY - topY );
318 m_moveOutAnimation->setPosAt( step, QPointF( xpos, y ) );
320 } else {
321 for ( qreal y = topY; y < centerY; ++y ) {
322 const qreal step = double( y - topY ) / ( centerY - topY );
323 m_moveInAnimation->setPosAt( step, QPointF( xpos, y ) );
326 for ( qreal y = centerY; y < bottomY; ++y ) {
327 const qreal step = double( y - centerY ) / ( bottomY - centerY );
328 m_moveOutAnimation->setPosAt( step, QPointF( xpos, y ) );
332 break;
334 case Settings::EnumPagingDirection::Leftwards:
335 case Settings::EnumPagingDirection::Rightwards: {
336 const qreal itemWidth = m_linkItem->boundingRect().width();
337 const qreal width = boundingRect().width();
338 const qreal ypos = ( boundingRect().height() - m_linkItem->boundingRect().height() ) / 2;
340 const qreal leftX = -itemWidth;
341 const qreal centerX = ( width - itemWidth ) / 2;
342 const qreal rightX = width;
344 if ( Settings::pagingDirection() == Settings::EnumPagingDirection::Leftwards ) {
345 for ( qreal x = rightX; x > centerX; --x ) {
346 const qreal step = double( rightX - x ) / ( rightX - centerX );
347 m_moveInAnimation->setPosAt( step, QPointF( x, ypos ) );
350 for ( qreal x = centerX; x > leftX; --x ) {
351 const qreal step = double( centerX - x ) / ( centerX - leftX );
352 m_moveOutAnimation->setPosAt( step, QPointF( x, ypos ) );
354 } else {
355 for ( qreal x = leftX; x < centerX; ++x ) {
356 const qreal step = double( x - leftX ) / ( centerX - leftX );
357 m_moveInAnimation->setPosAt( step, QPointF( x, ypos ) );
360 for ( qreal x = centerX; x < rightX; ++x ) {
361 const qreal step = double( x - centerX ) / ( rightX - centerX );
362 m_moveOutAnimation->setPosAt( step, QPointF( x, ypos ) );
366 break;
368 case Settings::EnumPagingDirection::COUNT:
369 // XXX Ahem; generated by kconfig_compiler for internal reasons.
370 break;
374 void PagingItemView::setItems( const QList<NewsItem> &items_ )
376 NewsItemView::setItems( items_ );
377 m_currentItem = items().begin();
378 updateLinkItem();
379 updateMoveAnimations();
380 moveItemIn();
383 void PagingItemView::moveItemIn()
385 m_moveInAnimation->reset();
386 m_moveInAnimation->timeLine()->start();
389 void PagingItemView::itemMovedIn()
391 if ( !items().isEmpty() ) {
392 QTimer::singleShot( Settings::pagingInterval() * 1000, this, SLOT( moveItemOut() ) );
396 void PagingItemView::moveItemOut()
398 m_moveOutAnimation->reset();
399 m_moveOutAnimation->timeLine()->start();
402 void PagingItemView::itemMovedOut()
404 if ( m_currentItem == items().end() || ++m_currentItem == items().end() ) {
405 m_currentItem = items().begin();
407 updateLinkItem();
408 if ( m_needToReloadSettings ) {
409 m_linkItem->setBrush( Settings::color() );
410 m_linkItem->setFont( Settings::font() );
411 m_needToReloadSettings = false;
413 updateMoveAnimations();
414 moveItemIn();
417 void PagingItemView::reloadSettings()
419 m_needToReloadSettings = true;
422 void PagingItemView::updateLinkItem()
424 if ( m_currentItem == items().end() ) {
425 m_linkItem->setText( i18n( "No unread news available" ) );
426 m_linkItem->setUrl( QString() );
427 m_linkItem->setToolTip( QString() );
428 } else {
429 m_linkItem->setText( ( *m_currentItem ).text );
430 m_linkItem->setUrl( ( *m_currentItem ).url );
431 m_linkItem->setToolTip( ( *m_currentItem ).description );
435 #include "itemviews.moc"