Difference between revisions of "Development/Tutorials/Plasma/JavaScript/API"

Jump to: navigation, search
m (Text replace - "<code javascript="javascript">" to "<syntaxhighlight lang="javascript">")
 
(6 intermediate revisions by 2 users not shown)
Line 3: Line 3:
 
This document provides an overview/reference of the Simplified JavaScript API for Plasmoids. The "Simplified" refers to the fact that it isn't a full binding to all of Qt or KDE's libraries, but a highly focused set of bindings designed to make writing Plasmoids fast and easy, while remaining powerful.
 
This document provides an overview/reference of the Simplified JavaScript API for Plasmoids. The "Simplified" refers to the fact that it isn't a full binding to all of Qt or KDE's libraries, but a highly focused set of bindings designed to make writing Plasmoids fast and easy, while remaining powerful.
  
The API in this documentation covers the JavaScript API as it appears in the KDE Software Compilation as of version 4.4, unless otherwise noted. This API ships as part of the KDE Base Runtime package, so can be relied on being there by any application that is Powered By KDE.
+
The API in this documentation covers the JavaScript API as it appears in the KDE Software Compilation as of version 4.4 and higher. Changes between versions are noted in the documentation, including when new API functionality was introduced. This API ships as part of the KDE Base Runtime package, so can be relied on being there by any application that is Powered By KDE.
 +
 
 +
= Examples and Tutorials =
 +
 
 +
