From 87117b232585accd4019fb6c17398fa80090fee9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Gr=C3=A4=C3=9Flin?= Date: Mon, 5 Jul 2010 09:45:36 +0000 Subject: [PATCH] Importing the effect written for Akademy 2010. It displays the score of the football match Argentina vs Germany and would be able to trigger an animation if the match would still be on. It's a nice example on what you can do with EffectFrames and therefore imported to test directory. svn path=/trunk/KDE/kdebase/workspace/; revision=1146130 --- effects/_test/CMakeLists.txt | 4 +- effects/_test/kicker/CMakeLists.txt | 29 ++++ effects/_test/kicker/kicker.cpp | 258 ++++++++++++++++++++++++++++ effects/_test/kicker/kicker.desktop | 17 ++ effects/_test/kicker/kicker.h | 84 +++++++++ 5 files changed, 391 insertions(+), 1 deletion(-) create mode 100644 effects/_test/kicker/CMakeLists.txt create mode 100644 effects/_test/kicker/kicker.cpp create mode 100644 effects/_test/kicker/kicker.desktop create mode 100644 effects/_test/kicker/kicker.h diff --git a/effects/_test/CMakeLists.txt b/effects/_test/CMakeLists.txt index 019b586b18..e4a1a5fc8c 100644 --- a/effects/_test/CMakeLists.txt +++ b/effects/_test/CMakeLists.txt @@ -1,7 +1,7 @@ # Adds effect plugin with given name. Sources are given after the name macro(KWIN4_ADD_EFFECT name) kde4_add_plugin(kwin4_effect_${name} ${ARGN}) - target_link_libraries(kwin4_effect_${name} kwineffects ${KDE4_KDEUI_LIBS} ${X11_LIBRARIES} kephal) + target_link_libraries(kwin4_effect_${name} kwineffects ${KDE4_KDEUI_LIBS} ${X11_LIBRARIES} ${QT_QTWEBKIT_LIBRARY} kephal) install(TARGETS kwin4_effect_${name} DESTINATION ${PLUGIN_INSTALL_DIR}) endmacro(KWIN4_ADD_EFFECT) @@ -25,6 +25,7 @@ SET(kwin4_effect_tests_sources howto.cpp test_input.cpp test_thumbnail.cpp + kicker/kicker.cpp ) install( FILES demo_shakymove.desktop @@ -34,6 +35,7 @@ install( FILES howto.desktop test_input.desktop test_thumbnail.desktop + kicker/kicker.desktop DESTINATION ${SERVICES_INSTALL_DIR}/kwin ) if(OPENGL_FOUND) diff --git a/effects/_test/kicker/CMakeLists.txt b/effects/_test/kicker/CMakeLists.txt new file mode 100644 index 0000000000..5fac700c8a --- /dev/null +++ b/effects/_test/kicker/CMakeLists.txt @@ -0,0 +1,29 @@ +####################################### +# Effect + +# Source files +set( kwin4_effect_builtins_sources ${kwin4_effect_builtins_sources} + kicker/kicker.cpp + ) + +# .desktop files +install( FILES + kicker/kicker.desktop + DESTINATION ${SERVICES_INSTALL_DIR}/kwin ) + +####################################### +# Config + +# Source files +#set( kwin4_effect_builtins_config_sources ${kwin4_effect_builtins_config_sources} + #cube/cube_config.cpp + #cube/cube_config.ui + #cube/cubeslide_config.cpp + #cube/cubeslide_config.ui + #) + +# .desktop files +#install( FILES + # cube/cube_config.desktop + # cube/cubeslide_config.desktop + # DESTINATION ${SERVICES_INSTALL_DIR}/kwin ) diff --git a/effects/_test/kicker/kicker.cpp b/effects/_test/kicker/kicker.cpp new file mode 100644 index 0000000000..f8966fe9b9 --- /dev/null +++ b/effects/_test/kicker/kicker.cpp @@ -0,0 +1,258 @@ +/******************************************************************** + KWin - the KDE window manager + This file is part of the KDE project. + + Copyright (C) 2010 Martin Gräßlin + +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 . +*********************************************************************/ +#include "kicker.h" + +#include +#include +#include +#include +#include +#include + +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 diff --git a/effects/_test/kicker/kicker.desktop b/effects/_test/kicker/kicker.desktop new file mode 100644 index 0000000000..2dc4d987b2 --- /dev/null +++ b/effects/_test/kicker/kicker.desktop @@ -0,0 +1,17 @@ +[Desktop Entry] +name=Kicker Liveticker Demo Effect +Icon=preferences-system-windows-effect-kicker +Comment=Demonstration effect for using EffectFrames + +Type=Service +X-KDE-ServiceTypes=KWin/Effect +X-KDE-PluginInfo-Author=Martin Gräßlin +X-KDE-PluginInfo-Email=kde@martin-graesslin.com +X-KDE-PluginInfo-Name=kwin4_effect_kicker +X-KDE-PluginInfo-Version=0.1.0 +X-KDE-PluginInfo-Category=Candy +X-KDE-PluginInfo-Depends= +X-KDE-PluginInfo-License=GPL +X-KDE-PluginInfo-EnabledByDefault=false +X-KDE-Library=kwin4_effect_builtins +X-KDE-Ordering=50 \ No newline at end of file diff --git a/effects/_test/kicker/kicker.h b/effects/_test/kicker/kicker.h new file mode 100644 index 0000000000..f5ef430f46 --- /dev/null +++ b/effects/_test/kicker/kicker.h @@ -0,0 +1,84 @@ +/******************************************************************** + KWin - the KDE window manager + This file is part of the KDE project. + + Copyright (C) 2010 Martin Gräßlin + +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 . +*********************************************************************/ + +#ifndef KWIN_KICKER_H +#define KWIN_KICKER_H + +#include +#include + +class QTimer; +class QWebPage; + +namespace KWin +{ + +/** + * This effect was written for a talk at Akademy 2010 to have the current + * score of the 2010 World Cup Quarterfinal Argentina vs Germany on screen + * during the presentation. + * + * The effect connects to the German football website "kicker.de" and parses + * the returned website. This explains the name of the effect - it is unrelated + * to the panel of KDE 3. + * + * The effect is a nice illustration on how to use EffectFrames and animations. + * Therefore it was imported into the test directory in SVN to provide a small + * example. + * + * The effect consists of two parts: + * displaying the current score and showing a goal animation when a goal is + * scored. The first uses a Plasma styled EffectFrame, while the latter uses + * an unstyled effect frame. + * + * @author Martin Gräßlin + */ +class KickerEffect : public QObject, public Effect + { + Q_OBJECT + public: + KickerEffect(); + ~KickerEffect(); + virtual void reconfigure( ReconfigureFlags ); + virtual void prePaintScreen( ScreenPrePaintData& data, int time ); + virtual void paintScreen( int mask, QRegion region, ScreenPaintData& data ); + + private Q_SLOTS: + void slotLoadFinished(bool ok); + void timeout(); + void goalTimeout(); + + private: + void generateGoalImage(); + QWebPage *m_page; + QTimer *m_timer; + QTimer *m_goalTimer; + QSize m_size; + QList m_frames; + QList m_goalFrames; + bool m_goalActive; + QString m_score; + TimeLine m_timeLine; + bool m_ascending; + }; + +} + +#endif // KWIN_KICKER_H