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"
17 #include <QFontMetrics>
21 #include <KIconLoader>
25 NewsItemView::NewsItemView( KNewsTicker
*parent
)
27 QGraphicsRectItem( parent
),
28 m_newsTicker( parent
)
32 const QList
<NewsItem
> &NewsItemView::items() const
37 void NewsItemView::setItems( const QList
<NewsItem
> &items
)
42 void NewsItemView::reloadSettings()
46 HyperlinkItem::HyperlinkItem( const QString
&text
, const QUrl
&url
,
47 QGraphicsItem
*parentItem
)
48 : QObject(), QGraphicsSimpleTextItem( text
, parentItem
),
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() ) {
65 f
.setUnderline( true );
70 void HyperlinkItem::hoverLeaveEvent( QGraphicsSceneHoverEvent
* )
72 if ( m_url
.isEmpty() ) {
77 f
.setUnderline( false );
79 setBrush( Settings::color() );
82 void HyperlinkItem::mouseReleaseEvent( QGraphicsSceneMouseEvent
* )
84 if ( m_url
.isEmpty() ) {
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 );
107 QGraphicsItem
*pi
= 0;
108 const QString favIcon
= KMimeType::favIconForUrl( url
);
109 if ( !favIcon
.isEmpty() ) {
110 pi
= new QGraphicsPixmapItem( SmallIcon( favIcon
), this );
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
);
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
);
146 void ScrollingItemView::relayoutItems()
148 qDeleteAll( m_graphicsItems
);
149 m_graphicsItems
.clear();
151 if ( items().empty() ) {
155 qreal ypos
= ( boundingRect().height() - QFontMetrics( Settings::font() ).height() ) / 2;
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() ) {
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
);
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
) ) {
196 NewsTickerItem
*i
= new NewsTickerItem( item
.text
, item
.url
, item
.description
,
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() ) {
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() ) ) {
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();
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 );
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 );
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
) );
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
) );
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
) );
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
) );
368 case Settings::EnumPagingDirection::COUNT
:
369 // XXX Ahem; generated by kconfig_compiler for internal reasons.
374 void PagingItemView::setItems( const QList
<NewsItem
> &items_
)
376 NewsItemView::setItems( items_
);
377 m_currentItem
= items().begin();
379 updateMoveAnimations();
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() ) {
405 m_currentItem
= items().begin();
408 if ( m_needToReloadSettings
) {
409 m_linkItem
->setBrush( Settings::color() );
410 m_linkItem
->setFont( Settings::font() );
411 m_needToReloadSettings
= false;
413 updateMoveAnimations();
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() );
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"