ok, this time JUST the plasma dir ;)

svn path=/trunk/KDE/kdelibs/; revision=879759
This commit is contained in:
Aaron J. Seigo 2008-11-03 23:08:39 +00:00
parent 44f44ebfea
commit 1b523b9054
244 changed files with 42388 additions and 0 deletions

299
CMakeLists.txt Normal file
View File

@ -0,0 +1,299 @@
find_package(Nepomuk REQUIRED)
include (KDE4Defaults)
include(NepomukMacros)
include_directories(${CMAKE_CURRENT_SOURCE_DIR} ${KDEBASE_WORKSPACE_SOURCE_DIR}/libs ${CMAKE_CURRENT_SOURCE_DIR}/.. ${KDE4_INCLUDES})
if(QT_QTOPENGL_FOUND AND OPENGL_FOUND)
include_directories(${OPENGL_INCLUDE_DIR})
endif(QT_QTOPENGL_FOUND AND OPENGL_FOUND)
add_subdirectory(tests)
add_definitions(-DKDE_DEFAULT_DEBUG_AREA=1209)
########### next target ###############
set(plasmagik_SRCS
packagemetadata.cpp
packagestructure.cpp
package.cpp
)
set(plasma_LIB_SRCS
${plasmagik_SRCS}
abstractrunner.cpp
animationdriver.cpp
animator.cpp
applet.cpp
configloader.cpp
containment.cpp
context.cpp
corona.cpp
datacontainer.cpp
dataengine.cpp
dataenginemanager.cpp
delegate.cpp
dialog.cpp
extender.cpp
extenderitem.cpp
paintutils.cpp
framesvg.cpp
plasma.cpp
popupapplet.cpp
private/applethandle.cpp
private/datacontainer_p.cpp
private/desktoptoolbox.cpp
private/extenderapplet.cpp
private/nativetabbar.cpp
private/packages.cpp
private/paneltoolbox.cpp
private/style.cpp
private/toolbox.cpp
private/tooltip.cpp
private/windowpreview.cpp
querymatch.cpp
runnercontext.cpp
runnermanager.cpp
scripting/appletscript.cpp
scripting/dataenginescript.cpp
scripting/runnerscript.cpp
scripting/scriptengine.cpp
scripting/uiloader.cpp
service.cpp
servicejob.cpp
svg.cpp
theme.cpp
tooltipcontent.cpp
tooltipmanager.cpp
version.cpp
view.cpp
wallpaper.cpp
widgets/checkbox.cpp
widgets/combobox.cpp
widgets/flashinglabel.cpp
widgets/frame.cpp
widgets/groupbox.cpp
widgets/iconwidget.cpp
widgets/label.cpp
widgets/lineedit.cpp
widgets/meter.cpp
widgets/pushbutton.cpp
widgets/radiobutton.cpp
widgets/scrollbar.cpp
widgets/signalplotter.cpp
widgets/slider.cpp
widgets/busywidget.cpp
widgets/svgwidget.cpp
widgets/tabbar.cpp
widgets/treeview.cpp
widgets/textedit.cpp
widgets/webview.cpp
)
#NEPOMUK_GENERATE_FROM_ONTOLOGY(
# nwc.nrl
# ${metadata_test_BINARY_DIR}
# TEST_HEADERS
# TEST_SOURCES
# TEST_INCLUDES
#)
if(QT_QTOPENGL_FOUND AND OPENGL_FOUND)
MESSAGE(STATUS "Adding support for OpenGL applets to libplasma")
set(plasma_LIB_SRCS
${plasma_LIB_SRCS}
glapplet.cpp)
endif(QT_QTOPENGL_FOUND AND OPENGL_FOUND)
kde4_add_library(plasma SHARED ${plasma_LIB_SRCS})
target_link_libraries(plasma ${KDE4_KIO_LIBS} ${KDE4_KFILE_LIBS} ${KDE4_KNEWSTUFF2_LIBS}
${QT_QTUITOOLS_LIBRARY} ${QT_QTWEBKIT_LIBRARY}
${KDE4_THREADWEAVER_LIBRARIES} ${KDE4_SOLID_LIBS} ${X11_LIBRARIES})
if(DL_LIBRARY)
target_link_libraries(plasma ${DL_LIBRARY})
endif(DL_LIBRARY)
if(QT_QTOPENGL_FOUND AND OPENGL_FOUND)
target_link_libraries(plasma ${QT_QTOPENGL_LIBRARY} ${OPENGL_gl_LIBRARY})
endif(QT_QTOPENGL_FOUND AND OPENGL_FOUND)
set_target_properties(plasma PROPERTIES
VERSION 3.0.0
SOVERSION 3
${KDE4_DISABLE_PROPERTY_}LINK_INTERFACE_LIBRARIES "${KDE4_KDEUI_LIBS}"
)
install(TARGETS plasma ${INSTALL_TARGETS_DEFAULT_ARGS})
########### install files ###############
set(plasmagik_HEADERS
packagemetadata.h
packagestructure.h
package.h
)
install(FILES ${plasmagik_HEADERS} DESTINATION ${INCLUDE_INSTALL_DIR}/plasma/ COMPONENT Devel)
set(plasma_LIB_INCLUDES
abstractrunner.h
animationdriver.h
animator.h
applet.h
configloader.h
containment.h
context.h
corona.h
datacontainer.h
dataengine.h
dataenginemanager.h
delegate.h
dialog.h
extender.h
extenderitem.h
paintutils.h
framesvg.h
plasma.h
plasma_export.h
popupapplet.h
querymatch.h
runnercontext.h
runnermanager.h
service.h
servicejob.h
svg.h
theme.h
tooltipcontent.h
tooltipmanager.h
tooltipmanager.h
version.h
view.h
wallpaper.h)
if(QT_QTOPENGL_FOUND AND OPENGL_FOUND)
set(plasma_LIB_INCLUDES
${plasma_LIB_INCLUDES}
glapplet.h)
endif(QT_QTOPENGL_FOUND AND OPENGL_FOUND)
install(FILES
${plasma_LIB_INCLUDES}
DESTINATION ${INCLUDE_INSTALL_DIR}/plasma COMPONENT Devel)
install(FILES
widgets/checkbox.h
widgets/combobox.h
widgets/flashinglabel.h
widgets/frame.h
widgets/groupbox.h
widgets/iconwidget.h
widgets/label.h
widgets/lineedit.h
widgets/meter.h
widgets/pushbutton.h
widgets/radiobutton.h
widgets/scrollbar.h
widgets/signalplotter.h
widgets/slider.h
widgets/busywidget.h
widgets/svgwidget.h
widgets/tabbar.h
widgets/treeview.h
widgets/textedit.h
widgets/webview.h
DESTINATION ${INCLUDE_INSTALL_DIR}/plasma/widgets COMPONENT Devel)
install(FILES
scripting/appletscript.h
scripting/dataenginescript.h
scripting/runnerscript.h
scripting/scriptengine.h
scripting/uiloader.h
DESTINATION ${INCLUDE_INSTALL_DIR}/plasma/scripting COMPONENT Devel)
install(FILES
includes/AbstractRunner
includes/AnimationDriver
includes/Animator
includes/Applet
includes/AppletScript
includes/CheckBox
includes/ComboBox
includes/ConfigLoader
includes/Containment
includes/Context
includes/Corona
includes/DataContainer
includes/DataEngine
includes/DataEngineManager
includes/DataEngineScript
includes/Delegate
includes/Dialog
includes/Extender
includes/ExtenderItem
includes/FlashingLabel
includes/Frame
includes/FrameSvg
includes/GroupBox
includes/IconWidget
includes/Label
includes/LineEdit
includes/Meter
includes/Package
includes/PackageMetadata
includes/PackageStructure
includes/PaintUtils
includes/Plasma
includes/PopupApplet
includes/PushButton
includes/QueryMatch
includes/RadioButton
includes/RunnerContext
includes/RunnerManager
includes/RunnerScript
includes/ScriptEngine
includes/ScrollBar
includes/Service
includes/ServiceJob
includes/SignalPlotter
includes/Slider
includes/BusyWidget
includes/Svg
includes/SvgWidget
includes/TabBar
includes/TextEdit
includes/ToolTipManager
includes/Theme
includes/TreeView
includes/UiLoader
includes/View
includes/Version
includes/Wallpaper
includes/WebView
DESTINATION ${INCLUDE_INSTALL_DIR}/KDE/Plasma COMPONENT Devel)
if(QT_QTOPENGL_FOUND AND OPENGL_FOUND)
install(FILES
includes/GLApplet
DESTINATION ${INCLUDE_INSTALL_DIR}/KDE/Plasma COMPONENT Devel)
endif(QT_QTOPENGL_FOUND AND OPENGL_FOUND)
install(FILES
servicetypes/plasma-animator.desktop
servicetypes/plasma-applet.desktop
servicetypes/plasma-containment.desktop
servicetypes/plasma-dataengine.desktop
servicetypes/plasma-packagestructure.desktop
servicetypes/plasma-runner.desktop
servicetypes/plasma-scriptengine.desktop
servicetypes/plasma-wallpaper.desktop
DESTINATION ${SERVICETYPES_INSTALL_DIR})
install(FILES
servicetypes/plasma-applet-extenderapplet.desktop
DESTINATION ${SERVICES_INSTALL_DIR})
install(FILES scripting/plasmoids.knsrc DESTINATION ${CONFIG_INSTALL_DIR})

100
Mainpage.dox Normal file
View File

@ -0,0 +1,100 @@
/** @mainpage Plasma libraries
libplasma is the core of the Plasma desktop. It provides a framework of graphical
widgets (Plasma::Applet) that can be organised into managed groupings
(Plasma::Containment), such as a desktop or panel. It also provides a data
abstraction layer (Plasma::DataEngine) and a corresponding service interaction
layer (Plasma::Service) to make implementing widgets easier and a
system of callouts (Plasma::AbstractRunner) that provide responses to queries,
from running an application to performing a quick calculation.
The <a href="http://doc.trolltech.com/latest/graphicsview.html">Qt Graphics View
framework</a> and the <a href="http://api.kde.org/4.x-api/kdelibs-apidocs/">KDE
libraries</a> provide the underpinning for libplasma. As a result, it should
work anywhere that Qt and KDE do.
Although libplasma is developed for the use of Plasma, the new desktop shell in
KDE 4, it is general enough to be useful in other applications.
<a href="http://amarok.kde.org">Amarok</a> is already using it for its context
view, allowing for pluggable widgets to display and interact with the music
collection, such as "current track" and "tag cloud" widgets.
libplasma itself only provides a framework, and the widgets, containments,
data engines and runners are all implemented as plugins. However, the framework
is designed to make implementing these plugins as easy as possible, including
providing scripting support. Also, infrastructure such as a dialog to install
new widgets and even download them from the web (Plasma::AppletBrowser) is also
included.
Other important classes are:
- Plasma::Corona: the canvas that containments are placed on
- Plasma::View: a QWidget for displaying a containment
- Plasma::Theme: provides theming support
- Plasma::Animator: provides animations for things like elements appearing
and disappearing
- Plasma::Delegate: provides an item delegate for Qt's
<a href="http://doc.trolltech.com/latest/model-view-programming.html">Model /
View framework</a> for menu items.
- Plasma::ToolTipManager: allows widgets have (themed) tooltips displayed when the
mouse is hovered over them
- Plasma::Dialog: displays a themed application dialog
- Plasma::Extender: provides detachable sections to Plasma::Applet
- Plasma::GLApplet: provides an OpneGL-rendered Plasma::Applet
- Plasma::PackageStructure: provides descriptions of packages containing plugins
for libplasma
- Plasma::PopupApplet: provides a simple way of implementing a Plasma::Applet
consisting of an icon that shows a popup when clicked
- Plasma::Svg and Plasma::FrameSvg: provides themable, cached SVGs
- Plasma::Wallpaper: provides pluggable backgrounds for containments
- Plasma::AppletScript, Plasma::DataEngineScript, Plasma::RunnerScript and
Plasma::ScriptEngine: provide scripting interfaces for plugins
- Various themed QGraphicsWidgets for use in creating a Plasma::Applet
The
<a href="http://techbase.kde.org/Development/Tutorials/Plasma">Plasma tutorials</a>
on TechBase provide a good introduction to writing plugins, such as widgets and
data engines, for libplasma-based applications.
@authors
Aaron Seigo \<aseigo@kde.org\><br>
Alessandro Diaferia \<alediaferia@gmail.com\><br>
Alex Merry \<kde@randomguy3.me.uk\><br>
Alexander Wiedenbruch \<wirr01@gmail.com\><br>
Alexis Ménard \<darktears31@gmail.com\><br>
André Duffeck \<andre@duffeck.de\><br>
Andrew Lake \<jamboarder@yahoo.com\><br>
Bertjan Broeksema \<b.broeksema@kdemail.net\><br>
Chani Armitage \<chanika@gmail.com\><br>
Davide Bettio \<davide.bettio@kdemail.net\><br>
Dan Meltzer \<hydrogen@notyetimplemented.com\><br>
Fredrik Höglund \<fredrik@kde.org\><br>
Ivan Cukic \<ivan.cukic+kde@gmail.com\><br>
John Tapsell \<tapsell@kde.org\><br>
Jordi Polo \<mumismo@gmail.com\><br>
Kevin Ottens \<ervin@kde.org\><br>
Montel Laurent \<montel@kde.org\<br>
Marco Martin \<notmart@gmail.com\><br>
Matt Broadstone \<mbroadst@gmail.com\><br>
Petri Damsten \<damu@iki.fi\><br>
Rafael Fernández López \<ereslibre@kde.org\><br>
Riccardo Iaconelli \<riccardo@kde.org\><br>
Richard J. Moore \<rich@kde.org\><br>
Rob Scheepmaker \<r.scheepmaker@student.utwente.nl\><br>
Robert Knight \<robertknight@gmail.com\><br>
Sebastian Kuegler \<sebas@kde.org\><br>
Siraj Razick \<siraj@kde.net\><br>
Zack Rusin \<zack@kde.org\>
@maintainers
Aaron Seigo \<aseigo@kde.org\>
@licenses
@lgpl
*/
// DOXYGEN_SET_PROJECT_NAME = libplasma
// DOXYGEN_SET_RECURSIVE = YES
// vim:ts=4:sw=4:expandtab:filetype=doxygen

2
Messages.sh Normal file
View File

@ -0,0 +1,2 @@
#! /usr/bin/env bash
$XGETTEXT *.cpp *.h */*.h */*.cpp -o $podir/libplasma.pot

23
README Normal file
View File

