Fix layout in Meta Contact Properties, Group Properties and Avatar Selector.
[kdenetwork.git] / knewsticker / knewsticker.cpp
blob65d189cba6cf065f59c8d2f588e39bc8d111097d
1 /*
2 * knewsticker.cpp
4 * Copyright (c) 2000, 2001, 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 "knewsticker.h"
12 #include "newsfeedmanager.h"
13 #include "settings.h"
14 #include "ui_feedsettings.h"
15 #include "ui_visualsettings.h"
17 #include <kconfigdialog.h>
18 #include <kiconloader.h>
19 #include <kmimetype.h>
20 #include <syndication/item.h>
22 #include <QDesktopServices>
23 #include <QGraphicsSceneContextMenuEvent>
24 #include <QMenu>
25 #include <QMessageBox>
26 #include <QSignalMapper>
27 #include <QTimer>
29 using namespace Syndication;
31 HyperlinkItem::HyperlinkItem( const QString &text, const QUrl &url,
32 QGraphicsItem *parentItem )
33 : QObject(), QGraphicsSimpleTextItem( text, parentItem ),
34 m_url( url )
36 setFont( Settings::font() );
37 setBrush( Settings::color() );
38 setCursor( Qt::PointingHandCursor );
39 setAcceptedMouseButtons( Qt::LeftButton );
40 setAcceptsHoverEvents( true );
43 void HyperlinkItem::hoverEnterEvent( QGraphicsSceneHoverEvent * )
45 if ( m_url.isEmpty() ) {
46 return;
49 QFont f = font();
50 f.setUnderline( true );
51 setFont( f );
52 setBrush( Qt::red );
55 void HyperlinkItem::hoverLeaveEvent( QGraphicsSceneHoverEvent * )
57 if ( m_url.isEmpty() ) {
58 return;
61 QFont f = font();
62 f.setUnderline( false );
63 setFont( f );
64 setBrush( Settings::color() );
67 void HyperlinkItem::mouseReleaseEvent( QGraphicsSceneMouseEvent * )
69 if ( m_url.isEmpty() ) {
70 return;
73 emit activated( m_url.toString() );
76 SeparatorItem::SeparatorItem( QGraphicsItem *parentItem )
77 : QGraphicsSimpleTextItem( " +++ ", parentItem )
79 setBrush( Settings::color() );
80 setFont( Settings::font() );
83 NewsTickerItem::NewsTickerItem( const QString &text, const QUrl &url,
84 QGraphicsItem *parentItem )
85 : QGraphicsItemGroup( parentItem )
87 setHandlesChildEvents( false );
89 qreal xpos = 0;
91 QGraphicsItem *pi = 0;
92 const QString favIcon = KMimeType::favIconForUrl( url );
93 if ( !favIcon.isEmpty() ) {
94 pi = new QGraphicsPixmapItem( SmallIcon( favIcon ), this );
95 addToGroup( pi );
96 xpos += boundingRect().width() + 8;
99 m_headlineItem = new HyperlinkItem( text, url, this );
100 m_headlineItem->setPos( xpos, 0 );
101 addToGroup( m_headlineItem );
103 m_separatorItem = new SeparatorItem( this );
104 m_separatorItem->setPos( boundingRect().width(), 0 );
105 addToGroup( m_separatorItem );
107 if ( pi ) {
108 pi->setPos( 0, ( boundingRect().height() - pi->boundingRect().height() ) / 2 );
112 KNewsTicker::KNewsTicker( QObject *parent, const QVariantList &args )
113 : Plasma::Applet( parent, args ),
114 m_signalMapper( 0 )
116 setDrawStandardBackground( true );
118 m_scrollerItem = new QGraphicsRectItem( 0, 0, 512, QFontMetrics( Settings::font() ).height() * 2, this );
120 connect( NewsFeedManager::self(), SIGNAL( feedLoaded( const QUrl & ) ),
121 this, SLOT( feedLoaded( const QUrl & ) ) );
122 connect( NewsFeedManager::self(), SIGNAL( updateFinished() ),
123 this, SLOT( feedUpdateFinished() ) );
125 foreach ( const QString &url, Settings::feedUrls() ) {
126 NewsFeedManager::self()->subscribeTo( url );
129 m_scrollTimer = new QTimer( this );
130 connect( m_scrollTimer, SIGNAL( timeout() ),
131 this, SLOT( advance() ) );
132 m_scrollTimer->start( 1000 / 25 );
134 m_updateTimer = new QTimer( this );
135 connect( m_updateTimer, SIGNAL( timeout() ),
136 this, SLOT( updateFeeds() ) );
137 m_updateTimer->start( Settings::updateInterval() * 60 * 1000 );
139 setHasConfigurationInterface( true );
141 updateFeeds();
144 void KNewsTicker::showConfigurationInterface()
146 KConfigDialog dlg( 0, "settings", Settings::self() );
147 connect( &dlg, SIGNAL( settingsChanged( const QString & ) ),
148 this, SLOT( settingsChanged( const QString & ) ) );
149 dlg.setFaceType( KPageDialog::Tabbed );
151 QWidget *page = new QWidget( 0 );
153 Ui::VisualSettings ui;
154 ui.setupUi( page );
156 dlg.addPage( page, i18n( "Appearance" ) );
158 page = new QWidget( 0 );
160 Ui::FeedSettings ui;
161 ui.setupUi( page );
163 dlg.addPage( page, i18n( "Feed Access" ) );
165 QFont origFont = Settings::font();
167 if ( dlg.exec() == QDialog::Accepted ) {
168 if ( Settings::font() != origFont ) {
169 relayoutItems();
171 reloadSettings();
175 void KNewsTicker::settingsChanged( const QString & /* dialogName */ )
177 relayoutItems();
178 reloadSettings();
181 QSizeF KNewsTicker::contentSizeHint() const
183 return m_scrollerItem->boundingRect().size();
186 QList<QAction *> KNewsTicker::contextActions()
188 QList<QAction *> actions;
190 delete m_signalMapper;
191 m_signalMapper = new QSignalMapper( this );
192 connect( m_signalMapper, SIGNAL( mapped( const QString & ) ),
193 this, SLOT( openFeedItem( const QString & ) ) );
195 QList<FeedPtr> availableFeeds = NewsFeedManager::self()->availableFeeds().values();
196 foreach ( FeedPtr feed, availableFeeds ) {
197 QMenu *feedMenu = new QMenu;
198 QList<ItemPtr> items = feed->items();
199 foreach ( ItemPtr item, items ) {
200 QString title = item->title();
201 title.replace( "&quot;", "\"" );
202 title.replace( "&", "&amp;" );
203 QAction *itemAction = feedMenu->addAction( title,
204 m_signalMapper, SLOT( map() ) );
206 m_signalMapper->setMapping( itemAction, item->link() );
209 QAction *feedAction = new QAction( feed->title(), 0 );
210 const QString favIcon = KMimeType::favIconForUrl( feed->link() );
211 if ( !favIcon.isEmpty() ) {
212 feedAction->setIcon( SmallIcon( favIcon ) );
214 feedAction->setMenu( feedMenu );
215 actions.append( feedAction );
218 return actions;
221 void KNewsTicker::relayoutItems()
223 foreach ( QGraphicsItem *item, m_graphicsItems ) {
224 delete item;
226 m_graphicsItems.clear();
228 if ( m_items.empty() ) {
229 return;
232 qreal ypos = ( contentSize().height() - QFontMetrics( font() ).height() ) / 2;
233 qreal xpos = 0;
234 QList<Item>::ConstIterator it, end = m_items.end();
235 for ( it = m_items.begin(); it != end; ++it ) {
236 addItemToLayout( *it, &xpos, &ypos );
239 /* In case none of the available items were added to the scroll text (this
240 * can happen if all of them have been read), add a filler item which tells
241 * the user that no unread news are available.
243 if ( m_graphicsItems.isEmpty() ) {
244 Item item;
245 item.text = i18n( "No unread news available" );
247 addItemToLayout( item, &xpos, &ypos );
248 const qreal firstItemWidth = m_graphicsItems.first()->boundingRect().width();
249 while ( xpos < contentSize().width() + firstItemWidth ) {
250 addItemToLayout( item, &xpos, &ypos );
252 return;
255 const qreal firstItemWidth = m_graphicsItems.first()->boundingRect().width();
257 it = m_items.begin();
258 while ( xpos < contentSize().width() + firstItemWidth ) {
259 addItemToLayout( *it, &xpos, &ypos );
261 if ( ++it == m_items.end() ) {
262 it = m_items.begin();
267 void KNewsTicker::addItemToLayout( const Item &item, qreal *xpos, qreal *ypos )
269 if ( Settings::hideReadArticles() && m_readArticles.contains( item.url ) ) {
270 return;
273 NewsTickerItem *i = new NewsTickerItem( item.text, item.url, m_scrollerItem );
274 connect( i->headlineItem(), SIGNAL( activated( const QString & ) ),
275 this, SLOT( openFeedItem( const QString & ) ) );
276 i->setPos( *xpos, *ypos );
277 m_graphicsItems.append( i );
278 *xpos += i->boundingRect().width();
281 void KNewsTicker::advance()
283 if ( m_graphicsItems.empty() ) {
284 return;
287 const qreal ypos = ( contentSize().height() - QFontMetrics( font() ).height() ) / 2;
289 /* In case an item scrolled out to the left, take it out of the m_graphicsItems list
290 * and insert it at the end again. Also, move it's position to the very end (determine
291 * the end by adding up the widths of the remaining items).
293 NewsTickerItem *firstItem = m_graphicsItems.first();
294 while ( firstItem->x() + firstItem->boundingRect().width() < 0 ) {
295 m_graphicsItems.erase( m_graphicsItems.begin() );
297 /* If we just scrolled an item out of view which we read already,
298 * don't show it again in case the user configured this.
300 if ( Settings::hideReadArticles() &&
301 m_readArticles.contains( firstItem->headlineItem()->url() ) ) {
302 delete firstItem;
303 } else {
304 qreal xpos = 0;
305 foreach ( QGraphicsItem *item, m_graphicsItems ) {
306 xpos += item->boundingRect().width();
308 firstItem->setPos( xpos, ypos );
309 m_graphicsItems.append( firstItem );
311 firstItem = m_graphicsItems.first();
314 foreach ( NewsTickerItem *item, m_graphicsItems ) {
315 item->moveBy( -1, 0 );
319 void KNewsTicker::feedLoaded( const QUrl &url )
321 FeedPtr feed = NewsFeedManager::self()->availableFeeds()[ url ];
322 foreach ( ItemPtr item, feed->items() ) {
323 Item i;
324 i.text = item->title();
325 i.text.replace( "&quot;", "\"" );
326 i.url = item->link();
327 m_items.append( i );
331 void KNewsTicker::feedUpdateFinished()
333 /* Filter out unneded entries in our m_readArticles set; first find out
334 * which URLs are shown after the update. Then, create a new list of read
335 * articles in which only those articles get added which are available
336 * and read.
338 QSet<QUrl> allUrls;
339 foreach ( const Item &item, m_items ) {
340 allUrls.insert( item.url );
343 QSet<QUrl> activeReadArticles;
344 foreach ( const QUrl &readUrl, m_readArticles ) {
345 if ( allUrls.contains( readUrl ) ) {
346 activeReadArticles.insert( readUrl );
349 m_readArticles = activeReadArticles;
351 relayoutItems();
354 void KNewsTicker::updateFeeds()
356 m_items.clear();
357 NewsFeedManager::self()->update();
360 void KNewsTicker::openFeedItem( const QString &url )
362 QDesktopServices::openUrl( url );
363 if ( !m_readArticles.contains( url ) ) {
364 m_readArticles.insert( url );
368 void KNewsTicker::reloadSettings()
370 foreach ( NewsTickerItem *item, m_graphicsItems ) {
371 item->headlineItem()->setBrush( Settings::color() );
372 item->separatorItem()->setBrush( Settings::color() );
374 m_scrollerItem->setRect( 0, 0, 512, QFontMetrics( Settings::font() ).height() * 2 );
375 m_updateTimer->setInterval( Settings::updateInterval() * 60 * 1000 );
378 #include "knewsticker.moc"