Tutorials can be found [[Development/Tutorials/Plasma|in the Plasma tutorials area here on Techbase]] and full, functioning examples can be found in the [https://projects.kde.org/projects/kde/kdeexamples KDE Examples] repository in the {{path|plasma/javascript}} folder.
 +
 
 +
= Using QML =
 +
 
 +
Starting with the KDE Platform 4.7 release, the Plasma team recommends writing your Plasmoids with [http://qt.nokia.com/qtquick/ QtQuick] technologies with the [http://techbase.kde.org/Development/Tutorials/Plasma#QML_Plasmoids Plasma QML integration API].
 +
 
 +
The QML API uses the same API as the Simplified JavaScript API with the exception of User Interface Elements, Animations and Events which are provided instead by QML itself.
 +
 
 +
The benefits of writing your Plasmoid with QML can include faster development with more sophisticated results visually and in its usability as well as much better performance when run in a QML-only Plasma shell such as Plasma Active.
  
 
= What Is A Simplified JavaScript Plasmoid?  =
 
= What Is A Simplified JavaScript Plasmoid?  =
Line 19: Line 31:
 
The Simplified JavaScript API is powered by Qt's QtScript system which provides access to a full featured ECMA Script interpreter. If it works in ECMA Script, it will work in a Simplified JavaScript Plasmoid. As an interesting implementation note, QtScript uses the high performance ECMA Script interpreter from WebKit and shares this code with QtWebKit.  
 
The Simplified JavaScript API is powered by Qt's QtScript system which provides access to a full featured ECMA Script interpreter. If it works in ECMA Script, it will work in a Simplified JavaScript Plasmoid. As an interesting implementation note, QtScript uses the high performance ECMA Script interpreter from WebKit and shares this code with QtWebKit.  
  
On top of the ECMA Script language, QtScript provides Qt integration features. Probably the most useful one in this context is the use of signals and slots which is Qt's callback mechanism. Signals may be emitted in QtScript by calling the signal method in question, a signal can be connected to a slot by using the connect() method (and disconnected with disconnect()) and any function defined in the Plasmoid may be used as a slot. For example: <code javascript="javascript">
+
On top of the ECMA Script language, QtScript provides Qt integration features. Probably the most useful one in this context is the use of signals and slots which is Qt's callback mechanism. Signals may be emitted in QtScript by calling the signal method in question, a signal can be connected to a slot by using the connect() method (and disconnected with disconnect()) and any function defined in the Plasmoid may be used as a slot. For example: <syntaxhighlight lang="javascript">
 
function onClick()
 
function onClick()
 
{
 
{
Line 35: Line 47:
 
button.clicked.connect(onFirstClick)
 
button.clicked.connect(onFirstClick)
 
button.clicked()
 
button.clicked()
</code> This will print out:  
+
</syntaxhighlight> This will print out:  
  
<code>
+
<syntaxhighlight lang="text">
 
We got clicked!
 
We got clicked!
 
First click!
 
First click!
</code>  
+
</syntaxhighlight>  
  
 
on the console when the Plasmoid starts, and the "We got clicked!" again whenever the button is clicked by the user.
 
on the console when the Plasmoid starts, and the "We got clicked!" again whenever the button is clicked by the user.
Line 53: Line 65:
 
* [[../API-UIElements|User Interface Elements]]
 
* [[../API-UIElements|User Interface Elements]]
 
* [[../API-Animations|Animations]]
 
* [[../API-Animations|Animations]]
* [[../API-Painting|Painting|]]
+
* [[../API-Painting|Painting]]
 
+
* [[../API-DataEnginesServices|Accessing Sources of Data with DataEngines and Services]]
== Accessing Sources of Data ==
+
* [[../API-Print|Printing To Console and Debug Output]]
 
+
* [[../API-IOJobs|Asynchronous Input/Output (IO) Jobs]]
See the [[Development/Tutorials/Plasma/JavaScript/DataEngine|JavaScript Plasmoid DataEngine tutorials]]
+
* [[../API-Timers|Timers]]
 
+
* [[../API-Misc|Utility API and Objects]]
=== Global Functions to Access DataEngines and Services ===
+
* [[../API-Extensions|Extensions]]
* dataEngine(string name): returns a DataEngine object
+
** [[../API-FileDialog|File Dialog]]
* service(string dataEngineName, string sourceName): returns a ServiceObject, see also DataEngine::serviceForSource
+
** [[../API-LocalIO|Local IO]]
 
+
** [[../API-NetworkIO|Network IO]]
=== DataEngine ===
+
** [[../API-HTTP|HTTP]]
Read-only properties:
+
** [[../API-LaunchApp|Launching Applications]]
* ''Array[String]'' '''sources'''
+
* [[../API-Addons|Custom Addons (Plugins) in Javascript]]
* ''boolean'' '''valid'''
+
* ''String'' '''icon'''
+
* ''String'' '''name'''
+
 
+
Signals:
+
* '''sourceAdded(String sourceName)'''
+
* '''sourceRemoved(String sourceName)'''
+
 
+
Functions:
+
* '''serviceForSource(String sourceName)'''
+
* '''connectSource(String sourceName, Object connectTo[, number interval[, IntervalAlignment alignment] ])''': if the object passed in as the object to connect to is the plasmoid object, then the plasmoid.dataUpdated(String source, Map[String, Any] data) callback will be called if it exists
+
* '''connectAllSources(Object connectTo[, number interval[, IntervalAlignment alignment] ])''': if the object passed in as the object to connect to is the plasmoid object, then the plasmoid.dataUpdated(String source, Map[String, Any] data) callback will be called if it exists
+
* '''disconnectSource(String sourceName, Object connectedTo)''': if the object passed in as the object to connect to is the plasmoid object, then the plasmoid.dataUpdated(String source, Map[String, Any] data) callback will be called if it exists
+
* '''Data query(String sourceName)'''
+
 
+
The following functions are only of interest to DataEngine reimplementations (e.g. JavaScript DataEngines):
+
 
+
* '''scheduleSourcesUpdated()'''
+
* '''removeSource(String)'''
+
* '''updateAllSources()'''
+
* '''forceImmediateUpdateOfAllVisualizations()'''
+
* '''DataContainer containerForSource(String)'''
+
 
+
=== Service ===
+
* function finished(Plasma::ServiceJob*)
+
* function operationsChanged()
+
* function serviceReady(Plasma::Service*)
+
* function setDestination(QString)
+
* function destination()
+
* function operationNames()
+
* function operationDescription(QString)
+
* function startOperationCall(KConfigGroup,QObject*)
+
* function startOperationCall(KConfigGroup)
+
* function isOperationEnabled(QString)
+
* function name()
+
* function associateWidget(QWidget*,QString)
+
* function disassociateWidget(QWidget*)
+
* function associateWidget(QGraphicsWidget*,QString)
+
* function disassociateWidget(QGraphicsWidget*)
+
* function parametersFromDescription(KConfigGroup)
+
 
+
== Other Functions and Classes  ==
+
=== Print and Debug ===
+
* '''print(string message)''': prints message to console
+
* '''debug(string message)''': print message to console if it is running in a KDE debug build
+
 
+
=== GraphicsItem ===
+
This class represents an item on the canvas. Support is only provided so that GraphicsItem objects returned or taken by other objects work. There is no meaningful API provided directly in the JavaScript runtime for these objects and they should not need to be used directly.
+
 
+
=== QSizePolicy ===
+
The QSizePolicy class is a layout attribute describing horizontal and vertical resizing policy. This can be set on any graphics widget that you have using the enums provided for this (QtSizePolicy ), for example:
+
 
+
button = new PushButton();
+
button.sizePolicy = QSizePolicy(QSizePolicyMaximum, QSizePolicyFixed);
+
 
+
This is useful when your widgets are being laid out by a layout (specially the anchor layout).
+
 
+
* ''QtSizePolicy '' '''horizontalPolicy''': The horizontal component of the size policy.
+
* ''QtSizePolicy '' '''verticalPolicy''': The vertical component of the size policy.
+
* ''number'' '''horizontalStretch''': The horizontal stretch factor of the size policy.
+
* ''number'' '''verticalStretch''': The vertical stretch factor of the size policy.
+
 
+
=== IOJob ===
+
This object is returned by input/output access for asynchronous file and data access (see the section on Extensions for documentation on getUrl). It is used by connecting Javascript functions in your code to the relevant signals.
+
 
+
Functions:
+
* '''kill()'''
+
* '''suspend()'''
+
* '''resume()'''
+
 
+
Signals:
+
* '''data(IOJob job, ByteArray data)''': emitted whenever data arrives. If data is empty (data.length == 0) then the transmission has completed.
+
* '''dataReq(IOJob job, ByteArray data)''': when sending data, this signal is emitted when data is requested; add the data to be sent ot the data member, or leave it empty to signal that the process is complete and there is no more data to send
+
* '''finished(IOJob job)''': emitted when the transmission has completed
+
* '''suspended(IOJob job)''': emitted when the job has been suspeneded
+
* '''resumed(IOJob job)'''
+
* '''canceled(IOJob job)'''
+
* '''connected(IOJob job)'''
+
* '''redirection(IOJob job, Url to)'''
+
* '''permanentRedirection(IOJob job, Url from, Url to)'''
+
* '''mimetype(IOJob job, String mimetype)'''
+
 
+
=== QTimer ===
+
Read-write properties:
+
* ''boolean'' '''active''': true if active, false if not
+
* ''boolean'' '''singleShot''': true if the timer will fire once when started, false if it will fire repeatedly until stopped
+
* ''boolean'' '''interval''': the interval in milliseconds that the timer will trigger
+
 
+
Functions:
+
* '''start(int msec)''': starts the timer with msec as the interval
+
* '''start()''': starts the timer with the default interval
+
* '''stop()''': stops the timer
+
 
+
Signals:
+
* '''timeout()''': this signal is emitted whenever the timer interval is reached
+
 
+
=== Url ===
+
Represents a local or remote address. To create a new Url (or assign to an existing one), use the following syntax:
+
 
+
<code javascript>var url = new Url("http://kde.org")</code>
+
 
+
Read-only properties:
+
* ''string'' '''toString''': the URL as a String object
+
 
+
Read-write properties (each representing a portion of the full URL):
+
* ''string'' '''protocol'''
+
* ''string'' '''host'''
+
* ''string'' '''path'''
+
* ''string'' '''user'''
+
* ''string'' '''password'''
+
 
+
=== ByteArray ===
+
This class provides an array of bytes. This is often used by data centric objects, such as the Job classes returned by getUrl.
+
 
+
Read-only properties:
+
* ''number'' '''length''': the size of the array (number of bytes)
+
 
+
Functions:
+
* '''chop(number numBytes)''': chops numBytes from the end of the array
+
* ''bool'' '''equals(ByteArray other)'''
+
* ''ByteArray '''left(number len)''': return len bytes from the left of the array
+
* ''ByteArray '''mid(number pos, number len)''': returns an array of bytes starting a post and length len (if len is -1, it returns all remaining bytes)
+
* ''ByteArray '''remove(number pos, number len)''': removes len bytes starting at index pos from the array and returns it
+
* ''ByteArray '''right(number len)''': returns len bytes from the right of the array
+
* ''ByteArray '''simplified()''': returns a byte array that has whitespace removed from the start and the end, and which has each sequence of internal whitespace replaced with a single space
+
* ''ByteArray '''toBase64()''': returns the array encoded in base64
+
* ''ByteArray '''toLower()''': returns a lowercased copy of the array
+
* ''ByteArray '''toUpper()''': returns an uppercased copy of the array
+
* ''ByteArray '''trimmed()''': returns a copy of the array with whitespace remove from the start and end
+
* truncate(number pos): truncates the array at index pos
+
* ''String'' '''toLatin1String()''': returns a Latin1-ized string based on the array
+
* ''String'' '''toUtf8()''': (API v3) returns a string from the contents of the array using a Utf8 conversation
+
* ''String'' '''valueOf()''': returns the raw data in the array as a string
+
 
+
== Addons (API V3) ==
+
Plasmoids may also have plugins of their own, also written in Javascript, and which are shipped separately to the Plasmoid. These are referred to as "Addons" and are packaged similarly to a Plasmoid. For more information on creating Javascript Addons, visit the [[/JavascriptAddons|Javascript Addons tutorial]].
+
 
+
It is possible to list, load and be notified of new Addons having been installed for your Plasmoid.
+
 
+
* ''Array[AddonInformation]'' '''listAddons(string type)''': an array of available addons of the provided type. The type name maps to the X-KDE-PluginInfo-Category entry in the Addon's metadata.desktop file.
+
* ''boolean'' '''loadAddon(String type, String id)''': load the addon with the given id and type, return true on success. In order to be notified when the addon is successfully created, add an event listener to the "addCreated" event.
+
 
+
The following are the Addon events which are recognized by the Plasmoid along with the type of event objects (if any) that are passed to registered event listeners that are registered with addEventListener:
+
 
+
* addonCreated: Object addOn
+
* newAddonsAvaiable
+
 
+
=== AddonInformation (API V3) ===
+
 
+
The AddonInformation object contains the following read-only properties:
+
 
+
* ''String'' '''id''': the id of the Addon. Can be used with loadAddon
+
* ''String'' '''name''': a string suitable for showing the user, such as in a configuration dialog
+
 
+
== Extensions  ==
+
An API extension is a security controlled set of functions and objects that are loaded on demand. These extensions are requested by the widget by listing the required and the optional extensions (if any) it wants loaded in its metadata.desktop file. This way, even prior to the widget being loaded, Plasma can know what it will want.
+
 
+
Required extensions are requested using the X-Plasma-RequiredExtensions key, and optional extensions with the X-Plasma-OptionalExtensions. For example:
+
 
+
<code ini>
+
X-Plasma-RequiredExtensions=FileDialog,MyCustomQScriptExtension
+
X-Plasma-OptionalExtensions=LaunchApp,HTTP
+
</code>
+
 
+
The Simplified Javascript Engine then decides if the widget will actually get that extension or not. Failure to load a required extension will result in script execution being aborted.
+
 
+
Each of the built-in extensions provided are described below.
+
 
+
=== FileDialog ===
+
 
+
Provides access to open and save dialog classes: OpenFileDialog and SaveFileDialog. Both are non-modal and run asynchronously, so the signals must be used. Other than the name difference (and resulting UI variance) the API for each is identical:
+
 
+
* Constructors
+
** '''OpenFileDialog'''
+
** '''SaveFileDialog'''
+
* Properties
+
** Read Only
+
*** ''array(Url)'' '''urls''': the selected file, as a Url object
+
*** ''Url'' '''baseUrl''', the current path (minus filename) as a Url
+
*** ''string '''file''': the selected file, as a string
+
*** ''array(string)'' '''files''': selected files (plural), as an array of strings
+
** Read/Write
+
*** ''Url'' '''url''': the current Url, can be read from when the user is done or assigned before to set the starting path
+
*** ''string'' '''filter''': a string representing the mimetype filter; e.g. "*.cpp|C++ Source Files\n*.h|Header files" or "*.cpp" or "*.cpp|*h"
+
*** ''boolean'' '''localOnly''': true to show only local files, false if network locations are Ok as well
+
*** ''boolean'' '''directoriesOnly''': true to only allow selection of a directory (not a file)
+
*** ''boolean'' '''existingOnly''': true if only existing files/directories may be selected
+
* Functions
+
** '''show()''': when called, the dialog will be shown to the user
+
* Signals
+
** '''accepted(FileDialogProxy)'''': emitted when the file dialog has been successfully accepted by the user with one or more files/directories.
+
** '''finished(FileDialogProxy)''': emitted when the file dialog closes, included when cancelled/closed without being accepted
+
 
+
=== LocalIO ===
+
This extension allows access to local files.
+
 
+
Functions:
+
* ''IOJob'' '''getUrl(Url url)''': attempts to fetch the file using an IOJob
+
* ''IOJob'' '''getUrl(String url)''': attempts to fetch the file using an IOJob
+
* ''String'' '''userDataPath([String type, String path])''':  (scripting version >= 4) returns the default path for user data. Called with no parameters, it returns the user's home directory. If only one string is passed in, the standard directory for that type of data in the user's home directory will be located; the following values are recognized:
+
** documents
+
** music
+
** video
+
** downloads
+
** pictures
+
** autostart
+
** desktop (should be considered deprecated for Plasma workspaces)
+
 
+
If a second string is passed in, it is considered a request for a specific path and the following types are recognized:
+
** apps - Applications menu (.desktop files).
+
** autostart - Autostart directories (both XDG and kde-specific)
+
** cache - Cached information (e.g. favicons, web-pages)
+
** cgi - CGIs to run from kdehelp.
+
** config - Configuration files.
+
** data - Where applications store data.
+
** emoticons - Emoticons themes
+
** exe - Executables in $prefix/bin. findExe() for a function that takes $PATH into account.
+
** html - HTML documentation.
+
** icon - Icons, see KIconLoader.
+
** kcfg - KConfigXT config files.
+
** lib - Libraries.
+
** locale - Translation files for KLocale.
+
** mime - Mime types defined by KDE-specific .desktop files.
+
** module - Module (dynamically loaded library).
+
** qtplugins - Qt plugins (dynamically loaded objects for Qt)
+
** services - Services.
+
** servicetypes - Service types.
+
** sound - Application sounds.
+
** templates - Templates for the "Create new file" functionality.
+
** wallpaper - Wallpapers.
+
** tmp - Temporary files (specific for both current host and current user)
+
** socket - UNIX Sockets (specific for both current host and current user)
+
** xdgconf-menu - Freedesktop.org standard location for menu layout (.menu) files.
+
** xdgdata-apps - Freedesktop.org standard location for application desktop files.
+
** xdgdata-dirs - Freedesktop.org standard location for menu descriptions (.directory files).
+
** xdgdata-mime - Freedesktop.org standard location for MIME type definitions.
+
** xdgdata-icon - Freedesktop.org standard location for icons.
+
** xdgdata-pixmap - Gnome-compatibility location for pixmaps.
+
 
+
The second parameter should be a specific resource to find the path to. An example might be userDataPath("data", "plasma-desktop").
+
 
+
=== NetworkIO ===
+
This extensions allows access to network addresses.
+
 
+
Functions:
+
* ''IOJob'' '''getUrl(Url url)''': attempts to fetch the file using an IOJob
+
* ''IOJob'' '''getUrl(String url)''': attempts to fetch the file using an IOJob
+
 
+
=== HTTP ===
+
This extension allows access to data and files via http and https.
+
 
+
Functions:
+
* ''IOJob'' '''getUrl(Url url)''': attempts to fetch the file using an IOJob
+
* ''IOJob'' '''getUrl(String url)''': attempts to fetch the file using an IOJob
+
* ''bool'' '''openUrl([string|Url] url)''': (API v4) Opens the url in the default application (or asks the user if there is no default application for the file). The url parameter may be either a string or a Url. Returns true on success, false on failure.
+
 
+
=== LaunchApp ===
+
 
+
Adds methods to the global plasmoid object that allow launching applications, running commands and opening files and urls.
+
 
+
* ''bool'' '''runApplication(string application[, array files])''' <br>Runs an application by name (can reference an installed .desktop file as well as an executable in the user's $PATH) with an optional array of files. The file array may contain either Urls or strings. Returns true on success, false on failure.
+
* ''bool'' '''runCommand(string exe[, array args])''' <br>Runs the executable with the given arguments. Returns true on success, false on failure.
+
* ''bool'' '''openUrl([string|Url] url)''': <br>Opens the url in the default application (or asks the user if there is no default application for the file). The url parameter may be either a string or a Url. Returns true on success, false on failure.
+
* ''boolean'' '''applicationExists(String name)''': (scripting version >= 4) searches $PATH first, then tries in the application menu system by application storage name (aka the .desktop file name), then Name= entries for apps with installed .desktop files, then GenericName= entries for same
+
* ''mixed'' '''defaultApplication(String kind [, boolean storageId = false])''': (scripting version >= 4) returns the executable (or if storageId is true, then the app menu system id, e.g. its .desktop file name) of the default app. The "kind" parameter may be a well-known application type including "browser", "mailer", "filemanager", "terminal", "imClient" and "windowmanager" (or any other entry in share/apps/kcm_componentchooser/kcm_*.desktop); it may also be a mimetype (e.g. "application/pdf"). On failure, it returns false.
+
* ''String'' '''applicationPath(String name)''':  (scripting version >= 4) returns the full local path to a given application or .desktop file if it exists.
+

Latest revision as of 22:07, 29 June 2011

Contents

[edit] Introduction to the Plasmoid JavaScript API

This document provides an overview/reference of the Simplified JavaScript API for Plasmoids. The "Simplified" refers to the fact that it isn't a full binding to all of Qt or KDE's libraries, but a highly focused set of bindings designed to make writing Plasmoids fast and easy, while remaining powerful.

The API in this documentation covers the JavaScript API as it appears in the KDE Software Compilation as of version 4.4 and higher. Changes between versions are noted in the documentation, including when new API functionality was introduced. This API ships as part of the KDE Base Runtime package, so can be relied on being there by any application that is Powered By KDE.

[edit] Examples and Tutorials

Tutorials can be found in the Plasma tutorials area here on Techbase and full, functioning examples can be found in the KDE Examples repository in the plasma/javascript folder.

[edit] Using QML

Starting with the KDE Platform 4.7 release, the Plasma team recommends writing your Plasmoids with QtQuick technologies with the Plasma QML integration API.

The QML API uses the same API as the Simplified JavaScript API with the exception of User Interface Elements, Animations and Events which are provided instead by QML itself.

The benefits of writing your Plasmoid with QML can include faster development with more sophisticated results visually and in its usability as well as much better performance when run in a QML-only Plasma shell such as Plasma Active.

[edit] What Is A Simplified JavaScript Plasmoid?

This document describes the native Plasma API available to Simplified JavaScript Plasmoids. What makes them "Simplified" is that they do not have access to the entire C++ API in the Plasma, KDE and Qt libraries (let alone things lower in the API stack). This helps ensure that these Plasmoids are more likely to work properly between releases as changes in underlying API don't affect them as well as allowing Plasma to offer stronger security guarantees around them.

To denote that this Plasmoid is a Simplified JavaScript widget, ensure that in the metadata.desktop file there is this line:

X-Plasma-API=javascript

What follows is a description of the runtime environment available to a Simplified JavaScript Plasmoid.

[edit] QtScript

The Simplified JavaScript API is powered by Qt's QtScript system which provides access to a full featured ECMA Script interpreter. If it works in ECMA Script, it will work in a Simplified JavaScript Plasmoid. As an interesting implementation note, QtScript uses the high performance ECMA Script interpreter from WebKit and shares this code with QtWebKit.

On top of the ECMA Script language, QtScript provides Qt integration features. Probably the most useful one in this context is the use of signals and slots which is Qt's callback mechanism. Signals may be emitted in QtScript by calling the signal method in question, a signal can be connected to a slot by using the connect() method (and disconnected with disconnect()) and any function defined in the Plasmoid may be used as a slot. For example:
function onClick()
{
    print("We got clicked!")
}
 
function onFirstClick()
{
    print("First click!")
    button.clicked.disconnect(onFirstClick)
}
 
button = new PushButton
button.clicked.connect(onClick)
button.clicked.connect(onFirstClick)
button.clicked()
This will print out:
We got clicked!
First click!

on the console when the Plasmoid starts, and the "We got clicked!" again whenever the button is clicked by the user.

The object that emitted the signal that caused a slot to be called can be retrieved using the QObject sender read-only property of the global plasmoid object.

[edit] API


This page was last modified on 29 June 2011, at 22:07. This page has been accessed 44,146 times. Content is available under Creative Commons License SA 3.0 as well as the GNU Free Documentation License 1.2.
KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V.Legal