@ -0,0 +1,23 @@
libplasma
Commit Rules:
* If your patch is not an obvious or trivial bug fix, have it peer reviewed
by another Plasma developer
* All code MUST follow the kdelibs coding style, as found at:
http://techbase.kde.org/Policies/Kdelibs_Coding_Style
* All new public API MUST have apidox written before committing
Unit tests are next to godliness. (Though as you can see, right now libplasma
is hellbound.)
This directory contains the classes making up libplasma, which provides the
core framework used by Plasma and its components. This includes applet and
extension definitions and loading, common GUI elements, etc.
Domain specific sets of functionality, e.g. for network awareness or sensors,
are not found here but in one of the Plasma Engines.
Please refer to the Plasma website (http://plasma.kde.org) and Plasma wiki
(http://techbase.kde.org/Projects/Plasma) for API documentation and design
documents regarding this library.

318
abstractrunner.cpp Normal file
View File

@ -0,0 +1,318 @@
/*
* Copyright 2006-2007 Aaron Seigo <aseigo@kde.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Library General Public License as
* published by the Free Software Foundation; either version 2, 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 Library General Public
* License along with this program; if not, write to the
* Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "abstractrunner.h"
#include <QAction>
#include <QHash>
#include <QMutex>
#include <QMutexLocker>
#include <KDebug>
#include <KPluginInfo>
#include <KServiceTypeTrader>
#include <KStandardDirs>
#include <QTimer>
#include <plasma/querymatch.h>
#include <plasma/package.h>
#include "scripting/runnerscript.h"
#include "runnercontext.h"
namespace Plasma
{
class AbstractRunnerPrivate
{
public:
AbstractRunnerPrivate(AbstractRunner *r, KService::Ptr service)
: priority(AbstractRunner::NormalPriority),
speed(AbstractRunner::NormalSpeed),
blackListed(0),
script(0),
runnerDescription(service),
runner(r),
fastRuns(0),
package(0)
{
if (runnerDescription.isValid()) {
const QString api = runnerDescription.property("X-Plasma-API").toString();
if (!api.isEmpty()) {
const QString path = KStandardDirs::locate("data",
"plasma/runners/" + runnerDescription.pluginName() + '/');
PackageStructure::Ptr structure =
Plasma::packageStructure(api, Plasma::RunnerComponent);
structure->setPath(path);
package = new Package(path, structure);
script = Plasma::loadScriptEngine(api, runner);
if (!script) {
kDebug() << "Could not create a(n)" << api << "ScriptEngine for the"
<< runnerDescription.name() << "Runner.";
delete package;
package = 0;
} else {
QTimer::singleShot(0, runner, SLOT(init()));
}
}
}
}
~AbstractRunnerPrivate()
{
delete script;
script = 0;
delete package;
package = 0;
}
bool hasRunOptions;
bool hasConfig;
AbstractRunner::Priority priority;
AbstractRunner::Speed speed;
RunnerContext::Types blackListed;
RunnerScript *script;
KPluginInfo runnerDescription;
AbstractRunner *runner;
QTime runtime;
int fastRuns;
Package *package;
QHash<QString, QAction*> actions;
};
K_GLOBAL_STATIC(QMutex, s_bigLock)
AbstractRunner::AbstractRunner(QObject *parent, const QString &serviceId)
: QObject(parent),
d(new AbstractRunnerPrivate(this, KService::serviceByStorageId(serviceId)))
{
}
AbstractRunner::AbstractRunner(QObject *parent, const QVariantList &args)
: QObject(parent),
d(new AbstractRunnerPrivate(this, KService::serviceByStorageId(args.count() > 0 ? args[0].toString() : QString())))
{
}
AbstractRunner::~AbstractRunner()
{
delete d;
}
KConfigGroup AbstractRunner::config() const
{
QString group = objectName();
if (group.isEmpty()) {
group = "UnnamedRunner";
}
KConfigGroup runners(KGlobal::config(), "Runners");
return KConfigGroup(&runners, group);
}
void AbstractRunner::reloadConfiguration()
{
}
void AbstractRunner::performMatch(Plasma::RunnerContext &globalContext)
{
static const int reasonableRunTime = 1500;
static const int fastEnoughTime = 250;
d->runtime.restart();
//TODO :this is a copy ctor
RunnerContext localContext(globalContext, 0);
match(localContext);
// automatically rate limit runners that become slooow
const int runtime = d->runtime.elapsed();
bool slowed = speed() == SlowSpeed;
if (!slowed && runtime > reasonableRunTime) {
// we punish runners that return too slowly, even if they don't bring
// back matches
kDebug() << id() << "runner is too slow, putting it on the back burner.";
d->fastRuns = 0;
setSpeed(SlowSpeed);
}
//If matches were not added, delete items on the heap
if (slowed && runtime < fastEnoughTime) {
++d->fastRuns;
if (d->fastRuns > 2) {
// we reward slowed runners who bring back matches fast enough
// 3 times in a row
kDebug() << id() << "runner is faster than we thought, kicking it up a notch";
setSpeed(NormalSpeed);
}
}
}
QList<QAction*> AbstractRunner::actionsForMatch(const Plasma::QueryMatch &match)
{
Q_UNUSED(match)
QList<QAction*> ret;
return ret;
}
QAction* AbstractRunner::addAction(const QString &id, const QIcon &icon, const QString &text)
{
QAction *a = new QAction(icon, text, this);
d->actions.insert(id, a);
return a;
}
void AbstractRunner::addAction(const QString &id, QAction *action)
{
d->actions.insert(id, action);
}
void AbstractRunner::removeAction(const QString &id)
{
QAction *a = d->actions.take(id);
delete a;
}
QAction* AbstractRunner::action(const QString &id) const
{
return d->actions.value(id);
}
QHash<QString, QAction*> AbstractRunner::actions() const
{
return d->actions;
}
void AbstractRunner::clearActions()
{
qDeleteAll(d->actions);
d->actions.clear();
}
bool AbstractRunner::hasRunOptions()
{
return d->hasRunOptions;
}
void AbstractRunner::setHasRunOptions(bool hasRunOptions)
{
d->hasRunOptions = hasRunOptions;
}
void AbstractRunner::createRunOptions(QWidget *parent)
{
Q_UNUSED(parent)
}
AbstractRunner::Speed AbstractRunner::speed() const
{
return d->speed;
}
void AbstractRunner::setSpeed(Speed speed)
{
d->speed = speed;
}
AbstractRunner::Priority AbstractRunner::priority() const
{
return d->priority;
}
void AbstractRunner::setPriority(Priority priority)
{
d->priority = priority;
}
RunnerContext::Types AbstractRunner::ignoredTypes() const
{
return d->blackListed;
}
void AbstractRunner::setIgnoredTypes(RunnerContext::Types types)
{
d->blackListed = types;
}
KService::List AbstractRunner::serviceQuery(const QString &serviceType, const QString &constraint) const
{
QMutexLocker lock(s_bigLock);
return KServiceTypeTrader::self()->query(serviceType, constraint);
}
QMutex* AbstractRunner::bigLock() const
{
return s_bigLock;
}
void AbstractRunner::run(const Plasma::RunnerContext &search, const Plasma::QueryMatch &action)
{
if (d->script) {
return d->script->run(search, action);
}
}
void AbstractRunner::match(Plasma::RunnerContext &search)
{
if (d->script) {
return d->script->match(search);
}
}
QString AbstractRunner::name() const
{
if (!d->runnerDescription.isValid()) {
return objectName();
}
return d->runnerDescription.name();
}
QString AbstractRunner::id() const
{
if (!d->runnerDescription.isValid()) {
return objectName();
}
return d->runnerDescription.pluginName();
}
QString AbstractRunner::description() const
{
if (!d->runnerDescription.isValid()) {
return objectName();
}
return d->runnerDescription.property("Comment").toString();
}
const Package* AbstractRunner::package() const
{
return d->package;
}
void AbstractRunner::init()
{
if (d->script) {
d->script->init();
}
}
} // Plasma namespace
#include "abstractrunner.moc"

349
abstractrunner.h Normal file
View File

@ -0,0 +1,349 @@
/*
* Copyright 2006-2007 Aaron Seigo <aseigo@kde.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Library General Public License as
* published by the Free Software Foundation; either version 2, 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 Library General Public
* License along with this program; if not, write to the
* Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef PLASMA_ABSTRACTRUNNER_H
#define PLASMA_ABSTRACTRUNNER_H
#include <QtCore/QObject>
#include <QtCore/QMutex>
#include <QtCore/QStringList>
#include <KDE/KConfigGroup>
#include <KDE/KService>
#include <plasma/plasma_export.h>
#include <plasma/runnercontext.h>
#include <plasma/querymatch.h>
#include <plasma/version.h>
class QAction;
class KCompletion;
namespace Plasma
{
class Package;
class RunnerScript;
class QueryMatch;
class AbstractRunnerPrivate;
/**
* @class AbstractRunner plasma/abstractrunner.h <Plasma/AbstractRunner>
*
* @short An abstract base class for Plasma Runner plugins.
*
* Be aware that runners have to be thread-safe. This is due to the fact that
* each runner is executed in its own thread for each new term. Thus, a runner
* may be executed more than once at the same time. See match() for details.
*/
class PLASMA_EXPORT AbstractRunner : public QObject
{
Q_OBJECT
public:
/** Specifies a nominal speed for the runner */
enum Speed {
SlowSpeed,
NormalSpeed
};
/** Specifies a priority for the runner */
enum Priority {
LowestPriority = 0,
LowPriority,
NormalPriority,
HighPriority,
HighestPriority
};
/** An ordered list of runners */
typedef QList<AbstractRunner*> List;
virtual ~AbstractRunner();
/**
* This is the main query method. It should trigger creation of
* QueryMatch instances through RunnerContext::addMatch and
* RunnerContext::addMatches. It is called internally by performMatch().
*
* If the runner can run precisely the requested term (RunnerContext::query()),
* it should create an exact match by setting the type to RunnerContext::ExactMatch.
* The first runner that creates a QueryMatch will be the
* default runner. Other runner's matches will be suggested in the
* interface. Non-exact matches should be offered via RunnerContext::PossibleMatch.
*
* The match will be activated via run() if the user selects it.
*
* Each runner is executed in its own thread. Whenever the user input changes this
* method is called again. Thus, it needs to be thread-safe. Also, all matches need
* to be reported once this method returns. Asyncroneous runners therefore need
* to make use of a local event loop to wait for all matches.
*
* It is recommended to use local status data in async runners. The simplest way is
* to have a separate class doing all the work like so:
*
* \code
* void MyFancyAsyncRunner::match( RunnerContext& context )
* {
* QEventLoop loop;
* MyAsyncWorker worker( context );
* connect( &worker, SIGNAL(finished()),
* &loop, SLOT(quit()) );
* worker.work();
* loop.exec();
* }
* \endcode
*
* Here MyAsyncWorker creates all the matches and calls RunnerContext::addMatch
* in some internal slot. It emits the finished() signal once done which will
* quit the loop and make the match() method return. The local status is kept
* entirely in MyAsyncWorker which makes match() trivially thread-safe.
*
* If a particular match supports multiple actions, set up the corresponding
* actions in the actionsForMatch method. Do not call any of the action methods
* within this method!
* @see actionsForMatch
*
* Execution of the correct action should be handled in the run method.
* @caution This method needs to be thread-safe since KRunner will simply
* start a new thread for each new term.
*
* @caution Returning from this method means to end execution of the runner.
*
* @sa run(), RunnerContext::addMatch, RunnerContext::addMatches, QueryMatch
*/
virtual void match(Plasma::RunnerContext &context);
/**
* Triggers a call to match. This will call match() internally.
*
* @arg context the search context used in executing this match.
*/
void performMatch(Plasma::RunnerContext &context);
/**
* If the runner has options that the user can interact with to modify
* what happens when run or one of the actions created in fillMatches
* is called, the runner should return true
*/
bool hasRunOptions();
/**
* If hasRunOptions() returns true, this method may be called to get
* a widget displaying the options the user can interact with to modify
* the behaviour of what happens when a given match is selected.
*
* @param widget the parent of the options widgets.
*/
virtual void createRunOptions(QWidget *widget);
/**
* Called whenever an exact or possible match associated with this
* runner is triggered.
*
* @param context The context in which the match is triggered, i.e. for which
* the match was created.
* @param match The actual match to run/execute.
*/
virtual void run(const Plasma::RunnerContext &context, const Plasma::QueryMatch &match);
/**
* The nominal speed of the runner.
* @see setSpeed
*/
Speed speed() const;
/**
* The priority of the runner.
* @see setPriority
*/
Priority priority() const;
/**
* Returns the OR'ed value of all the Information types (as defined in RunnerContext::Type)
* this runner is not interested in.
* @return OR'ed value of black listed types
*/
RunnerContext::Types ignoredTypes() const;
/**
* Sets the types this runner will ignore
* @param types OR'ed listed of ignored types
*/
void setIgnoredTypes(RunnerContext::Types types);
/**
* Returns the user visible engine name for the Runner
*/
QString name() const;
/**
* Returns an id string for the Runner
*/
QString id() const;
/**
* Returns the description of this Runner
*/
QString description() const;
/**
* Accessor for the associated Package object if any.
*
* Note that the returned pointer is only valid for the lifetime of
* the runner.
*
* @return the Package object, or 0 if none
**/
const Package *package() const;
/**
* Signal runner to reload its configuration.
*/
virtual void reloadConfiguration();
protected:
friend class RunnerManager;
friend class RunnerManagerPrivate;
/**
* Constructs a Runner object. Since AbstractRunner has pure virtuals,
* this constructor can not be called directly. Rather a subclass must
* be created
*/
explicit AbstractRunner(QObject *parent = 0, const QString &serviceId = QString());
AbstractRunner(QObject *parent, const QVariantList &args);
/**
* Provides access to the runner's configuration object.
*/
KConfigGroup config() const;
/**
* Sets whether or not the runner has options for matches
*/
void setHasRunOptions(bool hasRunOptions);
/**
* Sets the nominal speed of the runner. Only slow runners need
* to call this within their constructor because the default
* speed is NormalSpeed. Runners that use DBUS should call
* this within their constructors.
*/
void setSpeed(Speed newSpeed);
/**
* Sets the priority of the runner. Lower priority runners are executed
* only after higher priority runners.
*/
void setPriority(Priority newPriority);
/**
* A blocking method to do queries of installed Services which can provide
* a measure of safety for runners running their own threads. This should
* be used instead of calling KServiceTypeTrader::query(..) directly.
*
* @arg serviceType a service type like "Plasma/Applet" or "KFilePlugin"
* @arg constraint a constraint to limit the choices returned.
* @see KServiceTypeTrader::query(const QString&, const QString&)
*
* @return a list of services that satisfy the query.
*/
KService::List serviceQuery(const QString &serviceType,
const QString &constraint = QString()) const;
QMutex *bigLock() const;
/**
* A given match can have more than action that can be performed on it.
* For example, a song match returned by a music player runner can be queued,
* added to the playlist, or played.
*
* Call this method to add actions that can be performed by the runner.
* Actions must first be added to the runner's action registry.
* Note: execution of correct action is left up to the runner.
*/
virtual QList<QAction*> actionsForMatch(const Plasma::QueryMatch &match);
/**
* Creates and then adds an action to the action registry.
* AbstractRunner assumes ownership of the created action.
*
* @param id A unique identifier string
* @param icon The icon to display
* @param text The text to display
* @return the created QAction
*/
QAction* addAction(const QString &id, const QIcon &icon, const QString &text);
/**
* Adds an action to the runner's action registry.
*
* The QAction must be created within the GUI thread;
* do not create it within the match method of AbstractRunner.
*
* @param id A unique identifier string
* @param action The QAction to be stored
*/
void addAction(const QString &id, QAction *action);
/**
* Removes the action from the action registry.
* AbstractRunner deletes the action once removed.
*
* @param id The id of the action to be removed
*/
void removeAction(const QString &id);
/**
* Returns the action associated with the id
*/
QAction* action(const QString &id) const;
/**
* Returns all registered actions
*/
QHash<QString, QAction*> actions() const;
/**
* Clears the action registry.
* The action pool deletes the actions.
*/
void clearActions();
protected Q_SLOTS:
void init();
private:
AbstractRunnerPrivate *const d;
};
} // Plasma namespace
#define K_EXPORT_PLASMA_RUNNER( libname, classname ) \
K_PLUGIN_FACTORY(factory, registerPlugin<classname>();) \
K_EXPORT_PLUGIN(factory("plasma_runner_" #libname)) \
K_EXPORT_PLUGIN_VERSION(PLASMA_VERSION)
#define K_EXPORT_RUNNER_CONFIG( name, classname ) \
K_PLUGIN_FACTORY(ConfigFactory, registerPlugin<classname>();) \
K_EXPORT_PLUGIN(ConfigFactory("kcm_krunner_" #name)) \
K_EXPORT_PLUGIN_VERSION(PLASMA_VERSION)
#endif

146
animationdriver.cpp Normal file
View File

@ -0,0 +1,146 @@
/*
* Copyright 2007 Aaron Seigo <aseigo@kde.org>
* 2007 Alexis Ménard <darktears31@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Library General Public License as
* published by the Free Software Foundation; either version 2, 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 Library General Public
* License along with this program; if not, write to the
* Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "animationdriver.h"
#include <QPainter>
#include <QGraphicsItem>
namespace Plasma
{
AnimationDriver::AnimationDriver(QObject *parent)
: QObject(parent),
d(0)
{
}
AnimationDriver::~AnimationDriver()
{
}
int AnimationDriver::animationFps(Plasma::Animator::Animation animation) const
{
Q_UNUSED(animation)
return 0;
}
int AnimationDriver::movementAnimationFps(Plasma::Animator::Movement movement) const
{
Q_UNUSED(movement)
return 40;
}
int AnimationDriver::elementAnimationFps(Plasma::Animator::Animation animation) const
{
Q_UNUSED(animation)
return 0;
}
int AnimationDriver::animationDuration(Plasma::Animator::Animation) const
{
return 200;
}
int AnimationDriver::movementAnimationDuration(Plasma::Animator::Movement movement) const
{
switch (movement) {
case Animator::FastSlideInMovement:
case Animator::FastSlideOutMovement:
return 150;
break;
default:
break;
}
return 250;
}
int AnimationDriver::elementAnimationDuration(Plasma::Animator::Animation) const
{
return 333;
}
Animator::CurveShape AnimationDriver::animationCurve(Plasma::Animator::Animation) const
{
return Animator::EaseInOutCurve;
}
Animator::CurveShape AnimationDriver::movementAnimationCurve(Plasma::Animator::Movement) const
{
return Animator::EaseInOutCurve;
}
Animator::CurveShape AnimationDriver::elementAnimationCurve(Plasma::Animator::Animation) const
{
return Animator::EaseInOutCurve;
}
QPixmap AnimationDriver::elementAppear(qreal progress, const QPixmap &pixmap)
{
Q_UNUSED(progress)
return pixmap;
}
QPixmap AnimationDriver::elementDisappear(qreal progress, const QPixmap &pixmap)
{
Q_UNUSED(progress)
QPixmap pix(pixmap.size());
pix.fill(Qt::transparent);
return pix;
}
void AnimationDriver::itemAppear(qreal frame, QGraphicsItem *item)
{
Q_UNUSED(frame)
Q_UNUSED(item)
}
void AnimationDriver::itemDisappear(qreal frame, QGraphicsItem *item)
{
Q_UNUSED(frame)
Q_UNUSED(item)
}
void AnimationDriver::itemActivated(qreal frame, QGraphicsItem *item)
{
Q_UNUSED(frame)
Q_UNUSED(item)
}
void AnimationDriver::itemSlideIn(qreal progress, QGraphicsItem *item, const QPoint &start, const QPoint &destination)
{
double x = start.x() + (destination.x() - start.x()) * progress;
double y = start.y() + (destination.y() - start.y()) * progress;
item->setPos(x, y);
}
void AnimationDriver::itemSlideOut(qreal progress, QGraphicsItem *item, const QPoint &start, const QPoint &destination)
{
//kDebug();
double x = start.x() + (destination.x() - start.x()) * progress;
double y = start.y() + (destination.y() - start.y()) * progress;
item->setPos(x, y);
}
} // Plasma namespace
#include "animationdriver.moc"

85
animationdriver.h Normal file
View File

@ -0,0 +1,85 @@
/*
* Copyright 2007 Aaron Seigo <aseigo@kde.org>
* Copyright 2007 Alexis Ménard <darktears31@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Library General Public License as
* published by the Free Software Foundation; either version 2, 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 Library General Public
* License along with this program; if not, write to the
* Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef PLASMA_ANIMATIONDRIVER_H
#define PLASMA_ANIMATIONDRIVER_H
#include <QtCore/QObject>
#include <QtGui/QRegion>
#include <QtGui/QPixmap>
#include <kgenericfactory.h>
#include <plasma/version.h>
#include <plasma/animator.h>
class QGraphicsItem;
namespace Plasma
{
class AnimationDriverPrivate;
class PLASMA_EXPORT AnimationDriver : public QObject
{
Q_OBJECT
public:
explicit AnimationDriver(QObject *parent = 0);
~AnimationDriver();
// Parameter definitions
virtual int animationFps(Plasma::Animator::Animation) const;
virtual int movementAnimationFps(Plasma::Animator::Movement) const;
virtual int elementAnimationFps(Plasma::Animator::Animation) const;
virtual int animationDuration(Plasma::Animator::Animation) const;
virtual int movementAnimationDuration(Plasma::Animator::Movement) const;
virtual int elementAnimationDuration(Plasma::Animator::Animation) const;
virtual Animator::CurveShape animationCurve(Plasma::Animator::Animation) const;
virtual Animator::CurveShape movementAnimationCurve(Plasma::Animator::Movement) const;
virtual Animator::CurveShape elementAnimationCurve(Plasma::Animator::Animation) const;
// Element animations
virtual QPixmap elementAppear(qreal progress, const QPixmap &pixmap);
virtual QPixmap elementDisappear(qreal progress, const QPixmap &pixmap);
// Item animations
virtual void itemAppear(qreal progress, QGraphicsItem *item);
virtual void itemDisappear(qreal progress, QGraphicsItem *item);
virtual void itemActivated(qreal progress, QGraphicsItem *item);
// Item movements
virtual void itemSlideIn(qreal progress, QGraphicsItem *item,
const QPoint &start, const QPoint &destination);
virtual void itemSlideOut(qreal progress, QGraphicsItem *item,
const QPoint &start, const QPoint &destination);
private:
AnimationDriverPrivate *const d;
};
} // Plasma namespace
#define K_EXPORT_PLASMA_ANIMATOR(libname, classname) \
K_PLUGIN_FACTORY(factory, registerPlugin<classname>();) \
K_EXPORT_PLUGIN(factory("plasma_animator_" #libname)) \
K_EXPORT_PLUGIN_VERSION(PLASMA_VERSION)
#endif // multiple inclusion guard

729
animator.cpp Normal file
View File

@ -0,0 +1,729 @@
/*
* Copyright 2007 Aaron Seigo <aseigo@kde.org>
* 2007 Alexis Ménard <darktears31@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Library General Public License as
* published by the Free Software Foundation; either version 2, 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 Library General Public
* License along with this program; if not, write to the
* Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "animator.h"
#include <QGraphicsItem>
#include <QTimeLine>
#include <KConfig>
#include <KConfigGroup>
#include <KService>
#include <KServiceTypeTrader>
#include <KGlobalSettings>
#include "animationdriver.h"
namespace Plasma
{
static const int MIN_TICK_RATE_INT = 10;
static const qreal MIN_TICK_RATE = 10;
struct AnimationState
{
QGraphicsItem *item;
QObject *qobj;
Animator::Animation animation;
Animator::CurveShape curve;
int interval;
int currentInterval;
int frames;
int currentFrame;
int id;
};
struct ElementAnimationState
{
QGraphicsItem *item;
QObject *qobj;
Animator::CurveShape curve;
Animator::Animation animation;
int interval;
int currentInterval;
int frames;
int currentFrame;
int id;
QPixmap pixmap;
};
struct MovementState
{
QGraphicsItem *item;
QObject *qobj;
Animator::CurveShape curve;
Animator::Movement movement;
int interval;
int currentInterval;
int frames;
int currentFrame;
QPoint start;
QPoint destination;
int id;
};
struct CustomAnimationState
{
Animator::CurveShape curve;
int frames;
int currentFrame;
int interval;
int currentInterval;
int id;
QObject *receiver;
char *slot;
};
class AnimatorPrivate
{
public:
AnimatorPrivate()
: driver(0),
animId(0),
timerId(0)
{
}
~AnimatorPrivate()
{
qDeleteAll(animatedItems);
qDeleteAll(animatedElements);
qDeleteAll(movingItems);
QMutableMapIterator<int, CustomAnimationState*> it(customAnims);
while (it.hasNext()) {
delete it.value()->slot;
delete it.value();
it.remove();
}
// Animator is a QObject
// and we don't own the items
}
qreal calculateProgress(int time, int duration, Animator::CurveShape curve)
{
if (!(KGlobalSettings::graphicEffectsLevel() & KGlobalSettings::SimpleAnimationEffects)) {
return qreal(1.0);
}
timeline.setCurveShape(static_cast<QTimeLine::CurveShape>(curve));
timeline.setDuration(duration);
qreal progress = timeline.valueForTime(time);
return progress;
}
void performAnimation(qreal amount, const AnimationState *state)
{
switch (state->animation) {
case Animator::AppearAnimation:
driver->itemAppear(amount, state->item);
break;
case Animator::DisappearAnimation:
driver->itemDisappear(amount, state->item);
if (amount >= 1) {
state->item->hide();
}
break;
case Animator::ActivateAnimation:
driver->itemActivated(amount, state->item);
break;
}
}
void performMovement(qreal amount, const MovementState *state)
{
switch (state->movement) {
case Animator::SlideInMovement:
case Animator::FastSlideInMovement:
//kDebug() << "performMovement, SlideInMovement";
driver->itemSlideIn(amount, state->item, state->start, state->destination);
break;
case Animator::SlideOutMovement:
case Animator::FastSlideOutMovement:
//kDebug() << "performMovement, SlideOutMovement";
driver->itemSlideOut(amount, state->item, state->start, state->destination);
break;
}
}
void init(Animator *q);
void animatedItemDestroyed(QObject*);
void movingItemDestroyed(QObject*);
void animatedElementDestroyed(QObject*);
void customAnimReceiverDestroyed(QObject*);
AnimationDriver *driver;
int animId;
int timerId;
QTime time;
QTimeLine timeline;
//TODO: eventually perhaps we should allow multiple animations simulataneously
// which would imply changing this to a QMap<QGraphicsItem*, QList<QTimeLine*> >
// and really making the code fun ;)
QMap<QGraphicsItem*, AnimationState*> animatedItems;
QMap<QGraphicsItem*, MovementState*> movingItems;
QMap<int, ElementAnimationState*> animatedElements;
QMap<int, CustomAnimationState*> customAnims;
};
class AnimatorSingleton
{
public:
Animator self;
};
K_GLOBAL_STATIC(AnimatorSingleton, privateSelf)
Animator *Animator::self()
{
return &privateSelf->self;
}
Animator::Animator(QObject *parent)
: QObject(parent),
d(new AnimatorPrivate)
{
d->init(this);
}
Animator::~Animator()
{
delete d;
}
void AnimatorPrivate::animatedItemDestroyed(QObject *o)
{
//kDebug() << "testing for" << (void*)o;
QMutableMapIterator<QGraphicsItem*, AnimationState*> it(animatedItems);
while (it.hasNext()) {
it.next();
//kDebug() << "comparing against" << it.value()->qobj;
if (it.value()->qobj == o) {
kDebug() << "found deleted animated item";
delete it.value();
it.remove();
}
}
}
void AnimatorPrivate::movingItemDestroyed(QObject *o)
{
QMutableMapIterator<QGraphicsItem*, MovementState*> it(movingItems);
while (it.hasNext()) {
it.next();
if (it.value()->qobj == o) {
delete it.value();
it.remove();
}
}
}
void AnimatorPrivate::animatedElementDestroyed(QObject *o)
{
QMutableMapIterator<int, ElementAnimationState*> it(animatedElements);
while (it.hasNext()) {
it.next();
if (it.value()->qobj == o) {
delete it.value();
it.remove();
}
}
}
void AnimatorPrivate::customAnimReceiverDestroyed(QObject *o)
{
QMutableMapIterator<int, CustomAnimationState*> it(customAnims);
while (it.hasNext()) {
if (it.next().value()->receiver == o) {
delete it.value()->slot;
delete it.value();
it.remove();
}
}
}
int Animator::animateItem(QGraphicsItem *item, Animation animation)
{
//kDebug();
// get rid of any existing animations on this item.
//TODO: shoudl we allow multiple anims per item?
QMap<QGraphicsItem*, AnimationState*>::iterator it = d->animatedItems.find(item);
if (it != d->animatedItems.end()) {
delete it.value();
d->animatedItems.erase(it);
}
int frames = d->driver->animationFps(animation);
if (frames < 1) {
// evidently this animator doesn't have an implementation
// for this Animation
return -1;
}
int duration = d->driver->animationDuration(animation);
AnimationState *state = new AnimationState;
state->id = ++d->animId;
state->item = item;
state->animation = animation;
state->curve = d->driver->animationCurve(animation);
state->frames = qMax(1.0, frames * (duration / 1000.0));
state->currentFrame = 0;
state->interval = d->driver->animationDuration(animation) / qreal(state->frames);
state->interval = qMax(MIN_TICK_RATE_INT, state->interval - (state->interval % MIN_TICK_RATE_INT));
state->currentInterval = state->interval;
state->qobj = dynamic_cast<QObject*>(item);
if (state->qobj) {
//kDebug() << "!!!!!!!!!!!!!!!!!!!!!!!!! got us an object!";
disconnect(state->qobj, SIGNAL(destroyed(QObject*)),
this, SLOT(animatedItemDestroyed(QObject*)));
connect(state->qobj, SIGNAL(destroyed(QObject*)),
this, SLOT(animatedItemDestroyed(QObject*)));
}
d->animatedItems[item] = state;
d->performAnimation(0, state);
if (!d->timerId) {
d->timerId = startTimer(MIN_TICK_RATE);
d->time.restart();
}
return state->id;
}
int Animator::moveItem(QGraphicsItem *item, Movement movement, const QPoint &destination)
{
//kDebug();
QMap<QGraphicsItem*, MovementState*>::iterator it = d->movingItems.find(item);
if (it != d->movingItems.end()) {
delete it.value();
d->movingItems.erase(it);
}
int frames = d->driver->movementAnimationFps(movement);
if (frames <= 1) {
// evidently this animator doesn't have an implementation
// for this Animation
return -1;
}
MovementState *state = new MovementState;
state->id = ++d->animId;
state->destination = destination;
state->start = item->pos().toPoint();
state->item = item;
state->movement = movement;
state->curve = d->driver->movementAnimationCurve(movement);
//TODO: variance in times based on the value of animation
int duration = d->driver->movementAnimationDuration(movement);
state->frames = qMax(1.0, frames * (duration / 1000.0));
state->currentFrame = 0;
state->interval = duration / qreal(state->frames);
state->interval = qMax(MIN_TICK_RATE_INT, state->interval - (state->interval % MIN_TICK_RATE_INT));
// state->interval = (state->interval / MIN_TICK_RATE) * MIN_TICK_RATE;
// kDebug() << "interval of" << state->interval << state->frames << duration << frames;
state->currentInterval = state->interval;
state->qobj = dynamic_cast<QObject*>(item);
if (state->qobj) {
disconnect(state->qobj, SIGNAL(destroyed(QObject*)), this, SLOT(movingItemDestroyed(QObject*)));
connect(state->qobj, SIGNAL(destroyed(QObject*)), this, SLOT(movingItemDestroyed(QObject*)));
}
d->movingItems[item] = state;
d->performMovement(0, state);
if (!d->timerId) {
d->timerId = startTimer(MIN_TICK_RATE);
d->time.restart();
}
return state->id;
}
int Animator::customAnimation(int frames, int duration, Animator::CurveShape curve,
QObject *receiver, const char *slot)
{
if (frames < 1 || duration < 1 || !receiver || !slot) {
return -1;
}
CustomAnimationState *state = new CustomAnimationState;
state->id = ++d->animId;
state->frames = frames;
state->currentFrame = 0;
state->curve = curve;
state->interval = duration / qreal(state->frames);
state->interval = qMax(MIN_TICK_RATE_INT, state->interval - (state->interval % MIN_TICK_RATE_INT));
state->currentInterval = state->interval;
state->receiver = receiver;
state->slot = qstrdup(slot);
d->customAnims[state->id] = state;
disconnect(receiver, SIGNAL(destroyed(QObject*)),
this, SLOT(customAnimReceiverDestroyed(QObject*)));
connect(receiver, SIGNAL(destroyed(QObject*)),
this, SLOT(customAnimReceiverDestroyed(QObject*)));
// try with only progress as argument
if (!QMetaObject::invokeMethod(receiver, slot, Q_ARG(qreal, 0))) {
//try to pass also the animation id
QMetaObject::invokeMethod(receiver, slot, Q_ARG(qreal, 0), Q_ARG(int, state->id));
}
if (!d->timerId) {
d->timerId = startTimer(MIN_TICK_RATE);
d->time.restart();
}
return state->id;
}
void Animator::stopCustomAnimation(int id)
{
QMap<int, CustomAnimationState*>::iterator it = d->customAnims.find(id);
if (it != d->customAnims.end()) {
delete [] it.value()->slot;
delete it.value();
d->customAnims.erase(it);
}
//kDebug() << "stopCustomAnimation(AnimId " << id << ") done";
}
void Animator::stopItemAnimation(int id)
{
QMutableMapIterator<QGraphicsItem*, AnimationState*> it(d->animatedItems);
while (it.hasNext()) {
it.next();
if (it.value()->id == id) {
delete it.value();
it.remove();
return;
}
}
}
void Animator::stopItemMovement(int id)
{
QMutableMapIterator<QGraphicsItem*, MovementState*> it(d->movingItems);
while (it.hasNext()) {
it.next();
if (it.value()->id == id) {
delete it.value();
it.remove();
return;
}
}
}
int Animator::animateElement(QGraphicsItem *item, Animation animation)
{
//kDebug() << "startElementAnimation(AnimId " << animation << ")";
int frames = d->driver->elementAnimationFps(animation);
int duration = d->driver->animationDuration(animation);
ElementAnimationState *state = new ElementAnimationState;
state->item = item;
state->curve = d->driver->elementAnimationCurve(animation);
state->animation = animation;
state->frames = qMax(1.0, frames * (duration / 1000.0));
state->currentFrame = 0;
state->interval = duration / qreal(state->frames);
state->interval = qMax(MIN_TICK_RATE_INT, state->interval - (state->interval % MIN_TICK_RATE_INT));
state->currentInterval = state->interval;
state->id = ++d->animId;
state->qobj = dynamic_cast<QObject*>(item);
if (state->qobj) {
disconnect(state->qobj, SIGNAL(destroyed(QObject*)),
this, SLOT(animatedElementDestroyed(QObject*)));
connect(state->qobj, SIGNAL(destroyed(QObject*)),
this, SLOT(animatedElementDestroyed(QObject*)));
}
//kDebug() << "animateElement " << animation << ", interval: "
// << state->interval << ", frames: " << state->frames;
bool needTimer = true;
if (state->frames < 1) {
state->frames = 1;
state->currentFrame = 1;
needTimer = false;
}
d->animatedElements[state->id] = state;
//kDebug() << "startElementAnimation(AnimId " << animation << ") returning " << state->id;
if (needTimer && !d->timerId) {
// start a 20fps timer;
//TODO: should be started at the maximum frame rate needed only?
d->timerId = startTimer(MIN_TICK_RATE);
d->time.restart();
}
return state->id;
}
void Animator::stopElementAnimation(int id)
{
QMap<int, ElementAnimationState*>::iterator it = d->animatedElements.find(id);
if (it != d->animatedElements.end()) {
delete it.value();
d->animatedElements.erase(it);
}
//kDebug() << "stopElementAnimation(AnimId " << id << ") done";
}
void Animator::setInitialPixmap(int id, const QPixmap &pixmap)
{
QMap<int, ElementAnimationState*>::iterator it = d->animatedElements.find(id);
if (it == d->animatedElements.end()) {
kDebug() << "No entry found for id " << id;
return;
}
it.value()->pixmap = pixmap;
}
QPixmap Animator::currentPixmap(int id)
{
QMap<int, ElementAnimationState*>::const_iterator it = d->animatedElements.find(id);
if (it == d->animatedElements.constEnd()) {
//kDebug() << "Animator::currentPixmap(" << id << ") found no entry for it!";
return QPixmap();
}
ElementAnimationState *state = it.value();
qreal progress = d->calculateProgress(state->currentFrame * state->interval,
state->frames * state->interval,
state->curve);
//kDebug() << "Animator::currentPixmap(" << id << " at " << progress;
switch (state->animation) {
case AppearAnimation:
return d->driver->elementAppear(progress, state->pixmap);
break;
case DisappearAnimation:
return d->driver->elementDisappear(progress, state->pixmap);
break;
case ActivateAnimation:
break;
}
return state->pixmap;
}
bool Animator::isAnimating() const
{
return (!d->animatedItems.isEmpty() ||
!d->movingItems.isEmpty() ||
!d->animatedElements.isEmpty() ||
!d->customAnims.isEmpty());
}
void Animator::timerEvent(QTimerEvent *event)
{
Q_UNUSED(event)
bool animationsRemain = false;
int elapsed = MIN_TICK_RATE;
if (d->time.elapsed() > elapsed) {
elapsed = d->time.elapsed();
}
d->time.restart();
//kDebug() << "timeEvent, elapsed time: " << elapsed;
foreach (AnimationState *state, d->animatedItems) {
if (state->currentInterval <= elapsed) {
// we need to step forward!
state->currentFrame +=
(KGlobalSettings::graphicEffectsLevel() & KGlobalSettings::SimpleAnimationEffects) ?
qMax(1, elapsed / state->interval) : state->frames - state->currentFrame;
if (state->currentFrame < state->frames) {
qreal progress = d->calculateProgress(state->currentFrame * state->interval,
state->frames * state->interval,
state->curve);
d->performAnimation(progress, state);
state->currentInterval = state->interval;
animationsRemain = true;
} else {
d->performAnimation(1, state);
d->animatedItems.erase(d->animatedItems.find(state->item));
emit animationFinished(state->item, state->animation);
delete state;
}
} else {
state->currentInterval -= elapsed;
animationsRemain = true;
}
}
foreach (MovementState *state, d->movingItems) {
if (state->currentInterval <= elapsed) {
// we need to step forward!
state->currentFrame +=
(KGlobalSettings::graphicEffectsLevel() & KGlobalSettings::SimpleAnimationEffects) ?
qMax(1, elapsed / state->interval) : state->frames - state->currentFrame;
if (state->currentFrame < state->frames) {
//kDebug() << "movement";
qreal progress = d->calculateProgress(state->currentFrame * state->interval,
state->frames * state->interval,
state->curve);
d->performMovement(progress, state);
animationsRemain = true;
} else {
//kDebug() << "movement";
d->performMovement(1, state);
d->movingItems.erase(d->movingItems.find(state->item));
emit movementFinished(state->item);
delete state;
}
} else {
state->currentInterval -= elapsed;
animationsRemain = true;
}
}
foreach (ElementAnimationState *state, d->animatedElements) {
if (state->currentFrame == state->frames) {
//kDebug() << "skipping" << state->id << "as its already at frame"
// << state->currentFrame << "of" << state->frames;
// since we keep element animations around until they are
// removed, we will end up with finished animations in the queue;
// just skip them
//TODO: should we move them to a separate QMap?
continue;
}
if (state->currentInterval <= elapsed) {
// we need to step forward!
/*kDebug() << "stepping forwards element anim " << state->id
<< " from " << state->currentFrame
<< " by " << qMax(1, elapsed / state->interval) << " to "
<< state->currentFrame + qMax(1, elapsed / state->interval) << endl;*/
state->currentFrame +=
(KGlobalSettings::graphicEffectsLevel() & KGlobalSettings::SimpleAnimationEffects) ?
qMax(1, elapsed / state->interval) : state->frames - state->currentFrame;
state->item->update();
if (state->currentFrame < state->frames) {
state->currentInterval = state->interval;
animationsRemain = true;
} else {
d->animatedElements.remove(state->id);
emit elementAnimationFinished(state->id);
delete state;
}
} else {
state->currentInterval -= elapsed;
animationsRemain = true;
}
}
foreach (CustomAnimationState *state, d->customAnims) {
if (state->currentInterval <= elapsed) {
// advance the frame
state->currentFrame +=
(KGlobalSettings::graphicEffectsLevel() & KGlobalSettings::SimpleAnimationEffects) ?
qMax(1, elapsed / state->interval) : state->frames - state->currentFrame;
/*kDebug() << "custom anim for" << state->receiver
<< "to slot" << state->slot
<< "with interval of" << state->interval
<< "at frame" << state->currentFrame;*/
if (state->currentFrame < state->frames) {
//kDebug () << "not the final frame";
//TODO: calculate a proper interval based on the curve
state->currentInterval = state->interval;
animationsRemain = true;
// signal the object
// try with only progress as argument
qreal progress = d->calculateProgress(state->currentFrame * state->interval,
state->frames * state->interval,
state->curve);
if (!QMetaObject::invokeMethod(state->receiver, state->slot, Q_ARG(qreal, progress))) {
//if fails try to add the animation id
QMetaObject::invokeMethod(state->receiver, state->slot, Q_ARG(qreal, progress),
Q_ARG(int, state->id));
}
} else {
if (!QMetaObject::invokeMethod(state->receiver, state->slot, Q_ARG(qreal, 1))) {
QMetaObject::invokeMethod(state->receiver, state->slot, Q_ARG(qreal, 1), Q_ARG(int, state->id));
}
d->customAnims.erase(d->customAnims.find(state->id));
emit customAnimationFinished(state->id);
delete [] state->slot;
delete state;
}
} else {
state->currentInterval -= elapsed;
animationsRemain = true;
}
}
if (!animationsRemain && d->timerId) {
killTimer(d->timerId);
d->timerId = 0;
}
}
void AnimatorPrivate::init(Animator *q)
{
//FIXME: usage between different applications?
KConfig c("plasmarc");
KConfigGroup cg(&c, "Animator");
QString pluginName = cg.readEntry("driver", "default");
if (!pluginName.isEmpty()) {
QString constraint = QString("[X-KDE-PluginInfo-Name] == '%1'").arg(pluginName);
KService::List offers = KServiceTypeTrader::self()->query("Plasma/Animator", constraint);
if (!offers.isEmpty()) {
QString error;
KPluginLoader plugin(*offers.first());
if (Plasma::isPluginVersionCompatible(plugin.pluginVersion())) {
driver = offers.first()->createInstance<Plasma::AnimationDriver>(0, QVariantList(), &error);
}
if (!driver) {
kDebug() << "Could not load requested animator "
<< offers.first() << ". Error given: " << error;
}
}
}
if (!driver) {
driver = new AnimationDriver(q);
}
}
} // namespace Plasma
#include <animator.moc>

180
animator.h Normal file
View File

@ -0,0 +1,180 @@
/*
* Copyright 2007 Aaron Seigo <aseigo@kde.org>
* 2007 Alexis Ménard <darktears31@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Library General Public License as
* published by the Free Software Foundation; either version 2, 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 Library General Public
* License along with this program; if not, write to the
* Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef PLASMA_ANIMATOR_H
#define PLASMA_ANIMATOR_H
#include <QtGui/QImage>
#include <QtCore/QObject>
#include <plasma/plasma_export.h>
class QGraphicsItem;
class QTimeLine;
namespace Plasma
{
class AnimatorPrivate;
/**
* @class Animator plasma/animator.h <Plasma/Animator>
*
* @short A system for applying effects to Plasma elements
*/
class PLASMA_EXPORT Animator : public QObject
{
Q_OBJECT
Q_ENUMS(Animation)
Q_ENUMS(CurveShape)
Q_ENUMS(Movement)
public:
enum Animation {
AppearAnimation = 0, /*<< Animate the appearance of an element */
DisappearAnimation, /*<< Animate the disappearance of an element */
ActivateAnimation /*<< When something is activated or launched,
such as an app icon being clicked */
};
enum CurveShape {
EaseInCurve = 0,
EaseOutCurve,
EaseInOutCurve,
LinearCurve
};
enum Movement {
SlideInMovement = 0,
SlideOutMovement,
FastSlideInMovement,
FastSlideOutMovement
};
/**
* Singleton accessor
**/
static Animator *self();
/**
* Starts a standard animation on a QGraphicsItem.
*
* @arg item the item to animate in some fashion
* @arg anim the type of animation to perform
* @return the id of the animation
**/
Q_INVOKABLE int animateItem(QGraphicsItem *item, Animation anim);
/**
* Stops an item animation before the animation is complete.
* Note that it is not necessary to call
* this on normal completion of the animation.
*
* @arg id the id of the animation as returned by animateItem
*/
Q_INVOKABLE void stopItemAnimation(int id);
/**
* Starts a standard animation on a QGraphicsItem.
*
* @arg item the item to animate in some fashion
* @arg anim the type of animation to perform
* @return the id of the animation
**/
Q_INVOKABLE int moveItem(QGraphicsItem *item, Movement movement, const QPoint &destination);
/**
* Stops an item movement before the animation is complete.
* Note that it is not necessary to call
* this on normal completion of the animation.
*
* @arg id the id of the animation as returned by moveItem
*/
Q_INVOKABLE void stopItemMovement(int id);
/**
* Starts a custom animation, preventing the need to create a timeline
* with its own timer tick.
*
* @arg frames the number of frames this animation should persist for
* @arg duration the length, in milliseconds, the animation will take
* @arg curve the curve applied to the frame rate
* @arg receive the object that will handle the actual animation
* @arg method the method name of slot to be invoked on each update.
* It must take a qreal. So if the slot is animate(qreal),
* pass in "animate" as the method parameter.
* It has an optional integer paramenter that takes an
* integer that reapresents the animation id, useful if
* you want to manage multiple animations with a sigle slot
*
* @return an id that can be used to identify this animation.
*/
Q_INVOKABLE int customAnimation(int frames, int duration, Animator::CurveShape curve,
QObject *receiver, const char *method);
/**
* Stops a custom animation. Note that it is not necessary to call
* this on object destruction, as custom animations associated with
* a given QObject are cleaned up automatically on QObject destruction.
*
* @arg id the id of the animation as returned by customAnimation
*/
Q_INVOKABLE void stopCustomAnimation(int id);
Q_INVOKABLE int animateElement(QGraphicsItem *obj, Animation);
Q_INVOKABLE void stopElementAnimation(int id);
Q_INVOKABLE void setInitialPixmap(int id, const QPixmap &pixmap);
Q_INVOKABLE QPixmap currentPixmap(int id);
/**
* Can be used to query if there are other animations happening. This way
* heavy operations can be delayed until all animations are finished.
* @return true if there are animations going on.
* @since 4.1
*/
Q_INVOKABLE bool isAnimating() const;
Q_SIGNALS:
void animationFinished(QGraphicsItem *item, Plasma::Animator::Animation anim);
void movementFinished(QGraphicsItem *item);
void elementAnimationFinished(int id);
void customAnimationFinished(int id);
protected:
void timerEvent(QTimerEvent *event);
private:
friend class AnimatorSingleton;
explicit Animator(QObject * parent = 0);
~Animator();
Q_PRIVATE_SLOT(d, void animatedItemDestroyed(QObject*))
Q_PRIVATE_SLOT(d, void movingItemDestroyed(QObject*))
Q_PRIVATE_SLOT(d, void animatedElementDestroyed(QObject*))
Q_PRIVATE_SLOT(d, void customAnimReceiverDestroyed(QObject*))
AnimatorPrivate * const d;
};
} // namespace Plasma
#endif

1957
applet.cpp Normal file

File diff suppressed because it is too large Load Diff

857
applet.h Normal file
View File

