kwin/effects/_test/kicker/kicker.cpp

241 lines
8.9 KiB
C++

/********************************************************************
KWin - the KDE window manager
This file is part of the KDE project.
Copyright (C) 2010 Martin Gräßlin <kde@martin-graesslin.com>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*********************************************************************/
#include "kicker.h"
#include <QtCore/QTimer>
#include <QtCore/QUrl>
#include <QtWebKit/QWebElement>
#include <QtWebKit/QWebElementCollection>
#include <QtWebKit/QWebFrame>
#include <QtWebKit/QWebPage>
namespace KWin
{
KWIN_EFFECT(kicker, KickerEffect)
KickerEffect::KickerEffect()
: m_page(new QWebPage(this))
, m_timer(new QTimer(this))
, m_goalTimer(new QTimer(this))
, m_goalActive(false)
, m_ascending(false)
{
connect(m_page, SIGNAL(loadFinished(bool)), this, SLOT(slotLoadFinished(bool)));
connect(m_timer, SIGNAL(timeout()), this, SLOT(timeout()));
connect(m_goalTimer, SIGNAL(timeout()), this, SLOT(goalTimeout()));
reconfigure(ReconfigureAll);
// reload the webpage each half a minute
m_timer->start(30 * 1000);
// goal animation should end after seven seconds
m_goalTimer->setInterval(7 * 1000);
m_goalTimer->setSingleShot(true);
// the animations should have a duration of half a second with an
// ease in out curve
m_timeLine.setCurveShape(TimeLine::EaseInOutCurve);
m_timeLine.setDuration(500);
// let's download the webpage immediatelly
timeout();
}
KickerEffect::~KickerEffect()
{
while (!m_frames.isEmpty()) {
delete m_frames.first();
m_frames.removeFirst();
}
while (!m_goalFrames.isEmpty()) {
delete m_goalFrames.first();
m_goalFrames.removeFirst();
}
}
void KickerEffect::reconfigure(ReconfigureFlags)
{
}
void KickerEffect::prePaintScreen(ScreenPrePaintData& data, int time)
{
// the goal animation uses a KWin::TimeLine to modify the opacity values
// as long as the goal timer has not timed out m_goalActive is true
// after the timeout an animation might still be running, so we continue
// till progress reaches 0.0 again
if (m_goalActive || m_timeLine.progress() != 0.0) {
// the animation might either be ascending (increasing opacity) or
// descending (decreasing opacity). In case of ascending we add time
// to the timeline till progress reaches 1.0. There we switch direction
// to descending. In descending case of course vice versa.
if (m_ascending) {
m_timeLine.addTime(time);
if (m_timeLine.progress() == 1.0) {
m_ascending = false;
}
} else {
m_timeLine.removeTime(time);
// it is possible that this is the last animation. Therefore the
// anding with goalActive. If it has been the last animation we
// do not need to keep the goal EffectFrame around any more, so
// we delete them.
if (m_timeLine.progress() == 0.0 && m_goalActive) {
m_ascending = true;
} else if (m_timeLine.progress() == 0.0) {
// goal animation finshed, let's delete the EffectFrames
while (!m_goalFrames.isEmpty()) {
delete m_goalFrames.first();
m_goalFrames.removeFirst();
}
m_goalFrames.clear();
}
}
}
effects->prePaintScreen(data, time);
}
void KickerEffect::paintScreen(int mask, QRegion region, ScreenPaintData &data)
{
effects->paintScreen(mask, region, data);
if (m_size.isValid()) {
// let's paint the score EffectFrame with unmodified opacity. As it
// uses Plasma styled textures it's still translucent, but the text
// is fully opaque.
foreach (EffectFrame * frame, m_frames)
frame->render(region, 1.0);
}
if (m_goalActive || m_timeLine.progress() != 0.0) {
// the goal animation changes the opacity. Therefore we modify the
// opacity by multiplying with the timeline's progress.
// The text should be fully opaque (1.0), while the background should
// be translucent (0.75)
foreach (EffectFrame * frame, m_goalFrames)
frame->render(region, 1.0 * m_timeLine.progress(), 0.75 * m_timeLine.progress());
// we are animating: need a new frame
effects->addRepaintFull();
}
}
void KickerEffect::slotLoadFinished(bool ok)
{
// if connection failed let's keep the last score
if (!ok)
return;
QWebElement doc = m_page->mainFrame()->documentElement();
// CSS selector for the teams
QWebElementCollection teams = doc.findAll("td.lttabver h1 a");
if (teams.count() != 2)
return;
QString firstTeam = teams[0].toPlainText();
QString secondTeam = teams[1].toPlainText();
// CSS selector for the current score
QString result = doc.findFirst("td.lttaberg h1").toPlainText();
if (m_score != result) {
// the score changed, a goal might have been scored
bool activate = true;
// but not if the web page has been loaded for the first time
if (m_score.isNull())
activate = false;
// not if we entered extra time
if (!m_score.contains("i.V.") && result.contains("i.V."))
activate = false;
// not if extra time ended
if (!m_score.contains("n.V.") && result.contains("n.V."))
activate = false;
// not if penality shootout begins
if (!m_score.contains("i.E.") && result.contains("i.E."))
activate = false;
// not if first or second half starts.
if (m_score.count('-') > result.count('-'))
activate = false;
// yeah it's a goal - generate the EffectFrame and start the animation
if (activate) {
generateGoalImage();
m_goalActive = true;
m_ascending = true;
m_goalTimer->start();
}
m_score = result;
}
QString text = firstTeam + ' ' + result + ' ' + secondTeam;
QFont font;
font.setBold(true);
QFontMetrics fm(font);
m_size = fm.boundingRect(text).adjusted(-10, -10, 10, 10).size();
// we don't want to reposition the EffectFrames, therefore we delete the
// old ones. Normally you wouldn't do that, but as we only update once in
// half a minute and it's easier to code...
while (!m_frames.isEmpty()) {
delete m_frames.first();
m_frames.removeFirst();
}
m_frames.clear();
// and properly position the frame on each screen
for (int i = 0; i < effects->numScreens(); i++) {
QRect area = effects->clientArea(ScreenArea, i, effects->currentDesktop());
QRect geometry = QRect(area.x() + area.width() - m_size.width() - 20, area.y() + 20, m_size.width(), m_size.height());
EffectFrame *frame = new EffectFrame(EffectFrame::Styled);
frame->setText(text);
frame->setFont(font);
frame->setGeometry(geometry);
m_frames.append(frame);
}
// effect frame changed and animation might have started - we need a full repaint
effects->addRepaintFull();
}
void KickerEffect::timeout()
{
// hard coded URL to the liveticker of the match Argentina vs Germany at World Cup 2010.
// If this URL is not valid anymore you can find newer examples on the referrenced web site.
m_page->mainFrame()->load(QUrl("http://www.kicker.de/news/fussball/wm/spielplan/weltmeisterschaft/2010/5/833353/livematch_argentinien_deutschland.html"));
}
void KickerEffect::generateGoalImage()
{
QFont font("FreeMono", 172);
QString goal("GOAL");
QFontMetrics fm(font);
QSize size = fm.boundingRect(goal).adjusted(-10, -10, 10, 10).size();
for (int i = 0; i < effects->numScreens(); i++) {
// place one frame on the center of each screen
QRect area = effects->clientArea(ScreenArea, i, effects->currentDesktop());
QRect geometry = QRect(area.x() + (area.width() - size.width()) / 2,
area.y() + (area.height() - size.height()) / 2,
size.width(), size.height());
EffectFrame *frame = new EffectFrame(EffectFrame::Unstyled, false);
frame->setText(goal);
frame->setFont(font);
frame->setGeometry(geometry);
m_goalFrames.append(frame);
}
}
void KickerEffect::goalTimeout()
{
// stop the animation
m_goalActive = false;
effects->addRepaintFull();
}
} // namespace