@ -0,0 +1,857 @@
/*
* Copyright 2006-2007 by Aaron Seigo <aseigo@kde.org>
* Copyright 2007 by Riccardo Iaconelli <riccardo@kde.org>
* Copyright 2008 by Ménard Alexis <darktears31@gmail.com>
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Library General Public License as
* published by the Free Software Foundation; either version 2, 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 Library General Public
* License along with this program; if not, write to the
* Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef PLASMA_APPLET_H
#define PLASMA_APPLET_H
#include <QtGui/QGraphicsItem>
#include <QtGui/QWidget>
#include <QtGui/QGraphicsWidget>
#include <KDE/KConfigGroup>
#include <KDE/KGenericFactory>
#include <KDE/KPluginInfo>
#include <KDE/KShortcut>
#include <plasma/configloader.h>
#include <plasma/packagestructure.h>
#include <plasma/plasma.h>
#include <plasma/animator.h>
#include <plasma/version.h>
class KConfigDialog;
class QGraphicsView;
class KActionCollection;
namespace Plasma
{
class AppletPrivate;
class Containment;
class Context;
class DataEngine;
class Extender;
class ExtenderItem;
class Package;
/**
* @class Applet plasma/applet.h <Plasma/Applet>
*
* @short The base Applet class
*
* Applet provides several important roles for add-ons widgets in Plasma.
*
* First, it is the base class for the plugin system and therefore is the
* interface to applets for host applications. It also handles the life time
* management of data engines (e.g. all data engines accessed via
* Applet::dataEngine(const QString&) are properly deref'd on Applet
* destruction), background painting (allowing for consistent and complex
* look and feel in just one line of code for applets), loading and starting
* of scripting support for each applet, providing access to the associated
* plasmoid package (if any) and access to configuration data.
*
* See techbase.kde.org for tutorial on writing Applets using this class.
*/
class PLASMA_EXPORT Applet : public QGraphicsWidget
{
Q_OBJECT
Q_PROPERTY(bool hasConfigurationInterface READ hasConfigurationInterface)
Q_PROPERTY(QString name READ name)
Q_PROPERTY(QString category READ category)
Q_PROPERTY(ImmutabilityType immutability READ immutability WRITE setImmutability)
Q_PROPERTY(bool hasFailedToLaunch READ hasFailedToLaunch WRITE setFailedToLaunch)
Q_PROPERTY(bool configurationRequired READ configurationRequired WRITE setConfigurationRequired)
Q_PROPERTY(QRectF geometry READ geometry WRITE setGeometry)
Q_PROPERTY(bool shouldConserveResources READ shouldConserveResources)
public:
typedef QList<Applet*> List;
typedef QHash<QString, Applet*> Dict;
/**
* Description on how draw a background for the applet
*/
enum BackgroundHint {
NoBackground = 0, /**< Not drawing a background under the
applet, the applet has its own implementation */
StandardBackground = 1, /**< The standard background from the theme is drawn */
TranslucentBackground = 2, /**< An alternate version of the background is drawn,
usually more translucent */
DefaultBackground = StandardBackground /**< Default settings:
both standard background */
};
Q_DECLARE_FLAGS(BackgroundHints, BackgroundHint)
~Applet();
/**
* @return a package structure representing a Theme
*/
static PackageStructure::Ptr packageStructure();
/**
* @return the id of this applet
*/
uint id() const;
/**
* Returns the KConfigGroup to access the applets configuration.
*
* This config object will write to an instance
* specific config file named \<appletname\>\<instanceid\>rc
* in the Plasma appdata directory.
**/
KConfigGroup config() const;
/**
* Returns a config group with the name provided. This ensures
* that the group name is properly namespaced to avoid collision
* with other applets that may be sharing this config file
*
* @param group the name of the group to access
**/
KConfigGroup config(const QString &group) const;
/**
* Saves state information about this applet that will
* be accessed when next instantiated in the restore(KConfigGroup&) method.
*
* This method does not need to be reimplmented by Applet
* subclasses, but can be useful for Applet specializations
* (such as Containment) to do so.
*
* Applet subclasses may instead want to reimplement saveState().
**/
virtual void save(KConfigGroup &group) const;
/**
* Restores state information about this applet saved previously
* in save(KConfigGroup&).
*
* This method does not need to be reimplmented by Applet
* subclasses, but can be useful for Applet specializations
* (such as Containment) to do so.
**/
virtual void restore(KConfigGroup &group);
/**
* Returns a KConfigGroup object to be shared by all applets of this
* type.
*
* This config object will write to an applet-specific config object
* named plasma_\<appletname\>rc in the local config directory.
*/
KConfigGroup globalConfig() const;
/**
* Returns the config skeleton object from this applet's package,
* if any.
*
* @return config skeleton object, or 0 if none
**/
ConfigLoader *configScheme() const;
/**
* Loads the given DataEngine
*
* Tries to load the data engine given by @p name. Each engine is
* only loaded once, and that instance is re-used on all subsequent
* requests.
*
* If the data engine was not found, an invalid data engine is returned
* (see DataEngine::isValid()).
*
* Note that you should <em>not</em> delete the returned engine.
*
* @param name Name of the data engine to load
* @return pointer to the data engine if it was loaded,
* or an invalid data engine if the requested engine
* could not be loaded
*/
Q_INVOKABLE DataEngine *dataEngine(const QString &name) const;
/**
* Accessor for the associated Package object if any.
* Generally, only Plasmoids come in a Package.
*
* @return the Package object, or 0 if none
**/
const Package *package() const;
/**
* Returns the view this widget is visible on, or 0 if none can be found.
* @warning do NOT assume this will always return a view!
* a null view probably means that either plasma isn't finished loading, or your applet is
* on an activity that's not being shown anywhere.
*/
QGraphicsView *view() const;
/**
* Maps a QRect from a view's coordinates to local coordinates.
* @param view the view from which rect should be mapped
* @param rect the rect to be mapped
*/
QRectF mapFromView(const QGraphicsView *view, const QRect &rect) const;
/**
* Maps a QRectF from local coordinates to a view's coordinates.
* @param view the view to which rect should be mapped
* @param rect the rect to be mapped
*/
QRect mapToView(const QGraphicsView *view, const QRectF &rect) const;
/**
* Reccomended position for a popup window like a menu or a tooltip
* given its size
* @param s size of the popup
* @returns reccomended position
*/
QPoint popupPosition(const QSize &s) const;
/**
* Called when any of the geometry constraints have been updated.
* This method calls constraintsEvent, which may be reimplemented,
* once the Applet has been prepared for updating the constraints.
*
* @param constraints the type of constraints that were updated
*/
void updateConstraints(Plasma::Constraints constraints = Plasma::AllConstraints);
/**
* Returns the current form factor the applet is being displayed in.
*
* @see Plasma::FormFactor
*/
virtual FormFactor formFactor() const;
/**
* Returns the location of the scene which is displaying applet.
*
* @see Plasma::Location
*/
virtual Location location() const;
/**
* Returns the workspace context which the applet is operating in
*/
Context *context() const;
/**
* @return the preferred aspect ratio mode for placement and resizing
*/
Plasma::AspectRatioMode aspectRatioMode() const;
/**
* Sets the preferred aspect ratio mode for placement and resizing
*/
void setAspectRatioMode(Plasma::AspectRatioMode);
/**
* Returns a list of all known applets.
*
* @param category Only applets matchin this category will be returned.
* Useful in conjunction with knownCategories.
* If "Misc" is passed in, then applets without a
* Categories= entry are also returned.
* If an empty string is passed in, all applets are
* returned.
* @param parentApp the application to filter applets on. Uses the
* X-KDE-ParentApp entry (if any) in the plugin info.
* The default value of QString() will result in a
* list containing only applets not specifically
* registered to an application.
* @return list of applets
**/
static KPluginInfo::List listAppletInfo(const QString &category = QString(),
const QString &parentApp = QString());
/**
* Returns a list of all known applets associated with a certain mimetype.
*
* @return list of applets
**/
static KPluginInfo::List listAppletInfoForMimetype(const QString &mimetype);
/**
* Returns a list of all the categories used by
* installed applets.
*
* @param parentApp the application to filter applets on. Uses the
* X-KDE-ParentApp entry (if any) in the plugin info.
* The default value of QString() will result in a
* list containing only applets not specifically
* registered to an application.
* @return list of categories
* @param visibleOnly true if it should only return applets that are marked as visible
*/
static QStringList listCategories(const QString &parentApp = QString(),
bool visibleOnly = true);
/**
* Attempts to load an applet
*
* Returns a pointer to the applet if successful.
* The caller takes responsibility for the applet, including
* deleting it when no longer needed.
*
* @param name the plugin name, as returned by KPluginInfo::pluginName()
* @param appletId unique ID to assign the applet, or zero to have one
* assigned automatically.
* @param args to send the applet extra arguments
* @return a pointer to the loaded applet, or 0 on load failure
**/
static Applet *load(const QString &name, uint appletId = 0,
const QVariantList &args = QVariantList());
/**
* Attempts to load an applet
*
* Returns a pointer to the applet if successful.
* The caller takes responsibility for the applet, including
* deleting it when no longer needed.
*
* @param info KPluginInfo object for the desired applet
* @param appletId unique ID to assign the applet, or zero to have one
* assigned automatically.
* @param args to send the applet extra arguments
* @return a pointer to the loaded applet, or 0 on load failure
**/
static Applet *load(const KPluginInfo &info, uint appletId = 0,
const QVariantList &args = QVariantList());
/**
* Get the category of the given applet
*
* @param applet a KPluginInfo object for the applet
*/
static QString category(const KPluginInfo &applet);
/**
* Get the category of the given applet
*
* @param appletName the name of the applet
*/
static QString category(const QString &appletName);
/**
* This method is called when the interface should be painted.
*
* @param painter the QPainter to use to do the paintiner
* @param option the style options object
* @param contentsRect the rect to paint within; automatically adjusted for
* the background, if any
**/
virtual void paintInterface(QPainter *painter,
const QStyleOptionGraphicsItem *option,
const QRect &contentsRect);
/**
* Returns the user-visible name for the applet, as specified in the
* .desktop file.
*
* @return the user-visible name for the applet.
**/
QString name() const;
/**
* @return the font currently set for this widget
**/
QFont font() const;
/**
* Returns the plugin name for the applet
*/
QString pluginName() const;
/**
* Whether the applet should conserve resources. If true, try to avoid doing stuff which
* is computationally heavy. Try to conserve power and resources.
*
* @return true if it should conserve resources, false if it does not.
*/
bool shouldConserveResources() const;
/**
* Returns the icon related to this applet
**/
QString icon() const;
/**
* Returns the category the applet is in, as specified in the
* .desktop file.
*/
QString category() const;
/**
* @return The type of immutability of this applet
*/
ImmutabilityType immutability() const;
void paintWindowFrame(QPainter *painter,
const QStyleOptionGraphicsItem *option, QWidget *widget);
/**
* If for some reason, the applet fails to get up on its feet (the
* library couldn't be loaded, necessary hardware support wasn't found,
* etc..) this method returns true
**/
bool hasFailedToLaunch() const;
/**
* @return true if the applet currently needs to be configured,
* otherwise, false
*/
bool configurationRequired() const;
/**
* @return true if this plasmoid provides a GUI configuration
**/
bool hasConfigurationInterface() const;
/**
* Returns a list of context-related QAction instances.
*
* This is used e.g. within the \a DesktopView to display a
* contextmenu.
*
* @return A list of actions. The default implementation returns an
* empty list.
**/
virtual QList<QAction*> contextualActions();
/**
* Returns the QAction with the given name from our collection
*/
QAction *action(QString name) const;
/**
* Adds the action to our collection under the given name
*/
void addAction(QString name, QAction *action);
/**
* Sets the BackgroundHints for this applet @see BackgroundHint
*
* @param hints the BackgroundHint combination for this applet
*/
void setBackgroundHints(const BackgroundHints hints);
/**
* @return BackgroundHints flags combination telling if the standard background is shown
* and if it has a drop shadow
*/
BackgroundHints backgroundHints() const;
/**
* @return true if this Applet is currently being used as a Containment, false otherwise
*/
bool isContainment() const;
/**
* This method returns screen coordinates for the widget; this method can be somewhat
* expensive and should ONLY be called when screen coordinates are required. For
* example when positioning top level widgets on top of the view to create the
* appearance of unit. This should NOT be used for popups (@see popupPosition) or
* for normal widget use (use Plasma:: widgets or QGraphicsProxyWidget instead).
*
* @return a rect of the applet in screen coordinates.
*/
QRect screenRect() const;
/**
* Reimplemented from QGraphicsItem
**/
int type() const;
enum {
Type = Plasma::AppletType
};
/**
* @return the Containment, if any, this applet belongs to
**/
Containment *containment() const;
/**
* Sets the global shorcut to associate with this widget.
*/
void setGlobalShortcut(const KShortcut &shortcut);
/**
* @return the global shortcut associated with this wiget, or
* an empty shortcut if no global shortcut is associated.
*/
KShortcut globalShortcut() const;
/**
* associate actions with this widget, including ones added after this call.
* needed to make keyboard shortcuts work.
*/
virtual void addAssociatedWidget(QWidget *widget);
/**
* un-associate actions from this widget, including ones added after this call.
* needed to make keyboard shortcuts work.
*/
virtual void removeAssociatedWidget(QWidget *widget);
/**
* Gets called when and extender item has to be initialized after a plasma restart. If you
* create ExtenderItems in your applet, you should implement this function to again create
* the widget that should be shown in this extender item. This function might look something
* like this:
*
* @code
* SuperCoolWidget *widget = new SuperCoolWidget();
* dataEngine("engine")->connectSource(item->config("dataSourceName"), widget);
* item->setWidget(widget);
* @endcode
*
* You can also add one or more custom qactions to this extender item in this function.
*
* Note that by default, not all ExtenderItems are persistent. Only items that are detached,
* will have their configuration stored when plasma exits.
*/
virtual void initExtenderItem(ExtenderItem *item);
/**
* @param parent the QGraphicsItem this applet is parented to
* @param serviceId the name of the .desktop file containing the
* information about the widget
* @param appletId a unique id used to differentiate between multiple
* instances of the same Applet type
*/
explicit Applet(QGraphicsItem *parent = 0,
const QString &serviceId = QString(),
uint appletId = 0);
Q_SIGNALS:
/**
* This signal indicates that an application launch, window
* creation or window focus event was triggered. This is used, for instance,
* to ensure that the Dashboard view in Plasma hides when such an event is
* triggered by an item it is displaying.
*/
void releaseVisualFocus();
/**
* Emitted whenever the applet makes a geometry change, so that views
* can coordinate themselves with these changes if they desire.
*/
void geometryChanged();
/**
* Emitted by Applet subclasses when they change a sizeHint and wants to announce the change
*/
void sizeHintChanged(Qt::SizeHint which);
/**
* Emitted when an applet has changed values in its configuration
* and wishes for them to be saved at the next save point. As this implies
* disk activity, this signal should be used with care.
*
* @note This does not need to be emitted from saveState by individual
* applets.
*/
void configNeedsSaving();
/**
* Emitted when activation is requested due to, for example, a global
* keyboard shortcut. By default the wiget is given focus.
*/
void activate();
public Q_SLOTS:
/**
* Sets the immutability type for this applet (not immutable,
* user immutable or system immutable)
* @arg immutable the new immutability type of this applet
*/
void setImmutability(const ImmutabilityType immutable);
/**
* Destroys the applet; it will be removed nicely and deleted.
* Its configuration will also be deleted.
*/
virtual void destroy();
/**
* Lets the user interact with the plasmoid options.
* Called when the user selects the configure entry
* from the context menu.
*
* Unless there is good reason for overriding this method,
* Applet subclasses should actually override createConfigurationInterface
* instead. A good example of when this isn't plausible is
* when using a dialog prepared by another library, such
* as KPropertiesDialog from libkfile.
*/
virtual void showConfigurationInterface();
/**
* Causes this applet to raise above all other applets.
*/
void raise();
/**
* Causes this applet to lower below all the other applets.
*/
void lower();
/**
* Sends all pending contraints updates to the applet. Will usually
* be called automatically, but can also be called manually if needed.
*/
void flushPendingConstraintsEvents();
/**
* This method is called once the applet is loaded and added to a Corona.
* If the applet requires a QGraphicsScene or has an particularly intensive
* set of initialization routines to go through, consider implementing it
* in this method instead of the constructor.
**/
virtual void init();
/**
* Called when applet configuration values has changed.
*/
virtual void configChanged();
protected:
/**
* This constructor is to be used with the plugin loading systems
* found in KPluginInfo and KService. The argument list is expected
* to have two elements: the KService service ID for the desktop entry
* and an applet ID which must be a base 10 number.
*
* @param parent a QObject parent; you probably want to pass in 0
* @param args a list of strings containing two entries: the service id
* and the applet id
*/
Applet(QObject *parent, const QVariantList &args);
/**
* Call this method when the applet fails to launch properly. An
* optional reason can be provided.
*
* Not that all children items will be deleted when this method is
* called. If you have pointers to these items, you will need to
* reset them after calling this method.
*
* @param failed true when the applet failed, false when it succeeded
* @param reason an optional reason to show the user why the applet
* failed to launch
**/
void setFailedToLaunch(bool failed, const QString &reason = QString());
/**
* When called, the Applet should write any information needed as part
* of the Applet's running state to the configuration object in config()
* and/or globalConfig().
*
* Applets that always sync their settings/state with the config
* objects when these settings/states change do not need to reimplement
* this method.
**/
virtual void saveState(KConfigGroup &config) const;
/**
* Sets whether or not this applet provides a user interface for
* configuring the applet.
*
* It defaults to false, and if true is passed in you should
* also reimplement createConfigurationInterface()
*
* @param hasInterface whether or not there is a user interface available
**/
void setHasConfigurationInterface(bool hasInterface);
/**
* When the applet needs to be configured before being usable, this
* method can be called to show a standard interface prompting the user
* to configure the applet
*
* Not that all children items will be deleted when this method is
* called. If you have pointers to these items, you will need to
* reset them after calling this method.
*
* @param needsConfiguring true if the applet needs to be configured,
* or false if it doesn't
*/
void setConfigurationRequired(bool needsConfiguring, const QString &reason = QString());
/**
* Reimplement this method so provide a configuration interface,
* parented to the supplied widget. Ownership of the widgets is passed
* to the parent widget.
*
* @param parent the dialog which is the parent of the configuration
* widgets
*/
virtual void createConfigurationInterface(KConfigDialog *parent);
/**
* Called when any of the geometry constraints have been updated.
*
* This is always called prior to painting and should be used as an
* opportunity to layout the widget, calculate sizings, etc.
*
* Do not call update() from this method; an update() will be triggered
* at the appropriate time for the applet.
*
* @param constraints the type of constraints that were updated
* @property constraint
*/
virtual void constraintsEvent(Plasma::Constraints constraints);
/**
* Register the widgets that manage mouse clicks but you still want
* to be able to drag the applet around when holding the mouse pointer
* on that widget.
*
* Calling this results in an eventFilter being places on the widget.
*
* @param item the item to watch for mouse move
*/
void registerAsDragHandle(QGraphicsItem *item);
/**
* Unregister a widget registered with registerAsDragHandle.
*
* @param item the item to unregister
*/
void unregisterAsDragHandle(QGraphicsItem *item);
/**
* @param item the item to look for if it is registered or not
* @return true if it is registered, false otherwise
*/
bool isRegisteredAsDragHandle(QGraphicsItem *item);
/**
* @return the extender of this applet.
*/
Extender *extender() const;
/**
* @internal event filter; used for focus watching
**/
bool eventFilter(QObject *o, QEvent *e);
/**
* @internal scene event filter; used to manage applet dragging
*/
bool sceneEventFilter (QGraphicsItem *watched, QEvent *event);
/**
* @internal manage the mouse movement to drag the applet around
*/
void mouseMoveEvent(QGraphicsSceneMouseEvent *event);
/**
* @internal manage the mouse movement to drag the applet around
*/
void mousePressEvent(QGraphicsSceneMouseEvent *event);
/**
* Reimplemented from QGraphicsItem
*/
void focusInEvent(QFocusEvent *event);
/**
* Reimplemented from QGraphicsItem
*/
void resizeEvent(QGraphicsSceneResizeEvent *event);
/**
* Reimplemented from QGraphicsItem
*/
QVariant itemChange(GraphicsItemChange change, const QVariant &value);
/**
* Reimplemented from QGraphicsItem
*/
QPainterPath shape() const;
/**
* Reimplemented from QGraphicsLayoutItem
*/
QSizeF sizeHint(Qt::SizeHint which, const QSizeF & constraint = QSizeF()) const;
/**
* Reimplemented from QGraphicsLayoutItem
*/
void hoverEnterEvent(QGraphicsSceneHoverEvent *event);
/**
* Reimplemented from QGraphicsLayoutItem
*/
void hoverLeaveEvent(QGraphicsSceneHoverEvent *event);
/**
* Reimplemented from QObject
*/
void timerEvent (QTimerEvent *event);
private:
Q_PRIVATE_SLOT(d, void setFocus())
Q_PRIVATE_SLOT(d, void checkImmutability())
Q_PRIVATE_SLOT(d, void themeChanged())
Q_PRIVATE_SLOT(d, void appletAnimationComplete(QGraphicsItem *item,
Plasma::Animator::Animation anim))
Q_PRIVATE_SLOT(d, void selectItemToDestroy())
Q_PRIVATE_SLOT(d, void updateRect(const QRectF& rect))
/**
* Reimplemented from QGraphicsItem
**/
void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget = 0);
AppletPrivate *const d;
//Corona needs to access setFailedToLaunch and init
friend class Corona;
friend class CoronaPrivate;
friend class Containment;
friend class ContainmentPrivate;
friend class AppletScript;
friend class AppletHandle;
friend class AppletPrivate;
friend class PopupApplet;
friend class PopupAppletPrivate;
friend class Extender;
friend class ExtenderItem;
};
} // Plasma namespace
Q_DECLARE_OPERATORS_FOR_FLAGS(Plasma::Applet::BackgroundHints)
/**
* Register an applet when it is contained in a loadable module
*/
#define K_EXPORT_PLASMA_APPLET(libname, classname) \
K_PLUGIN_FACTORY(factory, registerPlugin<classname>();) \
K_EXPORT_PLUGIN(factory("plasma_applet_" #libname)) \
K_EXPORT_PLUGIN_VERSION(PLASMA_VERSION)
#endif // multiple inclusion guard

567
configloader.cpp Normal file
View File

@ -0,0 +1,567 @@
/*
* Copyright 2007 Aaron Seigo <aseigo@kde.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Library General Public License as
* published by the Free Software Foundation; either version 2, 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 Library General Public
* License along with this program; if not, write to the
* Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "configloader.h"
#include <QColor>
#include <QFont>
#include <QHash>
#include <QXmlContentHandler>
#include <QXmlInputSource>
#include <QXmlSimpleReader>
#include <KDebug>
#include <KUrl>
namespace Plasma
{
class ConfigLoaderPrivate
{
public:
~ConfigLoaderPrivate()
{
qDeleteAll(bools);
qDeleteAll(strings);
qDeleteAll(stringlists);
qDeleteAll(colors);
qDeleteAll(fonts);
qDeleteAll(ints);
qDeleteAll(uints);
qDeleteAll(urls);
qDeleteAll(dateTimes);
qDeleteAll(doubles);
qDeleteAll(intlists);
qDeleteAll(longlongs);
qDeleteAll(points);
qDeleteAll(rects);
qDeleteAll(sizes);
qDeleteAll(ulonglongs);
qDeleteAll(urllists);
}
bool *newBool()
{
bool *v = new bool;
bools.append(v);
return v;
}
QString *newString()
{
QString *v = new QString;
strings.append(v);
return v;
}
QStringList *newStringList()
{
QStringList *v = new QStringList;
stringlists.append(v);
return v;
}
QColor *newColor()
{
QColor *v = new QColor;
colors.append(v);
return v;
}
QFont *newFont()
{
QFont *v = new QFont;
fonts.append(v);
return v;
}
qint32 *newInt()
{
qint32 *v = new qint32;
ints.append(v);
return v;
}
quint32 *newUint()
{
quint32 *v = new quint32;
uints.append(v);
return v;
}
KUrl *newUrl()
{
KUrl *v = new KUrl;
urls.append(v);
return v;
}
QDateTime *newDateTime()
{
QDateTime *v = new QDateTime;
dateTimes.append(v);
return v;
}
double *newDouble()
{
double *v = new double;
doubles.append(v);
return v;
}
QList<qint32>* newIntList()
{
QList<qint32> *v = new QList<qint32>;
intlists.append(v);
return v;
}
qint64 *newLongLong()
{
qint64 *v = new qint64;
longlongs.append(v);
return v;
}
QPoint *newPoint()
{
QPoint *v = new QPoint;
points.append(v);
return v;
}
QRect *newRect()
{
QRect *v = new QRect;
rects.append(v);
return v;
}
QSize *newSize()
{
QSize *v = new QSize;
sizes.append(v);
return v;
}
quint64 *newULongLong()
{
quint64 *v = new quint64;
ulonglongs.append(v);
return v;
}
KUrl::List *newUrlList()
{
KUrl::List *v = new KUrl::List;
urllists.append(v);
return v;
}
void parse(ConfigLoader *loader, QIODevice *xml);
QList<bool *> bools;
QList<QString *> strings;
QList<QStringList *> stringlists;
QList<QColor *> colors;
QList<QFont *> fonts;
QList<qint32 *> ints;
QList<quint32 *> uints;
QList<KUrl *> urls;
QList<QDateTime *> dateTimes;
QList<double *> doubles;
QList<QList<qint32> *> intlists;
QList<qint64 *> longlongs;
QList<QPoint *> points;
QList<QRect *> rects;
QList<QSize *> sizes;
QList<quint64 *> ulonglongs;
QList<KUrl::List *> urllists;
QString baseGroup;
QStringList groups;
QHash<QString, QString> keysToNames;
};
class ConfigLoaderHandler : public QXmlDefaultHandler
{
public:
ConfigLoaderHandler(ConfigLoader *config, ConfigLoaderPrivate *d);
bool startElement(const QString &namespaceURI, const QString &localName,
const QString &qName, const QXmlAttributes &atts);
bool endElement(const QString &namespaceURI, const QString &localName,
const QString &qName);
bool characters(const QString &ch);
private:
void addItem();
void resetState();
ConfigLoader *m_config;
ConfigLoaderPrivate *d;
int m_min;
int m_max;
QString m_name;
QString m_key;
QString m_type;
QString m_label;
QString m_default;
QString m_cdata;
QString m_whatsThis;
KConfigSkeleton::ItemEnum::Choice m_choice;
QList<KConfigSkeleton::ItemEnum::Choice> m_enumChoices;
bool m_haveMin;
bool m_haveMax;
bool m_inChoice;
};
void ConfigLoaderPrivate::parse(ConfigLoader *loader, QIODevice *xml)
{
QXmlInputSource source(xml);
QXmlSimpleReader reader;
ConfigLoaderHandler handler(loader, this);
reader.setContentHandler(&handler);
reader.parse(&source, false);
}
ConfigLoaderHandler::ConfigLoaderHandler(ConfigLoader *config, ConfigLoaderPrivate *d)
: QXmlDefaultHandler(),
m_config(config),
d(d)
{
resetState();
}
bool ConfigLoaderHandler::startElement(const QString &namespaceURI, const QString &localName,
const QString &qName, const QXmlAttributes &attrs)
{
Q_UNUSED(namespaceURI)
Q_UNUSED(qName)
// kDebug() << "ConfigLoaderHandler::startElement(" << localName << qName;
int numAttrs = attrs.count();
QString tag = localName.toLower();
if (tag == "group") {
QString group;
for (int i = 0; i < numAttrs; ++i) {
QString name = attrs.localName(i).toLower();
if (name == "name") {
//kDebug() << "set group to" << attrs.value(i);
group = attrs.value(i);
}
}
if (group.isEmpty()) {
group = d->baseGroup;
} else {
d->groups.append(group);
if (!d->baseGroup.isEmpty()) {
group = d->baseGroup + '\x1d' + group;
}
}
m_config->setCurrentGroup(group);
} else if (tag == "entry") {
for (int i = 0; i < numAttrs; ++i) {
QString name = attrs.localName(i).toLower();
if (name == "name") {
m_name = attrs.value(i);
} else if (name == "type") {
m_type = attrs.value(i).toLower();
} else if (name == "key") {
m_key = attrs.value(i);
}
}
} else if (tag == "choice") {
m_choice.name.clear();
m_choice.label.clear();
m_choice.whatsThis.clear();
for (int i = 0; i < numAttrs; ++i) {
QString name = attrs.localName(i).toLower();
if (name == "name") {
m_choice.name = attrs.value(i);
}
}
m_inChoice = true;
}
return true;
}
bool ConfigLoaderHandler::characters(const QString &ch)
{
m_cdata.append(ch);
return true;
}
bool ConfigLoaderHandler::endElement(const QString &namespaceURI,
const QString &localName, const QString &qName)
{
Q_UNUSED(namespaceURI)
Q_UNUSED(qName)
// kDebug() << "ConfigLoaderHandler::endElement(" << localName << qName;
QString tag = localName.toLower();
if (tag == "entry") {
addItem();
resetState();
} else if (tag == "label") {
if (m_inChoice) {
m_choice.label = m_cdata.trimmed();
} else {
m_label = m_cdata.trimmed();
}
} else if (tag == "whatsthis") {
if (m_inChoice) {
m_choice.whatsThis = m_cdata.trimmed();
} else {
m_whatsThis = m_cdata.trimmed();
}
} else if (tag == "default") {
m_default = m_cdata.trimmed();
} else if (tag == "min") {
m_min = m_cdata.toInt(&m_haveMin);
} else if (tag == "max") {
m_max = m_cdata.toInt(&m_haveMax);
} else if (tag == "choice") {
m_enumChoices.append(m_choice);
m_inChoice = false;
}
m_cdata.clear();
return true;
}
void ConfigLoaderHandler::addItem()
{
if (m_name.isEmpty()) {
return;
}
KConfigSkeletonItem *item = 0;
if (m_type == "bool") {
bool defaultValue = m_default.toLower() == "true";
item = m_config->addItemBool(m_name, *d->newBool(), defaultValue, m_key);
} else if (m_type == "color") {
item = m_config->addItemColor(m_name, *d->newColor(), QColor(m_default), m_key);
} else if (m_type == "datetime") {
item = m_config->addItemDateTime(m_name, *d->newDateTime(),
QDateTime::fromString(m_default), m_key);
} else if (m_type == "enum") {
m_key = (m_key.isEmpty()) ? m_name : m_key;
KConfigSkeleton::ItemEnum *enumItem =
new KConfigSkeleton::ItemEnum(m_config->currentGroup(),
m_key, *d->newInt(),
m_enumChoices,
m_default.toUInt());
m_config->addItem(enumItem, m_name);
item = enumItem;
} else if (m_type == "font") {
item = m_config->addItemFont(m_name, *d->newFont(), QFont(m_default), m_key);
} else if (m_type == "int") {
KConfigSkeleton::ItemInt *intItem = m_config->addItemInt(m_name, *d->newInt(),
m_default.toInt(), m_key);
if (m_haveMin) {
intItem->setMinValue(m_min);
}
if (m_haveMax) {
intItem->setMaxValue(m_max);
}
item = intItem;
} else if (m_type == "password") {
item = m_config->addItemPassword(m_name, *d->newString(), m_default, m_key);
} else if (m_type == "path") {
item = m_config->addItemPath(m_name, *d->newString(), m_default, m_key);
} else if (m_type == "string") {
item = m_config->addItemString(m_name, *d->newString(), m_default, m_key);
} else if (m_type == "stringlist") {
//FIXME: the split() is naive and will break on lists with ,'s in them
item = m_config->addItemStringList(m_name, *d->newStringList(),
m_default.split(','), m_key);
} else if (m_type == "uint") {
KConfigSkeleton::ItemUInt *uintItem =
m_config->addItemUInt(m_name, *d->newUint(), m_default.toUInt(), m_key);
if (m_haveMin) {
uintItem->setMinValue(m_min);
}
if (m_haveMax) {
uintItem->setMaxValue(m_max);
}
item = uintItem;
} else if (m_type == "url") {
m_key = (m_key.isEmpty()) ? m_name : m_key;
KConfigSkeleton::ItemUrl *urlItem =
new KConfigSkeleton::ItemUrl(m_config->currentGroup(),
m_key, *d->newUrl(),
m_default);
m_config->addItem(urlItem, m_name);
item = urlItem;
} else if (m_type == "double") {
KConfigSkeleton::ItemDouble *doubleItem = m_config->addItemDouble(m_name,
*d->newDouble(), m_default.toDouble(), m_key);
if (m_haveMin) {
doubleItem->setMinValue(m_min);
}
if (m_haveMax) {
doubleItem->setMaxValue(m_max);
}
item = doubleItem;
} else if (m_type == "intlist") {
QStringList tmpList = m_default.split(',');
QList<qint32> defaultList;
foreach (const QString &tmp, tmpList) {
defaultList.append(tmp.toInt());
}
item = m_config->addItemIntList(m_name, *d->newIntList(), defaultList, m_key);
} else if (m_type == "longlong") {
KConfigSkeleton::ItemLongLong *longlongItem = m_config->addItemLongLong(m_name,
*d->newLongLong(), m_default.toLongLong(), m_key);
if (m_haveMin) {
longlongItem->setMinValue(m_min);
}
if (m_haveMax) {
longlongItem->setMaxValue(m_max);
}
item = longlongItem;
/* No addItemPathList in KConfigSkeleton ?
} else if (m_type == "PathList") {
//FIXME: the split() is naive and will break on lists with ,'s in them
item = m_config->addItemPathList(m_name, *d->newStringList(), m_default.split(","), m_key);
*/
} else if (m_type == "point") {
QPoint defaultPoint;
QStringList tmpList = m_default.split(',');
while (tmpList.size() >= 2) {
defaultPoint.setX(tmpList[0].toInt());
defaultPoint.setY(tmpList[1].toInt());
}
item = m_config->addItemPoint(m_name, *d->newPoint(), defaultPoint, m_key);
} else if (m_type == "rect") {
QRect defaultRect;
QStringList tmpList = m_default.split(',');
while (tmpList.size() >= 4) {
defaultRect.setCoords(tmpList[0].toInt(), tmpList[1].toInt(),
tmpList[2].toInt(), tmpList[3].toInt());
}
item = m_config->addItemRect(m_name, *d->newRect(), defaultRect, m_key);
} else if (m_type == "size") {
QSize defaultSize;
QStringList tmpList = m_default.split(',');
while (tmpList.size() >= 2) {
defaultSize.setWidth(tmpList[0].toInt());
defaultSize.setHeight(tmpList[1].toInt());
}
item = m_config->addItemSize(m_name, *d->newSize(), defaultSize, m_key);
} else if (m_type == "ulonglong") {
KConfigSkeleton::ItemULongLong *ulonglongItem =
m_config->addItemULongLong(m_name, *d->newULongLong(), m_default.toULongLong(), m_key);
if (m_haveMin) {
ulonglongItem->setMinValue(m_min);
}
if (m_haveMax) {
ulonglongItem->setMaxValue(m_max);
}
item = ulonglongItem;
/* No addItemUrlList in KConfigSkeleton ?
} else if (m_type == "urllist") {
//FIXME: the split() is naive and will break on lists with ,'s in them
QStringList tmpList = m_default.split(",");
KUrl::List defaultList;
foreach (const QString& tmp, tmpList) {
defaultList.append(KUrl(tmp));
}
item = m_config->addItemUrlList(m_name, *d->newUrlList(), defaultList, m_key);*/
}
if (item) {
item->setLabel(m_label);
item->setWhatsThis(m_whatsThis);
d->keysToNames.insert(item->group() + item->key(), item->name());
}
}
void ConfigLoaderHandler::resetState()
{
m_haveMin = false;
m_min = 0;
m_haveMax = false;
m_max = 0;
m_name.clear();
m_type.clear();
m_label.clear();
m_default.clear();
m_key.clear();
m_whatsThis.clear();
m_enumChoices.clear();
m_inChoice = false;
}
ConfigLoader::ConfigLoader(const QString &configFile, QIODevice *xml, QObject *parent)
: KConfigSkeleton(configFile, parent),
d(new ConfigLoaderPrivate)
{
d->parse(this, xml);
}
ConfigLoader::ConfigLoader(KSharedConfigPtr config, QIODevice *xml, QObject *parent)
: KConfigSkeleton(config, parent),
d(new ConfigLoaderPrivate)
{
d->parse(this, xml);
}
//FIXME: obviously this is broken and should be using the group as the root,
// but KConfigSkeleton does not currently support this. it will eventually though,
// at which point this can be addressed properly
ConfigLoader::ConfigLoader(const KConfigGroup *config, QIODevice *xml, QObject *parent)
: KConfigSkeleton(KSharedConfig::openConfig(config->config()->name()), parent),
d(new ConfigLoaderPrivate)
{
KConfigGroup group = config->parent();
d->baseGroup = config->name();
while (group.isValid() && group.name() != "<default>") {
d->baseGroup = group.name() + '\x1d' + d->baseGroup;
group = group.parent();
}
d->parse(this, xml);
}
ConfigLoader::~ConfigLoader()
{
delete d;
}
KConfigSkeletonItem *ConfigLoader::findItem(const QString &group, const QString &key)
{
return KConfigSkeleton::findItem(d->keysToNames[group + key]);
}
bool ConfigLoader::hasGroup(const QString &group) const
{
return d->groups.contains(group);
}
QStringList ConfigLoader::groupList() const
{
return d->groups;
}
} // Plasma namespace

137
configloader.h Normal file
View File

@ -0,0 +1,137 @@
/*
* Copyright 2007 Aaron Seigo <aseigo@kde.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Library General Public License as
* published by the Free Software Foundation; either version 2, 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 Library General Public
* License along with this program; if not, write to the
* Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef PLASMA_CONFIGLOADER_H
#define PLASMA_CONFIGLOADER_H
#include <KDE/KConfigGroup>
#include <KDE/KConfigSkeleton>
#include <KDE/KSharedConfig>
#include <plasma/plasma_export.h>
/**
* @class ConfigLoader plasma/configloader.h <Plasma/ConfigLoader>
*
* @short A KConfigSkeleton that populates itself based on KConfigXT XML
*
* This class allows one to ship an XML file and reconstitute it into a
* KConfigSkeleton object at runtime. Common usage might look like this:
*
* \code
* QFile file(xmlFilePath);
* Plasma::ConfigLoader appletConfig(configFilePath, &file);
* \endcode
*
* Alternatively, any QIODevice may be used in place of QFile in the
* example above.
*
* Currently the following data types are supported:
*
* @li bools
* @li colors
* @li datetimes
* @li enumerations
* @li fonts
* @li ints
* @li passwords
* @li paths
* @li strings
* @li stringlists
* @li uints
* @li urls
* @li doubles
* @li int lists
* @li longlongs
* @li path lists
* @li points
* @li rects
* @li sizes
* @li ulonglongs
* @li url lists
**/
namespace Plasma
{
class ConfigLoaderPrivate;
class PLASMA_EXPORT ConfigLoader : public KConfigSkeleton
{
public:
/**
* Creates a KConfigSkeleton populated using the definition found in
* the XML data passed in.
*
* @param configFile path to the configuration file to use
* @param xml the xml data; must be valid KConfigXT data
* @param parent optional QObject parent
**/
ConfigLoader(const QString &configFile, QIODevice *xml, QObject *parent = 0);
/**
* Creates a KConfigSkeleton populated using the definition found in
* the XML data passed in.
*
* @param config the configuration object to use
* @param xml the xml data; must be valid KConfigXT data
* @param parent optional QObject parent
**/
ConfigLoader(KSharedConfigPtr config, QIODevice *xml, QObject *parent = 0);
/**
* Creates a KConfigSkeleton populated using the definition found in
* the XML data passed in.
*
* @param config the group to use as the root for configuration items
* @param xml the xml data; must be valid KConfigXT data
* @param parent optional QObject parent
**/
ConfigLoader(const KConfigGroup *config, QIODevice *xml, QObject *parent = 0);
~ConfigLoader();
/**
* Finds the item for the given group and key.
*
* @arg group the group in the config file to look in
* @arg key the configuration key to find
* @return the associated KConfigSkeletonItem, or 0 if none
*/
KConfigSkeletonItem *findItem(const QString &group, const QString &key);
/**
* Check to see if a group exists
*
* @param group the name of the group to check for
* @return true if the group exists, or false if it does not
*/
bool hasGroup(const QString &group) const;
/**
* @return the list of groups defined by the XML
*/
QStringList groupList() const;
private:
ConfigLoaderPrivate * const d;
};
} // Plasma namespace
#endif //multiple inclusion guard

1869
containment.cpp Normal file

File diff suppressed because it is too large Load Diff

508
containment.h Normal file
View File

@ -0,0 +1,508 @@
/*
* Copyright 2007 by Aaron Seigo <aseigo@kde.org>
* Copyright 2008 by Ménard Alexis <darktears31@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Library General Public License as
* published by the Free Software Foundation; either version 2, 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 Library General Public
* License along with this program; if not, write to the
* Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef PLASMA_CONTAINMENT_H
#define PLASMA_CONTAINMENT_H
#include <QtGui/QGraphicsItem>
#include <QtGui/QWidget>
#include <QtGui/QStyleOptionGraphicsItem>
#include <kplugininfo.h>
#include <ksharedconfig.h>
#include <kgenericfactory.h>
#include <plasma/applet.h>
#include <plasma/animator.h>
namespace Plasma
{
class AppletHandle;
class DataEngine;
class Package;
class Corona;
class View;
class Wallpaper;
class ContainmentPrivate;
/**
* @class Containment plasma/containment.h <Plasma/Containment>
*
* @short The base class for plugins that provide backgrounds and applet grouping containers
*
* Containment objects provide the means to group applets into functional sets.
* They also provide the following:
*
* creation of focussing event
* - drawing of the background image (which can be interactive)
* - form factors (e.g. panel, desktop, full screen, etc)
* - applet layout management
*
* Since containment is actually just a Plasma::Applet, all the techniques used
* for writing the visual presentation of Applets is applicable to Containtments.
* Containments are differentiated from Applets by being marked with the ServiceType
* of Plasma/Containment. Plugins registered with both the Applet and the Containment
* ServiceTypes can be loaded for us in either situation.
*
* See techbase.kde.org for a tutorial on writing Containments using this class.
*/
class PLASMA_EXPORT Containment : public Applet
{
Q_OBJECT
public:
class StyleOption : public QStyleOptionGraphicsItem
{
public:
explicit StyleOption();
explicit StyleOption(const StyleOption &other);
explicit StyleOption(const QStyleOptionGraphicsItem &other);
enum StyleOptionType {
Type = SO_CustomBase + 1
};
enum StyleOptionVersion {
Version = QStyleOptionGraphicsItem::Version + 1
};
/**
* The View, if any, that this containment is currently
* being rendered into. Note: this may be NULL, so be
* sure to check it before using it!
*/
Plasma::View *view;
};
enum Type {
NoContainmentType = -1, /**< @internal */
DesktopContainment = 0, /**< A desktop containment */
PanelContainment, /**< A desktop panel */
CustomContainment = 127, /**< A containment that is neither a desktop nor a panel
but something application specific */
CustomPanelContainment = 128 /**< A customized desktop panel */
};
/**
* @param parent the QGraphicsItem this applet is parented to
* @param serviceId the name of the .desktop file containing the
* information about the widget
* @param containmentId a unique id used to differentiate between multiple
* instances of the same Applet type
*/
explicit Containment(QGraphicsItem *parent = 0,
const QString &serviceId = QString(),
uint containmentId = 0);
/**
* This constructor is to be used with the plugin loading systems
* found in KPluginInfo and KService. The argument list is expected
* to have two elements: the KService service ID for the desktop entry
* and an applet ID which must be a base 10 number.
*
* @param parent a QObject parent; you probably want to pass in 0
* @param args a list of strings containing two entries: the service id
* and the applet id
*/
Containment(QObject *parent, const QVariantList &args);
~Containment();
/**
* Reimplemented from Applet
*/
void init();
/**
* Returns the type of containment
*/
Type containmentType() const;
/**
* Returns the Corona (if any) that this Containment is hosted by
*/
Corona *corona() const;
/**
* Returns a list of all known containments.
*
* @param category Only applets matchin this category will be returned.
* Useful in conjunction with knownCategories.
* If "Misc" is passed in, then applets without a
* Categories= entry are also returned.
* If an empty string is passed in, all applets are
* returned.
* @param parentApp the application to filter applets on. Uses the
* X-KDE-ParentApp entry (if any) in the plugin info.
* The default value of QString() will result in a
* list containing only applets not specifically
* registered to an application.
* @return list of applets
**/
static KPluginInfo::List listContainments(const QString &category = QString(),
const QString &parentApp = QString());
/**
* Returns a list of all known applets associated with a certain mimetype
*
* @return list of applets
**/
static KPluginInfo::List listContainmentsForMimetype(const QString &mimetype);
/**
* Adds an applet to this Containment
*
* @param name the plugin name for the applet, as given by
* KPluginInfo::pluginName()
* @param args argument list to pass to the plasmoid
* @param geometry where to place the applet, or to auto-place it if an invalid
* is provided
*
* @return a pointer to the applet on success, or 0 on failure
*/
Applet *addApplet(const QString &name, const QVariantList &args = QVariantList(),
const QRectF &geometry = QRectF(-1, -1, -1, -1));
/**
* Add an existing applet to this Containment
*
* If dontInit is true, the pending constraints are not flushed either.
* So it is your responsibility to call both init() and
* flushPendingConstraints() on the applet.
*
* @param applet the applet that should be added
* @param pos the containment-relative position
* @param dontInit if true, init() will not be called on the applet
*/
void addApplet(Applet *applet, const QPointF &pos = QPointF(-1, -1), bool dontInit = true);
/**
* @return the applets currently in this Containment
*/
Applet::List applets() const;
/**
* Removes all applets from this Containment
*/
void clearApplets();
/**
* Sets the physical screen this Containment is associated with.
*
* @param screen the screen number this containment is the desktop for, or -1
* if it is not serving as the desktop for any screen
*/
void setScreen(int screen);
/**
* @return the screen number this containment is serving as the desktop for
* or -1 if none
*/
int screen() const;
/**
* @reimplemented from Applet
*/
void save(KConfigGroup &group) const;
/**
* @reimplemented from Applet
*/
void restore(KConfigGroup &group);
/**
* convenience function - enables or disables an action by name
*
* @param name the name of the action in our collection
* @param enable true to enable, false to disable
*/
void enableAction(const QString &name, bool enable);
/**
* Add an action to the toolbox
*/
void addToolBoxAction(QAction *action);
/**
* Remove an action from the toolbox
*/
void removeToolBoxAction(QAction *action);
/**
* Sets the open or closed state of the Containment's toolbox
*
* @arg open true to open the ToolBox, false to close it
*/
void setToolBoxOpen(bool open);
/**
* Open the Containment's toolbox
*/
void openToolBox();
/**
* Closes Containment's toolbox
*/
void closeToolBox();
/**
* associate actions with this widget, including ones added after this call.
* needed to make keyboard shortcuts work.
*/
void addAssociatedWidget(QWidget *widget);
/**
* un-associate actions from this widget, including ones added after this call.
* needed to make keyboard shortcuts work.
*/
void removeAssociatedWidget(QWidget *widget);
/**
* Return whether wallpaper is painted or not.
*/
bool drawWallpaper();
/**
* Sets wallpaper plugin.
*
* @param pluginName the name of the wallpaper to attempt to load
* @param mode optional mode or the wallpaper plugin (e.g. "Slideshow").
* These values are pugin specific and enumerated in the plugin's
* .desktop file.
*/
void setWallpaper(const QString &pluginName, const QString &mode = QString());
/**
* Return wallpaper plugin.
*/
Plasma::Wallpaper *wallpaper() const;
/**
* Sets the current activity by name
*
* @param activity the name of the activity; if it doesn't exist in the
* semantic store, it will be created.
*/
void setActivity(const QString &activity);
/**
* @return the current activity associated with this activity
*/
QString activity() const;
/**
* Shows the context menu for the containment directly, bypassing Applets
* altogether.
*/
void showContextMenu(const QPointF &containmentPos, const QPoint &screenPos);
/**
* Shows a visual clue for drag and drop
* This implementation does nothing, reimplement in containments that needs it
*
* @param pos point where to show the drop target
*/
virtual void showDropZone(const QPoint pos);
Q_SIGNALS:
/**
* This signal is emitted when a new applet is created by the containment
*/
void appletAdded(Plasma::Applet *applet, const QPointF &pos);
/**
* This signal is emitted when an applet is destroyed
*/
void appletRemoved(Plasma::Applet *applet);
/**
* Emitted when the containment requests zooming in or out one step.
*/
void zoomRequested(Plasma::Containment *containment, Plasma::ZoomDirection direction);
/**
* Emitted when the user clicks on the toolbox
*/
void toolBoxToggled();
/**
* Emitted when the containment wants a new containment to be created.
* Usually only used for desktop containments.
*/
void addSiblingContainment(Plasma::Containment *);
/**
* Emitted when the containment requests an add widgets dialog is shown.
* Usually only used for desktop containments.
*
* @param pos where in the containment this request was made from, or
* an invalid position (QPointF()) is not location specific
*/
void showAddWidgetsInterface(const QPointF &pos);
/**
* This signal indicates that a containment has been newly
* associated (or dissociated) with a physical screen.
*
* @param wasScreen the screen it was associated with
* @param isScreen the screen it is now associated with
* @param containment the containment switching screens
*/
void screenChanged(int wasScreen, int isScreen, Plasma::Containment *containment);
/**
* Emitted when the user wants to configure/change containment.
*/
void configureRequested(Plasma::Containment *containment);
/**
* The activity associated to this containemnt has changed
*/
void contextChanged(Plasma::Context *context);
public Q_SLOTS:
/**
* Informs the Corona as to what position it is in. This is informational
* only, as the Corona doesn't change it's actual location. This is,
* however, passed on to Applets that may be managed by this Corona.
*
* @param location the new location of this Corona
*/
void setLocation(Plasma::Location location);
/**
* Sets the form factor for this Containment. This may cause changes in both
* the arrangement of Applets as well as the display choices of individual
* Applets.
*/
void setFormFactor(Plasma::FormFactor formFactor);
/**
* Tells the corona to create a new desktop containment
*/
void addSiblingContainment();
/**
* switch keyboard focus to the next of our applets
*/
void focusNextApplet();
/**
* switch keyboard focus to the previous one of our applets
*/
void focusPreviousApplet();
/**
* Destroys this containment and all its applets (after a confirmation dialog);
* it will be removed nicely and deleted.
* Its configuration will also be deleted.
*/
void destroy();
/**
* Destroys this containment and all its applets (after a confirmation dialog);
* it will be removed nicely and deleted.
* Its configuration will also be deleted.
*
* @arg confirm whether or not confirmation from the user should be requested
*/
void destroy(bool confirm);
/**
* @reimplemented from Plasma::Applet
*/
void showConfigurationInterface();
protected:
/**
* Sets the type of this containment.
*/
void setContainmentType(Containment::Type type);
/**
* Sets whether wallpaper is painted or not.
*/
void setDrawWallpaper(bool drawWallpaper);
/**
* Called when the contents of the containment should be saved. By default this saves
* all loaded Applets
*
* @param group the KConfigGroup to save settings under
*/
virtual void saveContents(KConfigGroup &group) const;
/**
* Called when the contents of the containment should be loaded. By default this loads
* all previously saved Applets
*
* @param group the KConfigGroup to save settings under
*/
virtual void restoreContents(KConfigGroup &group);
void mouseMoveEvent(QGraphicsSceneMouseEvent *event);
void mousePressEvent(QGraphicsSceneMouseEvent *event);
void mouseReleaseEvent(QGraphicsSceneMouseEvent *event);
void contextMenuEvent(QGraphicsSceneContextMenuEvent *event);
void keyPressEvent(QKeyEvent *event);
void wheelEvent(QGraphicsSceneWheelEvent *event);
bool sceneEventFilter(QGraphicsItem *watched, QEvent *event);
QVariant itemChange(GraphicsItemChange change, const QVariant &value);
/**
* @reimplemented from QGraphicsItem
*/
void dragEnterEvent(QGraphicsSceneDragDropEvent *event);
/**
* @reimplemented from QGraphicsItem
*/
void dragMoveEvent(QGraphicsSceneDragDropEvent *event);
/**
* @reimplemented from QGraphicsItem
*/
void dropEvent(QGraphicsSceneDragDropEvent *event);
/**
* @reimplemented from QGraphicsItem
*/
void resizeEvent(QGraphicsSceneResizeEvent *event);
private:
Q_PRIVATE_SLOT(d, void appletDestroyed(QObject*))
Q_PRIVATE_SLOT(d, void containmentAppletAnimationComplete(QGraphicsItem *item,
Plasma::Animator::Animation anim))
Q_PRIVATE_SLOT(d, void triggerShowAddWidgets())
Q_PRIVATE_SLOT(d, void handleDisappeared(AppletHandle *handle))
Q_PRIVATE_SLOT(d, void positionToolBox())
Q_PRIVATE_SLOT(d, void zoomIn())
Q_PRIVATE_SLOT(d, void zoomOut())
Q_PRIVATE_SLOT(d, void toggleDesktopImmutability())
friend class Applet;
friend class AppletPrivate;
friend class CoronaPrivate;
friend class ContainmentPrivate;
ContainmentPrivate *const d;
};
} // Plasma namespace
#endif // multiple inclusion guard

76
context.cpp Normal file
View File

@ -0,0 +1,76 @@
/*
* Copyright 2008 by Aaron Seigo <aseigo@kde.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Library General Public License as
* published by the Free Software Foundation; either version 2, 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 Library General Public
* License along with this program; if not, write to the
* Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "context.h"
namespace Plasma
{
class ContextPrivate
{
public:
QString activity;
};
Context::Context(QObject *parent)
: QObject(parent),
d(new ContextPrivate)
{
//TODO: look up activity in Nepomuk
}
Context::~Context()
{
delete d;
}
void Context::createActivity(const QString &name)
{
}
QStringList Context::listActivities() const
{
return QStringList();
}
void Context::setCurrentActivity(const QString &name)
{
if (d->activity == name || name.isEmpty()) {
return;
}
d->activity = name;
emit activityChanged(this);
emit changed(this);
QStringList activities = listActivities();
if (!activities.contains(name)) {
createActivity(name);
}
}
QString Context::currentActivity() const
{
return d->activity;
}
} // namespace Plasma
#include "context.moc"

61
context.h Normal file
View File

@ -0,0 +1,61 @@
/*
* Copyright 2008 by Aaron Seigo <aseigo@kde.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Library General Public License as
* published by the Free Software Foundation; either version 2, 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 Library General Public
* License along with this program; if not, write to the
* Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef PLASMA_CONTEXT_H
#define PLASMA_CONTEXT_H
#include <QtCore/QObject>
#include <QtCore/QStringList>
#include "plasma_export.h"
namespace Plasma
{
class ContextPrivate;
class PLASMA_EXPORT Context : public QObject
{
Q_OBJECT
public:
explicit Context(QObject *parent = 0);
~Context();
void createActivity(const QString &name);
QStringList listActivities() const;
void setCurrentActivity(const QString &name);
QString currentActivity() const;
//TODO: location
Q_SIGNALS:
void changed(Plasma::Context *context);
void activityChanged(Plasma::Context *context);
void locationChanged(Plasma::Context *context);
private:
ContextPrivate * const d;
};
} // namespace Plasma
#endif // multiple inclusion guard

526
corona.cpp Normal file
View File

@ -0,0 +1,526 @@
/*
* Copyright 2007 Matt Broadstone <mbroadst@gmail.com>
* Copyright 2007 Aaron Seigo <aseigo@kde.org>
* Copyright 2007 Riccardo Iaconelli <riccardo@kde.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Library General Public License as
* published by the Free Software Foundation; either version 2, 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 Library General Public
* License along with this program; if not, write to the
* Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "corona.h"
#include <QApplication>
#include <QGraphicsView>
#include <QGraphicsSceneDragDropEvent>
#include <QGraphicsGridLayout>
#include <QMimeData>
#include <QPainter>
#include <QTimer>
#include <KDebug>
#include <KGlobal>
#include <KLocale>
#include <KMimeType>
#include "containment.h"
#include "view.h"
#include "private/applet_p.h"
#include "tooltipmanager.h"
using namespace Plasma;
namespace Plasma
{
// constant controlling how long between requesting a configuration sync
// and one happening should occur. currently 10 seconds
const int CONFIG_SYNC_TIMEOUT = 10000;
class CoronaPrivate
{
public:
CoronaPrivate(Corona *corona)
: q(corona),
immutability(Mutable),
mimetype("text/x-plasmoidservicename"),
config(0),
offscreenLayout(0)
{
if (KGlobal::hasMainComponent()) {
configName = KGlobal::mainComponent().componentName() + "-appletsrc";
} else {
configName = "plasma-appletsrc";
}
}
~CoronaPrivate()
{
qDeleteAll(containments);
}
void init()
{
configSyncTimer.setSingleShot(true);
QObject::connect(&configSyncTimer, SIGNAL(timeout()), q, SLOT(syncConfig()));
}
void saveLayout(KSharedConfigPtr cg) const
{
KConfigGroup containmentsGroup(cg, "Containments");
foreach (const Containment *containment, containments) {
QString cid = QString::number(containment->id());
KConfigGroup containmentConfig(&containmentsGroup, cid);
containment->save(containmentConfig);
}
}
void updateContainmentImmutability()
{
foreach (Containment *c, containments) {
// we need to tell each containment that immutability has been altered
c->updateConstraints(ImmutableConstraint);
}
}
void containmentDestroyed(QObject *obj)
{
// we do a static_cast here since it really isn't an Containment by this
// point anymore since we are in the qobject dtor. we don't actually
// try and do anything with it, we just need the value of the pointer
// so this unsafe looking code is actually just fine.
Containment* containment = static_cast<Plasma::Containment*>(obj);
int index = containments.indexOf(containment);
if (index > -1) {
containments.removeAt(index);
q->requestConfigSync();
}
}
void syncConfig()
{
q->config()->sync();
emit q->configSynced();
}
Containment *addContainment(const QString &name, const QVariantList &args,
uint id, bool delayedInit)
{
QString pluginName = name;
Containment *containment = 0;
Applet *applet = 0;
//kDebug() << "Loading" << name << args << id;
if (pluginName.isEmpty()) {
// default to the desktop containment
pluginName = "desktop";
}
if (pluginName != "null") {
applet = Applet::load(pluginName, id, args);
containment = dynamic_cast<Containment*>(applet);
}
if (!containment) {
kDebug() << "loading of containment" << name << "failed.";
// in case we got a non-Containment from Applet::loadApplet or
// a null containment was requested
delete applet;
containment = new Containment(0, 0, id);
if (pluginName == "null") {
containment->setDrawWallpaper(false);
}
// we want to provide something and don't care about the failure to launch
containment->setFailedToLaunch(false);
containment->setFormFactor(Plasma::Planar);
}
static_cast<Applet*>(containment)->d->setIsContainment(true);
q->addItem(containment);
if (!delayedInit) {
containment->init();
containment->updateConstraints(Plasma::StartupCompletedConstraint);
KConfigGroup cg = containment->config();
containment->save(cg);
q->requestConfigSync();
}
containments.append(containment);
QObject::connect(containment, SIGNAL(destroyed(QObject*)),
q, SLOT(containmentDestroyed(QObject*)));
QObject::connect(containment, SIGNAL(configNeedsSaving()),
q, SLOT(requestConfigSync()));
QObject::connect(containment, SIGNAL(releaseVisualFocus()),
q, SIGNAL(releaseVisualFocus()));
QObject::connect(containment, SIGNAL(screenChanged(int,int,Plasma::Containment*)),
q, SIGNAL(screenOwnerChanged(int,int,Plasma::Containment*)));
if (!delayedInit) {
emit q->containmentAdded(containment);
}
return containment;
}
Corona *q;
ImmutabilityType immutability;
QString mimetype;
QString configName;
KSharedConfigPtr config;
QTimer configSyncTimer;
QList<Containment*> containments;
QGraphicsGridLayout *offscreenLayout;
};
Corona::Corona(QObject *parent)
: QGraphicsScene(parent),
d(new CoronaPrivate(this))
{
d->init();
ToolTipManager::self()->m_corona = this;
//setViewport(new QGLWidget(QGLFormat(QGL::StencilBuffer | QGL::AlphaChannel)));
}
Corona::~Corona()
{
// FIXME: Same fix as in Plasma::View - make sure that when the focused widget is
// destroyed we don't try to transfer it to something that's already been
// deleted.
clearFocus();
KConfigGroup cg(config(), "General");
// we call the dptr member directly for locked since isImmutable()
// also checks kiosk and parent containers
cg.writeEntry("immutability", (int)d->immutability);
delete d;
}
void Corona::setAppletMimeType(const QString &type)
{
d->mimetype = type;
}
QString Corona::appletMimeType()
{
return d->mimetype;
}
void Corona::saveLayout(const QString &configName) const
{
KSharedConfigPtr c;
if (configName.isEmpty() || configName == d->configName) {
c = config();
} else {
c = KSharedConfig::openConfig(configName);
}
d->saveLayout(c);
}
void Corona::requestConfigSync()
{
// TODO: should we check into our immutability before doing this?
//NOTE: this is a pretty simplistic model: we simply save no more than CONFIG_SYNC_TIMEOUT
// after the first time this is called. not much of a heuristic for save points, but
// it should at least compress these activities a bit and provide a way for applet
// authors to ween themselves from the sync() disease. A more interesting/dynamic
// algorithm for determining when to actually sync() to disk might be better, though.
if (!d->configSyncTimer.isActive()) {
d->configSyncTimer.start(CONFIG_SYNC_TIMEOUT);
}
}
void Corona::initializeLayout(const QString &configName)
{
clearContainments();
loadLayout(configName);
if (d->containments.isEmpty()) {
loadDefaultLayout();
if (!d->containments.isEmpty()) {
requestConfigSync();
}
}
if (config()->isImmutable()) {
d->updateContainmentImmutability();
}
KConfigGroup coronaConfig(config(), "General");
setImmutability((ImmutabilityType)coronaConfig.readEntry("immutability", (int)Mutable));
}
void Corona::loadLayout(const QString &configName)
{
KSharedConfigPtr c;
if (configName.isEmpty() || configName == d->configName) {
c = config();
} else {
c = KSharedConfig::openConfig(configName);
}
KConfigGroup containments(config(), "Containments");
foreach (const QString &group, containments.groupList()) {
KConfigGroup containmentConfig(&containments, group);
if (containmentConfig.entryMap().isEmpty()) {
continue;
}
int cid = group.toUInt();
//kDebug() << "got a containment in the config, trying to make a" << containmentConfig.readEntry("plugin", QString()) << "from" << group;
Containment *c = d->addContainment(containmentConfig.readEntry("plugin", QString()), QVariantList(),
cid, true);
if (!c) {
continue;
}
//addItem(c);
c->init();
c->restore(containmentConfig);
}
foreach (Containment *containment, d->containments) {
QString cid = QString::number(containment->id());
KConfigGroup containmentConfig(&containments, cid);
foreach (Applet *applet, containment->applets()) {
applet->init();
// We have to flush the applet constraints manually
applet->flushPendingConstraintsEvents();
}
containment->updateConstraints(Plasma::StartupCompletedConstraint);
containment->flushPendingConstraintsEvents();
emit containmentAdded(containment);
}
}
Containment *Corona::containmentForScreen(int screen) const
{
foreach (Containment *containment, d->containments) {
if (containment->screen() == screen &&
(containment->containmentType() == Containment::DesktopContainment ||
containment->containmentType() >= Containment::CustomContainment)) {
return containment;
}
}
return 0;
}
QList<Containment*> Corona::containments() const
{
return d->containments;
}
void Corona::clearContainments()
{
foreach (Containment *containment, d->containments) {
containment->clearApplets();
}
}
KSharedConfigPtr Corona::config() const
{
if (!d->config) {
d->config = KSharedConfig::openConfig(d->configName);
}
return d->config;
}
Containment *Corona::addContainment(const QString &name, const QVariantList &args)
{
return d->addContainment(name, args, 0, false);
}
Containment *Corona::addContainmentDelayed(const QString &name, const QVariantList &args)
{
return d->addContainment(name, args, 0, true);
}
void Corona::addOffscreenWidget(QGraphicsWidget *widget)
{
widget->setParentItem(0);
if (!d->offscreenLayout) {
kDebug() << "adding offscreen widget.";
QGraphicsWidget *offscreenWidget = new QGraphicsWidget(0);
addItem(offscreenWidget);
d->offscreenLayout = new QGraphicsGridLayout(offscreenWidget);
//FIXME: do this a nice way.
offscreenWidget->setPos(-10000, -10000);
offscreenWidget->setLayout(d->offscreenLayout);
}
//check if the layout already contains this widget.
//XXX: duplicated from removeOffscreenWidget()
for (int i = 0; i < d->offscreenLayout->count(); i++) {
QGraphicsWidget *foundWidget = dynamic_cast<QGraphicsWidget*>(d->offscreenLayout->itemAt(i));
if (foundWidget == widget) {
return;
}
}
d->offscreenLayout->addItem(widget, d->offscreenLayout->rowCount() + 1,
d->offscreenLayout->columnCount() + 1);
widget->update();
}
void Corona::removeOffscreenWidget(QGraphicsWidget *widget)
{
if (!d->offscreenLayout) {
return;
}
for (int i = 0; i < d->offscreenLayout->count(); i++) {
QGraphicsWidget *foundWidget = dynamic_cast<QGraphicsWidget*>(d->offscreenLayout->itemAt(i));
if (foundWidget == widget) {
d->offscreenLayout->removeAt(i);
}
}
}
int Corona::numScreens() const
{
return 1;
}
QRect Corona::screenGeometry(int id) const
{
Q_UNUSED(id);
if (views().isEmpty()) {
return sceneRect().toRect();
} else {
QGraphicsView *v = views()[0];
QRect r = sceneRect().toRect();
r.moveTo(v->mapToGlobal(v->pos()));
return r;
}
}
QRegion Corona::availableScreenRegion(int id) const
{
return QRegion(screenGeometry(id));
}
QPoint Corona::popupPosition(const QGraphicsItem *item, const QSize &s)
{
QGraphicsView *v = viewFor(item);
if (!v) {
return QPoint(0, 0);
}
QPoint pos = v->mapFromScene(item->scenePos());
pos = v->mapToGlobal(pos);
//kDebug() << "==> position is" << item->scenePos() << v->mapFromScene(item->scenePos()) << pos;
Plasma::View *pv = dynamic_cast<Plasma::View *>(v);
Plasma::Location loc = Floating;
if (pv && pv->containment()) {
loc = pv->containment()->location();
}
switch (loc) {
case BottomEdge:
pos = QPoint(pos.x(), pos.y() - s.height());
break;
case TopEdge:
pos = QPoint(pos.x(), pos.y() + (int)item->boundingRect().size().height());
break;
case LeftEdge:
pos = QPoint(pos.x() + (int)item->boundingRect().size().width(), pos.y());
break;
case RightEdge:
pos = QPoint(pos.x() - s.width(), pos.y());
break;
default:
if (pos.y() - s.height() > 0) {
pos = QPoint(pos.x(), pos.y() - s.height());
} else {
pos = QPoint(pos.x(), pos.y() + (int)item->boundingRect().size().height());
}
}
//are we out of screen?
QRect screenRect =
screenGeometry((pv && pv->containment()) ? pv->containment()->screen() : -1);
//kDebug() << "==> rect for" << (pv ? pv->containment()->screen() : -1) << "is" << screenRect;
if (pos.rx() + s.width() > screenRect.right()) {
pos.rx() -= ((pos.rx() + s.width()) - screenRect.right());
}
if (pos.ry() + s.height() > screenRect.bottom()) {
pos.ry() -= ((pos.ry() + s.height()) - screenRect.bottom());
}
pos.rx() = qMax(0, pos.rx());
return pos;
}
void Corona::loadDefaultLayout()
{
}
void Corona::dragEnterEvent(QGraphicsSceneDragDropEvent *event)
{
QGraphicsScene::dragEnterEvent(event);
}
void Corona::dragLeaveEvent(QGraphicsSceneDragDropEvent *event)
{
QGraphicsScene::dragLeaveEvent(event);
}
void Corona::dragMoveEvent(QGraphicsSceneDragDropEvent *event)
{
QGraphicsScene::dragMoveEvent(event);
}
ImmutabilityType Corona::immutability() const
{
return d->immutability;
}
void Corona::setImmutability(const ImmutabilityType immutable)
{
if (d->immutability == immutable ||
d->immutability == SystemImmutable) {
return;
}
kDebug() << "setting immutability to" << immutable;
d->immutability = immutable;
d->updateContainmentImmutability();
}
} // namespace Plasma
#include "corona.moc"

261
corona.h Normal file
View File

@ -0,0 +1,261 @@
/*
* Copyright 2007 Aaron Seigo <aseigo@kde.org>
* Copyright 2007 Matt Broadstone <mbroadst@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Library General Public License as
* published by the Free Software Foundation; either version 2, 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 Library General Public
* License along with this program; if not, write to the
* Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef PLASMA_CORONA_H
#define PLASMA_CORONA_H
#include <QtGui/QGraphicsScene>
#include <plasma/applet.h>
#include <plasma/plasma.h>
#include <plasma/plasma_export.h>
class QGraphicsGridLayout;
namespace Plasma
{
class Containment;
class CoronaPrivate;
/**
* @class Corona plasma/corona.h <Plasma/Corona>
*
* @short A QGraphicsScene for Plasma::Applets
*/
class PLASMA_EXPORT Corona : public QGraphicsScene
{
Q_OBJECT
//typedef QHash<QString, QList<Plasma::Applet*> > layouts;
public:
explicit Corona(QObject * parent = 0);
~Corona();
/**
* Sets the mimetype of Drag/Drop items. Default is
* text/x-plasmoidservicename
*/
void setAppletMimeType(const QString &mimetype);
/**
* The current mime type of Drag/Drop items.
*/
QString appletMimeType();
/**
* @return all containments on this Corona
*/
QList<Containment*> containments() const;
/**
* Clear the Corona from all applets.
*/
void clearContainments();
/**
* Returns the config file used to store the configuration for this Corona
*/
KSharedConfig::Ptr config() const;
/**
* Adds a Containment to the Corona
*
* @param name the plugin name for the containment, as given by
* KPluginInfo::pluginName(). If an empty string is passed in, the defalt
* containment plugin will be used (usually DesktopContainment). If the
* string literal "null" is passed in, then no plugin will be loaded and
* a simple Containment object will be created instead.
* @param args argument list to pass to the containment
*
* @return a pointer to the containment on success, or 0 on failure
*/
Containment *addContainment(const QString &name, const QVariantList &args = QVariantList());
/**
* Returns the Containment, if any, for a given physical screen
*
* @param screen number of the physical screen to locate
*/
Containment *containmentForScreen(int screen) const;
/**
* Adds a widget in the topleft quadrant in the scene. Widgets in the topleft quadrant are
* normally never shown unless you specifically aim a view at it, which makes it ideal for
* toplevel views etc.
* @param widget the widget to add.
*/
void addOffscreenWidget(QGraphicsWidget *widget);
/**
* Removes a widget from the topleft quadrant in the scene.
* @param widget the widget to remove.
*/
void removeOffscreenWidget(QGraphicsWidget *widget);
/**
* Returns the number of screens available to plasma.
* Subclasses should override this method as the default
* implementation returns a meaningless value.
*/
virtual int numScreens() const;
/**
* Returns the geometry of a given screen.
* Valid screen ids are 0 to numScreen()-1, or -1 for the full desktop geometry.
* Subclasses should override this method as the default
* implementation returns a meaningless value.
*/
virtual QRect screenGeometry(int id) const;
/**
* Returns the available region for a given screen.
* The available region excludes panels and similar windows.
* Valid screen ids are 0 to numScreens()-1.
* By default this method returns a rectangular region
* equal to screenGeometry(id); subclasses that need another
* behavior should override this method.
*/
virtual QRegion availableScreenRegion(int id) const;
/**
* Reccomended position for a popup window like a menu or a tooltip
* given its size
* @param s size of the popup
* @returns reccomended position
*/
QPoint popupPosition(const QGraphicsItem *item, const QSize &s);
public Q_SLOTS:
/**
* Initializes the layout from a config file. This will first clear any existing
* Containments, load a layout from the requested configuration file, request the
* default layout if needed and update immutability.
*
* @param config the name of the config file to load from,
* or the default config file if QString()
*/
void initializeLayout(const QString &config = QString());
/**
* Load applet layout from a config file. The results will be added to the
* current set of Containments.
*
* @param config the name of the config file to load from,
* or the default config file if QString()
*/
void loadLayout(const QString &config = QString());
/**
* Save applets layout to file
* @arg config the file to save to, or the default config file if QString()
*/
void saveLayout(const QString &config = QString()) const;
/**
* @return The type of immutability of this Corona
*/
ImmutabilityType immutability() const;
/**
* Sets the immutability type for this Corona (not immutable,
* user immutable or system immutable)
* @arg immutable the new immutability type of this applet
*/
void setImmutability(const ImmutabilityType immutable);
/**
* Schedules a flush-to-disk synchronization of the configuration state
* at the next convenient moment.
*/
void requestConfigSync();
Q_SIGNALS:
/**
* This signal indicates a new containment has been added to
* the Corona
*/
void containmentAdded(Plasma::Containment *containment);
/**
* This signal indicates that a containment has been newly
* associated (or dissociated) with a physical screen.
*
* @param wasScreen the screen it was associated with
* @param isScreen the screen it is now associated with
* @param containment the containment switching screens
*/
void screenOwnerChanged(int wasScreen, int isScreen, Plasma::Containment *containment);
/**
* This signal indicates that an application launch, window
* creation or window focus event was triggered. This is used, for instance,
* to ensure that the Dashboard view in Plasma hides when such an event is
* triggered by an item it is displaying.
*/
void releaseVisualFocus();
/**
* This signal indicates that the configuration file was flushed to disc.
*/
void configSynced();
protected:
/**
* Loads the default (system wide) layout for this user
**/
virtual void loadDefaultLayout();
/**
* Loads a containment with delayed initialization, primarily useful
* for implementations of loadDefaultLayout. The caller is responsible
* for all initializating, saving and notification of a new containment.
*
* @param name the plugin name for the containment, as given by
* KPluginInfo::pluginName(). If an empty string is passed in, the defalt
* containment plugin will be used (usually DesktopContainment). If the
* string literal "null" is passed in, then no plugin will be loaded and
* a simple Containment object will be created instead.
* @param args argument list to pass to the containment
*
* @return a pointer to the containment on success, or 0 on failure
**/
Containment *addContainmentDelayed(const QString &name,
const QVariantList &args = QVariantList());
//Reimplemented from QGraphicsScene
void dragEnterEvent(QGraphicsSceneDragDropEvent *event);
void dragLeaveEvent(QGraphicsSceneDragDropEvent *event);
void dragMoveEvent(QGraphicsSceneDragDropEvent *event);
private:
CoronaPrivate *const d;
Q_PRIVATE_SLOT(d, void containmentDestroyed(QObject*))
Q_PRIVATE_SLOT(d, void syncConfig())
friend class CoronaPrivate;
};
} // namespace Plasma
#endif

200
datacontainer.cpp Normal file
View File

@ -0,0 +1,200 @@
/*
* Copyright 2006-2007 Aaron Seigo <aseigo@kde.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Library General Public License as
* published by the Free Software Foundation; either version 2, 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 Library General Public
* License along with this program; if not, write to the
* Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "datacontainer.h"
#include "private/datacontainer_p.h"
#include <QVariant>
#include <KDebug>
#include "plasma.h"
namespace Plasma
{
DataContainer::DataContainer(QObject *parent)
: QObject(parent),
d(new DataContainerPrivate)
{
}
DataContainer::~DataContainer()
{
delete d;
}
const DataEngine::Data DataContainer::data() const
{
return d->data;
}
void DataContainer::setData(const QString &key, const QVariant &value)
{
if (value.isNull() || !value.isValid()) {
d->data.remove(key);
} else {
d->data[key] = value;
}
d->dirty = true;
d->updateTs.start();
}
void DataContainer::removeAllData()
{
if (d->data.count() < 1) {
// avoid an update if we don't have any data anyways
return;
}
d->data.clear();
d->dirty = true;
d->updateTs.start();
}
bool DataContainer::visualizationIsConnected(QObject *visualization) const
{
return d->relayObjects.contains(visualization);
}
void DataContainer::connectVisualization(QObject *visualization, uint pollingInterval,
Plasma::IntervalAlignment alignment)
{
//kDebug() << "connecting visualization" << visualization << "at interval of"
// << pollingInterval << "to" << objectName();
QMap<QObject *, SignalRelay *>::iterator objIt = d->relayObjects.find(visualization);
bool connected = objIt != d->relayObjects.end();
if (connected) {
// this visualization is already connected. just adjust the update
// frequency if necessary
SignalRelay *relay = objIt.value();
if (relay) {
// connected to a relay
//kDebug() << " already connected, but to a relay";
if (relay->m_interval == pollingInterval) {
//kDebug() << " already connected to a relay of the same interval of"
// << pollingInterval << ", nothing to do";
return;
}
if (relay->receiverCount() == 1) {
//kDebug() << " removing relay, as it is now unused";
d->relays.remove(relay->m_interval);
delete relay;
} else {
disconnect(relay, SIGNAL(dataUpdated(QString,Plasma::DataEngine::Data)),
visualization, SLOT(dataUpdated(QString,Plasma::DataEngine::Data)));
//relay->isUnused();
}
} else if (pollingInterval < 1) {
// the visualization was connected already, but not to a relay
// and it still doesn't want to connect to a relay, so we have
// nothing to do!
//kDebug() << " already connected, nothing to do";
return;
} else {
disconnect(this, SIGNAL(dataUpdated(QString,Plasma::DataEngine::Data)),
visualization, SLOT(dataUpdated(QString,Plasma::DataEngine::Data)));
}
} else {
connect(visualization, SIGNAL(destroyed(QObject*)),
this, SLOT(disconnectVisualization(QObject*)));//, Qt::QueuedConnection);
}
if (pollingInterval < 1) {
//kDebug() << " connecting directly";
d->relayObjects[visualization] = 0;
connect(this, SIGNAL(dataUpdated(QString,Plasma::DataEngine::Data)),
visualization, SLOT(dataUpdated(QString,Plasma::DataEngine::Data)));
} else {
//kDebug() << " connecting to a relay";
// we only want to do an imediate update if this is not the first object to connect to us
// if it is the first visualization, then the source will already have been populated
// engine's sourceRequested method
bool immediateUpdate = connected || d->relayObjects.count() > 1;
SignalRelay *relay = d->signalRelay(this, visualization, pollingInterval,
alignment, immediateUpdate);
connect(relay, SIGNAL(dataUpdated(QString,Plasma::DataEngine::Data)),
visualization, SLOT(dataUpdated(QString,Plasma::DataEngine::Data)));
}
}
void DataContainer::disconnectVisualization(QObject *visualization)
{
QMap<QObject *, SignalRelay *>::iterator objIt = d->relayObjects.find(visualization);
if (objIt == d->relayObjects.end() || !objIt.value()) {
// it is connected directly to the DataContainer itself
disconnect(this, SIGNAL(dataUpdated(QString,Plasma::DataEngine::Data)),
visualization, SLOT(dataUpdated(QString,Plasma::DataEngine::Data)));
} else {
SignalRelay *relay = objIt.value();
if (relay->receiverCount() == 1) {
d->relays.remove(relay->m_interval);
delete relay;
} else {
disconnect(relay, SIGNAL(dataUpdated(QString,Plasma::DataEngine::Data)),
visualization, SLOT(dataUpdated(QString,Plasma::DataEngine::Data)));
}
}
d->relayObjects.erase(objIt);
checkUsage();
}
void DataContainer::checkForUpdate()
{
if (d->dirty) {
emit dataUpdated(objectName(), d->data);
foreach (SignalRelay *relay, d->relays) {
relay->checkQueueing();
}
d->dirty = false;
}
}
uint DataContainer::timeSinceLastUpdate() const
{
//FIXME: we still assume it's been <24h
//and ignore possible daylight savings changes
return d->updateTs.elapsed();
}
void DataContainer::setNeedsUpdate(bool update)
{
d->cached = update;
}
void DataContainer::checkUsage()
{
if (d->relays.count() < 1 &&
receivers(SIGNAL(dataUpdated(QString, Plasma::DataEngine::Data))) < 1) {
// DO NOT CALL ANYTHING AFTER THIS LINE AS IT MAY GET DELETED!
emit becameUnused(objectName());
}
}
} // Plasma namespace
#include "datacontainer.moc"

216
datacontainer.h Normal file
View File

@ -0,0 +1,216 @@
/*
* Copyright 2006-2007 Aaron Seigo <aseigo@kde.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Library General Public License as
* published by the Free Software Foundation; either version 2, 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 Library General Public
* License along with this program; if not, write to the
* Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef PLASMA_DATACONTAINER_H
#define PLASMA_DATACONTAINER_H
#include <QtCore/QHash>
#include <QtCore/QObject>
#include <plasma/plasma_export.h>
#include <plasma/dataengine.h>
namespace Plasma
{
class DataContainerPrivate;
/**
* @class DataContainer plasma/datacontainer.h <Plasma/DataContainer>
*
* @brief A set of data exported via a DataEngine
*
* Plasma::DataContainer wraps the data exported by a DataEngine
* implementation, providing a generic wrapper for the data.
*
* A DataContainer may have zero or more associated pieces of data which
* are keyed by strings. The data itself is stored as QVariants. This allows
* easy and flexible retrieval of the information associated with this object
* without writing DataContainer or DataEngine specific code in visualizations.
*
* If you are creating your own DataContainer objects (and are passing them to
* DataEngine::addSource()), you normally just need to listen to the
* updateRequested() signal (as well as any other methods you might have of
* being notified of new data) and call setData() to actually update the data.
* Then you need to either trigger the scheduleSourcesUpdated signal of the
* parent DataEngine or call checkForUpdate() on the DataContainer.
*
* You also need to set a suitable name for the source with setObjectName().
* See DataEngine::addSource() for more information.
*
* Note that there is normally no need to subclass DataContainer, except as
* a way of encapsulating the data retreival for a source, since all notifications
* are done via signals rather than virtual methods.
**/
class PLASMA_EXPORT DataContainer : public QObject
{
friend class DataEngine;
friend class DataEnginePrivate;
Q_OBJECT
public:
/**
* Constructs a default DataContainer that has no name or data
* associated with it
**/
explicit DataContainer(QObject *parent = 0);
virtual ~DataContainer();
/**
* Returns the data for this DataContainer
**/
const DataEngine::Data data() const;
/**
* Set a value for a key.
*
* This also marks this source as needing to signal an update.
*
* If you call setData() directly on a DataContainer, you need to
* either trigger the scheduleSourcesUpdated() slot for the
* data engine it belongs to or call checkForUpdate() on the
* DataContainer.
*
* @param key a string used as the key for the data
* @param value a QVariant holding the actual data. If a null or invalid
* QVariant is passed in and the key currently exists in the
* data, then the data entry is removed
**/
void setData(const QString &key, const QVariant &value);
/**
* Removes all data currently associated with this source
*
* If you call removeAllData() on a DataContainer, you need to
* either trigger the scheduleSourcesUpdated() slot for the
* data engine it belongs to or call checkForUpdate() on the
* DataContainer.
**/
void removeAllData();
/**
* @return true if the visualization is currently connected
*/
bool visualizationIsConnected(QObject *visualization) const;
/**
* Connects an object to this DataContainer.
*
* May be called repeatedly for the same visualization without
* side effects
*
* @param visualization the object to connect to this DataContainer
* @param pollingInterval the time in milliseconds between updates
**/
void connectVisualization(QObject *visualization, uint pollingInterval,
Plasma::IntervalAlignment alignment);
public Q_SLOTS:
/**
* Disconnects an object from this DataContainer.
*
* Note that if this source was created by DataEngine::sourceRequestEvent(),
* it will be deleted by DataEngine once control returns to the event loop.
**/
void disconnectVisualization(QObject *visualization);
Q_SIGNALS:
/**
* Emitted when the data has been updated, allowing visualizations to
* reflect the new data.
*
* Note that you should not normally emit this directly. Instead, use
* checkForUpdates() or the DataEngine::scheduleSourcesUpdated() slot.
*
* @param source the objectName() of the DataContainer (and hence the name
* of the source) that updated its data
* @param data the updated data
**/
void dataUpdated(const QString &source, const Plasma::DataEngine::Data &data);
/**
* Emitted when the last visualization is disconnected.
*
* Note that if this source was created by DataEngine::sourceRequestEvent(),
* it will be deleted by DataEngine once control returns to the event loop
* after this signal is emitted.
*
* @param source the name of the source that became unused
**/
void becameUnused(const QString &source);
/**
* Emitted when an update is requested.
*
* If a polling interval was passed connectVisualization(), this signal
* will be emitted every time the interval expires.
*
* Note that if you create your own DataContainer (and pass it to
* DataEngine::addSource()), you will need to listen to this signal
* and refresh the data when it is triggered.
*
* @param source the datacontainer the update was requested for. Useful
* for classes that update the data for several containers.
**/
void updateRequested(DataContainer *source);
protected:
/**
* Checks whether any data has changed and, if so, emits dataUpdated().
**/
void checkForUpdate();
/**
* Returns how long ago, in msecs, that the data in this container was last updated.
*
* This is used by DataEngine to compress updates that happen more quickly than the
* minimum polling interval by calling setNeedsUpdate() instead of calling
* updateSourceEvent() immediately.
**/
uint timeSinceLastUpdate() const;
/**
* Indicates that the data should be treated as dirty the next time hasUpdates() is called.
*
* This is needed for the case where updateRequested() is triggered but we don't want to
* update the data immediately because it has just been updated. The second request won't
* be fulfilled in this case, because we never updated the data and so never called
* checkForUpdate(). So we claim it needs an update anyway.
**/
void setNeedsUpdate(bool update = true);
protected Q_SLOTS:
/**
* Check if the DataContainer is still in use.
*
* If not the signal "becameUnused" will be emitted.
*
* Warning: The DataContainer may be invalid after calling this function, because a listener
* to becameUnused() may have deleted it.
**/
void checkUsage();
private:
friend class SignalRelay;
DataContainerPrivate *const d;
};
} // Plasma namespace
#endif // multiple inclusion guard

642
dataengine.cpp Normal file
View File

@ -0,0 +1,642 @@
/*
* Copyright 2006-2007 Aaron Seigo <aseigo@kde.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Library General Public License as
* published by the Free Software Foundation; either version 2, 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 Library General Public
* License along with this program; if not, write to the
* Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "dataengine.h"
#include "private/dataengine_p.h"
#include <QQueue>
#include <QTimer>
#include <QTime>
#include <QTimerEvent>
#include <QVariant>
#include <KDebug>
#include <KPluginInfo>
#include <KService>
#include <KStandardDirs>
#include "datacontainer.h"
#include "package.h"
#include "service.h"
#include "scripting/dataenginescript.h"
#include "private/service_p.h"
namespace Plasma
{
DataEngine::DataEngine(QObject *parent, KService::Ptr service)
: QObject(parent),
d(new DataEnginePrivate(this, service))
{
connect(d->updateTimer, SIGNAL(timeout()), this, SLOT(scheduleSourcesUpdated()));
}
DataEngine::DataEngine(QObject *parent, const QVariantList &args)
: QObject(parent),
d(new DataEnginePrivate(this, KService::serviceByStorageId(args.count() > 0 ? args[0].toString() : QString())))
{
connect(d->updateTimer, SIGNAL(timeout()), this, SLOT(scheduleSourcesUpdated()));
}
DataEngine::~DataEngine()
{
//kDebug() << objectName() << ": bye bye birdy! ";
delete d;
}
QStringList DataEngine::sources() const
{
return d->sources.keys();
}
Service *DataEngine::serviceForSource(const QString &source)
{
return new NullService(source, this);
}
void DataEngine::connectSource(const QString &source, QObject *visualization,
uint pollingInterval,
Plasma::IntervalAlignment intervalAlignment) const
{
//kDebug() << "connectSource" << source;
bool newSource;
DataContainer *s = d->requestSource(source, &newSource);
if (s) {
// we suppress the immediate invocation of dataUpdated here if the
// source was prexisting and they don't request delayed updates
// (we want to do an immediate update in that case so they don't
// have to wait for the first time out)
d->connectSource(s, visualization, pollingInterval, intervalAlignment,
!newSource || pollingInterval > 0);
//kDebug() << " ==> source connected";
}
}
void DataEngine::connectAllSources(QObject *visualization, uint pollingInterval,
Plasma::IntervalAlignment intervalAlignment) const
{
foreach (DataContainer *s, d->sources) {
d->connectSource(s, visualization, pollingInterval, intervalAlignment);
}
}
void DataEngine::disconnectSource(const QString &source, QObject *visualization) const
{
DataContainer *s = d->source(source, false);
if (s) {
s->disconnectVisualization(visualization);
}
}
DataContainer *DataEngine::containerForSource(const QString &source)
{
return d->source(source, false);
}
DataEngine::Data DataEngine::query(const QString &source) const
{
bool newSource;
DataContainer *s = d->requestSource(source, &newSource);
if (!s) {
return DataEngine::Data();
} else if (!newSource && d->minPollingInterval >= 0 &&
s->timeSinceLastUpdate() >= uint(d->minPollingInterval)) {
if (const_cast<DataEngine*>(this)->updateSourceEvent(source)) {
d->queueUpdate();
}
}
DataEngine::Data data = s->data();
s->checkUsage();
return data;
}
void DataEngine::init()
{
if (d->script) {
d->script->init();
} else {
// kDebug() << "called";
// default implementation does nothing. this is for engines that have to
// start things in motion external to themselves before they can work
}
}
bool DataEngine::sourceRequestEvent(const QString &name)
{
if (d->script) {
return d->script->sourceRequestEvent(name);
} else {
return false;
}
}
bool DataEngine::updateSourceEvent(const QString &source)
{
if (d->script) {
return d->script->updateSourceEvent(source);
} else {
//kDebug() << source;
return false; //TODO: should this be true to trigger, even needless, updates on every tick?
}
}
void DataEngine::setData(const QString &source, const QVariant &value)
{
setData(source, source, value);
}
void DataEngine::setData(const QString &source, const QString &key, const QVariant &value)
{
DataContainer *s = d->source(source, false);
bool isNew = !s;
if (isNew) {
s = d->source(source);
}
s->setData(key, value);
if (isNew) {
emit sourceAdded(source);
}
d->queueUpdate();
}
void DataEngine::setData(const QString &source, const Data &data)
{
DataContainer *s = d->source(source, false);
bool isNew = !s;
if (isNew) {
s = d->source(source);
}
Data::const_iterator it = data.constBegin();
while (it != data.constEnd()) {
s->setData(it.key(), it.value());
++it;
}
if (isNew) {
emit sourceAdded(source);
}
d->queueUpdate();
}
void DataEngine::removeAllData(const QString &source)
{
DataContainer *s = d->source(source, false);
if (s) {
s->removeAllData();
d->queueUpdate();
}
}
void DataEngine::removeData(const QString &source, const QString &key)
{
DataContainer *s = d->source(source, false);
if (s) {
s->setData(key, QVariant());
d->queueUpdate();
}
}
void DataEngine::addSource(DataContainer *source)
{
if (d->sources.contains(source->objectName())) {
kDebug() << "source named \"" << source->objectName() << "\" already exists.";
return;
}
QObject::connect(source, SIGNAL(updateRequested(DataContainer*)),
this, SLOT(internalUpdateSource(DataContainer*)));
d->sources.insert(source->objectName(), source);
emit sourceAdded(source->objectName());
d->queueUpdate();
}
void DataEngine::setMaxSourceCount(uint limit)
{
if (d->limit == limit) {
return;
}
d->limit = limit;
if (d->limit > 0) {
d->trimQueue();
} else {
d->sourceQueue.clear();
}
}
uint DataEngine::maxSourceCount() const
{
return d->limit;
}
void DataEngine::setMinimumPollingInterval(int minimumMs)
{
if (minimumMs < 0) {
minimumMs = 0;
}
d->minPollingInterval = minimumMs;
}
int DataEngine::minimumPollingInterval() const
{
return d->minPollingInterval;
}
void DataEngine::setPollingInterval(uint frequency)
{
killTimer(d->updateTimerId);
d->updateTimerId = 0;
if (frequency > 0) {
d->updateTimerId = startTimer(frequency);
}
}
/*
NOTE: This is not implemented to prevent having to store the value internally.
When there is a good use case for needing access to this value, we can
add another member to the Private class and add this method.
void DataEngine::pollingInterval()
{
return d->pollingInterval;
}
*/
void DataEngine::removeSource(const QString &source)
{
//kDebug() << "removing source " << source;
SourceDict::iterator it = d->sources.find(source);
if (it != d->sources.end()) {
DataContainer *s = it.value();
// remove it from the limit queue if we're keeping one
if (d->limit > 0) {
QQueue<DataContainer*>::iterator it = d->sourceQueue.begin();
while (it != d->sourceQueue.end()) {
if (*it == s) {
d->sourceQueue.erase(it);
break;
}
++it;
}
}
s->deleteLater();
d->sources.erase(it);
emit sourceRemoved(source);
}
}
void DataEngine::removeAllSources()
{
QMutableHashIterator<QString, Plasma::DataContainer*> it(d->sources);
while (it.hasNext()) {
it.next();
emit sourceRemoved(it.key());
delete it.value();
it.remove();
}
}
bool DataEngine::isValid() const
{
return d->valid;
}
bool DataEngine::isEmpty() const
{
return d->sources.isEmpty();
}
void DataEngine::setValid(bool valid)
{
d->valid = valid;
}
DataEngine::SourceDict DataEngine::containerDict() const
{
return d->sources;
}
void DataEngine::timerEvent(QTimerEvent *event)
{
if (event->timerId() != d->updateTimerId) {
kDebug() << "bzzzt";
return;
}
event->accept();
// if the freq update is less than 0, don't bother
if (d->minPollingInterval < 0) {
//kDebug() << "uh oh.. no polling allowed!";
return;
}
// minPollingInterval
if (d->updateTimestamp.elapsed() < d->minPollingInterval) {
//kDebug() << "hey now.. slow down!";
return;
}
d->updateTimestamp.restart();
QHashIterator<QString, Plasma::DataContainer*> it(d->sources);
while (it.hasNext()) {
it.next();
//kDebug() << "updating" << it.key();
updateSourceEvent(it.key());
}
scheduleSourcesUpdated();
}
void DataEngine::setIcon(const QString &icon)
{
d->icon = icon;
}
QString DataEngine::icon() const
{
return d->icon;
}
QString DataEngine::pluginName() const
{
if (!d->dataEngineDescription.isValid()) {
return QString();
}
return d->dataEngineDescription.pluginName();
}
const Package *DataEngine::package() const
{
return d->package;
}
void DataEngine::scheduleSourcesUpdated()
{
QHashIterator<QString, Plasma::DataContainer*> it(d->sources);
while (it.hasNext()) {
it.next();
it.value()->checkForUpdate();
}
}
QString DataEngine::name() const
{
return d->engineName;
}
void DataEngine::setName(const QString &name)
{
d->engineName = name;
setObjectName(name);
}
// Private class implementations
DataEnginePrivate::DataEnginePrivate(DataEngine *e, KService::Ptr service)
: q(e),
dataEngineDescription(service),
refCount(-1), // first ref
updateTimerId(0),
minPollingInterval(-1),
limit(0),
valid(true),
script(0),
package(0)
{
updateTimer = new QTimer(q);
updateTimer->setSingleShot(true);
updateTimestamp.start();
if (!service) {
engineName = i18n("Unnamed");
return;
}
engineName = service->name();
if (engineName.isEmpty()) {
engineName = i18n("Unnamed");
}
e->setObjectName(engineName);
icon = service->icon();
if (dataEngineDescription.isValid()) {
QString api = dataEngineDescription.property("X-Plasma-API").toString();
if (!api.isEmpty()) {
const QString path =
KStandardDirs::locate("data",
"plasma/engines/" + dataEngineDescription.pluginName() + '/');
PackageStructure::Ptr structure =
Plasma::packageStructure(api, Plasma::DataEngineComponent);
structure->setPath(path);
package = new Package(path, structure);
script = Plasma::loadScriptEngine(api, q);
if (!script) {
kDebug() << "Could not create a" << api << "ScriptEngine for the"
<< dataEngineDescription.name() << "DataEngine.";
delete package;
package = 0;
}
}
}
}
DataEnginePrivate::~DataEnginePrivate()
{
delete script;
script = 0;
delete package;
package = 0;
}
void DataEnginePrivate::internalUpdateSource(DataContainer *source)
{
if (minPollingInterval > 0 &&
source->timeSinceLastUpdate() < (uint)minPollingInterval) {
// skip updating this source; it's been too soon
//kDebug() << "internal update source is delaying" << source->timeSinceLastUpdate() << minPollingInterval;
//but fake an update so that the signalrelay that triggered this gets the data from the
//recent update. this way we don't have to worry about queuing - the relay will send a
//signal immediately and everyone else is undisturbed.
source->setNeedsUpdate();
return;
}
if (q->updateSourceEvent(source->objectName())) {
//kDebug() << "queuing an update";
queueUpdate();
}/* else {
kDebug() << "no update";
}*/
}
void DataEnginePrivate::ref()
{
--refCount;
}
void DataEnginePrivate::deref()
{
++refCount;
}
bool DataEnginePrivate::isUsed() const
{
return refCount != 0;
}
DataContainer *DataEnginePrivate::source(const QString &sourceName, bool createWhenMissing)
{
DataEngine::SourceDict::const_iterator it = sources.find(sourceName);
if (it != sources.constEnd()) {
DataContainer *s = it.value();
if (limit > 0) {
QQueue<DataContainer*>::iterator it = sourceQueue.begin();
while (it != sourceQueue.end()) {
if (*it == s) {
sourceQueue.erase(it);
break;
}
++it;
}
sourceQueue.enqueue(s);
}
return it.value();
}
if (!createWhenMissing) {
return 0;
}
/*kDebug() << "DataEngine " << q->objectName()
<< ": could not find DataContainer " << sourceName
<< ", creating" << endl;*/
DataContainer *s = new DataContainer(q);
s->setObjectName(sourceName);
sources.insert(sourceName, s);
QObject::connect(s, SIGNAL(updateRequested(DataContainer*)),
q, SLOT(internalUpdateSource(DataContainer*)));
if (limit > 0) {
trimQueue();
sourceQueue.enqueue(s);
}
return s;
}
void DataEnginePrivate::connectSource(DataContainer *s, QObject *visualization,
uint pollingInterval,
Plasma::IntervalAlignment align,
bool immediateCall)
{
//kDebug() << "connect source called" << s->objectName() << "with interval" << pollingInterval;
if (pollingInterval > 0) {
// never more frequently than allowed, never more than 20 times per second
uint min = qMax(50, minPollingInterval); // for qMax below
pollingInterval = qMax(min, pollingInterval);
// align on the 50ms
pollingInterval = pollingInterval - (pollingInterval % 50);
}
if (immediateCall) {
// we don't want to do an immediate call if we are simply
// reconnecting
//kDebug() << "immediate call requested, we have:" << s->visualizationIsConnected(visualization);
immediateCall = !s->visualizationIsConnected(visualization);
}
s->connectVisualization(visualization, pollingInterval, align);
if (immediateCall) {
QMetaObject::invokeMethod(visualization, "dataUpdated",
Q_ARG(QString, s->objectName()),
Q_ARG(Plasma::DataEngine::Data, s->data()));
}
}
DataContainer *DataEnginePrivate::requestSource(const QString &sourceName, bool *newSource)
{
if (newSource) {
*newSource = false;
}
//kDebug() << "requesting source " << sourceName;
DataContainer *s = source(sourceName, false);
if (!s) {
// we didn't find a data source, so give the engine an opportunity to make one
/*kDebug() << "DataEngine " << q->objectName()
<< ": could not find DataContainer " << sourceName
<< " will create on request" << endl;*/
if (q->sourceRequestEvent(sourceName)) {
s = source(sourceName, false);
if (s) {
// now we have a source; since it was created on demand, assume
// it should be removed when not used
if (newSource) {
*newSource = true;
}
QObject::connect(s, SIGNAL(becameUnused(QString)), q, SLOT(removeSource(QString)));
}
}
}
return s;
}
void DataEnginePrivate::trimQueue()
{
uint queueCount = sourceQueue.count();
while (queueCount >= limit) {
DataContainer *punted = sourceQueue.dequeue();
q->removeSource(punted->objectName());
}
}
void DataEnginePrivate::queueUpdate()
{
if (updateTimer->isActive()) {
return;
}
updateTimer->start(0);
}
}
#include "dataengine.moc"

464
dataengine.h Normal file
View File

@ -0,0 +1,464 @@
/*
* Copyright 2006-2007 Aaron Seigo <aseigo@kde.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Library General Public License as
* published by the Free Software Foundation; either version 2, 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 Library General Public
* License along with this program; if not, write to the
* Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef PLASMA_DATAENGINE_H
#define PLASMA_DATAENGINE_H
#include <QtCore/QHash>
#include <QtCore/QObject>
#include <QtCore/QStringList>
#include <KDE/KGenericFactory>
#include <KDE/KService>
#include <plasma/version.h>
#include <plasma/plasma.h>
namespace Plasma
{
class DataContainer;
class DataEngineScript;
class Package;
class Service;
class DataEnginePrivate;
/**
* @class DataEngine plasma/dataengine.h <Plasma/DataEngine>
*
* @short Data provider for plasmoids (Plasma plugins)
*
* This is the base class for DataEngines, which provide access to bodies of
* data via a common and consistent interface. The common use of a DataEngine
* is to provide data to a widget for display. This allows a user interface
* element to show all sorts of data: as long as there is a DataEngine, the
* data is retrievable.
*
* DataEngines are loaded as plugins on demand and provide zero, one or more
* data sources which are identified by name. For instance, a network
* DataEngine might provide a data source for each network interface.
**/
class PLASMA_EXPORT DataEngine : public QObject
{
Q_OBJECT
Q_PROPERTY(QStringList sources READ sources)
Q_PROPERTY(bool valid READ isValid)
Q_PROPERTY(QString icon READ icon WRITE setIcon)
public:
typedef QHash<QString, DataEngine*> Dict;
typedef QHash<QString, QVariant> Data;
typedef QHashIterator<QString, QVariant> DataIterator;
typedef QHash<QString, DataContainer*> SourceDict;
/**
* Constructor.
*
* @param parent The parent object.
* @param service pointer to the service that describes the engine
**/
explicit DataEngine(QObject *parent = 0, KService::Ptr service = KService::Ptr(0));
DataEngine(QObject *parent, const QVariantList &args);
~DataEngine();
/**
* This method is called when the DataEngine is started. When this
* method is called the DataEngine is fully constructed and ready to be
* used. This method should be reimplemented by DataEngine subclasses
* which have the need to perform a startup routine.
**/
virtual void init();
/**
* @return a list of all the data sources available via this DataEngine
* Whether these sources are currently available (which is what
* the default implementation provides) or not is up to the
* DataEngine to decide.
**/
virtual QStringList sources() const;
/**
* @param source the source to target the Service at
* @return a Service that has the source as a destination. The service
* is parented to the DataEngine, but may be deleted by the
* caller when finished with it
*/
virtual Service *serviceForSource(const QString &source);
/**
* Returns the engine name for the DataEngine
*/
QString name() const;
/**
* Connects a source to an object for data updates. The object must
* have a slot with the following signature:
*
* dataUpdated(const QString &sourceName, const Plasma::DataEngine::Data &data)
*
* The data is a QHash of QVariants keyed by QString names, allowing
* one data source to provide sets of related data.
*
* @param source the name of the data source
* @param visualization the object to connect the data source to
* @param pollingInterval the frequency, in milliseconds, with which to check for updates;
* a value of 0 (the default) means to update only
* when there is new data spontaneously generated
* (e.g. by the engine); any other value results in
* periodic updates from this source. This value is
* per-visualization and can be handy for items that require
* constant updates such as scrolling graphs or clocks.
* If the data has not changed, no update will be sent.
* @param intervalAlignment the number of ms to align the interval to
**/
Q_INVOKABLE void connectSource(
const QString &source, QObject *visualization,
uint pollingInterval = 0,
Plasma::IntervalAlignment intervalAlignment = NoAlignment) const;
/**
* Connects all currently existing sources to an object for data updates.
* The object must have a slot with the following signature:
*
* SLOT(dataUpdated(QString,Plasma::DataEngine::Data))
*
* The data is a QHash of QVariants keyed by QString names, allowing
* one data source to provide sets of related data.
*
* This method may be called multiple times for the same visualization
* without side-effects. This can be useful to change the pollingInterval.
*
* Note that this method does not automatically connect sources that
* may appear later on. Connecting and responding to the sourceAdded sigal
* is still required to achieve that.
*
* @param visualization the object to connect the data source to
* @param pollingInterval the frequency, in milliseconds, with which to check for updates;
* a value of 0 (the default) means to update only
* when there is new data spontaneously generated
* (e.g. by the engine); any other value results in
* periodic updates from this source. This value is
* per-visualization and can be handy for items that require
* constant updates such as scrolling graphs or clocks.
* If the data has not changed, no update will be sent.
* @param intervalAlignment the number of ms to align the interval to
**/
Q_INVOKABLE void connectAllSources(
QObject *visualization, uint pollingInterval = 0,
Plasma::IntervalAlignment intervalAlignment = NoAlignment) const;
/**
* Disconnects a source to an object that was receiving data updates.
*
* @param source the name of the data source
* @param visualization the object to connect the data source to
**/
Q_INVOKABLE void disconnectSource(const QString &source, QObject *visualization) const;
/**
* Retrevies a pointer to the DataContainer for a given source. This method
* should not be used if possible. An exception is for script engines that
* can not provide a QMetaObject as required by connectSource for the initial
* call to dataUpdated. Using this method, such engines can provide their own
* connectSource API.
*
* @param source the name of the source.
* @return pointer to a DataContainer, or zero on failure
**/
Q_INVOKABLE DataContainer *containerForSource(const QString &source);
/**
* Gets the Data associated with a data source.
*
* The data is a QHash of QVariants keyed by QString names, allowing
* one data source to provide sets of related data.
*
* @param source the data source to retrieve the data for
* @return the Data associated with the source; if the source doesn't
* exist an empty data set is returned
**/
Q_INVOKABLE DataEngine::Data query(const QString &source) const;
/**
* Returns true if this engine is valid, otherwise returns false
**/
bool isValid() const;
/**
* Returns true if the data engine is empty, which is to say that it has no
* data sources currently.
*/
bool isEmpty() const;
/**
* Returns the maximum number of sources this DataEngine will have
* at any given time.
*
* @return the maximum number of sources; zero means no limit.
*/
uint maxSourceCount() const;
/**
* @return the name of the icon for this data engine; and empty string
* is returned if there is no associated icon.
**/
QString icon() const;
/**
* Accessor for the associated Package object if any.
*
* @return the Package object, or 0 if none
**/
const Package *package() const;
/**
* Returns the plugin name for the applet
*/
QString pluginName() const;
Q_SIGNALS:
/**
* Emitted when a new data source is created
*
* Note that you do not need to emit this yourself unless
* you are reimplementing sources() and want to advertise
* that a new source is available (but hasn't been created
* yet).
*
* @param source the name of the new data source
**/
void sourceAdded(const QString &source);
/**
* Emitted when a data source is removed.
*
* Note that you do not need to emit this yourself unless
* you have reimplemented sources() and want to signal that
* a source that was available but was never created is no
* longer available.
*
* @param source the name of the data source that was removed
**/
void sourceRemoved(const QString &source);
protected:
/**
* When a source that does not currently exist is requested by the
* consumer, this method is called to give the DataEngine the
* opportunity to create one.
*
* The name of the data source (e.g. the source parameter passed into
* setData) must be the same as the name passed to sourceRequestEvent
* otherwise the requesting visualization may not receive notice of a
* data update.
*
* If the source can not be populated with data immediately (e.g. due to
* an asynchronous data acquisition method such as an HTTP request)
* the source must still be created, even if it is empty. This can
* be accomplished in these cases with the follow line:
*
* setData(name, DataEngine::Data());
*
* @param source the name of the source that has been requested
* @return true if a DataContainer was set up, false otherwise
*/
virtual bool sourceRequestEvent(const QString &source);
/**
* Called by internal updating mechanisms to trigger the engine
* to refresh the data contained in a given source. Reimplement this
* method when using facilities such as setPollingInterval.
* @see setPollingInterval
*
* @param source the name of the source that should be updated
* @return true if the data was changed, or false if there was no
* change or if the change will occur later
**/
virtual bool updateSourceEvent(const QString &source);
/**
* Sets a value for a data source. If the source
* doesn't exist then it is created.
*
* @param source the name of the data source
* @param value the data to associated with the source
**/
void setData(const QString &source, const QVariant &value);
/**
* Sets a value for a data source. If the source
* doesn't exist then it is created.
*
* @param source the name of the data source
* @param key the key to use for the data
* @param value the data to associated with the source
**/
void setData(const QString &source, const QString &key, const QVariant &value);
/**
* Adds a set of data to a data source. If the source
* doesn't exist then it is created.
*
* @param source the name of the data source
* @param data the data to add to the source
**/
void setData(const QString &source, const Data &data);
/**
* Removes all the data associated with a data source.
*
* @param source the name of the data source
**/
void removeAllData(const QString &source);
/**
* Removes a data entry from a source
*
* @param source the name of the data source
* @param key the data entry to remove
**/
void removeData(const QString &source, const QString &key);
/**
* Adds an already constructed data source. The DataEngine takes
* ownership of the DataContainer object. The objectName of the source
* is used for the source name.
*
* @param source the DataContainer to add to the DataEngine
**/
void addSource(DataContainer *source);
/**
* Sets an upper limit on the number of data sources to keep in this engine.
* If the limit is exceeded, then the oldest data source, as defined by last
* update, is dropped.
*
* @param limit the maximum number of sources to keep active
**/
void setMaxSourceCount(uint limit);
/**
* Sets the minimum amount of time, in milliseconds, that must pass between
* successive updates of data. This can help prevent too many updates happening
* due to multiple update requests coming in, which can be useful for
* expensive (time- or resource-wise) update mechanisms.
*
* @param minimumMs the minimum time lapse, in milliseconds, between updates.
* A value less than 0 means to never perform automatic updates,
* a value of 0 means update immediately on every update request,
* a value >0 will result in a minimum time lapse being enforced.
**/
void setMinimumPollingInterval(int minimumMs);
/**
* @return the minimum time between updates. @see setMinimumPollingInterval
**/
int minimumPollingInterval() const;
/**
* Sets up an internal update tick for all data sources. On every update,
* updateSourceEvent will be called for each applicable source.
* @see updateSourceEvent
*
* @param frequency the time, in milliseconds, between updates. A value of 0
* will stop internally triggered updates.
**/
void setPollingInterval(uint frequency);
/**
* Returns the current update frequency.
* @see setPollingInterval
NOTE: This is not implemented to prevent having to store the value internally.
When there is a good use case for needing access to this value, we can
add another member to the Private class and add this method.
uint pollingInterval();
**/
/**
* Removes all data sources
**/
void removeAllSources();
/**
* Sets whether or not this engine is valid, e.g. can be used.
* In practice, only the internal fall-back engine, the NullEngine
* should have need for this.
*
* @param valid whether or not the engine is valid
**/
void setValid(bool valid);
/**
* @return the list of active DataContainers.
*/
SourceDict containerDict() const;
/**
* Reimplemented from QObject
**/
void timerEvent(QTimerEvent *event);
/**
* Sets the engine name for the DataEngine
*/
void setName(const QString &name);
/**
* Sets the icon for this data engine
**/
void setIcon(const QString &icon);
protected Q_SLOTS:
/**
* Call this method when you call setData directly on a DataContainer instead
* of using the DataEngine::setData methods.
* If this method is not called, no dataUpdated(..) signals will be emitted!
*/
void scheduleSourcesUpdated();
/**
* Removes a data source.
* @param source the name of the data source to remove
**/
void removeSource(const QString &source);
private:
friend class DataEnginePrivate;
friend class DataEngineScript;
friend class DataEngineManager;
friend class NullEngine;
Q_PRIVATE_SLOT(d, void internalUpdateSource(DataContainer *source))
DataEnginePrivate *const d;
};
} // Plasma namespace
/**
* Register a data engine when it is contained in a loadable module
*/
#define K_EXPORT_PLASMA_DATAENGINE(libname, classname) \
K_PLUGIN_FACTORY(factory, registerPlugin<classname>();) \
K_EXPORT_PLUGIN(factory("plasma_engine_" #libname)) \
K_EXPORT_PLUGIN_VERSION(PLASMA_VERSION)
#endif // multiple inclusion guard

184
dataenginemanager.cpp Normal file
View File

@ -0,0 +1,184 @@
/*
* Copyright 2006-2007 Aaron Seigo <aseigo@kde.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Library General Public License as
* published by the Free Software Foundation; either version 2, 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 Library General Public
* License along with this program; if not, write to the
* Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "dataenginemanager.h"
#include <KDebug>
#include <KServiceTypeTrader>
#include "private/dataengine_p.h"
#include "scripting/scriptengine.h"
namespace Plasma
{
class NullEngine : public DataEngine
{
public:
NullEngine(QObject *parent = 0)
: DataEngine(parent)
{
setValid(false);
// ref() ourselves to ensure we never get deleted
d->ref();
}
};
class DataEngineManagerPrivate
{
public:
DataEngineManagerPrivate()
: nullEng(0)
{}
~DataEngineManagerPrivate()
{
foreach (Plasma::DataEngine *engine, engines) {
delete engine;
}
engines.clear();
delete nullEng;
}
DataEngine *nullEngine()
{
if (!nullEng) {
nullEng = new NullEngine;
}
return nullEng;
}
DataEngine::Dict engines;
DataEngine *nullEng;
};
class DataEngineManagerSingleton
{
public:
DataEngineManager self;
};
K_GLOBAL_STATIC(DataEngineManagerSingleton, privateDataEngineManagerSelf)
DataEngineManager *DataEngineManager::self()
{
return &privateDataEngineManagerSelf->self;
}
DataEngineManager::DataEngineManager()
: d(new DataEngineManagerPrivate)
{
}
DataEngineManager::~DataEngineManager()
{
delete d;
}
Plasma::DataEngine *DataEngineManager::engine(const QString &name) const
{
Plasma::DataEngine::Dict::const_iterator it = d->engines.find(name);
if (it != d->engines.end()) {
// ref and return the engine
//Plasma::DataEngine *engine = *it;
return *it;
}
return d->nullEngine();
}
Plasma::DataEngine *DataEngineManager::loadEngine(const QString &name)
{
Plasma::DataEngine *engine = 0;
Plasma::DataEngine::Dict::const_iterator it = d->engines.find(name);
if (it != d->engines.end()) {
engine = *it;
engine->d->ref();
return engine;
}
// load the engine, add it to the engines
QString constraint = QString("[X-KDE-PluginInfo-Name] == '%1'").arg(name);
KService::List offers = KServiceTypeTrader::self()->query("Plasma/DataEngine",
constraint);
QString error;
if (offers.isEmpty()) {
kDebug() << "offers are empty for " << name << " with constraint " << constraint;
} else {
QVariantList allArgs;
allArgs << offers.first()->storageId();
QString api = offers.first()->property("X-Plasma-API").toString();
if (api.isEmpty()) {
if (offers.first()) {
KPluginLoader plugin(*offers.first());
if (Plasma::isPluginVersionCompatible(plugin.pluginVersion())) {
engine = offers.first()->createInstance<Plasma::DataEngine>(0, allArgs, &error);
}
}
} else {
engine = new DataEngine(0, offers.first());
}
}
if (!engine) {
kDebug() << "Couldn't load engine \"" << name << "\". Error given: " << error;
return d->nullEngine();
}
engine->init();
d->engines[name] = engine;
return engine;
}
void DataEngineManager::unloadEngine(const QString &name)
{
Plasma::DataEngine::Dict::iterator it = d->engines.find(name);
if (it != d->engines.end()) {
Plasma::DataEngine *engine = *it;
engine->d->deref();
if (!engine->d->isUsed()) {
d->engines.erase(it);
delete engine;
}
}
}
QStringList DataEngineManager::listAllEngines()
{
QStringList engines;
KService::List offers = KServiceTypeTrader::self()->query("Plasma/DataEngine");
foreach (const KService::Ptr &service, offers) {
QString name = service->property("X-KDE-PluginInfo-Name").toString();
if (!name.isEmpty()) {
engines.append(name);
}
}
return engines;
}
} // namespace Plasma
#include "dataenginemanager.moc"

94
dataenginemanager.h Normal file
View File

@ -0,0 +1,94 @@
/*
* Copyright 2006-2007 Aaron Seigo <aseigo@kde.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Library General Public License as
* published by the Free Software Foundation; either version 2, 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 Library General Public
* License along with this program; if not, write to the
* Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef PLASMA_DATAENGINEMANAGER_H
#define PLASMA_DATAENGINEMANAGER_H
#include <QtCore/QHash>
#include <plasma/dataengine.h>
namespace Plasma
{
class DataEngineManagerPrivate;
/**
* @class DataEngineManager plasma/dataenginemanager.h <Plasma/DataEngineManager>
*
* @short DataEngine loader and life time manager
*
* Plasma::DataEngineManager provides facilities for listing, loading and
* according to reference count unloading of DataEngines.
**/
class PLASMA_EXPORT DataEngineManager: public QObject
{
Q_OBJECT
public:
/**
* Singleton pattern accessor.
*/
static DataEngineManager *self();
/**
* Returns a data engine object if one is loaded and available.
* On failure, the fallback NullEngine (which does nothing and
* !isValid()) is returned.
*
* @param name the name of the engine
*/
Plasma::DataEngine *engine(const QString &name) const;
/**
* Loads a data engine and increases the reference count on it.
* This should be called once per object (or set of objects) using the
* DataEngine. Afterwards, dataEngine should be used or the return
* value cached. Call unloadDataEngine when finished with the engine.
*
* @param name the name of the engine
* @return the data engine that was loaded, or the NullEngine on failure.
*/
Plasma::DataEngine *loadEngine(const QString &name);
/**
* Decreases the reference count on the engine. If the count reaches
* zero, then the engine is deleted to save resources.
*/
void unloadEngine(const QString &name);
/**
* Returns a listing of all known engines by name
*/
static QStringList listAllEngines();
private:
/**
* Default constructor. The singleton method self() is the
* preferred access mechanism.
*/
DataEngineManager();
~DataEngineManager();
DataEngineManagerPrivate *const d;
friend class DataEngineManagerSingleton;
};
} // namespace Plasma
#endif // multiple inclusion guard

385
delegate.cpp Normal file
View File

@ -0,0 +1,385 @@
/*
Copyright 2007 Robert Knight <robertknight@gmail.com>
Copyright 2007 Kevin Ottens <ervin@kde.org>
Copyright 2008 Marco Martin <notmart@gmail.com>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library 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
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
// Own
#include "delegate.h"
#include <cmath>
#include <math.h>
// Qt
#include <QApplication>
#include <QFontMetrics>
#include <QIcon>
#include <QModelIndex>
#include <QPainter>
#include <QStyleOptionViewItem>
// KDE
#include <KColorUtils>
#include <KDebug>
#include <KGlobal>
#include <KGlobalSettings>
#include <KColorScheme>
// plasma
#include <plasma/paintutils.h>
namespace Plasma
{
class DelegatePrivate
{
public:
DelegatePrivate() { }
~DelegatePrivate() { }
QFont fontForSubTitle(const QFont &titleFont) const;
QRect titleRect(const QStyleOptionViewItem &option, const QModelIndex &index) const;
QRect subTitleRect(const QStyleOptionViewItem &option, const QModelIndex &index) const;
QMap<int, int> roles;
static const int ICON_TEXT_MARGIN = 10;
static const int TEXT_RIGHT_MARGIN = 5;
static const int ACTION_ICON_SIZE = 22;
static const int ITEM_LEFT_MARGIN = 5;
static const int ITEM_RIGHT_MARGIN = 5;
static const int ITEM_TOP_MARGIN = 5;
static const int ITEM_BOTTOM_MARGIN = 5;
};
QFont DelegatePrivate::fontForSubTitle(const QFont &titleFont) const
{
QFont subTitleFont = titleFont;
subTitleFont.setPointSize(qMax(subTitleFont.pointSize() - 2,
KGlobalSettings::smallestReadableFont().pointSize()));
return subTitleFont;
}
QRect DelegatePrivate::titleRect(const QStyleOptionViewItem &option, const QModelIndex &index) const
{
QFont font(option.font);
font.setBold(true);
QFontMetrics fm(font);
Qt::Alignment textAlignment =
option.decorationAlignment & Qt::AlignRight ? Qt::AlignRight : Qt::AlignLeft;
QRect emptyRect;
if (option.direction == Qt::LeftToRight) {
emptyRect = option.rect.adjusted(
option.decorationSize.width() + ICON_TEXT_MARGIN + ITEM_LEFT_MARGIN,
ITEM_TOP_MARGIN, -ITEM_RIGHT_MARGIN, -ITEM_BOTTOM_MARGIN);
} else {
emptyRect = option.rect.adjusted(
ITEM_LEFT_MARGIN, ITEM_TOP_MARGIN,
-ITEM_RIGHT_MARGIN - option.decorationSize.width() - ICON_TEXT_MARGIN, -ITEM_BOTTOM_MARGIN);
}
if (emptyRect.width() < 0) {
emptyRect.setWidth(0);
return emptyRect;
}
QRect textRect = QStyle::alignedRect(
option.direction,
textAlignment,
fm.boundingRect(index.data(Qt::DisplayRole).toString()).size(),
emptyRect);
textRect.setWidth(textRect.width() + TEXT_RIGHT_MARGIN);
textRect.setHeight(emptyRect.height() / 2);
return textRect;
}
QRect DelegatePrivate::subTitleRect(const QStyleOptionViewItem &option,
const QModelIndex &index) const
{
QString subTitle = index.data(roles[Delegate::SubTitleRole]).toString();
QFontMetrics fm(fontForSubTitle(option.font));
QRect textRect = titleRect(option, index);
int right = textRect.right();
//if title=subtitle subtitle won't be displayed
if (subTitle != index.data(Qt::DisplayRole).toString()) {
textRect.setWidth(fm.width(" " + subTitle) + TEXT_RIGHT_MARGIN);
} else {
textRect.setWidth(0);
}
textRect.translate(0, textRect.height());
if (option.direction == Qt::RightToLeft) {
textRect.moveRight(right);
}
return textRect;
}
Delegate::Delegate(QObject *parent)
: QAbstractItemDelegate(parent),
d(new DelegatePrivate)
{
}
Delegate::~Delegate()
{
delete d;
}
void Delegate::setRoleMapping(SpecificRoles role, int actual)
{
d->roles[role] = actual;
}
int Delegate::roleMapping(SpecificRoles role) const
{
return d->roles[role];
}
QRect Delegate::rectAfterTitle(const QStyleOptionViewItem &option, const QModelIndex &index) const
{
QRect textRect = d->titleRect(option, index);
QRect emptyRect(0, textRect.top(), option.rect.width() - textRect.width() - DelegatePrivate::ITEM_LEFT_MARGIN - DelegatePrivate::ITEM_RIGHT_MARGIN - option.decorationSize.width() - DelegatePrivate::ICON_TEXT_MARGIN, textRect.height());
if (option.direction == Qt::LeftToRight) {
emptyRect.moveLeft(textRect.right());
} else {
emptyRect.moveRight(textRect.left());
}
if (emptyRect.width() < 0) {
emptyRect.setWidth(0);
}
return emptyRect;
}
QRect Delegate::rectAfterSubTitle(const QStyleOptionViewItem &option, const QModelIndex &index) const
{
QRect textRect = d->subTitleRect(option, index);
QRect emptyRect(0, textRect.top(), option.rect.width() - textRect.width() - DelegatePrivate::ITEM_LEFT_MARGIN - DelegatePrivate::ITEM_RIGHT_MARGIN - option.decorationSize.width() - DelegatePrivate::ICON_TEXT_MARGIN, textRect.height());
if (option.direction == Qt::LeftToRight) {
emptyRect.moveLeft(textRect.right());
} else {
emptyRect.moveRight(textRect.left());
}
if (emptyRect.width() < 0) {
emptyRect.setWidth(0);
}
return emptyRect;
}
QRect Delegate::emptyRect(const QStyleOptionViewItem &option, const QModelIndex &index) const
{
QRect afterTitleRect = rectAfterTitle(option, index);
QRect afterSubTitleRect = rectAfterSubTitle(option, index);
afterTitleRect.setHeight(afterTitleRect.height() * 2);
afterSubTitleRect.setTop(afterTitleRect.top());
return afterTitleRect.intersected(afterSubTitleRect);
}
void Delegate::paint(QPainter *painter, const QStyleOptionViewItem &option,
const QModelIndex &index) const
{
const bool hover = option.state & (QStyle::State_MouseOver | QStyle::State_Selected);
QRect contentRect = option.rect;
contentRect.setBottom(contentRect.bottom() - 1);
QRect decorationRect =
QStyle::alignedRect(option.direction,
option.decorationPosition == QStyleOptionViewItem::Left ?
Qt::AlignLeft : Qt::AlignRight,
option.decorationSize,
contentRect.adjusted(DelegatePrivate::ITEM_LEFT_MARGIN, DelegatePrivate::ITEM_TOP_MARGIN, -DelegatePrivate::ITEM_RIGHT_MARGIN, -DelegatePrivate::ITEM_BOTTOM_MARGIN));
decorationRect.moveTop(contentRect.top() + qMax(0, (contentRect.height() - decorationRect.height())) / 2);
QString titleText = index.data(Qt::DisplayRole).value<QString>();
QString subTitleText = index.data(d->roles[SubTitleRole]).value<QString>();
QRect titleRect = d->titleRect(option, index);
QRect subTitleRect = d->subTitleRect(option, index);
bool uniqueTitle = !index.data(d->roles[SubTitleMandatoryRole]).value<bool>();// true;
if (uniqueTitle) {
QModelIndex sib = index.sibling(index.row() + 1, index.column());
if (sib.isValid()) {
uniqueTitle = sib.data(Qt::DisplayRole).value<QString>() != titleText;
}
if (uniqueTitle) {
sib = index.sibling(index.row() + -1, index.column());
if (sib.isValid()) {
uniqueTitle = sib.data(Qt::DisplayRole).value<QString>() != titleText;
}
}
}
if (subTitleText == titleText) {
subTitleText.clear();
}
QFont subTitleFont = d->fontForSubTitle(option.font);
QFont titleFont(option.font);
if (hover) {
painter->save();
painter->setRenderHint(QPainter::Antialiasing);
const int column = index.column();
const int columns = index.model()->columnCount();
const int roundedRadius = 5;
// use a slightly translucent version of the palette's highlight color
// for the background
QColor backgroundColor = option.palette.color(QPalette::Highlight);
backgroundColor.setAlphaF(0.2);
QColor backgroundColor2 = option.palette.color(QPalette::Highlight);
backgroundColor.setAlphaF(0.5);
QRect highlightRect = option.rect.adjusted(2, 2, -2, -2);
QPen outlinePen(backgroundColor, 2);
if (column == 0) {
//clip right (or left for rtl languages) to make the connection with the next column
if (columns > 1) {
if (option.direction == Qt::LeftToRight) {
painter->setClipRect(option.rect);
highlightRect.adjust(0, 0, roundedRadius, 0);
} else {
painter->setClipRect(option.rect);
highlightRect.adjust(-roundedRadius, 0, 0, 0);
}
}
QLinearGradient gradient(highlightRect.topLeft(), highlightRect.topRight());
//reverse the gradient
if (option.direction == Qt::RightToLeft) {
gradient.setStart(highlightRect.topRight());
gradient.setFinalStop(highlightRect.topLeft());
}
gradient.setColorAt(0, backgroundColor);
gradient.setColorAt(((qreal)titleRect.width()/3.0) / (qreal)highlightRect.width(), backgroundColor2);
gradient.setColorAt(0.7, backgroundColor);
outlinePen.setBrush(gradient);
//last column, clip left (right for rtl)
} else if (column == columns-1) {
if (option.direction == Qt::LeftToRight) {
painter->setClipRect(option.rect);
highlightRect.adjust(-roundedRadius, 0, 0, 0);
} else {
painter->setClipRect(option.rect);
highlightRect.adjust(0, 0, +roundedRadius, 0);
}
//column < columns-1; clip both ways
} else {
painter->setClipRect(option.rect);
highlightRect.adjust(-roundedRadius, 0, +roundedRadius, 0);
}
painter->setPen(outlinePen);
painter->drawPath(PaintUtils::roundedRectangle(highlightRect, roundedRadius));
painter->restore();
}
// draw icon
QIcon decorationIcon = index.data(Qt::DecorationRole).value<QIcon>();
if (index.data(d->roles[ColumnTypeRole]).toInt() == SecondaryActionColumn) {
if (hover) {
// Only draw on hover
const int delta = floor((qreal)(option.decorationSize.width() - DelegatePrivate::ACTION_ICON_SIZE) / 2.0);
decorationRect.adjust(delta, delta-1, -delta-1, -delta);
decorationIcon.paint(painter, decorationRect, option.decorationAlignment);
}
} else {
// as default always draw as main column
decorationIcon.paint(painter, decorationRect, option.decorationAlignment);
}
painter->save();
// draw title
painter->setFont(titleFont);
painter->drawText(titleRect, Qt::AlignLeft|Qt::AlignVCenter, titleText);
if (hover || !uniqueTitle) {
// draw sub-title, BUT only if:
// * it isn't a unique title, this allows two items to have the same title and be
// disambiguated by their subtitle
// * we are directed by the model that this item should never be treated as unique
// e.g. the documents list in the recently used tab
// * the mouse is hovered over the item, causing the additional information to be
// displayed
//
// the rational for this is that subtitle text should in most cases not be
// required to understand the item itself and that showing all the subtexts in a
// listing makes the information density very high, impacting both the speed at
// which one can scan the list visually and the aesthetic qualities of the listing.
painter->setPen(QPen(KColorScheme(QPalette::Active).foreground(KColorScheme::InactiveText), 1));
painter->setFont(subTitleFont);
painter->drawText(subTitleRect, Qt::AlignLeft|Qt::AlignVCenter, " " + subTitleText);
}
painter->restore();
}
QSize Delegate::sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const
{
Q_UNUSED(index)
QSize size = option.rect.size();
QFontMetrics metrics(option.font);
QFontMetrics subMetrics(d->fontForSubTitle(option.font));
size.setHeight(qMax(option.decorationSize.height(), qMax(size.height(), metrics.height() + subMetrics.ascent()) + 3) + 4);
// kDebug() << "size hint is" << size << (metrics.height() + subMetrics.ascent());
size *= 1.1;
return size;
}
}

122
delegate.h Normal file
View File

@ -0,0 +1,122 @@
/*
Copyright 2007 Robert Knight <robertknight@gmail.com>
Copyright 2008 Marco Martin <notmart@gmail.com>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library 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
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#ifndef PLASMA_DELEGATE_H
#define PLASMA_DELEGATE_H
// Qt
#include <QtGui/QAbstractItemDelegate>
// Plasma
#include <plasma/plasma_export.h>
namespace Plasma
{
class DelegatePrivate;
/**
* @class Delegate plasma/delegate.h <Plasma/Delegate>
*
* Item delegate for rendering items in Plasma menus implemented with item views.
*
* The delegate makes use of its own data roles that are:
* SubTitleRole: the text of the subtitle
* SubTitleMandatoryRole: if the subtitle is to always be displayed
* (as default the subtitle is displayed only on mouse over)
* ColumnTypeRole: if the column is a main column (with title and subtitle)
* or a secondary action column (only a little icon that appears on mouse
* over is displayed)
*/
class PLASMA_EXPORT Delegate : public QAbstractItemDelegate
{
Q_OBJECT
public:
enum SpecificRoles {
SubTitleRole = Qt::UserRole + 1,
SubTitleMandatoryRole = Qt::UserRole + 2,
ColumnTypeRole = Qt::UserRole + 3
};
enum ColumnType {
MainColumn = 1,
SecondaryActionColumn = 2
};
Delegate(QObject *parent = 0);
~Delegate();
/**
* Maps an arbitrary role to a role belonging to SpecificRoles.
* Using this function you can use any model with this delegate.
*
* @param role a role belonging to SpecificRoles
* @param actual an arbitrary role of the model we are using
*/
void setRoleMapping(SpecificRoles role, int actual);
int roleMapping(SpecificRoles role) const;
//Reimplemented
virtual void paint(QPainter *painter, const QStyleOptionViewItem &option,
const QModelIndex &index) const;
protected:
/**
* Returns the empty area after the title.
* The height is the height of the subtitle.
* It can be used by subclasses that wants to paint additional data after
* calling the paint function of the superclass.
*
* @param option options for the title text
* @param index model index that we want to compute the free area
*/
QRect rectAfterTitle(const QStyleOptionViewItem &option, const QModelIndex &index) const;
/**
* Returns the empty area after the subtitle.
* The height is the height of the subtitle.
* It can be used by subclasses, that wants to paint additional data.
*
* @param option options for the subtitle text
* @param index model index that we want to compute the free area
*/
QRect rectAfterSubTitle(const QStyleOptionViewItem &option, const QModelIndex &index) const;
/**
* Returns the empty area after both the title and the subtitle.
* The height is the height of the item.
* It can be used by subclasses that wants to paint additional data
*
* @param option options for the title and subtitle text
* @param index model index that we want to compute the free area
*/
QRect emptyRect(const QStyleOptionViewItem &option, const QModelIndex &index) const;
virtual QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const;
private:
DelegatePrivate *const d;
};
}
#endif // PLASMA_DELEGATE_H

423
dialog.cpp Normal file
View File

@ -0,0 +1,423 @@
/*
* Copyright 2008 by Alessandro Diaferia <alediaferia@gmail.com>
* Copyright 2007 by Alexis Ménard <darktears31@gmail.com>
* Copyright 2007 Sebastian Kuegler <sebas@kde.org>
* Copyright 2006 Aaron Seigo <aseigo@kde.org>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301 USA
*/
#include "dialog.h"
#include <QPainter>
#include <QSvgRenderer>
#include <QResizeEvent>
#include <QMouseEvent>
#ifdef Q_WS_X11
#include <QX11Info>
#endif
#include <QBitmap>
#include <QtGui/QVBoxLayout>
#include <QtGui/QGraphicsSceneEvent>
#include <QtGui/QGraphicsView>
#include <QtGui/QGraphicsWidget>
#include <KDebug>
#include <NETRootInfo>
#include "plasma/applet.h"
#include "plasma/extender.h"
#include "plasma/private/extender_p.h"
#include "plasma/framesvg.h"
#include "plasma/theme.h"
#ifdef Q_WS_X11
#include <X11/Xlib.h>
#endif
const int resizeAreaMargin = 20;
namespace Plasma
{
class DialogPrivate
{
public:
DialogPrivate(Dialog *dialog)
: q(dialog),
background(0),
view(0),
widget(0),
resizeCorners(Dialog::NoCorner),
resizeStartCorner(Dialog::NoCorner)
{
}
~DialogPrivate()
{
}
void themeUpdated();
void adjustView();
Plasma::Dialog *q;
/**
* Holds the background SVG, to be re-rendered when the cache is invalidated,
* for example by resizing the dialogue.
*/
Plasma::FrameSvg *background;
QGraphicsView *view;
QGraphicsWidget *widget;
Dialog::ResizeCorners resizeCorners;
QMap<Dialog::ResizeCorner, QRect> resizeAreas;
Dialog::ResizeCorner resizeStartCorner;
};
void DialogPrivate::themeUpdated()
{
const int topHeight = background->marginSize(Plasma::TopMargin);
const int leftWidth = background->marginSize(Plasma::LeftMargin);
const int rightWidth = background->marginSize(Plasma::RightMargin);
const int bottomHeight = background->marginSize(Plasma::BottomMargin);
//TODO: correct handling of the situation when having vertical panels.
Extender *extender = qobject_cast<Extender*>(widget);
if (extender) {
switch (extender->d->applet->location()) {
case BottomEdge:
background->setEnabledBorders(FrameSvg::LeftBorder | FrameSvg::TopBorder
| FrameSvg::RightBorder);
q->setContentsMargins(0, topHeight, 0, 0);
break;
case TopEdge:
background->setEnabledBorders(FrameSvg::LeftBorder | FrameSvg::BottomBorder
| FrameSvg::RightBorder);
q->setContentsMargins(0, 0, 0, bottomHeight);
break;
case LeftEdge:
background->setEnabledBorders(FrameSvg::TopBorder | FrameSvg::BottomBorder
| FrameSvg::RightBorder);
q->setContentsMargins(0, topHeight, 0, bottomHeight);
break;
case RightEdge:
background->setEnabledBorders(FrameSvg::TopBorder | FrameSvg::BottomBorder
| FrameSvg::LeftBorder);
q->setContentsMargins(0, topHeight, 0, bottomHeight);
break;
default:
background->setEnabledBorders(FrameSvg::AllBorders);
q->setContentsMargins(leftWidth, topHeight, rightWidth, bottomHeight);
}
} else {
q->setContentsMargins(leftWidth, topHeight, rightWidth, bottomHeight);
}
q->update();
}
void DialogPrivate::adjustView()
{
if (view && widget) {
QSize prevSize = q->size();
/*
kDebug() << "Widget size:" << widget->size()
<< "| Widget size hint:" << widget->effectiveSizeHint(Qt::PreferredSize)
<< "| Widget minsize hint:" << widget->minimumSize()
<< "| Widget maxsize hint:" << widget->maximumSize()
<< "| Widget bounding rect:" << widget->boundingRect();
*/
//set the sizehints correctly:
int left, top, right, bottom;
q->getContentsMargins(&left, &top, &right, &bottom);
q->setMinimumSize(qMin(int(widget->minimumSize().width()) + left + right, QWIDGETSIZE_MAX),
qMin(int(widget->minimumSize().height()) + top + bottom, QWIDGETSIZE_MAX));
q->setMaximumSize(qMin(int(widget->maximumSize().width()) + left + right, QWIDGETSIZE_MAX),
qMin(int(widget->maximumSize().height()) + top + bottom, QWIDGETSIZE_MAX));
q->resize(qMin(int(view->size().width()) + left + right, QWIDGETSIZE_MAX),
qMin(int(view->size().height()) + top + bottom, QWIDGETSIZE_MAX));
q->updateGeometry();
//reposition and resize the view.
view->setSceneRect(widget->sceneBoundingRect());
view->resize(view->mapFromScene(view->sceneRect()).boundingRect().size());
view->centerOn(widget);
if (q->size() != prevSize) {
//the size of the dialog has changed, emit the signal:
emit q->dialogResized();
}
}
}
Dialog::Dialog(QWidget *parent, Qt::WindowFlags f)
: QWidget(parent, f),
d(new DialogPrivate(this))
{
setWindowFlags(Qt::FramelessWindowHint);
d->background = new FrameSvg(this);
d->background->setImagePath("dialogs/background");
d->background->setEnabledBorders(FrameSvg::AllBorders);
d->background->resizeFrame(size());
connect(d->background, SIGNAL(repaintNeeded()), this, SLOT(update()));
connect(Plasma::Theme::defaultTheme(), SIGNAL(themeChanged()), this, SLOT(themeUpdated()));
d->themeUpdated();
setMouseTracking(true);
}
Dialog::~Dialog()
{
delete d;
}
void Dialog::paintEvent(QPaintEvent *e)
{
QPainter p(this);
p.setRenderHint(QPainter::Antialiasing);
p.setClipRect(e->rect());
p.setCompositionMode(QPainter::CompositionMode_Source);
p.fillRect(rect(), Qt::transparent);
d->background->paintFrame(&p);
//we set the resize handlers
d->resizeAreas.clear();
if (d->resizeCorners & Dialog::NorthEast) {
d->resizeAreas[Dialog::NorthEast] = QRect(rect().right() - resizeAreaMargin, 0,
resizeAreaMargin, resizeAreaMargin);
}
if (d->resizeCorners & Dialog::NorthWest) {
d->resizeAreas[Dialog::NorthWest] = QRect(0, 0, resizeAreaMargin, resizeAreaMargin);
}
if (d->resizeCorners & Dialog::SouthEast) {
d->resizeAreas[Dialog::SouthEast] = QRect(rect().right() - resizeAreaMargin,
rect().bottom() - resizeAreaMargin,
resizeAreaMargin, resizeAreaMargin);
}
if (d->resizeCorners & Dialog::SouthWest) {
d->resizeAreas[Dialog::SouthWest] = QRect(0, rect().bottom() - resizeAreaMargin,
resizeAreaMargin, resizeAreaMargin);
}
}
void Dialog::mouseMoveEvent(QMouseEvent *event)
{
if (d->resizeAreas[Dialog::NorthEast].contains(event->pos()) && d->resizeCorners & Dialog::NorthEast) {
setCursor(Qt::SizeBDiagCursor);
} else if (d->resizeAreas[Dialog::NorthWest].contains(event->pos()) && d->resizeCorners & Dialog::NorthWest) {
setCursor(Qt::SizeFDiagCursor);
} else if (d->resizeAreas[Dialog::SouthEast].contains(event->pos()) && d->resizeCorners & Dialog::SouthEast) {
setCursor(Qt::SizeFDiagCursor);
} else if (d->resizeAreas[Dialog::SouthWest].contains(event->pos()) && d->resizeCorners & Dialog::SouthWest) {
setCursor(Qt::SizeBDiagCursor);
} else {
unsetCursor();
}
// here we take care of resize..
if (d->resizeStartCorner != Dialog::NoCorner) {
int newWidth;
int newHeight;
QPoint position;
switch(d->resizeStartCorner) {
case Dialog::NorthEast:
newWidth = event->x();
newHeight = height() - event->y();
position = QPoint(x(), y() + height() - newHeight);
break;
case Dialog::NorthWest:
newWidth = width() - event->x();
newHeight = height() - event->y();
position = QPoint(x() + width() - newWidth, y() + height() - newHeight);
break;
case Dialog::SouthWest:
newWidth = width() - event->x();
newHeight = event->y();
position = QPoint(x() + width() - newWidth, y());
break;
case Dialog::SouthEast:
newWidth = event->x();
newHeight = event->y();
position = QPoint(x(), y());
break;
default:
newWidth = width();
newHeight = height();
position = QPoint(x(), y());
break;
}
// let's check for limitations
if (newWidth < minimumWidth() || newWidth > maximumWidth()) {
newWidth = width();
position.setX(x());
}
if (newHeight < minimumHeight() || newHeight > maximumHeight()) {
newHeight = height();
position.setY(y());
}
setGeometry(QRect(position, QSize(newWidth, newHeight)));
}
QWidget::mouseMoveEvent(event);
}
void Dialog::mousePressEvent(QMouseEvent *event)
{
if (d->resizeAreas[Dialog::NorthEast].contains(event->pos()) && d->resizeCorners & Dialog::NorthEast) {
d->resizeStartCorner = Dialog::NorthEast;
} else if (d->resizeAreas[Dialog::NorthWest].contains(event->pos()) && d->resizeCorners & Dialog::NorthWest) {
d->resizeStartCorner = Dialog::NorthWest;
} else if (d->resizeAreas[Dialog::SouthEast].contains(event->pos()) && d->resizeCorners & Dialog::SouthEast) {
d->resizeStartCorner = Dialog::SouthEast;
} else if (d->resizeAreas[Dialog::SouthWest].contains(event->pos()) && d->resizeCorners & Dialog::SouthWest) {
d->resizeStartCorner = Dialog::SouthWest;
} else {
d->resizeStartCorner = Dialog::NoCorner;
}
QWidget::mousePressEvent(event);
}
void Dialog::mouseReleaseEvent(QMouseEvent *event)
{
if (d->resizeStartCorner != Dialog::NoCorner) {
d->resizeStartCorner = Dialog::NoCorner;
emit dialogResized();
}
QWidget::mouseReleaseEvent(event);
}
void Dialog::resizeEvent(QResizeEvent *e)
{
d->background->resizeFrame(e->size());
setMask(d->background->mask());
if (d->resizeStartCorner != Dialog::NoCorner && d->view && d->widget) {
d->widget->setPreferredSize(d->view->size());
QGraphicsLayoutItem *layout = d->widget->parentLayoutItem();
QGraphicsWidget *parentWidget = d->widget->parentWidget();
if (layout && parentWidget) {
layout->updateGeometry();
parentWidget->resize(layout->preferredSize());
}
d->view->setSceneRect(d->widget->mapToScene(d->widget->boundingRect()).boundingRect());
d->view->centerOn(d->widget);
}
}
void Dialog::setGraphicsWidget(QGraphicsWidget *widget)
{
if (d->widget) {
d->widget->removeEventFilter(this);
}
d->widget = widget;
if (widget) {
if (!layout()) {
QVBoxLayout *lay = new QVBoxLayout(this);
lay->setMargin(0);
lay->setSpacing(0);
}
d->themeUpdated();
if (!d->view) {
d->view = new QGraphicsView(this);
d->view->setFrameShape(QFrame::NoFrame);
d->view->viewport()->setAutoFillBackground(false);
layout()->addWidget(d->view);
}
d->view->setScene(widget->scene());
d->adjustView();
adjustSize();
widget->installEventFilter(this);
} else {
delete d->view;
d->view = 0;
}
}
QGraphicsWidget *Dialog::graphicsWidget()
{
return d->widget;
}
bool Dialog::eventFilter(QObject *watched, QEvent *event)
{
if (d->resizeStartCorner == Dialog::NoCorner && watched == d->widget &&
(event->type() == QEvent::GraphicsSceneResize || event->type() == QEvent::GraphicsSceneMove)) {
d->adjustView();
}
return QWidget::eventFilter(watched, event);
}
void Dialog::hideEvent(QHideEvent * event)
{
Q_UNUSED(event);
emit dialogVisible(false);
}
void Dialog::showEvent(QShowEvent * event)
{
Q_UNUSED(event);
emit dialogVisible(true);
}
void Dialog::setResizeHandleCorners(ResizeCorners corners)
{
d->resizeCorners = corners;
update();
}
Dialog::ResizeCorners Dialog::resizeCorners() const
{
return d->resizeCorners;
}
bool Dialog::inControlArea(const QPoint &point)
{
foreach (const QRect &r, d->resizeAreas) {
if (r.contains(point)) {
return true;
}
}
return false;
}
}
#include "dialog.moc"

134
dialog.h Normal file
View File

@ -0,0 +1,134 @@
/*
* Copyright 2008 by Alessandro Diaferia <alediaferia@gmail.com>
* Copyright 2007 by Alexis Ménard <darktears31@gmail.com>
* Copyright 2007 Sebastian Kuegler <sebas@kde.org>
* Copyright 2006 Aaron Seigo <aseigo@kde.org>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301 USA
*/
#ifndef PLASMA_DIALOG_H
#define PLASMA_DIALOG_H
#include <QtGui/QWidget>
#include <QtGui/QGraphicsSceneEvent>
#include <QtGui/QGraphicsView>
#include <plasma/plasma_export.h>
namespace Plasma
{
class DialogPrivate;
/**
* @class Dialog plasma/dialog.h <Plasma/Dialog>
*
* @short A dialog that uses the Plasma style
*
* Dialog provides a dialog-like widget that can be used to display additional
* information.
*
* Dialog uses the plasma theme, and usually has no window decoration. It's meant
* as an interim solution to display widgets as extension to plasma applets, for
* example when you click on an applet like the devicenotifier or the clock, the
* widget that is then displayed, is a Dialog.
*/
class PLASMA_EXPORT Dialog : public QWidget
{
Q_OBJECT
public:
/**
* Use these flags to choose the active resize corners.
*/
enum ResizeCorner {
NoCorner = 0,
NorthEast = 1,
SouthEast = 2,
NorthWest = 4,
SouthWest = 8,
All = NorthEast | SouthEast | NorthWest | SouthWest
};
Q_DECLARE_FLAGS(ResizeCorners, ResizeCorner)
/**
* @arg parent the parent widget, for plasmoids, this is usually 0.
* @arg f the Qt::WindowFlags, default is to not show a windowborder.
*/
explicit Dialog(QWidget * parent = 0, Qt::WindowFlags f = Qt::Window);
virtual ~Dialog();
void setGraphicsWidget(QGraphicsWidget *widget);
QGraphicsWidget *graphicsWidget();
/**
* @arg corners the corners the resize handlers should be placed in.
*/
void setResizeHandleCorners(ResizeCorners corners);
/**
* Convenience method to get the enabled resize corners.
* @return which resize corners are active.
*/
ResizeCorners resizeCorners() const;
Q_SIGNALS:
/**
* Fires when the dialog automatically resizes.
*/
void dialogResized();
/**
* Emit a signal when the dialog become visible/invisible
*/
void dialogVisible(bool status);
protected:
/**
* Reimplemented from QWidget
*/
void paintEvent(QPaintEvent *e);
void resizeEvent(QResizeEvent *e);
bool eventFilter(QObject *watched, QEvent *event);
void hideEvent (QHideEvent *event);
void showEvent (QShowEvent *event);
void mouseMoveEvent (QMouseEvent *event);
void mousePressEvent (QMouseEvent *event);
void mouseReleaseEvent (QMouseEvent *event);
/**
* Convenience method to know whether the point is in a control area (e.g. resize area)
* or not.
* @return true if the point is in the control area.
*/
bool inControlArea(const QPoint &point);
private:
DialogPrivate *const d;
friend class DialogPrivate;
/**
* React to theme changes
*/
Q_PRIVATE_SLOT(d, void themeUpdated())
};
} // Plasma namespace
Q_DECLARE_OPERATORS_FOR_FLAGS(Plasma::Dialog::ResizeCorners)
#endif

147
effects/blur.cpp Normal file
View File

@ -0,0 +1,147 @@
#ifndef BLUR_CPP
#define BLUR_CPP
/*
* Copyright 2007 Jani Huhtanen <jani.huhtanen@tut.fi>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Library General Public License version 2 as
* published by the Free Software Foundation
*
* 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 Library General Public
* License along with this program; if not, write to the
* Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include <cmath>
// Exponential blur, Jani Huhtanen, 2006
//
template<int aprec, int zprec>
static inline void blurinner(unsigned char *bptr, int &zR, int &zG, int &zB, int &zA, int alpha);
template<int aprec,int zprec>
static inline void blurrow(QImage &im, int line, int alpha);
template<int aprec, int zprec>
static inline void blurcol(QImage &im, int col, int alpha);
/*
* expblur(QImage &img, int radius)
*
* In-place blur of image 'img' with kernel
* of approximate radius 'radius'.
*
* Blurs with two sided exponential impulse
* response.
*
* aprec = precision of alpha parameter
* in fixed-point format 0.aprec
*
* zprec = precision of state parameters
* zR,zG,zB and zA in fp format 8.zprec
*/
template<int aprec,int zprec>
void expblur(QImage &img, int radius)
{
if(radius < 1) {
return;
}
/* Calculate the alpha such that 90% of
the kernel is within the radius.
(Kernel extends to infinity)
*/
int alpha = (int)((1 << aprec) * (1.0f - std::exp(-2.3f / (radius + 1.f))));
for (int row=0; row<img.height(); row++) {
blurrow<aprec,zprec>(img, row, alpha);
}
for (int col=0; col<img.width(); col++) {
blurcol<aprec,zprec>(img, col, alpha);
}
return;
}
template<int aprec, int zprec>
static inline void blurinner(unsigned char *bptr, int &zR, int &zG, int &zB, int &zA, int alpha)
{
int R, G, B, A;
R = *bptr;
G = *(bptr + 1);
B = *(bptr + 2);
A = *(bptr + 3);
zR += (alpha * ((R << zprec) - zR)) >> aprec;
zG += (alpha * ((G << zprec) - zG)) >> aprec;
zB += (alpha * ((B << zprec) - zB)) >> aprec;
zA += (alpha * ((A << zprec) - zA)) >> aprec;
*bptr = zR >> zprec;
*(bptr+1) = zG >> zprec;
*(bptr+2) = zB >> zprec;
*(bptr+3) = zA >> zprec;
}
template<int aprec,int zprec>
static inline void blurrow(QImage &im, int line, int alpha)
{
int zR, zG, zB, zA;
QRgb *ptr = (QRgb *)im.scanLine(line);
zR = *((unsigned char *)ptr ) << zprec;
zG = *((unsigned char *)ptr + 1) << zprec;
zB = *((unsigned char *)ptr + 2) << zprec;
zA = *((unsigned char *)ptr + 3) << zprec;
for (int index=1; index<im.width(); index++) {
blurinner<aprec,zprec>((unsigned char *)&ptr[index],zR,zG,zB,zA,alpha);
}
for (int index=im.width()-2; index>=0; index--) {
blurinner<aprec,zprec>((unsigned char *)&ptr[index],zR,zG,zB,zA,alpha);
}
}
template<int aprec, int zprec>
static inline void blurcol(QImage &im, int col, int alpha)
{
int zR, zG, zB, zA;
QRgb *ptr = (QRgb *)im.bits();
ptr += col;
zR = *((unsigned char *)ptr ) << zprec;
zG = *((unsigned char *)ptr + 1) << zprec;
zB = *((unsigned char *)ptr + 2) << zprec;
zA = *((unsigned char *)ptr + 3) << zprec;
for (int index=im.width(); index<(im.height()-1)*im.width(); index+=im.width()) {
blurinner<aprec,zprec>((unsigned char *)&ptr[index], zR, zG, zB, zA, alpha);
}
for (int index=(im.height()-2)*im.width(); index>=0; index-=im.width()) {
blurinner<aprec,zprec>((unsigned char *)&ptr[index], zR, zG, zB, zA, alpha);
}
}
template<class T>
inline const T &qClamp(const T &x, const T &low, const T &high)
{
if (x < low) {
return low;
} else if (x > high) {
return high;
} else {
return x;
}
}
#endif

446
extender.cpp Normal file
View File

@ -0,0 +1,446 @@
/*
* Copyright 2008 by Rob Scheepmaker <r.scheepmaker@student.utwente.nl>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301 USA
*/
#include "extender.h"
#include <QAction>
#include <QGraphicsGridLayout>
#include <QGraphicsLinearLayout>
#include "applet.h"
#include "containment.h"
#include "corona.h"
#include "extenderitem.h"
#include "framesvg.h"
#include "popupapplet.h"
#include "svg.h"
#include "widgets/label.h"
#include "private/applet_p.h"
#include "private/extender_p.h"
#include "private/extenderitem_p.h"
namespace Plasma
{
Extender::Extender(Applet *applet)
: QGraphicsWidget(applet),
d(new ExtenderPrivate(applet, this))
{
//At multiple places in the extender code, we make the assumption that an applet doesn't have
//more then one extender. If a second extender is created, destroy the first one to avoid leaks.
if (applet->d->extender) {
kWarning() << "Applet already has an extender, and can have only one extender."
<< "The previous extender will be destroyed.";
delete applet->d->extender;
}
applet->d->extender = this;
setContentsMargins(0, 0, 0, 0);
d->layout = new QGraphicsLinearLayout(this);
d->layout->setOrientation(Qt::Vertical);
d->layout->setContentsMargins(0, 0, 0, 0);
d->layout->setSpacing(0);
setLayout(d->layout);
setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Fixed);
d->emptyExtenderLabel = new Label(this);
d->emptyExtenderLabel->setText(d->emptyExtenderMessage);
d->emptyExtenderLabel->setMinimumSize(QSizeF(150, 64));
d->emptyExtenderLabel->setPreferredSize(QSizeF(200, 64));
d->emptyExtenderLabel->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Fixed);
d->layout->addItem(d->emptyExtenderLabel);
d->loadExtenderItems();
}
Extender::~Extender()
{
d->applet->d->extender = 0;
delete d;
}
void Extender::setEmptyExtenderMessage(const QString &message)
{
d->emptyExtenderMessage = message;
if (d->emptyExtenderLabel) {
d->emptyExtenderLabel->setText(message);
}
}
QString Extender::emptyExtenderMessage() const
{
return d->emptyExtenderMessage;
}
QList<ExtenderItem*> Extender::items() const
{
QList<ExtenderItem*> result;
//FIXME: a triple nested loop ... ew. there should be a more efficient way to do this
//iterate through all extenders we can find and check each extenders source applet.
foreach (Containment *c, d->applet->containment()->corona()->containments()) {
foreach (Applet *applet, c->applets()) {
if (applet->d->extender) {
foreach (ExtenderItem *item, applet->d->extender->attachedItems()) {
if (item->d->sourceApplet == d->applet) {
result.append(item);
}
}
}
}
}
return result;
}
QList<ExtenderItem*> Extender::attachedItems() const
{
return d->attachedExtenderItems;
}
QList<ExtenderItem*> Extender::detachedItems() const
{
QList<ExtenderItem*> result;
foreach (ExtenderItem *item, items()) {
if (item->isDetached()) {
result.append(item);
}
}
return result;
}
ExtenderItem *Extender::item(const QString &name) const
{
foreach (ExtenderItem *item, items()) {
if (item->name() == name) {
return item;
}
}
return 0;
}
void Extender::setAppearance(Appearance appearance)
{
if (d->appearance == appearance) {
return;
}
d->appearance = appearance;
d->updateBorders();
}
Extender::Appearance Extender::appearance() const
{
return d->appearance;
}
void Extender::saveState()
{
foreach (ExtenderItem *item, attachedItems()) {
item->config().writeEntry("extenderItemPosition", item->geometry().y());
}
}
QVariant Extender::itemChange(GraphicsItemChange change, const QVariant &value)
{
if (change == QGraphicsItem::ItemPositionHasChanged) {
d->adjustSize();
}
return QGraphicsWidget::itemChange(change, value);
}
void Extender::resizeEvent(QGraphicsSceneResizeEvent *event)
{
QGraphicsWidget::resizeEvent(event);
emit geometryChanged();
}
void Extender::itemAddedEvent(ExtenderItem *item, const QPointF &pos)
{
//this is a sane size policy imo.
item->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Fixed);
if (pos == QPointF(-1, -1)) {
d->layout->addItem(item);
} else {
d->layout->insertItem(d->insertIndexFromPos(pos), item);
}
//remove the empty extender message if needed.
if (d->emptyExtenderLabel) {
d->layout->removeItem(d->emptyExtenderLabel);
d->emptyExtenderLabel->hide();
}
d->adjustSize();
}
void Extender::itemRemovedEvent(ExtenderItem *item)
{
d->layout->removeItem(item);
//add the empty extender message if needed.
if (!attachedItems().count() && !d->spacerWidget) {
d->emptyExtenderLabel->show();
d->emptyExtenderLabel->setMinimumSize(item->size());
//just in case:
d->layout->removeItem(d->emptyExtenderLabel);
d->layout->addItem(d->emptyExtenderLabel);
}
d->adjustSize();
}
void Extender::itemHoverEnterEvent(ExtenderItem *item)
{
itemHoverMoveEvent(item, QPointF(0, 0));
}
void Extender::itemHoverMoveEvent(ExtenderItem *item, const QPointF &pos)
{
int insertIndex = d->insertIndexFromPos(pos);
if ((insertIndex == d->currentSpacerIndex) || (insertIndex == -1)) {
//relayouting is resource intensive, so don't do that when not necesarry.
return;
}
//Make sure we remove any spacer that might already be in the layout.
itemHoverLeaveEvent(item);
d->currentSpacerIndex = insertIndex;
//Create a widget that functions as spacer, and add that to the layout.
QGraphicsWidget *widget = new QGraphicsWidget(this);
widget->setMinimumSize(QSizeF(item->minimumSize().width(), item->size().height()));
widget->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed);
d->spacerWidget = widget;
d->layout->insertItem(insertIndex, widget);
//Make sure we remove any 'no detachables' label that might be there, and update the layout.
//XXX: duplicated from itemAttachedEvent.
if (d->emptyExtenderLabel) {
d->layout->removeItem(d->emptyExtenderLabel);
d->emptyExtenderLabel->hide();
}
d->adjustSize();
}
void Extender::itemHoverLeaveEvent(ExtenderItem *item)
{
Q_UNUSED(item);
if (d->spacerWidget) {
//Remove any trace of the spacer widget.
d->layout->removeItem(d->spacerWidget);
delete d->spacerWidget;
d->spacerWidget = 0;
d->currentSpacerIndex = -1;
//Make sure we add a 'no detachables' label when the layout is empty.
if (!attachedItems().count()) {
d->emptyExtenderLabel->show();
d->emptyExtenderLabel->setMinimumSize(item->size());
d->layout->removeItem(d->emptyExtenderLabel);
d->layout->addItem(d->emptyExtenderLabel);
}
d->adjustSize();
}
}
FrameSvg::EnabledBorders Extender::enabledBordersForItem(ExtenderItem *item) const
{
ExtenderItem *topItem = dynamic_cast<ExtenderItem*>(d->layout->itemAt(0));
ExtenderItem *bottomItem = dynamic_cast<ExtenderItem*>(d->layout->itemAt(d->layout->count() - 1));
if (d->appearance == TopDownStacked && bottomItem != item) {
return FrameSvg::LeftBorder | FrameSvg::BottomBorder | FrameSvg::RightBorder;
} else if (d->appearance == BottomUpStacked && topItem != item) {
return FrameSvg::LeftBorder | FrameSvg::TopBorder | FrameSvg::RightBorder;
} else if (d->appearance != NoBorders) {
return FrameSvg::LeftBorder | FrameSvg::RightBorder;
} else {
return 0;
}
}
ExtenderPrivate::ExtenderPrivate(Applet *applet, Extender *extender) :
q(extender),
applet(applet),
currentSpacerIndex(-1),
spacerWidget(0),
emptyExtenderMessage(i18n("no items")),
emptyExtenderLabel(0),
popup(false),
appearance(Extender::NoBorders)
{
}
ExtenderPrivate::~ExtenderPrivate()
{
}
void ExtenderPrivate::addExtenderItem(ExtenderItem *item, const QPointF &pos)
{
attachedExtenderItems.append(item);
q->itemHoverLeaveEvent(item);
q->itemAddedEvent(item, pos);
updateBorders();
emit q->itemAttached(item);
}
void ExtenderPrivate::removeExtenderItem(ExtenderItem *item)
{
attachedExtenderItems.removeOne(item);
//collapse the popupapplet if the last item is removed.
if (!q->attachedItems().count()) {
PopupApplet *popupApplet = qobject_cast<PopupApplet*>(applet);
if (popupApplet) {
popupApplet->hidePopup();
}
}
q->itemRemovedEvent(item);
updateBorders();
}
int ExtenderPrivate::insertIndexFromPos(const QPointF &pos) const
{
int insertIndex = -1;
//XXX: duplicated from panel
if (pos != QPointF(-1, -1)) {
for (int i = 0; i < layout->count(); ++i) {
QRectF siblingGeometry = layout->itemAt(i)->geometry();
qreal middle = (siblingGeometry.top() + siblingGeometry.bottom()) / 2.0;
if (pos.y() < middle) {
insertIndex = i;
break;
} else if (pos.y() <= siblingGeometry.bottom()) {
insertIndex = i + 1;
break;
}
}
}
return insertIndex;
}
void ExtenderPrivate::loadExtenderItems()
{
KConfigGroup cg = applet->config("ExtenderItems");
//first create a list of extenderItems, and then sort them on their position, so the items get
//recreated in the correct order.
//TODO: this restoring of the correct order should now be done in itemAddedEvent instead of
//here, to allow easy subclassing of Extender.
QList<QPair<int, QString> > groupList;
foreach (const QString &extenderItemId, cg.groupList()) {
KConfigGroup dg = cg.group(extenderItemId);
groupList.append(qMakePair(dg.readEntry("extenderItemPosition", 0), extenderItemId));
}
qSort(groupList);
//iterate over the extender items
for (int i = 0; i < groupList.count(); i++) {
QPair<int, QString> pair = groupList[i];
KConfigGroup dg = cg.group(pair.second);
//load the relevant settings.
QString extenderItemId = dg.name();
QString extenderItemName = dg.readEntry("extenderItemName", "");
QString appletName = dg.readEntry("sourceAppletPluginName", "");
uint sourceAppletId = dg.readEntry("sourceAppletId", 0);
bool temporarySourceApplet = false;
//find the source applet.
Corona *corona = applet->containment()->corona();
Applet *sourceApplet = 0;
foreach (Containment *containment, corona->containments()) {
foreach (Applet *applet, containment->applets()) {
if (applet->id() == sourceAppletId) {
sourceApplet = applet;
}
}
}
//There is no source applet. We just instantiate one just for the sake of creating
//detachables.
if (!sourceApplet) {
kDebug() << "creating a temporary applet as factory";
sourceApplet = Applet::load(appletName);
temporarySourceApplet = true;
//TODO: maybe add an option to applet to indicate that it shouldn't be deleted after
//having used it as factory.
}
if (!sourceApplet) {
kDebug() << "sourceApplet is null? appletName = " << appletName;
kDebug() << " extenderItemId = " << extenderItemId;
} else {
ExtenderItem *item = new ExtenderItem(q, extenderItemId.toInt());
item->setName(extenderItemName);
sourceApplet->initExtenderItem(item);
if (temporarySourceApplet) {
delete sourceApplet;
}
}
}
adjustSize();
}
void ExtenderPrivate::updateBorders()
{
foreach (ExtenderItem *item, q->attachedItems()) {
if (item && (item->d->background->enabledBorders() != q->enabledBordersForItem(item))) {
//call themeChanged to change the backgrounds enabled borders, and move all contained
//widgets according to it's changed margins.
item->d->themeChanged();
}
}
}
void ExtenderPrivate::adjustSize()
{
if (q->layout()) {
q->layout()->updateGeometry();
//FIXME: for some reason the preferred size hint get's set correctly,
//but the minimumSizeHint doesn't. Investigate why.
q->setMinimumSize(q->layout()->preferredSize());
}
emit q->geometryChanged();
}
} // Plasma namespace
#include "extender.moc"

250
extender.h Normal file
View File

@ -0,0 +1,250 @@
/*
* Copyright 2008 by Rob Scheepmaker <r.scheepmaker@student.utwente.nl>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301 USA
*/
#ifndef PLASMA_EXTENDER_H
#define PLASMA_EXTENDER_H
#include <QtGui/QGraphicsWidget>
#include "plasma/framesvg.h"
#include "plasma/plasma_export.h"
namespace Plasma
{
class ExtenderPrivate;
class ExtenderItem;
class Applet;
/**
* @class Extender plasma/extender.h <Plasma/Extender>
*
* @short Extends applets to allow detachable parts
*
* An Extender is a widget that visually extends the normal contents of an applet with
* additional dynamic widgets called ExtenderItems. These ExtenderItems can be
* detached by the user and dropped either on another Extender or on the canvas directly.
*
* This widget allows using ExtenderItems in your applet. Extender takes care of the presentation
* of a collection of ExtenderItems and keeps track of ExtenderItems that originate in it.
*
* The default Extender implementation displays extender items in a vertical layout with
* spacers that appear when dropping an ExtenderItem over it.
*
* If you wish to have a different presentation of extender items, you can choose to subclass
* Extender and reimplement the extenderItem* events and, optionally, the saveState function.
*
* To use an Extender in you applet, you'll have to instantiate one. A call to extender() in your
* applet will create an extender on your applet if you haven't got one already. Every applet can
* contain only one extender. Think of it as a decorator that adds some functionality to applets
* that require it. Never instantiate an Extender before init() in your applet. This won't work
* correctly since a scene is required when an Extender is instantiated.
*
* As soon as an Extender is instantiated, ExtenderItems contained previously in this Extender are
* restored using the initExtenderItem function from the applet the items originally came from. For
* more information on how this works and how to use ExtenderItems in general, see the ExtenderItem
* API documentation.
*/
class PLASMA_EXPORT Extender : public QGraphicsWidget
{
Q_OBJECT
Q_PROPERTY(QString emptyExtenderMessage READ emptyExtenderMessage WRITE setEmptyExtenderMessage)
public:
/**
* Description on how to render the extender's items.
*/
enum Appearance {
NoBorders = 0, /**< Draws no borders on the extender's items. When placed in an applet
on the desktop, use this setting and use the standard margins of
the applet containing this extender. */
BottomUpStacked = 1, /**< Draws no borders on the topmost extenderitem, but draws the
left, top and right border on subsequent items. When margins
of the containing dialog are set to 0, except for the top
margin, this leads to the 'stacked' look, recommended for
extenders of applet's contained in a panel at the bottom of
the screen. */
TopDownStacked = 2 /**< Draws no borders on the bottom extenderitem, but draws the
left, bottom and right border on subsequent items. When margins
of the containing dialog are set to 0, except for the bottom
margin, this leads to the 'stacked' look, recommended for
extenders of applet's contained in a panel at the top of
the screen. */
};
/**
* Creates an extender. Note that extender expects applet to have a config(), and needs a
* scene because of that. So you should only instantiate an extender in init() or later, not
* in an applet's constructor.
* The constructor also takes care of restoring ExtenderItems that were contained in this
* extender before, so ExtenderItems are persistent between sessions.
* @param applet The applet this extender is part of. Null is not allowed here.
*/
explicit Extender(Applet *applet);
~Extender();
/**
* @param message The text to be shown whenever the applet's extender is empty.
* Defaults to i18n'ed "no items".
*/
void setEmptyExtenderMessage(const QString &message);
/**
* @return The text to be shown whenever the applet's layout is empty.
*/
QString emptyExtenderMessage() const;
/**
* @returns a list of all extender items (attached AND detached) where the source applet is
* this applet.
*/
QList<ExtenderItem*> items() const;
/**
* @returns a list of all attached extender items.
*/
QList<ExtenderItem*> attachedItems() const;
/**
* @returns a list of all detached extender items.
*/
QList<ExtenderItem*> detachedItems() const;
/**
* This function can be used for easily determining if a certain item is already displayed
* in a extender item somewhere, so your applet doesn't duplicate this item. Say the applet
* displays 'jobs', from an engine which add's a source for every job. In sourceAdded you
* could do something like:
* if (!item(source)) {
* //add an extender item monitoring this source.
* }
*/
ExtenderItem *item(const QString &name) const;
/**
* Use this function to instruct the extender on how to render it's items. Usually you will
* want to call this function in your applet's constraintsEvent, allthough this is already
* done for you when using PopupApplet at base class for your applet. Defaults to NoBorders.
*/
void setAppearance(Appearance appearance);
/**
* @return the current way of rendering extender items that is used.
*/
Appearance appearance() const;
protected:
/**
* Get's called after an item has been added to this extender. The bookkeeping has already
* been done when this function get's called. The only thing left to do is put it somewhere
* appropriate. The default implementation adds the extenderItem to the appropriate place in
* a QGraphicsLinearLayout.
* @param item The item that has just been added.
* @param pos The location the item has been dropped in local coordinates.
*/
virtual void itemAddedEvent(ExtenderItem *item, const QPointF &pos);
/**
* Get's called after an item has been removed from this extender. All bookkeeping has
* already been done when this function get's called.
* @param item The item that has just been removed.
*/
virtual void itemRemovedEvent(ExtenderItem *item);
/**
* Get's called when an ExtenderItem that get's dragged enters this extender. Default
* implementation does nothing.
*/
virtual void itemHoverEnterEvent(ExtenderItem *item);
/**
* Gets called when an ExtenderItem is hovering over this extender. Implement this function
* to give some visual feedback about what will happen when the mouse button is released at
* that position. The default implementation shows a spacer at the appropriate location in
* the layout.
* @param item The item that's hovering over this extender. Most useful for obtaining the
* size of the spacer.
* @param pos The location the item is hovering.
*/
virtual void itemHoverMoveEvent(ExtenderItem *item, const QPointF &pos);
/**
* Get's called when an ExtenderItem that was previously hovering over this extender moves
* away from this extender. The default implementation removes any spacer from the layout.
*/
virtual void itemHoverLeaveEvent(ExtenderItem *item);
/**
* This function get's called for every extender when plasma exits. Implement this function
* to store the current state of this extender (position in a layout for example), so this
* can be restored when applet starts again. The default implementation stores the y
* coordinate of every extender item in the config field extenderItemPos.
*/
virtual void saveState();
/**
* This function get's called on every item to determine which background border's to
* render.
* @param item the item for which it's position or extender has changed.
* @return the borders that have to be enabled on it's background.
*/
virtual FrameSvg::EnabledBorders enabledBordersForItem(ExtenderItem *item) const;
/**
* Reimplemented from QGraphicsWidget
*/
QVariant itemChange(GraphicsItemChange change, const QVariant &value);
/**
* Reimplemented from QGraphicsWidget
*/
void resizeEvent(QGraphicsSceneResizeEvent *event);
Q_SIGNALS:
/**
* Fires when an extender item is added to this extender.
*/
void itemAttached(Plasma::ExtenderItem *);
/**
* Fires when an extender item is removed from this extender.
*/
void itemDetached(Plasma::ExtenderItem *);
/**
* Fires when an extender's preferred size changes.
*/
void geometryChanged();
private:
ExtenderPrivate *const d;
friend class ExtenderPrivate;
friend class ExtenderItem;
friend class ExtenderItemPrivate;
//dialog needs access to the extender's applet location.
friend class DialogPrivate;
//applet should be able to call saveState();
friend class Applet;
};
} // Plasma namespace
#endif //PLASMA_EXTENDER_H

1001
extenderitem.cpp Normal file

File diff suppressed because it is too large Load Diff

257
extenderitem.h Normal file
View File

@ -0,0 +1,257 @@
/*
* Copyright 2008 by Rob Scheepmaker <r.scheepmaker@student.utwente.nl>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301 USA
*/
#ifndef PLASMA_EXTENDERITEM_H
#define PLASMA_EXTENDERITEM_H
#include <QtGui/QGraphicsWidget>
#include <KDE/KConfigGroup>
#include <KDE/KIcon>
#include "plasma/plasma_export.h"
namespace Plasma
{
class Applet;
class Extender;
class ExtenderItemPrivate;
/**
* @class ExtenderItem plasma/extenderitem.h <Plasma/ExtenderItem>
*
* @short Provides detachable items for an Extender
*
* This class wraps around a QGraphicsWidget and provides drag&drop handling, a draghandle,
* title and ability to display qactions as a row of icon, ability to expand, collapse, return
* to source and tracks configuration associated with this item for you.
*
* Typical usage of ExtenderItems in your applet could look like this:
*
* @code
* ExtenderItem *item = new ExtenderItem(extender());
* //name can be used to later access this item through extender()->item(name):
* item->setName("networkmonitoreth0");
* item->config().writeEntry("device", "eth0");
* initExtenderItem(item);
* @endcode
*
* You'll then need to implement the initExtenderItem function. Having this function in your applet
* makes sure that detached extender items get restored after plasma is restarted, just like applets
* are. That is also the reason that we write an entry in item->config().
* In this function you should instantiate a QGraphicsWidget or QGraphicsItem and call the
* setWidget function on the ExtenderItem. This is the only correct way of adding actual content to
* a extenderItem. An example:
*
* @code
* void MyApplet::initExtenderItem(Plasma::ExtenderItem *item)
* {
* QGraphicsWidget *myNetworkMonitorWidget = new NetworkMonitorWidget(item);
* dataEngine("networktraffic")->connectSource(item->config().readEntry("device", ""),
* myNetworkMonitorWidget);
* item->setWidget(myNetworkMonitorWidget);
* }
* @endcode
*
*/
class PLASMA_EXPORT ExtenderItem : public QGraphicsWidget
{
Q_OBJECT
Q_PROPERTY(QGraphicsItem * widget READ widget WRITE setWidget)
Q_PROPERTY(QString title READ title WRITE setTitle)
Q_PROPERTY(QString name READ name WRITE setName)
Q_PROPERTY(QIcon icon READ icon WRITE setIcon)
Q_PROPERTY(Extender * extender READ extender WRITE setExtender)
Q_PROPERTY(bool collapsed READ isCollapsed WRITE setCollapsed)
Q_PROPERTY(bool detached READ isDetached)
Q_PROPERTY(uint autoExpireDelay WRITE setAutoExpireDelay)
public:
/**
* The constructor takes care of adding this item to an extender.
* @param hostExtender The extender the extender item belongs to.
* @param extenderItemId the id of the extender item. Use the default 0 to assign a new,
* unique id to this extender item.
*/
explicit ExtenderItem(Extender *hostExtender, uint extenderItemId = 0);
~ExtenderItem();
/**
* fetch the configuration of this widget.
* @return the configuration of this widget.
*/
KConfigGroup config() const;
/**
* @param widget The widget that should be wrapped into the extender item.
*/
void setWidget(QGraphicsItem *widget);
/**
* @return The widget that is wrapped into the extender item.
*/
QGraphicsItem *widget() const;
/**
* @param title the title that will be shown in the extender item's dragger. Default is
* no title. This title will also be stored in the item's configuration, so you don't have
* to manually store/restore this information for your extender items.
*/
void setTitle(const QString &title);
/**
* @return the title shown in the extender item's dragger.
*/
QString title() const;
/**
* You can assign names to extender items to look them up through the item() function.
* Make sure you only use unique names. This name will be stored in the item's
* configuration.
* @param name the name of the item. Defaults to an empty string.
*/
void setName(const QString &name);
/**
* @return the name of the item.
*/
QString name() const;
/**
* @param icon the icon name to display in the extender item's
* drag handle. Defaults to the source applet's icon. This icon name will also be stored
* in the item's configuration, so you don't have to manually store/restore this
* information.
*/
void setIcon(const QString &icon);
/**
* @param icon the icon to display in the extender item's drag handle. Defaults to the
* source applet's icon.
*/
void setIcon(const QIcon &icon);
/**
* @return the icon being displayed in the extender item's drag handle.
*/
QIcon icon() const;
/**
* @param extender the extender this item belongs to.
* @param pos the position in the extender this item should be added. Defaults to 'just
* append'.
*/
void setExtender(Extender *extender, const QPointF &pos = QPointF(-1, -1));
/**
* @return the extender this items belongs to.
*/
Extender *extender() const;
/**
* @param time (in ms) before this extender item destroys itself unless it is detached,
* in which case this extender stays around. 0 means forever and is the default.
*/
void setAutoExpireDelay(uint time);
/**
* @return whether or not this extender item has an auto expire delay.
*/
uint autoExpireDelay() const;
/**
* @return whether or not this item is detached from it's original source.
*/
bool isDetached() const;
/**
* @return whether or not the extender item is collapsed.
*/
bool isCollapsed() const;
/**
* @param name the name to store the action under in our collection.
* @param action the action to add. Actions will be displayed as an icon in the drag
* handle.
*/
void addAction(const QString &name, QAction *action);
/**
* @return the QAction with the given name from our collection. By default the action
* collection contains a "movebacktosource" action which will be only shown when the
* item is detached.
*/
QAction *action(const QString &name) const;
public Q_SLOTS:
/**
* Destroys the extender item. As opposed to calling delete on this class, destroy also
* removes the config group associated with this item.
*/
void destroy();
/**
* Collapse or expand the extender item. Defaults to false.
*/
void setCollapsed(bool collapsed);
/**
* Returns the extender item to its source applet.
*/
void returnToSource();
/**
* Shows a close button in this item's drag handle. By default a close button will not be
* shown.
*/
void showCloseButton();
/**
* Hides the close button in this item's drag handle.
*/
void hideCloseButton();
protected:
void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget);
void resizeEvent(QGraphicsSceneResizeEvent *event);
void mousePressEvent(QGraphicsSceneMouseEvent *event);
void mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event);
void mouseMoveEvent(QGraphicsSceneMouseEvent *event);
void mouseReleaseEvent(QGraphicsSceneMouseEvent *event);
void hoverMoveEvent(QGraphicsSceneHoverEvent *event);
void hoverLeaveEvent(QGraphicsSceneHoverEvent *event);
private:
Q_PRIVATE_SLOT(d, void toggleCollapse())
Q_PRIVATE_SLOT(d, void updateToolBox())
Q_PRIVATE_SLOT(d, void themeChanged())
Q_PRIVATE_SLOT(d, void sourceAppletRemoved())
ExtenderItemPrivate * const d;
friend class Extender;
friend class ExtenderPrivate;
};
} // namespace Plasma
#endif // PLASMA_EXTENDERITEM_H

676
framesvg.cpp Normal file
View File

@ -0,0 +1,676 @@
/*
* Copyright 2008 by Aaron Seigo <aseigo@kde.org>
* Copyright 2008 Marco Martin <notmart@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Library General Public License as
* published by the Free Software Foundation; either version 2, 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 Library General Public
* License along with this program; if not, write to the
* Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "framesvg.h"
#include <QPainter>
#include <QSize>
#include <QBitmap>
#include <KDebug>
#include <plasma/theme.h>
namespace Plasma
{
class FrameData
{
public:
FrameData()
: enabledBorders(FrameSvg::AllBorders),
frameSize(-1,-1)
{
}
FrameData(const FrameData &other)
: enabledBorders(other.enabledBorders),
frameSize(other.frameSize)
{
}
~FrameData()
{
}
FrameSvg::EnabledBorders enabledBorders;
QPixmap cachedBackground;
QBitmap cachedMask;
QSizeF frameSize;
//measures
int topHeight;
int leftWidth;
int rightWidth;
int bottomHeight;
//margins, are equal to the measures by default
int topMargin;
int leftMargin;
int rightMargin;
int bottomMargin;
//size of the svg where the size of the "center"
//element is contentWidth x contentHeight
bool noBorderPadding : 1;
bool stretchBorders : 1;
bool tileCenter : 1;
};
class FrameSvgPrivate
{
public:
FrameSvgPrivate(FrameSvg *psvg)
: q(psvg),
cacheAll(false)
{
}
~FrameSvgPrivate()
{
qDeleteAll(frames);
frames.clear();
}
void generateBackground(FrameData *frame);
void updateSizes();
void updateNeeded();
void updateAndSignalSizes();
Location location;
QString prefix;
FrameSvg *q;
bool cacheAll : 1;
QHash<QString, FrameData*> frames;
};
FrameSvg::FrameSvg(QObject *parent)
: Svg(parent),
d(new FrameSvgPrivate(this))
{
connect(this, SIGNAL(repaintNeeded()), this, SLOT(updateNeeded()));
d->frames.insert(QString(), new FrameData());
}
FrameSvg::~FrameSvg()
{
delete d;
}
void FrameSvg::setImagePath(const QString &path)
{
if (path == imagePath()) {
return;
}
Svg::setImagePath(path);
setContainsMultipleImages(true);
clearCache();
d->updateAndSignalSizes();
}
void FrameSvg::setEnabledBorders(const EnabledBorders borders)
{
if (borders == d->frames[d->prefix]->enabledBorders) {
return;
}
d->frames[d->prefix]->enabledBorders = borders;
d->updateAndSignalSizes();
}
FrameSvg::EnabledBorders FrameSvg::enabledBorders() const
{
QHash<QString, FrameData*>::const_iterator it = d->frames.constFind(d->prefix);
if (it != d->frames.constEnd()) {
return it.value()->enabledBorders;
} else {
return NoBorder;
}
}
void FrameSvg::setElementPrefix(Plasma::Location location)
{
switch (location) {
case TopEdge:
setElementPrefix("north");
break;
case BottomEdge:
setElementPrefix("south");
break;
case LeftEdge:
setElementPrefix("west");
break;
case RightEdge:
setElementPrefix("east");
break;
default:
setElementPrefix(QString());
break;
}
d->location = location;
}
void FrameSvg::setElementPrefix(const QString & prefix)
{
const QString oldPrefix(d->prefix);
if (!hasElement(prefix + "-center")) {
d->prefix.clear();
} else {
d->prefix = prefix;
if (!d->prefix.isEmpty()) {
d->prefix += '-';
}
}
if (oldPrefix == d->prefix && d->frames[oldPrefix]) {
return;
}
if (!d->frames.contains(d->prefix)) {
d->frames.insert(d->prefix, new FrameData(*(d->frames[oldPrefix])));
d->updateSizes();
}
if (!d->cacheAll) {
delete d->frames[oldPrefix];
d->frames.remove(oldPrefix);
}
d->location = Floating;
}
bool FrameSvg::hasElementPrefix(const QString & prefix) const
{
//for now it simply checks if a center element exists,
//because it could make sense for certain themes to not have all the elements
if (prefix.isEmpty()) {
return hasElement("center");
} else {
return hasElement(prefix + "-center");
}
}
bool FrameSvg::hasElementPrefix(Plasma::Location location) const
{
switch (location) {
case TopEdge:
return hasElementPrefix("north");
break;
case BottomEdge:
return hasElementPrefix("south");
break;
case LeftEdge:
return hasElementPrefix("west");
break;
case RightEdge:
return hasElementPrefix("east");
break;
default:
return hasElementPrefix(QString());
break;
}
}
QString FrameSvg::prefix()
{
if (d->prefix.isEmpty()) {
return d->prefix;
}
return d->prefix.left(d->prefix.size() - 1);
}
void FrameSvg::resizeFrame(const QSizeF &size)
{
if (size.isEmpty()) {
kWarning() << "Invalid size" << size;
return;
}
if (size == d->frames[d->prefix]->frameSize) {
return;
}
d->updateSizes();
d->frames[d->prefix]->frameSize = size;
}
QSizeF FrameSvg::frameSize() const
{
QHash<QString, FrameData*>::const_iterator it = d->frames.constFind(d->prefix);
if (it != d->frames.constEnd()) {
return it.value()->frameSize;
} else {
return QSize(-1, -1);
}
}
qreal FrameSvg::marginSize(const Plasma::MarginEdge edge) const
{
if (d->frames[d->prefix]->noBorderPadding) {
return .0;
}
switch (edge) {
case Plasma::TopMargin:
return d->frames[d->prefix]->topMargin;
break;
case Plasma::LeftMargin:
return d->frames[d->prefix]->leftMargin;
break;
case Plasma::RightMargin:
return d->frames[d->prefix]->rightMargin;
break;
//Plasma::BottomMargin
default:
return d->frames[d->prefix]->bottomMargin;
break;
}
}
void FrameSvg::getMargins(qreal &left, qreal &top, qreal &right, qreal &bottom) const
{
FrameData *frame = d->frames[d->prefix];
if (!frame || frame->noBorderPadding) {
left = top = right = bottom = 0;
return;
}
top = frame->topMargin;
left = frame->leftMargin;
right = frame->rightMargin;
bottom = frame->bottomMargin;
}
QRectF FrameSvg::contentsRect() const
{
QSizeF size(frameSize());
if (size.isValid()) {
QRectF rect(QPointF(0, 0), size);
FrameData *frame = d->frames[d->prefix];
return rect.adjusted(frame->leftMargin, frame->topMargin,
-frame->rightMargin, -frame->bottomMargin);
} else {
return QRectF();
}
}
QBitmap FrameSvg::mask() const
{
FrameData *frame = d->frames[d->prefix];
if (!frame->cachedMask) {
if (frame->cachedBackground.isNull()) {
d->generateBackground(frame);
if (frame->cachedBackground.isNull()) {
return QBitmap();
}
}
frame->cachedMask = QBitmap(frame->cachedBackground.alphaChannel().createMaskFromColor(Qt::black));
}
return frame->cachedMask;
}
void FrameSvg::setCacheAllRenderedFrames(bool cache)
{
if (d->cacheAll && !cache) {
clearCache();
}
d->cacheAll = cache;
}
bool FrameSvg::cacheAllRenderedFrames() const
{
return d->cacheAll;
}
void FrameSvg::clearCache()
{
FrameData *frame = d->frames[d->prefix];
// delete all the frames that aren't this one
QMutableHashIterator<QString, FrameData*> it(d->frames);
while (it.hasNext()) {
FrameData *p = it.next().value();
if (frame != p) {
delete p;
it.remove();
}
}
}
QPixmap FrameSvg::framePixmap()
{
FrameData *frame = d->frames[d->prefix];
if (frame->cachedBackground.isNull()) {
d->generateBackground(frame);
if (frame->cachedBackground.isNull()) {
return QPixmap();
}
}
return frame->cachedBackground;
}
void FrameSvg::paintFrame(QPainter *painter, const QRectF &target, const QRectF &source)
{
FrameData *frame = d->frames[d->prefix];
if (frame->cachedBackground.isNull()) {
d->generateBackground(frame);
if (frame->cachedBackground.isNull()) {
return;
}
}
painter->drawPixmap(target, frame->cachedBackground, source.isValid() ? source : target);
}
void FrameSvg::paintFrame(QPainter *painter, const QPointF &pos)
{
FrameData *frame = d->frames[d->prefix];
if (frame->cachedBackground.isNull()) {
d->generateBackground(frame);
if (frame->cachedBackground.isNull()) {
return;
}
}
painter->drawPixmap(pos, frame->cachedBackground);
}
void FrameSvgPrivate::generateBackground(FrameData *frame)
{
if (!frame->cachedBackground.isNull()) {
return;
}
QString id = QString::fromLatin1("%5_%4_%3_%2_%1_").
arg(frame->enabledBorders).arg(frame->frameSize.width()).arg(frame->frameSize.height()).arg(prefix).arg(q->imagePath());
Theme *theme = Theme::defaultTheme();
if (theme->findInCache(id, frame->cachedBackground) && !frame->cachedBackground.isNull()) {
return;
}
//kDebug() << "generating background";
const int topWidth = q->elementSize(prefix + "top").width();
const int leftHeight = q->elementSize(prefix + "left").height();
const int topOffset = 0;
const int leftOffset = 0;
if (!frame->frameSize.isValid()) {
kWarning() << "Invalid frame size" << frame->frameSize;
return;
}
const int contentWidth = frame->frameSize.width() - frame->leftWidth - frame->rightWidth;
const int contentHeight = frame->frameSize.height() - frame->topHeight - frame->bottomHeight;
int contentTop = 0;
int contentLeft = 0;
int rightOffset = contentWidth;
int bottomOffset = contentHeight;
frame->cachedBackground = QPixmap(frame->leftWidth + contentWidth + frame->rightWidth,
frame->topHeight + contentHeight + frame->bottomHeight);
frame->cachedBackground.fill(Qt::transparent);
QPainter p(&frame->cachedBackground);
p.setCompositionMode(QPainter::CompositionMode_Source);
p.setRenderHint(QPainter::SmoothPixmapTransform);
//if we must stretch the center or the borders we compute how much we will have to stretch
//the svg to get the desired element sizes
QSizeF scaledContentSize(0, 0);
if (q->elementSize(prefix + "center").width() > 0 &&
q->elementSize(prefix + "center").height() > 0 &&
(!frame->tileCenter || frame->stretchBorders)) {
scaledContentSize = QSizeF(contentWidth * ((qreal)q->size().width() / (qreal)q->elementSize(prefix + "center").width()),
contentHeight * ((qreal)q->size().height() / (qreal)q->elementSize(prefix + "center").height()));
}
//CENTER
if (frame->tileCenter) {
if (contentHeight > 0 && contentWidth > 0) {
int centerTileHeight;
int centerTileWidth;
centerTileHeight = q->elementSize(prefix + "center").height();
centerTileWidth = q->elementSize(prefix + "center").width();
QPixmap center(centerTileWidth, centerTileHeight);
center.fill(Qt::transparent);
{
QPainter centerPainter(&center);
centerPainter.setCompositionMode(QPainter::CompositionMode_Source);
q->paint(&centerPainter, QRect(QPoint(0, 0), q->elementSize(prefix + "center")), prefix + "center");
}
p.drawTiledPixmap(QRect(frame->leftWidth, frame->topHeight,
contentWidth, contentHeight), center);
}
} else {
if (contentHeight > 0 && contentWidth > 0) {
q->paint(&p, QRect(frame->leftWidth, frame->topHeight,
contentWidth, contentHeight),
prefix + "center");
}
}
// Corners
if (q->hasElement(prefix + "top") && frame->enabledBorders & FrameSvg::TopBorder) {
contentTop = frame->topHeight;
bottomOffset += frame->topHeight;
if (q->hasElement(prefix + "topleft") && frame->enabledBorders & FrameSvg::LeftBorder) {
q->paint(&p, QRect(leftOffset, topOffset, frame->leftWidth, frame->topHeight), prefix + "topleft");
contentLeft = frame->leftWidth;
rightOffset = contentWidth + frame->leftWidth;
}
if (q->hasElement(prefix + "topright") && frame->enabledBorders & FrameSvg::RightBorder) {
q->paint(&p, QRect(rightOffset, topOffset, frame->rightWidth, frame->topHeight), prefix + "topright");
}
}
if (q->hasElement(prefix + "bottom") && frame->enabledBorders & FrameSvg::BottomBorder) {
if (q->hasElement(prefix + "bottomleft") && frame->enabledBorders & FrameSvg::LeftBorder) {
q->paint(&p, QRect(leftOffset, bottomOffset, frame->leftWidth, frame->bottomHeight), prefix + "bottomleft");
contentLeft = frame->leftWidth;
rightOffset = contentWidth + frame->leftWidth;
}
if (q->hasElement(prefix + "bottomright") && frame->enabledBorders & FrameSvg::RightBorder) {
q->paint(&p, QRect(rightOffset, bottomOffset, frame->rightWidth, frame->bottomHeight), prefix + "bottomright");
}
}
// Sides
if (frame->stretchBorders) {
if (frame->enabledBorders & FrameSvg::LeftBorder || frame->enabledBorders & FrameSvg::RightBorder) {
if (q->hasElement(prefix + "left") &&
frame->enabledBorders & FrameSvg::LeftBorder) {
q->paint(&p, QRect(leftOffset, contentTop, frame->leftWidth, contentHeight), prefix + "left");
}
if (q->hasElement(prefix + "right") &&
frame->enabledBorders & FrameSvg::RightBorder) {
q->paint(&p, QRect(rightOffset, contentTop, frame->rightWidth, contentHeight), prefix + "right");
}
}
if (frame->enabledBorders & FrameSvg::TopBorder ||
frame->enabledBorders & FrameSvg::BottomBorder) {
if (q->hasElement(prefix + "top") &&
frame->enabledBorders & FrameSvg::TopBorder) {
q->paint(&p, QRect(contentLeft, topOffset, contentWidth, frame->topHeight), prefix + "top");
}
if (q->hasElement(prefix + "bottom") &&
frame->enabledBorders & FrameSvg::BottomBorder) {
q->paint(&p, QRect(contentLeft, bottomOffset, contentWidth, frame->bottomHeight), prefix + "bottom");
}
}
} else {
if (q->hasElement(prefix + "left") &&
frame->enabledBorders & FrameSvg::LeftBorder) {
QPixmap left(frame->leftWidth, leftHeight);
left.fill(Qt::transparent);
QPainter sidePainter(&left);
sidePainter.setCompositionMode(QPainter::CompositionMode_Source);
q->paint(&sidePainter, QRect(QPoint(0, 0), left.size()), prefix + "left");
p.drawTiledPixmap(QRect(leftOffset, contentTop, frame->leftWidth, contentHeight), left);
}
if (q->hasElement(prefix + "right") && frame->enabledBorders & FrameSvg::RightBorder) {
QPixmap right(frame->rightWidth, leftHeight);
right.fill(Qt::transparent);
QPainter sidePainter(&right);
sidePainter.setCompositionMode(QPainter::CompositionMode_Source);
q->paint(&sidePainter, QRect(QPoint(0, 0), right.size()), prefix + "right");
p.drawTiledPixmap(QRect(rightOffset, contentTop, frame->rightWidth, contentHeight), right);
}
if (q->hasElement(prefix + "top") && frame->enabledBorders & FrameSvg::TopBorder) {
QPixmap top(topWidth, frame->topHeight);
top.fill(Qt::transparent);
QPainter sidePainter(&top);
sidePainter.setCompositionMode(QPainter::CompositionMode_Source);
q->paint(&sidePainter, QRect(QPoint(0, 0), top.size()), prefix + "top");
p.drawTiledPixmap(QRect(contentLeft, topOffset, contentWidth, frame->topHeight), top);
}
if (q->hasElement(prefix + "bottom") && frame->enabledBorders & FrameSvg::BottomBorder) {
QPixmap bottom(topWidth, frame->bottomHeight);
bottom.fill(Qt::transparent);
QPainter sidePainter(&bottom);
sidePainter.setCompositionMode(QPainter::CompositionMode_Source);
q->paint(&sidePainter, QRect(QPoint(0, 0), bottom.size()), prefix + "bottom");
p.drawTiledPixmap(QRect(contentLeft, bottomOffset, contentWidth, frame->bottomHeight), bottom);
}
}
theme->insertIntoCache(id, frame->cachedBackground);
}
void FrameSvgPrivate::updateSizes()
{
//kDebug() << "!!!!!!!!!!!!!!!!!!!!!! updating sizes" << prefix;
FrameData *frame = frames[prefix];
Q_ASSERT(frame);
frame->cachedBackground = QPixmap();
frame->cachedMask = QPixmap();
if (frame->enabledBorders & FrameSvg::TopBorder) {
frame->topHeight = q->elementSize(prefix + "top").height();
if (q->hasElement(prefix + "hint-top-margin")) {
frame->topMargin = q->elementSize(prefix + "hint-top-margin").height();
} else {
frame->topMargin = frame->topHeight;
}
} else {
frame->topMargin = frame->topHeight = 0;
}
if (frame->enabledBorders & FrameSvg::LeftBorder) {
frame->leftWidth = q->elementSize(prefix + "left").width();
if (q->hasElement(prefix + "hint-left-margin")) {
frame->leftMargin = q->elementSize(prefix + "hint-left-margin").height();
} else {
frame->leftMargin = frame->leftWidth;
}
} else {
frame->leftMargin = frame->leftWidth = 0;
}
if (frame->enabledBorders & FrameSvg::RightBorder) {
frame->rightWidth = q->elementSize(prefix + "right").width();
if (q->hasElement(prefix + "hint-right-margin")) {
frame->rightMargin = q->elementSize(prefix + "hint-right-margin").height();
} else {
frame->rightMargin = frame->rightWidth;
}
} else {
frame->rightMargin = frame->rightWidth = 0;
}
if (frame->enabledBorders & FrameSvg::BottomBorder) {
frame->bottomHeight = q->elementSize(prefix + "bottom").height();
if (q->hasElement(prefix + "hint-bottom-margin")) {
frame->bottomMargin = q->elementSize(prefix + "hint-bottom-margin").height();
} else {
frame->bottomMargin = frame->bottomHeight;
}
} else {
frame->bottomMargin = frame->bottomHeight = 0;
}
//since it's rectangular, topWidth and bottomWidth must be the same
frame->tileCenter = q->hasElement("hint-tile-center");
frame->noBorderPadding = q->hasElement("hint-no-border-padding");
frame->stretchBorders = q->hasElement("hint-stretch-borders");
}
void FrameSvgPrivate::updateNeeded()
{
q->clearCache();
updateSizes();
}
void FrameSvgPrivate::updateAndSignalSizes()
{
updateSizes();
emit q->repaintNeeded();
}
} // Plasma namespace
#include "framesvg.moc"

263
framesvg.h Normal file
View File

@ -0,0 +1,263 @@
/*
* Copyright 2008 by Aaron Seigo <aseigo@kde.org>
* Copyright 2008 Marco Martin <notmart@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Library General Public License as
* published by the Free Software Foundation; either version 2, 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 Library General Public
* License along with this program; if not, write to the
* Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef PLASMA_FRAMESVG_H
#define PLASMA_FRAMESVG_H
#include <QtCore/QObject>
#include <QtGui/QPixmap>
#include <plasma/plasma_export.h>
#include <plasma/plasma.h>
#include <plasma/svg.h>
class QPainter;
class QPoint;
class QPointF;
class QRect;
class QRectF;
class QSize;
class QSizeF;
class QMatrix;
namespace Plasma
{
class FrameSvgPrivate;
/**
* @class FrameSvg plasma/framesvg.h <Plasma/FrameSvg>
*
* @short Provides an SVG with borders.
*
* When using SVG images for a background of an object that may change
* its aspect ratio, such as a dialog, simply scaling a single image
* may not be enough.
*
* FrameSvg allows SVGs to provide several elements for borders as well
* as a central element, each of which are scaled individually. These
* elements should be named
*
* - @c center - the central element, which will be scaled in both directions
* - @c top - the top border; the height is fixed, but it will be scaled
* horizontally to the same width as @c center
* - @c bottom - the bottom border; scaled in the same way as @c top
* - @c left - the left border; the width is fixed, but it will be scaled
* vertically to the same height as @c center
* - @c right - the right border; scaled in the same way as @c left
* - @c topleft - fixed size; must be the same height as @c top and the same
* width as @c left
* - @c bottomleft, @c topright, @c bottomright - similar to @c topleft
*
* @c center must exist, but all the others are optional. @c topleft and
* @c topright will be ignored if @c top does not exist, and similarly for
* @c bottomleft and @c bottomright.
*
* @see Plamsa::Svg
**/
class PLASMA_EXPORT FrameSvg : public Svg
{
Q_OBJECT
public:
/**
* These flags represents what borders should be drawn
*/
enum EnabledBorder {
NoBorder = 0,
TopBorder = 1,
BottomBorder = 2,
LeftBorder = 4,
RightBorder = 8,
AllBorders = TopBorder | BottomBorder | LeftBorder | RightBorder
};
Q_DECLARE_FLAGS(EnabledBorders, EnabledBorder)
/**
* Constructs a new FrameSvg that paints the proper named subelements
* as borders. It may also be used as a regular Plasma::Svg object
* for direct access to elements in the Svg.
*
* @arg parent options QObject to parent this to
*
* @related Plasma::Theme
*/
explicit FrameSvg(QObject *parent = 0);
~FrameSvg();
/**
* Loads a new Svg
* @arg imagePath the new file
*/
void setImagePath(const QString &path);
/**
* Sets what borders should be painted
* @arg flags borders we want to paint
*/
void setEnabledBorders(const EnabledBorders borders);
/**
* Convenience method to get the enabled borders
* @return what borders are painted
*/
EnabledBorders enabledBorders() const;
/**
* Resize the frame maintaining the same border size
* @arg size the new size of the frame
*/
void resizeFrame(const QSizeF &size);
/**
* @returns the size of the frame
*/
QSizeF frameSize() const;
/**
* Returns the margin size given the margin edge we want
* @arg edge the margin edge we want, top, bottom, left or right
* @return the margin size
*/
qreal marginSize(const Plasma::MarginEdge edge) const;
/**
* Convenience method that extracts the size of the four margins
* in the four output parameters
* @arg left left margin size
* @arg top top margin size
* @arg right right margin size
* @arg bottom bottom margin size
*/
void getMargins(qreal &left, qreal &top, qreal &right, qreal &bottom) const;
/**
* @return the rectangle of the center element, taking the margins into account.
*/
QRectF contentsRect() const;
/**
* Sets the prefix (@see setElementPrefix) to 'north', 'south', 'west' and 'east'
* when the location is TopEdge, BottomEdge, LeftEdge and RightEdge,
* respectively. Clears the prefix in other cases.
* @arg location location
*/
void setElementPrefix(Plasma::Location location);
/**
* Sets the prefix for the SVG elements to be used for painting. For example,
* if prefix is 'active', then instead of using the 'top' element of the SVG
* file to paint the top border, 'active-top' element will be used. The same
* goes for other SVG elements.
*
* If the elements with prefixes are not present, the default ones are used.
* (for the sake of speed, the test is present only for the 'center' element)
*
* Setting the prefix manually resets the location to Floating.
* If the
* @arg prefix prefix for the SVG element names
*/
void setElementPrefix(const QString & prefix);
/**
* @return true if the svg has the necessary elements with the given prefix
* to draw a frame
* @arg prefix the given prefix we want to check if drawable
*/
bool hasElementPrefix(const QString & prefix) const;
/**
* This is an overloaded method provided for convenience equivalent to
* hasElementPrefix("north"), hasElementPrefix("south")
* hasElementPrefix("west") and hasElementPrefix("east")
* @return true if the svg has the necessary elements with the given prefix
* to draw a frame.
* @arg location the given prefix we want to check if drawable
*/
bool hasElementPrefix(Plasma::Location location) const;
/**
* Returns the prefix for SVG elements of the FrameSvg
* @return the prefix
*/
QString prefix();
/**
* Returns a monochrome mask that tightly contains the fully opaque areas of the svg
* @return a monochrome bitmap of opaque areas
*/
QBitmap mask() const;
/**
* Sets whether saving all the rendered prefixes in a cache or not
* @arg cache if use the cache or not
*/
void setCacheAllRenderedFrames(bool cache);
/**
* @return if all the different prefixes should be kept in a cache when rendered
*/
bool cacheAllRenderedFrames() const;
/**
* Deletes the internal cache freeing memory: use this if you want to switch the rendered
* element and you don't plan to switch back to the previous one for a long time and you
* used setUseCache(true)
*/
void clearCache();
/**
* Returns a pixmap of the SVG represented by this object.
*
* @arg elelementId the ID string of the element to render, or an empty
* string for the whole SVG (the default)
* @return a QPixmap of the rendered SVG
*/
Q_INVOKABLE QPixmap framePixmap();
/**
* Paints the loaded SVG with the elements that represents the border
* @arg painter the QPainter to use
* @arg target the target rectangle on the paint device
* @arg source the portion rectangle of the source image
*/
Q_INVOKABLE void paintFrame(QPainter *painter, const QRectF &target,
const QRectF &source = QRectF());
/**
* Paints the loaded SVG with the elements that represents the border
* This is an overloaded member provided for convenience
* @arg painter the QPainter to use
* @arg pos where to paint the svg
*/
Q_INVOKABLE void paintFrame(QPainter *painter, const QPointF &pos = QPointF(0, 0));
private:
FrameSvgPrivate *const d;
Q_PRIVATE_SLOT(d, void updateSizes())
Q_PRIVATE_SLOT(d, void updateNeeded())
};
} // Plasma namespace
Q_DECLARE_OPERATORS_FOR_FLAGS(Plasma::FrameSvg::EnabledBorders)
#endif // multiple inclusion guard

217
glapplet.cpp Normal file
View File

@ -0,0 +1,217 @@
/*
* Copyright 2007 Zack Rusin <zack@kde.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Library General Public License as
* published by the Free Software Foundation; either version 2, 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 Library General Public
* License along with this program; if not, write to the
* Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "glapplet.h"
#include <QtOpenGL/QGLPixelBuffer>
#include <QtGui/QPainter>
#include <QtGui/QImage>
namespace Plasma {
class GLAppletPrivate
{
public:
GLAppletPrivate()
{
init();
}
~GLAppletPrivate()
{
delete pbuf;
delete dummy;
}
void init()
{
dummy = new QGLWidget((QWidget *) 0);
QGLFormat format = QGLFormat::defaultFormat();
format.setSampleBuffers(true);
format.setAlphaBufferSize(8);
//dummy size construction
pbuf = new QGLPixelBuffer(300, 300, format, dummy);
if (pbuf->isValid()) {
pbuf->makeCurrent();
}
}
void updateGlSize(const QSize &size)
{
if (size.width() > pbuf->width() ||
size.height() > pbuf->height()) {
QGLFormat format = pbuf->format();
delete pbuf;
pbuf = new QGLPixelBuffer(size, format, dummy);
}
}
public:
QGLPixelBuffer *pbuf;
QGLWidget *dummy;
};
GLApplet::GLApplet(QGraphicsItem *parent,
const QString &serviceId,
int appletId)
: Applet(parent, serviceId, appletId),
d(new GLAppletPrivate)
{
if (!d->dummy->isValid() ||
!QGLPixelBuffer::hasOpenGLPbuffers() ||
!d->pbuf->isValid()) {
setFailedToLaunch(true, i18n("This system does not support OpenGL widgets."));
}
}
GLApplet::GLApplet(QObject *parent, const QVariantList &args)
: Applet(parent, args),
d(new GLAppletPrivate)
{
if (!d->dummy->isValid() ||
!QGLPixelBuffer::hasOpenGLPbuffers() ||
!d->pbuf->isValid()) {
setFailedToLaunch(true, i18n("This system does not support OpenGL widgets."));
}
}
GLApplet::~GLApplet()
{
delete d;
}
GLuint GLApplet::bindTexture(const QImage &image, GLenum target)
{
Q_ASSERT(d->pbuf);
if (!d->dummy->isValid()) {
return 0;
}
return d->dummy->bindTexture(image, target);
}
void GLApplet::deleteTexture(GLuint textureId)
{
Q_ASSERT(d->pbuf);
d->dummy->deleteTexture(textureId);
}
void GLApplet::paintGLInterface(QPainter *painter,
const QStyleOptionGraphicsItem *option)
{
Q_UNUSED(painter)
Q_UNUSED(option)
}
static inline QPainterPath headerPath(const QRectF &r, int roundness,
int headerHeight=10)
{
QPainterPath path;
int xRnd = roundness;
int yRnd = roundness;
if (r.width() > r.height()) {
xRnd = int(roundness * r.height() / r.width());
} else {
yRnd = int(roundness * r.width() / r.height());
}
if(xRnd >= 100) { // fix ranges
xRnd = 99;
}
if(yRnd >= 100) {
yRnd = 99;
}
if(xRnd <= 0 || yRnd <= 0) { // add normal rectangle
path.addRect(r);
return path;
}
QRectF rect = r.normalized();
if (rect.isNull()) {
return path;
}
qreal x = rect.x();
qreal y = rect.y();
qreal w = rect.width();
qreal h = rect.height();
qreal rxx = w * xRnd / 200;
qreal ryy = h * yRnd / 200;
// were there overflows?
if (rxx < 0) {
rxx = w / 200 * xRnd;
}
if (ryy < 0) {
ryy = h / 200 * yRnd;
}
qreal rxx2 = 2 * rxx;
qreal ryy2 = 2 * ryy;
path.arcMoveTo(x, y, rxx2, ryy2, 90);
path.arcTo(x, y, rxx2, ryy2, 90, 90);
QPointF pt = path.currentPosition();
path.lineTo(x, pt.y() + headerHeight);
path.lineTo(x + w, pt.y() + headerHeight);
path.lineTo(x + w, pt.y());
path.arcTo(x + w - rxx2, y, rxx2, ryy2, 0, 90);
path.closeSubpath();
return path;
}
void GLApplet::paintInterface(QPainter *painter,
const QStyleOptionGraphicsItem *option,
const QRect &contentsRect)
{
Q_UNUSED(contentsRect)
Q_ASSERT(d->pbuf);
if ((!d->dummy->isValid() ||
!d->pbuf->isValid())) {
if (!hasFailedToLaunch()) {
setFailedToLaunch(true, i18n("Your machine does not support OpenGL widgets."));
}
return;
}
d->pbuf->makeCurrent();
// handle background filling
glClearColor(0, 0, 0, 0);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
QMatrix m = painter->worldMatrix();
QRect deviceRect = m.mapRect(QRect(QPoint(23, 25), boundingRect().size().toSize()));
d->updateGlSize(deviceRect.size());
// redirect this widget's painting into the pbuffer
QPainter p(d->pbuf);
paintGLInterface(&p, option);
// draw the pbuffer contents to the backingstore
QImage image = d->pbuf->toImage();
painter->drawImage(0, 0, image);
}
void GLApplet::makeCurrent()
{
if (!d->dummy->isValid() || !d->pbuf->isValid()) {
d->dummy->makeCurrent();
}
}
} // Plasma namespace
#include "glapplet.moc"

88
glapplet.h Normal file
View File

@ -0,0 +1,88 @@
/*
* Copyright 2007 Zack Rusin <zack@kde.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Library General Public License as
* published by the Free Software Foundation; either version 2, 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 Library General Public
* License along with this program; if not, write to the
* Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef PLASMA_GLAPPLET_H
#define PLASMA_GLAPPLET_H
#include <plasma/applet.h>
#include <QtOpenGL/QGLWidget>
namespace Plasma
{
class GLAppletPrivate;
/**
* @class GLApplet plasma/glapplet.h <Plasma/GLApplet>
*
* @short Plasma Applet that is fully rendered using OpengGL
*/
class PLASMA_EXPORT GLApplet : public Applet
{
Q_OBJECT
public:
/**
* @arg parent the QGraphicsItem this applet is parented to
* @arg serviceId the name of the .desktop file containing the
* information about the widget
* @arg appletId a unique id used to differentiate between multiple
* instances of the same Applet type
*/
GLApplet(QGraphicsItem *parent,
const QString &serviceId,
int appletId);
/**
* This constructor is to be used with the plugin loading systems
* found in KPluginInfo and KService. The argument list is expected
* to have two elements: the KService service ID for the desktop entry
* and an applet ID which must be a base 10 number.
*
* @arg parent a QObject parent; you probably want to pass in 0
* @arg args a list of strings containing two entries: the service id
* and the applet id
*/
GLApplet(QObject *parent, const QVariantList &args);
~GLApplet();
GLuint bindTexture(const QImage &image, GLenum target = GL_TEXTURE_2D);
void deleteTexture(GLuint texture_id);
/**
* Reimplement this method to render using OpenGL. QPainter passed
* to this method will always use OpenGL engine and rendering
* using OpenGL api directly is supported.
*/
virtual void paintGLInterface(QPainter *painter,
const QStyleOptionGraphicsItem *option);
void makeCurrent();
private:
virtual void paintInterface(QPainter *painter,
const QStyleOptionGraphicsItem *option,
const QRect &contentsRect);
private:
GLAppletPrivate *const d;
};
}
#endif

1
includes/AbstractRunner Normal file
View File

@ -0,0 +1 @@
#include "../../plasma/abstractrunner.h"

1
includes/AnimationDriver Normal file
View File

@ -0,0 +1 @@
#include "../../plasma/animationdriver.h"

1
includes/Animator Normal file
View File

@ -0,0 +1 @@
#include "../../plasma/animator.h"

2
includes/Applet Normal file
View File

@ -0,0 +1,2 @@
#include "../../plasma/applet.h"

1
includes/AppletScript Normal file
View File

@ -0,0 +1 @@
#include "../../plasma/scripting/appletscript.h"

1
includes/BusyWidget Normal file
View File

@ -0,0 +1 @@
#include "../../plasma/widgets/busywidget.h"

1
includes/CheckBox Normal file
View File

@ -0,0 +1 @@
#include "../../plasma/widgets/checkbox.h"

1
includes/ComboBox Normal file
View File

@ -0,0 +1 @@
#include "../../plasma/widgets/combobox.h"

1
includes/ConfigLoader Normal file
View File

@ -0,0 +1 @@
#include "../../plasma/configloader.h"

2
includes/Containment Normal file
View File

@ -0,0 +1,2 @@
#include "../../plasma/containment.h"

2
includes/Context Normal file
View File

@ -0,0 +1,2 @@
#include "../../plasma/context.h"

1
includes/Corona Normal file
View File

@ -0,0 +1 @@
#include "../../plasma/corona.h"

1
includes/DataContainer Normal file
View File

@ -0,0 +1 @@
#include "../../plasma/datacontainer.h"

1
includes/DataEngine Normal file
View File

@ -0,0 +1 @@
#include "../../plasma/dataengine.h"

View File

@ -0,0 +1 @@
#include "../../plasma/dataenginemanager.h"

View File

@ -0,0 +1 @@
#include "../../plasma/scripting/dataenginescript.h"

1
includes/Delegate Normal file
View File

@ -0,0 +1 @@
#include "../../plasma/delegate.h"

1
includes/Dialog Normal file
View File

@ -0,0 +1 @@
#include "../../plasma/dialog.h"

2
includes/Extender Normal file
View File

@ -0,0 +1,2 @@
#include "../../plasma/extender.h"

2
includes/ExtenderItem Normal file
View File

@ -0,0 +1,2 @@
#include "../../plasma/extenderitem.h"

1
includes/FlashingLabel Normal file
View File

@ -0,0 +1 @@
#include "../../plasma/widgets/flashinglabel.h"

1
includes/Frame Normal file
View File

@ -0,0 +1 @@
#include "../../plasma/widgets/frame.h"

1
includes/FrameSvg Normal file
View File

@ -0,0 +1 @@
#include "../../plasma/framesvg.h"

1
includes/GLApplet Normal file
View File

@ -0,0 +1 @@
#include "../../plasma/glapplet.h"

1
includes/GroupBox Normal file
View File

@ -0,0 +1 @@
#include "../../plasma/widgets/groupbox.h"

1
includes/IconWidget Normal file
View File

@ -0,0 +1 @@
#include "../../plasma/widgets/iconwidget.h"

1
includes/Label Normal file
View File

@ -0,0 +1 @@
#include "../../plasma/widgets/label.h"

1
includes/LineEdit Normal file
View File

@ -0,0 +1 @@
#include "../../plasma/widgets/lineedit.h"

1
includes/Meter Normal file
View File

@ -0,0 +1 @@
#include "../../plasma/widgets/meter.h"

1
includes/Package Normal file
View File

@ -0,0 +1 @@
#include "../../plasma/package.h"

2
includes/PackageMetadata Normal file
View File

@ -0,0 +1,2 @@
#include "../../plasma/packagemetadata.h"

View File

@ -0,0 +1 @@
#include "../../plasma/packagestructure.h"

1
includes/PaintUtils Normal file
View File

@ -0,0 +1 @@
#include "../../plasma/paintutils.h"

1
includes/Plasma Normal file
View File

@ -0,0 +1 @@
#include "../../plasma/plasma.h"

1
includes/PopupApplet Normal file
View File

@ -0,0 +1 @@
#include "../../plasma/popupapplet.h"

1
includes/PushButton Normal file
View File

@ -0,0 +1 @@
#include "../../plasma/widgets/pushbutton.h"

1
includes/QueryMatch Normal file
View File

@ -0,0 +1 @@
#include "../../plasma/querymatch.h"

1
includes/RadioButton Normal file
View File

@ -0,0 +1 @@
#include "../../plasma/widgets/radiobutton.h"

1
includes/RunnerContext Normal file
View File

@ -0,0 +1 @@
#include "../../plasma/runnercontext.h"

1
includes/RunnerManager Normal file
View File

@ -0,0 +1 @@
#include "../../plasma/runnermanager.h"

1
includes/RunnerScript Normal file
View File

@ -0,0 +1 @@
#include "../../plasma/scripting/runnerscript.h"

1
includes/ScriptEngine Normal file
View File

@ -0,0 +1 @@
#include "../../plasma/scripting/scriptengine.h"

1
includes/ScrollBar Normal file
View File

@ -0,0 +1 @@
#include "../../plasma/widgets/scrollbar.h"

1
includes/Service Normal file
View File

@ -0,0 +1 @@
#include "../../plasma/service.h"

1
includes/ServiceJob Normal file
View File

@ -0,0 +1 @@
#include "../../plasma/servicejob.h"

1
includes/SignalPlotter Normal file
View File

@ -0,0 +1 @@
#include "../../plasma/widgets/signalplotter.h"

1
includes/Slider Normal file
View File

@ -0,0 +1 @@
#include "../../plasma/widgets/slider.h"

1
includes/Svg Normal file
View File

@ -0,0 +1 @@
#include "../../plasma/svg.h"

1
includes/SvgWidget Normal file
View File

@ -0,0 +1 @@
#include "../../plasma/widgets/svgwidget.h"

1
includes/TabBar Normal file
View File

@ -0,0 +1 @@
#include "../../plasma/widgets/tabbar.h"

1
includes/TextEdit Normal file
View File

@ -0,0 +1 @@
#include "../../plasma/widgets/textedit.h"

1
includes/Theme Normal file
View File

@ -0,0 +1 @@
#include "../../plasma/theme.h"

1
includes/ToolTipManager Normal file
View File

@ -0,0 +1 @@
#include "../../plasma/tooltipmanager.h"

1
includes/TreeView Normal file
View File

@ -0,0 +1 @@
#include "../../plasma/widgets/treeview.h"

2
includes/UiLoader Normal file
View File

@ -0,0 +1,2 @@
#include "../../plasma/scripting/uiloader.h"

1
includes/Version Normal file
View File

@ -0,0 +1 @@
#include "../../plasma/version.h"

1
includes/View Normal file
View File

@ -0,0 +1 @@
#include "../../plasma/view.h"

1
includes/Wallpaper Normal file
View File

@ -0,0 +1 @@
#include "../../plasma/wallpaper.h"

1
includes/WebView Normal file
View File

@ -0,0 +1 @@
#include "../../plasma/widgets/webview.h"

447
package.cpp Normal file
View File

@ -0,0 +1,447 @@
/******************************************************************************
* Copyright 2007 by Aaron Seigo <aseigo@kde.org> *
* Copyright 2007 by Riccardo Iaconelli <riccardo@kde.org> *
* *
* This library is free software; you can redistribute it and/or *
* modify it under the terms of the GNU Library General Public *
* License as published by the Free Software Foundation; either *
* version 2 of the License, or (at your option) any later version. *
* *
* This library 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 *
* Library General Public License for more details. *
* *
* You should have received a copy of the GNU Library General Public License *
* along with this library; see the file COPYING.LIB. If not, write to *
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, *
* Boston, MA 02110-1301, USA. *
*******************************************************************************/
#include "package.h"
#include <QDir>
#include <QFile>
#include <QRegExp>
#include <KArchiveDirectory>
#include <KArchiveEntry>
#include <KComponentData>
#include <KDesktopFile>
#include <KIO/CopyJob>
#include <KIO/DeleteJob>
#include <KIO/FileCopyJob>
#include <KIO/Job>
#include <KPluginInfo>
#include <KStandardDirs>
#include <KTempDir>
#include <KTemporaryFile>
#include <KZip>
#include <KDebug>
#include "packagemetadata.h"
namespace Plasma
{
class PackagePrivate
{
public:
PackagePrivate(const PackageStructure::Ptr st, const QString &p)
: structure(st),
basePath(p),
valid(QFile::exists(basePath))
{
QFileInfo info(basePath);
if (valid && info.isDir() && basePath[basePath.length() - 1] != '/') {
basePath.append('/');
}
}
~PackagePrivate()
{
}
PackageStructure::Ptr structure;
QString basePath;
bool valid;
};
Package::Package(const QString &packageRoot, const QString &package,
PackageStructure::Ptr structure)
: d(new PackagePrivate(structure, packageRoot + '/' + package))
{
structure->setPath(d->basePath);
}
Package::Package(const QString &packagePath, PackageStructure::Ptr structure)
: d(new PackagePrivate(structure, packagePath))
{
structure->setPath(d->basePath);
}
Package::~Package()
{
delete d;
}
bool Package::isValid() const
{
if (!d->valid) {
return false;
}
foreach (const char *dir, d->structure->requiredDirectories()) {
if (!QFile::exists(d->basePath + d->structure->contentsPrefix() + d->structure->path(dir))) {
kWarning() << "Could not find required directory" << dir;
d->valid = false;
return false;
}
}
foreach (const char *file, d->structure->requiredFiles()) {
if (!QFile::exists(d->basePath + d->structure->contentsPrefix() + d->structure->path(file))) {
kWarning() << "Could not find required file" << file << ", look in"
<< d->basePath + d->structure->contentsPrefix() + d->structure->path(file) << endl;
d->valid = false;
return false;
}
}
return true;
}
QString Package::filePath(const char *fileType, const QString &filename) const
{
if (!d->valid) {
kDebug() << "package is not valid";
return QString();
}
QString path = d->structure->path(fileType);
if (path.isEmpty()) {
kDebug() << "no matching path came of it";
return QString();
}
path.prepend(d->basePath + d->structure->contentsPrefix());
if (!filename.isEmpty()) {
path.append("/").append(filename);
}
if (QFile::exists(path)) {
// ensure that we don't return files outside of our base path
// due to symlink or ../ games
QDir dir(path);
QString canonicalized = dir.canonicalPath() + QDir::separator();
if (canonicalized.startsWith(d->basePath)) {
return path;
}
}
kDebug() << path << "does not exist";
return QString();
}
QString Package::filePath(const char *fileType) const
{
return filePath(fileType, QString());
}
QStringList Package::entryList(const char *fileType) const
{
if (!d->valid) {
return QStringList();
}
QString path = d->structure->path(fileType);
if (path.isEmpty()) {
return QStringList();
}
QDir dir(d->basePath + d->structure->contentsPrefix() + path);
if (dir.exists()) {
// ensure that we don't return files outside of our base path
// due to symlink or ../ games
QString canonicalized = dir.canonicalPath();
if (canonicalized.startsWith(d->basePath)) {
return dir.entryList(QDir::Files | QDir::Readable);
}
}
return QStringList();
}
PackageMetadata Package::metadata() const
{
return d->structure->metadata();
}
const QString Package::path() const
{
return d->basePath;
}
const PackageStructure::Ptr Package::structure() const
{
return d->structure;
}
//TODO: provide a version of this that allows one to ask for certain types of packages, etc?
// should we be using KService here instead/as well?
QStringList Package::listInstalled(const QString &packageRoot) // static
{
QDir dir(packageRoot);
if (!dir.exists()) {
return QStringList();
}
QStringList packages;
foreach (const QString &sdir, dir.entryList(QDir::AllDirs | QDir::Readable)) {
QString metadata = packageRoot + '/' + sdir + "/metadata.desktop";
if (QFile::exists(metadata)) {
PackageMetadata m(metadata);
packages << m.pluginName();
}
}
return packages;
}
bool Package::installPackage(const QString &package,
const QString &packageRoot,
const QString &servicePrefix) // static
{
//TODO: report *what* failed if something does fail
QDir root(packageRoot);
if (!root.exists()) {
KStandardDirs::makeDir(packageRoot);
if (!root.exists()) {
kWarning() << "Could not create package root directory:" << packageRoot;
return false;
}
}
QFileInfo fileInfo(package);
if (!fileInfo.exists()) {
kWarning() << "No such file:" << package;
return false;
}
QString path;
KTempDir tempdir;
bool archivedPackage = false;
if (fileInfo.isDir()) {
// we have a directory, so let's just install what is in there
path = package;
// make sure we end in a slash!
if (path[path.size() - 1] != '/') {
path.append('/');
}
} else {
KZip archive(package);
if (!archive.open(QIODevice::ReadOnly)) {
kWarning() << "Could not open package file:" << package;
return false;
}
archivedPackage = true;
const KArchiveDirectory *source = archive.directory();
const KArchiveEntry *metadata = source->entry("metadata.desktop");
if (!metadata) {
kWarning() << "No metadata file in package" << package;
return false;
}
path = tempdir.name();
source->copyTo(path);
}
QString metadataPath = path + "metadata.desktop";
if (!QFile::exists(metadataPath)) {
kWarning() << "No metadata file in package" << package;
return false;
}
PackageMetadata meta(metadataPath);
QString targetName = meta.pluginName();
if (targetName.isEmpty()) {
kWarning() << "Package plugin name not specified";
return false;
}
// Ensure that package names are safe so package uninstall can't inject
// bad characters into the paths used for removal.
QRegExp validatePluginName("^[\\w-\\.]+$"); // Only allow letters, numbers, underscore and period.
if (!validatePluginName.exactMatch(targetName)) {
kWarning() << "Package plugin name " << targetName << "contains invalid characters";
return false;
}
targetName = packageRoot + '/' + targetName;
if (QFile::exists(targetName)) {
kWarning() << targetName << "already exists";
return false;
}
if (archivedPackage) {
// it's in a temp dir, so just move it over.
KIO::CopyJob *job = KIO::move(KUrl(path), KUrl(targetName), KIO::HideProgressInfo);
if (!job->exec()) {
kWarning() << "Could not move package to destination:" << targetName << " : " << job->errorString();
return false;
}
} else {
// it's a directory containing the stuff, so copy the contents rather
// than move them
KIO::CopyJob *job = KIO::copy(KUrl(path), KUrl(targetName), KIO::HideProgressInfo);
if (!job->exec()) {
kWarning() << "Could not copy package to destination:" << targetName << " : " << job->errorString();
return false;
}
}
if (archivedPackage) {
// no need to remove the temp dir (which has been successfully moved if it's an archive)
tempdir.setAutoRemove(false);
}
// and now we register it as a service =)
QString metaPath = targetName + "/metadata.desktop";
KDesktopFile df(metaPath);
KConfigGroup cg = df.desktopGroup();
// Q: should not installing it as a service disqualify it?
// Q: i don't think so since KServiceTypeTrader may not be
// used by the installing app in any case, and the
// package is properly installed - aseigo
//TODO: reduce code duplication with registerPackage below
QString serviceName = servicePrefix + meta.pluginName();
QString service = KStandardDirs::locateLocal("services", serviceName + ".desktop");
KIO::FileCopyJob *job = KIO::file_copy(metaPath, service, -1, KIO::HideProgressInfo);
if (job->exec()) {
// the icon in the installed file needs to point to the icon in the
// installation dir!
QString iconPath = targetName + '/' + cg.readEntry("Icon");
QFile icon(iconPath);
if (icon.exists()) {
KDesktopFile df(service);
KConfigGroup cg = df.desktopGroup();
cg.writeEntry("Icon", iconPath);
}
}
return true;
}
bool Package::uninstallPackage(const QString &pluginName,
const QString &packageRoot,
const QString &servicePrefix) // static
{
// We need to remove the package directory and its metadata file.
QString targetName = pluginName;
targetName = packageRoot + '/' + targetName;
if (!QFile::exists(targetName)) {
kWarning() << targetName << "does not exist";
return false;
}
QString serviceName = servicePrefix + pluginName;
QString service = KStandardDirs::locateLocal("services", serviceName + ".desktop");
kDebug() << "Removing service file " << service;
bool ok = QFile::remove(service);
if (!ok) {
kWarning() << "Unable to remove " << service;
return ok;
}
KIO::DeleteJob *job = KIO::del(KUrl(targetName));
if (!job->exec()) {
kWarning() << "Could not delete package from:" << targetName << " : " << job->errorString();
return false;
}
return true;
}
bool Package::registerPackage(const PackageMetadata &data, const QString &iconPath)
{
QString serviceName("plasma-applet-" + data.pluginName());
QString service = KStandardDirs::locateLocal("services", serviceName + ".desktop");
if (data.pluginName().isEmpty()) {
return false;
}
data.write(service);
KDesktopFile config(service);
KConfigGroup cg = config.desktopGroup();
const QString type = data.type().isEmpty() ? "Service" : data.type();
cg.writeEntry("Type", type);
const QString serviceTypes = data.serviceType().isNull() ? "Plasma/Applet,Plasma/Containment" : data.serviceType();
cg.writeEntry("X-KDE-ServiceTypes", serviceTypes);
cg.writeEntry("X-KDE-PluginInfo-EnabledByDefault", true);
QFile icon(iconPath);
if (icon.exists()) {
//FIXME: the '/' search will break on non-UNIX. do we care?
QString installedIcon("plasma_applet_" + data.pluginName() +
iconPath.right(iconPath.length() - iconPath.lastIndexOf("/")));
cg.writeEntry("Icon", installedIcon);
installedIcon = KStandardDirs::locateLocal("icon", installedIcon);
KIO::FileCopyJob *job = KIO::file_copy(iconPath, installedIcon, -1, KIO::HideProgressInfo);
job->exec();
}
return true;
}
bool Package::createPackage(const PackageMetadata &metadata,
const QString &source,
const QString &destination,
const QString &icon) // static
{
Q_UNUSED(icon)
if (!metadata.isValid()) {
kWarning() << "Metadata file is not complete";
return false;
}
// write metadata in a temporary file
KTemporaryFile metadataFile;
if (!metadataFile.open()) {
return false;
}
metadata.write(metadataFile.fileName());
// put everything into a zip archive
KZip creation(destination);
creation.setCompression(KZip::NoCompression);
if (!creation.open(QIODevice::WriteOnly)) {
return false;
}
creation.addLocalFile(metadataFile.fileName(), "metadata.desktop");
creation.addLocalDirectory(source, "contents");
creation.close();
return true;
}
} // Namespace

180
package.h Normal file
View File

@ -0,0 +1,180 @@
/******************************************************************************
* Copyright 2007 by Aaron Seigo <aseigo@kde.org> *
* Copyright 2007 by Riccardo Iaconelli <riccardo@kde.org> *
* *
* This library is free software; you can redistribute it and/or *
* modify it under the terms of the GNU Library General Public *
* License as published by the Free Software Foundation; either *
* version 2 of the License, or (at your option) any later version. *
* *
* This library 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 *
* Library General Public License for more details. *
* *
* You should have received a copy of the GNU Library General Public License *
* along with this library; see the file COPYING.LIB. If not, write to *
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, *
* Boston, MA 02110-1301, USA. *
*******************************************************************************/
#ifndef PLASMA_PACKAGE_H
#define PLASMA_PACKAGE_H
#include <QtCore/QStringList>
#include <plasma/plasma_export.h>
#include <plasma/packagestructure.h>
namespace Plasma
{
/**
* @class Package plasma/package.h <Plasma/Package>
*
* @short object representing an installed Plasmagik package
**/
class PackageMetadata;
class PackagePrivate;
class PLASMA_EXPORT Package
{
public:
/**
* Default constructor
*
* @arg packageRoot path to the package installation root
* @arg package the name of the package
* @arg structure the package structure describing this package
**/
Package(const QString &packageRoot, const QString &package,
PackageStructure::Ptr structure);
/**
* Construct a Package object.
*
* @arg packagePath full path to the package directory
* @arg structure the package structure describing this package
*/
Package(const QString &packagePath, PackageStructure::Ptr structure);
//TODO for 4.1: be able to load an uninstalled/uncompressed file.
~Package();
/**
* @return true if all the required components as defined in
* the PackageStructure exist
**/
bool isValid() const;
/**
* Get the path to a given file.
*
* @arg fileType the type of file to look for, as defined in the
* package structure
* @arg filename the name of the file
* @return path to the file on disk. QString() if not found.
**/
QString filePath(const char *fileType, const QString &filename) const;
/**
* Get the path to a given file.
*
* @arg fileType the type of file to look for, as defined in the
* package structure. The type must refer to a file
* in the package structure and not a directory.
* @return path to the file on disk. QString() if not found
**/
QString filePath(const char *fileType) const;
/**
* Get the list of files of a given type.
*
* @arg fileType the type of file to look for, as defined in the
* package structure.
* @return list of files by name, suitable for passing to filePath
**/
QStringList entryList(const char *fileType) const;
/**
* @return the package metadata object.
*/
PackageMetadata metadata() const;
/**
* @return the path to the root of this particular package
*/
const QString path() const;
/**
* @return the PackageStructure use in this Package
*/
const PackageStructure::Ptr structure() const;
/**
* Returns a list of all installed packages
*
* @param packageRoot path to the directory where Plasmagik packages
* have been installed to
* @return a list of installed Plasmagik packages
**/
static QStringList listInstalled(const QString &packageRoot);
/**
* Installs a package.
*
* @param package path to the Plasmagik package
* @param packageRoot path to the directory where the package should be
* installed to
* @return true on successful installation, false otherwise
**/
static bool installPackage(const QString &package,
const QString &packageRoot,
const QString &servicePrefix);
/**
* Uninstalls a package.
*
* @param package path to the Plasmagik package
* @param packageRoot path to the directory where the package should be
* installed to
* @return true on successful uninstallation, false otherwise
**/
static bool uninstallPackage(const QString &package,
const QString &packageRoot,
const QString &servicePrefix);
/**
* Registers a package described by the given desktop file
*
* @arg the full path to the desktop file (must be KPluginInfo compatible)
* @return true on success, false on failure
*/
static bool registerPackage(const PackageMetadata &data, const QString &iconPath);
/**
* Creates a package based on the metadata from the files contained
* in the source directory
*
* @arg metadata description of the package to create
* @arg source path to local directory containing the individual
* files to be added to the package
* @arg destination path to the package that should be created
* @arg icon path to the package icon
**/
static bool createPackage(const PackageMetadata &metadata,
const QString &source,
const QString &destination,
const QString &icon = QString());
private:
Q_DISABLE_COPY(Package)
PackagePrivate * const d;
};
} // Namespace
#endif

Some files were not shown because too many files have changed in this diff Show More