* [gentoo-commits] dev/dilfridge:master commit in: kde-base/plasma-workspace/files/, kde-base/plasma-workspace/
@ 2013-10-09 12:02 Andreas Hüttel
0 siblings, 0 replies; 2+ messages in thread
From: Andreas Hüttel @ 2013-10-09 12:02 UTC (permalink / raw
To: gentoo-commits
commit: bc6ae00719b0b577cc0814f167a55211fec65003
Author: Andreas K. Huettel <dilfridge <AT> gentoo <DOT> org>
AuthorDate: Wed Oct 9 12:00:44 2013 +0000
Commit: Andreas Hüttel <dilfridge <AT> gentoo <DOT> org>
CommitDate: Wed Oct 9 12:01:48 2013 +0000
URL: http://git.overlays.gentoo.org/gitweb/?p=dev/dilfridge.git;a=commit;h=bc6ae007
[kde-base/plasma-workspace] add version with old notify code, to avoid crash bug
https://bugs.kde.org/show_bug.cgi?id=311871
---
kde-base/plasma-workspace/Manifest | 1 +
kde-base/plasma-workspace/files/oldnotify.patch | 8557 ++++++++++++++++++++
.../plasma-workspace-4.10.1-noplasmalock.patch | 11 +
kde-base/plasma-workspace/metadata.xml | 9 +
.../plasma-workspace-4.11.2-r2.ebuild | 125 +
5 files changed, 8703 insertions(+)
diff --git a/kde-base/plasma-workspace/Manifest b/kde-base/plasma-workspace/Manifest
new file mode 100644
index 0000000..48cd387
--- /dev/null
+++ b/kde-base/plasma-workspace/Manifest
@@ -0,0 +1 @@
+DIST kde-workspace-4.11.2.tar.xz 13870828 SHA256 260e46f30b8faaa1ad834d9fa69465ab1f565cb18a174bd814327083bb28d917 SHA512 8d034e147ebb630941465b77e302aa2385c2f1419847e8a65ac19d445846435b61d78323fdb094b9a71089ab183c96af141db93c7c9c53a9b30c84ae69bddf2a WHIRLPOOL 0db07ef64caf873c0aa6398a28b92fdc9334eb04897c9a71ed0e3c54ad77e9e8dc23b13b6343844650a2ec7c2784664185f01cf64424d694a8c33167affb5720
diff --git a/kde-base/plasma-workspace/files/oldnotify.patch b/kde-base/plasma-workspace/files/oldnotify.patch
new file mode 100644
index 0000000..c4c5c3b
--- /dev/null
+++ b/kde-base/plasma-workspace/files/oldnotify.patch
@@ -0,0 +1,8557 @@
+diff --git a/plasma/generic/applets/notifications/CMakeLists.txt b/plasma/generic/applets/notifications/CMakeLists.txt
+index 02afb93..a38fe47 100644
+--- a/plasma/generic/applets/notifications/CMakeLists.txt
++++ b/plasma/generic/applets/notifications/CMakeLists.txt
+@@ -1,2 +1,47 @@
+-installPackage(. org.kde.notifications)
++project(plasma-notifications)
++#TODO: see if is still the case
++# 'engineName' causes error
++kde4_no_enable_final(plasma-notifications)
+
++set(notifications_SRCS
++
++ core/notificationsmanager.cpp
++ core/protocol.cpp
++ core/notification.cpp
++ core/completedjobnotification.cpp
++ core/job.cpp
++
++ protocols/notifications/dbusnotificationprotocol.cpp
++ protocols/notifications/dbusnotification.cpp
++
++ protocols/jobs/dbusjobprotocol.cpp
++ protocols/jobs/dbusjob.cpp
++
++ ui/busywidget.cpp
++ ui/notifications.cpp
++ ui/notificationwidget.cpp
++ ui/jobtotalswidget.cpp
++ ui/jobwidget.cpp
++ ui/notificationgroup.cpp
++ ui/notificationstack.cpp
++ ui/stackdialog.cpp
++ )
++
++kde4_add_ui_files(notifications_SRCS
++ ui/notificationsconfig.ui)
++
++
++include (CheckLibraryExists)
++check_library_exists (Xss XScreenSaverQueryInfo "" HAVE_LIBXSS)
++configure_file (${CMAKE_CURRENT_SOURCE_DIR}/config-notifications.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config-notifications.h)
++IF (HAVE_LIBXSS)
++ SET (IDLE_DETECTION_LIB "Xss")
++ENDIF (HAVE_LIBXSS)
++
++
++kde4_add_plugin(plasma_applet_notifications ${notifications_SRCS})
++include_directories(${CMAKE_SOURCE_DIR})
++target_link_libraries(plasma_applet_notifications ${KDE4_KDEUI_LIBS} ${KDE4_PLASMA_LIBS} ${X11_LIBRARIES} ${X11_Xrender_LIB} ${X11_Xfixes_LIB} ${X11_Xdamage_LIB} ${X11_Xcomposite_LIB} ${KDE4_SOLID_LIBS} ${IDLE_DETECTION_LIB})
++
++install(TARGETS plasma_applet_notifications DESTINATION ${PLUGIN_INSTALL_DIR})
++install(FILES plasma-applet-notifications.desktop DESTINATION ${SERVICES_INSTALL_DIR})
+diff --git a/plasma/generic/applets/notifications/Messages.sh b/plasma/generic/applets/notifications/Messages.sh
+old mode 100644
+new mode 100755
+index bd3e2d9..5582bc6
+--- a/plasma/generic/applets/notifications/Messages.sh
++++ b/plasma/generic/applets/notifications/Messages.sh
+@@ -1,5 +1,4 @@
+ #! /usr/bin/env bash
+-$EXTRACTRC `find . -name \*.rc -o -name \*.ui -o -name \*.kcfg` >> rc.cpp
+-$XGETTEXT `find . -name \*.qml` -L Java -o $podir/plasma_applet_org.kde.notifications.pot
+-$XGETTEXT rc.cpp -jo $podir/plasma_applet_org.kde.notifications.pot
++$EXTRACTRC `find . -name '*.ui'` >> rc.cpp
++$XGETTEXT `find . -name '*.cpp'` -o $podir/plasma_applet_notifications.pot
+ rm -f rc.cpp
+diff --git a/plasma/generic/applets/notifications/config-notifications.h.cmake b/plasma/generic/applets/notifications/config-notifications.h.cmake
+new file mode 100644
+index 0000000..aac3113
+--- /dev/null
++++ b/plasma/generic/applets/notifications/config-notifications.h.cmake
+@@ -0,0 +1 @@
++#cmakedefine HAVE_LIBXSS
+diff --git a/plasma/generic/applets/notifications/contents/config/main.xml b/plasma/generic/applets/notifications/contents/config/main.xml
+deleted file mode 100644
+index 399159d..0000000
+--- a/plasma/generic/applets/notifications/contents/config/main.xml
++++ /dev/null
+@@ -1,23 +0,0 @@
+-<?xml version="1.0" encoding="UTF-8"?>
+-<kcfg xmlns="http://www.kde.org/standards/kcfg/1.0"
+- xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+- xsi:schemaLocation="http://www.kde.org/standards/kcfg/1.0
+- http://www.kde.org/standards/kcfg/1.0/kcfg.xsd" >
+- <kcfgfile name=""/>
+-
+- <group name="General">
+- <entry name="CustomPosition" type="Point">
+- <label>where the notification popup will be</label>
+- <default>-1,-1</default>
+- </entry>
+- <entry name="ShowNotifications" type="Bool">
+- <label>Show the notifications</label>
+- <default>true</default>
+- </entry>
+- <entry name="ShowJobs" type="Bool">
+- <label>Show the jobs progress</label>
+- <default>true</default>
+- </entry>
+- </group>
+-
+-</kcfg>
+diff --git a/plasma/generic/applets/notifications/contents/ui/JobDelegate.qml b/plasma/generic/applets/notifications/contents/ui/JobDelegate.qml
+deleted file mode 100644
+index e94866d..0000000
+--- a/plasma/generic/applets/notifications/contents/ui/JobDelegate.qml
++++ /dev/null
+@@ -1,310 +0,0 @@
+-/*
+- * Copyright 2011 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 Library 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.
+- */
+-
+-import QtQuick 1.0
+-import org.kde.plasma.core 0.1 as PlasmaCore
+-import org.kde.plasma.components 0.1 as PlasmaComponents
+-import org.kde.plasma.graphicswidgets 0.1 as PlasmaWidgets
+-import org.kde.qtextracomponents 0.1
+-
+-PlasmaComponents.ListItem {
+- id: notificationItem
+- width: popupFlickable.width
+-
+- property int toolIconSize: theme.smallMediumIconSize
+- property int layoutSpacing: 4
+-
+- function getData(data, name, defaultValue) {
+- return data[modelData] ? (data[modelData][name] ? data[modelData][name] : defaultValue) : defaultValue;
+- }
+-
+- property string labelName0: getData(jobsSource.data, "labelName0", '')
+- property string label0: getData(jobsSource.data, "label0", '')
+- property string labelName1: getData(jobsSource.data, "labelName1", '')
+- property string label1: getData(jobsSource.data, "label1", '')
+- property string jobstate: getData(jobsSource.data, "state", '')
+- property int eta: getData(jobsSource.data, "eta", 0)
+- property string speed: getData(jobsSource.data, "speed", '')
+-
+- Column {
+- spacing: notificationItem.layoutSpacing
+- width: parent.width
+- PlasmaComponents.Label {
+- text: getData(jobsSource.data, "infoMessage", '')
+- font.bold: true
+- color: theme.textColor
+- anchors.horizontalCenter: parent.horizontalCenter
+- }
+- Grid {
+- anchors {
+- left: parent.left
+- right: parent.right
+- rightMargin: notificationItem.layoutSpacing
+- }
+- spacing: notificationItem.layoutSpacing
+- rows: 4
+- columns: 2
+-
+- PlasmaComponents.Label {
+- id: labelName0Text
+- text: labelName0 ? i18n("%1:", labelName0) : ''
+- width: Math.max(paintedWidth, labelName1Text.paintedWidth)
+- horizontalAlignment: Text.AlignRight
+- visible: labelName0 != ''
+- }
+- PlasmaComponents.Label {
+- id: label0Text
+- text: label0 ? label0 : ''
+- width: parent.width - labelName0Text.width
+- elide: Text.ElideMiddle
+- visible: label0 != ''
+-
+- PlasmaCore.ToolTip {
+- target: label0Text
+- subText: label0Text.truncated ? label0 : ""
+- }
+-
+- }
+- PlasmaComponents.Label {
+- id: labelName1Text
+- text: labelName1 ? i18n("%1:", labelName1) : ''
+- width: Math.max(paintedWidth, labelName0Text.paintedWidth)
+- horizontalAlignment: Text.AlignRight
+- visible: labelName1 != ''
+- }
+- PlasmaComponents.Label {
+- id: label1Text
+- text: label1 ? label1 : ''
+- width: parent.width - labelName0Text.width
+- elide: Text.ElideMiddle
+- visible: label1 != ''
+-
+- PlasmaCore.ToolTip {
+- target: label1Text
+- subText: label1Text.truncated ? label1 : ""
+- }
+- }
+- QIconItem {
+- icon: getData(jobsSource.data, "appIconName", '')
+- width: notificationItem.toolIconSize
+- height: width
+- anchors {
+- verticalCenter: progressItem.verticalCenter
+- right: progressItem.left
+- rightMargin: notificationItem.layoutSpacing
+- }
+- }
+- Item {
+- id: progressItem
+- width: parent.width - labelName0Text.width
+- height: childrenRect.height
+- PlasmaComponents.ProgressBar {
+- width: parent.width - pauseButton.width*2 - theme.largeIconSize - notificationItem.layoutSpacing*3
+- height: 16
+- orientation: Qt.Horizontal
+- minimumValue: 0
+- maximumValue: 100
+- //percentage doesn't always exist, so doesn't get in the model
+- value: getData(jobsSource.data, "percentage", 0)
+-
+- anchors {
+- left: parent.left
+- right: buttonsRow.left
+- verticalCenter: parent.verticalCenter
+- rightMargin: notificationItem.layoutSpacing
+- }
+- }
+- Row {
+- id: buttonsRow
+- spacing: notificationItem.layoutSpacing
+- anchors.right: parent.right
+- PlasmaComponents.ToolButton {
+- id: pauseButton
+- width: notificationItem.toolIconSize
+- height: width
+- iconSource: jobstate == "suspended" ? "media-playback-start" : "media-playback-pause"
+- flat: false
+- onClicked: {
+- var operationName = "suspend"
+- if (jobstate == "suspended") {
+- operationName = "resume"
+- }
+- var service = jobsSource.serviceForSource(modelData)
+- var operation = service.operationDescription(operationName)
+- service.startOperationCall(operation)
+- }
+- }
+- PlasmaComponents.ToolButton {
+- id: stopButton
+- width: notificationItem.toolIconSize
+- height: width
+- iconSource: "media-playback-stop"
+- flat: false
+- onClicked: {
+- var service = jobsSource.serviceForSource(modelData)
+- var operation = service.operationDescription("stop")
+- service.startOperationCall(operation)
+- }
+- }
+- }
+- }
+- PlasmaComponents.ToolButton {
+- id: expandButton
+- width: notificationItem.toolIconSize
+- height: width
+- flat: false
+- iconSource: checked ? "list-remove" : "list-add"
+- checkable: true
+- anchors {
+- right: speedLabel.left
+- rightMargin: notificationItem.layoutSpacing
+- verticalCenter: speedLabel.verticalCenter
+- }
+- }
+- PlasmaComponents.Label {
+- id: speedLabel
+- text: eta > 0 ? i18nc("Speed and estimated time to completition", "%1 (%2 remaining)", speed, locale.prettyFormatDuration(eta)) : speed
+- }
+- }
+-
+-
+- Item {
+- id: detailsItem
+- state: expandButton.checked ? "expanded" : "collapsed"
+- anchors {
+- left: parent.left
+- right: parent.right
+- leftMargin: speedLabel.x
+- }
+- property Item contentsItem
+- Component {
+- id: detailsComponent
+- Column {
+- id: detailsColumn
+- anchors {
+- left: parent.left
+- right: parent.right
+- }
+-
+- function localizeProcessedAmount(id) {
+- //if bytes localise the unit
+- if (jobsSource.data[modelData]["processedUnit"+id] == "bytes") {
+- return i18nc("How much many bytes (or whether unit in the locale has been copied over total", "%1 of %2",
+- locale.formatByteSize(jobsSource.data[modelData]["processedAmount"+id]),
+- locale.formatByteSize(jobsSource.data[modelData]["totalAmount"+id]))
+- //else print something only if is interesting data (ie more than one file/directory etc to copy
+- } else if (jobsSource.data[modelData]["totalAmount"+id] > 1) {
+- return i18n( "%1 of %2 %3",
+- jobsSource.data[modelData]["processedAmount"+id],
+- jobsSource.data[modelData]["totalAmount"+id],
+- jobsSource.data[modelData]["processedUnit"+id])
+- } else {
+- return ""
+- }
+- }
+- PlasmaComponents.Label {
+- text: jobsSource.data[modelData] ? detailsColumn.localizeProcessedAmount(0) : ""
+- anchors.left: parent.left
+- visible: text != ""
+- }
+- PlasmaComponents.Label {
+- text: jobsSource.data[modelData] ? detailsColumn.localizeProcessedAmount(1) : ""
+- anchors.left: parent.left
+- visible: text != ""
+- }
+- PlasmaComponents.Label {
+- text: jobsSource.data[modelData] ? detailsColumn.localizeProcessedAmount(2) : ""
+- anchors.left: parent.left
+- visible: text != ""
+- }
+- PlasmaWidgets.SignalPlotter {
+- id: plotter
+- width: parent.width
+- useAutoRange: true
+- showVerticalLines: false
+- unit: i18n("KiB/s")
+- height: theme.defaultFont.mSize.height * 5
+- Component.onCompleted: plotter.addPlot(theme.highlightColor)
+- }
+- Connections {
+- target: jobsSource
+- onDataChanged: {
+- plotter.addSample([jobsSource.data[modelData]["numericSpeed"]/1000])
+- }
+- }
+- }
+- }
+-
+- states: [
+- State {
+- name: "expanded"
+- PropertyChanges {
+- target: detailsItem
+- height: detailsItem.childrenRect.height
+- }
+- },
+- State {
+- name: "collapsed"
+- PropertyChanges {
+- target: detailsItem
+- height: 0
+- }
+- }
+- ]
+- transitions : [
+- Transition {
+- from: "collapsed"
+- to: "expanded"
+- SequentialAnimation {
+- ScriptAction {
+- script: {
+- detailsItem.visible = true
+- detailsItem.clip = true
+- //create the contents if they don't exist yet
+- if (!detailsItem.contentsItem) {
+- detailsItem.contentsItem = detailsComponent.createObject(detailsItem)
+- }
+- }
+- }
+- NumberAnimation {
+- duration: 250
+- properties: "height"
+- easing: PropertyAnimation.EaseInOut
+- }
+- ScriptAction {script: detailsItem.clip = false}
+- }
+- },
+- Transition {
+- from: "expanded"
+- to: "collapsed"
+- SequentialAnimation {
+- ScriptAction {script: detailsItem.clip = true}
+- NumberAnimation {
+- duration: 250
+- properties: "height"
+- easing: PropertyAnimation.EaseInOut
+- }
+- //TODO: delete the details?
+- ScriptAction {script: detailsItem.visible = false}
+- }
+- }
+- ]
+- }
+- }
+-}
+diff --git a/plasma/generic/applets/notifications/contents/ui/Jobs.qml b/plasma/generic/applets/notifications/contents/ui/Jobs.qml
+deleted file mode 100644
+index 7e2f2a2..0000000
+--- a/plasma/generic/applets/notifications/contents/ui/Jobs.qml
++++ /dev/null
+@@ -1,105 +0,0 @@
+-/*
+- * Copyright 2012 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 Library 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.
+- */
+-
+-import QtQuick 1.1
+-import org.kde.plasma.core 0.1 as PlasmaCore
+-import org.kde.plasma.components 0.1 as PlasmaComponents
+-
+-Column {
+- id: jobsRoot
+- property alias count: jobsRepeater.count
+- anchors {
+- left: parent.left
+- right: parent.right
+- }
+-
+- PlasmaCore.DataSource {
+- id: jobsSource
+- engine: "applicationjobs"
+- interval: 0
+-
+- onSourceAdded: {
+- connectSource(source);
+- }
+- property variant runningJobs
+-
+- onSourceRemoved: {
+- if (!notifications) {
+- return
+- }
+- var message = runningJobs[source]["label1"] ? runningJobs[source]["label1"] : runningJobs[source]["label0"]
+- notifications.addNotification(
+- source,
+- runningJobs[source]["appIconName"],
+- 0,
+- runningJobs[source]["appName"],
+- i18n("%1 [Finished]", runningJobs[source]["infoMessage"]),
+- message,
+- 0,
+- 0,
+- [{"id": message, "text": i18n("Open")}])
+- delete runningJobs[source]
+- }
+- Component.onCompleted: {
+- jobsSource.runningJobs = new Object
+- connectedSources = sources
+- }
+- onNewData: {
+- var jobs = runningJobs
+- jobs[sourceName] = data
+- runningJobs = jobs
+- }
+- onDataChanged: {
+- var total = 0
+- for (var i = 0; i < sources.length; ++i) {
+- if (jobsSource.data[sources[i]]["percentage"]) {
+- total += jobsSource.data[sources[i]]["percentage"]
+- }
+- }
+-
+- total /= sources.length
+- notificationsApplet.globalProgress = total/100
+- }
+- }
+-
+- Title {
+- visible: jobsRepeater.count > 0 && notifications && notifications.count > 0
+- text: i18n("Transfers")
+- }
+- PlasmaComponents.ListItem {
+- visible: jobsRepeater.count > 1
+- PlasmaComponents.ProgressBar {
+- anchors {
+- verticalCenter: parent.verticalCenter
+- left: parent.left
+- right: parent.right
+- }
+- minimumValue: 0
+- maximumValue: 100
+- value: notificationsApplet.globalProgress * 100
+- }
+- }
+- Repeater {
+- id: jobsRepeater
+- model: jobsSource.sources
+- delegate: JobDelegate {
+- toolIconSize: notificationsApplet.toolIconSize
+- }
+- }
+-}
+\ No newline at end of file
+diff --git a/plasma/generic/applets/notifications/contents/ui/LastNotificationPopup.qml b/plasma/generic/applets/notifications/contents/ui/LastNotificationPopup.qml
+deleted file mode 100644
+index 26bce0f..0000000
+--- a/plasma/generic/applets/notifications/contents/ui/LastNotificationPopup.qml
++++ /dev/null
+@@ -1,414 +0,0 @@
+-/***************************************************************************
+- * Copyright 2011 Davide Bettio <davide.bettio@kdemail.net> *
+- * Copyright 2011 Marco Martin <mart@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 of the License, or *
+- * (at your option) any later version. *
+- * *
+- * This program is distributed in the hope that it will be useful, *
+- * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+- * GNU Library 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 . *
+- ***************************************************************************/
+-
+-import QtQuick 1.1
+-import org.kde.plasma.core 0.1 as PlasmaCore
+-import org.kde.plasma.components 0.1 as PlasmaComponents
+-import org.kde.qtextracomponents 0.1
+-import org.kde.plasma.extras 0.1 as PlasmaExtras
+-
+-
+-PlasmaCore.Dialog {
+- id: lastNotificationPopup
+-
+- property variant savedPos
+- property bool customPosition: false
+-
+- onHeightChanged: setCustomPosition(lastNotificationPopup.savedPos, false)
+- onWidthChanged: setCustomPosition(lastNotificationPopup.savedPos, false)
+-
+- function popup(notification)
+- {
+- if (!lastNotificationPopup.visible) {
+- lastNotificationsModel.clear()
+- }
+- lastNotificationsModel.append(notification)
+-
+- setCustomPosition(lastNotificationPopup.savedPos, false)
+-
+- lastNotificationPopup.visible = true
+- lastNotificationTimer.interval = Math.max(4000, Math.min(60*1000, notificationsModel.get(0).expireTimeout))
+- notificationsView.currentIndex = lastNotificationsModel.count - 1
+- lastNotificationTimer.restart()
+- mainItem.buttonPressed = false
+- }
+-
+- function setCustomPosition(pos, writeConfig)
+- {
+- var popupPos = lastNotificationPopup.popupPosition(notificationIcon, Qt.AlignCenter)
+- var finalPos
+-
+- //custom
+- if ((pos.x >= 0 || pos.y >= 0) &&
+- (Math.abs(popupPos.x - pos.x) > 40 ||
+- Math.abs(popupPos.y - pos.y) > 40)) {
+- finalPos = pos
+- if (writeConfig) {
+- plasmoid.writeConfig("CustomPosition", pos)
+- lastNotificationPopup.savedPos = pos
+- lastNotificationPopup.customPosition = true
+- }
+- } else {
+- finalPos = popupPos
+- if (writeConfig) {
+- plasmoid.writeConfig("CustomPosition", QPoint(-1,-1))
+- lastNotificationPopup.savedPos = QPoint(-1,-1)
+- lastNotificationPopup.customPosition = false
+- }
+- }
+- lastNotificationPopup.x = finalPos.x
+- lastNotificationPopup.y = finalPos.y
+- }
+-
+- location: customPosition ? Floating : plasmoid.location
+- windowFlags: Qt.WindowStaysOnTopHint
+- Component.onCompleted: {
+- setAttribute(Qt.WA_X11NetWmWindowTypeDock, true)
+-
+- lastNotificationPopup.savedPos = plasmoid.readConfig("CustomPosition")
+- setCustomPosition(lastNotificationPopup.savedPos, true)
+- plasmoid.popupEvent.connect(lastNotificationPopup.popupEvent)
+- }
+-
+- function popupEvent(popupShowing)
+- {
+- if(popupShowing) {
+- lastNotificationPopup.visible = false
+- }
+- }
+-
+- mainItem: MouseEventListener {
+- id: mainItem
+- width: maximumWidth
+- height: maximumHeight
+- property int maximumWidth: theme.defaultFont.mSize.width * 35
+- property int maximumHeight: theme.defaultFont.mSize.width * 10
+- property int minimumWidth: maximumWidth
+- property int minimumHeight: maximumHeight
+-
+- property int startX: 0
+- property int startY: 0
+- property int startScreenX: 0
+- property int startScreenY: 0
+- hoverEnabled: true
+- property bool buttonPressed: false
+-
+- state: "controlsHidden"
+- onContainsMouseChanged: {
+- if (containsMouse) {
+- mainItem.state = "controlsShown"
+- lastNotificationTimer.running = false
+- } else {
+- mainItem.state = "controlsHidden"
+- lastNotificationTimer.restart()
+- }
+- }
+- onPressed: {
+- startX = mouse.x + lastNotificationPopup.margins.left
+- startY = mouse.y + lastNotificationPopup.margins.top
+- startScreenX = mouse.screenX
+- startScreenY = mouse.screenY
+- lastNotificationTimer.running = false
+- }
+- onReleased: {
+- //FIXME: bind startdragdistance
+- if ((navigationButtonsColumn.visible && mouse.x < navigationButtonsColumn.width) ||
+- buttonPressed ||
+- Math.sqrt(Math.pow(startScreenX - mouse.screenX, 2) + Math.pow(startScreenY - mouse.screenY, 2)) > 4) {
+- } else {
+- lastNotificationPopup.visible = false
+- }
+-
+- setCustomPosition(QPoint(Math.max(0, mouse.screenX - startX), Math.max(mouse.screenY - startY)), true)
+- }
+- onPositionChanged: {
+- lastNotificationPopup.x = Math.max(0, mouse.screenX - startX)
+- lastNotificationPopup.y = Math.max(0, mouse.screenY - startY)
+- }
+- onWheelMoved: {
+- if (notificationsView.moving) {
+- return
+- }
+-
+- if (wheel.delta > 0) {
+- notificationsView.currentIndex = Math.max(0, notificationsView.currentIndex-1)
+- } else {
+- notificationsView.currentIndex = Math.min(notificationsView.count-1, notificationsView.currentIndex+1)
+- }
+- }
+-
+- Timer {
+- id: lastNotificationTimer
+- interval: 4000
+- repeat: false
+- running: false
+- onTriggered: lastNotificationPopup.visible = false
+- }
+-
+- ListView {
+- id: notificationsView
+- clip: true
+- snapMode: ListView.SnapOneItem
+- orientation: ListView.Horizontal
+- anchors.fill: parent
+- model: ListModel {
+- id: lastNotificationsModel
+- }
+- interactive: false
+- delegate: Item {
+- height: notificationsView.height
+- width: notificationsView.width
+-
+- PlasmaComponents.Label {
+- id: titleLabel
+- text: model.summary
+- //font.weight: Font.Bold
+- visible: model.summary.length > 0
+- height: model.summary.length > 0 ? paintedHeight : 0
+- horizontalAlignment: Text.AlignHCenter
+- color: theme.textColor
+- elide: Text.ElideRight
+- anchors {
+- left: appIconItem.y > y + height ? parent.left : appIconItem.right
+- right: parent.right
+- rightMargin: settingsButton.visible ? settingsButton.width + closeButton.width : closeButton.width
+- top: parent.top
+- topMargin: 6
+- leftMargin: 6
+- }
+- onLinkActivated: plasmoid.openUrl(link)
+- }
+-
+- QIconItem {
+- id: appIconItem
+- icon: model.appIcon
+- width: (model.appIcon.length > 0 || imageItem.visible) ? theme.largeIconSize : 0
+- height: theme.largeIconSize
+- visible: !imageItem.visible
+- anchors {
+- left: parent.left
+- verticalCenter: parent.verticalCenter
+- leftMargin: navigationButtonsColumn.width
+- }
+- }
+- QImageItem {
+- id: imageItem
+- anchors.fill: appIconItem
+- image: model.image
+- smooth: true
+- visible: nativeWidth > 0
+- }
+- /*
+- * this extra item is for clip the overflowed body text
+- * maximumLineCount cannot control the behavior of rich text,
+- * so manual clip is required.
+- */
+- Item {
+- id: bodyLabel
+- clip: true
+- height: Math.min(parent.height - (titleLabel.height+titleLabel.y), lastNotificationText.height)
+- property bool tallText: bodyLabel.height >= (bodyLabel.parent.height - (titleLabel.height+titleLabel.y)*2)
+- anchors {
+- top: tallText ? titleLabel.bottom : undefined
+- verticalCenter: tallText ? undefined : parent.verticalCenter
+- left: appIconItem.right
+- right: actionsColumn.left
+- leftMargin: 6
+- rightMargin: 6
+- }
+- PlasmaComponents.Label {
+- id: lastNotificationText
+- text: model.body
+- width: parent.width
+- //textFormat: Text.PlainText
+- color: theme.textColor
+- wrapMode: Text.Wrap
+- elide: Text.ElideRight
+- maximumLineCount: 4
+- onLinkActivated: plasmoid.openUrl(link)
+- }
+- }
+- Column {
+- id: actionsColumn
+- spacing: 6
+- anchors {
+- right: parent.right
+- rightMargin: 6
+- verticalCenter: parent.verticalCenter
+- }
+- Repeater {
+- model: actions
+- PlasmaComponents.Button {
+- text: model.text
+- width: theme.defaultFont.mSize.width * 8
+- height: theme.defaultFont.mSize.width * 2
+- onPressedChanged: {
+- if (pressed) {
+- mainItem.buttonPressed = true
+- } else {
+- mainItem.buttonPressed = false
+- }
+- }
+- onClicked: {
+- executeAction(source, model.id)
+- actionsColumn.visible = false
+- }
+- }
+- }
+- }
+- }
+- }
+-
+- Column {
+- id: navigationButtonsColumn
+- opacity: 0
+- visible: backButton.enabled || nextButton.enabled
+- anchors {
+- left: parent.left
+- verticalCenter: parent.verticalCenter
+- }
+-
+- PlasmaComponents.ToolButton {
+- id: nextButton
+- iconSource: "go-next"
+- width: theme.smallMediumIconSize
+- height: mainItem.height/2 - 4
+- enabled: notificationsView.currentIndex < notificationsView.count-1
+- onPressedChanged: {
+- if (pressed) {
+- mainItem.buttonPressed = true
+- } else {
+- mainItem.buttonPressed = false
+- }
+- }
+- onClicked: {
+- notificationsView.currentIndex = Math.min(notificationsView.count-1, notificationsView.currentIndex+1)
+- }
+- }
+-
+- PlasmaComponents.ToolButton {
+- id: backButton
+- iconSource: "go-previous"
+- width: theme.smallMediumIconSize
+- height: mainItem.height/2 - 4
+- enabled: notificationsView.currentIndex > 0
+- onPressedChanged: {
+- if (pressed) {
+- mainItem.buttonPressed = true
+- } else {
+- mainItem.buttonPressed = false
+- }
+- }
+- onClicked: {
+- notificationsView.currentIndex = Math.max(0, notificationsView.currentIndex-1)
+- }
+- }
+- }
+- PlasmaComponents.ToolButton {
+- id: closeButton
+- opacity: 0
+- iconSource: "window-close"
+- width: theme.smallMediumIconSize
+- height: width
+- anchors {
+- right: parent.right
+- top: parent.top
+- }
+- onPressedChanged: {
+- if (pressed) {
+- mainItem.buttonPressed = true
+- } else {
+- mainItem.buttonPressed = false
+- }
+- }
+- onClicked: {
+- lastNotificationPopup.visible = false
+- lastNotificationTimer.running = false
+- closeNotification(notificationsModel.get((notificationsView.count-1)-notificationsView.currentIndex).source)
+- notificationsModel.remove((notificationsView.count-1)-notificationsView.currentIndex)
+- }
+- }
+- PlasmaComponents.ToolButton {
+- id: settingsButton
+- opacity: 0
+- iconSource: "configure"
+- width: theme.smallMediumIconSize
+- height: width
+- visible: notificationsModel.get((notificationsView.count-1)-notificationsView.currentIndex).configurable
+- anchors {
+- right: closeButton.left
+- top: parent.top
+- rightMargin: 5
+- }
+- onPressedChanged: {
+- if (pressed) {
+- mainItem.buttonPressed = true
+- } else {
+- mainItem.buttonPressed = false
+- }
+- }
+- onClicked: {
+- lastNotificationPopup.visible = false
+- configureNotification(notificationsModel.get((notificationsView.count-1)-notificationsView.currentIndex).appRealName)
+- }
+- }
+- states: [
+- State {
+- name: "controlsShown"
+- PropertyChanges {
+- target: navigationButtonsColumn
+- opacity: 1
+- }
+- PropertyChanges {
+- target: closeButton
+- opacity: 1
+- }
+- PropertyChanges {
+- target: settingsButton
+- opacity: 1
+- }
+- },
+- State {
+- name: "controlsHidden"
+- PropertyChanges {
+- target: navigationButtonsColumn
+- opacity: 0
+- }
+- PropertyChanges {
+- target: closeButton
+- opacity: 0
+- }
+- PropertyChanges {
+- target: settingsButton
+- opacity: 0
+- }
+- }
+- ]
+- transitions: [
+- Transition {
+- NumberAnimation {
+- properties: "opacity"
+- easing.type: Easing.InOutQuad
+- duration: 250
+- }
+- }
+- ]
+- }
+-}
+diff --git a/plasma/generic/applets/notifications/contents/ui/NotificationDelegate/NotificationDelegate.qml b/plasma/generic/applets/notifications/contents/ui/NotificationDelegate/NotificationDelegate.qml
+deleted file mode 100644
+index bf33eb1..0000000
+--- a/plasma/generic/applets/notifications/contents/ui/NotificationDelegate/NotificationDelegate.qml
++++ /dev/null
+@@ -1,253 +0,0 @@
+-/*
+- * Copyright 2011 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 Library 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.
+- */
+-
+-import QtQuick 1.1
+-import org.kde.plasma.core 0.1 as PlasmaCore
+-import org.kde.plasma.components 0.1 as PlasmaComponents
+-import org.kde.qtextracomponents 0.1
+-
+-PlasmaComponents.ListItem {
+- id: notificationItem
+- opacity: 1-Math.abs(x)/width
+- width: popupFlickable.width
+- property int toolIconSize: theme.smallMediumIconSize
+- property int layoutSpacing: 4
+-
+- visible: appTabBar.currentTab == allAppsTab || appTabBar.currentTab.text == appName
+-
+- Component.onCompleted: {
+- allApplicationsModel.addApplication(appIcon, appName)
+- mainScrollArea.height = mainScrollArea.implicitHeight
+- }
+- Component.onDestruction: {
+- allApplicationsModel.removeApplication(model.appName)
+- mainScrollArea.height = mainScrollArea.implicitHeight
+- }
+- Timer {
+- interval: 10*60*1000
+- repeat: false
+- running: !idleTimeSource.idle
+- onTriggered: {
+- notificationsModel.remove(index)
+- }
+- }
+-
+- MouseArea {
+- width: parent.width
+- height: childrenRect.height
+- drag {
+- target: notificationItem
+- axis: Drag.XAxis
+- //kind of an hack over Column being too smart
+- minimumX: -parent.width + 1
+- maximumX: parent.width - 1
+- }
+- onReleased: {
+- if (notificationItem.x < -notificationItem.width/2) {
+- removeAnimation.exitFromRight = false
+- removeAnimation.running = true
+- } else if (notificationItem.x > notificationItem.width/2 ) {
+- removeAnimation.exitFromRight = true
+- removeAnimation.running = true
+- } else {
+- resetAnimation.running = true
+- }
+- }
+- SequentialAnimation {
+- id: removeAnimation
+- property bool exitFromRight: true
+- NumberAnimation {
+- target: notificationItem
+- properties: "x"
+- to: removeAnimation.exitFromRight ? notificationItem.width-1 : 1-notificationItem.width
+- duration: 250
+- easing.type: Easing.InOutQuad
+- }
+- NumberAnimation {
+- target: notificationItem
+- properties: "height"
+- to: 0
+- duration: 250
+- easing.type: Easing.InOutQuad
+- }
+- ScriptAction {
+- script: notificationsModel.remove(index)
+- }
+- }
+- SequentialAnimation {
+- id: resetAnimation
+- NumberAnimation {
+- target: notificationItem
+- properties: "x"
+- to: 0
+- duration: 250
+- easing.type: Easing.InOutQuad
+- }
+- }
+- Column {
+- spacing: notificationItem.layoutSpacing
+- width: parent.width
+- Item {
+- width: parent.width
+- height: summaryLabel.height
+-
+- PlasmaComponents.Label {
+- id: summaryLabel
+- text: summary
+- height: paintedHeight
+- anchors {
+- left: parent.left
+- right: parent.right
+- leftMargin: closeButton.width
+- rightMargin: settingsButton.visible ? settingsButton.width + closeButton.width : closeButton.width
+- }
+- horizontalAlignment: Text.AlignHCenter
+- elide: Text.ElideRight
+- onLinkActivated: plasmoid.openUrl(link)
+- }
+-
+- PlasmaComponents.ToolButton {
+- id: closeButton
+- iconSource: "window-close"
+- width: notificationItem.toolIconSize
+- height: width
+- onClicked: {
+- if (notificationsModel.count > 1) {
+- removeAnimation.running = true
+- } else {
+- closeNotification(model.source)
+- notificationsModel.remove(index)
+- }
+- }
+- anchors {
+- top: parent.top
+- right: parent.right
+- }
+- }
+-
+- PlasmaComponents.ToolButton {
+- id: settingsButton
+- iconSource: "configure"
+- width: notificationItem.toolIconSize
+- height: width
+- visible: model.configurable
+- onClicked: {
+- plasmoid.hidePopup()
+- configureNotification(model.appRealName)
+- }
+- anchors {
+- top: parent.top
+- right: closeButton.left
+- rightMargin: 5
+- }
+- }
+- }
+-
+- Item {
+- height: childrenRect.height
+- width: parent.width
+- QIconItem {
+- id: appIconItem
+- icon: QIcon(appIcon)
+- width: theme.largeIconSize
+- height: theme.largeIconSize
+- visible: !imageItem.visible
+- anchors {
+- left: parent.left
+- verticalCenter: parent.verticalCenter
+- }
+- }
+- QImageItem {
+- id: imageItem
+- anchors.fill: appIconItem
+- image: model.image
+- smooth: true
+- visible: nativeWidth > 0
+- }
+- PlasmaComponents.ContextMenu {
+- id: contextMenu
+- visualParent: contextMouseArea
+- PlasmaComponents.MenuItem {
+- text: i18n("Copy")
+- onClicked: bodyText.copy()
+- }
+- PlasmaComponents.MenuItem {
+- text: i18n("Select All")
+- onClicked: bodyText.selectAll()
+- }
+- }
+- MouseArea {
+- id: contextMouseArea
+- anchors {
+- left: appIconItem.right
+- right: actionsColumn.left
+- verticalCenter: parent.verticalCenter
+- leftMargin: 6
+- rightMargin: 6
+- }
+- acceptedButtons: Qt.RightButton
+- height: bodyText.paintedHeight
+- preventStealing: true
+- onPressed: contextMenu.open(mouse.x, mouse.y)
+- TextEdit {
+- id: bodyText
+- anchors.fill: parent
+- text: body
+- color: theme.textColor
+- font.capitalization: theme.defaultFont.capitalization
+- font.family: theme.defaultFont.family
+- font.italic: theme.defaultFont.italic
+- font.letterSpacing: theme.defaultFont.letterSpacing
+- font.pointSize: theme.defaultFont.pointSize
+- font.strikeout: theme.defaultFont.strikeout
+- font.underline: theme.defaultFont.underline
+- font.weight: theme.defaultFont.weight
+- font.wordSpacing: theme.defaultFont.wordSpacing
+- selectByMouse: true
+- readOnly: true
+- wrapMode: Text.Wrap
+- textFormat: TextEdit.RichText
+- onLinkActivated: plasmoid.openUrl(link)
+- }
+- }
+- Column {
+- id: actionsColumn
+- spacing: notificationItem.layoutSpacing
+- anchors {
+- right: parent.right
+- rightMargin: 6
+- verticalCenter: parent.verticalCenter
+- }
+- Repeater {
+- model: actions
+- PlasmaComponents.Button {
+- text: model.text
+- width: theme.defaultFont.mSize.width * 8
+- height: theme.defaultFont.mSize.width * 2
+- onClicked: {
+- executeAction(source, model.id)
+- actionsColumn.visible = false
+- }
+- }
+- }
+- }
+- }
+- }
+- }
+-}
+diff --git a/plasma/generic/applets/notifications/contents/ui/NotificationDelegate/qmldir b/plasma/generic/applets/notifications/contents/ui/NotificationDelegate/qmldir
+deleted file mode 100644
+index 88fc37a..0000000
+--- a/plasma/generic/applets/notifications/contents/ui/NotificationDelegate/qmldir
++++ /dev/null
+@@ -1 +0,0 @@
+-NotificationDelegate 0.1 NotificationDelegate.qml
+diff --git a/plasma/generic/applets/notifications/contents/ui/NotificationIcon.qml b/plasma/generic/applets/notifications/contents/ui/NotificationIcon.qml
+deleted file mode 100644
+index f12cd56..0000000
+--- a/plasma/generic/applets/notifications/contents/ui/NotificationIcon.qml
++++ /dev/null
+@@ -1,188 +0,0 @@
+-/***************************************************************************
+- * Copyright 2011 Davide Bettio <davide.bettio@kdemail.net> *
+- * Copyright 2011 Marco Martin <mart@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 of the License, or *
+- * (at your option) any later version. *
+- * *
+- * This program is distributed in the hope that it will be useful, *
+- * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+- * GNU Library 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 . *
+- ***************************************************************************/
+-
+-import QtQuick 1.1
+-import org.kde.plasma.core 0.1 as PlasmaCore
+-import org.kde.plasma.components 0.1 as PlasmaComponents
+-import org.kde.qtextracomponents 0.1
+-import org.kde.plasma.extras 0.1 as PlasmaExtras
+-
+-
+-Item {
+- PlasmaCore.SvgItem {
+- id: notificationSvgItem
+- svg: notificationSvg
+- elementId: "notification-disabled"
+- anchors.centerIn: parent
+- width: Math.min(parent.width, parent.height)
+- height: width
+- state: notificationsApplet.state
+-
+- PlasmaCore.Svg {
+- id: notificationSvg
+- imagePath: "icons/notification"
+- }
+-
+- Item {
+- id: jobProgressItem
+- width: notificationSvgItem.width * globalProgress
+- clip: true
+- visible: jobs.count > 0
+- anchors {
+- left: parent.left
+- top: parent.top
+- bottom: parent.bottom
+- }
+- PlasmaCore.SvgItem {
+- svg: notificationSvg
+- elementId: "notification-progress-active"
+- anchors {
+- left: parent.left
+- top: parent.top
+- bottom: parent.bottom
+- }
+- width: notificationSvgItem.width
+- }
+- }
+- PlasmaComponents.BusyIndicator {
+- anchors.fill: parent
+- visible: jobs ? jobs.count > 0 : false
+- running: visible
+- }
+-
+- Column {
+- id: countColumn
+- visible: false
+- anchors.centerIn: parent
+- PlasmaCore.SvgItem {
+- svg: notificationSvg
+- elementId: {
+- switch (plasmoid.location) {
+- case TopEdge:
+- return "expander-top"
+- case LeftEdge:
+- return "expander-left"
+- case RightEdge:
+- return "expander-right"
+- default:
+- return "expander-bottom"
+- }
+- }
+- width: naturalSize.width
+- height: naturalSize.height
+- anchors.horizontalCenter: parent.horizontalCenter
+- }
+- PlasmaComponents.Label {
+- property int totalCount: notificationsApplet.totalCount
+- text: totalCount
+-
+- property int oldTotalCount: 0
+- font.pointSize: theme.smallestFont.pointSize
+- height: paintedHeight - 3
+- onTotalCountChanged: {
+- if (totalCount > oldTotalCount) {
+- notificationAnimation.running = true
+- }
+- oldTotalCount = totalCount
+- }
+- }
+- }
+-
+- PlasmaCore.SvgItem {
+- id: notificationAnimatedItem
+- anchors.fill: parent
+- svg: notificationSvg
+- elementId: "notification-active"
+- opacity: 0
+- scale: 2
+-
+- SequentialAnimation {
+- id: notificationAnimation
+- NumberAnimation {
+- target: notificationAnimatedItem
+- duration: 250
+- properties: "opacity, scale"
+- to: 1
+- easing.type: Easing.InOutQuad
+- }
+- PauseAnimation { duration: 500 }
+- ParallelAnimation {
+- NumberAnimation {
+- target: notificationAnimatedItem
+- duration: 250
+- properties: "opacity"
+- to: 0
+- easing.type: Easing.InOutQuad
+- }
+- NumberAnimation {
+- target: notificationAnimatedItem
+- duration: 250
+- properties: "scale"
+- to: 2
+- easing.type: Easing.InOutQuad
+- }
+- }
+- }
+- }
+- MouseArea {
+- anchors.fill: parent
+- onClicked: {
+- if (notificationsApplet.totalCount > 0) {
+- plasmoid.togglePopup()
+- } else {
+- plasmoid.hidePopup()
+- }
+- }
+- }
+- states: [
+- State {
+- name: "default"
+- PropertyChanges {
+- target: notificationSvgItem
+- elementId: "notification-disabled"
+- }
+- PropertyChanges {
+- target: countColumn
+- visible: false
+- }
+- PropertyChanges {
+- target: plasmoid
+- status: PassiveStatus
+- }
+- },
+- State {
+- name: "new-notifications"
+- PropertyChanges {
+- target: notificationSvgItem
+- elementId: jobs.count > 0 ? "notification-progress-inactive" : "notification-empty"
+- }
+- PropertyChanges {
+- target: countColumn
+- visible: true
+- }
+- PropertyChanges {
+- target: plasmoid
+- status: ActiveStatus
+- }
+- }
+- ]
+- }
+-}
+-
+diff --git a/plasma/generic/applets/notifications/contents/ui/Notifications.qml b/plasma/generic/applets/notifications/contents/ui/Notifications.qml
+deleted file mode 100644
+index 114ead2..0000000
+--- a/plasma/generic/applets/notifications/contents/ui/Notifications.qml
++++ /dev/null
+@@ -1,227 +0,0 @@
+-/*
+- * Copyright 2012 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 Library 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.
+- */
+-
+-import QtQuick 1.1
+-import org.kde.plasma.core 0.1 as PlasmaCore
+-import org.kde.plasma.components 0.1 as PlasmaComponents
+-
+-import "plasmapackage:/ui/NotificationDelegate"
+-
+-Column {
+- id: notificationsRoot
+- property alias count: notificationsRepeater.count
+- anchors {
+- left: parent.left
+- right: parent.right
+- }
+-
+- function addNotification(source, appIcon, image, appName, summary, body, expireTimeout, urgency, appRealName, configurable, actions) {
+- // Do not show duplicated notifications
+- for (var i = 0; i < notificationsModel.count; ++i) {
+- if (notificationsModel.get(i).source == source &&
+- notificationsModel.get(i).appName == appName &&
+- notificationsModel.get(i).summary == summary &&
+- notificationsModel.get(i).body == body) {
+- return
+- }
+- }
+-
+- for (var i = 0; i < notificationsModel.count; ++i) {
+- if (notificationsModel.get(i).source == source) {
+- notificationsModel.remove(i)
+- break
+- }
+- }
+- if (notificationsModel.count > 20) {
+- notificationsModel.remove(notificationsModel.count-1)
+- }
+- var notification = {"source" : source,
+- "appIcon" : appIcon,
+- "image" : image,
+- "appName" : appName,
+- "summary" : summary,
+- "body" : body,
+- "expireTimeout": expireTimeout,
+- "urgency" : urgency,
+- "configurable": configurable,
+- "appRealName": appRealName,
+- "actions" : actions}
+- notificationsModel.insert(0, notification);
+- if (plasmoid.popupShowing) {
+- return
+- }
+- if (!lastNotificationPopup) {
+- lastNotificationPopup = lastNotificationPopupComponent.createObject(notificationsRoot)
+- }
+- lastNotificationPopup.popup(notification)
+- }
+-
+- function executeAction(source, id) {
+- //try to use the service
+- if (source.indexOf("notification") !== -1) {
+- var service = notificationsSource.serviceForSource(source)
+- var op = service.operationDescription("invokeAction")
+- op["actionId"] = id
+-
+- service.startOperationCall(op)
+- //try to open the id as url
+- } else if (source.indexOf("Job") !== -1) {
+- plasmoid.openUrl(id)
+- }
+- }
+-
+- function configureNotification(appRealName) {
+- var service = notificationsSource.serviceForSource("notification")
+- var op = service.operationDescription("configureNotification")
+- op["appRealName"] = appRealName;
+- service.startOperationCall(op)
+- }
+-
+- function closeNotification(source) {
+- var service = notificationsSource.serviceForSource(source)
+- var op = service.operationDescription("userClosed")
+- service.startOperationCall(op)
+- }
+-
+- property QtObject lastNotificationPopup
+- Component {
+- id: lastNotificationPopupComponent
+- LastNotificationPopup {
+- }
+- }
+-
+- ListModel {
+- id: notificationsModel
+- }
+- ListModel {
+- id: allApplicationsModel
+- function addApplication(icon, name)
+- {
+- for (var i = 0; i < count; ++i) {
+- var item = get(i)
+- if (item.name == name) {
+- setProperty(i, "count", item.count + 1)
+- return
+- }
+- }
+- append({"icon": icon, "name": name, "count": 1})
+- }
+- function removeApplication(name)
+- {
+- for (var i = 0; i < count; ++i) {
+- var item = get(i)
+- if (item.name == name) {
+- if (item.count <= 1) {
+- remove(i)
+- appTabBar.currentTab = allAppsTab
+- return
+- }
+- setProperty(i, "count", item.count - 1)
+- return
+- }
+- }
+- }
+- }
+-
+- PlasmaCore.DataSource {
+- id: idleTimeSource
+- engine: "powermanagement"
+- interval: 30000
+- connectedSources: ["UserActivity"]
+- //Idle whith more than 5 minutes of user inactivity
+- property bool idle: data["UserActivity"]["IdleTime"] > 300000
+- }
+-
+- PlasmaCore.DataSource {
+- id: notificationsSource
+- engine: "notifications"
+- interval: 0
+-
+- onSourceAdded: {
+- connectSource(source);
+- }
+-
+- onNewData: {
+- var actions = new Array()
+- if (data["actions"] && data["actions"].length % 2 == 0) {
+- for (var i = 0; i < data["actions"].length; i += 2) {
+- var action = new Object()
+- action["id"] = data["actions"][i]
+- action["text"] = data["actions"][i+1]
+- actions.push(action)
+- }
+- }
+- notificationsRoot.addNotification(
+- sourceName,
+- data["appIcon"],
+- data["image"],
+- data["appName"],
+- data["summary"],
+- data["body"],
+- data["expireTimeout"],
+- data["urgency"],
+- data["appRealName"],
+- data["configurable"],
+- actions)
+- }
+-
+- }
+-
+- Title {
+- visible: notificationsRepeater.count > 1 || (jobs && jobs.count > 0 && notificationsRepeater.count > 0)
+- text: i18n("Notifications")
+- PlasmaComponents.ToolButton {
+- iconSource: "window-close"
+- width: notificationsApplet.toolIconSize
+- height: width
+- anchors {
+- right: parent.right
+- verticalCenter: parent.verticalCenter
+- }
+- onClicked: notificationsModel.clear()
+- }
+- }
+- PlasmaComponents.ListItem {
+- visible: allApplicationsModel.count > 1
+- PlasmaComponents.TabBar {
+- id: appTabBar
+- anchors.horizontalCenter: parent.horizontalCenter
+- width: Math.min(implicitWidth, parent.width-8)
+- PlasmaComponents.TabButton {
+- id: allAppsTab
+- text: i18n("All")
+- iconSource: "dialog-information"
+- }
+- Repeater {
+- model: allApplicationsModel
+- PlasmaComponents.TabButton {
+- text: name
+- iconSource: icon
+- }
+- }
+- }
+- }
+- Repeater {
+- id: notificationsRepeater
+- model: notificationsModel
+- delegate: NotificationDelegate {
+- toolIconSize: notificationsApplet.toolIconSize
+- }
+- }
+-}
+diff --git a/plasma/generic/applets/notifications/contents/ui/Title.qml b/plasma/generic/applets/notifications/contents/ui/Title.qml
+deleted file mode 100644
+index dff4f72..0000000
+--- a/plasma/generic/applets/notifications/contents/ui/Title.qml
++++ /dev/null
+@@ -1,43 +0,0 @@
+-/*
+- * Copyright 2012 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 Library 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.
+- */
+-
+-import QtQuick 1.1
+-import org.kde.plasma.core 0.1 as PlasmaCore
+-import org.kde.plasma.components 0.1 as PlasmaComponents
+-
+-PlasmaComponents.ListItem {
+- id: root
+- property alias text: titleLabel.text
+-
+-
+- sectionDelegate: true
+-
+- width: parent.width
+-
+- PlasmaComponents.Label {
+- id: titleLabel
+- horizontalAlignment: Text.AlignHCenter
+- elide: Text.ElideRight
+- anchors {
+- verticalCenter: parent.verticalCenter
+- left: parent.left
+- right: parent.right
+- }
+- }
+-}
+diff --git a/plasma/generic/applets/notifications/contents/ui/config.ui b/plasma/generic/applets/notifications/contents/ui/config.ui
+deleted file mode 100644
+index 7769bba..0000000
+--- a/plasma/generic/applets/notifications/contents/ui/config.ui
++++ /dev/null
+@@ -1,69 +0,0 @@
+-<?xml version="1.0" encoding="UTF-8"?>
+-<ui version="4.0">
+- <class>feedsConfig</class>
+- <widget class="QWidget" name="feedsConfig">
+- <property name="geometry">
+- <rect>
+- <x>0</x>
+- <y>0</y>
+- <width>337</width>
+- <height>181</height>
+- </rect>
+- </property>
+- <layout class="QFormLayout" name="formLayout">
+- <item row="1" column="0">
+- <widget class="QLabel" name="label_2">
+- <property name="text">
+- <string>&Application notifications:</string>
+- </property>
+- <property name="buddy">
+- <cstring>kcfg_ShowNotifications</cstring>
+- </property>
+- </widget>
+- </item>
+- <item row="1" column="1">
+- <widget class="QCheckBox" name="kcfg_ShowNotifications">
+- <property name="text">
+- <string/>
+- </property>
+- </widget>
+- </item>
+- <item row="2" column="0">
+- <widget class="QLabel" name="label_3">
+- <property name="text">
+- <string>&File transfers and jobs:</string>
+- </property>
+- <property name="buddy">
+- <cstring>kcfg_ShowJobs</cstring>
+- </property>
+- </widget>
+- </item>
+- <item row="2" column="1">
+- <widget class="QCheckBox" name="kcfg_ShowJobs">
+- <property name="text">
+- <string/>
+- </property>
+- </widget>
+- </item>
+- <item row="0" column="0" colspan="2">
+- <widget class="QLabel" name="label">
+- <property name="font">
+- <font>
+- <weight>75</weight>
+- <bold>true</bold>
+- </font>
+- </property>
+- <property name="text">
+- <string>Choose which information to show</string>
+- </property>
+- </widget>
+- </item>
+- </layout>
+- </widget>
+- <tabstops>
+- <tabstop>kcfg_ShowNotifications</tabstop>
+- <tabstop>kcfg_ShowJobs</tabstop>
+- </tabstops>
+- <resources/>
+- <connections/>
+-</ui>
+diff --git a/plasma/generic/applets/notifications/contents/ui/main.qml b/plasma/generic/applets/notifications/contents/ui/main.qml
+deleted file mode 100644
+index 4d4b0f7..0000000
+--- a/plasma/generic/applets/notifications/contents/ui/main.qml
++++ /dev/null
+@@ -1,171 +0,0 @@
+-/***************************************************************************
+- * Copyright 2011 Davide Bettio <davide.bettio@kdemail.net> *
+- * Copyright 2011 Marco Martin <mart@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 of the License, or *
+- * (at your option) any later version. *
+- * *
+- * This program is distributed in the hope that it will be useful, *
+- * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+- * GNU Library 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 . *
+- ***************************************************************************/
+-
+-import QtQuick 1.1
+-import org.kde.plasma.core 0.1 as PlasmaCore
+-import org.kde.plasma.components 0.1 as PlasmaComponents
+-import org.kde.qtextracomponents 0.1
+-import org.kde.plasma.extras 0.1 as PlasmaExtras
+-import org.kde.locale 0.1 as KLocale
+-
+-import "plasmapackage:/ui/uiproperties.js" as UiProperties
+-
+-
+-MouseEventListener {
+- id: notificationsApplet
+- state: "default"
+- width: 32
+- height: 32
+- property int minimumWidth: mainScrollArea.implicitWidth
+- property int minimumHeight: mainScrollArea.implicitHeight
+- property int maximumWidth: -1
+- property int maximumHeight: mainScrollArea.implicitHeight
+-
+- property int toolIconSize: UiProperties.toolIconSize
+- property int layoutSpacing: UiProperties.layoutSpacing
+-
+- property real globalProgress: 0
+-
+- property bool showNotifications: false
+- property bool showJobs: false
+-
+- property Item notifications: notificationsLoader.item
+- property Item jobs: jobsLoader.item
+-
+- //notifications + jobs
+- property int totalCount: (notifications ? notifications.count : 0) + (jobs ? jobs.count : 0)
+- onTotalCountChanged: {
+- if (totalCount > 0) {
+- state = "new-notifications"
+- } else {
+- state = "default"
+- plasmoid.hidePopup()
+- }
+-
+- var data = new Object
+- data["image"] = "preferences-desktop-notification"
+- data["mainText"] = i18n("Notifications and Jobs")
+- if (totalCount == 0) {
+- data["subText"] = i18n("No notifications or jobs")
+- } else if (!notifications.count) {
+- data["subText"] = i18np("%1 running job", "%1 running jobs", jobs.count)
+- } else if (!jobs.count) {
+- data["subText"] = i18np("%1 notification", "%1 notifications", notifications.count)
+- } else {
+- data["subText"] = i18np("%1 running job", "%1 running jobs", jobs.count) + "<br/>" + i18np("%1 notification", "%1 notifications", notifications.count)
+- }
+- plasmoid.popupIconToolTip = data
+- plasmoid.passivePopup = jobs.count != 0
+- }
+-
+- property Item notificationIcon
+-
+- Component.onCompleted: {
+- //plasmoid.popupIcon = QIcon("preferences-desktop-notification")
+- plasmoid.aspectRatioMode = "ConstrainedSquare"
+- plasmoid.status = PassiveStatus
+- allApplications = new Object
+- plasmoid.addEventListener('ConfigChanged', configChanged);
+- configChanged()
+- }
+-
+- function configChanged()
+- {
+- showNotifications = plasmoid.readConfig("ShowNotifications")
+- showJobs = plasmoid.readConfig("ShowJobs")
+- }
+-
+- KLocale.Locale {
+- id: locale
+- }
+-
+- PlasmaCore.Svg {
+- id: configIconsSvg
+- imagePath: "widgets/configuration-icons"
+- }
+-
+- property Component compactRepresentation: Component {
+- NotificationIcon {
+- id: notificationIcon
+- Component.onCompleted: notificationsApplet.notificationIcon = notificationIcon
+- }
+- }
+-
+- hoverEnabled: !UiProperties.touchInput
+-
+- PlasmaExtras.ScrollArea {
+- id: mainScrollArea
+- anchors.fill: parent
+- implicitWidth: theme.defaultFont.mSize.width * 40
+- implicitHeight: Math.min(theme.defaultFont.mSize.height * 40, Math.max(theme.defaultFont.mSize.height * 6, contentsColumn.height))
+- state: ""
+-
+- states: [
+- State {
+- name: "underMouse"
+- when: notificationsApplet.containsMouse
+- PropertyChanges {
+- target: mainScrollArea
+- implicitHeight: implicitHeight
+- }
+- },
+- State {
+- name: ""
+- when: !notificationsApplet.containsMouse
+- PropertyChanges {
+- target: mainScrollArea
+- implicitHeight: Math.min(theme.defaultFont.mSize.height * 40, Math.max(theme.defaultFont.mSize.height * 6, contentsColumn.height))
+- }
+- }
+- ]
+-
+- Flickable {
+- id: popupFlickable
+- anchors.fill:parent
+-
+- contentWidth: width
+- contentHeight: contentsColumn.height
+- clip: true
+-
+- Column {
+- id: contentsColumn
+- width: popupFlickable.width
+-
+- //TODO: load those on demand based on configuration
+- Loader {
+- id: jobsLoader
+- source: showJobs ? "Jobs.qml" : ""
+- anchors {
+- left: parent.left
+- right: parent.right
+- }
+- }
+- Loader {
+- id: notificationsLoader
+- source: showNotifications ? "Notifications.qml" : ""
+- anchors {
+- left: parent.left
+- right: parent.right
+- }
+- }
+- }
+- }
+- }
+-}
+diff --git a/plasma/generic/applets/notifications/contents/ui/uiproperties.js b/plasma/generic/applets/notifications/contents/ui/uiproperties.js
+deleted file mode 100644
+index efad371..0000000
+--- a/plasma/generic/applets/notifications/contents/ui/uiproperties.js
++++ /dev/null
+@@ -1,23 +0,0 @@
+-/*
+- * Copyright 2012 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 Library 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.
+- */
+-
+-
+-var toolIconSize = theme.smallMediumIconSize
+-var layoutSpacing = 4
+-var touchInput = false
+diff --git a/plasma/generic/applets/notifications/core/completedjobnotification.cpp b/plasma/generic/applets/notifications/core/completedjobnotification.cpp
+new file mode 100644
+index 0000000..9a08390
+--- /dev/null
++++ b/plasma/generic/applets/notifications/core/completedjobnotification.cpp
+@@ -0,0 +1,99 @@
++/***************************************************************************
++ * completedjobnotification.h *
++ * Copyright (C) 2010 Marco Martin <notmart@gmail.com> *
++ * *
++ * This program is free software; you can redistribute it and/or modify *
++ * it under the terms of the GNU General Public License as published by *
++ * the Free Software Foundation; either version 2 of the License, or *
++ * (at your option) any later version. *
++ * *
++ * This program is distributed in the hope that it will be useful, *
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
++ * GNU General Public License for more details. *
++ * *
++ * You should have received a copy of the GNU General Public License *
++ * along with this program; if not, write to the *
++ * Free Software Foundation, Inc., *
++ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA . *
++ ***************************************************************************/
++
++#include "completedjobnotification.h"
++#include "job.h"
++
++#include <QProcess>
++
++#include <KIcon>
++#include <KLocale>
++#include <KDebug>
++
++
++static const int completedJobExpireDelay = 60 * 1000;
++static const int completedShortJobExpireDelay = 8 * 1000;
++static const uint shortJobsLength = 30 * 1000;
++
++CompletedJobNotification::CompletedJobNotification(QObject *parent)
++ : Notification(parent)
++{
++}
++
++CompletedJobNotification::~CompletedJobNotification()
++{
++}
++
++void CompletedJobNotification::setJob(Job *job)
++{
++ setApplicationName(job->applicationName());
++ setApplicationIcon(KIcon(job->applicationIconName()));
++ setSummary(i18n("%1 [Finished]", job->message()));
++
++ if (job->error().isEmpty()) {
++ setMessage(job->completedMessage());
++ } else {
++ setMessage(job->error());
++ }
++
++ if (job->elapsed() < shortJobsLength) {
++ setTimeout(completedShortJobExpireDelay);
++ } else {
++ setTimeout(completedJobExpireDelay);
++ }
++
++ if (job->destination().isValid()) {
++ QHash<QString, QString> actions;
++ actions.insert("open", i18n("Open"));
++ setActions(actions);
++ setActionOrder(QStringList()<<"open");
++
++ // create location url as is done in job->completedMessage()
++ KUrl location(job->destination());
++ if (job->totalAmounts().value("files") > 1) {
++ location.setFileName(QString());
++ }
++
++ m_destinationPrettyUrl = location.prettyUrl();
++ }
++
++ m_job = job;
++}
++
++void CompletedJobNotification::linkActivated(const QString &url)
++{
++ kDebug() << "open " << url;
++ QProcess::startDetached("kde-open", QStringList() << url);
++}
++
++Job *CompletedJobNotification::job() const
++{
++ return m_job;
++}
++
++void CompletedJobNotification::triggerAction(const QString &actionId)
++{
++ if (actionId == "open" && !m_destinationPrettyUrl.isNull()) {
++ linkActivated(m_destinationPrettyUrl);
++ }
++}
++
++
++#include "completedjobnotification.moc"
+diff --git a/plasma/generic/applets/notifications/core/completedjobnotification.h b/plasma/generic/applets/notifications/core/completedjobnotification.h
+new file mode 100644
+index 0000000..b5e1f15
+--- /dev/null
++++ b/plasma/generic/applets/notifications/core/completedjobnotification.h
+@@ -0,0 +1,51 @@
++/***************************************************************************
++ * completedjobnotification.h *
++ * Copyright (C) 2010 Marco Martin <notmart@gmail.com> *
++ * *
++ * This program is free software; you can redistribute it and/or modify *
++ * it under the terms of the GNU General Public License as published by *
++ * the Free Software Foundation; either version 2 of the License, or *
++ * (at your option) any later version. *
++ * *
++ * This program is distributed in the hope that it will be useful, *
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
++ * GNU General Public License for more details. *
++ * *
++ * You should have received a copy of the GNU General Public License *
++ * along with this program; if not, write to the *
++ * Free Software Foundation, Inc., *
++ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA . *
++ ***************************************************************************/
++
++#ifndef COMPLETEDJOBNOTIFICATION_H
++#define COMPLETEDJOBNOTIFICATION_H
++
++#include "notification.h"
++
++
++class Job;
++
++class CompletedJobNotification : public Notification
++{
++ Q_OBJECT
++
++public:
++ CompletedJobNotification(QObject *parent = 0);
++ virtual ~CompletedJobNotification();
++
++ void setJob(Job *job);
++ Job *job() const;
++
++public Q_SLOTS:
++ void linkActivated(const QString &link);
++ void triggerAction(const QString &actionId);
++
++private:
++ Job *m_job;
++ QString m_destinationPrettyUrl;
++};
++
++
++#endif
++
+diff --git a/plasma/generic/applets/notifications/core/job.cpp b/plasma/generic/applets/notifications/core/job.cpp
+new file mode 100644
+index 0000000..77ea03f
+--- /dev/null
++++ b/plasma/generic/applets/notifications/core/job.cpp
+@@ -0,0 +1,360 @@
++/***************************************************************************
++ * Copyright (C) 2008 Rob Scheepmaker <r.scheepmaker@student.utwente.nl> *
++ * *
++ * This program is free software; you can redistribute it and/or modify *
++ * it under the terms of the GNU General Public License as published by *
++ * the Free Software Foundation; either version 2 of the License, or *
++ * (at your option) any later version. *
++ * *
++ * This program is distributed in the hope that it will be useful, *
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
++ * GNU General Public License for more details. *
++ * *
++ * You should have received a copy of the GNU General Public License *
++ * along with this program; if not, write to the *
++ * Free Software Foundation, Inc., *
++ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA . *
++ ***************************************************************************/
++
++#include "job.h"
++
++#include <QtCore/QTimer>
++#include <QtCore/QTime>
++#include <QTextDocument>
++#include <QFontMetrics>
++#include <QApplication>
++
++#include <KDebug>
++#include <KUrl>
++#include <KLocalizedString>
++
++
++class Job::Private
++{
++public:
++ Private() :
++ numericSpeed(0),
++ finalElapsed(0),
++ state(Running),
++ percentage(0),
++ eta(0),
++ timerId(0),
++ killable(false),
++ suspendable(false),
++ shown(false)
++ {
++ }
++
++ QString applicationName;
++ QString applicationIconName;
++ QString message;
++ QString error;
++ QString speed;
++ QString destination;
++ qlonglong numericSpeed;
++
++
++ QMap<QString, qlonglong> totalAmounts;
++ QMap<QString, qlonglong> processedAmounts;
++
++ QList<QPair<QString, QString> > labels;
++
++ QTime elapsed;
++ uint finalElapsed;
++
++ State state;
++ uint percentage;
++ uint eta;
++ int timerId;
++
++ bool killable : 1;
++ bool suspendable : 1;
++ bool shown : 1;
++};
++
++Job::Job(QObject *parent)
++ : QObject(parent),
++ d(new Private)
++{
++ //delay a little the job to avoid the user to be distracted with short ones
++ QTimer::singleShot(1500, this, SLOT(show()));
++ d->elapsed.restart();
++}
++
++Job::~Job()
++{
++ delete d;
++}
++
++void Job::destroy()
++{
++ emit destroyed(this);
++ deleteLater();
++}
++
++QString Job::applicationName() const
++{
++ return d->applicationName;
++}
++
++void Job::setApplicationName(const QString &applicationName)
++{
++ if (d->applicationName != applicationName) {
++ d->applicationName = applicationName;
++ scheduleChangedSignal();
++ }
++}
++
++QString Job::applicationIconName() const
++{
++ return d->applicationIconName;
++}
++
++void Job::setApplicationIconName(const QString &applicationIcon)
++{
++ if (d->applicationIconName != applicationIcon) {
++ d->applicationIconName = applicationIcon;
++ scheduleChangedSignal();
++ }
++}
++
++QString Job::message() const
++{
++ return d->message;
++}
++
++void Job::setMessage(const QString &message)
++{
++ if (d->message != message) {
++ d->message = message;
++ scheduleChangedSignal();
++ }
++}
++
++QString Job::error() const
++{
++ return d->error;
++}
++
++void Job::setError(const QString &error)
++{
++ if (d->error != error) {
++ d->error = error;
++ scheduleChangedSignal();
++ }
++}
++
++QString Job::speed() const
++{
++ return d->speed;
++}
++
++void Job::setSpeed(const QString &speed)
++{
++ if (d->speed != speed) {
++ d->speed = speed;
++ scheduleChangedSignal();
++ }
++}
++
++qlonglong Job::numericSpeed() const
++{
++ return d->numericSpeed;
++}
++
++void Job::setNumericSpeed(const qlonglong speed)
++{
++ if (d->numericSpeed != speed) {
++ d->numericSpeed = speed;
++ scheduleChangedSignal();
++ }
++}
++
++QString Job::completedMessage() const
++{
++ KUrl location(d->destination);
++ if (location.isValid()) {
++ if (totalAmounts().value("files") > 1) {
++ location.setFileName(QString());
++ }
++
++ QString destinationString;
++ if (location.isLocalFile()) {
++ destinationString = location.toLocalFile();
++ } else {
++ destinationString = location.prettyUrl();
++ }
++
++ //FIXME: this is visualization stuff, but putting html here is not so model as well
++
++ kDebug() << "href = " << location.url();
++ QString destinationLink = QString("<a href=\"%1\">%2</a>").arg(location.url())
++ .arg(Qt::escape(destinationString));
++
++ if (totalAmounts().value("files") > 1) {
++ return i18np("%1 file, to: %2", "%1 files, to: %2", totalAmounts().value("files"),
++ destinationLink);
++ } else {
++ return destinationLink;
++ }
++ } else {
++ return QString("%1: %2").arg(labels().value(0).first).arg(labels().value(0).second);
++ }
++}
++
++KUrl Job::destination() const
++{
++ return d->destination;
++}
++
++ulong Job::eta() const
++{
++ return d->eta;
++}
++
++void Job::setEta(ulong eta)
++{
++ d->eta = eta;
++}
++
++QMap<QString, qlonglong> Job::totalAmounts() const
++{
++ return d->totalAmounts;
++}
++
++void Job::setTotalAmounts(QMap<QString, qlonglong> amounts)
++{
++ d->totalAmounts = amounts;
++ scheduleChangedSignal();
++}
++
++QMap<QString, qlonglong> Job::processedAmounts() const
++{
++ return d->processedAmounts;
++}
++
++void Job::setProcessedAmounts(QMap<QString, qlonglong> amounts)
++{
++ d->processedAmounts = amounts;
++ scheduleChangedSignal();
++}
++
++Job::State Job::state() const
++{
++ return d->state;
++}
++
++void Job::setState(State state)
++{
++ if (d->state != state) {
++ d->state = state;
++ show();
++ if (state == Stopped) {
++ d->finalElapsed = d->elapsed.elapsed();
++ }
++ emit stateChanged(this);
++ }
++}
++
++QList<QPair<QString, QString> > Job::labels() const
++{
++ return d->labels;
++}
++
++void Job::setLabels(QList<QPair<QString, QString> > labels)
++{
++ d->labels = labels;
++ if (d->labels.count() > 1 && d->destination.isEmpty()) {
++ d->destination = d->labels.value(1).second;
++ }
++ scheduleChangedSignal();
++}
++
++uint Job::percentage() const
++{
++ return d->percentage;
++}
++
++void Job::setPercentage(uint percentage)
++{
++ if (d->percentage != percentage) {
++ d->percentage = percentage;
++ scheduleChangedSignal();
++ }
++}
++
++uint Job::elapsed() const
++{
++ if (d->finalElapsed) {
++ return d->finalElapsed;
++ } else {
++ return d->elapsed.elapsed();
++ }
++}
++
++bool Job::isSuspendable() const
++{
++ return d->suspendable;
++}
++
++void Job::setSuspendable(bool suspendable)
++{
++ if (d->suspendable != suspendable) {
++ d->suspendable = suspendable;
++ scheduleChangedSignal();
++ }
++}
++
++bool Job::isKillable() const
++{
++ return d->killable;
++}
++
++void Job::setKillable(bool killable)
++{
++ if (d->killable != killable) {
++ d->killable = killable;
++ scheduleChangedSignal();
++ }
++}
++
++void Job::suspend()
++{
++ kWarning() << "Suspend is not implemented in this job provider.";
++}
++
++void Job::resume()
++{
++ kWarning() << "Resume is not implemented in this job provider.";
++}
++
++void Job::stop()
++{
++ kWarning() << "Stop is not implemented in this job provider.";
++}
++
++void Job::show()
++{
++ if (state() == Job::Running && !d->shown) {
++ d->shown = true;
++ emit ready(this);
++ }
++}
++
++void Job::scheduleChangedSignal()
++{
++ if (d->shown && !d->timerId) {
++ d->timerId = startTimer(0);
++ }
++}
++
++void Job::timerEvent(QTimerEvent *)
++{
++ killTimer(d->timerId);
++ d->timerId = 0;
++ emit changed(this);
++}
++
++
++#include "job.moc"
+diff --git a/plasma/generic/applets/notifications/core/job.h b/plasma/generic/applets/notifications/core/job.h
+new file mode 100644
+index 0000000..3d2984d
+--- /dev/null
++++ b/plasma/generic/applets/notifications/core/job.h
+@@ -0,0 +1,188 @@
++/***************************************************************************
++ * Copyright (C) 2008 Rob Scheepmaker <r.scheepmaker@student.utwente.nl> *
++ * *
++ * This program is free software; you can redistribute it and/or modify *
++ * it under the terms of the GNU General Public License as published by *
++ * the Free Software Foundation; either version 2 of the License, or *
++ * (at your option) any later version. *
++ * *
++ * This program is distributed in the hope that it will be useful, *
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
++ * GNU General Public License for more details. *
++ * *
++ * You should have received a copy of the GNU General Public License *
++ * along with this program; if not, write to the *
++ * Free Software Foundation, Inc., *
++ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA . *
++ ***************************************************************************/
++
++#ifndef JOB_H
++#define JOB_H
++
++#include <QtCore/QHash>
++#include <QtCore/QObject>
++
++#include <KUrl>
++
++
++class Job : public QObject
++{
++ Q_OBJECT
++
++public:
++ enum State {
++ Running = 0,
++ Suspended = 1,
++ Stopped = 2
++ };
++
++ Job(QObject *parent = 0);
++ virtual ~Job();
++
++ /**
++ * Request and signal destruction of this object
++ */
++ void destroy();
++
++ /**
++ * @return the name of the application which started this job.
++ */
++ QString applicationName() const;
++
++ /**
++ * @return the name of the icon to be used for this job.
++ */
++ QString applicationIconName() const;
++
++ /**
++ * @return the descripion of the activity that is performed.
++ */
++ QString message() const;
++
++ /**
++ * @return the errormessage if an error has occurred.
++ */
++ QString error() const;
++
++ /**
++ * @return the (human readable) speed at which the jobs is progressing.
++ */
++ QString speed() const;
++
++ /**
++ * @return the (in bytes per second) speed at which the jobs is progressing.
++ */
++ qlonglong numericSpeed() const;
++
++ /**
++ * @return a nice description of the job that has been completed.
++ */
++ QString completedMessage() const;
++
++ /**
++ * @return the time (in seconds) in which this job is expected to complete.
++ */
++ ulong eta() const;
++
++ void setEta(ulong eta);
++
++ QMap<QString, qlonglong> totalAmounts() const;
++
++ QMap<QString, qlonglong> processedAmounts() const;
++
++ /**
++ * @return a list of pairs containing label names/values in the order they should be displayed.
++ */
++ QList<QPair<QString, QString> > labels() const;
++
++ /**
++ * @return the state this job is in.
++ */
++ State state() const;
++
++ bool isSuspendable() const;
++
++ bool isKillable() const;
++
++ /**
++ * @retun the percentage of the job that has been completed.
++ */
++ uint percentage() const;
++
++ /**
++ * total elapsed job time
++ */
++ uint elapsed() const;
++
++ /**
++ * Destination url
++ */
++ KUrl destination() const;
++
++public slots:
++ /**
++ * suspend this job.
++ */
++ virtual void suspend();
++
++ /**
++ * resume this job.
++ */
++ virtual void resume();
++
++ /**
++ * stop this job.
++ */
++ virtual void stop();
++
++signals:
++ /**
++ * Emitted when the job is ready to be shown
++ */
++ void ready(Job *job);
++
++ /**
++ * Emitted when the job changes state
++ */
++ void stateChanged(Job *job);
++
++ /**
++ * Emitted when the job details change
++ */
++ void changed(Job *job);
++
++ /**
++ * Emitted when the job is about to be destroyed
++ **/
++ void destroyed(Job *job);
++
++protected:
++ void setApplicationName(const QString &applicationName);
++ void setApplicationIconName(const QString &applicationIcon);
++ void setMessage(const QString &message);
++ void setError(const QString &error);
++ void setSpeed(const QString &speed);
++ void setNumericSpeed(const qlonglong speed);
++ void setTotalAmounts(QMap<QString, qlonglong> amount);
++ void setProcessedAmounts(QMap<QString, qlonglong> amount);
++ void setState(State state);
++ void setSuspendable(bool suspendable);
++ void setKillable(bool killable);
++ void setPercentage(uint percentage);
++ void setLabels(QList<QPair<QString, QString> > labels);
++ void timerEvent(QTimerEvent *);
++
++private slots:
++ void show();
++
++private:
++ void scheduleChangedSignal();
++
++ class Private;
++ Private* const d;
++
++ friend class Manager;
++};
++
++#endif
+diff --git a/plasma/generic/applets/notifications/core/notification.cpp b/plasma/generic/applets/notifications/core/notification.cpp
+new file mode 100644
+index 0000000..26833b6
+--- /dev/null
++++ b/plasma/generic/applets/notifications/core/notification.cpp
+@@ -0,0 +1,272 @@
++/***************************************************************************
++ * notification.cpp *
++ * *
++ * Copyright (C) 2008 Jason Stubbs <jasonbstubbs@gmail.com> *
++ * *
++ * This program is free software; you can redistribute it and/or modify *
++ * it under the terms of the GNU General Public License as published by *
++ * the Free Software Foundation; either version 2 of the License, or *
++ * (at your option) any later version. *
++ * *
++ * This program is distributed in the hope that it will be useful, *
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
++ * GNU General Public License for more details. *
++ * *
++ * You should have received a copy of the GNU General Public License *
++ * along with this program; if not, write to the *
++ * Free Software Foundation, Inc., *
++ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA . *
++ ***************************************************************************/
++
++#include "notification.h"
++
++#include <QImage>
++#include <QtCore/QTimer>
++
++#include <KDebug>
++
++
++class Notification::Private
++{
++public:
++ Private()
++ : timeout(0),
++ urgency(0),
++ hideTimer(0),
++ expired(false),
++ read(false)
++ {
++ }
++
++ QString identifier;
++ QString applicationName;
++ QIcon applicationIcon;
++ QString message;
++ QString summary;
++ int timeout;
++ int urgency;
++ QImage image;
++ QTimer *deleteTimer;
++ QTimer *hideTimer;
++ bool expired;
++ bool read;
++
++ QHash<QString, QString> actions;
++ QStringList actionOrder;
++};
++
++Notification::Notification(QObject *parent)
++ : QObject(parent),
++ d(new Private)
++{
++ d->deleteTimer = new QTimer(this);
++ d->deleteTimer->setSingleShot(true);
++ connect(d->deleteTimer, SIGNAL(timeout()), this, SLOT(destroy()));
++}
++
++
++Notification::~Notification()
++{
++ emit notificationDestroyed(this);
++ delete d;
++}
++
++void Notification::destroy()
++{
++ emit notificationDestroyed(this);
++ deleteLater();
++}
++
++QString Notification::applicationName() const
++{
++ return d->applicationName;
++}
++
++
++void Notification::setApplicationName(const QString &applicationName)
++{
++ d->applicationName = applicationName;
++}
++
++
++QIcon Notification::applicationIcon() const
++{
++ return d->applicationIcon;
++}
++
++
++void Notification::setApplicationIcon(const QIcon &applicationIcon)
++{
++ d->applicationIcon = applicationIcon;
++}
++
++
++QString Notification::message() const
++{
++ return d->message;
++}
++
++
++void Notification::setMessage(const QString &message)
++{
++ d->message = message;
++}
++
++
++QString Notification::summary() const
++{
++ return d->summary;
++}
++
++
++void Notification::setSummary(const QString &summary)
++{
++ d->summary = summary;
++}
++
++
++int Notification::timeout() const
++{
++ return d->timeout;
++}
++
++QImage Notification::image() const
++{
++ return d->image;
++}
++
++void Notification::setImage(QImage image)
++{
++ d->image = image;
++}
++
++void Notification::setTimeout(int timeout)
++{
++ //show them at most 30 seconds
++ if (!timeout) {
++ d->timeout = 30 * 1000;
++ } else {
++ d->timeout = timeout;
++ }
++
++ if (d->urgency >= 2) {
++ return;
++ }
++
++ if (!d->hideTimer) {
++ d->hideTimer = new QTimer(this);
++ d->hideTimer->setSingleShot(true);
++ connect(d->hideTimer, SIGNAL(timeout()), this, SLOT(hide()));
++ }
++ d->hideTimer->start(d->timeout);
++}
++
++void Notification::setUrgency(int urgency)
++{
++ if (urgency != d->urgency) {
++ d->urgency = urgency;
++ if (urgency >= 2) {
++ if (d->hideTimer) {
++ d->hideTimer->stop();
++ }
++ d->deleteTimer->stop();
++ } else {
++ setTimeout(d->timeout);
++ }
++ }
++}
++
++int Notification::urgency() const
++{
++ return d->urgency;
++}
++
++QHash<QString, QString> Notification::actions() const
++{
++ return d->actions;
++}
++
++
++void Notification::setActions(const QHash<QString, QString> &actions)
++{
++ d->actions = actions;
++ emit changed(this);
++}
++
++
++QStringList Notification::actionOrder() const
++{
++ return d->actionOrder;
++}
++
++
++void Notification::setActionOrder(const QStringList &actionOrder)
++{
++ d->actionOrder = actionOrder;
++}
++
++
++void Notification::triggerAction(const QString &actionId)
++{
++ Q_UNUSED(actionId);
++ kDebug() << "action triggered but no handler implemented";
++}
++
++void Notification::remove()
++{
++ kDebug() << "remove requested but no handler implemented";
++}
++
++void Notification::linkActivated(const QString &link)
++{
++ Q_UNUSED(link)
++ kDebug() << "link activation requested but no handler implemented";
++}
++
++void Notification::hide()
++{
++ d->expired = true;
++ emit expired(this);
++}
++
++void Notification::startDeletionCountdown()
++{
++ if (d->urgency >= 2) {
++ return;
++ }
++
++ //keep it available for 10 minutes
++ d->deleteTimer->start(10*60*1000);
++}
++
++bool Notification::isExpired() const
++{
++ return d->expired;
++}
++
++void Notification::setRead(const bool read)
++{
++ d->read = read;
++}
++
++bool Notification::isRead() const
++{
++ return d->read;
++}
++
++void Notification::setDeleteTimeout(const int time)
++{
++ if (d->deleteTimer->interval() != time) {
++ d->deleteTimer->start(time);
++ }
++}
++
++int Notification::deleteTimeOut() const
++{
++ return d->deleteTimer->interval();
++}
++
++
++
++#include "notification.moc"
+diff --git a/plasma/generic/applets/notifications/core/notification.h b/plasma/generic/applets/notifications/core/notification.h
+new file mode 100644
+index 0000000..e04fdba
+--- /dev/null
++++ b/plasma/generic/applets/notifications/core/notification.h
+@@ -0,0 +1,100 @@
++/***************************************************************************
++ * notification.h *
++ * *
++ * Copyright (C) 2008 Jason Stubbs <jasonbstubbs@gmail.com> *
++ * *
++ * This program is free software; you can redistribute it and/or modify *
++ * it under the terms of the GNU General Public License as published by *
++ * the Free Software Foundation; either version 2 of the License, or *
++ * (at your option) any later version. *
++ * *
++ * This program is distributed in the hope that it will be useful, *
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
++ * GNU General Public License for more details. *
++ * *
++ * You should have received a copy of the GNU General Public License *
++ * along with this program; if not, write to the *
++ * Free Software Foundation, Inc., *
++ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA . *
++ ***************************************************************************/
++
++#ifndef NOTIFICATION_H
++#define NOTIFICATION_H
++
++#include <QImage>
++#include <QtCore/QHash>
++#include <QtCore/QObject>
++
++#include <QtGui/QIcon>
++
++
++
++class Notification : public QObject
++{
++ Q_OBJECT
++
++public:
++ Notification(QObject *parent = 0);
++ virtual ~Notification();
++
++ QString applicationName() const;
++ QIcon applicationIcon() const;
++ QString message() const;
++ QString summary() const;
++ int timeout() const;
++ QImage image() const;
++
++ void setUrgency(int urgency);
++ int urgency() const;
++
++ QHash<QString, QString> actions() const;
++ QStringList actionOrder() const;
++
++ bool isExpired() const;
++
++ void setRead(const bool read);
++ bool isRead() const;
++
++ void setDeleteTimeout(const int time);
++ int deleteTimeOut() const;
++
++public slots:
++ virtual void triggerAction(const QString &actionId);
++ virtual void remove();
++ virtual void linkActivated(const QString &link);
++ void startDeletionCountdown();
++ void hide();
++ void destroy();
++
++signals:
++ void changed(Notification *notification = 0);
++
++ /**
++ * Emitted when the notification is about to be destroyed
++ **/
++ void notificationDestroyed(Notification *notification = 0);
++
++ /**
++ * emitted when the notification wants to hide itself
++ */
++ void expired(Notification *notification = 0);
++
++protected:
++ void setApplicationName(const QString &applicationName);
++ void setApplicationIcon(const QIcon &applicationIcon);
++ void setMessage(const QString &message);
++ void setSummary(const QString &summary);
++ void setImage(QImage image);
++ void setTimeout(int timeout);
++ void setActions(const QHash<QString, QString> &actions);
++ void setActionOrder(const QStringList &actionOrder);
++
++private:
++ class Private;
++ Private* const d;
++};
++
++
++
++#endif
+diff --git a/plasma/generic/applets/notifications/core/notificationsmanager.cpp b/plasma/generic/applets/notifications/core/notificationsmanager.cpp
+new file mode 100644
+index 0000000..6892361
+--- /dev/null
++++ b/plasma/generic/applets/notifications/core/notificationsmanager.cpp
+@@ -0,0 +1,245 @@
++/***************************************************************************
++ * manager.cpp *
++ * *
++ * Copyright (C) 2008 Jason Stubbs <jasonbstubbs@gmail.com> *
++ * Copyright (C) 2010 Marco Martin <notmart@gmail.com> *
++ * *
++ * This program is free software; you can redistribute it and/or modify *
++ * it under the terms of the GNU General Public License as published by *
++ * the Free Software Foundation; either version 2 of the License, or *
++ * (at your option) any later version. *
++ * *
++ * This program is distributed in the hope that it will be useful, *
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
++ * GNU General Public License for more details. *
++ * *
++ * You should have received a copy of the GNU General Public License *
++ * along with this program; if not, write to the *
++ * Free Software Foundation, Inc., *
++ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA . *
++ ***************************************************************************/
++
++#include "notificationsmanager.h"
++
++#include <QTimer>
++
++#include <KGlobal>
++
++#include <plasma/applet.h>
++
++#include "job.h"
++#include "notification.h"
++#include "protocol.h"
++
++#include "../protocols/notifications/dbusnotificationprotocol.h"
++#include "../protocols/jobs/dbusjobprotocol.h"
++
++
++static const int idleCheckInterval = 60 * 1000;
++
++class Manager::Private
++{
++public:
++ Private(Manager *manager)
++ : q(manager),
++ jobTotals(new Job(manager)),
++ jobProtocol(0),
++ notificationProtocol(0)
++ {
++ }
++
++ void setupProtocol(Protocol *protocol);
++ void checkIdle();
++
++ Manager *q;
++ QList<Task *> tasks;
++ QList<Notification*> notifications;
++ QList<Job *> jobs;
++ Job *jobTotals;
++ Notifications *notificationsApplet;
++ Protocol *jobProtocol;
++ Protocol *notificationProtocol;
++ QTimer *idleTimer;
++ static const int s_notificationLimit = 15;
++};
++
++
++Manager::Manager(Notifications *parentApplet)
++ : QObject(parentApplet),
++ d(new Private(this))
++{
++ d->notificationsApplet = parentApplet;
++ d->idleTimer = new QTimer(this);
++ d->idleTimer->setSingleShot(false);
++ connect(d->idleTimer, SIGNAL(timeout()), this, SLOT(checkIdle()));
++}
++
++Manager::~Manager()
++{
++ delete d;
++}
++
++void Manager::registerNotificationProtocol()
++{
++ if (!d->notificationProtocol) {
++ d->notificationProtocol = new DBusNotificationProtocol(this);
++ d->setupProtocol(d->notificationProtocol);
++ }
++}
++
++void Manager::unregisterNotificationProtocol()
++{
++ delete d->notificationProtocol;
++ d->notificationProtocol = 0;
++}
++
++void Manager::addNotification(Notification* notification)
++{
++ connect(notification, SIGNAL(notificationDestroyed(Notification*)),
++ this, SLOT(removeNotification(Notification*)));
++ connect(notification, SIGNAL(changed(Notification*)),
++ this, SIGNAL(notificationChanged(Notification*)));
++ connect(notification, SIGNAL(expired(Notification*)),
++ this, SIGNAL(notificationExpired(Notification*)));
++
++ d->notifications.append(notification);
++
++ if (!d->idleTimer->isActive()) {
++ d->idleTimer->start(idleCheckInterval);
++ }
++ connect(this, SIGNAL(idleTerminated()), notification, SLOT(startDeletionCountdown()));
++
++ emit notificationAdded(notification);
++
++ if (d->notifications.count() > d->s_notificationLimit) {
++ Notification *notification = d->notifications.first();
++ d->notifications.pop_front();
++ notification->deleteLater();
++ }
++}
++
++void Manager::removeNotification(Notification *notification)
++{
++ d->notifications.removeAll(notification);
++ disconnect(notification, 0, this, 0);
++ disconnect(this, 0, notification, 0);
++
++ if (d->notifications.isEmpty()) {
++ d->idleTimer->stop();
++ }
++
++ emit notificationRemoved(notification);
++}
++
++QList<Notification*> Manager::notifications() const
++{
++ return d->notifications;
++}
++
++void Manager::clearNotifications()
++{
++ qDeleteAll(d->notifications);
++ d->notifications.clear();
++}
++
++void Manager::registerJobProtocol()
++{
++ if (!d->jobProtocol) {
++ d->jobProtocol = new DBusJobProtocol(this);
++ d->setupProtocol(d->jobProtocol);
++ }
++}
++
++void Manager::unregisterJobProtocol()
++{
++ delete d->jobProtocol;
++ d->jobProtocol = 0;
++}
++
++void Manager::addJob(Job *job)
++{
++ connect(job, SIGNAL(destroyed(Job*)), this, SLOT(removeJob(Job*)));
++ connect(job, SIGNAL(changed(Job*)), this, SIGNAL(jobChanged(Job*)));
++ connect(job, SIGNAL(stateChanged(Job*)), this, SIGNAL(jobStateChanged(Job*)));
++ connect(job, SIGNAL(changed(Job*)), this, SLOT(updateTotals()));
++
++ d->jobs.append(job);
++ emit jobAdded(job);
++}
++
++void Manager::removeJob(Job *job)
++{
++ d->jobs.removeAll(job);
++ disconnect(job);
++ updateTotals();
++ emit jobRemoved(job);
++}
++
++void Manager::updateTotals()
++{
++ uint totalPercent = 0;
++ ulong totalEta = 0;
++ foreach (Job *job, d->jobs) {
++ totalPercent += job->percentage();
++ if (job->eta() > totalEta) {
++ totalEta = job->eta();
++ }
++ }
++
++ if (d->jobs.count() > 0) {
++ d->jobTotals->setPercentage(totalPercent / d->jobs.count());
++ d->jobTotals->setMessage(i18np("1 running job (%2 remaining)", "%1 running jobs (%2 remaining)",
++ d->jobs.count(),
++ KGlobal::locale()->prettyFormatDuration(totalEta)));
++ } else {
++ d->jobTotals->setPercentage(0);
++ d->jobTotals->setMessage(i18n("no running jobs"));
++ }
++ //TODO: set a sensible icon
++}
++
++Job *Manager::jobTotals() const
++{
++ return d->jobTotals;
++}
++
++QList<Job*> Manager::jobs() const
++{
++ return d->jobs;
++}
++
++void Manager::checkIdle()
++{
++ int totalIdle;
++#ifdef HAVE_LIBXSS // Idle detection.
++ XScreenSaverInfo* _mit_info;
++ _mit_info = XScreenSaverAllocInfo();
++ XScreenSaverQueryInfo( QX11Info::display(), QX11Info::appRootWindow(), _mit_info );
++ totalIdle = _mit_info->idle;
++ XFree( _mit_info );
++#else
++ totalIdle = 0;
++#endif // HAVE_LIBXSS
++
++ if (totalIdle < idleCheckInterval) {
++ d->idleTimer->stop();
++ emit idleTerminated();
++ }
++}
++
++void Manager::Private::setupProtocol(Protocol *protocol)
++{
++ connect(protocol, SIGNAL(jobCreated(Job*)), q, SLOT(addJob(Job*)));
++ connect(protocol, SIGNAL(notificationCreated(Notification*)),
++ q, SLOT(addNotification(Notification*)));
++ protocol->init();
++}
++
++Notifications *Manager::applet() const
++{
++ return d->notificationsApplet;
++}
++
++
++#include "notificationsmanager.moc"
+diff --git a/plasma/generic/applets/notifications/core/notificationsmanager.h b/plasma/generic/applets/notifications/core/notificationsmanager.h
+new file mode 100644
+index 0000000..a6d6639
+--- /dev/null
++++ b/plasma/generic/applets/notifications/core/notificationsmanager.h
+@@ -0,0 +1,161 @@
++/***************************************************************************
++ * manager.h *
++ * *
++ * Copyright (C) 2008 Jason Stubbs <jasonbstubbs@gmail.com> *
++ * Copyright (C) 2010 Marco Martin <notmart@gmail.com> *
++ * *
++ * This program is free software; you can redistribute it and/or modify *
++ * it under the terms of the GNU General Public License as published by *
++ * the Free Software Foundation; either version 2 of the License, or *
++ * (at your option) any later version. *
++ * *
++ * This program is distributed in the hope that it will be useful, *
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
++ * GNU General Public License for more details. *
++ * *
++ * You should have received a copy of the GNU General Public License *
++ * along with this program; if not, write to the *
++ * Free Software Foundation, Inc., *
++ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA . *
++ ***************************************************************************/
++
++#ifndef NOTIFICATIONSMANAGER_H
++#define NOTIFICATIONSMANAGER_H
++
++#include <QtCore/QObject>
++
++#include <KConfigGroup>
++
++#include <plasma/plasma.h>
++#include "../ui/notifications.h"
++
++
++namespace Plasma
++{
++class Applet;
++}
++
++class Notifications;
++class Notification;
++class Task;
++class Job;
++
++/**
++ * w
++ * @short Creator and amalgamator of the supported system tray specifications
++ **/
++class Manager : public QObject
++{
++ Q_OBJECT
++
++public:
++ Manager(Notifications *parentApplet);
++ ~Manager();
++
++ /**
++ * @return a list of all known Notification instances
++ **/
++ QList<Notification*> notifications() const;
++
++ /**
++ * clear all notifications
++ */
++ void clearNotifications();
++
++ /**
++ * @return a list of all known Job instances
++ **/
++ QList<Job*> jobs() const;
++
++ /**
++ * @return a Job instance that can be used to monitor total progress
++ **/
++ Job *jobTotals() const;
++
++ /**
++ * Integrates the Job progress info into the applet's notification system
++ **/
++ void registerJobProtocol();
++
++ /**
++ * Iintegrates the notifications into the applet's notification system
++ **/
++ void registerNotificationProtocol();
++
++ /**
++ * Removes the Job progress info from the applet's notification system
++ **/
++ void unregisterJobProtocol();
++
++ /**
++ * Removes the notifications from the applet's notification system
++ **/
++ void unregisterNotificationProtocol();
++
++ Notifications *applet() const;
++
++signals:
++ /**
++ * Emitted when a new notification has been added
++ **/
++ void notificationAdded(Notification *notification);
++
++ /**
++ * Emitted when something about a notification changes
++ **/
++ void notificationChanged(Notification *notification);
++
++ /**
++ * The notification is expired and wants to hide itself
++ */
++ void notificationExpired(Notification *notification);
++
++ /**
++ * Emitted when a notification has been removed
++ **/
++ void notificationRemoved(Notification *notification);
++
++ /**
++ * Emitted when a new job has been added
++ **/
++ void jobAdded(Job *job);
++
++ /**
++ * Emitted when the state of a job changes
++ **/
++ void jobStateChanged(Job *job);
++
++ /**
++ * Emitted when something about a job changes
++ **/
++ void jobChanged(Job *job);
++
++ /**
++ * Emitted when a job has been removed
++ **/
++ void jobRemoved(Job *job);
++
++ /**
++ * the pc is out of idle and is starting being used
++ */
++ void idleTerminated();
++
++private slots:
++ void addNotification(Notification *notification);
++ void removeNotification(Notification *notification);
++ void addJob(Job *job);
++ void removeJob(Job *job);
++ void updateTotals();
++ void checkIdle();
++
++private:
++ class Private;
++ Private* const d;
++
++ friend class Notifications;
++};
++
++
++
++#endif
+diff --git a/plasma/generic/applets/notifications/core/protocol.cpp b/plasma/generic/applets/notifications/core/protocol.cpp
+new file mode 100644
+index 0000000..a3716a2
+--- /dev/null
++++ b/plasma/generic/applets/notifications/core/protocol.cpp
+@@ -0,0 +1,31 @@
++/***************************************************************************
++ * taskprotocol.cpp *
++ * *
++ * Copyright (C) 2008 Jason Stubbs <jasonbstubbs@gmail.com> *
++ * *
++ * This program is free software; you can redistribute it and/or modify *
++ * it under the terms of the GNU General Public License as published by *
++ * the Free Software Foundation; either version 2 of the License, or *
++ * (at your option) any later version. *
++ * *
++ * This program is distributed in the hope that it will be useful, *
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
++ * GNU General Public License for more details. *
++ * *
++ * You should have received a copy of the GNU General Public License *
++ * along with this program; if not, write to the *
++ * Free Software Foundation, Inc., *
++ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA . *
++ ***************************************************************************/
++
++#include "protocol.h"
++
++
++Protocol::Protocol(QObject *parent)
++ : QObject(parent)
++{
++}
++
++
++#include "protocol.moc"
+diff --git a/plasma/generic/applets/notifications/core/protocol.h b/plasma/generic/applets/notifications/core/protocol.h
+new file mode 100644
+index 0000000..ea47292
+--- /dev/null
++++ b/plasma/generic/applets/notifications/core/protocol.h
+@@ -0,0 +1,68 @@
++/***************************************************************************
++ * taskprotocol.h *
++ * *
++ * Copyright (C) 2008 Jason Stubbs <jasonbstubbs@gmail.com> *
++ * *
++ * This program is free software; you can redistribute it and/or modify *
++ * it under the terms of the GNU General Public License as published by *
++ * the Free Software Foundation; either version 2 of the License, or *
++ * (at your option) any later version. *
++ * *
++ * This program is distributed in the hope that it will be useful, *
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
++ * GNU General Public License for more details. *
++ * *
++ * You should have received a copy of the GNU General Public License *
++ * along with this program; if not, write to the *
++ * Free Software Foundation, Inc., *
++ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA . *
++ ***************************************************************************/
++
++#ifndef PROTOCOL_H
++#define PROTOCOL_H
++
++#include <QtCore/QObject>
++
++
++class Job;
++class Notification;
++class Task;
++
++
++/**
++ * @short System tray protocol base class
++ *
++ * To support a new system tray protocol, this class and Task should be
++ * subclassed and the subclass of this class registered with the global
++ * Manager. The Protocol subclass should emit taskCreated() for each new
++ * task created.
++ **/
++class Protocol : public QObject
++{
++ Q_OBJECT
++public:
++ explicit Protocol(QObject *parent);
++
++ virtual void init() = 0;
++
++signals:
++ /**
++ * Signals that a new task has been created
++ **/
++ void taskCreated(Task *task);
++
++ /**
++ * Signals that a new notification has been created
++ **/
++ void jobCreated(Job *job);
++
++ /**
++ * Signals that a new notification has been created
++ **/
++ void notificationCreated(Notification *notification);
++};
++
++
++
++#endif
+diff --git a/plasma/generic/applets/notifications/metadata.desktop b/plasma/generic/applets/notifications/metadata.desktop
+deleted file mode 100644
+index 3a2be65..0000000
+--- a/plasma/generic/applets/notifications/metadata.desktop
++++ /dev/null
+@@ -1,166 +0,0 @@
+-[Desktop Entry]
+-Encoding=UTF-8
+-Name=Notifications
+-Name[ar]=تنبيهات
+-Name[ast]=Notificaciones
+-Name[be]=Абвяшчэнні
+-Name[be@latin]=Infarmavańnie
+-Name[bg]=Уведомяване
+-Name[bn]=বিজ্ঞপ্তি
+-Name[bn_IN]=সূচনাবার্তা
+-Name[br]=Kemenn
+-Name[bs]=obavještenja
+-Name[ca]=Notificacions
+-Name[ca@valencia]=Notificacions
+-Name[cs]=Oznamování
+-Name[csb]=Dôwanié wiédzë
+-Name[da]=Bekendtgørelser
+-Name[de]=Benachrichtigungen
+-Name[el]=Ειδοποιήσεις
+-Name[en_GB]=Notifications
+-Name[eo]=Atentigoj
+-Name[es]=Notificaciones
+-Name[et]=Märguanded
+-Name[eu]=Jakinarazpenak
+-Name[fa]=اخطارها
+-Name[fi]=Ilmoitukset
+-Name[fr]=Notifications
+-Name[fy]=Notifikaasjes
+-Name[ga]=Fógairt
+-Name[gl]=Notificacións
+-Name[gu]=નોંધણીઓ
+-Name[he]=הודעות
+-Name[hi]=सूचनाएँ
+-Name[hne]=सूचना मन ल
+-Name[hr]=Obavijesti
+-Name[hu]=Rendszerüzenetek
+-Name[ia]=Notificationes
+-Name[id]=Notifikasi
+-Name[is]=Kerfistilkynningar
+-Name[it]=Notifiche
+-Name[ja]=通知
+-Name[kk]=Құлақтандыру
+-Name[km]=សេចក្តីជូនដំណឹង
+-Name[kn]=ಸೂಚನೆಗಳು
+-Name[ko]=알림
+-Name[ku]=Agahdarî
+-Name[lt]=Pranešimai
+-Name[lv]=Paziņojumi
+-Name[mai]=सूचनासभ
+-Name[mk]=Известувања
+-Name[ml]=അറിയിപ്പുകള്
+-Name[mr]=सूचना
+-Name[ms]=Pemberitahuan
+-Name[nb]=Varslinger
+-Name[nds]=Bescheden
+-Name[ne]=सूचना
+-Name[nl]=Meldingen
+-Name[nn]=Varslingar
+-Name[oc]=Notificacions
+-Name[or]=ବିଜ୍ଞପ୍ତି
+-Name[pa]=ਨੋਟੀਫਿਕੇਸ਼ਨ
+-Name[pl]=Powiadomienia
+-Name[pt]=Notificações
+-Name[pt_BR]=Notificações
+-Name[ro]=Notificări
+-Name[ru]=Системные уведомления
+-Name[se]=Dieđáhusat
+-Name[si]=දැනුම් දීම්
+-Name[sk]=Upozornenia
+-Name[sl]=Obvestila
+-Name[sr]=обавештења
+-Name[sr@ijekavian]=обавјештења
+-Name[sr@ijekavianlatin]=obavještenja
+-Name[sr@latin]=obaveštenja
+-Name[sv]=Underrättelser
+-Name[ta]=Notifications
+-Name[te]=నోటీసులు
+-Name[tg]=Огоҳиномаҳо
+-Name[th]=การแจ้งให้ทราบต่าง ๆ
+-Name[tr]=Bildirimler
+-Name[ug]=ئۇقتۇرۇشلار
+-Name[uk]=Сповіщення
+-Name[uz]=Xabarnomalar
+-Name[uz@cyrillic]=Хабарномалар
+-Name[wa]=Notifiaedjes
+-Name[x-test]=xxNotificationsxx
+-Name[zh_CN]=通知
+-Name[zh_TW]=通知
+-Comment=Display notifications and jobs
+-Comment[ar]=أظهر التنبيهات والمهام
+-Comment[ast]=Amosar notificaciones y xeres
+-Comment[bg]=Показване на уведомления и задачи
+-Comment[bs]=Prikazuje obavještenja i poslove
+-Comment[ca]=Mostra les notificacions i els treballs
+-Comment[ca@valencia]=Mostra les notificacions i els treballs
+-Comment[cs]=Oznámení a úlohy
+-Comment[da]=Vis bekendtgørelser og job
+-Comment[de]=Benachrichtigungen und Aktionen anzeigen
+-Comment[el]=Εμφανίζει ειδοποιήσεις και εργασίες
+-Comment[en_GB]=Display notifications and jobs
+-Comment[es]=Mostrar notificaciones y tareas
+-Comment[et]=Märguannete ja tööde näitamine
+-Comment[eu]=Bistaratu jakinarazpenak eta lanak
+-Comment[fi]=Näyttää ilmoituksia ja töitä
+-Comment[fr]=Affiche les notifications et les tâches
+-Comment[ga]=Taispeáin fógraí agus jabanna
+-Comment[gl]=Mostra notificacións e tarefas
+-Comment[he]=משמש להצגת הודעות ועבודות
+-Comment[hr]=Prikazuje obavijesti i poslove
+-Comment[hu]=Értesítések és feladatok megjelenítése
+-Comment[ia]=Monstra notificationes e labores
+-Comment[id]=Tampilan notifikasi dan tugas
+-Comment[is]=Birting tilkynninga og verka
+-Comment[it]=Mostra notifiche e processi
+-Comment[ja]=ディスプレイ通知とジョブ
+-Comment[kk]=Құлақтандыру мен тапсырмаларды көрсету
+-Comment[km]=បង្ហាញការជូនដំណឹង និងការងារ
+-Comment[kn]=ಸೂಚನೆಗಳು ಹಾಗು ಕಾರ್ಯಗಳನ್ನು ಪ್ರದರ್ಶಿಸು
+-Comment[ko]=알림과 작업 표시
+-Comment[lt]=Rodyti pranešimus ir darbus
+-Comment[lv]=Parāda paziņojumus un darbus
+-Comment[mr]=सूचना व कार्यै दर्शवा
+-Comment[nb]=Vis varslinger og jobber
+-Comment[nds]=Bescheden un Opgaven wiesen
+-Comment[nl]=Meldingen en taken tonen
+-Comment[pa]=ਨੋਟੀਫਿਕੇਸ਼ਨ ਤੇ ਜਾਬ ਵੇਖੋ
+-Comment[pl]=Wyświetlanie powiadomień i zadań
+-Comment[pt]=Mostrar as notificações e tarefas
+-Comment[pt_BR]=Exibe notificações e tarefas
+-Comment[ro]=Afișează notificări și sarcini
+-Comment[ru]=Уведомления и задания
+-Comment[si]=කාර්ය සහ දැනුම්දීමෙ පෙන්වන්න
+-Comment[sk]=Zobrazenie upozornení a úloh
+-Comment[sl]=Prikazuje obvestila in opravila
+-Comment[sr]=Приказује обавештења и послове
+-Comment[sr@ijekavian]=Приказује обавјештења и послове
+-Comment[sr@ijekavianlatin]=Prikazuje obavještenja i poslove
+-Comment[sr@latin]=Prikazuje obaveštenja i poslove
+-Comment[sv]=Visa underrättelser och jobb
+-Comment[tg]=Иттилооти огоҳиҳо ва амалҳо
+-Comment[th]=แสดงการแจ้งให้ทราบและงานต่าง ๆ
+-Comment[tr]=Bildirimleri ve görevleri göster
+-Comment[ug]=ئۇقتۇرۇش ۋە ۋەزىپىلەرنى كۆرسىتىدۇ
+-Comment[uk]=Показ сповіщень і завдань
+-Comment[vi]=Hiển thị thông báo và công việc
+-Comment[wa]=Håyner notifiaedjes eyet bouyes
+-Comment[x-test]=xxDisplay notifications and jobsxx
+-Comment[zh_CN]=显示通知和任务
+-Comment[zh_TW]=顯示通知與工作
+-
+-Type=Service
+-Icon=preferences-desktop-notification
+-X-KDE-ParentApp=
+-X-KDE-PluginInfo-Author=Davide Bettio
+-X-KDE-PluginInfo-Category=Tasks
+-X-KDE-PluginInfo-Email=davide.bettio@kdemail.net
+-X-KDE-PluginInfo-License=GPL
+-X-KDE-PluginInfo-Name=org.kde.notifications
+-X-KDE-PluginInfo-Version=0.1
+-X-KDE-PluginInfo-Website=http://plasma.kde.org/
+-X-KDE-ServiceTypes=Plasma/PopupApplet,Plasma/Applet
+-X-Plasma-API=declarativeappletscript
+-X-Plasma-DefaultSize=100,100
+-X-Plasma-MainScript=ui/main.qml
+-X-Plasma-RequiredExtensions=LaunchApp
+-X-Plasma-NotificationArea=true
+diff --git a/plasma/generic/applets/notifications/plasma-applet-notifications.desktop b/plasma/generic/applets/notifications/plasma-applet-notifications.desktop
+new file mode 100644
+index 0000000..78a76f8
+--- /dev/null
++++ b/plasma/generic/applets/notifications/plasma-applet-notifications.desktop
+@@ -0,0 +1,164 @@
++[Desktop Entry]
++Name=Notifications
++Name[ar]=تنبيهات
++Name[ast]=Notificaciones
++Name[be]=Абвяшчэнні
++Name[be@latin]=Infarmavańnie
++Name[bg]=Уведомяване
++Name[bn]=বিজ্ঞপ্তি
++Name[bn_IN]=সূচনাবার্তা
++Name[br]=Kemenn
++Name[bs]=obavještenja
++Name[ca]=Notificacions
++Name[ca@valencia]=Notificacions
++Name[cs]=Oznamování
++Name[csb]=Dôwanié wiédzë
++Name[da]=Bekendtgørelser
++Name[de]=Benachrichtigungen
++Name[el]=Ειδοποιήσεις
++Name[en_GB]=Notifications
++Name[eo]=Atentigoj
++Name[es]=Notificaciones
++Name[et]=Märguanded
++Name[eu]=Jakinarazpenak
++Name[fa]=اخطارها
++Name[fi]=Ilmoitukset
++Name[fr]=Notifications
++Name[fy]=Notifikaasjes
++Name[ga]=Fógairt
++Name[gl]=Notificacións
++Name[gu]=નોંધણીઓ
++Name[he]=הודעות
++Name[hi]=सूचनाएँ
++Name[hne]=सूचना मन ल
++Name[hr]=Obavijesti
++Name[hu]=Rendszerüzenetek
++Name[ia]=Notificationes
++Name[id]=Notifikasi
++Name[is]=Kerfistilkynningar
++Name[it]=Notifiche
++Name[ja]=通知
++Name[kk]=Құлақтандыру
++Name[km]=សេចក្តីជូនដំណឹង
++Name[kn]=ಸೂಚನೆಗಳು
++Name[ko]=알림
++Name[ku]=Agahdarî
++Name[lt]=Pranešimai
++Name[lv]=Paziņojumi
++Name[mai]=सूचनासभ
++Name[mk]=Известувања
++Name[ml]=അറിയിപ്പുകള്
++Name[mr]=सूचना
++Name[ms]=Pemberitahuan
++Name[nb]=Varslinger
++Name[nds]=Bescheden
++Name[ne]=सूचना
++Name[nl]=Meldingen
++Name[nn]=Varslingar
++Name[oc]=Notificacions
++Name[or]=ବିଜ୍ଞପ୍ତି
++Name[pa]=ਨੋਟੀਫਿਕੇਸ਼ਨ
++Name[pl]=Powiadomienia
++Name[pt]=Notificações
++Name[pt_BR]=Notificações
++Name[ro]=Notificări
++Name[ru]=Системные уведомления
++Name[se]=Dieđáhusat
++Name[si]=දැනුම් දීම්
++Name[sk]=Upozornenia
++Name[sl]=Obvestila
++Name[sr]=обавештења
++Name[sr@ijekavian]=обавјештења
++Name[sr@ijekavianlatin]=obavještenja
++Name[sr@latin]=obaveštenja
++Name[sv]=Underrättelser
++Name[ta]=Notifications
++Name[te]=నోటీసులు
++Name[tg]=Огоҳиномаҳо
++Name[th]=การแจ้งให้ทราบต่าง ๆ
++Name[tr]=Bildirimler
++Name[ug]=ئۇقتۇرۇشلار
++Name[uk]=Сповіщення
++Name[uz]=Xabarnomalar
++Name[uz@cyrillic]=Хабарномалар
++Name[wa]=Notifiaedjes
++Name[x-test]=xxNotificationsxx
++Name[zh_CN]=通知
++Name[zh_TW]=通知
++Comment=Display notifications and jobs
++Comment[ar]=أظهر التنبيهات والمهام
++Comment[ast]=Amosar notificaciones y xeres
++Comment[bg]=Показване на уведомления и задачи
++Comment[bs]=Prikazuje obavještenja i poslove
++Comment[ca]=Mostra les notificacions i els treballs
++Comment[ca@valencia]=Mostra les notificacions i els treballs
++Comment[cs]=Oznámení a úlohy
++Comment[da]=Vis bekendtgørelser og job
++Comment[de]=Benachrichtigungen und Aktionen anzeigen
++Comment[el]=Εμφανίζει ειδοποιήσεις και εργασίες
++Comment[en_GB]=Display notifications and jobs
++Comment[es]=Mostrar notificaciones y tareas
++Comment[et]=Märguannete ja tööde näitamine
++Comment[eu]=Bistaratu jakinarazpenak eta lanak
++Comment[fi]=Näyttää ilmoituksia ja töitä
++Comment[fr]=Affiche les notifications et les tâches
++Comment[ga]=Taispeáin fógraí agus jabanna
++Comment[gl]=Mostra notificacións e tarefas
++Comment[he]=משמש להצגת הודעות ועבודות
++Comment[hr]=Prikazuje obavijesti i poslove
++Comment[hu]=Értesítések és feladatok megjelenítése
++Comment[ia]=Monstra notificationes e labores
++Comment[id]=Tampilan notifikasi dan tugas
++Comment[is]=Birting tilkynninga og verka
++Comment[it]=Mostra notifiche e processi
++Comment[ja]=ディスプレイ通知とジョブ
++Comment[kk]=Құлақтандыру мен тапсырмаларды көрсету
++Comment[km]=បង្ហាញការជូនដំណឹង និងការងារ
++Comment[kn]=ಸೂಚನೆಗಳು ಹಾಗು ಕಾರ್ಯಗಳನ್ನು ಪ್ರದರ್ಶಿಸು
++Comment[ko]=알림과 작업 표시
++Comment[lt]=Rodyti pranešimus ir darbus
++Comment[lv]=Parāda paziņojumus un darbus
++Comment[nb]=Vis varslinger og jobber
++Comment[nds]=Bescheden un Opgaven wiesen
++Comment[nl]=Meldingen en taken tonen
++Comment[pa]=ਨੋਟੀਫਿਕੇਸ਼ਨ ਤੇ ਜਾਬ ਵੇਖੋ
++Comment[pl]=Wyświetlanie powiadomień i zadań
++Comment[pt]=Mostrar as notificações e tarefas
++Comment[pt_BR]=Exibe notificações e tarefas
++Comment[ro]=Afișează notificări și sarcini
++Comment[ru]=Уведомления и задания
++Comment[si]=කාර්ය සහ දැනුම්දීමෙ පෙන්වන්න
++Comment[sk]=Zobrazenie upozornení a úloh
++Comment[sl]=Prikazuje obvestila in opravila
++Comment[sr]=Приказује обавештења и послове
++Comment[sr@ijekavian]=Приказује обавјештења и послове
++Comment[sr@ijekavianlatin]=Prikazuje obavještenja i poslove
++Comment[sr@latin]=Prikazuje obaveštenja i poslove
++Comment[sv]=Visa underrättelser och jobb
++Comment[tg]=Иттилооти огоҳиҳо ва амалҳо
++Comment[th]=แสดงการแจ้งให้ทราบและงานต่าง ๆ
++Comment[tr]=Bildirimleri ve görevleri göster
++Comment[ug]=ئۇقتۇرۇش ۋە ۋەزىپىلەرنى كۆرسىتىدۇ
++Comment[uk]=Показ сповіщень і завдань
++Comment[vi]=Hiển thị thông báo và công việc
++Comment[wa]=Håyner notifiaedjes eyet bouyes
++Comment[x-test]=xxDisplay notifications and jobsxx
++Comment[zh_CN]=显示通知和任务
++Comment[zh_TW]=顯示通知與工作
++
++Icon=dialog-information
++Type=Service
++X-KDE-ServiceTypes=Plasma/Applet
++
++X-KDE-Library=plasma_applet_notifications
++X-KDE-PluginInfo-Author=Marco Martin
++X-KDE-PluginInfo-Email=notmart@gmail.com
++X-KDE-PluginInfo-Name=notifications
++X-KDE-PluginInfo-Version=1.0
++X-KDE-PluginInfo-Website=http://plasma.kde.org/
++X-KDE-PluginInfo-Category=System Information
++X-KDE-PluginInfo-Depends=
++X-KDE-PluginInfo-License=GPL v2+
++X-KDE-PluginInfo-EnabledByDefault=true
++
++X-Plasma-NotificationArea=true
+diff --git a/plasma/generic/applets/notifications/platformcontents/touch/ui/NotificationDelegate/NotificationDelegate.qml b/plasma/generic/applets/notifications/platformcontents/touch/ui/NotificationDelegate/NotificationDelegate.qml
+deleted file mode 100644
+index bdb74c2..0000000
+--- a/plasma/generic/applets/notifications/platformcontents/touch/ui/NotificationDelegate/NotificationDelegate.qml
++++ /dev/null
+@@ -1,195 +0,0 @@
+-/*
+- * Copyright 2011 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 Library 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.
+- */
+-
+-import QtQuick 1.0
+-import org.kde.plasma.core 0.1 as PlasmaCore
+-import org.kde.plasma.components 0.1 as PlasmaComponents
+-import org.kde.qtextracomponents 0.1
+-
+-PlasmaComponents.ListItem {
+- id: notificationItem
+- opacity: 1-Math.abs(x)/width
+- width: popupFlickable.width
+- property int toolIconSize: theme.smallMediumIconSize
+- property int layoutSpacing: 4
+-
+- visible: appTabBar.currentTab == allAppsTab || appTabBar.currentTab.text == appName
+-
+- Component.onCompleted: {
+- allApplicationsModel.addApplication(appIcon, appName)
+- mainScrollArea.height = mainScrollArea.implicitHeight
+- }
+- Component.onDestruction: {
+- allApplicationsModel.removeApplication(model.appName)
+- mainScrollArea.height = mainScrollArea.implicitHeight
+- }
+- Timer {
+- interval: 10*60*1000
+- repeat: false
+- running: !idleTimeSource.idle
+- onTriggered: {
+- notificationsModel.remove(index)
+- }
+- }
+-
+-
+- MouseArea {
+- width: parent.width
+- height: childrenRect.height
+- drag {
+- target: notificationItem
+- axis: Drag.XAxis
+- //kind of an hack over Column being too smart
+- minimumX: -parent.width + 1
+- maximumX: parent.width - 1
+- }
+- onReleased: {
+- if (notificationItem.x < -notificationItem.width/2) {
+- removeAnimation.exitFromRight = false
+- removeAnimation.running = true
+- } else if (notificationItem.x > notificationItem.width/2 ) {
+- removeAnimation.exitFromRight = true
+- removeAnimation.running = true
+- } else {
+- resetAnimation.running = true
+- }
+- }
+- SequentialAnimation {
+- id: removeAnimation
+- property bool exitFromRight: true
+- NumberAnimation {
+- target: notificationItem
+- properties: "x"
+- to: removeAnimation.exitFromRight ? notificationItem.width-1 : 1-notificationItem.width
+- duration: 250
+- easing.type: Easing.InOutQuad
+- }
+- NumberAnimation {
+- target: notificationItem
+- properties: "height"
+- to: 0
+- duration: 250
+- easing.type: Easing.InOutQuad
+- }
+- ScriptAction {
+- script: notificationsModel.remove(index)
+- }
+- }
+- SequentialAnimation {
+- id: resetAnimation
+- NumberAnimation {
+- target: notificationItem
+- properties: "x"
+- to: 0
+- duration: 250
+- easing.type: Easing.InOutQuad
+- }
+- }
+- Column {
+- spacing: notificationItem.layoutSpacing
+- width: parent.width
+- Item {
+- width: parent.width
+- height: summaryLabel.height
+-
+- PlasmaComponents.Label {
+- id: summaryLabel
+- text: summary
+- height: paintedHeight
+- anchors {
+- left: parent.left
+- right: parent.right
+- leftMargin: closeButton.width
+- rightMargin: closeButton.width
+- }
+- horizontalAlignment: Text.AlignHCenter
+- elide: Text.ElideRight
+- }
+-
+- PlasmaComponents.ToolButton {
+- id: closeButton
+- iconSource: "window-close"
+- width: notificationItem.toolIconSize
+- height: width
+- onClicked: removeAnimation.running = true
+- anchors {
+- top: parent.top
+- right: parent.right
+- }
+- }
+- }
+-
+- Item {
+- height: childrenRect.height
+- width: parent.width
+- QIconItem {
+- id: appIconItem
+- icon: QIcon(appIcon)
+- width: theme.largeIconSize
+- height: theme.largeIconSize
+- visible: !imageItem.visible
+- anchors {
+- left: parent.left
+- verticalCenter: parent.verticalCenter
+- }
+- }
+- QImageItem {
+- id: imageItem
+- anchors.fill: appIconItem
+- image: model.image
+- smooth: true
+- visible: nativeWidth > 0
+- }
+- PlasmaComponents.Label {
+- text: body
+- color: theme.textColor
+- anchors {
+- left: appIconItem.right
+- right: actionsColumn.left
+- verticalCenter: parent.verticalCenter
+- leftMargin: 6
+- rightMargin: 6
+- }
+- wrapMode: Text.Wrap
+- }
+- Column {
+- id: actionsColumn
+- spacing: notificationItem.layoutSpacing
+- anchors {
+- right: parent.right
+- rightMargin: 6
+- verticalCenter: parent.verticalCenter
+- }
+- Repeater {
+- model: actions
+- PlasmaComponents.Button {
+- text: model.text
+- width: theme.defaultFont.mSize.width * 8
+- height: theme.defaultFont.mSize.width * 3
+- onClicked: {
+- executeAction(source, model.id)
+- actionsColumn.visible = false
+- }
+- }
+- }
+- }
+- }
+- }
+- }
+-}
+diff --git a/plasma/generic/applets/notifications/platformcontents/touch/ui/NotificationDelegate/qmldir b/plasma/generic/applets/notifications/platformcontents/touch/ui/NotificationDelegate/qmldir
+deleted file mode 100644
+index 88fc37a..0000000
+--- a/plasma/generic/applets/notifications/platformcontents/touch/ui/NotificationDelegate/qmldir
++++ /dev/null
+@@ -1 +0,0 @@
+-NotificationDelegate 0.1 NotificationDelegate.qml
+diff --git a/plasma/generic/applets/notifications/platformcontents/touch/ui/uiproperties.js b/plasma/generic/applets/notifications/platformcontents/touch/ui/uiproperties.js
+deleted file mode 100644
+index f2f3d6b..0000000
+--- a/plasma/generic/applets/notifications/platformcontents/touch/ui/uiproperties.js
++++ /dev/null
+@@ -1,23 +0,0 @@
+-/*
+- * Copyright 2012 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 Library 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.
+- */
+-
+-
+-var toolIconSize = theme.mediumIconSize
+-var layoutSpacing = 6
+-var touchInput = true
+diff --git a/plasma/generic/applets/notifications/protocols/jobs/dbusjob.cpp b/plasma/generic/applets/notifications/protocols/jobs/dbusjob.cpp
+new file mode 100644
+index 0000000..7c89275
+--- /dev/null
++++ b/plasma/generic/applets/notifications/protocols/jobs/dbusjob.cpp
+@@ -0,0 +1,54 @@
++/***************************************************************************
++ * Copyright (C) 2008 Rob Scheepmaker <r.scheepmaker@student.utwente.nl> *
++ * *
++ * This program is free software; you can redistribute it and/or modify *
++ * it under the terms of the GNU General Public License as published by *
++ * the Free Software Foundation; either version 2 of the License, or *
++ * (at your option) any later version. *
++ * *
++ * This program is distributed in the hope that it will be useful, *
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
++ * GNU General Public License for more details. *
++ * *
++ * You should have received a copy of the GNU General Public License *
++ * along with this program; if not, write to the *
++ * Free Software Foundation, Inc., *
++ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA . *
++ ***************************************************************************/
++
++#include "dbusjob.h"
++
++#include <KDebug>
++
++
++
++DBusJob::DBusJob(const QString &source, QObject *parent)
++ : Job(parent),
++ m_source(source)
++{
++}
++
++DBusJob::~DBusJob()
++{
++ emit jobDeleted(m_source);
++}
++
++void DBusJob::suspend()
++{
++ emit suspend(m_source);
++ kDebug() << "suspend";
++}
++
++void DBusJob::resume()
++{
++ emit resume(m_source);
++ kDebug() << "resume";
++}
++
++void DBusJob::stop()
++{
++ emit stop(m_source);
++ kDebug() << "cancel";
++}
++
+diff --git a/plasma/generic/applets/notifications/protocols/jobs/dbusjob.h b/plasma/generic/applets/notifications/protocols/jobs/dbusjob.h
+new file mode 100644
+index 0000000..2e973d6
+--- /dev/null
++++ b/plasma/generic/applets/notifications/protocols/jobs/dbusjob.h
+@@ -0,0 +1,54 @@
++/***************************************************************************
++ * Copyright (C) 2008 Rob Scheepmaker <r.scheepmaker@student.utwente.nl> *
++ * *
++ * This program is free software; you can redistribute it and/or modify *
++ * it under the terms of the GNU General Public License as published by *
++ * the Free Software Foundation; either version 2 of the License, or *
++ * (at your option) any later version. *
++ * *
++ * This program is distributed in the hope that it will be useful, *
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
++ * GNU General Public License for more details. *
++ * *
++ * You should have received a copy of the GNU General Public License *
++ * along with this program; if not, write to the *
++ * Free Software Foundation, Inc., *
++ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA . *
++ ***************************************************************************/
++
++#ifndef DBUSJOB_H
++#define DBUSJOB_H
++
++#include "../../core/job.h"
++
++
++
++class DBusJob : public Job
++{
++ Q_OBJECT
++
++ friend class DBusJobProtocol;
++
++public:
++ DBusJob(const QString &source, QObject *parent = 0);
++ ~DBusJob();
++
++public slots:
++ void suspend();
++ void resume();
++ void stop();
++
++signals:
++ void jobDeleted(const QString &source);
++ void suspend(const QString &source);
++ void resume(const QString &source);
++ void stop(const QString &source);
++
++private:
++ QString m_source;
++};
++
++
++
++#endif
+diff --git a/plasma/generic/applets/notifications/protocols/jobs/dbusjobprotocol.cpp b/plasma/generic/applets/notifications/protocols/jobs/dbusjobprotocol.cpp
+new file mode 100644
+index 0000000..dff6dc9
+--- /dev/null
++++ b/plasma/generic/applets/notifications/protocols/jobs/dbusjobprotocol.cpp
+@@ -0,0 +1,180 @@
++/***************************************************************************
++ * Copyright (C) 2008 Rob Scheepmaker <r.scheepmaker@student.utwente.nl> *
++ * *
++ * This program is free software; you can redistribute it and/or modify *
++ * it under the terms of the GNU General Public License as published by *
++ * the Free Software Foundation; either version 2 of the License, or *
++ * (at your option) any later version. *
++ * *
++ * This program is distributed in the hope that it will be useful, *
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
++ * GNU General Public License for more details. *
++ * *
++ * You should have received a copy of the GNU General Public License *
++ * along with this program; if not, write to the *
++ * Free Software Foundation, Inc., *
++ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA . *
++ ***************************************************************************/
++
++#include "dbusjob.h"
++#include "dbusjobprotocol.h"
++
++
++#include <KConfigGroup>
++
++#include <Plasma/DataEngineManager>
++#include <Plasma/Service>
++#include <Plasma/ServiceJob>
++
++static const char engineName[] = "applicationjobs";
++
++DBusJobProtocol::DBusJobProtocol(Manager *parent)
++ : Protocol(parent),
++ m_manager(parent),
++ m_engine(0)
++{
++}
++
++
++DBusJobProtocol::~DBusJobProtocol()
++{
++ if (m_engine) {
++ Plasma::DataEngineManager::self()->unloadEngine(engineName);
++ }
++
++ foreach (DBusJob *job, m_jobs) {
++ disconnect(job);
++ job->destroy();
++ }
++
++ m_jobs.clear();
++}
++
++
++void DBusJobProtocol::init()
++{
++ m_engine = Plasma::DataEngineManager::self()->loadEngine(engineName);
++
++ if (!m_engine->isValid()) {
++ Plasma::DataEngineManager::self()->unloadEngine(engineName);
++ m_engine = 0;
++ return;
++ }
++
++ connect(m_engine, SIGNAL(sourceAdded(QString)),
++ this, SLOT(prepareJob(QString)));
++ connect(m_engine, SIGNAL(sourceRemoved(QString)),
++ this, SLOT(removeJob(QString)));
++}
++
++void DBusJobProtocol::prepareJob(const QString &source)
++{
++ m_engine->connectSource(source, this);
++}
++
++void DBusJobProtocol::dataUpdated(const QString &source, const Plasma::DataEngine::Data &data)
++{
++ DBusJob *job = m_jobs.value(source, 0);
++
++ if (!job) {
++ job = new DBusJob(source, this);
++ m_jobs.insert(source, job);
++ connect(job, SIGNAL(jobDeleted(QString)),
++ this, SLOT(removeJob(QString)));
++ connect(job, SIGNAL(suspend(QString)),
++ this, SLOT(suspend(QString)));
++ connect(job, SIGNAL(resume(QString)),
++ this, SLOT(resume(QString)));
++ connect(job, SIGNAL(stop(QString)),
++ this, SLOT(stop(QString)));
++ connect(job, SIGNAL(ready(Job*)),
++ this, SIGNAL(jobCreated(Job*)));
++ }
++
++ job->setApplicationName(data.value("appName").toString());
++ job->setApplicationIconName(data.value("appIconName").toString());
++ job->setPercentage(data["percentage"].toUInt());
++ job->setError(data["error"].toString());
++ job->setMessage(data["infoMessage"].toString());
++ job->setSuspendable(data["suspendable"].toBool());
++ job->setKillable(data["killable"].toBool());
++ job->setSpeed(data["speed"].toString());
++ job->setNumericSpeed(data["numericSpeed"].toLongLong());
++ job->setEta(data["eta"].toULongLong());
++
++ if (data["state"].toString() == "running") {
++ job->setState(Job::Running);
++ } else if (data["state"].toString() == "suspended") {
++ job->setState(Job::Suspended);
++ } else {
++ job->setState(Job::Stopped);
++ }
++
++ int i = 0;
++ QList<QPair<QString, QString> > labels;
++ while (data.contains(QString("label%1").arg(i))) {
++ QPair<QString, QString> label;
++ label.first = data[QString("labelName%1").arg(i)].toString();
++ label.second = data[QString("label%1").arg(i)].toString();
++ labels << label;
++ i++;
++ }
++ job->setLabels(labels);
++
++ i = 0;
++ QMap<QString, qlonglong> totalAmounts;
++ while (data.contains(QString("totalUnit%1").arg(i))) {
++ QString unit = data[QString("totalUnit%1").arg(i)].toString();
++ qlonglong amount = data[QString("totalAmount%1").arg(i)].toLongLong();
++ totalAmounts[unit] = amount;
++ i++;
++ }
++ job->setTotalAmounts(totalAmounts);
++
++ i = 0;
++ QMap<QString, qlonglong> processedAmounts;
++ while (data.contains(QString("processedUnit%1").arg(i))) {
++ QString unit = data[QString("processedUnit%1").arg(i)].toString();
++ qlonglong amount = data[QString("processedAmount%1").arg(i)].toLongLong();
++ processedAmounts[unit] = amount;
++ i++;
++ }
++
++ job->setProcessedAmounts(processedAmounts);
++}
++
++void DBusJobProtocol::removeJob(const QString &source)
++{
++ if (m_jobs.contains(source)) {
++ DBusJob *job = m_jobs.take(source);
++ job->setState(Job::Stopped);
++ job->destroy();
++ }
++}
++
++void DBusJobProtocol::suspend(const QString &source)
++{
++ Plasma::Service *service = m_engine->serviceForSource(source);
++ KConfigGroup op = service->operationDescription("suspend");
++ KJob *job = service->startOperationCall(op);
++ connect(job, SIGNAL(finished(KJob*)), service, SLOT(deleteLater()));
++}
++
++void DBusJobProtocol::resume(const QString &source)
++{
++ Plasma::Service *service = m_engine->serviceForSource(source);
++ KConfigGroup op = service->operationDescription("resume");
++ KJob *job = service->startOperationCall(op);
++ connect(job, SIGNAL(finished(KJob*)), service, SLOT(deleteLater()));
++}
++
++void DBusJobProtocol::stop(const QString &source)
++{
++ Plasma::Service *service = m_engine->serviceForSource(source);
++ KConfigGroup op = service->operationDescription("stop");
++ KJob *job = service->startOperationCall(op);
++ connect(job, SIGNAL(finished(KJob*)), service, SLOT(deleteLater()));
++}
++
++#include "dbusjobprotocol.moc"
+diff --git a/plasma/generic/applets/notifications/protocols/jobs/dbusjobprotocol.h b/plasma/generic/applets/notifications/protocols/jobs/dbusjobprotocol.h
+new file mode 100644
+index 0000000..230356c
+--- /dev/null
++++ b/plasma/generic/applets/notifications/protocols/jobs/dbusjobprotocol.h
+@@ -0,0 +1,57 @@
++/***************************************************************************
++ * Copyright (C) 2008 Rob Scheepmaker <r.scheepmaker@student.utwente.nl> *
++ * *
++ * This program is free software; you can redistribute it and/or modify *
++ * it under the terms of the GNU General Public License as published by *
++ * the Free Software Foundation; either version 2 of the License, or *
++ * (at your option) any later version. *
++ * *
++ * This program is distributed in the hope that it will be useful, *
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
++ * GNU General Public License for more details. *
++ * *
++ * You should have received a copy of the GNU General Public License *
++ * along with this program; if not, write to the *
++ * Free Software Foundation, Inc., *
++ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA . *
++ ***************************************************************************/
++
++#ifndef DBUSJOBPROTOCOL_H
++#define DBUSJOBPROTOCOL_H
++
++#include "../../core/protocol.h"
++#include "../../core/notificationsmanager.h"
++
++#include <plasma/dataengine.h>
++
++
++class DBusJob;
++
++class DBusJobProtocol : public Protocol
++{
++ Q_OBJECT
++
++public:
++ DBusJobProtocol(Manager *parent);
++ ~DBusJobProtocol();
++ void init();
++
++private slots:
++ void prepareJob(const QString &source);
++ void dataUpdated(const QString &source, const Plasma::DataEngine::Data &data);
++ void removeJob(const QString &source);
++ //void relayAction(const QString &source, const QString &actionName);
++ void suspend(const QString &source);
++ void resume(const QString &source);
++ void stop(const QString &source);
++
++private:
++ Manager *m_manager;
++ Plasma::DataEngine *m_engine;
++ QHash<QString, DBusJob*> m_jobs;
++};
++
++
++
++#endif
+diff --git a/plasma/generic/applets/notifications/protocols/notifications/dbusnotification.cpp b/plasma/generic/applets/notifications/protocols/notifications/dbusnotification.cpp
+new file mode 100644
+index 0000000..2c28ff2
+--- /dev/null
++++ b/plasma/generic/applets/notifications/protocols/notifications/dbusnotification.cpp
+@@ -0,0 +1,48 @@
++/***************************************************************************
++ * dbusnotification.cpp *
++ * *
++ * Copyright (C) 2008 Jason Stubbs <jasonbstubbs@gmail.com> *
++ * *
++ * This program is free software; you can redistribute it and/or modify *
++ * it under the terms of the GNU General Public License as published by *
++ * the Free Software Foundation; either version 2 of the License, or *
++ * (at your option) any later version. *
++ * *
++ * This program is distributed in the hope that it will be useful, *
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
++ * GNU General Public License for more details. *
++ * *
++ * You should have received a copy of the GNU General Public License *
++ * along with this program; if not, write to the *
++ * Free Software Foundation, Inc., *
++ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA . *
++ ***************************************************************************/
++
++#include "dbusnotification.h"
++
++#include <KDebug>
++
++
++DBusNotification::DBusNotification(const QString &source, QObject *parent)
++ : Notification(parent),
++ m_source(source)
++{
++}
++
++DBusNotification::~DBusNotification()
++{
++ emit notificationDeleted(m_source);
++}
++
++void DBusNotification::remove()
++{
++ emit unregisterNotification(m_source);
++ deleteLater();
++}
++
++void DBusNotification::triggerAction(const QString &actionId)
++{
++ emit actionTriggered(m_source, actionId);
++}
++
+diff --git a/plasma/generic/applets/notifications/protocols/notifications/dbusnotification.h b/plasma/generic/applets/notifications/protocols/notifications/dbusnotification.h
+new file mode 100644
+index 0000000..213cc71
+--- /dev/null
++++ b/plasma/generic/applets/notifications/protocols/notifications/dbusnotification.h
+@@ -0,0 +1,54 @@
++/***************************************************************************
++ * dbusnotification.h *
++ * *
++ * Copyright (C) 2008 Jason Stubbs <jasonbstubbs@gmail.com> *
++ * *
++ * This program is free software; you can redistribute it and/or modify *
++ * it under the terms of the GNU General Public License as published by *
++ * the Free Software Foundation; either version 2 of the License, or *
++ * (at your option) any later version. *
++ * *
++ * This program is distributed in the hope that it will be useful, *
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
++ * GNU General Public License for more details. *
++ * *
++ * You should have received a copy of the GNU General Public License *
++ * along with this program; if not, write to the *
++ * Free Software Foundation, Inc., *
++ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA . *
++ ***************************************************************************/
++
++#ifndef DBUSNOTIFICATION_H
++#define DBUSNOTIFICATION_H
++
++#include "../../core/notification.h"
++
++
++
++class DBusNotification : public Notification
++{
++ Q_OBJECT
++
++ friend class DBusNotificationProtocol;
++
++public:
++ DBusNotification(const QString &source, QObject *parent = 0);
++ ~DBusNotification();
++
++public slots:
++ void remove();
++ void triggerAction(const QString &actionId);
++
++signals:
++ void notificationDeleted(const QString &source);
++ void actionTriggered(const QString &source, const QString &actionId);
++ void unregisterNotification(const QString &source);
++
++private:
++ QString m_source;
++};
++
++
++
++#endif
+diff --git a/plasma/generic/applets/notifications/protocols/notifications/dbusnotificationprotocol.cpp b/plasma/generic/applets/notifications/protocols/notifications/dbusnotificationprotocol.cpp
+new file mode 100644
+index 0000000..fd94048
+--- /dev/null
++++ b/plasma/generic/applets/notifications/protocols/notifications/dbusnotificationprotocol.cpp
+@@ -0,0 +1,192 @@
++/***************************************************************************
++ * fdoprotocol.cpp *
++ * *
++ * Copyright (C) 2008 Jason Stubbs <jasonbstubbs@gmail.com> *
++ * *
++ * This program is free software; you can redistribute it and/or modify *
++ * it under the terms of the GNU General Public License as published by *
++ * the Free Software Foundation; either version 2 of the License, or *
++ * (at your option) any later version. *
++ * *
++ * This program is distributed in the hope that it will be useful, *
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
++ * GNU General Public License for more details. *
++ * *
++ * You should have received a copy of the GNU General Public License *
++ * along with this program; if not, write to the *
++ * Free Software Foundation, Inc., *
++ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA . *
++ ***************************************************************************/
++
++#include "dbusnotification.h"
++#include "dbusnotificationprotocol.h"
++
++#include <KConfigGroup>
++#include <KDebug>
++#include <KIcon>
++
++#include <Plasma/DataEngineManager>
++#include <Plasma/Service>
++#include <Plasma/ServiceJob>
++
++
++static const char engineName[] = "notifications";
++
++DBusNotificationProtocol::DBusNotificationProtocol(Manager *parent)
++ : Protocol(parent),
++ m_manager(parent),
++ m_engine(0)
++{
++}
++
++
++DBusNotificationProtocol::~DBusNotificationProtocol()
++{
++ if (m_engine) {
++ Plasma::DataEngineManager::self()->unloadEngine(engineName);
++ }
++}
++
++
++void DBusNotificationProtocol::init()
++{
++ m_engine = Plasma::DataEngineManager::self()->loadEngine(engineName);
++
++ if (!m_engine->isValid()) {
++ m_engine = 0;
++ Plasma::DataEngineManager::self()->unloadEngine(engineName);
++ return;
++ }
++
++ connect(m_engine, SIGNAL(sourceAdded(QString)),
++ this, SLOT(prepareNotification(QString)));
++ connect(m_engine, SIGNAL(sourceRemoved(QString)),
++ this, SLOT(hideNotification(QString)));
++}
++
++
++void DBusNotificationProtocol::prepareNotification(const QString &source)
++{
++ if (m_engine) {
++ m_engine->connectSource(source, this);
++ }
++}
++
++
++void DBusNotificationProtocol::dataUpdated(const QString &source, const Plasma::DataEngine::Data &data)
++{
++ bool isNew = !m_notifications.contains(source);
++
++ if (isNew) {
++ DBusNotification * notification = new DBusNotification(source, this);
++ connect(notification, SIGNAL(unregisterNotification(QString)),
++ this, SLOT(unregisterNotification(QString)));
++ connect(notification, SIGNAL(notificationDeleted(QString)),
++ this, SLOT(notificationDeleted(QString)));
++ connect(notification, SIGNAL(actionTriggered(QString,QString)),
++ this, SLOT(relayAction(QString,QString)));
++ m_notifications[source] = notification;
++ }
++
++ DBusNotification* notification = m_notifications[source];
++ notification->setApplicationName(data.value("appName").toString());
++ notification->setApplicationIcon(KIcon(data.value("appIcon").toString()));
++ notification->setSummary(data.value("summary").toString());
++ notification->setMessage(data.value("body").toString());
++ notification->setTimeout(data.value("expireTimeout").toInt());
++ notification->setUrgency(data.value("urgency").toInt());
++
++ if (data.contains("image")) {
++ QImage image = qvariant_cast<QImage>(data.value("image"));
++ notification->setImage(image);
++ }
++
++ QStringList codedActions = data.value("actions").toStringList();
++ if (codedActions.count() % 2 != 0) {
++ kDebug() << "Invalid actions" << codedActions << "from" << notification->applicationName();
++ codedActions.clear();
++ }
++
++ QHash<QString, QString> actions;
++ QStringList actionOrder;
++
++ while (!codedActions.isEmpty()) {
++ QString actionId = codedActions.takeFirst();
++ QString actionName = codedActions.takeFirst();
++ actions.insert(actionId, actionName);
++ actionOrder.append(actionId);
++ }
++
++ notification->setActions(actions);
++ notification->setActionOrder(actionOrder);
++
++ if (isNew) {
++ emit notificationCreated(notification);
++ } else {
++ emit notification->changed(notification);
++ }
++}
++
++
++void DBusNotificationProtocol::relayAction(const QString &source, const QString &actionId)
++{
++ if (!m_engine) {
++ return;
++ }
++
++ Plasma::Service *service = m_engine->serviceForSource(source);
++ KConfigGroup op = service->operationDescription("invokeAction");
++
++ if (op.isValid()) {
++ op.writeEntry("actionId", actionId);
++ KJob *job = service->startOperationCall(op);
++ connect(job, SIGNAL(finished(KJob*)), service, SLOT(deleteLater()));
++ } else {
++ delete service;
++ kDebug() << "invalid operation";
++ }
++}
++
++void DBusNotificationProtocol::unregisterNotification(const QString &source)
++{
++ if (!m_engine) {
++ return;
++ }
++
++ Plasma::Service *service = m_engine->serviceForSource(source);
++ KConfigGroup op = service->operationDescription("userClosed");
++ KJob *job = service->startOperationCall(op);
++ connect(job, SIGNAL(finished(KJob*)), service, SLOT(deleteLater()));
++}
++
++void DBusNotificationProtocol::hideNotification(const QString &source)
++{
++ if (m_notifications.contains(source)) {
++ m_notifications.value(source)->hide();
++ }
++}
++
++void DBusNotificationProtocol::removeNotification(const QString &source)
++{
++ if (m_notifications.contains(source)) {
++ m_notifications.take(source)->destroy();
++ }
++}
++
++void DBusNotificationProtocol::notificationDeleted(const QString &source)
++{
++ if (!m_engine) {
++ return;
++ }
++
++ Plasma::Service *service = m_engine->serviceForSource(source);
++ KConfigGroup op = service->operationDescription("userClosed");
++ KJob *job = service->startOperationCall(op);
++ connect(job, SIGNAL(finished(KJob*)), service, SLOT(deleteLater()));
++
++ m_notifications.remove(source);
++}
++
++
++#include "dbusnotificationprotocol.moc"
+diff --git a/plasma/generic/applets/notifications/protocols/notifications/dbusnotificationprotocol.h b/plasma/generic/applets/notifications/protocols/notifications/dbusnotificationprotocol.h
+new file mode 100644
+index 0000000..e52dfd9
+--- /dev/null
++++ b/plasma/generic/applets/notifications/protocols/notifications/dbusnotificationprotocol.h
+@@ -0,0 +1,62 @@
++/***************************************************************************
++ * dbusnotificationprotocol.h *
++ * *
++ * Copyright (C) 2008 Jason Stubbs <jasonbstubbs@gmail.com> *
++ * Copyright (C) 2008 Dmitry Suzdalev <dimsuz@gmail.com> *
++ * *
++ * This program is free software; you can redistribute it and/or modify *
++ * it under the terms of the GNU General Public License as published by *
++ * the Free Software Foundation; either version 2 of the License, or *
++ * (at your option) any later version. *
++ * *
++ * This program is distributed in the hope that it will be useful, *
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
++ * GNU General Public License for more details. *
++ * *
++ * You should have received a copy of the GNU General Public License *
++ * along with this program; if not, write to the *
++ * Free Software Foundation, Inc., *
++ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA . *
++ ***************************************************************************/
++
++#ifndef DBUSNOTIFICATIONPROTOCOL_H
++#define DBUSNOTIFICATIONPROTOCOL_H
++
++#include "../../core/protocol.h"
++#include "../../core/notificationsmanager.h"
++
++#include <QHash>
++
++#include <Plasma/DataEngine>
++
++
++class DBusNotification;
++
++class DBusNotificationProtocol : public Protocol
++{
++ Q_OBJECT
++
++public:
++ DBusNotificationProtocol(Manager *parent);
++ ~DBusNotificationProtocol();
++ void init();
++
++private slots:
++ void prepareNotification(const QString &source);
++ void dataUpdated(const QString &source, const Plasma::DataEngine::Data &data);
++ void removeNotification(const QString &source);
++ void notificationDeleted(const QString &source);
++ void relayAction(const QString &source, const QString &actionId);
++ void unregisterNotification(const QString&);
++ void hideNotification(const QString &source);
++
++private:
++ Manager *m_manager;
++ Plasma::DataEngine *m_engine;
++ QHash<QString, DBusNotification*> m_notifications;
++};
++
++
++
++#endif
+diff --git a/plasma/generic/applets/notifications/ui/busywidget.cpp b/plasma/generic/applets/notifications/ui/busywidget.cpp
+new file mode 100644
+index 0000000..6bce0d7
+--- /dev/null
++++ b/plasma/generic/applets/notifications/ui/busywidget.cpp
+@@ -0,0 +1,352 @@
++/***************************************************************************
++ * Copyright (C) 2008, 2009 Rob Scheepmaker <r.scheepmaker@student.utwente.nl> *
++ * Copyright (C) 2010 Marco Martin <notmart@gmail.com> *
++ * *
++ * This program is free software; you can redistribute it and/or modify *
++ * it under the terms of the GNU General Public License as published by *
++ * the Free Software Foundation; either version 2 of the License, or *
++ * (at your option) any later version. *
++ * *
++ * This program is distributed in the hope that it will be useful, *
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
++ * GNU General Public License for more details. *
++ * *
++ * You should have received a copy of the GNU General Public License *
++ * along with this program; if not, write to the *
++ * Free Software Foundation, Inc., *
++ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA . *
++ ***************************************************************************/
++
++#include "busywidget.h"
++#include <fixx11h.h>
++
++#include <QtGui/QPainter>
++#include <QtGui/QTextOption>
++#include <QtGui/QStyleOptionGraphicsItem>
++#include <QtGui/QWidget> // QWIDGETSIZE_MAX
++#include <QSequentialAnimationGroup>
++
++#include <plasma/extender.h>
++#include <plasma/extenderitem.h>
++#include <plasma/extendergroup.h>
++#include <plasma/popupapplet.h>
++#include <plasma/tooltipmanager.h>
++#include <plasma/theme.h>
++#include <Plasma/Animation>
++#include <Plasma/Animator>
++
++#include <KIcon>
++#include <KGlobalSettings>
++
++#include "../core/notificationsmanager.h"
++#include "../core/job.h"
++#include "../core/notification.h"
++#include "../core/completedjobnotification.h"
++
++
++
++BusyWidget::BusyWidget(Plasma::PopupApplet *parent, const Manager *manager)
++ : Plasma::BusyWidget(parent),
++ m_icon("dialog-information"),
++ m_state(Empty),
++ m_svg(new Plasma::Svg(this)),
++ m_systray(parent),
++ m_manager(manager),
++ m_total(0),
++ m_suppressToolTips(false)
++{
++ setAcceptsHoverEvents(true);
++ m_svg->setImagePath("icons/notification");
++ m_svg->setContainsMultipleImages(true);
++ setRunning(false);
++
++ m_fadeInAnimation = Plasma::Animator::create(Plasma::Animator::PixmapTransitionAnimation);
++ m_fadeInAnimation->setTargetWidget(this);
++ m_fadeInAnimation->setProperty("duration", 1000);
++ m_fadeInAnimation->setProperty("targetPixmap", m_svg->pixmap("notification-active"));
++
++ m_fadeOutAnimation = Plasma::Animator::create(Plasma::Animator::PixmapTransitionAnimation);
++ m_fadeOutAnimation->setTargetWidget(this);
++ m_fadeOutAnimation->setProperty("duration", 1000);
++ m_fadeOutAnimation->setProperty("startPixmap", m_svg->pixmap("notification-active"));
++
++
++ m_fadeGroup = new QSequentialAnimationGroup(this);
++ m_fadeGroup->addAnimation(m_fadeInAnimation);
++ m_fadeGroup->addAnimation(m_fadeOutAnimation);
++
++ connect(manager, SIGNAL(notificationAdded(Notification*)),
++ this, SLOT(updateTask()));
++ connect(manager, SIGNAL(notificationRemoved(Notification*)),
++ this, SLOT(updateTask()));
++ connect(manager, SIGNAL(notificationChanged(Notification*)),
++ this, SLOT(updateTask()));
++ connect(manager, SIGNAL(notificationExpired(Notification*)),
++ this, SLOT(updateTask()));
++ connect(manager, SIGNAL(jobAdded(Job*)),
++ this, SLOT(updateTask()));
++ connect(manager, SIGNAL(jobRemoved(Job*)),
++ this, SLOT(updateTask()));
++ connect(manager, SIGNAL(jobStateChanged(Job*)),
++ this, SLOT(updateTask()));
++
++ Plasma::Extender *extender = qobject_cast<Plasma::Extender *>(m_systray->graphicsWidget());
++ if (extender) {
++ connect(extender, SIGNAL(itemDetached(Plasma::ExtenderItem*)),
++ this, SLOT(updateTask()));
++ }
++
++ Plasma::ToolTipManager::self()->registerWidget(this);
++ updateTask();
++}
++
++void BusyWidget::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
++{
++ QRectF iconRect(0, 0, qMin(size().width(), size().height()), qMin(size().width(), size().height()));
++ iconRect.moveCenter(boundingRect().center());
++
++ if (m_state == Running) {
++ const int arcStart = 90*16;
++ const int arcEnd = -(360*(qreal)m_manager->jobTotals()->percentage()/100)*16;
++
++ Plasma::BusyWidget::paint(painter, option, widget);
++
++ //kDebug() << arcStart << arcEnd;
++
++ QPixmap activePixmap(iconRect.size().toSize());
++ activePixmap.fill(Qt::transparent);
++ QPixmap inActivePixmap(iconRect.size().toSize());
++ inActivePixmap.fill(Qt::transparent);
++ QRect pieRect(QPoint(0, 0), activePixmap.size()*2);
++ pieRect.moveCenter(activePixmap.rect().center());
++
++ QPainter p(&activePixmap);
++ p.setPen(Qt::NoPen);
++ p.setBrush(Qt::black);
++ p.setCompositionMode(QPainter::CompositionMode_Source);
++ p.drawPie(pieRect, arcStart, arcEnd);
++ p.setCompositionMode(QPainter::CompositionMode_SourceIn);
++ m_svg->paint(&p, QRectF(QPointF(0, 0), iconRect.size()), "notification-progress-active");
++ p.end();
++
++ p.begin(&inActivePixmap);
++ p.setPen(Qt::NoPen);
++ p.setBrush(Qt::black);
++ p.setCompositionMode(QPainter::CompositionMode_Source);
++ p.drawPie(pieRect, arcStart, (360*16)+arcEnd);
++ p.setCompositionMode(QPainter::CompositionMode_SourceIn);
++ m_svg->paint(&p, QRectF(QPointF(0, 0), iconRect.size()), "notification-progress-inactive");
++ p.end();
++
++ painter->drawPixmap(iconRect.topLeft().toPoint(), activePixmap);
++ painter->drawPixmap(iconRect.topLeft().toPoint(), inActivePixmap);
++
++ Plasma::BusyWidget::paint(painter, option, widget);
++
++ } else if (m_state == Empty && m_manager->notifications().count() > 0) {
++ m_svg->paint(painter, iconRect, "notification-inactive");
++ } else if (m_state == Empty && m_manager->notifications().count() == 0) {
++ m_svg->paint(painter, iconRect, "notification-disabled");
++ } else {
++ // m_state == Info
++ m_svg->paint(painter, iconRect, "notification-empty");
++ QFont font(KGlobalSettings::smallestReadableFont());
++ painter->setFont(font);
++ QRectF r = rect();
++
++ painter->setPen(Plasma::Theme::defaultTheme()->color(Plasma::Theme::TextColor));
++
++ const QFontMetrics fm(font);
++ const QSize textSize = fm.boundingRect(label()).size();
++ const bool textFits = textSize.width() <= r.width() && textSize.height() <= r.height();
++ if (m_svg && m_svg->hasElement(expanderElement())) {
++ QSizeF arrowSize(m_svg->elementSize(expanderElement()));
++ QRectF arrowRect(r.center() - QPointF(arrowSize.width() / 2, arrowSize.height() + fm.xHeight() / 2), arrowSize);
++ m_svg->paint(painter, arrowRect, expanderElement());
++
++ r.setTop(arrowRect.bottom());
++
++ if (textFits) {
++ painter->drawText(r, Qt::AlignHCenter|Qt::AlignTop, label());
++ }
++ } else if (textFits) {
++ painter->drawText(r, Qt::AlignCenter, label());
++ }
++ }
++
++ if (m_fadeInAnimation->state() == QAbstractAnimation::Running) {
++ QPixmap pix = m_fadeInAnimation->property("currentPixmap").value<QPixmap>();
++ painter->drawPixmap(iconRect, pix, pix.rect());
++ } else if (m_fadeOutAnimation->state() == QAbstractAnimation::Running) {
++ QPixmap pix = m_fadeOutAnimation->property("currentPixmap").value<QPixmap>();
++ painter->drawPixmap(iconRect, pix, pix.rect());
++ }
++}
++
++void BusyWidget::resizeEvent(QGraphicsSceneResizeEvent *)
++{
++ //regenerate pixmaps
++ m_svg->resize(contentsRect().size());
++ m_fadeInAnimation->setProperty("targetPixmap", m_svg->pixmap("notification-active"));
++ m_fadeOutAnimation->setProperty("startPixmap", m_svg->pixmap("notification-active"));
++ m_svg->resize();
++}
++
++void BusyWidget::setState(State state)
++{
++ if (m_state == state) {
++ return;
++ }
++
++ m_state = state;
++ setRunning(m_state == Running);
++ update();
++}
++
++QString BusyWidget::expanderElement() const
++{
++ switch (m_systray->location()) {
++ case Plasma::TopEdge:
++ return "expander-top";
++ case Plasma::RightEdge:
++ return "expander-right";
++ case Plasma::LeftEdge:
++ return "expander-left";
++ case Plasma::BottomEdge:
++ default:
++ return "expander-bottom";
++ }
++}
++
++void BusyWidget::getJobCounts(int &runningJobs, int &pausedJobs, int &completedJobs, int &jobSpeed)
++{
++ runningJobs = pausedJobs = completedJobs = jobSpeed = 0;
++ foreach (const Job *job, m_manager->jobs()) {
++ switch (job->state()) {
++ case Job::Running:
++ jobSpeed += job->numericSpeed();
++ ++runningJobs;
++ break;
++ case Job::Suspended:
++ ++pausedJobs;
++ break;
++ default:
++ break;
++ }
++ }
++
++}
++
++void BusyWidget::updateTask()
++{
++ int runningJobs, pausedJobs, completedJobs, jobSpeed;
++ getJobCounts(runningJobs, pausedJobs, completedJobs, jobSpeed);
++
++ int total = m_manager->jobs().count();
++ int activeNotifications = 0;
++ bool hasOldNotifications = false;
++
++ foreach (Notification *notification, m_manager->notifications()) {
++ if (qobject_cast<CompletedJobNotification *>(notification)) {
++ ++completedJobs;
++ } else if (notification->isExpired()) {
++ hasOldNotifications = true;
++ } else {
++ ++activeNotifications;
++ }
++ }
++
++ total += completedJobs + activeNotifications;
++
++ if (total + m_manager->notifications().count() < 0) {
++ m_systray->hidePopup();
++ }
++
++ if (total > m_total) {
++ m_fadeGroup->start();
++ }
++
++ m_total = total;
++
++ if (activeNotifications > 0) {
++ m_systray->setStatus(Plasma::NeedsAttentionStatus);
++ } else if (m_total > 0 || hasOldNotifications) {
++ m_systray->setStatus(Plasma::ActiveStatus);
++ } else {
++ m_systray->setStatus(Plasma::PassiveStatus);
++ }
++
++ if (!total) {
++ setState(BusyWidget::Empty);
++ setLabel(QString());
++ } else if (runningJobs) {
++ setState(BusyWidget::Running);
++ setLabel(QString("%1").arg(QString::number(total)));
++ } else {
++ setState(BusyWidget::Info);
++ setLabel(QString::number(total));
++ }
++
++ if (Plasma::ToolTipManager::self()->isVisible(this)) {
++ toolTipAboutToShow();
++ }
++}
++
++void BusyWidget::suppressToolTips(bool suppress)
++{
++ m_suppressToolTips = suppress;
++}
++
++void BusyWidget::toolTipAboutToShow()
++{
++ if (m_suppressToolTips) {
++ Plasma::ToolTipManager::self()->setContent(this, Plasma::ToolTipContent());
++ return;
++ }
++
++ int runningJobs, pausedJobs, completedJobs, jobSpeed;
++ getJobCounts(runningJobs, pausedJobs, completedJobs, jobSpeed);
++
++ //make a nice plasma tooltip
++ QString tooltipContent;
++ if (runningJobs > 0) {
++ tooltipContent += i18ncp("Number of jobs and the speed at which they are downloading",
++ "%1 running job (%2/s)", "%1 running jobs (%2/s)", runningJobs,
++ KGlobal::locale()->formatByteSize(jobSpeed));
++ if (pausedJobs > 0 || completedJobs > 0 || !m_manager->notifications().isEmpty()) {
++ tooltipContent += "<br>";
++ }
++ }
++
++ if (pausedJobs > 0) {
++ tooltipContent += i18np("%1 suspended job", "%1 suspended jobs", pausedJobs);
++ if (completedJobs > 0 || !m_manager->notifications().isEmpty()) {
++ tooltipContent += "<br>";
++ }
++ }
++
++ if (completedJobs > 0) {
++ tooltipContent += i18np("%1 completed job", "%1 completed jobs", completedJobs);
++ if (!m_manager->notifications().isEmpty()) {
++ tooltipContent += "<br>";
++ }
++ }
++
++ if (!m_manager->notifications().isEmpty()) {
++ tooltipContent += i18np("%1 notification", "%1 notifications",
++ m_manager->notifications().count());
++ }
++
++ if (tooltipContent.isEmpty()) {
++ tooltipContent = i18n("No active jobs or notifications");
++ }
++
++ Plasma::ToolTipContent data(i18n("Notifications and jobs"),
++ tooltipContent,
++ KIcon("help-about"));
++ Plasma::ToolTipManager::self()->setContent(this, data);
++}
++
++
++#include "busywidget.moc"
+diff --git a/plasma/generic/applets/notifications/ui/busywidget.h b/plasma/generic/applets/notifications/ui/busywidget.h
+new file mode 100644
+index 0000000..ae5f994
+--- /dev/null
++++ b/plasma/generic/applets/notifications/ui/busywidget.h
+@@ -0,0 +1,80 @@
++/***************************************************************************
++ * Copyright (C) 2008 Rob Scheepmaker <r.scheepmaker@student.utwente.nl> *
++ * Copyright (C) 2010 Marco Martin <notmart@gmail.com> *
++ * *
++ * This program is free software; you can redistribute it and/or modify *
++ * it under the terms of the GNU General Public License as published by *
++ * the Free Software Foundation; either version 2 of the License, or *
++ * (at your option) any later version. *
++ * *
++ * This program is distributed in the hope that it will be useful, *
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
++ * GNU General Public License for more details. *
++ * *
++ * You should have received a copy of the GNU General Public License *
++ * along with this program; if not, write to the *
++ * Free Software Foundation, Inc., *
++ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA . *
++ ***************************************************************************/
++
++#ifndef NOTIFICATIONBUSYWIDGET_H
++#define NOTIFICATIONBUSYWIDGET_H
++
++#include <KIcon>
++
++#include <Plasma/BusyWidget>
++
++class QStyleOptionGraphicsItem;
++class QSequentialAnimationGroup;
++
++namespace Plasma
++{
++ class Animation;
++ class Extender;
++ class PopupApplet;
++ class Svg;
++}
++
++
++class Manager;
++
++class BusyWidget : public Plasma::BusyWidget
++{
++ Q_OBJECT
++
++public:
++ enum State { Empty, Info, Running };
++
++ BusyWidget(Plasma::PopupApplet *parent, const Manager *manager);
++ void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget = 0);
++ void setState(State state);
++ void suppressToolTips(bool suppress);
++
++public slots:
++ void toolTipAboutToShow();
++
++protected:
++ void resizeEvent(QGraphicsSceneResizeEvent *event);
++
++protected slots:
++ void updateTask();
++
++private:
++ QString expanderElement() const;
++ void getJobCounts(int &runningJobs, int &pausedJobs, int &completedJobs, int &jobSpeed);
++
++ KIcon m_icon;
++ State m_state;
++ Plasma::Svg *m_svg;
++ Plasma::PopupApplet *m_systray;
++ const Manager *m_manager;
++ Plasma::Animation *m_fadeInAnimation;
++ Plasma::Animation *m_fadeOutAnimation;
++ QSequentialAnimationGroup *m_fadeGroup;
++ int m_total;
++ bool m_suppressToolTips;
++};
++
++
++#endif
+diff --git a/plasma/generic/applets/notifications/ui/jobtotalswidget.cpp b/plasma/generic/applets/notifications/ui/jobtotalswidget.cpp
+new file mode 100644
+index 0000000..6552ed3
+--- /dev/null
++++ b/plasma/generic/applets/notifications/ui/jobtotalswidget.cpp
+@@ -0,0 +1,94 @@
++/***************************************************************************
++ * Copyright 2009 by Rob Scheepmaker <r.scheepmaker@student.utwente.nl> *
++ * *
++ * This program is free software; you can redistribute it and/or modify *
++ * it under the terms of the GNU General Public License as published by *
++ * the Free Software Foundation; either version 2 of the License, or *
++ * (at your option) any later version. *
++ * *
++ * This program is distributed in the hope that it will be useful, *
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
++ * GNU General Public License for more details. *
++ * *
++ * You should have received a copy of the GNU General Public License *
++ * along with this program; if not, write to the *
++ * Free Software Foundation, Inc., *
++ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA . *
++ ***************************************************************************/
++
++#include "jobtotalswidget.h"
++#include "../core/job.h"
++
++#include <Plasma/ExtenderItem>
++#include <Plasma/Meter>
++
++static const int UPDATE_TIMER_INTERVAL = 200;
++
++
++JobTotalsWidget::JobTotalsWidget(Job *job, QGraphicsWidget *parent)
++ : Meter(parent),
++ m_job(job),
++ m_updateTimerId(0)
++{
++ m_extenderGroup = qobject_cast<Plasma::ExtenderGroup *>(parent),
++
++ setSvg("widgets/bar_meter_horizontal");
++ setMeterType(Plasma::Meter::BarMeterHorizontal);
++
++ setMinimumWidth(350);
++ setMinimumHeight(16);
++ setMaximumHeight(16);
++ setMaximum(100);
++ setValue(0);
++
++ if (m_job) {
++ connect(m_job, SIGNAL(changed(Job*)),
++ this, SLOT(scheduleJobUpdate()));
++
++ updateJob();
++ }
++}
++
++JobTotalsWidget::~JobTotalsWidget()
++{
++}
++
++void JobTotalsWidget::scheduleJobUpdate()
++{
++ if (!m_updateTimerId) {
++ m_updateTimerId = startTimer(UPDATE_TIMER_INTERVAL);
++ }
++}
++
++void JobTotalsWidget::timerEvent(QTimerEvent *event)
++{
++ if (event->timerId() == m_updateTimerId) {
++ killTimer(m_updateTimerId);
++ m_updateTimerId = 0;
++ updateJob();
++ } else {
++ Meter::timerEvent(event);
++ }
++}
++
++void JobTotalsWidget::updateJob()
++{
++ setValue(m_job->percentage());
++
++ if (m_extenderGroup) {
++ if (m_extenderGroup->items().count() > 1 || m_extenderGroup->isGroupCollapsed()) {
++ m_extenderGroup->setTitle(m_job->message());
++ } else {
++ m_extenderGroup->setTitle(i18nc("Generic title for the job transfer popup", "Jobs"));
++ }
++ m_extenderGroup->setIcon(m_job->applicationIconName());
++ } else {
++ setLabelAlignment(0, Qt::AlignLeft|Qt::AlignVCenter);
++ setLabel(0, m_job->message());
++ }
++}
++
++
++#include "jobtotalswidget.moc"
++
+diff --git a/plasma/generic/applets/notifications/ui/jobtotalswidget.h b/plasma/generic/applets/notifications/ui/jobtotalswidget.h
+new file mode 100644
+index 0000000..4f07c86
+--- /dev/null
++++ b/plasma/generic/applets/notifications/ui/jobtotalswidget.h
+@@ -0,0 +1,65 @@
++/***************************************************************************
++ * Copyright 2009 by Rob Scheepmaker <r.scheepmaker@student.utwente.nl *
++ * *
++ * This program is free software; you can redistribute it and/or modify *
++ * it under the terms of the GNU General Public License as published by *
++ * the Free Software Foundation; either version 2 of the License, or *
++ * (at your option) any later version. *
++ * *
++ * This program is distributed in the hope that it will be useful, *
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
++ * GNU General Public License for more details. *
++ * *
++ * You should have received a copy of the GNU General Public License *
++ * along with this program; if not, write to the *
++ * Free Software Foundation, Inc., *
++ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA . *
++ ***************************************************************************/
++
++#ifndef JOBTOTALSWIDGET_H
++#define JOBTOTALSWIDGET_H
++
++#include "../core/job.h"
++
++
++#include <QGraphicsWidget>
++
++#include <plasma/widgets/meter.h>
++#include <Plasma/Service>
++#include <Plasma/ExtenderGroup>
++#include <plasma/dataengine.h>
++
++namespace Plasma
++{
++ class ExtenderItem;
++ class Meter;
++} // namespace Plasma
++
++
++class Job;
++
++class JobTotalsWidget : public Plasma::Meter
++{
++ Q_OBJECT
++
++ public:
++ explicit JobTotalsWidget(Job *job, QGraphicsWidget *parent);
++ ~JobTotalsWidget();
++
++ protected:
++ void timerEvent(QTimerEvent *event);
++
++ private Q_SLOTS:
++ void scheduleJobUpdate();
++
++ private:
++ void updateJob();
++
++ Plasma::ExtenderGroup *m_extenderGroup;
++ Job *m_job;
++ int m_updateTimerId;
++};
++
++
++#endif
+diff --git a/plasma/generic/applets/notifications/ui/jobwidget.cpp b/plasma/generic/applets/notifications/ui/jobwidget.cpp
+new file mode 100644
+index 0000000..06096fd
+--- /dev/null
++++ b/plasma/generic/applets/notifications/ui/jobwidget.cpp
+@@ -0,0 +1,435 @@
++/***************************************************************************
++ * Copyright 2008 by Rob Scheepmaker <r.scheepmaker@student.utwente.nl> *
++ * *
++ * This program is free software; you can redistribute it and/or modify *
++ * it under the terms of the GNU General Public License as published by *
++ * the Free Software Foundation; either version 2 of the License, or *
++ * (at your option) any later version. *
++ * *
++ * This program is distributed in the hope that it will be useful, *
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
++ * GNU General Public License for more details. *
++ * *
++ * You should have received a copy of the GNU General Public License *
++ * along with this program; if not, write to the *
++ * Free Software Foundation, Inc., *
++ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA . *
++ ***************************************************************************/
++
++#include "jobwidget.h"
++#include "../core/job.h"
++
++
++#include <QFont>
++#include <QAction>
++#include <QGraphicsGridLayout>
++#include <QLabel>
++
++#include <KIconLoader>
++
++#include <Plasma/DataEngine>
++#include <Plasma/Extender>
++#include <Plasma/ExtenderItem>
++#include <Plasma/Label>
++#include <Plasma/Meter>
++#include <Plasma/PopupApplet>
++#include <Plasma/PushButton>
++#include <Plasma/Service>
++#include <Plasma/SignalPlotter>
++#include <Plasma/IconWidget>
++#include <Plasma/Theme>
++#include <Plasma/ToolTipManager>
++
++static const int UPDATE_INTERVAL = 200;
++
++JobWidget::JobWidget(Job *job, Plasma::ExtenderItem *parent)
++ : QGraphicsWidget(parent),
++ m_extenderItem(parent),
++ m_job(job),
++ m_updateTimerId(0),
++ m_extenderItemDestroyed(false)
++{
++ Q_ASSERT(m_extenderItem);
++
++ m_meter = new Plasma::Meter(this);
++ m_meter->setSvg("widgets/bar_meter_horizontal");
++ m_meter->setMeterType(Plasma::Meter::BarMeterHorizontal);
++ m_meter->setMaximumHeight(16);
++ m_meter->setMaximum(100);
++ m_meter->setValue(0);
++
++ m_plotter = new Plasma::SignalPlotter(this);
++ m_plotter->setUseAutoRange(true);
++ m_plotter->setShowVerticalLines(false);
++ m_plotter->setUnit(i18n("KiB/s"));
++ m_plotter->addPlot(Plasma::Theme::defaultTheme()->color(Plasma::Theme::HighlightColor));
++
++ m_fromNameLabel = new Plasma::Label(this);
++ m_fromLabel = new Plasma::Label(this);
++ m_toNameLabel = new Plasma::Label(this);
++ m_toLabel = new Plasma::Label(this);
++ m_totalBytesLabel = new Plasma::Label(this);
++ m_dirCountLabel = new Plasma::Label(this);
++ m_fileCountLabel = new Plasma::Label(this);
++ m_eta = new Plasma::Label(this);
++ m_details = new Plasma::IconWidget(this);
++ m_details->setSvg("widgets/action-overlays", "add-normal");
++ m_details->setMaximumSize(KIconLoader::SizeSmallMedium, KIconLoader::SizeSmallMedium);
++ m_details->setMinimumSize(KIconLoader::SizeSmallMedium, KIconLoader::SizeSmallMedium);
++
++ m_totalBytesLabel->setVisible(false);
++ m_dirCountLabel->setVisible(false);
++ m_fileCountLabel->setVisible(false);
++ m_plotter->setVisible(false);
++
++ m_fromNameLabel->setAlignment(Qt::AlignRight);
++ m_fromLabel->setAlignment(Qt::AlignLeft);
++ m_toNameLabel->setAlignment(Qt::AlignRight);
++ m_toLabel->setAlignment(Qt::AlignLeft);
++ m_totalBytesLabel->setAlignment(Qt::AlignRight);
++ m_dirCountLabel->setAlignment(Qt::AlignRight);
++ m_fileCountLabel->setAlignment(Qt::AlignRight);
++ m_eta->setAlignment(Qt::AlignRight);
++
++ m_fromLabel->nativeWidget()->setWordWrap(false);
++ m_toLabel->nativeWidget()->setWordWrap(false);
++ m_dirCountLabel->nativeWidget()->setWordWrap(false);
++ m_fileCountLabel->nativeWidget()->setWordWrap(false);
++ m_totalBytesLabel->nativeWidget()->setWordWrap(false);
++ m_eta->nativeWidget()->setWordWrap(false);
++
++ m_layout = new QGraphicsGridLayout(this);
++ m_layout->addItem(m_fromNameLabel, 0, 0);
++ m_layout->addItem(m_fromLabel, 0, 1);
++ m_layout->addItem(m_toNameLabel, 1, 0);
++ m_layout->addItem(m_toLabel, 1, 1);
++ m_layout->addItem(m_eta, 2, 1);
++ m_layout->addItem(m_details, 3, 0, Qt::AlignVCenter|Qt::AlignRight);
++ m_layout->addItem(m_meter, 3, 1, Qt::AlignCenter);
++
++ setMinimumWidth(350);
++
++ if (m_job.data()) {
++ m_details->setToolTip(i18n("More"));
++ m_details->setSvg("widgets/action-overlays", "add-normal");
++
++ connect(m_job.data(), SIGNAL(stateChanged(Job*)), this, SLOT(updateJobState()));
++ connect(m_job.data(), SIGNAL(destroyed(Job*)), this, SLOT(destroyExtenderItem()));
++ connect(m_details, SIGNAL(clicked()), this, SLOT(detailsClicked()));
++
++ //the suspend action
++ QAction *suspendAction = new QAction(m_extenderItem);
++ suspendAction->setIcon(KIcon("media-playback-pause"));
++ suspendAction->setEnabled(true);
++ suspendAction->setVisible(false);
++ suspendAction->setToolTip(i18n("Pause job"));
++ m_extenderItem->addAction("suspend", suspendAction);
++ connect(suspendAction, SIGNAL(triggered()), m_job.data(), SLOT(suspend()));
++
++ //the resume action
++ QAction *resumeAction = new QAction(m_extenderItem);
++ resumeAction->setIcon(KIcon("media-playback-start"));
++ resumeAction->setEnabled(true);
++ resumeAction->setVisible(false);
++ resumeAction->setToolTip(i18n("Resume job"));
++ m_extenderItem->addAction("resume", resumeAction);
++ connect(resumeAction, SIGNAL(triggered()), m_job.data(), SLOT(resume()));
++
++ //the friendly stop action
++ QAction *stopAction = new QAction(m_extenderItem);
++ stopAction->setIcon(KIcon("media-playback-stop"));
++ stopAction->setEnabled(true);
++ stopAction->setVisible(true);
++ stopAction->setToolTip(i18n("Cancel job"));
++ m_extenderItem->addAction("stop", stopAction);
++ connect(stopAction, SIGNAL(triggered()), m_job.data(), SLOT(stop()));
++
++ updateJob();
++ updateJobState(); // make sure to set the title
++ } else {
++ m_extenderItem->showCloseButton();
++
++ labelName0 = m_extenderItem->config().readEntry("labelName0", "");
++ label0= m_extenderItem->config().readEntry("label0", "");
++ labelName1 = m_extenderItem->config().readEntry("labelName1", "");
++ label1 = m_extenderItem->config().readEntry("label1", "");
++
++ updateLabels();
++ }
++}
++
++JobWidget::~JobWidget()
++{
++}
++
++void JobWidget::destroyExtenderItem()
++{
++ m_extenderItem->destroy();
++ m_extenderItemDestroyed = true;
++}
++
++void JobWidget::scheduleUpdateJob()
++{
++ if (m_extenderItemDestroyed) {
++ return;
++ }
++
++ if (!m_updateTimerId) {
++ m_updateTimerId = startTimer(UPDATE_INTERVAL);
++ }
++}
++
++void JobWidget::updateJobState()
++{
++ if (m_extenderItemDestroyed && m_job.data()) {
++ return;
++ }
++
++ //show the current status in the title.
++ if (!m_job.data()->error().isEmpty()) {
++ m_extenderItem->setTitle(m_job.data()->error());
++ } else if (m_job.data()->state() == Job::Running) {
++ m_extenderItem->setTitle(m_job.data()->message());
++ if (m_job.data()->eta()) {
++ m_eta->setText(i18n("%1 (%2 remaining)", m_job.data()->speed(),
++ KGlobal::locale()->prettyFormatDuration(m_job.data()->eta())));
++ } else {
++ m_eta->setText(QString());
++ }
++ } else if (m_job.data()->state() == Job::Suspended) {
++ m_extenderItem->setTitle(
++ i18nc("%1 is the name of the job, can be things like Copying, deleting, moving",
++ "%1 [Paused]", m_job.data()->message()));
++ m_eta->setText(i18n("Paused"));
++ } else {
++ m_extenderItem->setTitle(
++ i18nc("%1 is the name of the job, can be things like Copying, deleting, moving",
++ "%1 [Finished]", m_job.data()->message()));
++ m_extenderItem->showCloseButton();
++ }
++}
++
++void JobWidget::updateJob()
++{
++ if (m_extenderItemDestroyed || !m_job.data()) {
++ return;
++ }
++
++ m_meter->setValue(m_job.data()->percentage());
++
++ //Update the ETA and job speed (only if running)
++ if (m_job.data()->state() == Job::Running) {
++ if (m_job.data()->eta()) {
++ m_eta->setText(i18n("%1 (%2 remaining)", m_job.data()->speed(),
++ KGlobal::locale()->prettyFormatDuration(m_job.data()->eta())));
++ } else {
++ m_eta->setText(QString());
++ }
++ }
++
++ if (m_job.data()->labels().count() > 0) {
++ labelName0 = m_job.data()->labels().value(0).first;
++ label0 = m_job.data()->labels().value(0).second;
++ }
++ if (m_job.data()->labels().count() > 1) {
++ labelName1 = m_job.data()->labels().value(1).first;
++ label1 = m_job.data()->labels().value(1).second;
++ }
++
++ //TODO: can we write this at some later point?
++ KConfigGroup cg = m_extenderItem->config();
++ cg.writeEntry("labelName0", labelName0);
++ cg.writeEntry("label0", label0);
++ cg.writeEntry("labelName1", labelName1);
++ cg.writeEntry("label1", label1);
++
++ updateLabels();
++
++ //set the correct actions to visible.
++ if (m_extenderItem->action("suspend")) {
++ m_extenderItem->action("suspend")->setVisible(m_job.data()->isSuspendable() &&
++ m_job.data()->state() == Job::Running);
++ }
++
++ if (m_extenderItem->action("resume")) {
++ m_extenderItem->action("resume")->setVisible(m_job.data()->isSuspendable() &&
++ m_job.data()->state() == Job::Suspended);
++ }
++
++ if (m_extenderItem->action("stop")) {
++ m_extenderItem->action("stop")->setVisible(m_job.data()->isKillable() &&
++ m_job.data()->state() != Job::Stopped);
++ }
++
++ QMap<QString, qlonglong> processed = m_job.data()->processedAmounts();
++ QMap<QString, qlonglong> totals = m_job.data()->totalAmounts();
++
++ qlonglong dirs = totals.value("dirs");
++ if (dirs > 1) {
++ m_dirCountLabel->setText(i18np("%2 / 1 folder", "%2 / %1 folders", dirs, processed["dirs"]));
++ m_dirCountLabel->setMaximumHeight(INT_MAX);
++ } else {
++ m_dirCountLabel->setMaximumHeight(0);
++ }
++
++ qlonglong files = totals.value("files");
++ if (files > 1) {
++ m_fileCountLabel->setText(i18np("%2 / 1 file", "%2 / %1 files", files, processed["files"]));
++ m_fileCountLabel->setMaximumHeight(INT_MAX);
++ } else {
++ m_fileCountLabel->setMaximumHeight(0);
++ }
++
++ QList<double> sample;
++ sample << m_job.data()->numericSpeed()/1000;
++ m_plotter->addSample(sample);
++
++ qlonglong total = totals["bytes"];
++ if (total > 0) {
++ QString processedString = KGlobal::locale()->formatByteSize(processed["bytes"]);
++ QString totalsString = KGlobal::locale()->formatByteSize(total);
++ m_totalBytesLabel->setText(QString("%1 / %2").arg(processedString, totalsString));
++ } else {
++ m_details->hide();
++ m_totalBytesLabel->hide();
++ }
++
++ if (m_totalBytesLabel->text().isEmpty() &&
++ m_dirCountLabel->text().isEmpty() &&
++ m_fileCountLabel->text().isEmpty()) {
++ m_details->hide();
++ } else {
++ m_details->show();
++ }
++
++ m_extenderItem->setIcon(m_job.data()->applicationIconName());
++}
++
++void JobWidget::showEvent(QShowEvent *)
++{
++ if (!m_job.data()) {
++ return;
++ }
++
++ Plasma::PopupApplet *applet = qobject_cast<Plasma::PopupApplet *>(m_extenderItem->extender()->applet());
++ if (applet && applet->isPopupShowing()) {
++ updateJob();
++ disconnect(m_job.data(), SIGNAL(changed(Job*)), this, SLOT(scheduleUpdateJob()));
++ connect(m_job.data(), SIGNAL(changed(Job*)), this, SLOT(scheduleUpdateJob()));
++ return;
++ }
++}
++
++void JobWidget::hideEvent(QHideEvent *)
++{
++ if (!m_job.data()) {
++ return;
++ }
++
++ disconnect(m_job.data(), SIGNAL(changed(Job*)), this, SLOT(scheduleUpdateJob()));
++}
++
++void JobWidget::poppedUp(bool shown)
++{
++ if (!m_job.data()) {
++ return;
++ }
++
++ disconnect(m_job.data(), SIGNAL(changed(Job*)), this, SLOT(scheduleUpdateJob()));
++
++ if (shown && isVisible()) {
++ updateJob();
++ connect(m_job.data(), SIGNAL(changed(Job*)), this, SLOT(scheduleUpdateJob()));
++ return;
++ }
++}
++
++Job *JobWidget::job() const
++{
++ return m_job.data();
++}
++
++void JobWidget::resizeEvent(QGraphicsSceneResizeEvent *event)
++{
++ Q_UNUSED(event)
++ updateLabels();
++}
++
++void JobWidget::timerEvent(QTimerEvent *event)
++{
++ if (event->timerId() == m_updateTimerId) {
++ killTimer(m_updateTimerId);
++ m_updateTimerId = 0;
++ updateJob();
++ }
++}
++
++void JobWidget::updateLabels()
++{
++ QFontMetricsF fm = m_fromLabel->nativeWidget()->fontMetrics();
++ if (!labelName0.isEmpty()) {
++ m_fromNameLabel->setText(QString("%1: ").arg(labelName0));
++ }
++ if (label0.startsWith(QLatin1String("file://"))) {
++ label0 = KUrl(label0).toLocalFile();
++ }
++
++ const QString shortLabel0(fm.elidedText(label0, Qt::ElideMiddle, m_fromLabel->size().width()));
++ m_fromLabel->setText(shortLabel0);
++
++
++ Plasma::ToolTipContent data;
++
++ if (label0.length() > shortLabel0.length()) {
++ data.setSubText(label0);
++ Plasma::ToolTipManager::self()->setContent(m_fromLabel, data);
++ }
++
++ if (!labelName1.isEmpty()) {
++ m_toNameLabel->setText(QString("%1: ").arg(labelName1));
++ }
++ if (label1.startsWith(QLatin1String("file://"))) {
++ label1 = KUrl(label1).toLocalFile();
++ }
++
++ const QString shortLabel1(fm.elidedText(label1, Qt::ElideMiddle, m_toLabel->size().width()));
++ m_toLabel->setText(shortLabel1);
++
++ if (label1.length() > shortLabel1.length()) {
++ data.setSubText(label1);
++ Plasma::ToolTipManager::self()->setContent(m_toLabel, data);
++ }
++}
++
++void JobWidget::detailsClicked()
++{
++ if (!m_totalBytesLabel->isVisible()) {
++ m_details->setToolTip(i18n("Less"));
++ m_details->setSvg("widgets/action-overlays", "remove-normal");
++ m_totalBytesLabel->setVisible(true);
++ m_dirCountLabel->setVisible(true);
++ m_fileCountLabel->setVisible(true);
++ m_plotter->setVisible(true);
++ m_layout->addItem(m_totalBytesLabel, 4, 1);
++ m_layout->addItem(m_fileCountLabel, 5, 1);
++ m_layout->addItem(m_dirCountLabel, 6, 1);
++ m_layout->addItem(m_plotter, 7, 1);
++ m_extenderItem->setCollapsed(m_extenderItem->isCollapsed());
++ } else {
++ m_details->setToolTip(i18n("More"));
++ m_details->setSvg("widgets/action-overlays", "add-normal");
++ m_totalBytesLabel->setVisible(false);
++ m_dirCountLabel->setVisible(false);
++ m_fileCountLabel->setVisible(false);
++ m_plotter->setVisible(false);
++ for (int i = 0; i < 4; i++) {
++ m_layout->removeAt(m_layout->count() - 1);
++ }
++ m_layout->updateGeometry();
++ m_extenderItem->setCollapsed(m_extenderItem->isCollapsed());
++ }
++}
++
++#include "jobwidget.moc"
++
+diff --git a/plasma/generic/applets/notifications/ui/jobwidget.h b/plasma/generic/applets/notifications/ui/jobwidget.h
+new file mode 100644
+index 0000000..b949bac
+--- /dev/null
++++ b/plasma/generic/applets/notifications/ui/jobwidget.h
+@@ -0,0 +1,102 @@
++/***************************************************************************
++ * Copyright 2008 by Rob Scheepmaker <r.scheepmaker@student.utwente.nl *
++ * *
++ * This program is free software; you can redistribute it and/or modify *
++ * it under the terms of the GNU General Public License as published by *
++ * the Free Software Foundation; either version 2 of the License, or *
++ * (at your option) any later version. *
++ * *
++ * This program is distributed in the hope that it will be useful, *
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
++ * GNU General Public License for more details. *
++ * *
++ * You should have received a copy of the GNU General Public License *
++ * along with this program; if not, write to the *
++ * Free Software Foundation, Inc., *
++ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA . *
++ ***************************************************************************/
++
++#ifndef JOBWIDGET_H
++#define JOBWIDGET_H
++
++#include "../core/job.h"
++
++
++#include <QGraphicsWidget>
++#include <QGraphicsGridLayout>
++
++#include <Plasma/Service>
++#include <Plasma/ExtenderItem>
++#include <plasma/dataengine.h>
++#include <Plasma/PushButton>
++
++namespace Plasma
++{
++ class ExtenderItem;
++ class PushButton;
++ class Label;
++ class Meter;
++ class IconWidget;
++ class SignalPlotter;
++} // namespace Plasma
++
++class Job;
++
++
++class JobWidget : public QGraphicsWidget
++{
++ Q_OBJECT
++
++ public:
++ explicit JobWidget(Job *job, Plasma::ExtenderItem *parent);
++ ~JobWidget();
++
++ void poppedUp(bool shown);
++
++ Job *job() const;
++
++ protected:
++ void resizeEvent(QGraphicsSceneResizeEvent *event);
++ void timerEvent(QTimerEvent *event);
++ void showEvent(QShowEvent *event);
++ void hideEvent(QHideEvent *event);
++
++ private Q_SLOTS:
++ void detailsClicked();
++ void destroyExtenderItem();
++ void scheduleUpdateJob();
++ void updateJobState();
++
++ private:
++ void updateLabels();
++ void updateJob();
++
++ Plasma::ExtenderItem *m_extenderItem;
++ QWeakPointer<Job>m_job;
++
++ Plasma::Meter *m_meter;
++ Plasma::Label *m_fromNameLabel;
++ Plasma::Label *m_fromLabel;
++ Plasma::Label *m_toNameLabel;
++ Plasma::Label *m_toLabel;
++ Plasma::Label *m_totalBytesLabel;
++ Plasma::Label *m_dirCountLabel;
++ Plasma::Label *m_fileCountLabel;
++ Plasma::Label *m_eta;
++ Plasma::IconWidget *m_details;
++ Plasma::SignalPlotter *m_plotter;
++
++ QGraphicsGridLayout *m_layout;
++
++ QString labelName0;
++ QString labelName1;
++ QString label0;
++ QString label1;
++
++ int m_updateTimerId;
++
++ bool m_extenderItemDestroyed;
++};
++
++#endif
+diff --git a/plasma/generic/applets/notifications/ui/notificationgroup.cpp b/plasma/generic/applets/notifications/ui/notificationgroup.cpp
+new file mode 100644
+index 0000000..f27e2c3
+--- /dev/null
++++ b/plasma/generic/applets/notifications/ui/notificationgroup.cpp
+@@ -0,0 +1,239 @@
++/***************************************************************************
++ * notificationgroup.cpp *
++ * Copyright (C) 2010 Marco Martin <notmart@gmail.com> *
++ * *
++ * This program is free software; you can redistribute it and/or modify *
++ * it under the terms of the GNU General Public License as published by *
++ * the Free Software Foundation; either version 2 of the License, or *
++ * (at your option) any later version. *
++ * *
++ * This program is distributed in the hope that it will be useful, *
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
++ * GNU General Public License for more details. *
++ * *
++ * You should have received a copy of the GNU General Public License *
++ * along with this program; if not, write to the *
++ * Free Software Foundation, Inc., *
++ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA . *
++ ***************************************************************************/
++
++#include "notificationgroup.h"
++#include "../core/notification.h"
++#include "notificationwidget.h"
++
++#include <QGraphicsLinearLayout>
++#include <QWidget>
++
++#include <KDebug>
++#include <KIcon>
++#include <KLocale>
++
++#include <Plasma/TabBar>
++#include <Plasma/ExtenderItem>
++
++
++NotificationGroup::NotificationGroup(Plasma::Extender *parent, uint groupId)
++ : Plasma::ExtenderGroup(parent, groupId)
++{
++ setTransient(true);
++ config().writeEntry("type", "notification");
++ setName("notifications");
++ setTitle(i18n("Notifications"));
++ setIcon("dialog-information");
++ showCloseButton();
++
++
++ m_notificationBar = new Plasma::TabBar(this);
++ m_notificationBar->nativeWidget()->setMaximumWidth(400);
++ m_notificationBar->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
++ m_notificationBar->addTab(KIcon("dialog-information"), i18nc("Show all notifications", "All"));
++ connect(m_notificationBar, SIGNAL(currentChanged(int)), this, SLOT(tabSwitched(int)));
++
++
++ QGraphicsWidget *widget = new QGraphicsWidget(this);
++ QGraphicsLinearLayout *tabsLayout = new QGraphicsLinearLayout(Qt::Horizontal, widget);
++ widget->setContentsMargins(0, 4, 0, 0);
++ tabsLayout->setContentsMargins(0, 0, 0, 0);
++ tabsLayout->addStretch();
++ tabsLayout->addItem(m_notificationBar);
++ tabsLayout->addStretch();
++
++ setWidget(widget);
++ setCollapsed(true);
++ setAutoCollapse(false);
++}
++
++NotificationGroup::~NotificationGroup()
++{
++ m_extenderItemsForNotification.clear();
++ m_notificationForExtenderItems.clear();
++ qDeleteAll(m_notifications);
++}
++
++
++void NotificationGroup::addNotification(Notification *notification)
++{
++ connect(notification, SIGNAL(notificationDestroyed(Notification*)), this, SLOT(removeNotification(Notification*)));
++
++ NotificationWidget *notificationWidget = new NotificationWidget(notification, this);
++ notificationWidget->setTitleBarVisible(false);
++ Plasma::ExtenderItem *extenderItem = new ExtenderItem(extender());
++ extenderItem->setGroup(this, QPointF(0,0));
++ extenderItem->setTransient(true);
++ extenderItem->config().writeEntry("type", "notification");
++ extenderItem->setWidget(notificationWidget);
++ extenderItem->setIcon(QIcon());
++ extenderItem->showCloseButton();
++ connect(extenderItem, SIGNAL(destroyed(Plasma::ExtenderItem*)), this, SLOT(extenderItemDestroyed(Plasma::ExtenderItem*)));
++ connect(notificationWidget, SIGNAL(destroyed()), extenderItem, SLOT(deleteLater()));
++
++ if (!notification->summary().isEmpty()) {
++ extenderItem->setTitle(notification->summary());
++ } else {
++ extenderItem->setTitle(i18n("Notification from %1", notification->applicationName()));
++ }
++
++ m_extenderItemsForNotification[notification] = extenderItem;
++ m_notificationForExtenderItems[extenderItem] = notification;
++ m_notifications.append(notification);
++ m_notificationsForApp[notification->applicationName()].insert(notification);
++ m_appForNotification[notification] = notification->applicationName();
++
++ if (!m_currentFilter.isNull() && m_currentFilter != notification->applicationName()) {
++ extenderItem->setMaximumHeight(0);
++ extenderItem->setVisible(false);
++ }
++
++ //adjust tabbar
++ bool found = false;
++ for (int i = 0; i < m_notificationBar->count(); ++i) {
++ if (m_notificationBar->tabText(i) == notification->applicationName()) {
++ found = true;
++ break;
++ }
++ }
++
++ if (!found) {
++ m_notificationBar->addTab(notification->applicationIcon(), notification->applicationName());
++ if (m_notificationBar->count() > 2) {
++ setCollapsed(false);
++ setAutoCollapse(true);
++ }
++ }
++
++ if (items().count() == 1) {
++ //ensure the notifications group is the last item
++ Plasma::ExtenderGroup *jobGroup = extender()->group("jobGroup");
++ if (jobGroup && jobGroup->isVisible()) {
++ if (extender()->appearance() == Plasma::Extender::TopDownStacked) {
++ setExtender(extender(), QPointF(0,0));
++ } else {
++ setExtender(extender(), jobGroup->geometry().bottomLeft());
++ }
++ }
++ }
++
++ notificationWidget->layout()->activate();
++}
++
++void NotificationGroup::extenderItemDestroyed(Plasma::ExtenderItem *object)
++{
++ if (m_extenderItemsForNotification.isEmpty()) {
++ // either we aren't tracking this notification or else we're being deleted
++ return;
++ }
++
++ Notification *n = m_notificationForExtenderItems.value(object);
++
++ if (n) {
++ m_notificationForExtenderItems.remove(object);
++ removeNotification(n);
++ n->deleteLater();
++ }
++}
++
++void NotificationGroup::removeNotification(Notification *notification)
++{
++ if (m_extenderItemsForNotification.isEmpty()) {
++ // either we aren't tracking this notification or else we're being deleted
++ return;
++ }
++
++ Plasma::ExtenderItem *item = m_extenderItemsForNotification.value(notification);
++ if (item) {
++ m_notificationForExtenderItems.remove(item);
++ }
++
++ m_extenderItemsForNotification.remove(notification);
++ m_notifications.removeAll(notification);
++ QString applicationName = m_appForNotification.value(notification);
++
++ if (applicationName.isEmpty()) {
++ return;
++ }
++
++ m_appForNotification.remove(notification);
++
++ if (m_notificationsForApp.contains(applicationName)) {
++ m_notificationsForApp[applicationName].remove(notification);
++ if (m_notificationsForApp[applicationName].isEmpty()) {
++ m_notificationsForApp.remove(applicationName);
++ }
++ }
++
++ //clear tabbar
++ for (int i = 1; i < m_notificationBar->count(); ++i) {
++ if (!m_notificationsForApp.contains(m_notificationBar->tabText(i))) {
++ if (i == m_notificationBar->currentIndex()) {
++ m_notificationBar->setCurrentIndex(0);
++ }
++ m_notificationBar->removeTab(i);
++ //2 tabs means just "all" and a single application, no need to display it
++ if (m_notificationBar->count() <= 2) {
++ setCollapsed(true);
++ setAutoCollapse(false);
++ }
++ }
++ }
++
++ if (m_notifications.count() == 0) {
++ emit scrollerEmpty();
++ return;
++ }
++}
++
++
++void NotificationGroup::filterNotificationsByOwner(const QString &owner)
++{
++ foreach (Notification *notification, m_notifications) {
++ Plasma::ExtenderItem *item = m_extenderItemsForNotification.value(notification);
++
++ if (!item || item->group() != this) {
++ continue;
++ }
++
++ if (owner.isNull() || notification->applicationName() == owner) {
++ item->setMaximumHeight(QWIDGETSIZE_MAX);
++ item->setVisible(true);
++ } else {
++ item->setMaximumHeight(0);
++ item->setVisible(false);
++ }
++ }
++
++ m_currentFilter = owner;
++}
++
++void NotificationGroup::tabSwitched(int index)
++{
++ if (index > 0) {
++ filterNotificationsByOwner(m_notificationBar->tabText(index));
++ } else {
++ filterNotificationsByOwner(QString());
++ }
++}
++
++
++#include "notificationgroup.moc"
++
+diff --git a/plasma/generic/applets/notifications/ui/notificationgroup.h b/plasma/generic/applets/notifications/ui/notificationgroup.h
+new file mode 100644
+index 0000000..ffa2112
+--- /dev/null
++++ b/plasma/generic/applets/notifications/ui/notificationgroup.h
+@@ -0,0 +1,85 @@
++/***************************************************************************
++ * notificationgroup.h *
++ * Copyright (C) 2010 Marco Martin <notmart@gmail.com> *
++ * *
++ * This program is free software; you can redistribute it and/or modify *
++ * it under the terms of the GNU General Public License as published by *
++ * the Free Software Foundation; either version 2 of the License, or *
++ * (at your option) any later version. *
++ * *
++ * This program is distributed in the hope that it will be useful, *
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
++ * GNU General Public License for more details. *
++ * *
++ * You should have received a copy of the GNU General Public License *
++ * along with this program; if not, write to the *
++ * Free Software Foundation, Inc., *
++ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA . *
++ ***************************************************************************/
++
++#ifndef NOTIFICATIONGROUP_H
++#define NOTIFICATIONGROUP_H
++
++
++#include <Plasma/Extender>
++#include <Plasma/ExtenderGroup>
++
++
++#include <Plasma/Plasma>
++
++class QGraphicsLinearLayout;
++
++namespace Plasma
++{
++ class ExtenderItem;
++ class TabBar;
++ class Extender;
++}
++
++class NotificationWidget;
++class Notification;
++
++class NotificationGroup : public Plasma::ExtenderGroup
++{
++ Q_OBJECT
++
++public:
++ NotificationGroup(Plasma::Extender *parent, uint groupId = 0);
++ ~NotificationGroup();
++
++ void addNotification(Notification *notification);
++
++ void filterNotificationsByOwner(const QString &owner);
++
++
++public Q_SLOTS:
++ void removeNotification(Notification *notification);
++
++protected Q_SLOTS:
++ void tabSwitched(int index);
++ void extenderItemDestroyed(Plasma::ExtenderItem *object);
++
++Q_SIGNALS:
++ void scrollerEmpty();
++
++private:
++ Plasma::TabBar *m_notificationBar;
++
++ //housekeeping data structures
++ QList<Notification *>m_notifications;
++
++ //Those two are kept on both ways since we are not sure the thing
++ //contained in the hash is still valid so we couldn't obtain the
++ //info to remove the proper key, unless both ways are stored
++ QHash<QString, QSet<Notification *> > m_notificationsForApp;
++ QHash<Notification *, QString> m_appForNotification;
++
++ QHash<Notification *, Plasma::ExtenderItem *>m_extenderItemsForNotification;
++ QHash<Plasma::ExtenderItem *, Notification *>m_notificationForExtenderItems;
++
++ QString m_currentFilter;
++};
++
++
++#endif
+diff --git a/plasma/generic/applets/notifications/ui/notifications.cpp b/plasma/generic/applets/notifications/ui/notifications.cpp
+new file mode 100644
+index 0000000..eea1d72
+--- /dev/null
++++ b/plasma/generic/applets/notifications/ui/notifications.cpp
+@@ -0,0 +1,434 @@
++/***************************************************************************
++ * applet.cpp *
++ * *
++ * Copyright (C) 2008 Jason Stubbs <jasonbstubbs@gmail.com> *
++ * Copyright (C) 2008 Sebastian Sauer *
++ * Copyright (C) 2010 Marco Martin <notmart@gmail.com> *
++ * *
++ * This program is free software; you can redistribute it and/or modify *
++ * it under the terms of the GNU General Public License as published by *
++ * the Free Software Foundation; either version 2 of the License, or *
++ * (at your option) any later version. *
++ * *
++ * This program is distributed in the hope that it will be useful, *
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
++ * GNU General Public License for more details. *
++ * *
++ * You should have received a copy of the GNU General Public License *
++ * along with this program; if not, write to the *
++ * Free Software Foundation, Inc., *
++ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA . *
++ ***************************************************************************/
++
++#include "notifications.h"
++
++#include <QtGui/QApplication>
++#include <QtGui/QGraphicsLayout>
++#include <QtGui/QGraphicsLinearLayout>
++#include <QtGui/QVBoxLayout>
++#include <QtGui/QIcon>
++#include <QtGui/QLabel>
++#include <QtGui/QListWidget>
++#include <QtGui/QTreeWidget>
++#include <QtGui/QCheckBox>
++#include <QtGui/QPainter>
++#include <QtGui/QX11Info>
++#include <QtCore/QProcess>
++
++
++#include <KConfigDialog>
++#include <KComboBox>
++#include <KWindowSystem>
++#include <KIconLoader>
++
++#include <Solid/Device>
++
++#include <plasma/extender.h>
++#include <plasma/extenderitem.h>
++#include <plasma/extendergroup.h>
++#include <plasma/framesvg.h>
++#include <plasma/widgets/label.h>
++#include <plasma/theme.h>
++#include <plasma/dataenginemanager.h>
++#include <plasma/dataengine.h>
++#include <Plasma/TabBar>
++#include <Plasma/Animator>
++#include <Plasma/Animation>
++#include <Plasma/Containment>
++#include <Plasma/Corona>
++#include <Plasma/Dialog>
++#include <Plasma/WindowEffects>
++
++#include "config-notifications.h"
++#ifdef HAVE_LIBXSS // Idle detection.
++#include <X11/Xlib.h>
++#include <X11/Xutil.h>
++#include <X11/extensions/scrnsaver.h>
++#include <fixx11h.h>
++#endif // HAVE_LIBXSS
++
++#include "../core/notificationsmanager.h"
++#include "../core/notification.h"
++#include "../core/completedjobnotification.h"
++#include "busywidget.h"
++#include "jobwidget.h"
++#include "jobtotalswidget.h"
++#include "notificationgroup.h"
++#include "notificationstack.h"
++#include "stackdialog.h"
++
++
++K_EXPORT_PLASMA_APPLET(notifications, Notifications)
++
++
++Notifications::Notifications(QObject *parent, const QVariantList &arguments)
++ : Plasma::PopupApplet(parent, arguments),
++ m_jobSummaryWidget(0),
++ m_autoHidePopup(true),
++ m_notificationStack(0),
++ m_notificationStackDialog(0),
++ m_standaloneJobSummaryWidget(0),
++ m_standaloneJobSummaryDialog(0),
++ m_busyWidget(0)
++{
++ m_manager = new Manager(this);
++
++ setPopupIcon(QIcon());
++ setPassivePopup(true);
++ setAspectRatioMode(Plasma::IgnoreAspectRatio);
++ setBackgroundHints(NoBackground);
++ setHasConfigurationInterface(true);
++ setMinimumSize(KIconLoader::SizeSmall, KIconLoader::SizeSmall);
++}
++
++Notifications::~Notifications()
++{
++ // stop listening to the manager
++ disconnect(m_manager, 0, this, 0);
++ if (m_notificationStackDialog) {
++ disconnect(m_notificationStackDialog, 0, this, 0);
++ }
++
++ foreach (Notification *notification, m_manager->notifications()) {
++ // we don't want a destroyed managed after the destruction of manager
++ disconnect(notification, 0, this, 0);
++ }
++
++ //has to be deleted before the manager because it will access it
++ delete m_busyWidget;
++ delete m_notificationStackDialog;
++ delete m_standaloneJobSummaryDialog;
++}
++
++void Notifications::init()
++{
++ extender()->setEmptyExtenderMessage(i18n("No notifications and no jobs"));
++
++ m_busyWidget = new BusyWidget(this, m_manager);
++ connect(m_busyWidget, SIGNAL(clicked()), this, SLOT(togglePopup()));
++ QGraphicsLinearLayout *lay = new QGraphicsLinearLayout(this);
++ setContentsMargins(0, 0, 0, 0);
++ lay->setContentsMargins(0, 0, 0, 0);
++ lay->addItem(m_busyWidget);
++
++ configChanged();
++ setStatus(Plasma::PassiveStatus);
++}
++
++void Notifications::configChanged()
++{
++ KConfigGroup cg = config();
++
++ m_autoHidePopup = cg.readEntry("AutoHidePopup", true);
++ if (m_notificationStackDialog) {
++ m_notificationStackDialog->setAutoHide(m_autoHidePopup);
++ }
++
++ if (cg.readEntry("ShowJobs", true)) {
++ createJobGroups();
++
++ m_manager->registerJobProtocol();
++ connect(m_manager, SIGNAL(jobAdded(Job*)),
++ this, SLOT(addJob(Job*)), Qt::UniqueConnection);
++ connect(m_manager, SIGNAL(jobRemoved(Job*)),
++ this, SLOT(finishJob(Job*)), Qt::UniqueConnection);
++ } else {
++ delete extender()->group("jobGroup");
++ m_manager->unregisterJobProtocol();
++ disconnect(m_manager, SIGNAL(jobAdded(Job*)),
++ this, SLOT(addJob(Job*)));
++ disconnect(m_manager, SIGNAL(jobRemoved(Job*)),
++ this, SLOT(finishJob(Job*)));
++ }
++
++ if (cg.readEntry("ShowNotifications", true)) {
++ m_manager->registerNotificationProtocol();
++ connect(m_manager, SIGNAL(notificationAdded(Notification*)),
++ this, SLOT(addNotification(Notification*)), Qt::UniqueConnection);
++ } else {
++ m_manager->unregisterNotificationProtocol();
++ disconnect(m_manager, SIGNAL(notificationAdded(Notification*)),
++ this, SLOT(addNotification(Notification*)));
++ }
++}
++
++void Notifications::syncNotificationBarNeeded()
++{
++ if (!m_manager) {
++ return;
++ }
++
++ if (m_manager->notifications().isEmpty()) {
++ if (extender()->item("notifications")) {
++ //don't let him in the config file
++ extender()->item("notifications")->destroy();
++ }
++ } else if (!extender()->item("notifications")) {
++ m_notificationGroup = new NotificationGroup(extender());
++ }
++}
++
++Manager *Notifications::manager() const
++{
++ return m_manager;
++}
++
++void Notifications::createConfigurationInterface(KConfigDialog *parent)
++{
++ if (!m_notificationInterface) {
++ KConfigGroup cg = config();
++ m_notificationInterface = new QWidget();
++
++ m_notificationUi.setupUi(m_notificationInterface.data());
++
++ m_notificationUi.showJobs->setChecked(cg.readEntry("ShowJobs", true));
++ m_notificationUi.showNotifications->setChecked(cg.readEntry("ShowNotifications", true));
++
++ m_notificationUi.autoHide->setChecked(config().readEntry("AutoHidePopup", true));
++
++ connect(parent, SIGNAL(applyClicked()), this, SLOT(configAccepted()));
++ connect(parent, SIGNAL(okClicked()), this, SLOT(configAccepted()));
++
++ parent->addPage(m_notificationInterface.data(), i18n("Information"),
++ "preferences-desktop-notification",
++ i18n("Choose which information to show"));
++
++ connect(m_notificationUi.showNotifications, SIGNAL(stateChanged(int)), parent, SLOT(settingsModified()));
++ connect(m_notificationUi.showJobs, SIGNAL(stateChanged(int)), parent, SLOT(settingsModified()));
++ connect(m_notificationUi.autoHide, SIGNAL(stateChanged(int)), parent, SLOT(settingsModified()));
++ }
++}
++
++void Notifications::configAccepted()
++{
++ //TODO put in a single page
++ //cg.writeEntry("AutoHidePopup", m_autoHideUi.autoHide->isChecked());
++
++ KConfigGroup cg = config();
++ cg.writeEntry("ShowJobs", m_notificationUi.showJobs->isChecked());
++ cg.writeEntry("ShowNotifications", m_notificationUi.showNotifications->isChecked());
++ cg.writeEntry("AutoHidePopup", m_notificationUi.autoHide->isChecked());
++
++ emit configNeedsSaving();
++}
++
++void Notifications::addNotification(Notification *notification)
++{
++ syncNotificationBarNeeded();
++
++ //At this point we are sure the pointer is valid
++ m_notificationGroup.data()->addNotification(notification);
++
++
++ if (isPopupShowing()) {
++ return;
++ }
++
++ if (!m_notificationStack) {
++ m_notificationStack = new NotificationStack(this);
++ if (containment() && containment()->corona()) {
++ containment()->corona()->addOffscreenWidget(m_notificationStack);
++ }
++ m_notificationStackDialog = new StackDialog;
++ m_notificationStackDialog->setNotificationStack(m_notificationStack);
++ m_notificationStackDialog->setApplet(this);
++ connect(m_notificationStack, SIGNAL(stackEmpty()), m_notificationStackDialog, SLOT(hide()));
++ connect(m_notificationStack, SIGNAL(showRequested()), m_notificationStackDialog, SLOT(perhapsShow()));
++ m_notificationStackDialog->setAutoHide(m_autoHidePopup);
++
++ if (m_standaloneJobSummaryDialog) {
++ m_notificationStackDialog->setWindowToTile(m_standaloneJobSummaryDialog);
++ }
++ }
++
++
++ m_notificationStack->addNotification(notification);
++ m_notificationStackDialog->syncToGraphicsWidget();
++
++ if (containment() && containment()->corona()) {
++ if (!m_notificationStackDialog->isVisible()) {
++ m_notificationStack->setCurrentNotification(notification);
++ }
++
++ KWindowSystem::setOnAllDesktops(m_notificationStackDialog->winId(), true);
++ m_notificationStackDialog->perhapsShow();
++ }
++
++ Plasma::Animation *pulse = Plasma::Animator::create(Plasma::Animator::PulseAnimation, m_busyWidget);
++ pulse->setTargetWidget(m_busyWidget);
++ pulse->start(QAbstractAnimation::DeleteWhenStopped);
++}
++
++void Notifications::addJob(Job *job)
++{
++ Plasma::ExtenderGroup *group = extender()->group("jobGroup");
++
++ Plasma::ExtenderItem *extenderItem = new Plasma::ExtenderItem(extender());
++ extenderItem->setTransient(true);
++ extenderItem->config().writeEntry("type", "job");
++ extenderItem->setWidget(new JobWidget(job, extenderItem));
++
++ extenderItem->setGroup(group);
++
++ if (group) {
++ group->setCollapsed(group->items().count() < 2);
++ }
++
++ if (isPopupShowing()) {
++ return;
++ }
++
++ //show the tiny standalone overview
++ if (!m_standaloneJobSummaryWidget) {
++ m_standaloneJobSummaryDialog = new Plasma::Dialog();
++ KWindowSystem::setType(m_standaloneJobSummaryDialog->winId(), NET::Dock);
++ if (m_notificationStackDialog) {
++ m_notificationStackDialog->setWindowToTile(m_standaloneJobSummaryDialog);
++ }
++
++ m_standaloneJobSummaryWidget = new JobTotalsWidget(m_manager->jobTotals(), this);
++ if (containment() && containment()->corona()) {
++ containment()->corona()->addOffscreenWidget(m_standaloneJobSummaryWidget);
++ }
++ m_standaloneJobSummaryDialog->setGraphicsWidget(m_standaloneJobSummaryWidget);
++ //FIXME:sizing hack and layout issues..
++ m_standaloneJobSummaryWidget->resize(m_standaloneJobSummaryWidget->size().width(), 32);
++ m_standaloneJobSummaryWidget->setMaximumHeight(32);
++ m_standaloneJobSummaryWidget->setMinimumHeight(32);
++ m_standaloneJobSummaryWidget->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
++ }
++
++ m_standaloneJobSummaryDialog->syncToGraphicsWidget();
++ KWindowSystem::setState(m_standaloneJobSummaryDialog->winId(), NET::KeepBelow);
++
++ if (containment() && containment()->corona()) {
++ m_standaloneJobSummaryDialog->move(containment()->corona()->popupPosition(this, m_standaloneJobSummaryDialog->size()));
++ m_standaloneJobSummaryDialog->show();
++ Plasma::WindowEffects::slideWindow(m_standaloneJobSummaryDialog, location());
++
++ KWindowSystem::setOnAllDesktops(m_standaloneJobSummaryDialog->winId(), true);
++ KWindowSystem::clearState(m_standaloneJobSummaryDialog->winId(), NET::KeepAbove|NET::StaysOnTop);
++
++ KWindowSystem::setState(m_standaloneJobSummaryDialog->winId(), NET::SkipTaskbar|NET::SkipPager);
++ KWindowSystem::raiseWindow(m_standaloneJobSummaryDialog->winId());
++ KWindowSystem::setOnAllDesktops(m_standaloneJobSummaryDialog->winId(), true);
++ }
++}
++
++void Notifications::initExtenderItem(Plasma::ExtenderItem *extenderItem)
++{
++ if (extenderItem->name() == "jobGroup") {
++ m_jobSummaryWidget = new JobTotalsWidget(m_manager->jobTotals(), extenderItem);
++ extenderItem->setWidget(m_jobSummaryWidget);
++ Plasma::ExtenderGroup *group = qobject_cast<Plasma::ExtenderGroup*>(extenderItem);
++ if (group) {
++ extenderItem->setCollapsed(!group->isGroupCollapsed());
++ }
++ return;
++ }
++
++ if (extenderItem->config().readEntry("type", QString()) == "job") {
++ extenderItem->setWidget(new JobWidget(0, extenderItem));
++ } else {
++ //unknown type, this should never happen
++ extenderItem->destroy();
++ }
++
++}
++
++void Notifications::popupEvent(bool show)
++{
++ if (m_busyWidget) {
++ m_busyWidget->suppressToolTips(show);
++ }
++
++ //decide about showing the tiny progressbar or not
++ if (m_standaloneJobSummaryDialog) {
++ if (show || !m_manager->jobs().isEmpty()) {
++ if (!show) {
++ KWindowSystem::raiseWindow(m_standaloneJobSummaryDialog->winId());
++ KWindowSystem::setState(m_standaloneJobSummaryDialog->winId(), NET::SkipTaskbar|NET::SkipPager);
++ KWindowSystem::setState(m_standaloneJobSummaryDialog->winId(), NET::KeepBelow);
++ } else {
++ m_standaloneJobSummaryDialog->hide();
++ }
++ }
++ }
++
++ if (m_notificationStackDialog && show) {
++ m_notificationStackDialog->hide();
++ }
++
++ Plasma::ExtenderGroup * jobGroup = extender()->group("jobGroup");
++ if (!jobGroup) {
++ return;
++ }
++
++ foreach (Plasma::ExtenderItem *item, jobGroup->items()) {
++ JobWidget *job = dynamic_cast<JobWidget *>(item->widget());
++ if (job) {
++ job->poppedUp(show);
++ }
++ }
++}
++
++void Notifications::finishJob(Job *job)
++{
++ //finished all jobs? hide the mini progressbar
++ if (m_standaloneJobSummaryDialog && m_manager->jobs().isEmpty()) {
++ m_standaloneJobSummaryDialog->hide();
++ }
++
++ //create a fake notification
++ CompletedJobNotification *notification = new CompletedJobNotification(this);
++ notification->setJob(job);
++ m_manager->addNotification(notification);
++
++ Plasma::ExtenderGroup *group = extender()->group("jobGroup");
++ if (group) {
++ // < 3 because the second still hasn't been removed from the extendergroup
++ group->setCollapsed(!group->isGroupCollapsed() && group->items().count() < 3);
++ }
++}
++
++void Notifications::open(const QString &url)
++{
++ //kDebug() << "open " << url;
++ QProcess::startDetached("kde-open", QStringList() << url);
++}
++
++void Notifications::createJobGroups()
++{
++ if (!extender()->hasItem("jobGroup")) {
++ Plasma::ExtenderGroup *extenderGroup = new Plasma::ExtenderGroup(extender());
++ extenderGroup->setName("jobGroup");
++ initExtenderItem(extenderGroup);
++ extenderGroup->setAutoHide(true);
++ } else if (extender()->group("jobGroup")) {
++ extender()->group("jobGroup")->setAutoHide(true);
++ }
++}
++
++
++#include "notifications.moc"
+diff --git a/plasma/generic/applets/notifications/ui/notifications.h b/plasma/generic/applets/notifications/ui/notifications.h
+new file mode 100644
+index 0000000..827b28e
+--- /dev/null
++++ b/plasma/generic/applets/notifications/ui/notifications.h
+@@ -0,0 +1,101 @@
++/***************************************************************************
++ * applet.h *
++ * *
++ * Copyright (C) 2008 Jason Stubbs <jasonbstubbs@gmail.com> *
++ * Copyright (C) 2010 Marco Martin <notmart@gmail.com> *
++ * *
++ * This program is free software; you can redistribute it and/or modify *
++ * it under the terms of the GNU General Public License as published by *
++ * the Free Software Foundation; either version 2 of the License, or *
++ * (at your option) any later version. *
++ * *
++ * This program is distributed in the hope that it will be useful, *
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
++ * GNU General Public License for more details. *
++ * *
++ * You should have received a copy of the GNU General Public License *
++ * along with this program; if not, write to the *
++ * Free Software Foundation, Inc., *
++ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA . *
++ ***************************************************************************/
++
++#ifndef NOTIFICATIONS_H
++#define NOTIFICATIONS_H
++
++#include <plasma/popupapplet.h>
++
++#include "ui_notificationsconfig.h"
++
++
++namespace Plasma
++{
++class ExtenderItem;
++class TabBar;
++class Dialog;
++}
++
++class NotificationWidget;
++class StackDialog;
++
++
++class Job;
++class JobTotalsWidget;
++class Manager;
++class Notification;
++class NotificationGroup;
++class NotificationStack;
++class BusyWidget;
++
++class Notifications : public Plasma::PopupApplet
++{
++ Q_OBJECT
++
++public:
++ explicit Notifications(QObject *parent, const QVariantList &arguments = QVariantList());
++ ~Notifications();
++
++ void init();
++ Manager *manager() const;
++
++protected:
++ void createConfigurationInterface(KConfigDialog *parent);
++ void initExtenderItem(Plasma::ExtenderItem *extenderItem);
++ void configChanged();
++
++ void popupEvent(bool show);
++
++private slots:
++ void configAccepted();
++ void addNotification(Notification *notification);
++ void addJob(Job *job);
++ void finishJob(Job *job);
++ void open(const QString &url);
++ void syncNotificationBarNeeded();
++
++private:
++ void createJobGroups();
++
++ Manager *m_manager;
++ static int s_managerUsage;
++
++ QWeakPointer<QWidget> m_notificationInterface;
++ QDateTime m_lastActivity;
++
++ JobTotalsWidget *m_jobSummaryWidget;
++ bool m_autoHidePopup;
++
++ QWeakPointer<NotificationGroup> m_notificationGroup;
++ NotificationStack *m_notificationStack;
++ StackDialog *m_notificationStackDialog;
++ JobTotalsWidget *m_standaloneJobSummaryWidget;
++ Plasma::Dialog *m_standaloneJobSummaryDialog;
++
++ BusyWidget *m_busyWidget;
++
++ Ui::NotificationsConfig m_notificationUi;
++};
++
++
++
++#endif
+diff --git a/plasma/generic/applets/notifications/ui/notificationsconfig.ui b/plasma/generic/applets/notifications/ui/notificationsconfig.ui
+new file mode 100644
+index 0000000..aaa6a82
+--- /dev/null
++++ b/plasma/generic/applets/notifications/ui/notificationsconfig.ui
+@@ -0,0 +1,114 @@
++<?xml version="1.0" encoding="UTF-8"?>
++<ui version="4.0">
++ <class>NotificationsConfig</class>
++ <widget class="QWidget" name="ProtocolsConfig">
++ <property name="geometry">
++ <rect>
++ <x>0</x>
++ <y>0</y>
++ <width>213</width>
++ <height>236</height>
++ </rect>
++ </property>
++ <layout class="QFormLayout" name="formLayout">
++ <property name="fieldGrowthPolicy">
++ <enum>QFormLayout::ExpandingFieldsGrow</enum>
++ </property>
++ <item row="0" column="0" colspan="2">
++ <widget class="QLabel" name="label_2">
++ <property name="font">
++ <font>
++ <weight>75</weight>
++ <bold>true</bold>
++ </font>
++ </property>
++ <property name="text">
++ <string>Pop Up Notices</string>
++ </property>
++ </widget>
++ </item>
++ <item row="2" column="0">
++ <widget class="QLabel" name="label_4">
++ <property name="text">
++ <string>Application notifications</string>
++ </property>
++ <property name="buddy">
++ <cstring>showNotifications</cstring>
++ </property>
++ </widget>
++ </item>
++ <item row="2" column="1">
++ <widget class="QCheckBox" name="showNotifications">
++ <property name="text">
++ <string/>
++ </property>
++ </widget>
++ </item>
++ <item row="4" column="0">
++ <widget class="QLabel" name="label_5">
++ <property name="text">
++ <string>File transfers and other jobs</string>
++ </property>
++ <property name="buddy">
++ <cstring>showJobs</cstring>
++ </property>
++ </widget>
++ </item>
++ <item row="4" column="1">
++ <widget class="QCheckBox" name="showJobs">
++ <property name="text">
++ <string/>
++ </property>
++ </widget>
++ </item>
++ <item row="8" column="0">
++ <spacer name="verticalSpacer">
++ <property name="orientation">
++ <enum>Qt::Vertical</enum>
++ </property>
++ <property name="sizeType">
++ <enum>QSizePolicy::Fixed</enum>
++ </property>
++ <property name="sizeHint" stdset="0">
++ <size>
++ <width>20</width>
++ <height>6</height>
++ </size>
++ </property>
++ </spacer>
++ </item>
++ <item row="9" column="0" colspan="2">
++ <widget class="QLabel" name="label_3">
++ <property name="font">
++ <font>
++ <weight>75</weight>
++ <bold>true</bold>
++ </font>
++ </property>
++ <property name="text">
++ <string>Popup</string>
++ </property>
++ </widget>
++ </item>
++ <item row="10" column="1">
++ <widget class="QCheckBox" name="autoHide">
++ <property name="text">
++ <string/>
++ </property>
++ </widget>
++ </item>
++ <item row="10" column="0">
++ <widget class="QLabel" name="label_10">
++ <property name="text">
++ <string>Automatically hide</string>
++ </property>
++ <property name="buddy">
++ <cstring>autoHide</cstring>
++ </property>
++ </widget>
++ </item>
++ </layout>
++ </widget>
++ <resources/>
++ <connections/>
++</ui>
+diff --git a/plasma/generic/applets/notifications/ui/notificationstack.cpp b/plasma/generic/applets/notifications/ui/notificationstack.cpp
+new file mode 100644
+index 0000000..f0fc52e
+--- /dev/null
++++ b/plasma/generic/applets/notifications/ui/notificationstack.cpp
+@@ -0,0 +1,233 @@
++/***************************************************************************
++ * notificationstack.cpp *
++ * Copyright (C) 2010 Marco Martin <notmart@gmail.com> *
++ * *
++ * This program is free software; you can redistribute it and/or modify *
++ * it under the terms of the GNU General Public License as published by *
++ * the Free Software Foundation; either version 2 of the License, or *
++ * (at your option) any later version. *
++ * *
++ * This program is distributed in the hope that it will be useful, *
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
++ * GNU General Public License for more details. *
++ * *
++ * You should have received a copy of the GNU General Public License *
++ * along with this program; if not, write to the *
++ * Free Software Foundation, Inc., *
++ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA . *
++ ***************************************************************************/
++
++#include "notificationstack.h"
++#include "../core/notification.h"
++#include "notificationwidget.h"
++
++#include <QGraphicsLinearLayout>
++#include <QTimer>
++
++#include <KDebug>
++#include <KGlobalSettings>
++
++#include <Plasma/FrameSvg>
++#include <Plasma/Dialog>
++
++NotificationStack::NotificationStack(QGraphicsItem *parent)
++ : QGraphicsWidget(parent),
++ m_size(4),
++ m_underMouse(false)
++{
++ m_mainLayout = new QGraphicsLinearLayout(Qt::Vertical, this);
++ m_canDismissTimer = new QTimer(this);
++ m_canDismissTimer->setSingleShot(true);
++
++ m_delayedRemoveTimer = new QTimer(this);
++ m_delayedRemoveTimer->setSingleShot(true);
++ connect(m_delayedRemoveTimer, SIGNAL(timeout()), this, SLOT(popNotification()));
++
++ setAcceptsHoverEvents(true);
++}
++
++NotificationStack::~NotificationStack()
++{
++}
++
++void NotificationStack::addNotification(Notification *notification)
++{
++ m_canDismissTimer->start(1000);
++ connect(notification, SIGNAL(notificationDestroyed(Notification*)), this, SLOT(removeNotification(Notification*)), Qt::UniqueConnection);
++ connect(notification, SIGNAL(expired(Notification*)), this, SLOT(delayedRemoveNotification(Notification*)), Qt::UniqueConnection);
++ connect(notification, SIGNAL(changed(Notification*)), this, SLOT(notificationChanged(Notification*)), Qt::UniqueConnection);
++
++ NotificationWidget *notificationWidget = new NotificationWidget(notification, this);
++ notificationWidget->installEventFilter(this);
++ notificationWidget->setAcceptsHoverEvents(this);
++ connect(notificationWidget, SIGNAL(actionTriggered(Notification*)), this, SLOT(removeNotification(Notification*)));
++
++ m_notificationWidgets[notification] = notificationWidget;
++ m_notifications.append(notification);
++
++ if (m_notifications.size() > 1) {
++ notificationWidget->setCollapsed(true, false);
++ } else {
++ m_currentNotificationWidget = notificationWidget;
++ }
++
++ if (m_notifications.size() > m_size) {
++ bool found = false;
++
++ //try to kill the oldest notification of the same app
++ foreach (Notification *notif, m_notifications) {
++ if (notif->applicationName() == notification->applicationName()) {
++ m_notificationWidgets[notif]->deleteLater();
++ m_notificationWidgets.remove(notif);
++ m_notifications.removeAll(notif);
++ found = true;
++ break;
++ }
++ }
++ //or kill the oldest one
++ if (!found) {
++ Notification *notif = m_notifications.first();
++ m_notificationWidgets[notif]->deleteLater();
++ m_notificationWidgets.remove(notif);
++ m_notifications.pop_front();
++ }
++ }
++
++ m_mainLayout->insertItem(0, notificationWidget);
++ m_mainLayout->activate();
++ updateGeometry();
++ resize(size().width(), effectiveSizeHint(Qt::MinimumSize).height());
++ emit updateRequested();
++}
++
++void NotificationStack::notificationChanged(Notification *notification)
++{
++ //if it was gone away put in on the stack again
++ if (!m_notificationWidgets.contains(notification)) {
++ addNotification(notification);
++ }
++ emit showRequested();
++}
++
++void NotificationStack::removeNotification(Notification *notification)
++{
++ NotificationWidget *nw = m_notificationWidgets.value(notification);
++ if (nw) {
++ nw->deleteLater();
++ }
++ m_mainLayout->removeItem(nw);
++ m_notificationWidgets.remove(notification);
++ m_notifications.removeAll(notification);
++
++ if (m_notifications.count() > 0) {
++ setCurrentNotification(m_notifications.first());
++ }
++
++ if (m_notifications.count() == 0) {
++ emit stackEmpty();
++ }
++
++ updateGeometry();
++ resize(size().width(), sizeHint(Qt::MinimumSize, QSizeF()).height());
++ emit updateRequested();
++}
++
++void NotificationStack::delayedRemoveNotification(Notification *notification)
++{
++ m_notificationsToRemove.append(notification);
++ if (!m_underMouse) {
++ m_delayedRemoveTimer->start(1000);
++ }
++}
++
++void NotificationStack::setCurrentNotification(Notification *notification)
++{
++ if (m_notificationWidgets.contains(notification)) {
++ if (m_currentNotificationWidget) {
++ m_currentNotificationWidget.data()->setCollapsed(true);
++ }
++ m_currentNotificationWidget = m_notificationWidgets.value(notification);
++ m_currentNotificationWidget.data()->setCollapsed(false);
++ }
++}
++
++void NotificationStack::hoverEnterEvent(QGraphicsSceneHoverEvent *event)
++{
++ Q_UNUSED(event)
++
++ m_underMouse = true;
++ m_delayedRemoveTimer->stop();
++}
++
++void NotificationStack::hoverLeaveEvent(QGraphicsSceneHoverEvent *event)
++{
++ Q_UNUSED(event)
++
++ m_underMouse = false;
++ m_delayedRemoveTimer->start(1000);
++}
++
++void NotificationStack::mousePressEvent(QGraphicsSceneMouseEvent *event)
++{
++ event->accept();
++}
++
++void NotificationStack::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
++{
++ Q_UNUSED(event)
++
++ if (!m_canDismissTimer->isActive() &&
++ QPointF(event->buttonDownScenePos(event->button()) - event->scenePos()).manhattanLength() < KGlobalSettings::dndEventDelay()) {
++ emit hideRequested();
++ }
++}
++
++NotificationWidget *NotificationStack::currentNotificationWidget() const
++{
++ if (m_currentNotificationWidget) {
++ return m_currentNotificationWidget.data();
++ } else {
++ return 0;
++ }
++}
++
++bool NotificationStack::eventFilter(QObject *watched, QEvent *event)
++{
++ NotificationWidget *nw = qobject_cast<NotificationWidget *>(watched);
++
++ if (!nw) {
++ return false;
++ }
++
++ if (event->type() == QEvent::GraphicsSceneHoverEnter) {
++ if (m_currentNotificationWidget && m_currentNotificationWidget.data() == nw) {
++ return false;
++ } else if (m_currentNotificationWidget) {
++ m_currentNotificationWidget.data()->setCollapsed(true);
++ }
++ nw->setCollapsed(false);
++ m_currentNotificationWidget = nw;
++ m_canDismissTimer->start(1000);
++ } else if (event->type() == QEvent::GraphicsSceneMove) {
++ emit updateRequested();
++ }
++
++
++ return false;
++}
++
++void NotificationStack::popNotification()
++{
++ if (m_notificationsToRemove.isEmpty()) {
++ return;
++ }
++
++ Notification *notif = m_notificationsToRemove.first();
++ removeNotification(notif);
++ m_notificationsToRemove.pop_front();
++ m_delayedRemoveTimer->start(1000);
++}
++
++
++#include "notificationstack.moc"
+diff --git a/plasma/generic/applets/notifications/ui/notificationstack.h b/plasma/generic/applets/notifications/ui/notificationstack.h
+new file mode 100644
+index 0000000..783e5ae
+--- /dev/null
++++ b/plasma/generic/applets/notifications/ui/notificationstack.h
+@@ -0,0 +1,81 @@
++/***************************************************************************
++ * notificationscroller.h *
++ * Copyright (C) 2010 Marco Martin <notmart@gmail.com> *
++ * *
++ * This program is free software; you can redistribute it and/or modify *
++ * it under the terms of the GNU General Public License as published by *
++ * the Free Software Foundation; either version 2 of the License, or *
++ * (at your option) any later version. *
++ * *
++ * This program is distributed in the hope that it will be useful, *
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
++ * GNU General Public License for more details. *
++ * *
++ * You should have received a copy of the GNU General Public License *
++ * along with this program; if not, write to the *
++ * Free Software Foundation, Inc., *
++ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA . *
++ ***************************************************************************/
++
++#ifndef NOTIFICATIONSTACK_H
++#define NOTIFICATIONSTACK_H
++
++#include <QGraphicsWidget>
++
++class QGraphicsLinearLayout;
++class QTimer;
++
++class NotificationWidget;
++
++class Notification;
++
++class NotificationStack : public QGraphicsWidget
++{
++ Q_OBJECT
++
++public:
++ NotificationStack(QGraphicsItem *parent = 0);
++ ~NotificationStack();
++
++ void addNotification(Notification *notification);
++
++ //TODO:accessor
++ void setCurrentNotification(Notification *notification);
++
++ NotificationWidget *currentNotificationWidget() const;
++
++protected:
++ void hoverEnterEvent(QGraphicsSceneHoverEvent *event);
++ void hoverLeaveEvent(QGraphicsSceneHoverEvent *event);
++ void mousePressEvent(QGraphicsSceneMouseEvent *event);
++ void mouseReleaseEvent(QGraphicsSceneMouseEvent *event);
++ bool eventFilter(QObject *watched, QEvent *event);
++
++public Q_SLOTS:
++ void removeNotification(Notification *notification);
++ void delayedRemoveNotification(Notification *notification);
++
++private Q_SLOTS:
++ void popNotification();
++ void notificationChanged(Notification *notification);
++
++Q_SIGNALS:
++ void stackEmpty();
++ void updateRequested();
++ void hideRequested();
++ void showRequested();
++
++private:
++ QList<Notification *> m_notifications;
++ QList<Notification *> m_notificationsToRemove;
++ QHash<Notification *, NotificationWidget *> m_notificationWidgets;
++ QGraphicsLinearLayout *m_mainLayout;
++ int m_size;
++ bool m_underMouse;
++ QWeakPointer<NotificationWidget> m_currentNotificationWidget;
++ QTimer *m_delayedRemoveTimer;
++ QTimer *m_canDismissTimer;
++};
++
++#endif
+diff --git a/plasma/generic/applets/notifications/ui/notificationwidget.cpp b/plasma/generic/applets/notifications/ui/notificationwidget.cpp
+new file mode 100644
+index 0000000..02ed38f
+--- /dev/null
++++ b/plasma/generic/applets/notifications/ui/notificationwidget.cpp
+@@ -0,0 +1,486 @@
++/***************************************************************************
++ * notificationwidget.cpp *
++ * *
++ * Copyright (C) 2008 Dmitry Suzdalev <dimsuz@gmail.com> *
++ * Copyright (C) 2008 Rob Scheepmaker <r.scheepmaker@student.utwente.nl> *
++ * Copyright (C) 2008 Jason Stubbs <jasonbstubbs@gmail.com> *
++ * Copyright (C) 2010 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 of the *
++ * License, or (at your option) any later version. *
++ * *
++ * This program is distributed in the hope that it will be useful, *
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
++ * GNU Library General Public License for more details. *
++ * *
++ * You should have received a copy of the GNU Library General Public *
++ * License along with this library; if not, write to the *
++ * Free Software Foundation, Inc., *
++ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA . *
++ ***************************************************************************/
++
++#include "notificationwidget.h"
++
++#include <QSignalMapper>
++
++#include <QtGui/QGraphicsLinearLayout>
++#include <QtGui/QGraphicsGridLayout>
++#include <QtGui/QTextDocument>
++#include <QtGui/QFontMetrics>
++#include <QtGui/QGraphicsSceneResizeEvent>
++#include <QtGui/QPainter>
++#include <QAction>
++#include <QLabel>
++#include <QPropertyAnimation>
++#include <QParallelAnimationGroup>
++
++#include <KColorScheme>
++#include <KPushButton>
++#include <KTextBrowser>
++
++#include <Plasma/Animation>
++#include <Plasma/Animator>
++#include <Plasma/Frame>
++#include <Plasma/IconWidget>
++#include <Plasma/Label>
++#include <Plasma/PushButton>
++#include <Plasma/TextBrowser>
++#include <Plasma/Theme>
++
++class NotificationWidgetPrivate
++{
++public:
++ NotificationWidgetPrivate(NotificationWidget *q)
++ : q(q),
++ destroyOnClose(true),
++ autoDelete(false),
++ collapsed(false),
++ icon(0),
++ actionsWidget(0),
++ signalMapper(new QSignalMapper(q))
++ {
++ }
++
++ void setTextFields(const QString &applicationName, const QString &summary, const QString &message);
++ void completeDetach();
++ void updateActions();
++ void updateNotification();
++ void buttonClicked();
++ void hideFinished();
++ QRectF bigIconRect() const;
++
++ NotificationWidget *q;
++
++ QWeakPointer<Notification> notification;
++ bool destroyOnClose;
++ bool autoDelete;
++ bool collapsed;
++
++ QString message;
++ Plasma::TextBrowser *messageLabel;
++ Plasma::Label *title;
++ Plasma::IconWidget *closeButton;
++ Plasma::IconWidget *icon;
++ QGraphicsLinearLayout *titleLayout;
++ QGraphicsLinearLayout *mainLayout;
++ QGraphicsGridLayout *bodyLayout;
++ QGraphicsWidget *body;
++ QGraphicsWidget *iconPlaceSmall;
++ QGraphicsWidget *iconPlaceBig;
++ QGraphicsWidget *actionsWidget;
++ QHash<QString, QString> actions;
++ QStringList actionOrder;
++ QPropertyAnimation *hideAnimation;
++ QPropertyAnimation *iconAnimation;
++ QParallelAnimationGroup *animationGroup;
++
++ QSignalMapper *signalMapper;
++};
++
++NotificationWidget::NotificationWidget(Notification *notification, QGraphicsWidget *parent)
++ : QGraphicsWidget(parent),
++ d(new NotificationWidgetPrivate(this))
++{
++ setFlag(QGraphicsItem::ItemHasNoContents, true);
++ setMinimumWidth(300);
++
++ setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Preferred);
++
++
++ d->iconPlaceSmall = new QGraphicsWidget(this);
++ d->iconPlaceSmall->setMinimumSize(KIconLoader::SizeSmall, KIconLoader::SizeSmall);
++ d->iconPlaceSmall->setMaximumSize(KIconLoader::SizeSmall, KIconLoader::SizeSmall);
++ d->icon = new Plasma::IconWidget(this);
++ d->icon->setAcceptHoverEvents(false);
++ d->icon->setAcceptedMouseButtons(Qt::NoButton);
++
++ d->title = new Plasma::Label(this);
++ d->title->setWordWrap(false);
++ d->title->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
++ d->title->setAlignment(Qt::AlignCenter);
++
++ d->closeButton = new Plasma::IconWidget(this);
++ d->closeButton->setSvg("widgets/configuration-icons", "close");
++ d->closeButton->setMaximumSize(d->closeButton->sizeFromIconSize(KIconLoader::SizeSmall));
++ d->closeButton->setMinimumSize(d->closeButton->maximumSize());
++ connect(d->closeButton, SIGNAL(clicked()), notification, SLOT(deleteLater()));
++
++ d->titleLayout = new QGraphicsLinearLayout(Qt::Horizontal);
++ d->titleLayout->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
++ d->titleLayout->addItem(d->iconPlaceSmall);
++ d->titleLayout->addItem(d->title);
++ d->titleLayout->addItem(d->closeButton);
++
++
++
++ d->body = new QGraphicsWidget(this);
++ d->body->setContentsMargins(0,0,0,0);
++ d->body->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
++ d->bodyLayout = new QGraphicsGridLayout(d->body);
++ d->bodyLayout->setSpacing(0);
++ d->bodyLayout->setContentsMargins(0,0,0,0);
++
++ d->messageLabel = new Plasma::TextBrowser(d->body);
++ d->messageLabel->setPreferredWidth(0);
++ d->messageLabel->nativeWidget()->setHorizontalScrollBarPolicy( Qt::ScrollBarAlwaysOff );
++ d->messageLabel->nativeWidget()->setVerticalScrollBarPolicy( Qt::ScrollBarAlwaysOff );
++ d->messageLabel->nativeWidget()->setAlignment(Qt::AlignLeft|Qt::AlignVCenter);
++ d->messageLabel->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed);
++ connect(d->messageLabel->nativeWidget(), SIGNAL(urlClick(QString)),
++ notification, SLOT(linkActivated(QString)));
++
++ d->iconPlaceBig = new QGraphicsWidget(this);
++ d->iconPlaceBig->setMaximumHeight(KIconLoader::SizeLarge);
++ d->iconPlaceBig->setMinimumHeight(KIconLoader::SizeLarge);
++ d->iconPlaceBig->setMaximumWidth(KIconLoader::SizeLarge);
++ d->iconPlaceBig->setMinimumWidth(KIconLoader::SizeLarge);
++ d->iconPlaceBig->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Expanding);
++ d->bodyLayout->addItem(d->iconPlaceBig, 0, 0, Qt::AlignCenter);
++ d->bodyLayout->addItem(d->messageLabel, 0, 1, Qt::AlignCenter);
++
++ d->mainLayout = new QGraphicsLinearLayout(Qt::Vertical, this);
++ d->mainLayout->setSpacing(0);
++ d->mainLayout->addItem(d->titleLayout);
++ d->mainLayout->addItem(d->body);
++
++ d->notification = notification;
++
++ connect(d->signalMapper, SIGNAL(mapped(QString)),
++ notification, SLOT(triggerAction(QString)));
++ connect(notification, SIGNAL(changed()),
++ this, SLOT(updateNotification()));
++ connect(notification, SIGNAL(destroyed()),
++ this, SLOT(deleteLater()));
++
++ d->hideAnimation = new QPropertyAnimation(this, "bodyHeight", this);
++ d->hideAnimation->setDuration(250);
++ connect(d->hideAnimation, SIGNAL(finished()), this, SLOT(hideFinished()));
++
++ d->iconAnimation = new QPropertyAnimation(d->icon, "geometry", d->icon);
++ d->iconAnimation->setDuration(250);
++
++ d->animationGroup = new QParallelAnimationGroup(this);
++ d->animationGroup->addAnimation(d->hideAnimation);
++ d->animationGroup->addAnimation(d->iconAnimation);
++
++ d->updateNotification();
++
++ d->mainLayout->activate();
++ updateGeometry();
++}
++
++NotificationWidget::~NotificationWidget()
++{
++ delete d;
++}
++
++void NotificationWidget::setCollapsed(bool collapse, bool animate)
++{
++ if (collapse == d->collapsed) {
++ return;
++ }
++
++ setTitleBarVisible(true);
++
++ //use this weird way to make easy to animate
++ if (animate) {
++ setMinimumHeight(-1);
++ if (collapse) {
++ d->hideAnimation->setStartValue(d->body->size().height());
++ d->hideAnimation->setEndValue(0);
++
++ d->iconAnimation->setStartValue(d->icon->geometry());
++ d->iconAnimation->setEndValue(d->iconPlaceSmall->geometry());
++ d->animationGroup->start();
++ } else {
++ d->body->setVisible(true);
++ d->hideAnimation->setStartValue(d->body->size().height());
++ d->body->setMaximumHeight(-1);
++ d->hideAnimation->setEndValue(d->body->effectiveSizeHint(Qt::PreferredSize).height());
++
++ d->iconAnimation->setStartValue(d->icon->geometry());
++ d->iconAnimation->setEndValue(d->bigIconRect());
++ d->animationGroup->start();
++ }
++ } else {
++ if (collapse) {
++ //setMaximumHeight(d->titleLayout->geometry().bottom());
++ d->body->setMinimumHeight(0);
++ d->body->setMaximumHeight(0);
++ d->body->hide();
++ setMinimumHeight(-1);
++ d->icon->setGeometry(d->iconPlaceSmall->geometry());
++ } else {
++ d->body->show();
++ d->body->setMaximumHeight(-1);
++ d->body->setMinimumHeight(-1);
++ d->body->setMaximumHeight(d->body->effectiveSizeHint(Qt::PreferredSize).height());
++ d->body->setMinimumHeight(d->body->maximumHeight());
++ updateGeometry();
++ d->mainLayout->invalidate();
++ setMinimumHeight(sizeHint(Qt::PreferredSize, QSizeF()).height());
++
++ d->icon->setGeometry(d->bigIconRect());
++ }
++ }
++
++ if (collapse) {
++ d->messageLabel->nativeWidget()->setTextInteractionFlags(Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse|Qt::TextSelectableByKeyboard);
++ } else {
++ d->messageLabel->nativeWidget()->setTextInteractionFlags(Qt::LinksAccessibleByMouse);
++ }
++
++ d->body->setFlag(QGraphicsItem::ItemClipsChildrenToShape, true);
++
++ d->collapsed = collapse;
++}
++
++Notification *NotificationWidget::notification() const
++{
++ return d->notification.data();
++}
++
++qreal NotificationWidget::bodyHeight() const
++{
++ return d->body->maximumHeight();
++}
++
++void NotificationWidget::setBodyHeight(const qreal height)
++{
++ d->body->setMaximumHeight(height);
++ d->body->setMinimumHeight(height);
++ updateGeometry();
++ d->mainLayout->invalidate();
++ setMinimumHeight(sizeHint(Qt::PreferredSize, QSizeF()).height());
++}
++
++bool NotificationWidget::isCollapsed() const
++{
++ return d->collapsed;
++}
++
++void NotificationWidget::setTitleBarVisible(bool visible)
++{
++ if (visible) {
++ d->iconPlaceSmall->show();
++ d->title->show();
++ d->closeButton->show();
++ d->titleLayout->setMaximumHeight(QWIDGETSIZE_MAX);
++ } else {
++ d->iconPlaceSmall->hide();
++ d->title->hide();
++ d->closeButton->hide();
++ d->titleLayout->setMaximumHeight(0);
++ }
++}
++
++bool NotificationWidget::isTitleBarVisible() const
++{
++ return d->title->isVisible();
++}
++
++void NotificationWidget::resizeEvent(QGraphicsSceneResizeEvent *event)
++{
++ QGraphicsWidget::resizeEvent(event);
++ if (d->icon && !d->collapsed && d->animationGroup->state() != QAbstractAnimation::Running) {
++ d->icon->setGeometry(d->bigIconRect());
++ }
++}
++
++QRectF NotificationWidgetPrivate::bigIconRect() const
++{
++ return q->mapFromScene(iconPlaceBig->mapToScene(iconPlaceBig->boundingRect())).boundingRect();
++}
++
++void NotificationWidgetPrivate::setTextFields(const QString &applicationName,
++ const QString &summary, const QString &message)
++{
++ if (!summary.isEmpty()) {
++ title->setText(summary);
++ } else {
++ title->setText(i18n("Notification from %1", applicationName));
++ }
++
++
++ QString processed = message.trimmed();
++
++ /*if there is a < that is not closed as a tag, replace it with an entity*/
++ processed = processed.replace(QRegExp("<(?![^<]*>)"), "<");
++ processed.replace('\n', "<br>");
++
++ QFontMetricsF fm(messageLabel->font());
++ qreal maxLine = messageLabel->rect().width();
++
++ QString parsed;
++
++ QString::const_iterator i = processed.begin();
++ bool inTag = false;
++ QString word;
++ QString sentence;
++
++ while (i != processed.end()) {
++ QChar c = *i;
++
++ if (c == '<') {
++ inTag = true;
++ sentence.append(word);
++ parsed.append(fm.elidedText(sentence, Qt::ElideRight, maxLine*4.6));
++ sentence = QString();
++ word = QString();
++ word.append(c);
++ } else if (c == '>') {
++ word.append(c);
++ if (!sentence.isEmpty()) {
++ parsed.append(fm.elidedText(sentence, Qt::ElideRight, maxLine*4.6));
++ sentence.clear();
++ }
++ inTag = false;
++ parsed.append(word);
++ word = QString();
++ } else if (c == ' ') {
++ word.append(c);
++ if (inTag) {
++ parsed.append(word);
++ } else {
++ sentence.append(word);
++ }
++ word = QString();
++ } else {
++ word.append(c);
++ }
++
++ ++i;
++ }
++
++ sentence.append(word);
++ parsed.append(fm.elidedText(sentence, Qt::ElideRight, maxLine*4.6));
++
++ messageLabel->setText(QLatin1String("<html>") + parsed + QLatin1String("</html>"));
++
++ if (!collapsed) {
++ icon->setGeometry(bigIconRect());
++ }
++}
++
++void NotificationWidgetPrivate::completeDetach()
++{
++ actions.clear();
++ actionOrder.clear();
++
++ delete actionsWidget;
++ actionsWidget = 0;
++}
++
++void NotificationWidgetPrivate::updateActions()
++{
++ if (actions.isEmpty() || actionsWidget) {
++ return;
++ }
++
++ actionsWidget = new QGraphicsWidget(body);
++ QGraphicsLinearLayout *layout = new QGraphicsLinearLayout(actionsWidget);
++ layout->setOrientation(Qt::Vertical);
++ layout->setContentsMargins(0, 0, 0, 0);
++ layout->addStretch();
++ actionsWidget->setContentsMargins(0, 0, 0, 0);
++
++ foreach (const QString &actionId, actionOrder) {
++ Plasma::PushButton *button = new Plasma::PushButton(actionsWidget);
++ QString &action = actions[actionId];
++ button->setText(action);
++ button->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum);
++ //TODO: we need smaller buttons but I don't like this method of accomplishing it.
++ button->setPreferredHeight(button->minimumHeight() - 6);
++
++ q->connect(button, SIGNAL(clicked()), signalMapper, SLOT(map()));
++ q->connect(button, SIGNAL(clicked()), q, SLOT(buttonClicked()));
++ signalMapper->setMapping(button, actionId);
++
++ layout->addItem(button);
++ }
++ layout->addStretch();
++ layout->activate();
++
++ if (actionsWidget->size().width() > q->size().width() * 0.4) {
++ layout->setOrientation(Qt::Horizontal);
++ bodyLayout->addItem(actionsWidget, 1, 0, 1, 2, Qt::AlignCenter);
++ } else {
++ bodyLayout->addItem(actionsWidget, 0, 2, Qt::AlignCenter);
++ }
++}
++
++void NotificationWidgetPrivate::buttonClicked()
++{
++ //a decsion has already been taken
++ if (actionsWidget) {
++ notification.data()->deleteLater();
++ }
++ emit q->actionTriggered(notification.data());
++}
++
++void NotificationWidgetPrivate::updateNotification()
++{
++ if (!notification) {
++ return;
++ }
++
++
++ //set text fields and icon
++ setTextFields(notification.data()->applicationName(), notification.data()->summary(), notification.data()->message());
++ icon->setIcon(notification.data()->applicationIcon());
++ messageLabel->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
++
++ //set the actions provided
++ actions = notification.data()->actions();
++ actionOrder = notification.data()->actionOrder();
++ updateActions();
++
++ if (!notification.data()->image().isNull()) {
++
++ icon->setIcon(QPixmap::fromImage(notification.data()->image()));
++
++ QSize imageSize = notification.data()->image().size();
++
++ if (imageSize.width() > KIconLoader::SizeLarge || imageSize.height() > KIconLoader::SizeLarge) {
++ imageSize.scale(KIconLoader::SizeLarge, KIconLoader::SizeLarge, Qt::KeepAspectRatio);
++ }
++
++ icon->setMaximumIconSize(QSizeF(qMin((int)KIconLoader::SizeLarge, imageSize.width()),
++ qMin((int)KIconLoader::SizeLarge, imageSize.height())));
++ }
++
++
++ //FIXME: this sounds wrong
++ q->setPreferredHeight(mainLayout->effectiveSizeHint(Qt::MinimumSize).height());
++}
++
++void NotificationWidgetPrivate::hideFinished()
++{
++ body->setVisible(!collapsed);
++ body->setFlag(QGraphicsItem::ItemClipsChildrenToShape, false);
++}
++
++#include "notificationwidget.moc"
+diff --git a/plasma/generic/applets/notifications/ui/notificationwidget.h b/plasma/generic/applets/notifications/ui/notificationwidget.h
+new file mode 100644
+index 0000000..7c1a436
+--- /dev/null
++++ b/plasma/generic/applets/notifications/ui/notificationwidget.h
+@@ -0,0 +1,72 @@
++/***************************************************************************
++ * notificationwidget.h *
++ * *
++ * Copyright (C) 2008 Dmitry Suzdalev <dimsuz@gmail.com> *
++ * Copyright (C) 2008 Rob Scheepmaker <r.scheepmaker@student.utwente.nl> *
++ * Copyright (C) 2008 Jason Stubbs <jasonbstubbs@gmail.com> *
++ * Copyright (C) 2010 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 of the *
++ * License, or (at your option) any later version. *
++ * *
++ * This program is distributed in the hope that it will be useful, *
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
++ * GNU Library General Public License for more details. *
++ * *
++ * You should have received a copy of the GNU Library General Public *
++ * License along with this library; if not, write to the *
++ * Free Software Foundation, Inc., *
++ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA . *
++ ***************************************************************************/
++
++#ifndef NOTIFICATIONWIDGET_H
++#define NOTIFICATIONWIDGET_H
++
++#include <Plasma/Frame>
++
++#include "../core/notification.h"
++
++class NotificationWidgetPrivate;
++
++/**
++ * A graphics item, representing notification message.
++ */
++class NotificationWidget : public QGraphicsWidget
++{
++ Q_OBJECT
++ Q_PROPERTY(qreal bodyHeight READ bodyHeight WRITE setBodyHeight)
++
++public:
++ NotificationWidget(Notification *notification, QGraphicsWidget *parent);
++ ~NotificationWidget();
++
++ void setCollapsed(bool collapse, bool animate = true);
++ bool isCollapsed() const;
++
++ void setTitleBarVisible(bool visible);
++ bool isTitleBarVisible() const;
++
++ qreal bodyHeight() const;
++ void setBodyHeight(const qreal height);
++
++ Notification *notification() const;
++
++protected:
++ void resizeEvent(QGraphicsSceneResizeEvent *event);
++
++Q_SIGNALS:
++ void actionTriggered(Notification *);
++
++private:
++ friend class NotificationWidgetPrivate;
++ NotificationWidgetPrivate* const d;
++
++ Q_PRIVATE_SLOT(d, void updateNotification())
++ Q_PRIVATE_SLOT(d, void buttonClicked())
++ Q_PRIVATE_SLOT(d, void hideFinished())
++};
++
++#endif // NOTIFICATIONWIDGET_H
+diff --git a/plasma/generic/applets/notifications/ui/stackdialog.cpp b/plasma/generic/applets/notifications/ui/stackdialog.cpp
+new file mode 100644
+index 0000000..9be364e
+--- /dev/null
++++ b/plasma/generic/applets/notifications/ui/stackdialog.cpp
+@@ -0,0 +1,446 @@
++/***************************************************************************
++ * stacdialog.h *
++ * Copyright (C) 2010 Marco Martin <notmart@gmail.com> *
++ * *
++ * This program is free software; you can redistribute it and/or modify *
++ * it under the terms of the GNU General Public License as published by *
++ * the Free Software Foundation; either version 2 of the License, or *
++ * (at your option) any later version. *
++ * *
++ * This program is distributed in the hope that it will be useful, *
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
++ * GNU General Public License for more details. *
++ * *
++ * You should have received a copy of the GNU General Public License *
++ * along with this program; if not, write to the *
++ * Free Software Foundation, Inc., *
++ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA . *
++ ***************************************************************************/
++
++#include "stackdialog.h"
++#include "notificationstack.h"
++#include "notificationwidget.h"
++
++#include <QApplication>
++#include <QDesktopWidget>
++#include <QGraphicsLayout>
++#include <QLayout>
++#include <QPropertyAnimation>
++#include <QTimer>
++
++#include <KWindowSystem>
++
++#include <Plasma/Applet>
++#include <Plasma/Containment>
++#include <Plasma/Corona>
++#include <Plasma/FrameSvg>
++#include <Plasma/WindowEffects>
++
++static const uint hideTimeout = 15 * 1000;
++
++StackDialog::StackDialog(QWidget *parent, Qt::WindowFlags f)
++ : Plasma::Dialog(parent, f),
++ m_applet(0),
++ m_windowToTile(0),
++ m_notificationStack(0),
++ m_view(0),
++ m_drawLeft(true),
++ m_drawRight(true),
++ m_autoHide(true),
++ m_hasCustomPosition(false)
++{
++ m_background = new Plasma::FrameSvg(this);
++ m_background->setImagePath("widgets/extender-background");
++ setWindowFlags(Qt::FramelessWindowHint | Qt::WindowStaysOnTopHint);
++ KWindowSystem::setType(winId(), NET::Dock);
++
++ m_showTimer = new QTimer(this);
++ m_showTimer->setSingleShot(true);
++ m_showTimer->setInterval(0);
++ connect(m_showTimer, SIGNAL(timeout()), this, SLOT(show()));
++
++ m_hideTimer = new QTimer(this);
++ m_hideTimer->setSingleShot(true);
++ connect(m_hideTimer, SIGNAL(timeout()), this, SLOT(hideRequested()));
++}
++
++StackDialog::~StackDialog()
++{
++}
++
++void StackDialog::setNotificationStack(NotificationStack *stack)
++{
++ setGraphicsWidget(stack);
++
++ if (!m_view && layout()) {
++ m_view = qobject_cast<QGraphicsView *>(layout()->itemAt(0)->widget());
++ if (m_view) {
++ m_view->installEventFilter(this);
++ }
++ }
++
++ if (m_notificationStack) {
++ disconnect(m_notificationStack, 0, this, 0);
++ }
++
++ m_notificationStack = stack;
++
++ connect(m_notificationStack, SIGNAL(updateRequested()), this, SLOT(update()));
++ connect(m_notificationStack, SIGNAL(hideRequested()), this, SLOT(hideRequested()));
++}
++
++NotificationStack *StackDialog::notificartionStack() const
++{
++ return m_notificationStack;
++}
++
++void StackDialog::setApplet(Plasma::Applet *applet)
++{
++ m_applet = applet;
++ adjustPosition(QPoint(-1, -1));
++}
++
++Plasma::Applet *StackDialog::applet() const
++{
++ return m_applet;
++}
++
++void StackDialog::setWindowToTile(QWidget *widget)
++{
++ if (m_windowToTile) {
++ m_windowToTile->removeEventFilter(this);
++ delete m_windowToTileAnimation;
++ }
++
++ m_windowToTile = widget;
++ m_windowToTile->installEventFilter(this);
++ m_windowToTileAnimation = new QPropertyAnimation(m_windowToTile, "pos", this);
++ m_windowToTileAnimation->setDuration(250);
++ m_windowToTileAnimation->setEasingCurve(QEasingCurve::InOutQuad);
++}
++
++QWidget *StackDialog::windowToTile() const
++{
++ return m_windowToTile;
++}
++
++void StackDialog::setAutoHide(const bool autoHide)
++{
++ m_autoHide = autoHide;
++}
++
++bool StackDialog::autoHide() const
++{
++ return m_autoHide;
++}
++
++void StackDialog::adjustWindowToTilePos()
++{
++ if (m_applet && m_windowToTile && m_hasCustomPosition) {
++ m_windowToTileAnimation->setStartValue(m_windowToTile->pos());
++
++ if (isVisible()) {
++ const QRect realScreenRect = qApp->desktop()->screenGeometry(m_applet->containment()->screen());
++ if (m_applet->location() == Plasma::TopEdge || (pos().y() - realScreenRect.top()) < m_windowToTile->size().height()) {
++ m_windowToTileAnimation->setEndValue(QPoint(m_windowToTile->pos().x(), geometry().bottom()));
++ } else {
++ m_windowToTileAnimation->setEndValue(QPoint(m_windowToTile->pos().x(), pos().y() - m_windowToTile->size().height()));
++ }
++ } else if (m_applet && m_applet->containment() && m_applet->containment()->corona()) {
++ m_windowToTileAnimation->setEndValue(QPoint(m_applet->containment()->corona()->popupPosition(m_applet, m_windowToTile->size())));
++ }
++ m_windowToTileAnimation->start();
++ }
++}
++
++void StackDialog::paintEvent(QPaintEvent *e)
++{
++ Plasma::Dialog::paintEvent(e);
++
++ QPainter painter(this);
++ if (m_notificationStack) {
++ //FIXME: assumption++
++ QGraphicsLayout *mainLayout = static_cast<QGraphicsLayout *>(m_notificationStack->layout());
++
++ if (!m_notificationStack->currentNotificationWidget() || mainLayout->count() < 2) {
++ return;
++ }
++
++ for (int i = 0; i < mainLayout->count(); ++i) {
++ //assumption++ all items are NotificationWidget
++ NotificationWidget *nw = static_cast<NotificationWidget *>(mainLayout->itemAt(i));
++
++ //first element
++ if (i == 0 && m_notificationStack->currentNotificationWidget() != nw) {
++ continue;
++ } else if (i == 0) {
++ m_background->setEnabledBorders((Plasma::FrameSvg::EnabledBorders)(Plasma::FrameSvg::AllBorders&~Plasma::FrameSvg::TopBorder));
++ //last element
++ } else if (i == mainLayout->count()-1 && m_notificationStack->currentNotificationWidget() != nw) {
++ continue;
++ } else if (i == mainLayout->count()-1) {
++ m_background->setEnabledBorders((Plasma::FrameSvg::EnabledBorders)Plasma::FrameSvg::AllBorders&~Plasma::FrameSvg::BottomBorder);
++ //element under the active one
++ } else if (m_notificationStack->currentNotificationWidget()->pos().y() < nw->pos().y()) {
++ m_background->setEnabledBorders((Plasma::FrameSvg::EnabledBorders)Plasma::FrameSvg::AllBorders&~Plasma::FrameSvg::TopBorder);
++ //element over the active one
++ } else if (m_notificationStack->currentNotificationWidget()->pos().y() > nw->pos().y()) {
++ m_background->setEnabledBorders((Plasma::FrameSvg::EnabledBorders)Plasma::FrameSvg::AllBorders&~Plasma::FrameSvg::BottomBorder);
++ //active element
++ } else {
++ m_background->setEnabledBorders(Plasma::FrameSvg::AllBorders);
++ }
++
++ qreal left, top, right, bottom;
++ m_background->getMargins(left, top, right, bottom);
++ m_background->resizeFrame(QSizeF(size().width(), nw->size().height() + top + bottom));
++
++ Plasma::FrameSvg::EnabledBorders borders = m_background->enabledBorders();
++
++ if (!m_drawLeft) {
++ borders &= ~Plasma::FrameSvg::LeftBorder;
++ }
++
++ if (!m_drawRight) {
++ borders &= ~Plasma::FrameSvg::RightBorder;
++ }
++
++ m_background->setEnabledBorders(borders);
++
++ const int topMargin = contentsRect().top();
++ m_background->paintFrame(&painter, QPointF(0, nw->pos().y() - top + topMargin));
++ }
++ }
++}
++
++void StackDialog::perhapsShow()
++{
++ if (m_applet && m_applet->view()) {
++ // we use a timer here because when the stack is going to be shown, the applet is likely
++ // to also be in movement (e.g. from hidden away to now shown to the user) and that will
++ // likely result in an odd geometry for the applet when popupPosition is called on this
++ // dialog. so instead we wait a bit after the show request to allow the applet to find
++ // it's proper place
++ m_showTimer->start();
++ }
++}
++
++void StackDialog::showEvent(QShowEvent *event)
++{
++ Q_UNUSED(event)
++
++ adjustPosition(adjustedSavedPos());
++ adjustWindowToTilePos();
++
++ if (m_autoHide) {
++ m_hideTimer->start(hideTimeout);
++ }
++
++ Plasma::Dialog::showEvent(event);
++}
++
++void StackDialog::hideRequested()
++{
++ // we have this method because hide() may not always cause a hideEvent, e.g. when
++ // the StackDialog has not been shown yet .. however, we always want to ensure the
++ // show timer is stopped. we also stop it in the hideEvent for completeness and to
++ // catch any direct calls to hide() that may happen
++ m_showTimer->stop();
++ hide();
++}
++
++void StackDialog::hideEvent(QHideEvent *event)
++{
++ Q_UNUSED(event)
++
++ m_showTimer->stop();
++ m_hideTimer->stop();
++
++ adjustWindowToTilePos();
++
++ m_notificationStack->adjustSize();
++ Plasma::Dialog::hideEvent(event);
++}
++
++void StackDialog::resizeEvent(QResizeEvent *event)
++{
++ Q_UNUSED(event)
++ adjustWindowToTilePos();
++ Plasma::Dialog::resizeEvent(event);
++ if (m_hasCustomPosition) {
++ adjustPosition(pos());
++ } else if (m_applet && m_applet->containment() && m_applet->containment()->corona()) {
++ move(m_applet->containment()->corona()->popupPosition(m_applet, size()));
++ }
++}
++
++void StackDialog::moveEvent(QMoveEvent *event)
++{
++ Q_UNUSED(event)
++
++ adjustWindowToTilePos();
++ Plasma::Dialog::moveEvent(event);
++}
++
++void StackDialog::enterEvent(QEvent *event)
++{
++ Q_UNUSED(event)
++
++ m_hideTimer->stop();
++}
++
++void StackDialog::leaveEvent(QEvent *event)
++{
++ Q_UNUSED(event)
++
++ if (m_autoHide) {
++ m_hideTimer->start(hideTimeout);
++ }
++ Plasma::Dialog::leaveEvent(event);
++}
++
++void StackDialog::adjustPosition(const QPoint &pos)
++{
++ if (!m_applet) {
++ return;
++ }
++
++ if (pos == QPoint(-1, -1)) {
++ const QPoint popupPosition = m_applet->containment()->corona()->popupPosition(m_applet, size());
++ move(popupPosition);
++ Plasma::WindowEffects::slideWindow(this, m_applet->location());
++ m_hasCustomPosition = false;
++ } else {
++ QPoint customPosition = pos;
++
++ if (m_applet->containment() &&
++ m_applet->containment()->corona() &&
++ m_notificationStack) {
++ QRect screenRect = m_applet->containment()->corona()->availableScreenRegion(m_applet->containment()->screen()).boundingRect();
++
++ customPosition.rx() = qMax(customPosition.x(), screenRect.left());
++ customPosition.ry() = qMax(customPosition.y(), screenRect.top());
++ customPosition.rx() = qMin(customPosition.x() + size().width(), screenRect.right()) - size().width();
++ customPosition.ry() = qMin(customPosition.y() + size().height(), screenRect.bottom()) - size().height();
++
++ bool closerToBottom = (customPosition.ry() > (screenRect.height() / 2));
++ if (!m_lastSize.isNull() && closerToBottom
++ && (m_lastSize.height() > size().height())) {
++ customPosition.ry() += m_lastSize.height() - size().height();
++ }
++ m_lastSize = size();
++ }
++
++ move(customPosition);
++ Plasma::WindowEffects::slideWindow(this, Plasma::Desktop);
++ m_hasCustomPosition = true;
++ }
++}
++
++void StackDialog::savePosition(const QPoint& pos)
++{
++ if (!m_applet || !m_applet->containment()) {
++ return;
++ }
++
++ QByteArray horizSide, vertSide;
++ QPoint pixelsToSave;
++ const QRect realScreenRect = qApp->desktop()->screenGeometry(m_applet->containment()->screen());
++
++ int screenRelativeX = pos.x() - realScreenRect.x();
++ int diffWithRight = realScreenRect.width() - (screenRelativeX + size().width());
++ if (screenRelativeX < diffWithRight) {
++ horizSide = "l";
++ pixelsToSave.rx() = screenRelativeX;
++ } else {
++ horizSide = "r";
++ pixelsToSave.rx() = diffWithRight;
++ }
++
++ int screenRelativeY = pos.y() - realScreenRect.y();
++ int diffWithBottom = realScreenRect.height() - (screenRelativeY + size().height());
++ if (screenRelativeY < diffWithBottom) {
++ vertSide = "t";
++ pixelsToSave.ry() = screenRelativeY;
++ } else {
++ vertSide = "b";
++ pixelsToSave.ry() = diffWithBottom;
++ }
++
++ kDebug() << "Affinity-v" << vertSide;
++ kDebug() << "Affinity-h" << horizSide;
++ kDebug() << "Y: " << pixelsToSave.ry();
++ kDebug() << "X: " << pixelsToSave.rx();
++
++ m_applet->config().writeEntry("customPosition", pixelsToSave);
++ m_applet->config().writeEntry("customPositionAffinityHoriz", horizSide);
++ m_applet->config().writeEntry("customPositionAffinityVert", vertSide);
++}
++
++QPoint StackDialog::adjustedSavedPos() const
++{
++ if (!m_applet) {
++ return QPoint(-1, -1);
++ }
++
++ QPoint pos = m_applet->config().readEntry("customPosition", QPoint(-1, -1));
++
++ if (pos != QPoint(-1, -1)) {
++ const QRect realScreenRect = qApp->desktop()->screenGeometry(m_applet->containment()->screen());
++ QByteArray horizSide = m_applet->config().readEntry("customPositionAffinityHoriz").toLatin1();
++ QByteArray vertSide = m_applet->config().readEntry("customPositionAffinityVert").toLatin1();
++
++ if (horizSide == "l") {
++ pos.rx() += realScreenRect.x();
++ } else {
++ pos.rx() = realScreenRect.x() + (realScreenRect.width() - pos.rx() - size().width());
++ }
++
++ if (vertSide == "t") {
++ pos.ry() += realScreenRect.y();
++ } else {
++ pos.ry() = realScreenRect.y() + (realScreenRect.height() - pos.ry() - size().height());
++ }
++ }
++
++ return pos;
++}
++
++bool StackDialog::event(QEvent *event)
++{
++ bool ret = Dialog::event(event);
++
++ if (event->type() == QEvent::ContentsRectChange) {
++ int left, top, right, bottom;
++ getContentsMargins(&left, &top, &right, &bottom);
++
++ m_drawLeft = (left != 0);
++ m_drawRight = (right != 0);
++ update();
++ }
++
++ return ret;
++}
++
++bool StackDialog::eventFilter(QObject *watched, QEvent *event)
++{
++ if (m_windowToTile && watched == m_windowToTile &&
++ event->type() == QEvent::Show && isVisible()) {
++ adjustWindowToTilePos();
++ } else if (watched == m_notificationStack && event->type() == QEvent::GraphicsSceneMousePress) {
++ QGraphicsSceneMouseEvent *me = static_cast<QGraphicsSceneMouseEvent *>(event);
++ m_dragPos = me->pos().toPoint();
++ } else if (watched == m_notificationStack && event->type() == QEvent::GraphicsSceneMouseMove) {
++ QGraphicsSceneMouseEvent *me = static_cast<QGraphicsSceneMouseEvent *>(event);
++ adjustPosition(me->screenPos() - m_dragPos);
++ } else if (watched == m_notificationStack && event->type() == QEvent::GraphicsSceneMouseRelease) {
++ QGraphicsSceneMouseEvent *me = static_cast<QGraphicsSceneMouseEvent *>(event);
++ adjustPosition(me->screenPos() - m_dragPos);
++ savePosition(me->screenPos() - m_dragPos);
++ }
++
++ return Plasma::Dialog::eventFilter(watched, event);
++}
++
++#include "stackdialog.moc"
+diff --git a/plasma/generic/applets/notifications/ui/stackdialog.h b/plasma/generic/applets/notifications/ui/stackdialog.h
+new file mode 100644
+index 0000000..4360c4a
+--- /dev/null
++++ b/plasma/generic/applets/notifications/ui/stackdialog.h
+@@ -0,0 +1,99 @@
++/***************************************************************************
++ * stacdialog.h *
++ * Copyright (C) 2010 Marco Martin <notmart@gmail.com> *
++ * *
++ * This program is free software; you can redistribute it and/or modify *
++ * it under the terms of the GNU General Public License as published by *
++ * the Free Software Foundation; either version 2 of the License, or *
++ * (at your option) any later version. *
++ * *
++ * This program is distributed in the hope that it will be useful, *
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
++ * GNU General Public License for more details. *
++ * *
++ * You should have received a copy of the GNU General Public License *
++ * along with this program; if not, write to the *
++ * Free Software Foundation, Inc., *
++ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA . *
++ ***************************************************************************/
++
++#ifndef STACKDIALOG_H
++#define STACKDIALOG_H
++
++#include <Plasma/Dialog>
++
++#include <QPoint>
++
++class QPropertyAnimation;
++class QTimer;
++
++namespace Plasma
++{
++ class Applet;
++ class FrameSvg;
++}
++
++class NotificationStack;
++
++
++class StackDialog : public Plasma::Dialog
++{
++ Q_OBJECT
++public:
++ StackDialog(QWidget *parent = 0, Qt::WindowFlags f = Qt::Window);
++ ~StackDialog();
++
++ void setNotificationStack(NotificationStack *stack);
++ NotificationStack *notificartionStack() const;
++
++ void setApplet(Plasma::Applet *applet);
++ Plasma::Applet *applet() const;
++
++ void setWindowToTile(QWidget *widget);
++ QWidget *windowToTile() const;
++
++ void setAutoHide(const bool autoHide);
++ bool autoHide() const;
++
++public Q_SLOTS:
++ void perhapsShow();
++
++protected:
++ void adjustWindowToTilePos();
++
++ void paintEvent(QPaintEvent *e);
++ void showEvent(QShowEvent *event);
++ void hideEvent(QHideEvent *event);
++ void enterEvent(QEvent *event);
++ void leaveEvent(QEvent *event);
++ void resizeEvent(QResizeEvent *event);
++ void moveEvent(QMoveEvent *event);
++ bool event(QEvent *event);
++ bool eventFilter(QObject *watched, QEvent *event);
++ void adjustPosition(const QPoint &pos = QPoint(-1, -1));
++ void savePosition(const QPoint &pos);
++ QPoint adjustedSavedPos() const;
++
++private Q_SLOTS:
++ void hideRequested();
++
++private:
++ Plasma::Applet *m_applet;
++ QWidget *m_windowToTile;
++ QPropertyAnimation *m_windowToTileAnimation;
++ QPoint m_dragPos;
++ QSize m_lastSize;
++
++ Plasma::FrameSvg *m_background;
++ NotificationStack *m_notificationStack;
++ QTimer *m_showTimer;
++ QTimer *m_hideTimer;
++ QGraphicsView *m_view;
++ bool m_drawLeft;
++ bool m_drawRight;
++ bool m_autoHide;
++ bool m_hasCustomPosition;
++};
++
++#endif
diff --git a/kde-base/plasma-workspace/files/plasma-workspace-4.10.1-noplasmalock.patch b/kde-base/plasma-workspace/files/plasma-workspace-4.10.1-noplasmalock.patch
new file mode 100644
index 0000000..2380d20
--- /dev/null
+++ b/kde-base/plasma-workspace/files/plasma-workspace-4.10.1-noplasmalock.patch
@@ -0,0 +1,11 @@
+diff --git a/plasma/screensaver/CMakeLists.txt b/plasma/screensaver/CMakeLists.txt
+index 514c856..9dcb5f4 100644
+--- a/plasma/screensaver/CMakeLists.txt
++++ b/plasma/screensaver/CMakeLists.txt
+@@ -1,6 +1,3 @@
+ add_subdirectory(containments)
+-if(NOT WIN32)
+-add_subdirectory(shell)
+-endif(NOT WIN32)
+
+
diff --git a/kde-base/plasma-workspace/metadata.xml b/kde-base/plasma-workspace/metadata.xml
new file mode 100644
index 0000000..3b3fc4f
--- /dev/null
+++ b/kde-base/plasma-workspace/metadata.xml
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE pkgmetadata SYSTEM "http://www.gentoo.org/dtd/metadata.dtd">
+<pkgmetadata>
+<herd>kde</herd>
+<use>
+ <flag name="json">Enable JSON support via <pkg>dev-libs/qjson</pkg></flag>
+ <flag name="qalculate">Enable Qalculate runner using <pkg>sci-libs/libqalculate</pkg></flag>
+</use>
+</pkgmetadata>
diff --git a/kde-base/plasma-workspace/plasma-workspace-4.11.2-r2.ebuild b/kde-base/plasma-workspace/plasma-workspace-4.11.2-r2.ebuild
new file mode 100644
index 0000000..4ed968e
--- /dev/null
+++ b/kde-base/plasma-workspace/plasma-workspace-4.11.2-r2.ebuild
@@ -0,0 +1,125 @@
+# Copyright 1999-2013 Gentoo Foundation
+# Distributed under the terms of the GNU General Public License v2
+# $Header: $
+
+EAPI=5
+
+DECLARATIVE_REQUIRED="always"
+KDE_HANDBOOK="optional"
+KMNAME="kde-workspace"
+KMMODULE="plasma"
+PYTHON_COMPAT=( python2_{6,7} )
+OPENGL_REQUIRED="always"
+inherit python-single-r1 kde4-meta
+
+DESCRIPTION="Plasma: KDE desktop framework"
+KEYWORDS="~amd64 ~arm ~ppc ~ppc64 ~x86 ~amd64-linux ~x86-linux"
+IUSE="debug gps json python qalculate semantic-desktop"
+
+REQUIRED_USE="python? ( ${PYTHON_REQUIRED_USE} )"
+
+COMMONDEPEND="
+ dev-libs/libdbusmenu-qt
+ >=dev-qt/qtcore-4.8.4-r3:4
+ !kde-misc/ktouchpadenabler
+ $(add_kdebase_dep kactivities)
+ $(add_kdebase_dep kdelibs 'semantic-desktop?')
+ $(add_kdebase_dep kdepimlibs)
+ $(add_kdebase_dep kephal)
+ $(add_kdebase_dep ksysguard)
+ $(add_kdebase_dep libkworkspace)
+ $(add_kdebase_dep libplasmaclock)
+ $(add_kdebase_dep libplasmagenericshell)
+ $(add_kdebase_dep libtaskmanager)
+ x11-libs/libX11
+ x11-libs/libXcomposite
+ x11-libs/libXdamage
+ x11-libs/libXext
+ x11-libs/libXfixes
+ x11-libs/libXi
+ x11-libs/libXrender
+ gps? ( >=sci-geosciences/gpsd-2.37 )
+ json? ( dev-libs/qjson )
+ python? (
+ ${PYTHON_DEPS}
+ >=dev-python/PyQt4-4.4.0[X,${PYTHON_USEDEP}]
+ $(add_kdebase_dep pykde4 "${PYTHON_USEDEP}")
+ )
+ qalculate? ( sci-libs/libqalculate )
+ semantic-desktop? (
+ dev-libs/soprano
+ $(add_kdebase_dep nepomuk-core)
+ )
+"
+DEPEND="${COMMONDEPEND}
+ dev-libs/boost
+ x11-proto/compositeproto
+ x11-proto/damageproto
+ x11-proto/fixesproto
+ x11-proto/renderproto
+"
+RDEPEND="${COMMONDEPEND}
+ $(add_kdebase_dep plasma-runtime)
+"
+
+KMEXTRA="
+ appmenu/
+ ktouchpadenabler/
+ statusnotifierwatcher/
+"
+KMEXTRACTONLY="
+ kcheckpass/
+ krunner/dbus/org.freedesktop.ScreenSaver.xml
+ krunner/dbus/org.kde.krunner.App.xml
+ ksmserver/org.kde.KSMServerInterface.xml
+ ksmserver/screenlocker/
+ libs/kephal/
+ libs/kworkspace/
+ libs/taskmanager/
+ libs/plasmagenericshell/
+ libs/ksysguard/
+ libs/kdm/kgreeterplugin.h
+ ksysguard/
+"
+
+KMLOADLIBS="libkworkspace libplasmaclock libplasmagenericshell libtaskmanager"
+
+PATCHES=( "${FILESDIR}/${PN}-4.10.1-noplasmalock.patch" "${FILESDIR}/oldnotify.patch" )
+
+pkg_setup() {
+ if use python ; then
+ python-single-r1_pkg_setup
+ fi
+ kde4-meta_pkg_setup
+}
+
+src_unpack() {
+ if use handbook; then
+ KMEXTRA+=" doc/plasma-desktop"
+ fi
+
+ kde4-meta_src_unpack
+}
+
+src_configure() {
+ mycmakeargs=(
+ $(cmake-utils_use_with gps libgps)
+ $(cmake-utils_use_with json QJSON)
+ $(cmake-utils_use_with python PythonLibrary)
+ $(cmake-utils_use_with qalculate)
+ $(cmake-utils_use_with semantic-desktop Akonadi)
+ $(cmake-utils_use_with semantic-desktop NepomukCore)
+ $(cmake-utils_use_with semantic-desktop Soprano)
+ -DWITH_Xmms=OFF
+ )
+
+ kde4-meta_src_configure
+}
+
+src_install() {
+ kde4-meta_src_install
+
+ if use python; then
+ python_optimize "${ED}"
+ fi
+}
^ permalink raw reply related [flat|nested] 2+ messages in thread
* [gentoo-commits] dev/dilfridge:master commit in: kde-base/plasma-workspace/files/, kde-base/plasma-workspace/
@ 2014-04-29 22:47 Andreas Hüttel
0 siblings, 0 replies; 2+ messages in thread
From: Andreas Hüttel @ 2014-04-29 22:47 UTC (permalink / raw
To: gentoo-commits
commit: e8643b94912d0327564600ee9196efebc06394ec
Author: Andreas K. Huettel (dilfridge) <dilfridge <AT> gentoo <DOT> org>
AuthorDate: Tue Apr 29 22:47:09 2014 +0000
Commit: Andreas Hüttel <dilfridge <AT> gentoo <DOT> org>
CommitDate: Tue Apr 29 22:47:34 2014 +0000
URL: http://git.overlays.gentoo.org/gitweb/?p=dev/dilfridge.git;a=commit;h=e8643b94
[kde-base/plasma-workspace] clean up
---
kde-base/plasma-workspace/Manifest | 1 -
kde-base/plasma-workspace/files/oldnotify.patch | 8557 --------------------
.../plasma-workspace-4.10.1-noplasmalock.patch | 11 -
kde-base/plasma-workspace/metadata.xml | 9 -
.../plasma-workspace-4.11.2-r2.ebuild | 125 -
5 files changed, 8703 deletions(-)
diff --git a/kde-base/plasma-workspace/Manifest b/kde-base/plasma-workspace/Manifest
deleted file mode 100644
index 48cd387..0000000
--- a/kde-base/plasma-workspace/Manifest
+++ /dev/null
@@ -1 +0,0 @@
-DIST kde-workspace-4.11.2.tar.xz 13870828 SHA256 260e46f30b8faaa1ad834d9fa69465ab1f565cb18a174bd814327083bb28d917 SHA512 8d034e147ebb630941465b77e302aa2385c2f1419847e8a65ac19d445846435b61d78323fdb094b9a71089ab183c96af141db93c7c9c53a9b30c84ae69bddf2a WHIRLPOOL 0db07ef64caf873c0aa6398a28b92fdc9334eb04897c9a71ed0e3c54ad77e9e8dc23b13b6343844650a2ec7c2784664185f01cf64424d694a8c33167affb5720
diff --git a/kde-base/plasma-workspace/files/oldnotify.patch b/kde-base/plasma-workspace/files/oldnotify.patch
deleted file mode 100644
index c4c5c3b..0000000
--- a/kde-base/plasma-workspace/files/oldnotify.patch
+++ /dev/null
@@ -1,8557 +0,0 @@
-diff --git a/plasma/generic/applets/notifications/CMakeLists.txt b/plasma/generic/applets/notifications/CMakeLists.txt
-index 02afb93..a38fe47 100644
---- a/plasma/generic/applets/notifications/CMakeLists.txt
-+++ b/plasma/generic/applets/notifications/CMakeLists.txt
-@@ -1,2 +1,47 @@
--installPackage(. org.kde.notifications)
-+project(plasma-notifications)
-+#TODO: see if is still the case
-+# 'engineName' causes error
-+kde4_no_enable_final(plasma-notifications)
-
-+set(notifications_SRCS
-+
-+ core/notificationsmanager.cpp
-+ core/protocol.cpp
-+ core/notification.cpp
-+ core/completedjobnotification.cpp
-+ core/job.cpp
-+
-+ protocols/notifications/dbusnotificationprotocol.cpp
-+ protocols/notifications/dbusnotification.cpp
-+
-+ protocols/jobs/dbusjobprotocol.cpp
-+ protocols/jobs/dbusjob.cpp
-+
-+ ui/busywidget.cpp
-+ ui/notifications.cpp
-+ ui/notificationwidget.cpp
-+ ui/jobtotalswidget.cpp
-+ ui/jobwidget.cpp
-+ ui/notificationgroup.cpp
-+ ui/notificationstack.cpp
-+ ui/stackdialog.cpp
-+ )
-+
-+kde4_add_ui_files(notifications_SRCS
-+ ui/notificationsconfig.ui)
-+
-+
-+include (CheckLibraryExists)
-+check_library_exists (Xss XScreenSaverQueryInfo "" HAVE_LIBXSS)
-+configure_file (${CMAKE_CURRENT_SOURCE_DIR}/config-notifications.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config-notifications.h)
-+IF (HAVE_LIBXSS)
-+ SET (IDLE_DETECTION_LIB "Xss")
-+ENDIF (HAVE_LIBXSS)
-+
-+
-+kde4_add_plugin(plasma_applet_notifications ${notifications_SRCS})
-+include_directories(${CMAKE_SOURCE_DIR})
-+target_link_libraries(plasma_applet_notifications ${KDE4_KDEUI_LIBS} ${KDE4_PLASMA_LIBS} ${X11_LIBRARIES} ${X11_Xrender_LIB} ${X11_Xfixes_LIB} ${X11_Xdamage_LIB} ${X11_Xcomposite_LIB} ${KDE4_SOLID_LIBS} ${IDLE_DETECTION_LIB})
-+
-+install(TARGETS plasma_applet_notifications DESTINATION ${PLUGIN_INSTALL_DIR})
-+install(FILES plasma-applet-notifications.desktop DESTINATION ${SERVICES_INSTALL_DIR})
-diff --git a/plasma/generic/applets/notifications/Messages.sh b/plasma/generic/applets/notifications/Messages.sh
-old mode 100644
-new mode 100755
-index bd3e2d9..5582bc6
---- a/plasma/generic/applets/notifications/Messages.sh
-+++ b/plasma/generic/applets/notifications/Messages.sh
-@@ -1,5 +1,4 @@
- #! /usr/bin/env bash
--$EXTRACTRC `find . -name \*.rc -o -name \*.ui -o -name \*.kcfg` >> rc.cpp
--$XGETTEXT `find . -name \*.qml` -L Java -o $podir/plasma_applet_org.kde.notifications.pot
--$XGETTEXT rc.cpp -jo $podir/plasma_applet_org.kde.notifications.pot
-+$EXTRACTRC `find . -name '*.ui'` >> rc.cpp
-+$XGETTEXT `find . -name '*.cpp'` -o $podir/plasma_applet_notifications.pot
- rm -f rc.cpp
-diff --git a/plasma/generic/applets/notifications/config-notifications.h.cmake b/plasma/generic/applets/notifications/config-notifications.h.cmake
-new file mode 100644
-index 0000000..aac3113
---- /dev/null
-+++ b/plasma/generic/applets/notifications/config-notifications.h.cmake
-@@ -0,0 +1 @@
-+#cmakedefine HAVE_LIBXSS
-diff --git a/plasma/generic/applets/notifications/contents/config/main.xml b/plasma/generic/applets/notifications/contents/config/main.xml
-deleted file mode 100644
-index 399159d..0000000
---- a/plasma/generic/applets/notifications/contents/config/main.xml
-+++ /dev/null
-@@ -1,23 +0,0 @@
--<?xml version="1.0" encoding="UTF-8"?>
--<kcfg xmlns="http://www.kde.org/standards/kcfg/1.0"
-- xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
-- xsi:schemaLocation="http://www.kde.org/standards/kcfg/1.0
-- http://www.kde.org/standards/kcfg/1.0/kcfg.xsd" >
-- <kcfgfile name=""/>
--
-- <group name="General">
-- <entry name="CustomPosition" type="Point">
-- <label>where the notification popup will be</label>
-- <default>-1,-1</default>
-- </entry>
-- <entry name="ShowNotifications" type="Bool">
-- <label>Show the notifications</label>
-- <default>true</default>
-- </entry>
-- <entry name="ShowJobs" type="Bool">
-- <label>Show the jobs progress</label>
-- <default>true</default>
-- </entry>
-- </group>
--
--</kcfg>
-diff --git a/plasma/generic/applets/notifications/contents/ui/JobDelegate.qml b/plasma/generic/applets/notifications/contents/ui/JobDelegate.qml
-deleted file mode 100644
-index e94866d..0000000
---- a/plasma/generic/applets/notifications/contents/ui/JobDelegate.qml
-+++ /dev/null
-@@ -1,310 +0,0 @@
--/*
-- * Copyright 2011 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 Library 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.
-- */
--
--import QtQuick 1.0
--import org.kde.plasma.core 0.1 as PlasmaCore
--import org.kde.plasma.components 0.1 as PlasmaComponents
--import org.kde.plasma.graphicswidgets 0.1 as PlasmaWidgets
--import org.kde.qtextracomponents 0.1
--
--PlasmaComponents.ListItem {
-- id: notificationItem
-- width: popupFlickable.width
--
-- property int toolIconSize: theme.smallMediumIconSize
-- property int layoutSpacing: 4
--
-- function getData(data, name, defaultValue) {
-- return data[modelData] ? (data[modelData][name] ? data[modelData][name] : defaultValue) : defaultValue;
-- }
--
-- property string labelName0: getData(jobsSource.data, "labelName0", '')
-- property string label0: getData(jobsSource.data, "label0", '')
-- property string labelName1: getData(jobsSource.data, "labelName1", '')
-- property string label1: getData(jobsSource.data, "label1", '')
-- property string jobstate: getData(jobsSource.data, "state", '')
-- property int eta: getData(jobsSource.data, "eta", 0)
-- property string speed: getData(jobsSource.data, "speed", '')
--
-- Column {
-- spacing: notificationItem.layoutSpacing
-- width: parent.width
-- PlasmaComponents.Label {
-- text: getData(jobsSource.data, "infoMessage", '')
-- font.bold: true
-- color: theme.textColor
-- anchors.horizontalCenter: parent.horizontalCenter
-- }
-- Grid {
-- anchors {
-- left: parent.left
-- right: parent.right
-- rightMargin: notificationItem.layoutSpacing
-- }
-- spacing: notificationItem.layoutSpacing
-- rows: 4
-- columns: 2
--
-- PlasmaComponents.Label {
-- id: labelName0Text
-- text: labelName0 ? i18n("%1:", labelName0) : ''
-- width: Math.max(paintedWidth, labelName1Text.paintedWidth)
-- horizontalAlignment: Text.AlignRight
-- visible: labelName0 != ''
-- }
-- PlasmaComponents.Label {
-- id: label0Text
-- text: label0 ? label0 : ''
-- width: parent.width - labelName0Text.width
-- elide: Text.ElideMiddle
-- visible: label0 != ''
--
-- PlasmaCore.ToolTip {
-- target: label0Text
-- subText: label0Text.truncated ? label0 : ""
-- }
--
-- }
-- PlasmaComponents.Label {
-- id: labelName1Text
-- text: labelName1 ? i18n("%1:", labelName1) : ''
-- width: Math.max(paintedWidth, labelName0Text.paintedWidth)
-- horizontalAlignment: Text.AlignRight
-- visible: labelName1 != ''
-- }
-- PlasmaComponents.Label {
-- id: label1Text
-- text: label1 ? label1 : ''
-- width: parent.width - labelName0Text.width
-- elide: Text.ElideMiddle
-- visible: label1 != ''
--
-- PlasmaCore.ToolTip {
-- target: label1Text
-- subText: label1Text.truncated ? label1 : ""
-- }
-- }
-- QIconItem {
-- icon: getData(jobsSource.data, "appIconName", '')
-- width: notificationItem.toolIconSize
-- height: width
-- anchors {
-- verticalCenter: progressItem.verticalCenter
-- right: progressItem.left
-- rightMargin: notificationItem.layoutSpacing
-- }
-- }
-- Item {
-- id: progressItem
-- width: parent.width - labelName0Text.width
-- height: childrenRect.height
-- PlasmaComponents.ProgressBar {
-- width: parent.width - pauseButton.width*2 - theme.largeIconSize - notificationItem.layoutSpacing*3
-- height: 16
-- orientation: Qt.Horizontal
-- minimumValue: 0
-- maximumValue: 100
-- //percentage doesn't always exist, so doesn't get in the model
-- value: getData(jobsSource.data, "percentage", 0)
--
-- anchors {
-- left: parent.left
-- right: buttonsRow.left
-- verticalCenter: parent.verticalCenter
-- rightMargin: notificationItem.layoutSpacing
-- }
-- }
-- Row {
-- id: buttonsRow
-- spacing: notificationItem.layoutSpacing
-- anchors.right: parent.right
-- PlasmaComponents.ToolButton {
-- id: pauseButton
-- width: notificationItem.toolIconSize
-- height: width
-- iconSource: jobstate == "suspended" ? "media-playback-start" : "media-playback-pause"
-- flat: false
-- onClicked: {
-- var operationName = "suspend"
-- if (jobstate == "suspended") {
-- operationName = "resume"
-- }
-- var service = jobsSource.serviceForSource(modelData)
-- var operation = service.operationDescription(operationName)
-- service.startOperationCall(operation)
-- }
-- }
-- PlasmaComponents.ToolButton {
-- id: stopButton
-- width: notificationItem.toolIconSize
-- height: width
-- iconSource: "media-playback-stop"
-- flat: false
-- onClicked: {
-- var service = jobsSource.serviceForSource(modelData)
-- var operation = service.operationDescription("stop")
-- service.startOperationCall(operation)
-- }
-- }
-- }
-- }
-- PlasmaComponents.ToolButton {
-- id: expandButton
-- width: notificationItem.toolIconSize
-- height: width
-- flat: false
-- iconSource: checked ? "list-remove" : "list-add"
-- checkable: true
-- anchors {
-- right: speedLabel.left
-- rightMargin: notificationItem.layoutSpacing
-- verticalCenter: speedLabel.verticalCenter
-- }
-- }
-- PlasmaComponents.Label {
-- id: speedLabel
-- text: eta > 0 ? i18nc("Speed and estimated time to completition", "%1 (%2 remaining)", speed, locale.prettyFormatDuration(eta)) : speed
-- }
-- }
--
--
-- Item {
-- id: detailsItem
-- state: expandButton.checked ? "expanded" : "collapsed"
-- anchors {
-- left: parent.left
-- right: parent.right
-- leftMargin: speedLabel.x
-- }
-- property Item contentsItem
-- Component {
-- id: detailsComponent
-- Column {
-- id: detailsColumn
-- anchors {
-- left: parent.left
-- right: parent.right
-- }
--
-- function localizeProcessedAmount(id) {
-- //if bytes localise the unit
-- if (jobsSource.data[modelData]["processedUnit"+id] == "bytes") {
-- return i18nc("How much many bytes (or whether unit in the locale has been copied over total", "%1 of %2",
-- locale.formatByteSize(jobsSource.data[modelData]["processedAmount"+id]),
-- locale.formatByteSize(jobsSource.data[modelData]["totalAmount"+id]))
-- //else print something only if is interesting data (ie more than one file/directory etc to copy
-- } else if (jobsSource.data[modelData]["totalAmount"+id] > 1) {
-- return i18n( "%1 of %2 %3",
-- jobsSource.data[modelData]["processedAmount"+id],
-- jobsSource.data[modelData]["totalAmount"+id],
-- jobsSource.data[modelData]["processedUnit"+id])
-- } else {
-- return ""
-- }
-- }
-- PlasmaComponents.Label {
-- text: jobsSource.data[modelData] ? detailsColumn.localizeProcessedAmount(0) : ""
-- anchors.left: parent.left
-- visible: text != ""
-- }
-- PlasmaComponents.Label {
-- text: jobsSource.data[modelData] ? detailsColumn.localizeProcessedAmount(1) : ""
-- anchors.left: parent.left
-- visible: text != ""
-- }
-- PlasmaComponents.Label {
-- text: jobsSource.data[modelData] ? detailsColumn.localizeProcessedAmount(2) : ""
-- anchors.left: parent.left
-- visible: text != ""
-- }
-- PlasmaWidgets.SignalPlotter {
-- id: plotter
-- width: parent.width
-- useAutoRange: true
-- showVerticalLines: false
-- unit: i18n("KiB/s")
-- height: theme.defaultFont.mSize.height * 5
-- Component.onCompleted: plotter.addPlot(theme.highlightColor)
-- }
-- Connections {
-- target: jobsSource
-- onDataChanged: {
-- plotter.addSample([jobsSource.data[modelData]["numericSpeed"]/1000])
-- }
-- }
-- }
-- }
--
-- states: [
-- State {
-- name: "expanded"
-- PropertyChanges {
-- target: detailsItem
-- height: detailsItem.childrenRect.height
-- }
-- },
-- State {
-- name: "collapsed"
-- PropertyChanges {
-- target: detailsItem
-- height: 0
-- }
-- }
-- ]
-- transitions : [
-- Transition {
-- from: "collapsed"
-- to: "expanded"
-- SequentialAnimation {
-- ScriptAction {
-- script: {
-- detailsItem.visible = true
-- detailsItem.clip = true
-- //create the contents if they don't exist yet
-- if (!detailsItem.contentsItem) {
-- detailsItem.contentsItem = detailsComponent.createObject(detailsItem)
-- }
-- }
-- }
-- NumberAnimation {
-- duration: 250
-- properties: "height"
-- easing: PropertyAnimation.EaseInOut
-- }
-- ScriptAction {script: detailsItem.clip = false}
-- }
-- },
-- Transition {
-- from: "expanded"
-- to: "collapsed"
-- SequentialAnimation {
-- ScriptAction {script: detailsItem.clip = true}
-- NumberAnimation {
-- duration: 250
-- properties: "height"
-- easing: PropertyAnimation.EaseInOut
-- }
-- //TODO: delete the details?
-- ScriptAction {script: detailsItem.visible = false}
-- }
-- }
-- ]
-- }
-- }
--}
-diff --git a/plasma/generic/applets/notifications/contents/ui/Jobs.qml b/plasma/generic/applets/notifications/contents/ui/Jobs.qml
-deleted file mode 100644
-index 7e2f2a2..0000000
---- a/plasma/generic/applets/notifications/contents/ui/Jobs.qml
-+++ /dev/null
-@@ -1,105 +0,0 @@
--/*
-- * Copyright 2012 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 Library 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.
-- */
--
--import QtQuick 1.1
--import org.kde.plasma.core 0.1 as PlasmaCore
--import org.kde.plasma.components 0.1 as PlasmaComponents
--
--Column {
-- id: jobsRoot
-- property alias count: jobsRepeater.count
-- anchors {
-- left: parent.left
-- right: parent.right
-- }
--
-- PlasmaCore.DataSource {
-- id: jobsSource
-- engine: "applicationjobs"
-- interval: 0
--
-- onSourceAdded: {
-- connectSource(source);
-- }
-- property variant runningJobs
--
-- onSourceRemoved: {
-- if (!notifications) {
-- return
-- }
-- var message = runningJobs[source]["label1"] ? runningJobs[source]["label1"] : runningJobs[source]["label0"]
-- notifications.addNotification(
-- source,
-- runningJobs[source]["appIconName"],
-- 0,
-- runningJobs[source]["appName"],
-- i18n("%1 [Finished]", runningJobs[source]["infoMessage"]),
-- message,
-- 0,
-- 0,
-- [{"id": message, "text": i18n("Open")}])
-- delete runningJobs[source]
-- }
-- Component.onCompleted: {
-- jobsSource.runningJobs = new Object
-- connectedSources = sources
-- }
-- onNewData: {
-- var jobs = runningJobs
-- jobs[sourceName] = data
-- runningJobs = jobs
-- }
-- onDataChanged: {
-- var total = 0
-- for (var i = 0; i < sources.length; ++i) {
-- if (jobsSource.data[sources[i]]["percentage"]) {
-- total += jobsSource.data[sources[i]]["percentage"]
-- }
-- }
--
-- total /= sources.length
-- notificationsApplet.globalProgress = total/100
-- }
-- }
--
-- Title {
-- visible: jobsRepeater.count > 0 && notifications && notifications.count > 0
-- text: i18n("Transfers")
-- }
-- PlasmaComponents.ListItem {
-- visible: jobsRepeater.count > 1
-- PlasmaComponents.ProgressBar {
-- anchors {
-- verticalCenter: parent.verticalCenter
-- left: parent.left
-- right: parent.right
-- }
-- minimumValue: 0
-- maximumValue: 100
-- value: notificationsApplet.globalProgress * 100
-- }
-- }
-- Repeater {
-- id: jobsRepeater
-- model: jobsSource.sources
-- delegate: JobDelegate {
-- toolIconSize: notificationsApplet.toolIconSize
-- }
-- }
--}
-\ No newline at end of file
-diff --git a/plasma/generic/applets/notifications/contents/ui/LastNotificationPopup.qml b/plasma/generic/applets/notifications/contents/ui/LastNotificationPopup.qml
-deleted file mode 100644
-index 26bce0f..0000000
---- a/plasma/generic/applets/notifications/contents/ui/LastNotificationPopup.qml
-+++ /dev/null
-@@ -1,414 +0,0 @@
--/***************************************************************************
-- * Copyright 2011 Davide Bettio <davide.bettio@kdemail.net> *
-- * Copyright 2011 Marco Martin <mart@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 of the License, or *
-- * (at your option) any later version. *
-- * *
-- * This program is distributed in the hope that it will be useful, *
-- * but WITHOUT ANY WARRANTY; without even the implied warranty of *
-- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
-- * GNU Library 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 . *
-- ***************************************************************************/
--
--import QtQuick 1.1
--import org.kde.plasma.core 0.1 as PlasmaCore
--import org.kde.plasma.components 0.1 as PlasmaComponents
--import org.kde.qtextracomponents 0.1
--import org.kde.plasma.extras 0.1 as PlasmaExtras
--
--
--PlasmaCore.Dialog {
-- id: lastNotificationPopup
--
-- property variant savedPos
-- property bool customPosition: false
--
-- onHeightChanged: setCustomPosition(lastNotificationPopup.savedPos, false)
-- onWidthChanged: setCustomPosition(lastNotificationPopup.savedPos, false)
--
-- function popup(notification)
-- {
-- if (!lastNotificationPopup.visible) {
-- lastNotificationsModel.clear()
-- }
-- lastNotificationsModel.append(notification)
--
-- setCustomPosition(lastNotificationPopup.savedPos, false)
--
-- lastNotificationPopup.visible = true
-- lastNotificationTimer.interval = Math.max(4000, Math.min(60*1000, notificationsModel.get(0).expireTimeout))
-- notificationsView.currentIndex = lastNotificationsModel.count - 1
-- lastNotificationTimer.restart()
-- mainItem.buttonPressed = false
-- }
--
-- function setCustomPosition(pos, writeConfig)
-- {
-- var popupPos = lastNotificationPopup.popupPosition(notificationIcon, Qt.AlignCenter)
-- var finalPos
--
-- //custom
-- if ((pos.x >= 0 || pos.y >= 0) &&
-- (Math.abs(popupPos.x - pos.x) > 40 ||
-- Math.abs(popupPos.y - pos.y) > 40)) {
-- finalPos = pos
-- if (writeConfig) {
-- plasmoid.writeConfig("CustomPosition", pos)
-- lastNotificationPopup.savedPos = pos
-- lastNotificationPopup.customPosition = true
-- }
-- } else {
-- finalPos = popupPos
-- if (writeConfig) {
-- plasmoid.writeConfig("CustomPosition", QPoint(-1,-1))
-- lastNotificationPopup.savedPos = QPoint(-1,-1)
-- lastNotificationPopup.customPosition = false
-- }
-- }
-- lastNotificationPopup.x = finalPos.x
-- lastNotificationPopup.y = finalPos.y
-- }
--
-- location: customPosition ? Floating : plasmoid.location
-- windowFlags: Qt.WindowStaysOnTopHint
-- Component.onCompleted: {
-- setAttribute(Qt.WA_X11NetWmWindowTypeDock, true)
--
-- lastNotificationPopup.savedPos = plasmoid.readConfig("CustomPosition")
-- setCustomPosition(lastNotificationPopup.savedPos, true)
-- plasmoid.popupEvent.connect(lastNotificationPopup.popupEvent)
-- }
--
-- function popupEvent(popupShowing)
-- {
-- if(popupShowing) {
-- lastNotificationPopup.visible = false
-- }
-- }
--
-- mainItem: MouseEventListener {
-- id: mainItem
-- width: maximumWidth
-- height: maximumHeight
-- property int maximumWidth: theme.defaultFont.mSize.width * 35
-- property int maximumHeight: theme.defaultFont.mSize.width * 10
-- property int minimumWidth: maximumWidth
-- property int minimumHeight: maximumHeight
--
-- property int startX: 0
-- property int startY: 0
-- property int startScreenX: 0
-- property int startScreenY: 0
-- hoverEnabled: true
-- property bool buttonPressed: false
--
-- state: "controlsHidden"
-- onContainsMouseChanged: {
-- if (containsMouse) {
-- mainItem.state = "controlsShown"
-- lastNotificationTimer.running = false
-- } else {
-- mainItem.state = "controlsHidden"
-- lastNotificationTimer.restart()
-- }
-- }
-- onPressed: {
-- startX = mouse.x + lastNotificationPopup.margins.left
-- startY = mouse.y + lastNotificationPopup.margins.top
-- startScreenX = mouse.screenX
-- startScreenY = mouse.screenY
-- lastNotificationTimer.running = false
-- }
-- onReleased: {
-- //FIXME: bind startdragdistance
-- if ((navigationButtonsColumn.visible && mouse.x < navigationButtonsColumn.width) ||
-- buttonPressed ||
-- Math.sqrt(Math.pow(startScreenX - mouse.screenX, 2) + Math.pow(startScreenY - mouse.screenY, 2)) > 4) {
-- } else {
-- lastNotificationPopup.visible = false
-- }
--
-- setCustomPosition(QPoint(Math.max(0, mouse.screenX - startX), Math.max(mouse.screenY - startY)), true)
-- }
-- onPositionChanged: {
-- lastNotificationPopup.x = Math.max(0, mouse.screenX - startX)
-- lastNotificationPopup.y = Math.max(0, mouse.screenY - startY)
-- }
-- onWheelMoved: {
-- if (notificationsView.moving) {
-- return
-- }
--
-- if (wheel.delta > 0) {
-- notificationsView.currentIndex = Math.max(0, notificationsView.currentIndex-1)
-- } else {
-- notificationsView.currentIndex = Math.min(notificationsView.count-1, notificationsView.currentIndex+1)
-- }
-- }
--
-- Timer {
-- id: lastNotificationTimer
-- interval: 4000
-- repeat: false
-- running: false
-- onTriggered: lastNotificationPopup.visible = false
-- }
--
-- ListView {
-- id: notificationsView
-- clip: true
-- snapMode: ListView.SnapOneItem
-- orientation: ListView.Horizontal
-- anchors.fill: parent
-- model: ListModel {
-- id: lastNotificationsModel
-- }
-- interactive: false
-- delegate: Item {
-- height: notificationsView.height
-- width: notificationsView.width
--
-- PlasmaComponents.Label {
-- id: titleLabel
-- text: model.summary
-- //font.weight: Font.Bold
-- visible: model.summary.length > 0
-- height: model.summary.length > 0 ? paintedHeight : 0
-- horizontalAlignment: Text.AlignHCenter
-- color: theme.textColor
-- elide: Text.ElideRight
-- anchors {
-- left: appIconItem.y > y + height ? parent.left : appIconItem.right
-- right: parent.right
-- rightMargin: settingsButton.visible ? settingsButton.width + closeButton.width : closeButton.width
-- top: parent.top
-- topMargin: 6
-- leftMargin: 6
-- }
-- onLinkActivated: plasmoid.openUrl(link)
-- }
--
-- QIconItem {
-- id: appIconItem
-- icon: model.appIcon
-- width: (model.appIcon.length > 0 || imageItem.visible) ? theme.largeIconSize : 0
-- height: theme.largeIconSize
-- visible: !imageItem.visible
-- anchors {
-- left: parent.left
-- verticalCenter: parent.verticalCenter
-- leftMargin: navigationButtonsColumn.width
-- }
-- }
-- QImageItem {
-- id: imageItem
-- anchors.fill: appIconItem
-- image: model.image
-- smooth: true
-- visible: nativeWidth > 0
-- }
-- /*
-- * this extra item is for clip the overflowed body text
-- * maximumLineCount cannot control the behavior of rich text,
-- * so manual clip is required.
-- */
-- Item {
-- id: bodyLabel
-- clip: true
-- height: Math.min(parent.height - (titleLabel.height+titleLabel.y), lastNotificationText.height)
-- property bool tallText: bodyLabel.height >= (bodyLabel.parent.height - (titleLabel.height+titleLabel.y)*2)
-- anchors {
-- top: tallText ? titleLabel.bottom : undefined
-- verticalCenter: tallText ? undefined : parent.verticalCenter
-- left: appIconItem.right
-- right: actionsColumn.left
-- leftMargin: 6
-- rightMargin: 6
-- }
-- PlasmaComponents.Label {
-- id: lastNotificationText
-- text: model.body
-- width: parent.width
-- //textFormat: Text.PlainText
-- color: theme.textColor
-- wrapMode: Text.Wrap
-- elide: Text.ElideRight
-- maximumLineCount: 4
-- onLinkActivated: plasmoid.openUrl(link)
-- }
-- }
-- Column {
-- id: actionsColumn
-- spacing: 6
-- anchors {
-- right: parent.right
-- rightMargin: 6
-- verticalCenter: parent.verticalCenter
-- }
-- Repeater {
-- model: actions
-- PlasmaComponents.Button {
-- text: model.text
-- width: theme.defaultFont.mSize.width * 8
-- height: theme.defaultFont.mSize.width * 2
-- onPressedChanged: {
-- if (pressed) {
-- mainItem.buttonPressed = true
-- } else {
-- mainItem.buttonPressed = false
-- }
-- }
-- onClicked: {
-- executeAction(source, model.id)
-- actionsColumn.visible = false
-- }
-- }
-- }
-- }
-- }
-- }
--
-- Column {
-- id: navigationButtonsColumn
-- opacity: 0
-- visible: backButton.enabled || nextButton.enabled
-- anchors {
-- left: parent.left
-- verticalCenter: parent.verticalCenter
-- }
--
-- PlasmaComponents.ToolButton {
-- id: nextButton
-- iconSource: "go-next"
-- width: theme.smallMediumIconSize
-- height: mainItem.height/2 - 4
-- enabled: notificationsView.currentIndex < notificationsView.count-1
-- onPressedChanged: {
-- if (pressed) {
-- mainItem.buttonPressed = true
-- } else {
-- mainItem.buttonPressed = false
-- }
-- }
-- onClicked: {
-- notificationsView.currentIndex = Math.min(notificationsView.count-1, notificationsView.currentIndex+1)
-- }
-- }
--
-- PlasmaComponents.ToolButton {
-- id: backButton
-- iconSource: "go-previous"
-- width: theme.smallMediumIconSize
-- height: mainItem.height/2 - 4
-- enabled: notificationsView.currentIndex > 0
-- onPressedChanged: {
-- if (pressed) {
-- mainItem.buttonPressed = true
-- } else {
-- mainItem.buttonPressed = false
-- }
-- }
-- onClicked: {
-- notificationsView.currentIndex = Math.max(0, notificationsView.currentIndex-1)
-- }
-- }
-- }
-- PlasmaComponents.ToolButton {
-- id: closeButton
-- opacity: 0
-- iconSource: "window-close"
-- width: theme.smallMediumIconSize
-- height: width
-- anchors {
-- right: parent.right
-- top: parent.top
-- }
-- onPressedChanged: {
-- if (pressed) {
-- mainItem.buttonPressed = true
-- } else {
-- mainItem.buttonPressed = false
-- }
-- }
-- onClicked: {
-- lastNotificationPopup.visible = false
-- lastNotificationTimer.running = false
-- closeNotification(notificationsModel.get((notificationsView.count-1)-notificationsView.currentIndex).source)
-- notificationsModel.remove((notificationsView.count-1)-notificationsView.currentIndex)
-- }
-- }
-- PlasmaComponents.ToolButton {
-- id: settingsButton
-- opacity: 0
-- iconSource: "configure"
-- width: theme.smallMediumIconSize
-- height: width
-- visible: notificationsModel.get((notificationsView.count-1)-notificationsView.currentIndex).configurable
-- anchors {
-- right: closeButton.left
-- top: parent.top
-- rightMargin: 5
-- }
-- onPressedChanged: {
-- if (pressed) {
-- mainItem.buttonPressed = true
-- } else {
-- mainItem.buttonPressed = false
-- }
-- }
-- onClicked: {
-- lastNotificationPopup.visible = false
-- configureNotification(notificationsModel.get((notificationsView.count-1)-notificationsView.currentIndex).appRealName)
-- }
-- }
-- states: [
-- State {
-- name: "controlsShown"
-- PropertyChanges {
-- target: navigationButtonsColumn
-- opacity: 1
-- }
-- PropertyChanges {
-- target: closeButton
-- opacity: 1
-- }
-- PropertyChanges {
-- target: settingsButton
-- opacity: 1
-- }
-- },
-- State {
-- name: "controlsHidden"
-- PropertyChanges {
-- target: navigationButtonsColumn
-- opacity: 0
-- }
-- PropertyChanges {
-- target: closeButton
-- opacity: 0
-- }
-- PropertyChanges {
-- target: settingsButton
-- opacity: 0
-- }
-- }
-- ]
-- transitions: [
-- Transition {
-- NumberAnimation {
-- properties: "opacity"
-- easing.type: Easing.InOutQuad
-- duration: 250
-- }
-- }
-- ]
-- }
--}
-diff --git a/plasma/generic/applets/notifications/contents/ui/NotificationDelegate/NotificationDelegate.qml b/plasma/generic/applets/notifications/contents/ui/NotificationDelegate/NotificationDelegate.qml
-deleted file mode 100644
-index bf33eb1..0000000
---- a/plasma/generic/applets/notifications/contents/ui/NotificationDelegate/NotificationDelegate.qml
-+++ /dev/null
-@@ -1,253 +0,0 @@
--/*
-- * Copyright 2011 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 Library 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.
-- */
--
--import QtQuick 1.1
--import org.kde.plasma.core 0.1 as PlasmaCore
--import org.kde.plasma.components 0.1 as PlasmaComponents
--import org.kde.qtextracomponents 0.1
--
--PlasmaComponents.ListItem {
-- id: notificationItem
-- opacity: 1-Math.abs(x)/width
-- width: popupFlickable.width
-- property int toolIconSize: theme.smallMediumIconSize
-- property int layoutSpacing: 4
--
-- visible: appTabBar.currentTab == allAppsTab || appTabBar.currentTab.text == appName
--
-- Component.onCompleted: {
-- allApplicationsModel.addApplication(appIcon, appName)
-- mainScrollArea.height = mainScrollArea.implicitHeight
-- }
-- Component.onDestruction: {
-- allApplicationsModel.removeApplication(model.appName)
-- mainScrollArea.height = mainScrollArea.implicitHeight
-- }
-- Timer {
-- interval: 10*60*1000
-- repeat: false
-- running: !idleTimeSource.idle
-- onTriggered: {
-- notificationsModel.remove(index)
-- }
-- }
--
-- MouseArea {
-- width: parent.width
-- height: childrenRect.height
-- drag {
-- target: notificationItem
-- axis: Drag.XAxis
-- //kind of an hack over Column being too smart
-- minimumX: -parent.width + 1
-- maximumX: parent.width - 1
-- }
-- onReleased: {
-- if (notificationItem.x < -notificationItem.width/2) {
-- removeAnimation.exitFromRight = false
-- removeAnimation.running = true
-- } else if (notificationItem.x > notificationItem.width/2 ) {
-- removeAnimation.exitFromRight = true
-- removeAnimation.running = true
-- } else {
-- resetAnimation.running = true
-- }
-- }
-- SequentialAnimation {
-- id: removeAnimation
-- property bool exitFromRight: true
-- NumberAnimation {
-- target: notificationItem
-- properties: "x"
-- to: removeAnimation.exitFromRight ? notificationItem.width-1 : 1-notificationItem.width
-- duration: 250
-- easing.type: Easing.InOutQuad
-- }
-- NumberAnimation {
-- target: notificationItem
-- properties: "height"
-- to: 0
-- duration: 250
-- easing.type: Easing.InOutQuad
-- }
-- ScriptAction {
-- script: notificationsModel.remove(index)
-- }
-- }
-- SequentialAnimation {
-- id: resetAnimation
-- NumberAnimation {
-- target: notificationItem
-- properties: "x"
-- to: 0
-- duration: 250
-- easing.type: Easing.InOutQuad
-- }
-- }
-- Column {
-- spacing: notificationItem.layoutSpacing
-- width: parent.width
-- Item {
-- width: parent.width
-- height: summaryLabel.height
--
-- PlasmaComponents.Label {
-- id: summaryLabel
-- text: summary
-- height: paintedHeight
-- anchors {
-- left: parent.left
-- right: parent.right
-- leftMargin: closeButton.width
-- rightMargin: settingsButton.visible ? settingsButton.width + closeButton.width : closeButton.width
-- }
-- horizontalAlignment: Text.AlignHCenter
-- elide: Text.ElideRight
-- onLinkActivated: plasmoid.openUrl(link)
-- }
--
-- PlasmaComponents.ToolButton {
-- id: closeButton
-- iconSource: "window-close"
-- width: notificationItem.toolIconSize
-- height: width
-- onClicked: {
-- if (notificationsModel.count > 1) {
-- removeAnimation.running = true
-- } else {
-- closeNotification(model.source)
-- notificationsModel.remove(index)
-- }
-- }
-- anchors {
-- top: parent.top
-- right: parent.right
-- }
-- }
--
-- PlasmaComponents.ToolButton {
-- id: settingsButton
-- iconSource: "configure"
-- width: notificationItem.toolIconSize
-- height: width
-- visible: model.configurable
-- onClicked: {
-- plasmoid.hidePopup()
-- configureNotification(model.appRealName)
-- }
-- anchors {
-- top: parent.top
-- right: closeButton.left
-- rightMargin: 5
-- }
-- }
-- }
--
-- Item {
-- height: childrenRect.height
-- width: parent.width
-- QIconItem {
-- id: appIconItem
-- icon: QIcon(appIcon)
-- width: theme.largeIconSize
-- height: theme.largeIconSize
-- visible: !imageItem.visible
-- anchors {
-- left: parent.left
-- verticalCenter: parent.verticalCenter
-- }
-- }
-- QImageItem {
-- id: imageItem
-- anchors.fill: appIconItem
-- image: model.image
-- smooth: true
-- visible: nativeWidth > 0
-- }
-- PlasmaComponents.ContextMenu {
-- id: contextMenu
-- visualParent: contextMouseArea
-- PlasmaComponents.MenuItem {
-- text: i18n("Copy")
-- onClicked: bodyText.copy()
-- }
-- PlasmaComponents.MenuItem {
-- text: i18n("Select All")
-- onClicked: bodyText.selectAll()
-- }
-- }
-- MouseArea {
-- id: contextMouseArea
-- anchors {
-- left: appIconItem.right
-- right: actionsColumn.left
-- verticalCenter: parent.verticalCenter
-- leftMargin: 6
-- rightMargin: 6
-- }
-- acceptedButtons: Qt.RightButton
-- height: bodyText.paintedHeight
-- preventStealing: true
-- onPressed: contextMenu.open(mouse.x, mouse.y)
-- TextEdit {
-- id: bodyText
-- anchors.fill: parent
-- text: body
-- color: theme.textColor
-- font.capitalization: theme.defaultFont.capitalization
-- font.family: theme.defaultFont.family
-- font.italic: theme.defaultFont.italic
-- font.letterSpacing: theme.defaultFont.letterSpacing
-- font.pointSize: theme.defaultFont.pointSize
-- font.strikeout: theme.defaultFont.strikeout
-- font.underline: theme.defaultFont.underline
-- font.weight: theme.defaultFont.weight
-- font.wordSpacing: theme.defaultFont.wordSpacing
-- selectByMouse: true
-- readOnly: true
-- wrapMode: Text.Wrap
-- textFormat: TextEdit.RichText
-- onLinkActivated: plasmoid.openUrl(link)
-- }
-- }
-- Column {
-- id: actionsColumn
-- spacing: notificationItem.layoutSpacing
-- anchors {
-- right: parent.right
-- rightMargin: 6
-- verticalCenter: parent.verticalCenter
-- }
-- Repeater {
-- model: actions
-- PlasmaComponents.Button {
-- text: model.text
-- width: theme.defaultFont.mSize.width * 8
-- height: theme.defaultFont.mSize.width * 2
-- onClicked: {
-- executeAction(source, model.id)
-- actionsColumn.visible = false
-- }
-- }
-- }
-- }
-- }
-- }
-- }
--}
-diff --git a/plasma/generic/applets/notifications/contents/ui/NotificationDelegate/qmldir b/plasma/generic/applets/notifications/contents/ui/NotificationDelegate/qmldir
-deleted file mode 100644
-index 88fc37a..0000000
---- a/plasma/generic/applets/notifications/contents/ui/NotificationDelegate/qmldir
-+++ /dev/null
-@@ -1 +0,0 @@
--NotificationDelegate 0.1 NotificationDelegate.qml
-diff --git a/plasma/generic/applets/notifications/contents/ui/NotificationIcon.qml b/plasma/generic/applets/notifications/contents/ui/NotificationIcon.qml
-deleted file mode 100644
-index f12cd56..0000000
---- a/plasma/generic/applets/notifications/contents/ui/NotificationIcon.qml
-+++ /dev/null
-@@ -1,188 +0,0 @@
--/***************************************************************************
-- * Copyright 2011 Davide Bettio <davide.bettio@kdemail.net> *
-- * Copyright 2011 Marco Martin <mart@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 of the License, or *
-- * (at your option) any later version. *
-- * *
-- * This program is distributed in the hope that it will be useful, *
-- * but WITHOUT ANY WARRANTY; without even the implied warranty of *
-- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
-- * GNU Library 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 . *
-- ***************************************************************************/
--
--import QtQuick 1.1
--import org.kde.plasma.core 0.1 as PlasmaCore
--import org.kde.plasma.components 0.1 as PlasmaComponents
--import org.kde.qtextracomponents 0.1
--import org.kde.plasma.extras 0.1 as PlasmaExtras
--
--
--Item {
-- PlasmaCore.SvgItem {
-- id: notificationSvgItem
-- svg: notificationSvg
-- elementId: "notification-disabled"
-- anchors.centerIn: parent
-- width: Math.min(parent.width, parent.height)
-- height: width
-- state: notificationsApplet.state
--
-- PlasmaCore.Svg {
-- id: notificationSvg
-- imagePath: "icons/notification"
-- }
--
-- Item {
-- id: jobProgressItem
-- width: notificationSvgItem.width * globalProgress
-- clip: true
-- visible: jobs.count > 0
-- anchors {
-- left: parent.left
-- top: parent.top
-- bottom: parent.bottom
-- }
-- PlasmaCore.SvgItem {
-- svg: notificationSvg
-- elementId: "notification-progress-active"
-- anchors {
-- left: parent.left
-- top: parent.top
-- bottom: parent.bottom
-- }
-- width: notificationSvgItem.width
-- }
-- }
-- PlasmaComponents.BusyIndicator {
-- anchors.fill: parent
-- visible: jobs ? jobs.count > 0 : false
-- running: visible
-- }
--
-- Column {
-- id: countColumn
-- visible: false
-- anchors.centerIn: parent
-- PlasmaCore.SvgItem {
-- svg: notificationSvg
-- elementId: {
-- switch (plasmoid.location) {
-- case TopEdge:
-- return "expander-top"
-- case LeftEdge:
-- return "expander-left"
-- case RightEdge:
-- return "expander-right"
-- default:
-- return "expander-bottom"
-- }
-- }
-- width: naturalSize.width
-- height: naturalSize.height
-- anchors.horizontalCenter: parent.horizontalCenter
-- }
-- PlasmaComponents.Label {
-- property int totalCount: notificationsApplet.totalCount
-- text: totalCount
--
-- property int oldTotalCount: 0
-- font.pointSize: theme.smallestFont.pointSize
-- height: paintedHeight - 3
-- onTotalCountChanged: {
-- if (totalCount > oldTotalCount) {
-- notificationAnimation.running = true
-- }
-- oldTotalCount = totalCount
-- }
-- }
-- }
--
-- PlasmaCore.SvgItem {
-- id: notificationAnimatedItem
-- anchors.fill: parent
-- svg: notificationSvg
-- elementId: "notification-active"
-- opacity: 0
-- scale: 2
--
-- SequentialAnimation {
-- id: notificationAnimation
-- NumberAnimation {
-- target: notificationAnimatedItem
-- duration: 250
-- properties: "opacity, scale"
-- to: 1
-- easing.type: Easing.InOutQuad
-- }
-- PauseAnimation { duration: 500 }
-- ParallelAnimation {
-- NumberAnimation {
-- target: notificationAnimatedItem
-- duration: 250
-- properties: "opacity"
-- to: 0
-- easing.type: Easing.InOutQuad
-- }
-- NumberAnimation {
-- target: notificationAnimatedItem
-- duration: 250
-- properties: "scale"
-- to: 2
-- easing.type: Easing.InOutQuad
-- }
-- }
-- }
-- }
-- MouseArea {
-- anchors.fill: parent
-- onClicked: {
-- if (notificationsApplet.totalCount > 0) {
-- plasmoid.togglePopup()
-- } else {
-- plasmoid.hidePopup()
-- }
-- }
-- }
-- states: [
-- State {
-- name: "default"
-- PropertyChanges {
-- target: notificationSvgItem
-- elementId: "notification-disabled"
-- }
-- PropertyChanges {
-- target: countColumn
-- visible: false
-- }
-- PropertyChanges {
-- target: plasmoid
-- status: PassiveStatus
-- }
-- },
-- State {
-- name: "new-notifications"
-- PropertyChanges {
-- target: notificationSvgItem
-- elementId: jobs.count > 0 ? "notification-progress-inactive" : "notification-empty"
-- }
-- PropertyChanges {
-- target: countColumn
-- visible: true
-- }
-- PropertyChanges {
-- target: plasmoid
-- status: ActiveStatus
-- }
-- }
-- ]
-- }
--}
--
-diff --git a/plasma/generic/applets/notifications/contents/ui/Notifications.qml b/plasma/generic/applets/notifications/contents/ui/Notifications.qml
-deleted file mode 100644
-index 114ead2..0000000
---- a/plasma/generic/applets/notifications/contents/ui/Notifications.qml
-+++ /dev/null
-@@ -1,227 +0,0 @@
--/*
-- * Copyright 2012 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 Library 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.
-- */
--
--import QtQuick 1.1
--import org.kde.plasma.core 0.1 as PlasmaCore
--import org.kde.plasma.components 0.1 as PlasmaComponents
--
--import "plasmapackage:/ui/NotificationDelegate"
--
--Column {
-- id: notificationsRoot
-- property alias count: notificationsRepeater.count
-- anchors {
-- left: parent.left
-- right: parent.right
-- }
--
-- function addNotification(source, appIcon, image, appName, summary, body, expireTimeout, urgency, appRealName, configurable, actions) {
-- // Do not show duplicated notifications
-- for (var i = 0; i < notificationsModel.count; ++i) {
-- if (notificationsModel.get(i).source == source &&
-- notificationsModel.get(i).appName == appName &&
-- notificationsModel.get(i).summary == summary &&
-- notificationsModel.get(i).body == body) {
-- return
-- }
-- }
--
-- for (var i = 0; i < notificationsModel.count; ++i) {
-- if (notificationsModel.get(i).source == source) {
-- notificationsModel.remove(i)
-- break
-- }
-- }
-- if (notificationsModel.count > 20) {
-- notificationsModel.remove(notificationsModel.count-1)
-- }
-- var notification = {"source" : source,
-- "appIcon" : appIcon,
-- "image" : image,
-- "appName" : appName,
-- "summary" : summary,
-- "body" : body,
-- "expireTimeout": expireTimeout,
-- "urgency" : urgency,
-- "configurable": configurable,
-- "appRealName": appRealName,
-- "actions" : actions}
-- notificationsModel.insert(0, notification);
-- if (plasmoid.popupShowing) {
-- return
-- }
-- if (!lastNotificationPopup) {
-- lastNotificationPopup = lastNotificationPopupComponent.createObject(notificationsRoot)
-- }
-- lastNotificationPopup.popup(notification)
-- }
--
-- function executeAction(source, id) {
-- //try to use the service
-- if (source.indexOf("notification") !== -1) {
-- var service = notificationsSource.serviceForSource(source)
-- var op = service.operationDescription("invokeAction")
-- op["actionId"] = id
--
-- service.startOperationCall(op)
-- //try to open the id as url
-- } else if (source.indexOf("Job") !== -1) {
-- plasmoid.openUrl(id)
-- }
-- }
--
-- function configureNotification(appRealName) {
-- var service = notificationsSource.serviceForSource("notification")
-- var op = service.operationDescription("configureNotification")
-- op["appRealName"] = appRealName;
-- service.startOperationCall(op)
-- }
--
-- function closeNotification(source) {
-- var service = notificationsSource.serviceForSource(source)
-- var op = service.operationDescription("userClosed")
-- service.startOperationCall(op)
-- }
--
-- property QtObject lastNotificationPopup
-- Component {
-- id: lastNotificationPopupComponent
-- LastNotificationPopup {
-- }
-- }
--
-- ListModel {
-- id: notificationsModel
-- }
-- ListModel {
-- id: allApplicationsModel
-- function addApplication(icon, name)
-- {
-- for (var i = 0; i < count; ++i) {
-- var item = get(i)
-- if (item.name == name) {
-- setProperty(i, "count", item.count + 1)
-- return
-- }
-- }
-- append({"icon": icon, "name": name, "count": 1})
-- }
-- function removeApplication(name)
-- {
-- for (var i = 0; i < count; ++i) {
-- var item = get(i)
-- if (item.name == name) {
-- if (item.count <= 1) {
-- remove(i)
-- appTabBar.currentTab = allAppsTab
-- return
-- }
-- setProperty(i, "count", item.count - 1)
-- return
-- }
-- }
-- }
-- }
--
-- PlasmaCore.DataSource {
-- id: idleTimeSource
-- engine: "powermanagement"
-- interval: 30000
-- connectedSources: ["UserActivity"]
-- //Idle whith more than 5 minutes of user inactivity
-- property bool idle: data["UserActivity"]["IdleTime"] > 300000
-- }
--
-- PlasmaCore.DataSource {
-- id: notificationsSource
-- engine: "notifications"
-- interval: 0
--
-- onSourceAdded: {
-- connectSource(source);
-- }
--
-- onNewData: {
-- var actions = new Array()
-- if (data["actions"] && data["actions"].length % 2 == 0) {
-- for (var i = 0; i < data["actions"].length; i += 2) {
-- var action = new Object()
-- action["id"] = data["actions"][i]
-- action["text"] = data["actions"][i+1]
-- actions.push(action)
-- }
-- }
-- notificationsRoot.addNotification(
-- sourceName,
-- data["appIcon"],
-- data["image"],
-- data["appName"],
-- data["summary"],
-- data["body"],
-- data["expireTimeout"],
-- data["urgency"],
-- data["appRealName"],
-- data["configurable"],
-- actions)
-- }
--
-- }
--
-- Title {
-- visible: notificationsRepeater.count > 1 || (jobs && jobs.count > 0 && notificationsRepeater.count > 0)
-- text: i18n("Notifications")
-- PlasmaComponents.ToolButton {
-- iconSource: "window-close"
-- width: notificationsApplet.toolIconSize
-- height: width
-- anchors {
-- right: parent.right
-- verticalCenter: parent.verticalCenter
-- }
-- onClicked: notificationsModel.clear()
-- }
-- }
-- PlasmaComponents.ListItem {
-- visible: allApplicationsModel.count > 1
-- PlasmaComponents.TabBar {
-- id: appTabBar
-- anchors.horizontalCenter: parent.horizontalCenter
-- width: Math.min(implicitWidth, parent.width-8)
-- PlasmaComponents.TabButton {
-- id: allAppsTab
-- text: i18n("All")
-- iconSource: "dialog-information"
-- }
-- Repeater {
-- model: allApplicationsModel
-- PlasmaComponents.TabButton {
-- text: name
-- iconSource: icon
-- }
-- }
-- }
-- }
-- Repeater {
-- id: notificationsRepeater
-- model: notificationsModel
-- delegate: NotificationDelegate {
-- toolIconSize: notificationsApplet.toolIconSize
-- }
-- }
--}
-diff --git a/plasma/generic/applets/notifications/contents/ui/Title.qml b/plasma/generic/applets/notifications/contents/ui/Title.qml
-deleted file mode 100644
-index dff4f72..0000000
---- a/plasma/generic/applets/notifications/contents/ui/Title.qml
-+++ /dev/null
-@@ -1,43 +0,0 @@
--/*
-- * Copyright 2012 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 Library 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.
-- */
--
--import QtQuick 1.1
--import org.kde.plasma.core 0.1 as PlasmaCore
--import org.kde.plasma.components 0.1 as PlasmaComponents
--
--PlasmaComponents.ListItem {
-- id: root
-- property alias text: titleLabel.text
--
--
-- sectionDelegate: true
--
-- width: parent.width
--
-- PlasmaComponents.Label {
-- id: titleLabel
-- horizontalAlignment: Text.AlignHCenter
-- elide: Text.ElideRight
-- anchors {
-- verticalCenter: parent.verticalCenter
-- left: parent.left
-- right: parent.right
-- }
-- }
--}
-diff --git a/plasma/generic/applets/notifications/contents/ui/config.ui b/plasma/generic/applets/notifications/contents/ui/config.ui
-deleted file mode 100644
-index 7769bba..0000000
---- a/plasma/generic/applets/notifications/contents/ui/config.ui
-+++ /dev/null
-@@ -1,69 +0,0 @@
--<?xml version="1.0" encoding="UTF-8"?>
--<ui version="4.0">
-- <class>feedsConfig</class>
-- <widget class="QWidget" name="feedsConfig">
-- <property name="geometry">
-- <rect>
-- <x>0</x>
-- <y>0</y>
-- <width>337</width>
-- <height>181</height>
-- </rect>
-- </property>
-- <layout class="QFormLayout" name="formLayout">
-- <item row="1" column="0">
-- <widget class="QLabel" name="label_2">
-- <property name="text">
-- <string>&Application notifications:</string>
-- </property>
-- <property name="buddy">
-- <cstring>kcfg_ShowNotifications</cstring>
-- </property>
-- </widget>
-- </item>
-- <item row="1" column="1">
-- <widget class="QCheckBox" name="kcfg_ShowNotifications">
-- <property name="text">
-- <string/>
-- </property>
-- </widget>
-- </item>
-- <item row="2" column="0">
-- <widget class="QLabel" name="label_3">
-- <property name="text">
-- <string>&File transfers and jobs:</string>
-- </property>
-- <property name="buddy">
-- <cstring>kcfg_ShowJobs</cstring>
-- </property>
-- </widget>
-- </item>
-- <item row="2" column="1">
-- <widget class="QCheckBox" name="kcfg_ShowJobs">
-- <property name="text">
-- <string/>
-- </property>
-- </widget>
-- </item>
-- <item row="0" column="0" colspan="2">
-- <widget class="QLabel" name="label">
-- <property name="font">
-- <font>
-- <weight>75</weight>
-- <bold>true</bold>
-- </font>
-- </property>
-- <property name="text">
-- <string>Choose which information to show</string>
-- </property>
-- </widget>
-- </item>
-- </layout>
-- </widget>
-- <tabstops>
-- <tabstop>kcfg_ShowNotifications</tabstop>
-- <tabstop>kcfg_ShowJobs</tabstop>
-- </tabstops>
-- <resources/>
-- <connections/>
--</ui>
-diff --git a/plasma/generic/applets/notifications/contents/ui/main.qml b/plasma/generic/applets/notifications/contents/ui/main.qml
-deleted file mode 100644
-index 4d4b0f7..0000000
---- a/plasma/generic/applets/notifications/contents/ui/main.qml
-+++ /dev/null
-@@ -1,171 +0,0 @@
--/***************************************************************************
-- * Copyright 2011 Davide Bettio <davide.bettio@kdemail.net> *
-- * Copyright 2011 Marco Martin <mart@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 of the License, or *
-- * (at your option) any later version. *
-- * *
-- * This program is distributed in the hope that it will be useful, *
-- * but WITHOUT ANY WARRANTY; without even the implied warranty of *
-- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
-- * GNU Library 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 . *
-- ***************************************************************************/
--
--import QtQuick 1.1
--import org.kde.plasma.core 0.1 as PlasmaCore
--import org.kde.plasma.components 0.1 as PlasmaComponents
--import org.kde.qtextracomponents 0.1
--import org.kde.plasma.extras 0.1 as PlasmaExtras
--import org.kde.locale 0.1 as KLocale
--
--import "plasmapackage:/ui/uiproperties.js" as UiProperties
--
--
--MouseEventListener {
-- id: notificationsApplet
-- state: "default"
-- width: 32
-- height: 32
-- property int minimumWidth: mainScrollArea.implicitWidth
-- property int minimumHeight: mainScrollArea.implicitHeight
-- property int maximumWidth: -1
-- property int maximumHeight: mainScrollArea.implicitHeight
--
-- property int toolIconSize: UiProperties.toolIconSize
-- property int layoutSpacing: UiProperties.layoutSpacing
--
-- property real globalProgress: 0
--
-- property bool showNotifications: false
-- property bool showJobs: false
--
-- property Item notifications: notificationsLoader.item
-- property Item jobs: jobsLoader.item
--
-- //notifications + jobs
-- property int totalCount: (notifications ? notifications.count : 0) + (jobs ? jobs.count : 0)
-- onTotalCountChanged: {
-- if (totalCount > 0) {
-- state = "new-notifications"
-- } else {
-- state = "default"
-- plasmoid.hidePopup()
-- }
--
-- var data = new Object
-- data["image"] = "preferences-desktop-notification"
-- data["mainText"] = i18n("Notifications and Jobs")
-- if (totalCount == 0) {
-- data["subText"] = i18n("No notifications or jobs")
-- } else if (!notifications.count) {
-- data["subText"] = i18np("%1 running job", "%1 running jobs", jobs.count)
-- } else if (!jobs.count) {
-- data["subText"] = i18np("%1 notification", "%1 notifications", notifications.count)
-- } else {
-- data["subText"] = i18np("%1 running job", "%1 running jobs", jobs.count) + "<br/>" + i18np("%1 notification", "%1 notifications", notifications.count)
-- }
-- plasmoid.popupIconToolTip = data
-- plasmoid.passivePopup = jobs.count != 0
-- }
--
-- property Item notificationIcon
--
-- Component.onCompleted: {
-- //plasmoid.popupIcon = QIcon("preferences-desktop-notification")
-- plasmoid.aspectRatioMode = "ConstrainedSquare"
-- plasmoid.status = PassiveStatus
-- allApplications = new Object
-- plasmoid.addEventListener('ConfigChanged', configChanged);
-- configChanged()
-- }
--
-- function configChanged()
-- {
-- showNotifications = plasmoid.readConfig("ShowNotifications")
-- showJobs = plasmoid.readConfig("ShowJobs")
-- }
--
-- KLocale.Locale {
-- id: locale
-- }
--
-- PlasmaCore.Svg {
-- id: configIconsSvg
-- imagePath: "widgets/configuration-icons"
-- }
--
-- property Component compactRepresentation: Component {
-- NotificationIcon {
-- id: notificationIcon
-- Component.onCompleted: notificationsApplet.notificationIcon = notificationIcon
-- }
-- }
--
-- hoverEnabled: !UiProperties.touchInput
--
-- PlasmaExtras.ScrollArea {
-- id: mainScrollArea
-- anchors.fill: parent
-- implicitWidth: theme.defaultFont.mSize.width * 40
-- implicitHeight: Math.min(theme.defaultFont.mSize.height * 40, Math.max(theme.defaultFont.mSize.height * 6, contentsColumn.height))
-- state: ""
--
-- states: [
-- State {
-- name: "underMouse"
-- when: notificationsApplet.containsMouse
-- PropertyChanges {
-- target: mainScrollArea
-- implicitHeight: implicitHeight
-- }
-- },
-- State {
-- name: ""
-- when: !notificationsApplet.containsMouse
-- PropertyChanges {
-- target: mainScrollArea
-- implicitHeight: Math.min(theme.defaultFont.mSize.height * 40, Math.max(theme.defaultFont.mSize.height * 6, contentsColumn.height))
-- }
-- }
-- ]
--
-- Flickable {
-- id: popupFlickable
-- anchors.fill:parent
--
-- contentWidth: width
-- contentHeight: contentsColumn.height
-- clip: true
--
-- Column {
-- id: contentsColumn
-- width: popupFlickable.width
--
-- //TODO: load those on demand based on configuration
-- Loader {
-- id: jobsLoader
-- source: showJobs ? "Jobs.qml" : ""
-- anchors {
-- left: parent.left
-- right: parent.right
-- }
-- }
-- Loader {
-- id: notificationsLoader
-- source: showNotifications ? "Notifications.qml" : ""
-- anchors {
-- left: parent.left
-- right: parent.right
-- }
-- }
-- }
-- }
-- }
--}
-diff --git a/plasma/generic/applets/notifications/contents/ui/uiproperties.js b/plasma/generic/applets/notifications/contents/ui/uiproperties.js
-deleted file mode 100644
-index efad371..0000000
---- a/plasma/generic/applets/notifications/contents/ui/uiproperties.js
-+++ /dev/null
-@@ -1,23 +0,0 @@
--/*
-- * Copyright 2012 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 Library 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.
-- */
--
--
--var toolIconSize = theme.smallMediumIconSize
--var layoutSpacing = 4
--var touchInput = false
-diff --git a/plasma/generic/applets/notifications/core/completedjobnotification.cpp b/plasma/generic/applets/notifications/core/completedjobnotification.cpp
-new file mode 100644
-index 0000000..9a08390
---- /dev/null
-+++ b/plasma/generic/applets/notifications/core/completedjobnotification.cpp
-@@ -0,0 +1,99 @@
-+/***************************************************************************
-+ * completedjobnotification.h *
-+ * Copyright (C) 2010 Marco Martin <notmart@gmail.com> *
-+ * *
-+ * This program is free software; you can redistribute it and/or modify *
-+ * it under the terms of the GNU General Public License as published by *
-+ * the Free Software Foundation; either version 2 of the License, or *
-+ * (at your option) any later version. *
-+ * *
-+ * This program is distributed in the hope that it will be useful, *
-+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
-+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
-+ * GNU General Public License for more details. *
-+ * *
-+ * You should have received a copy of the GNU General Public License *
-+ * along with this program; if not, write to the *
-+ * Free Software Foundation, Inc., *
-+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA . *
-+ ***************************************************************************/
-+
-+#include "completedjobnotification.h"
-+#include "job.h"
-+
-+#include <QProcess>
-+
-+#include <KIcon>
-+#include <KLocale>
-+#include <KDebug>
-+
-+
-+static const int completedJobExpireDelay = 60 * 1000;
-+static const int completedShortJobExpireDelay = 8 * 1000;
-+static const uint shortJobsLength = 30 * 1000;
-+
-+CompletedJobNotification::CompletedJobNotification(QObject *parent)
-+ : Notification(parent)
-+{
-+}
-+
-+CompletedJobNotification::~CompletedJobNotification()
-+{
-+}
-+
-+void CompletedJobNotification::setJob(Job *job)
-+{
-+ setApplicationName(job->applicationName());
-+ setApplicationIcon(KIcon(job->applicationIconName()));
-+ setSummary(i18n("%1 [Finished]", job->message()));
-+
-+ if (job->error().isEmpty()) {
-+ setMessage(job->completedMessage());
-+ } else {
-+ setMessage(job->error());
-+ }
-+
-+ if (job->elapsed() < shortJobsLength) {
-+ setTimeout(completedShortJobExpireDelay);
-+ } else {
-+ setTimeout(completedJobExpireDelay);
-+ }
-+
-+ if (job->destination().isValid()) {
-+ QHash<QString, QString> actions;
-+ actions.insert("open", i18n("Open"));
-+ setActions(actions);
-+ setActionOrder(QStringList()<<"open");
-+
-+ // create location url as is done in job->completedMessage()
-+ KUrl location(job->destination());
-+ if (job->totalAmounts().value("files") > 1) {
-+ location.setFileName(QString());
-+ }
-+
-+ m_destinationPrettyUrl = location.prettyUrl();
-+ }
-+
-+ m_job = job;
-+}
-+
-+void CompletedJobNotification::linkActivated(const QString &url)
-+{
-+ kDebug() << "open " << url;
-+ QProcess::startDetached("kde-open", QStringList() << url);
-+}
-+
-+Job *CompletedJobNotification::job() const
-+{
-+ return m_job;
-+}
-+
-+void CompletedJobNotification::triggerAction(const QString &actionId)
-+{
-+ if (actionId == "open" && !m_destinationPrettyUrl.isNull()) {
-+ linkActivated(m_destinationPrettyUrl);
-+ }
-+}
-+
-+
-+#include "completedjobnotification.moc"
-diff --git a/plasma/generic/applets/notifications/core/completedjobnotification.h b/plasma/generic/applets/notifications/core/completedjobnotification.h
-new file mode 100644
-index 0000000..b5e1f15
---- /dev/null
-+++ b/plasma/generic/applets/notifications/core/completedjobnotification.h
-@@ -0,0 +1,51 @@
-+/***************************************************************************
-+ * completedjobnotification.h *
-+ * Copyright (C) 2010 Marco Martin <notmart@gmail.com> *
-+ * *
-+ * This program is free software; you can redistribute it and/or modify *
-+ * it under the terms of the GNU General Public License as published by *
-+ * the Free Software Foundation; either version 2 of the License, or *
-+ * (at your option) any later version. *
-+ * *
-+ * This program is distributed in the hope that it will be useful, *
-+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
-+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
-+ * GNU General Public License for more details. *
-+ * *
-+ * You should have received a copy of the GNU General Public License *
-+ * along with this program; if not, write to the *
-+ * Free Software Foundation, Inc., *
-+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA . *
-+ ***************************************************************************/
-+
-+#ifndef COMPLETEDJOBNOTIFICATION_H
-+#define COMPLETEDJOBNOTIFICATION_H
-+
-+#include "notification.h"
-+
-+
-+class Job;
-+
-+class CompletedJobNotification : public Notification
-+{
-+ Q_OBJECT
-+
-+public:
-+ CompletedJobNotification(QObject *parent = 0);
-+ virtual ~CompletedJobNotification();
-+
-+ void setJob(Job *job);
-+ Job *job() const;
-+
-+public Q_SLOTS:
-+ void linkActivated(const QString &link);
-+ void triggerAction(const QString &actionId);
-+
-+private:
-+ Job *m_job;
-+ QString m_destinationPrettyUrl;
-+};
-+
-+
-+#endif
-+
-diff --git a/plasma/generic/applets/notifications/core/job.cpp b/plasma/generic/applets/notifications/core/job.cpp
-new file mode 100644
-index 0000000..77ea03f
---- /dev/null
-+++ b/plasma/generic/applets/notifications/core/job.cpp
-@@ -0,0 +1,360 @@
-+/***************************************************************************
-+ * Copyright (C) 2008 Rob Scheepmaker <r.scheepmaker@student.utwente.nl> *
-+ * *
-+ * This program is free software; you can redistribute it and/or modify *
-+ * it under the terms of the GNU General Public License as published by *
-+ * the Free Software Foundation; either version 2 of the License, or *
-+ * (at your option) any later version. *
-+ * *
-+ * This program is distributed in the hope that it will be useful, *
-+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
-+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
-+ * GNU General Public License for more details. *
-+ * *
-+ * You should have received a copy of the GNU General Public License *
-+ * along with this program; if not, write to the *
-+ * Free Software Foundation, Inc., *
-+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA . *
-+ ***************************************************************************/
-+
-+#include "job.h"
-+
-+#include <QtCore/QTimer>
-+#include <QtCore/QTime>
-+#include <QTextDocument>
-+#include <QFontMetrics>
-+#include <QApplication>
-+
-+#include <KDebug>
-+#include <KUrl>
-+#include <KLocalizedString>
-+
-+
-+class Job::Private
-+{
-+public:
-+ Private() :
-+ numericSpeed(0),
-+ finalElapsed(0),
-+ state(Running),
-+ percentage(0),
-+ eta(0),
-+ timerId(0),
-+ killable(false),
-+ suspendable(false),
-+ shown(false)
-+ {
-+ }
-+
-+ QString applicationName;
-+ QString applicationIconName;
-+ QString message;
-+ QString error;
-+ QString speed;
-+ QString destination;
-+ qlonglong numericSpeed;
-+
-+
-+ QMap<QString, qlonglong> totalAmounts;
-+ QMap<QString, qlonglong> processedAmounts;
-+
-+ QList<QPair<QString, QString> > labels;
-+
-+ QTime elapsed;
-+ uint finalElapsed;
-+
-+ State state;
-+ uint percentage;
-+ uint eta;
-+ int timerId;
-+
-+ bool killable : 1;
-+ bool suspendable : 1;
-+ bool shown : 1;
-+};
-+
-+Job::Job(QObject *parent)
-+ : QObject(parent),
-+ d(new Private)
-+{
-+ //delay a little the job to avoid the user to be distracted with short ones
-+ QTimer::singleShot(1500, this, SLOT(show()));
-+ d->elapsed.restart();
-+}
-+
-+Job::~Job()
-+{
-+ delete d;
-+}
-+
-+void Job::destroy()
-+{
-+ emit destroyed(this);
-+ deleteLater();
-+}
-+
-+QString Job::applicationName() const
-+{
-+ return d->applicationName;
-+}
-+
-+void Job::setApplicationName(const QString &applicationName)
-+{
-+ if (d->applicationName != applicationName) {
-+ d->applicationName = applicationName;
-+ scheduleChangedSignal();
-+ }
-+}
-+
-+QString Job::applicationIconName() const
-+{
-+ return d->applicationIconName;
-+}
-+
-+void Job::setApplicationIconName(const QString &applicationIcon)
-+{
-+ if (d->applicationIconName != applicationIcon) {
-+ d->applicationIconName = applicationIcon;
-+ scheduleChangedSignal();
-+ }
-+}
-+
-+QString Job::message() const
-+{
-+ return d->message;
-+}
-+
-+void Job::setMessage(const QString &message)
-+{
-+ if (d->message != message) {
-+ d->message = message;
-+ scheduleChangedSignal();
-+ }
-+}
-+
-+QString Job::error() const
-+{
-+ return d->error;
-+}
-+
-+void Job::setError(const QString &error)
-+{
-+ if (d->error != error) {
-+ d->error = error;
-+ scheduleChangedSignal();
-+ }
-+}
-+
-+QString Job::speed() const
-+{
-+ return d->speed;
-+}
-+
-+void Job::setSpeed(const QString &speed)
-+{
-+ if (d->speed != speed) {
-+ d->speed = speed;
-+ scheduleChangedSignal();
-+ }
-+}
-+
-+qlonglong Job::numericSpeed() const
-+{
-+ return d->numericSpeed;
-+}
-+
-+void Job::setNumericSpeed(const qlonglong speed)
-+{
-+ if (d->numericSpeed != speed) {
-+ d->numericSpeed = speed;
-+ scheduleChangedSignal();
-+ }
-+}
-+
-+QString Job::completedMessage() const
-+{
-+ KUrl location(d->destination);
-+ if (location.isValid()) {
-+ if (totalAmounts().value("files") > 1) {
-+ location.setFileName(QString());
-+ }
-+
-+ QString destinationString;
-+ if (location.isLocalFile()) {
-+ destinationString = location.toLocalFile();
-+ } else {
-+ destinationString = location.prettyUrl();
-+ }
-+
-+ //FIXME: this is visualization stuff, but putting html here is not so model as well
-+
-+ kDebug() << "href = " << location.url();
-+ QString destinationLink = QString("<a href=\"%1\">%2</a>").arg(location.url())
-+ .arg(Qt::escape(destinationString));
-+
-+ if (totalAmounts().value("files") > 1) {
-+ return i18np("%1 file, to: %2", "%1 files, to: %2", totalAmounts().value("files"),
-+ destinationLink);
-+ } else {
-+ return destinationLink;
-+ }
-+ } else {
-+ return QString("%1: %2").arg(labels().value(0).first).arg(labels().value(0).second);
-+ }
-+}
-+
-+KUrl Job::destination() const
-+{
-+ return d->destination;
-+}
-+
-+ulong Job::eta() const
-+{
-+ return d->eta;
-+}
-+
-+void Job::setEta(ulong eta)
-+{
-+ d->eta = eta;
-+}
-+
-+QMap<QString, qlonglong> Job::totalAmounts() const
-+{
-+ return d->totalAmounts;
-+}
-+
-+void Job::setTotalAmounts(QMap<QString, qlonglong> amounts)
-+{
-+ d->totalAmounts = amounts;
-+ scheduleChangedSignal();
-+}
-+
-+QMap<QString, qlonglong> Job::processedAmounts() const
-+{
-+ return d->processedAmounts;
-+}
-+
-+void Job::setProcessedAmounts(QMap<QString, qlonglong> amounts)
-+{
-+ d->processedAmounts = amounts;
-+ scheduleChangedSignal();
-+}
-+
-+Job::State Job::state() const
-+{
-+ return d->state;
-+}
-+
-+void Job::setState(State state)
-+{
-+ if (d->state != state) {
-+ d->state = state;
-+ show();
-+ if (state == Stopped) {
-+ d->finalElapsed = d->elapsed.elapsed();
-+ }
-+ emit stateChanged(this);
-+ }
-+}
-+
-+QList<QPair<QString, QString> > Job::labels() const
-+{
-+ return d->labels;
-+}
-+
-+void Job::setLabels(QList<QPair<QString, QString> > labels)
-+{
-+ d->labels = labels;
-+ if (d->labels.count() > 1 && d->destination.isEmpty()) {
-+ d->destination = d->labels.value(1).second;
-+ }
-+ scheduleChangedSignal();
-+}
-+
-+uint Job::percentage() const
-+{
-+ return d->percentage;
-+}
-+
-+void Job::setPercentage(uint percentage)
-+{
-+ if (d->percentage != percentage) {
-+ d->percentage = percentage;
-+ scheduleChangedSignal();
-+ }
-+}
-+
-+uint Job::elapsed() const
-+{
-+ if (d->finalElapsed) {
-+ return d->finalElapsed;
-+ } else {
-+ return d->elapsed.elapsed();
-+ }
-+}
-+
-+bool Job::isSuspendable() const
-+{
-+ return d->suspendable;
-+}
-+
-+void Job::setSuspendable(bool suspendable)
-+{
-+ if (d->suspendable != suspendable) {
-+ d->suspendable = suspendable;
-+ scheduleChangedSignal();
-+ }
-+}
-+
-+bool Job::isKillable() const
-+{
-+ return d->killable;
-+}
-+
-+void Job::setKillable(bool killable)
-+{
-+ if (d->killable != killable) {
-+ d->killable = killable;
-+ scheduleChangedSignal();
-+ }
-+}
-+
-+void Job::suspend()
-+{
-+ kWarning() << "Suspend is not implemented in this job provider.";
-+}
-+
-+void Job::resume()
-+{
-+ kWarning() << "Resume is not implemented in this job provider.";
-+}
-+
-+void Job::stop()
-+{
-+ kWarning() << "Stop is not implemented in this job provider.";
-+}
-+
-+void Job::show()
-+{
-+ if (state() == Job::Running && !d->shown) {
-+ d->shown = true;
-+ emit ready(this);
-+ }
-+}
-+
-+void Job::scheduleChangedSignal()
-+{
-+ if (d->shown && !d->timerId) {
-+ d->timerId = startTimer(0);
-+ }
-+}
-+
-+void Job::timerEvent(QTimerEvent *)
-+{
-+ killTimer(d->timerId);
-+ d->timerId = 0;
-+ emit changed(this);
-+}
-+
-+
-+#include "job.moc"
-diff --git a/plasma/generic/applets/notifications/core/job.h b/plasma/generic/applets/notifications/core/job.h
-new file mode 100644
-index 0000000..3d2984d
---- /dev/null
-+++ b/plasma/generic/applets/notifications/core/job.h
-@@ -0,0 +1,188 @@
-+/***************************************************************************
-+ * Copyright (C) 2008 Rob Scheepmaker <r.scheepmaker@student.utwente.nl> *
-+ * *
-+ * This program is free software; you can redistribute it and/or modify *
-+ * it under the terms of the GNU General Public License as published by *
-+ * the Free Software Foundation; either version 2 of the License, or *
-+ * (at your option) any later version. *
-+ * *
-+ * This program is distributed in the hope that it will be useful, *
-+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
-+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
-+ * GNU General Public License for more details. *
-+ * *
-+ * You should have received a copy of the GNU General Public License *
-+ * along with this program; if not, write to the *
-+ * Free Software Foundation, Inc., *
-+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA . *
-+ ***************************************************************************/
-+
-+#ifndef JOB_H
-+#define JOB_H
-+
-+#include <QtCore/QHash>
-+#include <QtCore/QObject>
-+
-+#include <KUrl>
-+
-+
-+class Job : public QObject
-+{
-+ Q_OBJECT
-+
-+public:
-+ enum State {
-+ Running = 0,
-+ Suspended = 1,
-+ Stopped = 2
-+ };
-+
-+ Job(QObject *parent = 0);
-+ virtual ~Job();
-+
-+ /**
-+ * Request and signal destruction of this object
-+ */
-+ void destroy();
-+
-+ /**
-+ * @return the name of the application which started this job.
-+ */
-+ QString applicationName() const;
-+
-+ /**
-+ * @return the name of the icon to be used for this job.
-+ */
-+ QString applicationIconName() const;
-+
-+ /**
-+ * @return the descripion of the activity that is performed.
-+ */
-+ QString message() const;
-+
-+ /**
-+ * @return the errormessage if an error has occurred.
-+ */
-+ QString error() const;
-+
-+ /**
-+ * @return the (human readable) speed at which the jobs is progressing.
-+ */
-+ QString speed() const;
-+
-+ /**
-+ * @return the (in bytes per second) speed at which the jobs is progressing.
-+ */
-+ qlonglong numericSpeed() const;
-+
-+ /**
-+ * @return a nice description of the job that has been completed.
-+ */
-+ QString completedMessage() const;
-+
-+ /**
-+ * @return the time (in seconds) in which this job is expected to complete.
-+ */
-+ ulong eta() const;
-+
-+ void setEta(ulong eta);
-+
-+ QMap<QString, qlonglong> totalAmounts() const;
-+
-+ QMap<QString, qlonglong> processedAmounts() const;
-+
-+ /**
-+ * @return a list of pairs containing label names/values in the order they should be displayed.
-+ */
-+ QList<QPair<QString, QString> > labels() const;
-+
-+ /**
-+ * @return the state this job is in.
-+ */
-+ State state() const;
-+
-+ bool isSuspendable() const;
-+
-+ bool isKillable() const;
-+
-+ /**
-+ * @retun the percentage of the job that has been completed.
-+ */
-+ uint percentage() const;
-+
-+ /**
-+ * total elapsed job time
-+ */
-+ uint elapsed() const;
-+
-+ /**
-+ * Destination url
-+ */
-+ KUrl destination() const;
-+
-+public slots:
-+ /**
-+ * suspend this job.
-+ */
-+ virtual void suspend();
-+
-+ /**
-+ * resume this job.
-+ */
-+ virtual void resume();
-+
-+ /**
-+ * stop this job.
-+ */
-+ virtual void stop();
-+
-+signals:
-+ /**
-+ * Emitted when the job is ready to be shown
-+ */
-+ void ready(Job *job);
-+
-+ /**
-+ * Emitted when the job changes state
-+ */
-+ void stateChanged(Job *job);
-+
-+ /**
-+ * Emitted when the job details change
-+ */
-+ void changed(Job *job);
-+
-+ /**
-+ * Emitted when the job is about to be destroyed
-+ **/
-+ void destroyed(Job *job);
-+
-+protected:
-+ void setApplicationName(const QString &applicationName);
-+ void setApplicationIconName(const QString &applicationIcon);
-+ void setMessage(const QString &message);
-+ void setError(const QString &error);
-+ void setSpeed(const QString &speed);
-+ void setNumericSpeed(const qlonglong speed);
-+ void setTotalAmounts(QMap<QString, qlonglong> amount);
-+ void setProcessedAmounts(QMap<QString, qlonglong> amount);
-+ void setState(State state);
-+ void setSuspendable(bool suspendable);
-+ void setKillable(bool killable);
-+ void setPercentage(uint percentage);
-+ void setLabels(QList<QPair<QString, QString> > labels);
-+ void timerEvent(QTimerEvent *);
-+
-+private slots:
-+ void show();
-+
-+private:
-+ void scheduleChangedSignal();
-+
-+ class Private;
-+ Private* const d;
-+
-+ friend class Manager;
-+};
-+
-+#endif
-diff --git a/plasma/generic/applets/notifications/core/notification.cpp b/plasma/generic/applets/notifications/core/notification.cpp
-new file mode 100644
-index 0000000..26833b6
---- /dev/null
-+++ b/plasma/generic/applets/notifications/core/notification.cpp
-@@ -0,0 +1,272 @@
-+/***************************************************************************
-+ * notification.cpp *
-+ * *
-+ * Copyright (C) 2008 Jason Stubbs <jasonbstubbs@gmail.com> *
-+ * *
-+ * This program is free software; you can redistribute it and/or modify *
-+ * it under the terms of the GNU General Public License as published by *
-+ * the Free Software Foundation; either version 2 of the License, or *
-+ * (at your option) any later version. *
-+ * *
-+ * This program is distributed in the hope that it will be useful, *
-+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
-+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
-+ * GNU General Public License for more details. *
-+ * *
-+ * You should have received a copy of the GNU General Public License *
-+ * along with this program; if not, write to the *
-+ * Free Software Foundation, Inc., *
-+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA . *
-+ ***************************************************************************/
-+
-+#include "notification.h"
-+
-+#include <QImage>
-+#include <QtCore/QTimer>
-+
-+#include <KDebug>
-+
-+
-+class Notification::Private
-+{
-+public:
-+ Private()
-+ : timeout(0),
-+ urgency(0),
-+ hideTimer(0),
-+ expired(false),
-+ read(false)
-+ {
-+ }
-+
-+ QString identifier;
-+ QString applicationName;
-+ QIcon applicationIcon;
-+ QString message;
-+ QString summary;
-+ int timeout;
-+ int urgency;
-+ QImage image;
-+ QTimer *deleteTimer;
-+ QTimer *hideTimer;
-+ bool expired;
-+ bool read;
-+
-+ QHash<QString, QString> actions;
-+ QStringList actionOrder;
-+};
-+
-+Notification::Notification(QObject *parent)
-+ : QObject(parent),
-+ d(new Private)
-+{
-+ d->deleteTimer = new QTimer(this);
-+ d->deleteTimer->setSingleShot(true);
-+ connect(d->deleteTimer, SIGNAL(timeout()), this, SLOT(destroy()));
-+}
-+
-+
-+Notification::~Notification()
-+{
-+ emit notificationDestroyed(this);
-+ delete d;
-+}
-+
-+void Notification::destroy()
-+{
-+ emit notificationDestroyed(this);
-+ deleteLater();
-+}
-+
-+QString Notification::applicationName() const
-+{
-+ return d->applicationName;
-+}
-+
-+
-+void Notification::setApplicationName(const QString &applicationName)
-+{
-+ d->applicationName = applicationName;
-+}
-+
-+
-+QIcon Notification::applicationIcon() const
-+{
-+ return d->applicationIcon;
-+}
-+
-+
-+void Notification::setApplicationIcon(const QIcon &applicationIcon)
-+{
-+ d->applicationIcon = applicationIcon;
-+}
-+
-+
-+QString Notification::message() const
-+{
-+ return d->message;
-+}
-+
-+
-+void Notification::setMessage(const QString &message)
-+{
-+ d->message = message;
-+}
-+
-+
-+QString Notification::summary() const
-+{
-+ return d->summary;
-+}
-+
-+
-+void Notification::setSummary(const QString &summary)
-+{
-+ d->summary = summary;
-+}
-+
-+
-+int Notification::timeout() const
-+{
-+ return d->timeout;
-+}
-+
-+QImage Notification::image() const
-+{
-+ return d->image;
-+}
-+
-+void Notification::setImage(QImage image)
-+{
-+ d->image = image;
-+}
-+
-+void Notification::setTimeout(int timeout)
-+{
-+ //show them at most 30 seconds
-+ if (!timeout) {
-+ d->timeout = 30 * 1000;
-+ } else {
-+ d->timeout = timeout;
-+ }
-+
-+ if (d->urgency >= 2) {
-+ return;
-+ }
-+
-+ if (!d->hideTimer) {
-+ d->hideTimer = new QTimer(this);
-+ d->hideTimer->setSingleShot(true);
-+ connect(d->hideTimer, SIGNAL(timeout()), this, SLOT(hide()));
-+ }
-+ d->hideTimer->start(d->timeout);
-+}
-+
-+void Notification::setUrgency(int urgency)
-+{
-+ if (urgency != d->urgency) {
-+ d->urgency = urgency;
-+ if (urgency >= 2) {
-+ if (d->hideTimer) {
-+ d->hideTimer->stop();
-+ }
-+ d->deleteTimer->stop();
-+ } else {
-+ setTimeout(d->timeout);
-+ }
-+ }
-+}
-+
-+int Notification::urgency() const
-+{
-+ return d->urgency;
-+}
-+
-+QHash<QString, QString> Notification::actions() const
-+{
-+ return d->actions;
-+}
-+
-+
-+void Notification::setActions(const QHash<QString, QString> &actions)
-+{
-+ d->actions = actions;
-+ emit changed(this);
-+}
-+
-+
-+QStringList Notification::actionOrder() const
-+{
-+ return d->actionOrder;
-+}
-+
-+
-+void Notification::setActionOrder(const QStringList &actionOrder)
-+{
-+ d->actionOrder = actionOrder;
-+}
-+
-+
-+void Notification::triggerAction(const QString &actionId)
-+{
-+ Q_UNUSED(actionId);
-+ kDebug() << "action triggered but no handler implemented";
-+}
-+
-+void Notification::remove()
-+{
-+ kDebug() << "remove requested but no handler implemented";
-+}
-+
-+void Notification::linkActivated(const QString &link)
-+{
-+ Q_UNUSED(link)
-+ kDebug() << "link activation requested but no handler implemented";
-+}
-+
-+void Notification::hide()
-+{
-+ d->expired = true;
-+ emit expired(this);
-+}
-+
-+void Notification::startDeletionCountdown()
-+{
-+ if (d->urgency >= 2) {
-+ return;
-+ }
-+
-+ //keep it available for 10 minutes
-+ d->deleteTimer->start(10*60*1000);
-+}
-+
-+bool Notification::isExpired() const
-+{
-+ return d->expired;
-+}
-+
-+void Notification::setRead(const bool read)
-+{
-+ d->read = read;
-+}
-+
-+bool Notification::isRead() const
-+{
-+ return d->read;
-+}
-+
-+void Notification::setDeleteTimeout(const int time)
-+{
-+ if (d->deleteTimer->interval() != time) {
-+ d->deleteTimer->start(time);
-+ }
-+}
-+
-+int Notification::deleteTimeOut() const
-+{
-+ return d->deleteTimer->interval();
-+}
-+
-+
-+
-+#include "notification.moc"
-diff --git a/plasma/generic/applets/notifications/core/notification.h b/plasma/generic/applets/notifications/core/notification.h
-new file mode 100644
-index 0000000..e04fdba
---- /dev/null
-+++ b/plasma/generic/applets/notifications/core/notification.h
-@@ -0,0 +1,100 @@
-+/***************************************************************************
-+ * notification.h *
-+ * *
-+ * Copyright (C) 2008 Jason Stubbs <jasonbstubbs@gmail.com> *
-+ * *
-+ * This program is free software; you can redistribute it and/or modify *
-+ * it under the terms of the GNU General Public License as published by *
-+ * the Free Software Foundation; either version 2 of the License, or *
-+ * (at your option) any later version. *
-+ * *
-+ * This program is distributed in the hope that it will be useful, *
-+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
-+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
-+ * GNU General Public License for more details. *
-+ * *
-+ * You should have received a copy of the GNU General Public License *
-+ * along with this program; if not, write to the *
-+ * Free Software Foundation, Inc., *
-+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA . *
-+ ***************************************************************************/
-+
-+#ifndef NOTIFICATION_H
-+#define NOTIFICATION_H
-+
-+#include <QImage>
-+#include <QtCore/QHash>
-+#include <QtCore/QObject>
-+
-+#include <QtGui/QIcon>
-+
-+
-+
-+class Notification : public QObject
-+{
-+ Q_OBJECT
-+
-+public:
-+ Notification(QObject *parent = 0);
-+ virtual ~Notification();
-+
-+ QString applicationName() const;
-+ QIcon applicationIcon() const;
-+ QString message() const;
-+ QString summary() const;
-+ int timeout() const;
-+ QImage image() const;
-+
-+ void setUrgency(int urgency);
-+ int urgency() const;
-+
-+ QHash<QString, QString> actions() const;
-+ QStringList actionOrder() const;
-+
-+ bool isExpired() const;
-+
-+ void setRead(const bool read);
-+ bool isRead() const;
-+
-+ void setDeleteTimeout(const int time);
-+ int deleteTimeOut() const;
-+
-+public slots:
-+ virtual void triggerAction(const QString &actionId);
-+ virtual void remove();
-+ virtual void linkActivated(const QString &link);
-+ void startDeletionCountdown();
-+ void hide();
-+ void destroy();
-+
-+signals:
-+ void changed(Notification *notification = 0);
-+
-+ /**
-+ * Emitted when the notification is about to be destroyed
-+ **/
-+ void notificationDestroyed(Notification *notification = 0);
-+
-+ /**
-+ * emitted when the notification wants to hide itself
-+ */
-+ void expired(Notification *notification = 0);
-+
-+protected:
-+ void setApplicationName(const QString &applicationName);
-+ void setApplicationIcon(const QIcon &applicationIcon);
-+ void setMessage(const QString &message);
-+ void setSummary(const QString &summary);
-+ void setImage(QImage image);
-+ void setTimeout(int timeout);
-+ void setActions(const QHash<QString, QString> &actions);
-+ void setActionOrder(const QStringList &actionOrder);
-+
-+private:
-+ class Private;
-+ Private* const d;
-+};
-+
-+
-+
-+#endif
-diff --git a/plasma/generic/applets/notifications/core/notificationsmanager.cpp b/plasma/generic/applets/notifications/core/notificationsmanager.cpp
-new file mode 100644
-index 0000000..6892361
---- /dev/null
-+++ b/plasma/generic/applets/notifications/core/notificationsmanager.cpp
-@@ -0,0 +1,245 @@
-+/***************************************************************************
-+ * manager.cpp *
-+ * *
-+ * Copyright (C) 2008 Jason Stubbs <jasonbstubbs@gmail.com> *
-+ * Copyright (C) 2010 Marco Martin <notmart@gmail.com> *
-+ * *
-+ * This program is free software; you can redistribute it and/or modify *
-+ * it under the terms of the GNU General Public License as published by *
-+ * the Free Software Foundation; either version 2 of the License, or *
-+ * (at your option) any later version. *
-+ * *
-+ * This program is distributed in the hope that it will be useful, *
-+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
-+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
-+ * GNU General Public License for more details. *
-+ * *
-+ * You should have received a copy of the GNU General Public License *
-+ * along with this program; if not, write to the *
-+ * Free Software Foundation, Inc., *
-+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA . *
-+ ***************************************************************************/
-+
-+#include "notificationsmanager.h"
-+
-+#include <QTimer>
-+
-+#include <KGlobal>
-+
-+#include <plasma/applet.h>
-+
-+#include "job.h"
-+#include "notification.h"
-+#include "protocol.h"
-+
-+#include "../protocols/notifications/dbusnotificationprotocol.h"
-+#include "../protocols/jobs/dbusjobprotocol.h"
-+
-+
-+static const int idleCheckInterval = 60 * 1000;
-+
-+class Manager::Private
-+{
-+public:
-+ Private(Manager *manager)
-+ : q(manager),
-+ jobTotals(new Job(manager)),
-+ jobProtocol(0),
-+ notificationProtocol(0)
-+ {
-+ }
-+
-+ void setupProtocol(Protocol *protocol);
-+ void checkIdle();
-+
-+ Manager *q;
-+ QList<Task *> tasks;
-+ QList<Notification*> notifications;
-+ QList<Job *> jobs;
-+ Job *jobTotals;
-+ Notifications *notificationsApplet;
-+ Protocol *jobProtocol;
-+ Protocol *notificationProtocol;
-+ QTimer *idleTimer;
-+ static const int s_notificationLimit = 15;
-+};
-+
-+
-+Manager::Manager(Notifications *parentApplet)
-+ : QObject(parentApplet),
-+ d(new Private(this))
-+{
-+ d->notificationsApplet = parentApplet;
-+ d->idleTimer = new QTimer(this);
-+ d->idleTimer->setSingleShot(false);
-+ connect(d->idleTimer, SIGNAL(timeout()), this, SLOT(checkIdle()));
-+}
-+
-+Manager::~Manager()
-+{
-+ delete d;
-+}
-+
-+void Manager::registerNotificationProtocol()
-+{
-+ if (!d->notificationProtocol) {
-+ d->notificationProtocol = new DBusNotificationProtocol(this);
-+ d->setupProtocol(d->notificationProtocol);
-+ }
-+}
-+
-+void Manager::unregisterNotificationProtocol()
-+{
-+ delete d->notificationProtocol;
-+ d->notificationProtocol = 0;
-+}
-+
-+void Manager::addNotification(Notification* notification)
-+{
-+ connect(notification, SIGNAL(notificationDestroyed(Notification*)),
-+ this, SLOT(removeNotification(Notification*)));
-+ connect(notification, SIGNAL(changed(Notification*)),
-+ this, SIGNAL(notificationChanged(Notification*)));
-+ connect(notification, SIGNAL(expired(Notification*)),
-+ this, SIGNAL(notificationExpired(Notification*)));
-+
-+ d->notifications.append(notification);
-+
-+ if (!d->idleTimer->isActive()) {
-+ d->idleTimer->start(idleCheckInterval);
-+ }
-+ connect(this, SIGNAL(idleTerminated()), notification, SLOT(startDeletionCountdown()));
-+
-+ emit notificationAdded(notification);
-+
-+ if (d->notifications.count() > d->s_notificationLimit) {
-+ Notification *notification = d->notifications.first();
-+ d->notifications.pop_front();
-+ notification->deleteLater();
-+ }
-+}
-+
-+void Manager::removeNotification(Notification *notification)
-+{
-+ d->notifications.removeAll(notification);
-+ disconnect(notification, 0, this, 0);
-+ disconnect(this, 0, notification, 0);
-+
-+ if (d->notifications.isEmpty()) {
-+ d->idleTimer->stop();
-+ }
-+
-+ emit notificationRemoved(notification);
-+}
-+
-+QList<Notification*> Manager::notifications() const
-+{
-+ return d->notifications;
-+}
-+
-+void Manager::clearNotifications()
-+{
-+ qDeleteAll(d->notifications);
-+ d->notifications.clear();
-+}
-+
-+void Manager::registerJobProtocol()
-+{
-+ if (!d->jobProtocol) {
-+ d->jobProtocol = new DBusJobProtocol(this);
-+ d->setupProtocol(d->jobProtocol);
-+ }
-+}
-+
-+void Manager::unregisterJobProtocol()
-+{
-+ delete d->jobProtocol;
-+ d->jobProtocol = 0;
-+}
-+
-+void Manager::addJob(Job *job)
-+{
-+ connect(job, SIGNAL(destroyed(Job*)), this, SLOT(removeJob(Job*)));
-+ connect(job, SIGNAL(changed(Job*)), this, SIGNAL(jobChanged(Job*)));
-+ connect(job, SIGNAL(stateChanged(Job*)), this, SIGNAL(jobStateChanged(Job*)));
-+ connect(job, SIGNAL(changed(Job*)), this, SLOT(updateTotals()));
-+
-+ d->jobs.append(job);
-+ emit jobAdded(job);
-+}
-+
-+void Manager::removeJob(Job *job)
-+{
-+ d->jobs.removeAll(job);
-+ disconnect(job);
-+ updateTotals();
-+ emit jobRemoved(job);
-+}
-+
-+void Manager::updateTotals()
-+{
-+ uint totalPercent = 0;
-+ ulong totalEta = 0;
-+ foreach (Job *job, d->jobs) {
-+ totalPercent += job->percentage();
-+ if (job->eta() > totalEta) {
-+ totalEta = job->eta();
-+ }
-+ }
-+
-+ if (d->jobs.count() > 0) {
-+ d->jobTotals->setPercentage(totalPercent / d->jobs.count());
-+ d->jobTotals->setMessage(i18np("1 running job (%2 remaining)", "%1 running jobs (%2 remaining)",
-+ d->jobs.count(),
-+ KGlobal::locale()->prettyFormatDuration(totalEta)));
-+ } else {
-+ d->jobTotals->setPercentage(0);
-+ d->jobTotals->setMessage(i18n("no running jobs"));
-+ }
-+ //TODO: set a sensible icon
-+}
-+
-+Job *Manager::jobTotals() const
-+{
-+ return d->jobTotals;
-+}
-+
-+QList<Job*> Manager::jobs() const
-+{
-+ return d->jobs;
-+}
-+
-+void Manager::checkIdle()
-+{
-+ int totalIdle;
-+#ifdef HAVE_LIBXSS // Idle detection.
-+ XScreenSaverInfo* _mit_info;
-+ _mit_info = XScreenSaverAllocInfo();
-+ XScreenSaverQueryInfo( QX11Info::display(), QX11Info::appRootWindow(), _mit_info );
-+ totalIdle = _mit_info->idle;
-+ XFree( _mit_info );
-+#else
-+ totalIdle = 0;
-+#endif // HAVE_LIBXSS
-+
-+ if (totalIdle < idleCheckInterval) {
-+ d->idleTimer->stop();
-+ emit idleTerminated();
-+ }
-+}
-+
-+void Manager::Private::setupProtocol(Protocol *protocol)
-+{
-+ connect(protocol, SIGNAL(jobCreated(Job*)), q, SLOT(addJob(Job*)));
-+ connect(protocol, SIGNAL(notificationCreated(Notification*)),
-+ q, SLOT(addNotification(Notification*)));
-+ protocol->init();
-+}
-+
-+Notifications *Manager::applet() const
-+{
-+ return d->notificationsApplet;
-+}
-+
-+
-+#include "notificationsmanager.moc"
-diff --git a/plasma/generic/applets/notifications/core/notificationsmanager.h b/plasma/generic/applets/notifications/core/notificationsmanager.h
-new file mode 100644
-index 0000000..a6d6639
---- /dev/null
-+++ b/plasma/generic/applets/notifications/core/notificationsmanager.h
-@@ -0,0 +1,161 @@
-+/***************************************************************************
-+ * manager.h *
-+ * *
-+ * Copyright (C) 2008 Jason Stubbs <jasonbstubbs@gmail.com> *
-+ * Copyright (C) 2010 Marco Martin <notmart@gmail.com> *
-+ * *
-+ * This program is free software; you can redistribute it and/or modify *
-+ * it under the terms of the GNU General Public License as published by *
-+ * the Free Software Foundation; either version 2 of the License, or *
-+ * (at your option) any later version. *
-+ * *
-+ * This program is distributed in the hope that it will be useful, *
-+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
-+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
-+ * GNU General Public License for more details. *
-+ * *
-+ * You should have received a copy of the GNU General Public License *
-+ * along with this program; if not, write to the *
-+ * Free Software Foundation, Inc., *
-+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA . *
-+ ***************************************************************************/
-+
-+#ifndef NOTIFICATIONSMANAGER_H
-+#define NOTIFICATIONSMANAGER_H
-+
-+#include <QtCore/QObject>
-+
-+#include <KConfigGroup>
-+
-+#include <plasma/plasma.h>
-+#include "../ui/notifications.h"
-+
-+
-+namespace Plasma
-+{
-+class Applet;
-+}
-+
-+class Notifications;
-+class Notification;
-+class Task;
-+class Job;
-+
-+/**
-+ * w
-+ * @short Creator and amalgamator of the supported system tray specifications
-+ **/
-+class Manager : public QObject
-+{
-+ Q_OBJECT
-+
-+public:
-+ Manager(Notifications *parentApplet);
-+ ~Manager();
-+
-+ /**
-+ * @return a list of all known Notification instances
-+ **/
-+ QList<Notification*> notifications() const;
-+
-+ /**
-+ * clear all notifications
-+ */
-+ void clearNotifications();
-+
-+ /**
-+ * @return a list of all known Job instances
-+ **/
-+ QList<Job*> jobs() const;
-+
-+ /**
-+ * @return a Job instance that can be used to monitor total progress
-+ **/
-+ Job *jobTotals() const;
-+
-+ /**
-+ * Integrates the Job progress info into the applet's notification system
-+ **/
-+ void registerJobProtocol();
-+
-+ /**
-+ * Iintegrates the notifications into the applet's notification system
-+ **/
-+ void registerNotificationProtocol();
-+
-+ /**
-+ * Removes the Job progress info from the applet's notification system
-+ **/
-+ void unregisterJobProtocol();
-+
-+ /**
-+ * Removes the notifications from the applet's notification system
-+ **/
-+ void unregisterNotificationProtocol();
-+
-+ Notifications *applet() const;
-+
-+signals:
-+ /**
-+ * Emitted when a new notification has been added
-+ **/
-+ void notificationAdded(Notification *notification);
-+
-+ /**
-+ * Emitted when something about a notification changes
-+ **/
-+ void notificationChanged(Notification *notification);
-+
-+ /**
-+ * The notification is expired and wants to hide itself
-+ */
-+ void notificationExpired(Notification *notification);
-+
-+ /**
-+ * Emitted when a notification has been removed
-+ **/
-+ void notificationRemoved(Notification *notification);
-+
-+ /**
-+ * Emitted when a new job has been added
-+ **/
-+ void jobAdded(Job *job);
-+
-+ /**
-+ * Emitted when the state of a job changes
-+ **/
-+ void jobStateChanged(Job *job);
-+
-+ /**
-+ * Emitted when something about a job changes
-+ **/
-+ void jobChanged(Job *job);
-+
-+ /**
-+ * Emitted when a job has been removed
-+ **/
-+ void jobRemoved(Job *job);
-+
-+ /**
-+ * the pc is out of idle and is starting being used
-+ */
-+ void idleTerminated();
-+
-+private slots:
-+ void addNotification(Notification *notification);
-+ void removeNotification(Notification *notification);
-+ void addJob(Job *job);
-+ void removeJob(Job *job);
-+ void updateTotals();
-+ void checkIdle();
-+
-+private:
-+ class Private;
-+ Private* const d;
-+
-+ friend class Notifications;
-+};
-+
-+
-+
-+#endif
-diff --git a/plasma/generic/applets/notifications/core/protocol.cpp b/plasma/generic/applets/notifications/core/protocol.cpp
-new file mode 100644
-index 0000000..a3716a2
---- /dev/null
-+++ b/plasma/generic/applets/notifications/core/protocol.cpp
-@@ -0,0 +1,31 @@
-+/***************************************************************************
-+ * taskprotocol.cpp *
-+ * *
-+ * Copyright (C) 2008 Jason Stubbs <jasonbstubbs@gmail.com> *
-+ * *
-+ * This program is free software; you can redistribute it and/or modify *
-+ * it under the terms of the GNU General Public License as published by *
-+ * the Free Software Foundation; either version 2 of the License, or *
-+ * (at your option) any later version. *
-+ * *
-+ * This program is distributed in the hope that it will be useful, *
-+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
-+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
-+ * GNU General Public License for more details. *
-+ * *
-+ * You should have received a copy of the GNU General Public License *
-+ * along with this program; if not, write to the *
-+ * Free Software Foundation, Inc., *
-+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA . *
-+ ***************************************************************************/
-+
-+#include "protocol.h"
-+
-+
-+Protocol::Protocol(QObject *parent)
-+ : QObject(parent)
-+{
-+}
-+
-+
-+#include "protocol.moc"
-diff --git a/plasma/generic/applets/notifications/core/protocol.h b/plasma/generic/applets/notifications/core/protocol.h
-new file mode 100644
-index 0000000..ea47292
---- /dev/null
-+++ b/plasma/generic/applets/notifications/core/protocol.h
-@@ -0,0 +1,68 @@
-+/***************************************************************************
-+ * taskprotocol.h *
-+ * *
-+ * Copyright (C) 2008 Jason Stubbs <jasonbstubbs@gmail.com> *
-+ * *
-+ * This program is free software; you can redistribute it and/or modify *
-+ * it under the terms of the GNU General Public License as published by *
-+ * the Free Software Foundation; either version 2 of the License, or *
-+ * (at your option) any later version. *
-+ * *
-+ * This program is distributed in the hope that it will be useful, *
-+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
-+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
-+ * GNU General Public License for more details. *
-+ * *
-+ * You should have received a copy of the GNU General Public License *
-+ * along with this program; if not, write to the *
-+ * Free Software Foundation, Inc., *
-+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA . *
-+ ***************************************************************************/
-+
-+#ifndef PROTOCOL_H
-+#define PROTOCOL_H
-+
-+#include <QtCore/QObject>
-+
-+
-+class Job;
-+class Notification;
-+class Task;
-+
-+
-+/**
-+ * @short System tray protocol base class
-+ *
-+ * To support a new system tray protocol, this class and Task should be
-+ * subclassed and the subclass of this class registered with the global
-+ * Manager. The Protocol subclass should emit taskCreated() for each new
-+ * task created.
-+ **/
-+class Protocol : public QObject
-+{
-+ Q_OBJECT
-+public:
-+ explicit Protocol(QObject *parent);
-+
-+ virtual void init() = 0;
-+
-+signals:
-+ /**
-+ * Signals that a new task has been created
-+ **/
-+ void taskCreated(Task *task);
-+
-+ /**
-+ * Signals that a new notification has been created
-+ **/
-+ void jobCreated(Job *job);
-+
-+ /**
-+ * Signals that a new notification has been created
-+ **/
-+ void notificationCreated(Notification *notification);
-+};
-+
-+
-+
-+#endif
-diff --git a/plasma/generic/applets/notifications/metadata.desktop b/plasma/generic/applets/notifications/metadata.desktop
-deleted file mode 100644
-index 3a2be65..0000000
---- a/plasma/generic/applets/notifications/metadata.desktop
-+++ /dev/null
-@@ -1,166 +0,0 @@
--[Desktop Entry]
--Encoding=UTF-8
--Name=Notifications
--Name[ar]=تنبيهات
--Name[ast]=Notificaciones
--Name[be]=Абвяшчэнні
--Name[be@latin]=Infarmavańnie
--Name[bg]=Уведомяване
--Name[bn]=বিজ্ঞপ্তি
--Name[bn_IN]=সূচনাবার্তা
--Name[br]=Kemenn
--Name[bs]=obavještenja
--Name[ca]=Notificacions
--Name[ca@valencia]=Notificacions
--Name[cs]=Oznamování
--Name[csb]=Dôwanié wiédzë
--Name[da]=Bekendtgørelser
--Name[de]=Benachrichtigungen
--Name[el]=Ειδοποιήσεις
--Name[en_GB]=Notifications
--Name[eo]=Atentigoj
--Name[es]=Notificaciones
--Name[et]=Märguanded
--Name[eu]=Jakinarazpenak
--Name[fa]=اخطارها
--Name[fi]=Ilmoitukset
--Name[fr]=Notifications
--Name[fy]=Notifikaasjes
--Name[ga]=Fógairt
--Name[gl]=Notificacións
--Name[gu]=નોંધણીઓ
--Name[he]=הודעות
--Name[hi]=सूचनाएँ
--Name[hne]=सूचना मन ल
--Name[hr]=Obavijesti
--Name[hu]=Rendszerüzenetek
--Name[ia]=Notificationes
--Name[id]=Notifikasi
--Name[is]=Kerfistilkynningar
--Name[it]=Notifiche
--Name[ja]=通知
--Name[kk]=Құлақтандыру
--Name[km]=សេចក្តីជូនដំណឹង
--Name[kn]=ಸೂಚನೆಗಳು
--Name[ko]=알림
--Name[ku]=Agahdarî
--Name[lt]=Pranešimai
--Name[lv]=Paziņojumi
--Name[mai]=सूचनासभ
--Name[mk]=Известувања
--Name[ml]=അറിയിപ്പുകള്
--Name[mr]=सूचना
--Name[ms]=Pemberitahuan
--Name[nb]=Varslinger
--Name[nds]=Bescheden
--Name[ne]=सूचना
--Name[nl]=Meldingen
--Name[nn]=Varslingar
--Name[oc]=Notificacions
--Name[or]=ବିଜ୍ଞପ୍ତି
--Name[pa]=ਨੋਟੀਫਿਕੇਸ਼ਨ
--Name[pl]=Powiadomienia
--Name[pt]=Notificações
--Name[pt_BR]=Notificações
--Name[ro]=Notificări
--Name[ru]=Системные уведомления
--Name[se]=Dieđáhusat
--Name[si]=දැනුම් දීම්
--Name[sk]=Upozornenia
--Name[sl]=Obvestila
--Name[sr]=обавештења
--Name[sr@ijekavian]=обавјештења
--Name[sr@ijekavianlatin]=obavještenja
--Name[sr@latin]=obaveštenja
--Name[sv]=Underrättelser
--Name[ta]=Notifications
--Name[te]=నోటీసులు
--Name[tg]=Огоҳиномаҳо
--Name[th]=การแจ้งให้ทราบต่าง ๆ
--Name[tr]=Bildirimler
--Name[ug]=ئۇقتۇرۇشلار
--Name[uk]=Сповіщення
--Name[uz]=Xabarnomalar
--Name[uz@cyrillic]=Хабарномалар
--Name[wa]=Notifiaedjes
--Name[x-test]=xxNotificationsxx
--Name[zh_CN]=通知
--Name[zh_TW]=通知
--Comment=Display notifications and jobs
--Comment[ar]=أظهر التنبيهات والمهام
--Comment[ast]=Amosar notificaciones y xeres
--Comment[bg]=Показване на уведомления и задачи
--Comment[bs]=Prikazuje obavještenja i poslove
--Comment[ca]=Mostra les notificacions i els treballs
--Comment[ca@valencia]=Mostra les notificacions i els treballs
--Comment[cs]=Oznámení a úlohy
--Comment[da]=Vis bekendtgørelser og job
--Comment[de]=Benachrichtigungen und Aktionen anzeigen
--Comment[el]=Εμφανίζει ειδοποιήσεις και εργασίες
--Comment[en_GB]=Display notifications and jobs
--Comment[es]=Mostrar notificaciones y tareas
--Comment[et]=Märguannete ja tööde näitamine
--Comment[eu]=Bistaratu jakinarazpenak eta lanak
--Comment[fi]=Näyttää ilmoituksia ja töitä
--Comment[fr]=Affiche les notifications et les tâches
--Comment[ga]=Taispeáin fógraí agus jabanna
--Comment[gl]=Mostra notificacións e tarefas
--Comment[he]=משמש להצגת הודעות ועבודות
--Comment[hr]=Prikazuje obavijesti i poslove
--Comment[hu]=Értesítések és feladatok megjelenítése
--Comment[ia]=Monstra notificationes e labores
--Comment[id]=Tampilan notifikasi dan tugas
--Comment[is]=Birting tilkynninga og verka
--Comment[it]=Mostra notifiche e processi
--Comment[ja]=ディスプレイ通知とジョブ
--Comment[kk]=Құлақтандыру мен тапсырмаларды көрсету
--Comment[km]=បង្ហាញការជូនដំណឹង និងការងារ
--Comment[kn]=ಸೂಚನೆಗಳು ಹಾಗು ಕಾರ್ಯಗಳನ್ನು ಪ್ರದರ್ಶಿಸು
--Comment[ko]=알림과 작업 표시
--Comment[lt]=Rodyti pranešimus ir darbus
--Comment[lv]=Parāda paziņojumus un darbus
--Comment[mr]=सूचना व कार्यै दर्शवा
--Comment[nb]=Vis varslinger og jobber
--Comment[nds]=Bescheden un Opgaven wiesen
--Comment[nl]=Meldingen en taken tonen
--Comment[pa]=ਨੋਟੀਫਿਕੇਸ਼ਨ ਤੇ ਜਾਬ ਵੇਖੋ
--Comment[pl]=Wyświetlanie powiadomień i zadań
--Comment[pt]=Mostrar as notificações e tarefas
--Comment[pt_BR]=Exibe notificações e tarefas
--Comment[ro]=Afișează notificări și sarcini
--Comment[ru]=Уведомления и задания
--Comment[si]=කාර්ය සහ දැනුම්දීමෙ පෙන්වන්න
--Comment[sk]=Zobrazenie upozornení a úloh
--Comment[sl]=Prikazuje obvestila in opravila
--Comment[sr]=Приказује обавештења и послове
--Comment[sr@ijekavian]=Приказује обавјештења и послове
--Comment[sr@ijekavianlatin]=Prikazuje obavještenja i poslove
--Comment[sr@latin]=Prikazuje obaveštenja i poslove
--Comment[sv]=Visa underrättelser och jobb
--Comment[tg]=Иттилооти огоҳиҳо ва амалҳо
--Comment[th]=แสดงการแจ้งให้ทราบและงานต่าง ๆ
--Comment[tr]=Bildirimleri ve görevleri göster
--Comment[ug]=ئۇقتۇرۇش ۋە ۋەزىپىلەرنى كۆرسىتىدۇ
--Comment[uk]=Показ сповіщень і завдань
--Comment[vi]=Hiển thị thông báo và công việc
--Comment[wa]=Håyner notifiaedjes eyet bouyes
--Comment[x-test]=xxDisplay notifications and jobsxx
--Comment[zh_CN]=显示通知和任务
--Comment[zh_TW]=顯示通知與工作
--
--Type=Service
--Icon=preferences-desktop-notification
--X-KDE-ParentApp=
--X-KDE-PluginInfo-Author=Davide Bettio
--X-KDE-PluginInfo-Category=Tasks
--X-KDE-PluginInfo-Email=davide.bettio@kdemail.net
--X-KDE-PluginInfo-License=GPL
--X-KDE-PluginInfo-Name=org.kde.notifications
--X-KDE-PluginInfo-Version=0.1
--X-KDE-PluginInfo-Website=http://plasma.kde.org/
--X-KDE-ServiceTypes=Plasma/PopupApplet,Plasma/Applet
--X-Plasma-API=declarativeappletscript
--X-Plasma-DefaultSize=100,100
--X-Plasma-MainScript=ui/main.qml
--X-Plasma-RequiredExtensions=LaunchApp
--X-Plasma-NotificationArea=true
-diff --git a/plasma/generic/applets/notifications/plasma-applet-notifications.desktop b/plasma/generic/applets/notifications/plasma-applet-notifications.desktop
-new file mode 100644
-index 0000000..78a76f8
---- /dev/null
-+++ b/plasma/generic/applets/notifications/plasma-applet-notifications.desktop
-@@ -0,0 +1,164 @@
-+[Desktop Entry]
-+Name=Notifications
-+Name[ar]=تنبيهات
-+Name[ast]=Notificaciones
-+Name[be]=Абвяшчэнні
-+Name[be@latin]=Infarmavańnie
-+Name[bg]=Уведомяване
-+Name[bn]=বিজ্ঞপ্তি
-+Name[bn_IN]=সূচনাবার্তা
-+Name[br]=Kemenn
-+Name[bs]=obavještenja
-+Name[ca]=Notificacions
-+Name[ca@valencia]=Notificacions
-+Name[cs]=Oznamování
-+Name[csb]=Dôwanié wiédzë
-+Name[da]=Bekendtgørelser
-+Name[de]=Benachrichtigungen
-+Name[el]=Ειδοποιήσεις
-+Name[en_GB]=Notifications
-+Name[eo]=Atentigoj
-+Name[es]=Notificaciones
-+Name[et]=Märguanded
-+Name[eu]=Jakinarazpenak
-+Name[fa]=اخطارها
-+Name[fi]=Ilmoitukset
-+Name[fr]=Notifications
-+Name[fy]=Notifikaasjes
-+Name[ga]=Fógairt
-+Name[gl]=Notificacións
-+Name[gu]=નોંધણીઓ
-+Name[he]=הודעות
-+Name[hi]=सूचनाएँ
-+Name[hne]=सूचना मन ल
-+Name[hr]=Obavijesti
-+Name[hu]=Rendszerüzenetek
-+Name[ia]=Notificationes
-+Name[id]=Notifikasi
-+Name[is]=Kerfistilkynningar
-+Name[it]=Notifiche
-+Name[ja]=通知
-+Name[kk]=Құлақтандыру
-+Name[km]=សេចក្តីជូនដំណឹង
-+Name[kn]=ಸೂಚನೆಗಳು
-+Name[ko]=알림
-+Name[ku]=Agahdarî
-+Name[lt]=Pranešimai
-+Name[lv]=Paziņojumi
-+Name[mai]=सूचनासभ
-+Name[mk]=Известувања
-+Name[ml]=അറിയിപ്പുകള്
-+Name[mr]=सूचना
-+Name[ms]=Pemberitahuan
-+Name[nb]=Varslinger
-+Name[nds]=Bescheden
-+Name[ne]=सूचना
-+Name[nl]=Meldingen
-+Name[nn]=Varslingar
-+Name[oc]=Notificacions
-+Name[or]=ବିଜ୍ଞପ୍ତି
-+Name[pa]=ਨੋਟੀਫਿਕੇਸ਼ਨ
-+Name[pl]=Powiadomienia
-+Name[pt]=Notificações
-+Name[pt_BR]=Notificações
-+Name[ro]=Notificări
-+Name[ru]=Системные уведомления
-+Name[se]=Dieđáhusat
-+Name[si]=දැනුම් දීම්
-+Name[sk]=Upozornenia
-+Name[sl]=Obvestila
-+Name[sr]=обавештења
-+Name[sr@ijekavian]=обавјештења
-+Name[sr@ijekavianlatin]=obavještenja
-+Name[sr@latin]=obaveštenja
-+Name[sv]=Underrättelser
-+Name[ta]=Notifications
-+Name[te]=నోటీసులు
-+Name[tg]=Огоҳиномаҳо
-+Name[th]=การแจ้งให้ทราบต่าง ๆ
-+Name[tr]=Bildirimler
-+Name[ug]=ئۇقتۇرۇشلار
-+Name[uk]=Сповіщення
-+Name[uz]=Xabarnomalar
-+Name[uz@cyrillic]=Хабарномалар
-+Name[wa]=Notifiaedjes
-+Name[x-test]=xxNotificationsxx
-+Name[zh_CN]=通知
-+Name[zh_TW]=通知
-+Comment=Display notifications and jobs
-+Comment[ar]=أظهر التنبيهات والمهام
-+Comment[ast]=Amosar notificaciones y xeres
-+Comment[bg]=Показване на уведомления и задачи
-+Comment[bs]=Prikazuje obavještenja i poslove
-+Comment[ca]=Mostra les notificacions i els treballs
-+Comment[ca@valencia]=Mostra les notificacions i els treballs
-+Comment[cs]=Oznámení a úlohy
-+Comment[da]=Vis bekendtgørelser og job
-+Comment[de]=Benachrichtigungen und Aktionen anzeigen
-+Comment[el]=Εμφανίζει ειδοποιήσεις και εργασίες
-+Comment[en_GB]=Display notifications and jobs
-+Comment[es]=Mostrar notificaciones y tareas
-+Comment[et]=Märguannete ja tööde näitamine
-+Comment[eu]=Bistaratu jakinarazpenak eta lanak
-+Comment[fi]=Näyttää ilmoituksia ja töitä
-+Comment[fr]=Affiche les notifications et les tâches
-+Comment[ga]=Taispeáin fógraí agus jabanna
-+Comment[gl]=Mostra notificacións e tarefas
-+Comment[he]=משמש להצגת הודעות ועבודות
-+Comment[hr]=Prikazuje obavijesti i poslove
-+Comment[hu]=Értesítések és feladatok megjelenítése
-+Comment[ia]=Monstra notificationes e labores
-+Comment[id]=Tampilan notifikasi dan tugas
-+Comment[is]=Birting tilkynninga og verka
-+Comment[it]=Mostra notifiche e processi
-+Comment[ja]=ディスプレイ通知とジョブ
-+Comment[kk]=Құлақтандыру мен тапсырмаларды көрсету
-+Comment[km]=បង្ហាញការជូនដំណឹង និងការងារ
-+Comment[kn]=ಸೂಚನೆಗಳು ಹಾಗು ಕಾರ್ಯಗಳನ್ನು ಪ್ರದರ್ಶಿಸು
-+Comment[ko]=알림과 작업 표시
-+Comment[lt]=Rodyti pranešimus ir darbus
-+Comment[lv]=Parāda paziņojumus un darbus
-+Comment[nb]=Vis varslinger og jobber
-+Comment[nds]=Bescheden un Opgaven wiesen
-+Comment[nl]=Meldingen en taken tonen
-+Comment[pa]=ਨੋਟੀਫਿਕੇਸ਼ਨ ਤੇ ਜਾਬ ਵੇਖੋ
-+Comment[pl]=Wyświetlanie powiadomień i zadań
-+Comment[pt]=Mostrar as notificações e tarefas
-+Comment[pt_BR]=Exibe notificações e tarefas
-+Comment[ro]=Afișează notificări și sarcini
-+Comment[ru]=Уведомления и задания
-+Comment[si]=කාර්ය සහ දැනුම්දීමෙ පෙන්වන්න
-+Comment[sk]=Zobrazenie upozornení a úloh
-+Comment[sl]=Prikazuje obvestila in opravila
-+Comment[sr]=Приказује обавештења и послове
-+Comment[sr@ijekavian]=Приказује обавјештења и послове
-+Comment[sr@ijekavianlatin]=Prikazuje obavještenja i poslove
-+Comment[sr@latin]=Prikazuje obaveštenja i poslove
-+Comment[sv]=Visa underrättelser och jobb
-+Comment[tg]=Иттилооти огоҳиҳо ва амалҳо
-+Comment[th]=แสดงการแจ้งให้ทราบและงานต่าง ๆ
-+Comment[tr]=Bildirimleri ve görevleri göster
-+Comment[ug]=ئۇقتۇرۇش ۋە ۋەزىپىلەرنى كۆرسىتىدۇ
-+Comment[uk]=Показ сповіщень і завдань
-+Comment[vi]=Hiển thị thông báo và công việc
-+Comment[wa]=Håyner notifiaedjes eyet bouyes
-+Comment[x-test]=xxDisplay notifications and jobsxx
-+Comment[zh_CN]=显示通知和任务
-+Comment[zh_TW]=顯示通知與工作
-+
-+Icon=dialog-information
-+Type=Service
-+X-KDE-ServiceTypes=Plasma/Applet
-+
-+X-KDE-Library=plasma_applet_notifications
-+X-KDE-PluginInfo-Author=Marco Martin
-+X-KDE-PluginInfo-Email=notmart@gmail.com
-+X-KDE-PluginInfo-Name=notifications
-+X-KDE-PluginInfo-Version=1.0
-+X-KDE-PluginInfo-Website=http://plasma.kde.org/
-+X-KDE-PluginInfo-Category=System Information
-+X-KDE-PluginInfo-Depends=
-+X-KDE-PluginInfo-License=GPL v2+
-+X-KDE-PluginInfo-EnabledByDefault=true
-+
-+X-Plasma-NotificationArea=true
-diff --git a/plasma/generic/applets/notifications/platformcontents/touch/ui/NotificationDelegate/NotificationDelegate.qml b/plasma/generic/applets/notifications/platformcontents/touch/ui/NotificationDelegate/NotificationDelegate.qml
-deleted file mode 100644
-index bdb74c2..0000000
---- a/plasma/generic/applets/notifications/platformcontents/touch/ui/NotificationDelegate/NotificationDelegate.qml
-+++ /dev/null
-@@ -1,195 +0,0 @@
--/*
-- * Copyright 2011 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 Library 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.
-- */
--
--import QtQuick 1.0
--import org.kde.plasma.core 0.1 as PlasmaCore
--import org.kde.plasma.components 0.1 as PlasmaComponents
--import org.kde.qtextracomponents 0.1
--
--PlasmaComponents.ListItem {
-- id: notificationItem
-- opacity: 1-Math.abs(x)/width
-- width: popupFlickable.width
-- property int toolIconSize: theme.smallMediumIconSize
-- property int layoutSpacing: 4
--
-- visible: appTabBar.currentTab == allAppsTab || appTabBar.currentTab.text == appName
--
-- Component.onCompleted: {
-- allApplicationsModel.addApplication(appIcon, appName)
-- mainScrollArea.height = mainScrollArea.implicitHeight
-- }
-- Component.onDestruction: {
-- allApplicationsModel.removeApplication(model.appName)
-- mainScrollArea.height = mainScrollArea.implicitHeight
-- }
-- Timer {
-- interval: 10*60*1000
-- repeat: false
-- running: !idleTimeSource.idle
-- onTriggered: {
-- notificationsModel.remove(index)
-- }
-- }
--
--
-- MouseArea {
-- width: parent.width
-- height: childrenRect.height
-- drag {
-- target: notificationItem
-- axis: Drag.XAxis
-- //kind of an hack over Column being too smart
-- minimumX: -parent.width + 1
-- maximumX: parent.width - 1
-- }
-- onReleased: {
-- if (notificationItem.x < -notificationItem.width/2) {
-- removeAnimation.exitFromRight = false
-- removeAnimation.running = true
-- } else if (notificationItem.x > notificationItem.width/2 ) {
-- removeAnimation.exitFromRight = true
-- removeAnimation.running = true
-- } else {
-- resetAnimation.running = true
-- }
-- }
-- SequentialAnimation {
-- id: removeAnimation
-- property bool exitFromRight: true
-- NumberAnimation {
-- target: notificationItem
-- properties: "x"
-- to: removeAnimation.exitFromRight ? notificationItem.width-1 : 1-notificationItem.width
-- duration: 250
-- easing.type: Easing.InOutQuad
-- }
-- NumberAnimation {
-- target: notificationItem
-- properties: "height"
-- to: 0
-- duration: 250
-- easing.type: Easing.InOutQuad
-- }
-- ScriptAction {
-- script: notificationsModel.remove(index)
-- }
-- }
-- SequentialAnimation {
-- id: resetAnimation
-- NumberAnimation {
-- target: notificationItem
-- properties: "x"
-- to: 0
-- duration: 250
-- easing.type: Easing.InOutQuad
-- }
-- }
-- Column {
-- spacing: notificationItem.layoutSpacing
-- width: parent.width
-- Item {
-- width: parent.width
-- height: summaryLabel.height
--
-- PlasmaComponents.Label {
-- id: summaryLabel
-- text: summary
-- height: paintedHeight
-- anchors {
-- left: parent.left
-- right: parent.right
-- leftMargin: closeButton.width
-- rightMargin: closeButton.width
-- }
-- horizontalAlignment: Text.AlignHCenter
-- elide: Text.ElideRight
-- }
--
-- PlasmaComponents.ToolButton {
-- id: closeButton
-- iconSource: "window-close"
-- width: notificationItem.toolIconSize
-- height: width
-- onClicked: removeAnimation.running = true
-- anchors {
-- top: parent.top
-- right: parent.right
-- }
-- }
-- }
--
-- Item {
-- height: childrenRect.height
-- width: parent.width
-- QIconItem {
-- id: appIconItem
-- icon: QIcon(appIcon)
-- width: theme.largeIconSize
-- height: theme.largeIconSize
-- visible: !imageItem.visible
-- anchors {
-- left: parent.left
-- verticalCenter: parent.verticalCenter
-- }
-- }
-- QImageItem {
-- id: imageItem
-- anchors.fill: appIconItem
-- image: model.image
-- smooth: true
-- visible: nativeWidth > 0
-- }
-- PlasmaComponents.Label {
-- text: body
-- color: theme.textColor
-- anchors {
-- left: appIconItem.right
-- right: actionsColumn.left
-- verticalCenter: parent.verticalCenter
-- leftMargin: 6
-- rightMargin: 6
-- }
-- wrapMode: Text.Wrap
-- }
-- Column {
-- id: actionsColumn
-- spacing: notificationItem.layoutSpacing
-- anchors {
-- right: parent.right
-- rightMargin: 6
-- verticalCenter: parent.verticalCenter
-- }
-- Repeater {
-- model: actions
-- PlasmaComponents.Button {
-- text: model.text
-- width: theme.defaultFont.mSize.width * 8
-- height: theme.defaultFont.mSize.width * 3
-- onClicked: {
-- executeAction(source, model.id)
-- actionsColumn.visible = false
-- }
-- }
-- }
-- }
-- }
-- }
-- }
--}
-diff --git a/plasma/generic/applets/notifications/platformcontents/touch/ui/NotificationDelegate/qmldir b/plasma/generic/applets/notifications/platformcontents/touch/ui/NotificationDelegate/qmldir
-deleted file mode 100644
-index 88fc37a..0000000
---- a/plasma/generic/applets/notifications/platformcontents/touch/ui/NotificationDelegate/qmldir
-+++ /dev/null
-@@ -1 +0,0 @@
--NotificationDelegate 0.1 NotificationDelegate.qml
-diff --git a/plasma/generic/applets/notifications/platformcontents/touch/ui/uiproperties.js b/plasma/generic/applets/notifications/platformcontents/touch/ui/uiproperties.js
-deleted file mode 100644
-index f2f3d6b..0000000
---- a/plasma/generic/applets/notifications/platformcontents/touch/ui/uiproperties.js
-+++ /dev/null
-@@ -1,23 +0,0 @@
--/*
-- * Copyright 2012 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 Library 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.
-- */
--
--
--var toolIconSize = theme.mediumIconSize
--var layoutSpacing = 6
--var touchInput = true
-diff --git a/plasma/generic/applets/notifications/protocols/jobs/dbusjob.cpp b/plasma/generic/applets/notifications/protocols/jobs/dbusjob.cpp
-new file mode 100644
-index 0000000..7c89275
---- /dev/null
-+++ b/plasma/generic/applets/notifications/protocols/jobs/dbusjob.cpp
-@@ -0,0 +1,54 @@
-+/***************************************************************************
-+ * Copyright (C) 2008 Rob Scheepmaker <r.scheepmaker@student.utwente.nl> *
-+ * *
-+ * This program is free software; you can redistribute it and/or modify *
-+ * it under the terms of the GNU General Public License as published by *
-+ * the Free Software Foundation; either version 2 of the License, or *
-+ * (at your option) any later version. *
-+ * *
-+ * This program is distributed in the hope that it will be useful, *
-+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
-+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
-+ * GNU General Public License for more details. *
-+ * *
-+ * You should have received a copy of the GNU General Public License *
-+ * along with this program; if not, write to the *
-+ * Free Software Foundation, Inc., *
-+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA . *
-+ ***************************************************************************/
-+
-+#include "dbusjob.h"
-+
-+#include <KDebug>
-+
-+
-+
-+DBusJob::DBusJob(const QString &source, QObject *parent)
-+ : Job(parent),
-+ m_source(source)
-+{
-+}
-+
-+DBusJob::~DBusJob()
-+{
-+ emit jobDeleted(m_source);
-+}
-+
-+void DBusJob::suspend()
-+{
-+ emit suspend(m_source);
-+ kDebug() << "suspend";
-+}
-+
-+void DBusJob::resume()
-+{
-+ emit resume(m_source);
-+ kDebug() << "resume";
-+}
-+
-+void DBusJob::stop()
-+{
-+ emit stop(m_source);
-+ kDebug() << "cancel";
-+}
-+
-diff --git a/plasma/generic/applets/notifications/protocols/jobs/dbusjob.h b/plasma/generic/applets/notifications/protocols/jobs/dbusjob.h
-new file mode 100644
-index 0000000..2e973d6
---- /dev/null
-+++ b/plasma/generic/applets/notifications/protocols/jobs/dbusjob.h
-@@ -0,0 +1,54 @@
-+/***************************************************************************
-+ * Copyright (C) 2008 Rob Scheepmaker <r.scheepmaker@student.utwente.nl> *
-+ * *
-+ * This program is free software; you can redistribute it and/or modify *
-+ * it under the terms of the GNU General Public License as published by *
-+ * the Free Software Foundation; either version 2 of the License, or *
-+ * (at your option) any later version. *
-+ * *
-+ * This program is distributed in the hope that it will be useful, *
-+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
-+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
-+ * GNU General Public License for more details. *
-+ * *
-+ * You should have received a copy of the GNU General Public License *
-+ * along with this program; if not, write to the *
-+ * Free Software Foundation, Inc., *
-+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA . *
-+ ***************************************************************************/
-+
-+#ifndef DBUSJOB_H
-+#define DBUSJOB_H
-+
-+#include "../../core/job.h"
-+
-+
-+
-+class DBusJob : public Job
-+{
-+ Q_OBJECT
-+
-+ friend class DBusJobProtocol;
-+
-+public:
-+ DBusJob(const QString &source, QObject *parent = 0);
-+ ~DBusJob();
-+
-+public slots:
-+ void suspend();
-+ void resume();
-+ void stop();
-+
-+signals:
-+ void jobDeleted(const QString &source);
-+ void suspend(const QString &source);
-+ void resume(const QString &source);
-+ void stop(const QString &source);
-+
-+private:
-+ QString m_source;
-+};
-+
-+
-+
-+#endif
-diff --git a/plasma/generic/applets/notifications/protocols/jobs/dbusjobprotocol.cpp b/plasma/generic/applets/notifications/protocols/jobs/dbusjobprotocol.cpp
-new file mode 100644
-index 0000000..dff6dc9
---- /dev/null
-+++ b/plasma/generic/applets/notifications/protocols/jobs/dbusjobprotocol.cpp
-@@ -0,0 +1,180 @@
-+/***************************************************************************
-+ * Copyright (C) 2008 Rob Scheepmaker <r.scheepmaker@student.utwente.nl> *
-+ * *
-+ * This program is free software; you can redistribute it and/or modify *
-+ * it under the terms of the GNU General Public License as published by *
-+ * the Free Software Foundation; either version 2 of the License, or *
-+ * (at your option) any later version. *
-+ * *
-+ * This program is distributed in the hope that it will be useful, *
-+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
-+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
-+ * GNU General Public License for more details. *
-+ * *
-+ * You should have received a copy of the GNU General Public License *
-+ * along with this program; if not, write to the *
-+ * Free Software Foundation, Inc., *
-+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA . *
-+ ***************************************************************************/
-+
-+#include "dbusjob.h"
-+#include "dbusjobprotocol.h"
-+
-+
-+#include <KConfigGroup>
-+
-+#include <Plasma/DataEngineManager>
-+#include <Plasma/Service>
-+#include <Plasma/ServiceJob>
-+
-+static const char engineName[] = "applicationjobs";
-+
-+DBusJobProtocol::DBusJobProtocol(Manager *parent)
-+ : Protocol(parent),
-+ m_manager(parent),
-+ m_engine(0)
-+{
-+}
-+
-+
-+DBusJobProtocol::~DBusJobProtocol()
-+{
-+ if (m_engine) {
-+ Plasma::DataEngineManager::self()->unloadEngine(engineName);
-+ }
-+
-+ foreach (DBusJob *job, m_jobs) {
-+ disconnect(job);
-+ job->destroy();
-+ }
-+
-+ m_jobs.clear();
-+}
-+
-+
-+void DBusJobProtocol::init()
-+{
-+ m_engine = Plasma::DataEngineManager::self()->loadEngine(engineName);
-+
-+ if (!m_engine->isValid()) {
-+ Plasma::DataEngineManager::self()->unloadEngine(engineName);
-+ m_engine = 0;
-+ return;
-+ }
-+
-+ connect(m_engine, SIGNAL(sourceAdded(QString)),
-+ this, SLOT(prepareJob(QString)));
-+ connect(m_engine, SIGNAL(sourceRemoved(QString)),
-+ this, SLOT(removeJob(QString)));
-+}
-+
-+void DBusJobProtocol::prepareJob(const QString &source)
-+{
-+ m_engine->connectSource(source, this);
-+}
-+
-+void DBusJobProtocol::dataUpdated(const QString &source, const Plasma::DataEngine::Data &data)
-+{
-+ DBusJob *job = m_jobs.value(source, 0);
-+
-+ if (!job) {
-+ job = new DBusJob(source, this);
-+ m_jobs.insert(source, job);
-+ connect(job, SIGNAL(jobDeleted(QString)),
-+ this, SLOT(removeJob(QString)));
-+ connect(job, SIGNAL(suspend(QString)),
-+ this, SLOT(suspend(QString)));
-+ connect(job, SIGNAL(resume(QString)),
-+ this, SLOT(resume(QString)));
-+ connect(job, SIGNAL(stop(QString)),
-+ this, SLOT(stop(QString)));
-+ connect(job, SIGNAL(ready(Job*)),
-+ this, SIGNAL(jobCreated(Job*)));
-+ }
-+
-+ job->setApplicationName(data.value("appName").toString());
-+ job->setApplicationIconName(data.value("appIconName").toString());
-+ job->setPercentage(data["percentage"].toUInt());
-+ job->setError(data["error"].toString());
-+ job->setMessage(data["infoMessage"].toString());
-+ job->setSuspendable(data["suspendable"].toBool());
-+ job->setKillable(data["killable"].toBool());
-+ job->setSpeed(data["speed"].toString());
-+ job->setNumericSpeed(data["numericSpeed"].toLongLong());
-+ job->setEta(data["eta"].toULongLong());
-+
-+ if (data["state"].toString() == "running") {
-+ job->setState(Job::Running);
-+ } else if (data["state"].toString() == "suspended") {
-+ job->setState(Job::Suspended);
-+ } else {
-+ job->setState(Job::Stopped);
-+ }
-+
-+ int i = 0;
-+ QList<QPair<QString, QString> > labels;
-+ while (data.contains(QString("label%1").arg(i))) {
-+ QPair<QString, QString> label;
-+ label.first = data[QString("labelName%1").arg(i)].toString();
-+ label.second = data[QString("label%1").arg(i)].toString();
-+ labels << label;
-+ i++;
-+ }
-+ job->setLabels(labels);
-+
-+ i = 0;
-+ QMap<QString, qlonglong> totalAmounts;
-+ while (data.contains(QString("totalUnit%1").arg(i))) {
-+ QString unit = data[QString("totalUnit%1").arg(i)].toString();
-+ qlonglong amount = data[QString("totalAmount%1").arg(i)].toLongLong();
-+ totalAmounts[unit] = amount;
-+ i++;
-+ }
-+ job->setTotalAmounts(totalAmounts);
-+
-+ i = 0;
-+ QMap<QString, qlonglong> processedAmounts;
-+ while (data.contains(QString("processedUnit%1").arg(i))) {
-+ QString unit = data[QString("processedUnit%1").arg(i)].toString();
-+ qlonglong amount = data[QString("processedAmount%1").arg(i)].toLongLong();
-+ processedAmounts[unit] = amount;
-+ i++;
-+ }
-+
-+ job->setProcessedAmounts(processedAmounts);
-+}
-+
-+void DBusJobProtocol::removeJob(const QString &source)
-+{
-+ if (m_jobs.contains(source)) {
-+ DBusJob *job = m_jobs.take(source);
-+ job->setState(Job::Stopped);
-+ job->destroy();
-+ }
-+}
-+
-+void DBusJobProtocol::suspend(const QString &source)
-+{
-+ Plasma::Service *service = m_engine->serviceForSource(source);
-+ KConfigGroup op = service->operationDescription("suspend");
-+ KJob *job = service->startOperationCall(op);
-+ connect(job, SIGNAL(finished(KJob*)), service, SLOT(deleteLater()));
-+}
-+
-+void DBusJobProtocol::resume(const QString &source)
-+{
-+ Plasma::Service *service = m_engine->serviceForSource(source);
-+ KConfigGroup op = service->operationDescription("resume");
-+ KJob *job = service->startOperationCall(op);
-+ connect(job, SIGNAL(finished(KJob*)), service, SLOT(deleteLater()));
-+}
-+
-+void DBusJobProtocol::stop(const QString &source)
-+{
-+ Plasma::Service *service = m_engine->serviceForSource(source);
-+ KConfigGroup op = service->operationDescription("stop");
-+ KJob *job = service->startOperationCall(op);
-+ connect(job, SIGNAL(finished(KJob*)), service, SLOT(deleteLater()));
-+}
-+
-+#include "dbusjobprotocol.moc"
-diff --git a/plasma/generic/applets/notifications/protocols/jobs/dbusjobprotocol.h b/plasma/generic/applets/notifications/protocols/jobs/dbusjobprotocol.h
-new file mode 100644
-index 0000000..230356c
---- /dev/null
-+++ b/plasma/generic/applets/notifications/protocols/jobs/dbusjobprotocol.h
-@@ -0,0 +1,57 @@
-+/***************************************************************************
-+ * Copyright (C) 2008 Rob Scheepmaker <r.scheepmaker@student.utwente.nl> *
-+ * *
-+ * This program is free software; you can redistribute it and/or modify *
-+ * it under the terms of the GNU General Public License as published by *
-+ * the Free Software Foundation; either version 2 of the License, or *
-+ * (at your option) any later version. *
-+ * *
-+ * This program is distributed in the hope that it will be useful, *
-+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
-+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
-+ * GNU General Public License for more details. *
-+ * *
-+ * You should have received a copy of the GNU General Public License *
-+ * along with this program; if not, write to the *
-+ * Free Software Foundation, Inc., *
-+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA . *
-+ ***************************************************************************/
-+
-+#ifndef DBUSJOBPROTOCOL_H
-+#define DBUSJOBPROTOCOL_H
-+
-+#include "../../core/protocol.h"
-+#include "../../core/notificationsmanager.h"
-+
-+#include <plasma/dataengine.h>
-+
-+
-+class DBusJob;
-+
-+class DBusJobProtocol : public Protocol
-+{
-+ Q_OBJECT
-+
-+public:
-+ DBusJobProtocol(Manager *parent);
-+ ~DBusJobProtocol();
-+ void init();
-+
-+private slots:
-+ void prepareJob(const QString &source);
-+ void dataUpdated(const QString &source, const Plasma::DataEngine::Data &data);
-+ void removeJob(const QString &source);
-+ //void relayAction(const QString &source, const QString &actionName);
-+ void suspend(const QString &source);
-+ void resume(const QString &source);
-+ void stop(const QString &source);
-+
-+private:
-+ Manager *m_manager;
-+ Plasma::DataEngine *m_engine;
-+ QHash<QString, DBusJob*> m_jobs;
-+};
-+
-+
-+
-+#endif
-diff --git a/plasma/generic/applets/notifications/protocols/notifications/dbusnotification.cpp b/plasma/generic/applets/notifications/protocols/notifications/dbusnotification.cpp
-new file mode 100644
-index 0000000..2c28ff2
---- /dev/null
-+++ b/plasma/generic/applets/notifications/protocols/notifications/dbusnotification.cpp
-@@ -0,0 +1,48 @@
-+/***************************************************************************
-+ * dbusnotification.cpp *
-+ * *
-+ * Copyright (C) 2008 Jason Stubbs <jasonbstubbs@gmail.com> *
-+ * *
-+ * This program is free software; you can redistribute it and/or modify *
-+ * it under the terms of the GNU General Public License as published by *
-+ * the Free Software Foundation; either version 2 of the License, or *
-+ * (at your option) any later version. *
-+ * *
-+ * This program is distributed in the hope that it will be useful, *
-+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
-+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
-+ * GNU General Public License for more details. *
-+ * *
-+ * You should have received a copy of the GNU General Public License *
-+ * along with this program; if not, write to the *
-+ * Free Software Foundation, Inc., *
-+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA . *
-+ ***************************************************************************/
-+
-+#include "dbusnotification.h"
-+
-+#include <KDebug>
-+
-+
-+DBusNotification::DBusNotification(const QString &source, QObject *parent)
-+ : Notification(parent),
-+ m_source(source)
-+{
-+}
-+
-+DBusNotification::~DBusNotification()
-+{
-+ emit notificationDeleted(m_source);
-+}
-+
-+void DBusNotification::remove()
-+{
-+ emit unregisterNotification(m_source);
-+ deleteLater();
-+}
-+
-+void DBusNotification::triggerAction(const QString &actionId)
-+{
-+ emit actionTriggered(m_source, actionId);
-+}
-+
-diff --git a/plasma/generic/applets/notifications/protocols/notifications/dbusnotification.h b/plasma/generic/applets/notifications/protocols/notifications/dbusnotification.h
-new file mode 100644
-index 0000000..213cc71
---- /dev/null
-+++ b/plasma/generic/applets/notifications/protocols/notifications/dbusnotification.h
-@@ -0,0 +1,54 @@
-+/***************************************************************************
-+ * dbusnotification.h *
-+ * *
-+ * Copyright (C) 2008 Jason Stubbs <jasonbstubbs@gmail.com> *
-+ * *
-+ * This program is free software; you can redistribute it and/or modify *
-+ * it under the terms of the GNU General Public License as published by *
-+ * the Free Software Foundation; either version 2 of the License, or *
-+ * (at your option) any later version. *
-+ * *
-+ * This program is distributed in the hope that it will be useful, *
-+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
-+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
-+ * GNU General Public License for more details. *
-+ * *
-+ * You should have received a copy of the GNU General Public License *
-+ * along with this program; if not, write to the *
-+ * Free Software Foundation, Inc., *
-+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA . *
-+ ***************************************************************************/
-+
-+#ifndef DBUSNOTIFICATION_H
-+#define DBUSNOTIFICATION_H
-+
-+#include "../../core/notification.h"
-+
-+
-+
-+class DBusNotification : public Notification
-+{
-+ Q_OBJECT
-+
-+ friend class DBusNotificationProtocol;
-+
-+public:
-+ DBusNotification(const QString &source, QObject *parent = 0);
-+ ~DBusNotification();
-+
-+public slots:
-+ void remove();
-+ void triggerAction(const QString &actionId);
-+
-+signals:
-+ void notificationDeleted(const QString &source);
-+ void actionTriggered(const QString &source, const QString &actionId);
-+ void unregisterNotification(const QString &source);
-+
-+private:
-+ QString m_source;
-+};
-+
-+
-+
-+#endif
-diff --git a/plasma/generic/applets/notifications/protocols/notifications/dbusnotificationprotocol.cpp b/plasma/generic/applets/notifications/protocols/notifications/dbusnotificationprotocol.cpp
-new file mode 100644
-index 0000000..fd94048
---- /dev/null
-+++ b/plasma/generic/applets/notifications/protocols/notifications/dbusnotificationprotocol.cpp
-@@ -0,0 +1,192 @@
-+/***************************************************************************
-+ * fdoprotocol.cpp *
-+ * *
-+ * Copyright (C) 2008 Jason Stubbs <jasonbstubbs@gmail.com> *
-+ * *
-+ * This program is free software; you can redistribute it and/or modify *
-+ * it under the terms of the GNU General Public License as published by *
-+ * the Free Software Foundation; either version 2 of the License, or *
-+ * (at your option) any later version. *
-+ * *
-+ * This program is distributed in the hope that it will be useful, *
-+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
-+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
-+ * GNU General Public License for more details. *
-+ * *
-+ * You should have received a copy of the GNU General Public License *
-+ * along with this program; if not, write to the *
-+ * Free Software Foundation, Inc., *
-+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA . *
-+ ***************************************************************************/
-+
-+#include "dbusnotification.h"
-+#include "dbusnotificationprotocol.h"
-+
-+#include <KConfigGroup>
-+#include <KDebug>
-+#include <KIcon>
-+
-+#include <Plasma/DataEngineManager>
-+#include <Plasma/Service>
-+#include <Plasma/ServiceJob>
-+
-+
-+static const char engineName[] = "notifications";
-+
-+DBusNotificationProtocol::DBusNotificationProtocol(Manager *parent)
-+ : Protocol(parent),
-+ m_manager(parent),
-+ m_engine(0)
-+{
-+}
-+
-+
-+DBusNotificationProtocol::~DBusNotificationProtocol()
-+{
-+ if (m_engine) {
-+ Plasma::DataEngineManager::self()->unloadEngine(engineName);
-+ }
-+}
-+
-+
-+void DBusNotificationProtocol::init()
-+{
-+ m_engine = Plasma::DataEngineManager::self()->loadEngine(engineName);
-+
-+ if (!m_engine->isValid()) {
-+ m_engine = 0;
-+ Plasma::DataEngineManager::self()->unloadEngine(engineName);
-+ return;
-+ }
-+
-+ connect(m_engine, SIGNAL(sourceAdded(QString)),
-+ this, SLOT(prepareNotification(QString)));
-+ connect(m_engine, SIGNAL(sourceRemoved(QString)),
-+ this, SLOT(hideNotification(QString)));
-+}
-+
-+
-+void DBusNotificationProtocol::prepareNotification(const QString &source)
-+{
-+ if (m_engine) {
-+ m_engine->connectSource(source, this);
-+ }
-+}
-+
-+
-+void DBusNotificationProtocol::dataUpdated(const QString &source, const Plasma::DataEngine::Data &data)
-+{
-+ bool isNew = !m_notifications.contains(source);
-+
-+ if (isNew) {
-+ DBusNotification * notification = new DBusNotification(source, this);
-+ connect(notification, SIGNAL(unregisterNotification(QString)),
-+ this, SLOT(unregisterNotification(QString)));
-+ connect(notification, SIGNAL(notificationDeleted(QString)),
-+ this, SLOT(notificationDeleted(QString)));
-+ connect(notification, SIGNAL(actionTriggered(QString,QString)),
-+ this, SLOT(relayAction(QString,QString)));
-+ m_notifications[source] = notification;
-+ }
-+
-+ DBusNotification* notification = m_notifications[source];
-+ notification->setApplicationName(data.value("appName").toString());
-+ notification->setApplicationIcon(KIcon(data.value("appIcon").toString()));
-+ notification->setSummary(data.value("summary").toString());
-+ notification->setMessage(data.value("body").toString());
-+ notification->setTimeout(data.value("expireTimeout").toInt());
-+ notification->setUrgency(data.value("urgency").toInt());
-+
-+ if (data.contains("image")) {
-+ QImage image = qvariant_cast<QImage>(data.value("image"));
-+ notification->setImage(image);
-+ }
-+
-+ QStringList codedActions = data.value("actions").toStringList();
-+ if (codedActions.count() % 2 != 0) {
-+ kDebug() << "Invalid actions" << codedActions << "from" << notification->applicationName();
-+ codedActions.clear();
-+ }
-+
-+ QHash<QString, QString> actions;
-+ QStringList actionOrder;
-+
-+ while (!codedActions.isEmpty()) {
-+ QString actionId = codedActions.takeFirst();
-+ QString actionName = codedActions.takeFirst();
-+ actions.insert(actionId, actionName);
-+ actionOrder.append(actionId);
-+ }
-+
-+ notification->setActions(actions);
-+ notification->setActionOrder(actionOrder);
-+
-+ if (isNew) {
-+ emit notificationCreated(notification);
-+ } else {
-+ emit notification->changed(notification);
-+ }
-+}
-+
-+
-+void DBusNotificationProtocol::relayAction(const QString &source, const QString &actionId)
-+{
-+ if (!m_engine) {
-+ return;
-+ }
-+
-+ Plasma::Service *service = m_engine->serviceForSource(source);
-+ KConfigGroup op = service->operationDescription("invokeAction");
-+
-+ if (op.isValid()) {
-+ op.writeEntry("actionId", actionId);
-+ KJob *job = service->startOperationCall(op);
-+ connect(job, SIGNAL(finished(KJob*)), service, SLOT(deleteLater()));
-+ } else {
-+ delete service;
-+ kDebug() << "invalid operation";
-+ }
-+}
-+
-+void DBusNotificationProtocol::unregisterNotification(const QString &source)
-+{
-+ if (!m_engine) {
-+ return;
-+ }
-+
-+ Plasma::Service *service = m_engine->serviceForSource(source);
-+ KConfigGroup op = service->operationDescription("userClosed");
-+ KJob *job = service->startOperationCall(op);
-+ connect(job, SIGNAL(finished(KJob*)), service, SLOT(deleteLater()));
-+}
-+
-+void DBusNotificationProtocol::hideNotification(const QString &source)
-+{
-+ if (m_notifications.contains(source)) {
-+ m_notifications.value(source)->hide();
-+ }
-+}
-+
-+void DBusNotificationProtocol::removeNotification(const QString &source)
-+{
-+ if (m_notifications.contains(source)) {
-+ m_notifications.take(source)->destroy();
-+ }
-+}
-+
-+void DBusNotificationProtocol::notificationDeleted(const QString &source)
-+{
-+ if (!m_engine) {
-+ return;
-+ }
-+
-+ Plasma::Service *service = m_engine->serviceForSource(source);
-+ KConfigGroup op = service->operationDescription("userClosed");
-+ KJob *job = service->startOperationCall(op);
-+ connect(job, SIGNAL(finished(KJob*)), service, SLOT(deleteLater()));
-+
-+ m_notifications.remove(source);
-+}
-+
-+
-+#include "dbusnotificationprotocol.moc"
-diff --git a/plasma/generic/applets/notifications/protocols/notifications/dbusnotificationprotocol.h b/plasma/generic/applets/notifications/protocols/notifications/dbusnotificationprotocol.h
-new file mode 100644
-index 0000000..e52dfd9
---- /dev/null
-+++ b/plasma/generic/applets/notifications/protocols/notifications/dbusnotificationprotocol.h
-@@ -0,0 +1,62 @@
-+/***************************************************************************
-+ * dbusnotificationprotocol.h *
-+ * *
-+ * Copyright (C) 2008 Jason Stubbs <jasonbstubbs@gmail.com> *
-+ * Copyright (C) 2008 Dmitry Suzdalev <dimsuz@gmail.com> *
-+ * *
-+ * This program is free software; you can redistribute it and/or modify *
-+ * it under the terms of the GNU General Public License as published by *
-+ * the Free Software Foundation; either version 2 of the License, or *
-+ * (at your option) any later version. *
-+ * *
-+ * This program is distributed in the hope that it will be useful, *
-+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
-+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
-+ * GNU General Public License for more details. *
-+ * *
-+ * You should have received a copy of the GNU General Public License *
-+ * along with this program; if not, write to the *
-+ * Free Software Foundation, Inc., *
-+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA . *
-+ ***************************************************************************/
-+
-+#ifndef DBUSNOTIFICATIONPROTOCOL_H
-+#define DBUSNOTIFICATIONPROTOCOL_H
-+
-+#include "../../core/protocol.h"
-+#include "../../core/notificationsmanager.h"
-+
-+#include <QHash>
-+
-+#include <Plasma/DataEngine>
-+
-+
-+class DBusNotification;
-+
-+class DBusNotificationProtocol : public Protocol
-+{
-+ Q_OBJECT
-+
-+public:
-+ DBusNotificationProtocol(Manager *parent);
-+ ~DBusNotificationProtocol();
-+ void init();
-+
-+private slots:
-+ void prepareNotification(const QString &source);
-+ void dataUpdated(const QString &source, const Plasma::DataEngine::Data &data);
-+ void removeNotification(const QString &source);
-+ void notificationDeleted(const QString &source);
-+ void relayAction(const QString &source, const QString &actionId);
-+ void unregisterNotification(const QString&);
-+ void hideNotification(const QString &source);
-+
-+private:
-+ Manager *m_manager;
-+ Plasma::DataEngine *m_engine;
-+ QHash<QString, DBusNotification*> m_notifications;
-+};
-+
-+
-+
-+#endif
-diff --git a/plasma/generic/applets/notifications/ui/busywidget.cpp b/plasma/generic/applets/notifications/ui/busywidget.cpp
-new file mode 100644
-index 0000000..6bce0d7
---- /dev/null
-+++ b/plasma/generic/applets/notifications/ui/busywidget.cpp
-@@ -0,0 +1,352 @@
-+/***************************************************************************
-+ * Copyright (C) 2008, 2009 Rob Scheepmaker <r.scheepmaker@student.utwente.nl> *
-+ * Copyright (C) 2010 Marco Martin <notmart@gmail.com> *
-+ * *
-+ * This program is free software; you can redistribute it and/or modify *
-+ * it under the terms of the GNU General Public License as published by *
-+ * the Free Software Foundation; either version 2 of the License, or *
-+ * (at your option) any later version. *
-+ * *
-+ * This program is distributed in the hope that it will be useful, *
-+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
-+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
-+ * GNU General Public License for more details. *
-+ * *
-+ * You should have received a copy of the GNU General Public License *
-+ * along with this program; if not, write to the *
-+ * Free Software Foundation, Inc., *
-+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA . *
-+ ***************************************************************************/
-+
-+#include "busywidget.h"
-+#include <fixx11h.h>
-+
-+#include <QtGui/QPainter>
-+#include <QtGui/QTextOption>
-+#include <QtGui/QStyleOptionGraphicsItem>
-+#include <QtGui/QWidget> // QWIDGETSIZE_MAX
-+#include <QSequentialAnimationGroup>
-+
-+#include <plasma/extender.h>
-+#include <plasma/extenderitem.h>
-+#include <plasma/extendergroup.h>
-+#include <plasma/popupapplet.h>
-+#include <plasma/tooltipmanager.h>
-+#include <plasma/theme.h>
-+#include <Plasma/Animation>
-+#include <Plasma/Animator>
-+
-+#include <KIcon>
-+#include <KGlobalSettings>
-+
-+#include "../core/notificationsmanager.h"
-+#include "../core/job.h"
-+#include "../core/notification.h"
-+#include "../core/completedjobnotification.h"
-+
-+
-+
-+BusyWidget::BusyWidget(Plasma::PopupApplet *parent, const Manager *manager)
-+ : Plasma::BusyWidget(parent),
-+ m_icon("dialog-information"),
-+ m_state(Empty),
-+ m_svg(new Plasma::Svg(this)),
-+ m_systray(parent),
-+ m_manager(manager),
-+ m_total(0),
-+ m_suppressToolTips(false)
-+{
-+ setAcceptsHoverEvents(true);
-+ m_svg->setImagePath("icons/notification");
-+ m_svg->setContainsMultipleImages(true);
-+ setRunning(false);
-+
-+ m_fadeInAnimation = Plasma::Animator::create(Plasma::Animator::PixmapTransitionAnimation);
-+ m_fadeInAnimation->setTargetWidget(this);
-+ m_fadeInAnimation->setProperty("duration", 1000);
-+ m_fadeInAnimation->setProperty("targetPixmap", m_svg->pixmap("notification-active"));
-+
-+ m_fadeOutAnimation = Plasma::Animator::create(Plasma::Animator::PixmapTransitionAnimation);
-+ m_fadeOutAnimation->setTargetWidget(this);
-+ m_fadeOutAnimation->setProperty("duration", 1000);
-+ m_fadeOutAnimation->setProperty("startPixmap", m_svg->pixmap("notification-active"));
-+
-+
-+ m_fadeGroup = new QSequentialAnimationGroup(this);
-+ m_fadeGroup->addAnimation(m_fadeInAnimation);
-+ m_fadeGroup->addAnimation(m_fadeOutAnimation);
-+
-+ connect(manager, SIGNAL(notificationAdded(Notification*)),
-+ this, SLOT(updateTask()));
-+ connect(manager, SIGNAL(notificationRemoved(Notification*)),
-+ this, SLOT(updateTask()));
-+ connect(manager, SIGNAL(notificationChanged(Notification*)),
-+ this, SLOT(updateTask()));
-+ connect(manager, SIGNAL(notificationExpired(Notification*)),
-+ this, SLOT(updateTask()));
-+ connect(manager, SIGNAL(jobAdded(Job*)),
-+ this, SLOT(updateTask()));
-+ connect(manager, SIGNAL(jobRemoved(Job*)),
-+ this, SLOT(updateTask()));
-+ connect(manager, SIGNAL(jobStateChanged(Job*)),
-+ this, SLOT(updateTask()));
-+
-+ Plasma::Extender *extender = qobject_cast<Plasma::Extender *>(m_systray->graphicsWidget());
-+ if (extender) {
-+ connect(extender, SIGNAL(itemDetached(Plasma::ExtenderItem*)),
-+ this, SLOT(updateTask()));
-+ }
-+
-+ Plasma::ToolTipManager::self()->registerWidget(this);
-+ updateTask();
-+}
-+
-+void BusyWidget::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
-+{
-+ QRectF iconRect(0, 0, qMin(size().width(), size().height()), qMin(size().width(), size().height()));
-+ iconRect.moveCenter(boundingRect().center());
-+
-+ if (m_state == Running) {
-+ const int arcStart = 90*16;
-+ const int arcEnd = -(360*(qreal)m_manager->jobTotals()->percentage()/100)*16;
-+
-+ Plasma::BusyWidget::paint(painter, option, widget);
-+
-+ //kDebug() << arcStart << arcEnd;
-+
-+ QPixmap activePixmap(iconRect.size().toSize());
-+ activePixmap.fill(Qt::transparent);
-+ QPixmap inActivePixmap(iconRect.size().toSize());
-+ inActivePixmap.fill(Qt::transparent);
-+ QRect pieRect(QPoint(0, 0), activePixmap.size()*2);
-+ pieRect.moveCenter(activePixmap.rect().center());
-+
-+ QPainter p(&activePixmap);
-+ p.setPen(Qt::NoPen);
-+ p.setBrush(Qt::black);
-+ p.setCompositionMode(QPainter::CompositionMode_Source);
-+ p.drawPie(pieRect, arcStart, arcEnd);
-+ p.setCompositionMode(QPainter::CompositionMode_SourceIn);
-+ m_svg->paint(&p, QRectF(QPointF(0, 0), iconRect.size()), "notification-progress-active");
-+ p.end();
-+
-+ p.begin(&inActivePixmap);
-+ p.setPen(Qt::NoPen);
-+ p.setBrush(Qt::black);
-+ p.setCompositionMode(QPainter::CompositionMode_Source);
-+ p.drawPie(pieRect, arcStart, (360*16)+arcEnd);
-+ p.setCompositionMode(QPainter::CompositionMode_SourceIn);
-+ m_svg->paint(&p, QRectF(QPointF(0, 0), iconRect.size()), "notification-progress-inactive");
-+ p.end();
-+
-+ painter->drawPixmap(iconRect.topLeft().toPoint(), activePixmap);
-+ painter->drawPixmap(iconRect.topLeft().toPoint(), inActivePixmap);
-+
-+ Plasma::BusyWidget::paint(painter, option, widget);
-+
-+ } else if (m_state == Empty && m_manager->notifications().count() > 0) {
-+ m_svg->paint(painter, iconRect, "notification-inactive");
-+ } else if (m_state == Empty && m_manager->notifications().count() == 0) {
-+ m_svg->paint(painter, iconRect, "notification-disabled");
-+ } else {
-+ // m_state == Info
-+ m_svg->paint(painter, iconRect, "notification-empty");
-+ QFont font(KGlobalSettings::smallestReadableFont());
-+ painter->setFont(font);
-+ QRectF r = rect();
-+
-+ painter->setPen(Plasma::Theme::defaultTheme()->color(Plasma::Theme::TextColor));
-+
-+ const QFontMetrics fm(font);
-+ const QSize textSize = fm.boundingRect(label()).size();
-+ const bool textFits = textSize.width() <= r.width() && textSize.height() <= r.height();
-+ if (m_svg && m_svg->hasElement(expanderElement())) {
-+ QSizeF arrowSize(m_svg->elementSize(expanderElement()));
-+ QRectF arrowRect(r.center() - QPointF(arrowSize.width() / 2, arrowSize.height() + fm.xHeight() / 2), arrowSize);
-+ m_svg->paint(painter, arrowRect, expanderElement());
-+
-+ r.setTop(arrowRect.bottom());
-+
-+ if (textFits) {
-+ painter->drawText(r, Qt::AlignHCenter|Qt::AlignTop, label());
-+ }
-+ } else if (textFits) {
-+ painter->drawText(r, Qt::AlignCenter, label());
-+ }
-+ }
-+
-+ if (m_fadeInAnimation->state() == QAbstractAnimation::Running) {
-+ QPixmap pix = m_fadeInAnimation->property("currentPixmap").value<QPixmap>();
-+ painter->drawPixmap(iconRect, pix, pix.rect());
-+ } else if (m_fadeOutAnimation->state() == QAbstractAnimation::Running) {
-+ QPixmap pix = m_fadeOutAnimation->property("currentPixmap").value<QPixmap>();
-+ painter->drawPixmap(iconRect, pix, pix.rect());
-+ }
-+}
-+
-+void BusyWidget::resizeEvent(QGraphicsSceneResizeEvent *)
-+{
-+ //regenerate pixmaps
-+ m_svg->resize(contentsRect().size());
-+ m_fadeInAnimation->setProperty("targetPixmap", m_svg->pixmap("notification-active"));
-+ m_fadeOutAnimation->setProperty("startPixmap", m_svg->pixmap("notification-active"));
-+ m_svg->resize();
-+}
-+
-+void BusyWidget::setState(State state)
-+{
-+ if (m_state == state) {
-+ return;
-+ }
-+
-+ m_state = state;
-+ setRunning(m_state == Running);
-+ update();
-+}
-+
-+QString BusyWidget::expanderElement() const
-+{
-+ switch (m_systray->location()) {
-+ case Plasma::TopEdge:
-+ return "expander-top";
-+ case Plasma::RightEdge:
-+ return "expander-right";
-+ case Plasma::LeftEdge:
-+ return "expander-left";
-+ case Plasma::BottomEdge:
-+ default:
-+ return "expander-bottom";
-+ }
-+}
-+
-+void BusyWidget::getJobCounts(int &runningJobs, int &pausedJobs, int &completedJobs, int &jobSpeed)
-+{
-+ runningJobs = pausedJobs = completedJobs = jobSpeed = 0;
-+ foreach (const Job *job, m_manager->jobs()) {
-+ switch (job->state()) {
-+ case Job::Running:
-+ jobSpeed += job->numericSpeed();
-+ ++runningJobs;
-+ break;
-+ case Job::Suspended:
-+ ++pausedJobs;
-+ break;
-+ default:
-+ break;
-+ }
-+ }
-+
-+}
-+
-+void BusyWidget::updateTask()
-+{
-+ int runningJobs, pausedJobs, completedJobs, jobSpeed;
-+ getJobCounts(runningJobs, pausedJobs, completedJobs, jobSpeed);
-+
-+ int total = m_manager->jobs().count();
-+ int activeNotifications = 0;
-+ bool hasOldNotifications = false;
-+
-+ foreach (Notification *notification, m_manager->notifications()) {
-+ if (qobject_cast<CompletedJobNotification *>(notification)) {
-+ ++completedJobs;
-+ } else if (notification->isExpired()) {
-+ hasOldNotifications = true;
-+ } else {
-+ ++activeNotifications;
-+ }
-+ }
-+
-+ total += completedJobs + activeNotifications;
-+
-+ if (total + m_manager->notifications().count() < 0) {
-+ m_systray->hidePopup();
-+ }
-+
-+ if (total > m_total) {
-+ m_fadeGroup->start();
-+ }
-+
-+ m_total = total;
-+
-+ if (activeNotifications > 0) {
-+ m_systray->setStatus(Plasma::NeedsAttentionStatus);
-+ } else if (m_total > 0 || hasOldNotifications) {
-+ m_systray->setStatus(Plasma::ActiveStatus);
-+ } else {
-+ m_systray->setStatus(Plasma::PassiveStatus);
-+ }
-+
-+ if (!total) {
-+ setState(BusyWidget::Empty);
-+ setLabel(QString());
-+ } else if (runningJobs) {
-+ setState(BusyWidget::Running);
-+ setLabel(QString("%1").arg(QString::number(total)));
-+ } else {
-+ setState(BusyWidget::Info);
-+ setLabel(QString::number(total));
-+ }
-+
-+ if (Plasma::ToolTipManager::self()->isVisible(this)) {
-+ toolTipAboutToShow();
-+ }
-+}
-+
-+void BusyWidget::suppressToolTips(bool suppress)
-+{
-+ m_suppressToolTips = suppress;
-+}
-+
-+void BusyWidget::toolTipAboutToShow()
-+{
-+ if (m_suppressToolTips) {
-+ Plasma::ToolTipManager::self()->setContent(this, Plasma::ToolTipContent());
-+ return;
-+ }
-+
-+ int runningJobs, pausedJobs, completedJobs, jobSpeed;
-+ getJobCounts(runningJobs, pausedJobs, completedJobs, jobSpeed);
-+
-+ //make a nice plasma tooltip
-+ QString tooltipContent;
-+ if (runningJobs > 0) {
-+ tooltipContent += i18ncp("Number of jobs and the speed at which they are downloading",
-+ "%1 running job (%2/s)", "%1 running jobs (%2/s)", runningJobs,
-+ KGlobal::locale()->formatByteSize(jobSpeed));
-+ if (pausedJobs > 0 || completedJobs > 0 || !m_manager->notifications().isEmpty()) {
-+ tooltipContent += "<br>";
-+ }
-+ }
-+
-+ if (pausedJobs > 0) {
-+ tooltipContent += i18np("%1 suspended job", "%1 suspended jobs", pausedJobs);
-+ if (completedJobs > 0 || !m_manager->notifications().isEmpty()) {
-+ tooltipContent += "<br>";
-+ }
-+ }
-+
-+ if (completedJobs > 0) {
-+ tooltipContent += i18np("%1 completed job", "%1 completed jobs", completedJobs);
-+ if (!m_manager->notifications().isEmpty()) {
-+ tooltipContent += "<br>";
-+ }
-+ }
-+
-+ if (!m_manager->notifications().isEmpty()) {
-+ tooltipContent += i18np("%1 notification", "%1 notifications",
-+ m_manager->notifications().count());
-+ }
-+
-+ if (tooltipContent.isEmpty()) {
-+ tooltipContent = i18n("No active jobs or notifications");
-+ }
-+
-+ Plasma::ToolTipContent data(i18n("Notifications and jobs"),
-+ tooltipContent,
-+ KIcon("help-about"));
-+ Plasma::ToolTipManager::self()->setContent(this, data);
-+}
-+
-+
-+#include "busywidget.moc"
-diff --git a/plasma/generic/applets/notifications/ui/busywidget.h b/plasma/generic/applets/notifications/ui/busywidget.h
-new file mode 100644
-index 0000000..ae5f994
---- /dev/null
-+++ b/plasma/generic/applets/notifications/ui/busywidget.h
-@@ -0,0 +1,80 @@
-+/***************************************************************************
-+ * Copyright (C) 2008 Rob Scheepmaker <r.scheepmaker@student.utwente.nl> *
-+ * Copyright (C) 2010 Marco Martin <notmart@gmail.com> *
-+ * *
-+ * This program is free software; you can redistribute it and/or modify *
-+ * it under the terms of the GNU General Public License as published by *
-+ * the Free Software Foundation; either version 2 of the License, or *
-+ * (at your option) any later version. *
-+ * *
-+ * This program is distributed in the hope that it will be useful, *
-+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
-+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
-+ * GNU General Public License for more details. *
-+ * *
-+ * You should have received a copy of the GNU General Public License *
-+ * along with this program; if not, write to the *
-+ * Free Software Foundation, Inc., *
-+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA . *
-+ ***************************************************************************/
-+
-+#ifndef NOTIFICATIONBUSYWIDGET_H
-+#define NOTIFICATIONBUSYWIDGET_H
-+
-+#include <KIcon>
-+
-+#include <Plasma/BusyWidget>
-+
-+class QStyleOptionGraphicsItem;
-+class QSequentialAnimationGroup;
-+
-+namespace Plasma
-+{
-+ class Animation;
-+ class Extender;
-+ class PopupApplet;
-+ class Svg;
-+}
-+
-+
-+class Manager;
-+
-+class BusyWidget : public Plasma::BusyWidget
-+{
-+ Q_OBJECT
-+
-+public:
-+ enum State { Empty, Info, Running };
-+
-+ BusyWidget(Plasma::PopupApplet *parent, const Manager *manager);
-+ void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget = 0);
-+ void setState(State state);
-+ void suppressToolTips(bool suppress);
-+
-+public slots:
-+ void toolTipAboutToShow();
-+
-+protected:
-+ void resizeEvent(QGraphicsSceneResizeEvent *event);
-+
-+protected slots:
-+ void updateTask();
-+
-+private:
-+ QString expanderElement() const;
-+ void getJobCounts(int &runningJobs, int &pausedJobs, int &completedJobs, int &jobSpeed);
-+
-+ KIcon m_icon;
-+ State m_state;
-+ Plasma::Svg *m_svg;
-+ Plasma::PopupApplet *m_systray;
-+ const Manager *m_manager;
-+ Plasma::Animation *m_fadeInAnimation;
-+ Plasma::Animation *m_fadeOutAnimation;
-+ QSequentialAnimationGroup *m_fadeGroup;
-+ int m_total;
-+ bool m_suppressToolTips;
-+};
-+
-+
-+#endif
-diff --git a/plasma/generic/applets/notifications/ui/jobtotalswidget.cpp b/plasma/generic/applets/notifications/ui/jobtotalswidget.cpp
-new file mode 100644
-index 0000000..6552ed3
---- /dev/null
-+++ b/plasma/generic/applets/notifications/ui/jobtotalswidget.cpp
-@@ -0,0 +1,94 @@
-+/***************************************************************************
-+ * Copyright 2009 by Rob Scheepmaker <r.scheepmaker@student.utwente.nl> *
-+ * *
-+ * This program is free software; you can redistribute it and/or modify *
-+ * it under the terms of the GNU General Public License as published by *
-+ * the Free Software Foundation; either version 2 of the License, or *
-+ * (at your option) any later version. *
-+ * *
-+ * This program is distributed in the hope that it will be useful, *
-+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
-+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
-+ * GNU General Public License for more details. *
-+ * *
-+ * You should have received a copy of the GNU General Public License *
-+ * along with this program; if not, write to the *
-+ * Free Software Foundation, Inc., *
-+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA . *
-+ ***************************************************************************/
-+
-+#include "jobtotalswidget.h"
-+#include "../core/job.h"
-+
-+#include <Plasma/ExtenderItem>
-+#include <Plasma/Meter>
-+
-+static const int UPDATE_TIMER_INTERVAL = 200;
-+
-+
-+JobTotalsWidget::JobTotalsWidget(Job *job, QGraphicsWidget *parent)
-+ : Meter(parent),
-+ m_job(job),
-+ m_updateTimerId(0)
-+{
-+ m_extenderGroup = qobject_cast<Plasma::ExtenderGroup *>(parent),
-+
-+ setSvg("widgets/bar_meter_horizontal");
-+ setMeterType(Plasma::Meter::BarMeterHorizontal);
-+
-+ setMinimumWidth(350);
-+ setMinimumHeight(16);
-+ setMaximumHeight(16);
-+ setMaximum(100);
-+ setValue(0);
-+
-+ if (m_job) {
-+ connect(m_job, SIGNAL(changed(Job*)),
-+ this, SLOT(scheduleJobUpdate()));
-+
-+ updateJob();
-+ }
-+}
-+
-+JobTotalsWidget::~JobTotalsWidget()
-+{
-+}
-+
-+void JobTotalsWidget::scheduleJobUpdate()
-+{
-+ if (!m_updateTimerId) {
-+ m_updateTimerId = startTimer(UPDATE_TIMER_INTERVAL);
-+ }
-+}
-+
-+void JobTotalsWidget::timerEvent(QTimerEvent *event)
-+{
-+ if (event->timerId() == m_updateTimerId) {
-+ killTimer(m_updateTimerId);
-+ m_updateTimerId = 0;
-+ updateJob();
-+ } else {
-+ Meter::timerEvent(event);
-+ }
-+}
-+
-+void JobTotalsWidget::updateJob()
-+{
-+ setValue(m_job->percentage());
-+
-+ if (m_extenderGroup) {
-+ if (m_extenderGroup->items().count() > 1 || m_extenderGroup->isGroupCollapsed()) {
-+ m_extenderGroup->setTitle(m_job->message());
-+ } else {
-+ m_extenderGroup->setTitle(i18nc("Generic title for the job transfer popup", "Jobs"));
-+ }
-+ m_extenderGroup->setIcon(m_job->applicationIconName());
-+ } else {
-+ setLabelAlignment(0, Qt::AlignLeft|Qt::AlignVCenter);
-+ setLabel(0, m_job->message());
-+ }
-+}
-+
-+
-+#include "jobtotalswidget.moc"
-+
-diff --git a/plasma/generic/applets/notifications/ui/jobtotalswidget.h b/plasma/generic/applets/notifications/ui/jobtotalswidget.h
-new file mode 100644
-index 0000000..4f07c86
---- /dev/null
-+++ b/plasma/generic/applets/notifications/ui/jobtotalswidget.h
-@@ -0,0 +1,65 @@
-+/***************************************************************************
-+ * Copyright 2009 by Rob Scheepmaker <r.scheepmaker@student.utwente.nl *
-+ * *
-+ * This program is free software; you can redistribute it and/or modify *
-+ * it under the terms of the GNU General Public License as published by *
-+ * the Free Software Foundation; either version 2 of the License, or *
-+ * (at your option) any later version. *
-+ * *
-+ * This program is distributed in the hope that it will be useful, *
-+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
-+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
-+ * GNU General Public License for more details. *
-+ * *
-+ * You should have received a copy of the GNU General Public License *
-+ * along with this program; if not, write to the *
-+ * Free Software Foundation, Inc., *
-+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA . *
-+ ***************************************************************************/
-+
-+#ifndef JOBTOTALSWIDGET_H
-+#define JOBTOTALSWIDGET_H
-+
-+#include "../core/job.h"
-+
-+
-+#include <QGraphicsWidget>
-+
-+#include <plasma/widgets/meter.h>
-+#include <Plasma/Service>
-+#include <Plasma/ExtenderGroup>
-+#include <plasma/dataengine.h>
-+
-+namespace Plasma
-+{
-+ class ExtenderItem;
-+ class Meter;
-+} // namespace Plasma
-+
-+
-+class Job;
-+
-+class JobTotalsWidget : public Plasma::Meter
-+{
-+ Q_OBJECT
-+
-+ public:
-+ explicit JobTotalsWidget(Job *job, QGraphicsWidget *parent);
-+ ~JobTotalsWidget();
-+
-+ protected:
-+ void timerEvent(QTimerEvent *event);
-+
-+ private Q_SLOTS:
-+ void scheduleJobUpdate();
-+
-+ private:
-+ void updateJob();
-+
-+ Plasma::ExtenderGroup *m_extenderGroup;
-+ Job *m_job;
-+ int m_updateTimerId;
-+};
-+
-+
-+#endif
-diff --git a/plasma/generic/applets/notifications/ui/jobwidget.cpp b/plasma/generic/applets/notifications/ui/jobwidget.cpp
-new file mode 100644
-index 0000000..06096fd
---- /dev/null
-+++ b/plasma/generic/applets/notifications/ui/jobwidget.cpp
-@@ -0,0 +1,435 @@
-+/***************************************************************************
-+ * Copyright 2008 by Rob Scheepmaker <r.scheepmaker@student.utwente.nl> *
-+ * *
-+ * This program is free software; you can redistribute it and/or modify *
-+ * it under the terms of the GNU General Public License as published by *
-+ * the Free Software Foundation; either version 2 of the License, or *
-+ * (at your option) any later version. *
-+ * *
-+ * This program is distributed in the hope that it will be useful, *
-+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
-+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
-+ * GNU General Public License for more details. *
-+ * *
-+ * You should have received a copy of the GNU General Public License *
-+ * along with this program; if not, write to the *
-+ * Free Software Foundation, Inc., *
-+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA . *
-+ ***************************************************************************/
-+
-+#include "jobwidget.h"
-+#include "../core/job.h"
-+
-+
-+#include <QFont>
-+#include <QAction>
-+#include <QGraphicsGridLayout>
-+#include <QLabel>
-+
-+#include <KIconLoader>
-+
-+#include <Plasma/DataEngine>
-+#include <Plasma/Extender>
-+#include <Plasma/ExtenderItem>
-+#include <Plasma/Label>
-+#include <Plasma/Meter>
-+#include <Plasma/PopupApplet>
-+#include <Plasma/PushButton>
-+#include <Plasma/Service>
-+#include <Plasma/SignalPlotter>
-+#include <Plasma/IconWidget>
-+#include <Plasma/Theme>
-+#include <Plasma/ToolTipManager>
-+
-+static const int UPDATE_INTERVAL = 200;
-+
-+JobWidget::JobWidget(Job *job, Plasma::ExtenderItem *parent)
-+ : QGraphicsWidget(parent),
-+ m_extenderItem(parent),
-+ m_job(job),
-+ m_updateTimerId(0),
-+ m_extenderItemDestroyed(false)
-+{
-+ Q_ASSERT(m_extenderItem);
-+
-+ m_meter = new Plasma::Meter(this);
-+ m_meter->setSvg("widgets/bar_meter_horizontal");
-+ m_meter->setMeterType(Plasma::Meter::BarMeterHorizontal);
-+ m_meter->setMaximumHeight(16);
-+ m_meter->setMaximum(100);
-+ m_meter->setValue(0);
-+
-+ m_plotter = new Plasma::SignalPlotter(this);
-+ m_plotter->setUseAutoRange(true);
-+ m_plotter->setShowVerticalLines(false);
-+ m_plotter->setUnit(i18n("KiB/s"));
-+ m_plotter->addPlot(Plasma::Theme::defaultTheme()->color(Plasma::Theme::HighlightColor));
-+
-+ m_fromNameLabel = new Plasma::Label(this);
-+ m_fromLabel = new Plasma::Label(this);
-+ m_toNameLabel = new Plasma::Label(this);
-+ m_toLabel = new Plasma::Label(this);
-+ m_totalBytesLabel = new Plasma::Label(this);
-+ m_dirCountLabel = new Plasma::Label(this);
-+ m_fileCountLabel = new Plasma::Label(this);
-+ m_eta = new Plasma::Label(this);
-+ m_details = new Plasma::IconWidget(this);
-+ m_details->setSvg("widgets/action-overlays", "add-normal");
-+ m_details->setMaximumSize(KIconLoader::SizeSmallMedium, KIconLoader::SizeSmallMedium);
-+ m_details->setMinimumSize(KIconLoader::SizeSmallMedium, KIconLoader::SizeSmallMedium);
-+
-+ m_totalBytesLabel->setVisible(false);
-+ m_dirCountLabel->setVisible(false);
-+ m_fileCountLabel->setVisible(false);
-+ m_plotter->setVisible(false);
-+
-+ m_fromNameLabel->setAlignment(Qt::AlignRight);
-+ m_fromLabel->setAlignment(Qt::AlignLeft);
-+ m_toNameLabel->setAlignment(Qt::AlignRight);
-+ m_toLabel->setAlignment(Qt::AlignLeft);
-+ m_totalBytesLabel->setAlignment(Qt::AlignRight);
-+ m_dirCountLabel->setAlignment(Qt::AlignRight);
-+ m_fileCountLabel->setAlignment(Qt::AlignRight);
-+ m_eta->setAlignment(Qt::AlignRight);
-+
-+ m_fromLabel->nativeWidget()->setWordWrap(false);
-+ m_toLabel->nativeWidget()->setWordWrap(false);
-+ m_dirCountLabel->nativeWidget()->setWordWrap(false);
-+ m_fileCountLabel->nativeWidget()->setWordWrap(false);
-+ m_totalBytesLabel->nativeWidget()->setWordWrap(false);
-+ m_eta->nativeWidget()->setWordWrap(false);
-+
-+ m_layout = new QGraphicsGridLayout(this);
-+ m_layout->addItem(m_fromNameLabel, 0, 0);
-+ m_layout->addItem(m_fromLabel, 0, 1);
-+ m_layout->addItem(m_toNameLabel, 1, 0);
-+ m_layout->addItem(m_toLabel, 1, 1);
-+ m_layout->addItem(m_eta, 2, 1);
-+ m_layout->addItem(m_details, 3, 0, Qt::AlignVCenter|Qt::AlignRight);
-+ m_layout->addItem(m_meter, 3, 1, Qt::AlignCenter);
-+
-+ setMinimumWidth(350);
-+
-+ if (m_job.data()) {
-+ m_details->setToolTip(i18n("More"));
-+ m_details->setSvg("widgets/action-overlays", "add-normal");
-+
-+ connect(m_job.data(), SIGNAL(stateChanged(Job*)), this, SLOT(updateJobState()));
-+ connect(m_job.data(), SIGNAL(destroyed(Job*)), this, SLOT(destroyExtenderItem()));
-+ connect(m_details, SIGNAL(clicked()), this, SLOT(detailsClicked()));
-+
-+ //the suspend action
-+ QAction *suspendAction = new QAction(m_extenderItem);
-+ suspendAction->setIcon(KIcon("media-playback-pause"));
-+ suspendAction->setEnabled(true);
-+ suspendAction->setVisible(false);
-+ suspendAction->setToolTip(i18n("Pause job"));
-+ m_extenderItem->addAction("suspend", suspendAction);
-+ connect(suspendAction, SIGNAL(triggered()), m_job.data(), SLOT(suspend()));
-+
-+ //the resume action
-+ QAction *resumeAction = new QAction(m_extenderItem);
-+ resumeAction->setIcon(KIcon("media-playback-start"));
-+ resumeAction->setEnabled(true);
-+ resumeAction->setVisible(false);
-+ resumeAction->setToolTip(i18n("Resume job"));
-+ m_extenderItem->addAction("resume", resumeAction);
-+ connect(resumeAction, SIGNAL(triggered()), m_job.data(), SLOT(resume()));
-+
-+ //the friendly stop action
-+ QAction *stopAction = new QAction(m_extenderItem);
-+ stopAction->setIcon(KIcon("media-playback-stop"));
-+ stopAction->setEnabled(true);
-+ stopAction->setVisible(true);
-+ stopAction->setToolTip(i18n("Cancel job"));
-+ m_extenderItem->addAction("stop", stopAction);
-+ connect(stopAction, SIGNAL(triggered()), m_job.data(), SLOT(stop()));
-+
-+ updateJob();
-+ updateJobState(); // make sure to set the title
-+ } else {
-+ m_extenderItem->showCloseButton();
-+
-+ labelName0 = m_extenderItem->config().readEntry("labelName0", "");
-+ label0= m_extenderItem->config().readEntry("label0", "");
-+ labelName1 = m_extenderItem->config().readEntry("labelName1", "");
-+ label1 = m_extenderItem->config().readEntry("label1", "");
-+
-+ updateLabels();
-+ }
-+}
-+
-+JobWidget::~JobWidget()
-+{
-+}
-+
-+void JobWidget::destroyExtenderItem()
-+{
-+ m_extenderItem->destroy();
-+ m_extenderItemDestroyed = true;
-+}
-+
-+void JobWidget::scheduleUpdateJob()
-+{
-+ if (m_extenderItemDestroyed) {
-+ return;
-+ }
-+
-+ if (!m_updateTimerId) {
-+ m_updateTimerId = startTimer(UPDATE_INTERVAL);
-+ }
-+}
-+
-+void JobWidget::updateJobState()
-+{
-+ if (m_extenderItemDestroyed && m_job.data()) {
-+ return;
-+ }
-+
-+ //show the current status in the title.
-+ if (!m_job.data()->error().isEmpty()) {
-+ m_extenderItem->setTitle(m_job.data()->error());
-+ } else if (m_job.data()->state() == Job::Running) {
-+ m_extenderItem->setTitle(m_job.data()->message());
-+ if (m_job.data()->eta()) {
-+ m_eta->setText(i18n("%1 (%2 remaining)", m_job.data()->speed(),
-+ KGlobal::locale()->prettyFormatDuration(m_job.data()->eta())));
-+ } else {
-+ m_eta->setText(QString());
-+ }
-+ } else if (m_job.data()->state() == Job::Suspended) {
-+ m_extenderItem->setTitle(
-+ i18nc("%1 is the name of the job, can be things like Copying, deleting, moving",
-+ "%1 [Paused]", m_job.data()->message()));
-+ m_eta->setText(i18n("Paused"));
-+ } else {
-+ m_extenderItem->setTitle(
-+ i18nc("%1 is the name of the job, can be things like Copying, deleting, moving",
-+ "%1 [Finished]", m_job.data()->message()));
-+ m_extenderItem->showCloseButton();
-+ }
-+}
-+
-+void JobWidget::updateJob()
-+{
-+ if (m_extenderItemDestroyed || !m_job.data()) {
-+ return;
-+ }
-+
-+ m_meter->setValue(m_job.data()->percentage());
-+
-+ //Update the ETA and job speed (only if running)
-+ if (m_job.data()->state() == Job::Running) {
-+ if (m_job.data()->eta()) {
-+ m_eta->setText(i18n("%1 (%2 remaining)", m_job.data()->speed(),
-+ KGlobal::locale()->prettyFormatDuration(m_job.data()->eta())));
-+ } else {
-+ m_eta->setText(QString());
-+ }
-+ }
-+
-+ if (m_job.data()->labels().count() > 0) {
-+ labelName0 = m_job.data()->labels().value(0).first;
-+ label0 = m_job.data()->labels().value(0).second;
-+ }
-+ if (m_job.data()->labels().count() > 1) {
-+ labelName1 = m_job.data()->labels().value(1).first;
-+ label1 = m_job.data()->labels().value(1).second;
-+ }
-+
-+ //TODO: can we write this at some later point?
-+ KConfigGroup cg = m_extenderItem->config();
-+ cg.writeEntry("labelName0", labelName0);
-+ cg.writeEntry("label0", label0);
-+ cg.writeEntry("labelName1", labelName1);
-+ cg.writeEntry("label1", label1);
-+
-+ updateLabels();
-+
-+ //set the correct actions to visible.
-+ if (m_extenderItem->action("suspend")) {
-+ m_extenderItem->action("suspend")->setVisible(m_job.data()->isSuspendable() &&
-+ m_job.data()->state() == Job::Running);
-+ }
-+
-+ if (m_extenderItem->action("resume")) {
-+ m_extenderItem->action("resume")->setVisible(m_job.data()->isSuspendable() &&
-+ m_job.data()->state() == Job::Suspended);
-+ }
-+
-+ if (m_extenderItem->action("stop")) {
-+ m_extenderItem->action("stop")->setVisible(m_job.data()->isKillable() &&
-+ m_job.data()->state() != Job::Stopped);
-+ }
-+
-+ QMap<QString, qlonglong> processed = m_job.data()->processedAmounts();
-+ QMap<QString, qlonglong> totals = m_job.data()->totalAmounts();
-+
-+ qlonglong dirs = totals.value("dirs");
-+ if (dirs > 1) {
-+ m_dirCountLabel->setText(i18np("%2 / 1 folder", "%2 / %1 folders", dirs, processed["dirs"]));
-+ m_dirCountLabel->setMaximumHeight(INT_MAX);
-+ } else {
-+ m_dirCountLabel->setMaximumHeight(0);
-+ }
-+
-+ qlonglong files = totals.value("files");
-+ if (files > 1) {
-+ m_fileCountLabel->setText(i18np("%2 / 1 file", "%2 / %1 files", files, processed["files"]));
-+ m_fileCountLabel->setMaximumHeight(INT_MAX);
-+ } else {
-+ m_fileCountLabel->setMaximumHeight(0);
-+ }
-+
-+ QList<double> sample;
-+ sample << m_job.data()->numericSpeed()/1000;
-+ m_plotter->addSample(sample);
-+
-+ qlonglong total = totals["bytes"];
-+ if (total > 0) {
-+ QString processedString = KGlobal::locale()->formatByteSize(processed["bytes"]);
-+ QString totalsString = KGlobal::locale()->formatByteSize(total);
-+ m_totalBytesLabel->setText(QString("%1 / %2").arg(processedString, totalsString));
-+ } else {
-+ m_details->hide();
-+ m_totalBytesLabel->hide();
-+ }
-+
-+ if (m_totalBytesLabel->text().isEmpty() &&
-+ m_dirCountLabel->text().isEmpty() &&
-+ m_fileCountLabel->text().isEmpty()) {
-+ m_details->hide();
-+ } else {
-+ m_details->show();
-+ }
-+
-+ m_extenderItem->setIcon(m_job.data()->applicationIconName());
-+}
-+
-+void JobWidget::showEvent(QShowEvent *)
-+{
-+ if (!m_job.data()) {
-+ return;
-+ }
-+
-+ Plasma::PopupApplet *applet = qobject_cast<Plasma::PopupApplet *>(m_extenderItem->extender()->applet());
-+ if (applet && applet->isPopupShowing()) {
-+ updateJob();
-+ disconnect(m_job.data(), SIGNAL(changed(Job*)), this, SLOT(scheduleUpdateJob()));
-+ connect(m_job.data(), SIGNAL(changed(Job*)), this, SLOT(scheduleUpdateJob()));
-+ return;
-+ }
-+}
-+
-+void JobWidget::hideEvent(QHideEvent *)
-+{
-+ if (!m_job.data()) {
-+ return;
-+ }
-+
-+ disconnect(m_job.data(), SIGNAL(changed(Job*)), this, SLOT(scheduleUpdateJob()));
-+}
-+
-+void JobWidget::poppedUp(bool shown)
-+{
-+ if (!m_job.data()) {
-+ return;
-+ }
-+
-+ disconnect(m_job.data(), SIGNAL(changed(Job*)), this, SLOT(scheduleUpdateJob()));
-+
-+ if (shown && isVisible()) {
-+ updateJob();
-+ connect(m_job.data(), SIGNAL(changed(Job*)), this, SLOT(scheduleUpdateJob()));
-+ return;
-+ }
-+}
-+
-+Job *JobWidget::job() const
-+{
-+ return m_job.data();
-+}
-+
-+void JobWidget::resizeEvent(QGraphicsSceneResizeEvent *event)
-+{
-+ Q_UNUSED(event)
-+ updateLabels();
-+}
-+
-+void JobWidget::timerEvent(QTimerEvent *event)
-+{
-+ if (event->timerId() == m_updateTimerId) {
-+ killTimer(m_updateTimerId);
-+ m_updateTimerId = 0;
-+ updateJob();
-+ }
-+}
-+
-+void JobWidget::updateLabels()
-+{
-+ QFontMetricsF fm = m_fromLabel->nativeWidget()->fontMetrics();
-+ if (!labelName0.isEmpty()) {
-+ m_fromNameLabel->setText(QString("%1: ").arg(labelName0));
-+ }
-+ if (label0.startsWith(QLatin1String("file://"))) {
-+ label0 = KUrl(label0).toLocalFile();
-+ }
-+
-+ const QString shortLabel0(fm.elidedText(label0, Qt::ElideMiddle, m_fromLabel->size().width()));
-+ m_fromLabel->setText(shortLabel0);
-+
-+
-+ Plasma::ToolTipContent data;
-+
-+ if (label0.length() > shortLabel0.length()) {
-+ data.setSubText(label0);
-+ Plasma::ToolTipManager::self()->setContent(m_fromLabel, data);
-+ }
-+
-+ if (!labelName1.isEmpty()) {
-+ m_toNameLabel->setText(QString("%1: ").arg(labelName1));
-+ }
-+ if (label1.startsWith(QLatin1String("file://"))) {
-+ label1 = KUrl(label1).toLocalFile();
-+ }
-+
-+ const QString shortLabel1(fm.elidedText(label1, Qt::ElideMiddle, m_toLabel->size().width()));
-+ m_toLabel->setText(shortLabel1);
-+
-+ if (label1.length() > shortLabel1.length()) {
-+ data.setSubText(label1);
-+ Plasma::ToolTipManager::self()->setContent(m_toLabel, data);
-+ }
-+}
-+
-+void JobWidget::detailsClicked()
-+{
-+ if (!m_totalBytesLabel->isVisible()) {
-+ m_details->setToolTip(i18n("Less"));
-+ m_details->setSvg("widgets/action-overlays", "remove-normal");
-+ m_totalBytesLabel->setVisible(true);
-+ m_dirCountLabel->setVisible(true);
-+ m_fileCountLabel->setVisible(true);
-+ m_plotter->setVisible(true);
-+ m_layout->addItem(m_totalBytesLabel, 4, 1);
-+ m_layout->addItem(m_fileCountLabel, 5, 1);
-+ m_layout->addItem(m_dirCountLabel, 6, 1);
-+ m_layout->addItem(m_plotter, 7, 1);
-+ m_extenderItem->setCollapsed(m_extenderItem->isCollapsed());
-+ } else {
-+ m_details->setToolTip(i18n("More"));
-+ m_details->setSvg("widgets/action-overlays", "add-normal");
-+ m_totalBytesLabel->setVisible(false);
-+ m_dirCountLabel->setVisible(false);
-+ m_fileCountLabel->setVisible(false);
-+ m_plotter->setVisible(false);
-+ for (int i = 0; i < 4; i++) {
-+ m_layout->removeAt(m_layout->count() - 1);
-+ }
-+ m_layout->updateGeometry();
-+ m_extenderItem->setCollapsed(m_extenderItem->isCollapsed());
-+ }
-+}
-+
-+#include "jobwidget.moc"
-+
-diff --git a/plasma/generic/applets/notifications/ui/jobwidget.h b/plasma/generic/applets/notifications/ui/jobwidget.h
-new file mode 100644
-index 0000000..b949bac
---- /dev/null
-+++ b/plasma/generic/applets/notifications/ui/jobwidget.h
-@@ -0,0 +1,102 @@
-+/***************************************************************************
-+ * Copyright 2008 by Rob Scheepmaker <r.scheepmaker@student.utwente.nl *
-+ * *
-+ * This program is free software; you can redistribute it and/or modify *
-+ * it under the terms of the GNU General Public License as published by *
-+ * the Free Software Foundation; either version 2 of the License, or *
-+ * (at your option) any later version. *
-+ * *
-+ * This program is distributed in the hope that it will be useful, *
-+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
-+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
-+ * GNU General Public License for more details. *
-+ * *
-+ * You should have received a copy of the GNU General Public License *
-+ * along with this program; if not, write to the *
-+ * Free Software Foundation, Inc., *
-+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA . *
-+ ***************************************************************************/
-+
-+#ifndef JOBWIDGET_H
-+#define JOBWIDGET_H
-+
-+#include "../core/job.h"
-+
-+
-+#include <QGraphicsWidget>
-+#include <QGraphicsGridLayout>
-+
-+#include <Plasma/Service>
-+#include <Plasma/ExtenderItem>
-+#include <plasma/dataengine.h>
-+#include <Plasma/PushButton>
-+
-+namespace Plasma
-+{
-+ class ExtenderItem;
-+ class PushButton;
-+ class Label;
-+ class Meter;
-+ class IconWidget;
-+ class SignalPlotter;
-+} // namespace Plasma
-+
-+class Job;
-+
-+
-+class JobWidget : public QGraphicsWidget
-+{
-+ Q_OBJECT
-+
-+ public:
-+ explicit JobWidget(Job *job, Plasma::ExtenderItem *parent);
-+ ~JobWidget();
-+
-+ void poppedUp(bool shown);
-+
-+ Job *job() const;
-+
-+ protected:
-+ void resizeEvent(QGraphicsSceneResizeEvent *event);
-+ void timerEvent(QTimerEvent *event);
-+ void showEvent(QShowEvent *event);
-+ void hideEvent(QHideEvent *event);
-+
-+ private Q_SLOTS:
-+ void detailsClicked();
-+ void destroyExtenderItem();
-+ void scheduleUpdateJob();
-+ void updateJobState();
-+
-+ private:
-+ void updateLabels();
-+ void updateJob();
-+
-+ Plasma::ExtenderItem *m_extenderItem;
-+ QWeakPointer<Job>m_job;
-+
-+ Plasma::Meter *m_meter;
-+ Plasma::Label *m_fromNameLabel;
-+ Plasma::Label *m_fromLabel;
-+ Plasma::Label *m_toNameLabel;
-+ Plasma::Label *m_toLabel;
-+ Plasma::Label *m_totalBytesLabel;
-+ Plasma::Label *m_dirCountLabel;
-+ Plasma::Label *m_fileCountLabel;
-+ Plasma::Label *m_eta;
-+ Plasma::IconWidget *m_details;
-+ Plasma::SignalPlotter *m_plotter;
-+
-+ QGraphicsGridLayout *m_layout;
-+
-+ QString labelName0;
-+ QString labelName1;
-+ QString label0;
-+ QString label1;
-+
-+ int m_updateTimerId;
-+
-+ bool m_extenderItemDestroyed;
-+};
-+
-+#endif
-diff --git a/plasma/generic/applets/notifications/ui/notificationgroup.cpp b/plasma/generic/applets/notifications/ui/notificationgroup.cpp
-new file mode 100644
-index 0000000..f27e2c3
---- /dev/null
-+++ b/plasma/generic/applets/notifications/ui/notificationgroup.cpp
-@@ -0,0 +1,239 @@
-+/***************************************************************************
-+ * notificationgroup.cpp *
-+ * Copyright (C) 2010 Marco Martin <notmart@gmail.com> *
-+ * *
-+ * This program is free software; you can redistribute it and/or modify *
-+ * it under the terms of the GNU General Public License as published by *
-+ * the Free Software Foundation; either version 2 of the License, or *
-+ * (at your option) any later version. *
-+ * *
-+ * This program is distributed in the hope that it will be useful, *
-+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
-+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
-+ * GNU General Public License for more details. *
-+ * *
-+ * You should have received a copy of the GNU General Public License *
-+ * along with this program; if not, write to the *
-+ * Free Software Foundation, Inc., *
-+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA . *
-+ ***************************************************************************/
-+
-+#include "notificationgroup.h"
-+#include "../core/notification.h"
-+#include "notificationwidget.h"
-+
-+#include <QGraphicsLinearLayout>
-+#include <QWidget>
-+
-+#include <KDebug>
-+#include <KIcon>
-+#include <KLocale>
-+
-+#include <Plasma/TabBar>
-+#include <Plasma/ExtenderItem>
-+
-+
-+NotificationGroup::NotificationGroup(Plasma::Extender *parent, uint groupId)
-+ : Plasma::ExtenderGroup(parent, groupId)
-+{
-+ setTransient(true);
-+ config().writeEntry("type", "notification");
-+ setName("notifications");
-+ setTitle(i18n("Notifications"));
-+ setIcon("dialog-information");
-+ showCloseButton();
-+
-+
-+ m_notificationBar = new Plasma::TabBar(this);
-+ m_notificationBar->nativeWidget()->setMaximumWidth(400);
-+ m_notificationBar->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
-+ m_notificationBar->addTab(KIcon("dialog-information"), i18nc("Show all notifications", "All"));
-+ connect(m_notificationBar, SIGNAL(currentChanged(int)), this, SLOT(tabSwitched(int)));
-+
-+
-+ QGraphicsWidget *widget = new QGraphicsWidget(this);
-+ QGraphicsLinearLayout *tabsLayout = new QGraphicsLinearLayout(Qt::Horizontal, widget);
-+ widget->setContentsMargins(0, 4, 0, 0);
-+ tabsLayout->setContentsMargins(0, 0, 0, 0);
-+ tabsLayout->addStretch();
-+ tabsLayout->addItem(m_notificationBar);
-+ tabsLayout->addStretch();
-+
-+ setWidget(widget);
-+ setCollapsed(true);
-+ setAutoCollapse(false);
-+}
-+
-+NotificationGroup::~NotificationGroup()
-+{
-+ m_extenderItemsForNotification.clear();
-+ m_notificationForExtenderItems.clear();
-+ qDeleteAll(m_notifications);
-+}
-+
-+
-+void NotificationGroup::addNotification(Notification *notification)
-+{
-+ connect(notification, SIGNAL(notificationDestroyed(Notification*)), this, SLOT(removeNotification(Notification*)));
-+
-+ NotificationWidget *notificationWidget = new NotificationWidget(notification, this);
-+ notificationWidget->setTitleBarVisible(false);
-+ Plasma::ExtenderItem *extenderItem = new ExtenderItem(extender());
-+ extenderItem->setGroup(this, QPointF(0,0));
-+ extenderItem->setTransient(true);
-+ extenderItem->config().writeEntry("type", "notification");
-+ extenderItem->setWidget(notificationWidget);
-+ extenderItem->setIcon(QIcon());
-+ extenderItem->showCloseButton();
-+ connect(extenderItem, SIGNAL(destroyed(Plasma::ExtenderItem*)), this, SLOT(extenderItemDestroyed(Plasma::ExtenderItem*)));
-+ connect(notificationWidget, SIGNAL(destroyed()), extenderItem, SLOT(deleteLater()));
-+
-+ if (!notification->summary().isEmpty()) {
-+ extenderItem->setTitle(notification->summary());
-+ } else {
-+ extenderItem->setTitle(i18n("Notification from %1", notification->applicationName()));
-+ }
-+
-+ m_extenderItemsForNotification[notification] = extenderItem;
-+ m_notificationForExtenderItems[extenderItem] = notification;
-+ m_notifications.append(notification);
-+ m_notificationsForApp[notification->applicationName()].insert(notification);
-+ m_appForNotification[notification] = notification->applicationName();
-+
-+ if (!m_currentFilter.isNull() && m_currentFilter != notification->applicationName()) {
-+ extenderItem->setMaximumHeight(0);
-+ extenderItem->setVisible(false);
-+ }
-+
-+ //adjust tabbar
-+ bool found = false;
-+ for (int i = 0; i < m_notificationBar->count(); ++i) {
-+ if (m_notificationBar->tabText(i) == notification->applicationName()) {
-+ found = true;
-+ break;
-+ }
-+ }
-+
-+ if (!found) {
-+ m_notificationBar->addTab(notification->applicationIcon(), notification->applicationName());
-+ if (m_notificationBar->count() > 2) {
-+ setCollapsed(false);
-+ setAutoCollapse(true);
-+ }
-+ }
-+
-+ if (items().count() == 1) {
-+ //ensure the notifications group is the last item
-+ Plasma::ExtenderGroup *jobGroup = extender()->group("jobGroup");
-+ if (jobGroup && jobGroup->isVisible()) {
-+ if (extender()->appearance() == Plasma::Extender::TopDownStacked) {
-+ setExtender(extender(), QPointF(0,0));
-+ } else {
-+ setExtender(extender(), jobGroup->geometry().bottomLeft());
-+ }
-+ }
-+ }
-+
-+ notificationWidget->layout()->activate();
-+}
-+
-+void NotificationGroup::extenderItemDestroyed(Plasma::ExtenderItem *object)
-+{
-+ if (m_extenderItemsForNotification.isEmpty()) {
-+ // either we aren't tracking this notification or else we're being deleted
-+ return;
-+ }
-+
-+ Notification *n = m_notificationForExtenderItems.value(object);
-+
-+ if (n) {
-+ m_notificationForExtenderItems.remove(object);
-+ removeNotification(n);
-+ n->deleteLater();
-+ }
-+}
-+
-+void NotificationGroup::removeNotification(Notification *notification)
-+{
-+ if (m_extenderItemsForNotification.isEmpty()) {
-+ // either we aren't tracking this notification or else we're being deleted
-+ return;
-+ }
-+
-+ Plasma::ExtenderItem *item = m_extenderItemsForNotification.value(notification);
-+ if (item) {
-+ m_notificationForExtenderItems.remove(item);
-+ }
-+
-+ m_extenderItemsForNotification.remove(notification);
-+ m_notifications.removeAll(notification);
-+ QString applicationName = m_appForNotification.value(notification);
-+
-+ if (applicationName.isEmpty()) {
-+ return;
-+ }
-+
-+ m_appForNotification.remove(notification);
-+
-+ if (m_notificationsForApp.contains(applicationName)) {
-+ m_notificationsForApp[applicationName].remove(notification);
-+ if (m_notificationsForApp[applicationName].isEmpty()) {
-+ m_notificationsForApp.remove(applicationName);
-+ }
-+ }
-+
-+ //clear tabbar
-+ for (int i = 1; i < m_notificationBar->count(); ++i) {
-+ if (!m_notificationsForApp.contains(m_notificationBar->tabText(i))) {
-+ if (i == m_notificationBar->currentIndex()) {
-+ m_notificationBar->setCurrentIndex(0);
-+ }
-+ m_notificationBar->removeTab(i);
-+ //2 tabs means just "all" and a single application, no need to display it
-+ if (m_notificationBar->count() <= 2) {
-+ setCollapsed(true);
-+ setAutoCollapse(false);
-+ }
-+ }
-+ }
-+
-+ if (m_notifications.count() == 0) {
-+ emit scrollerEmpty();
-+ return;
-+ }
-+}
-+
-+
-+void NotificationGroup::filterNotificationsByOwner(const QString &owner)
-+{
-+ foreach (Notification *notification, m_notifications) {
-+ Plasma::ExtenderItem *item = m_extenderItemsForNotification.value(notification);
-+
-+ if (!item || item->group() != this) {
-+ continue;
-+ }
-+
-+ if (owner.isNull() || notification->applicationName() == owner) {
-+ item->setMaximumHeight(QWIDGETSIZE_MAX);
-+ item->setVisible(true);
-+ } else {
-+ item->setMaximumHeight(0);
-+ item->setVisible(false);
-+ }
-+ }
-+
-+ m_currentFilter = owner;
-+}
-+
-+void NotificationGroup::tabSwitched(int index)
-+{
-+ if (index > 0) {
-+ filterNotificationsByOwner(m_notificationBar->tabText(index));
-+ } else {
-+ filterNotificationsByOwner(QString());
-+ }
-+}
-+
-+
-+#include "notificationgroup.moc"
-+
-diff --git a/plasma/generic/applets/notifications/ui/notificationgroup.h b/plasma/generic/applets/notifications/ui/notificationgroup.h
-new file mode 100644
-index 0000000..ffa2112
---- /dev/null
-+++ b/plasma/generic/applets/notifications/ui/notificationgroup.h
-@@ -0,0 +1,85 @@
-+/***************************************************************************
-+ * notificationgroup.h *
-+ * Copyright (C) 2010 Marco Martin <notmart@gmail.com> *
-+ * *
-+ * This program is free software; you can redistribute it and/or modify *
-+ * it under the terms of the GNU General Public License as published by *
-+ * the Free Software Foundation; either version 2 of the License, or *
-+ * (at your option) any later version. *
-+ * *
-+ * This program is distributed in the hope that it will be useful, *
-+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
-+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
-+ * GNU General Public License for more details. *
-+ * *
-+ * You should have received a copy of the GNU General Public License *
-+ * along with this program; if not, write to the *
-+ * Free Software Foundation, Inc., *
-+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA . *
-+ ***************************************************************************/
-+
-+#ifndef NOTIFICATIONGROUP_H
-+#define NOTIFICATIONGROUP_H
-+
-+
-+#include <Plasma/Extender>
-+#include <Plasma/ExtenderGroup>
-+
-+
-+#include <Plasma/Plasma>
-+
-+class QGraphicsLinearLayout;
-+
-+namespace Plasma
-+{
-+ class ExtenderItem;
-+ class TabBar;
-+ class Extender;
-+}
-+
-+class NotificationWidget;
-+class Notification;
-+
-+class NotificationGroup : public Plasma::ExtenderGroup
-+{
-+ Q_OBJECT
-+
-+public:
-+ NotificationGroup(Plasma::Extender *parent, uint groupId = 0);
-+ ~NotificationGroup();
-+
-+ void addNotification(Notification *notification);
-+
-+ void filterNotificationsByOwner(const QString &owner);
-+
-+
-+public Q_SLOTS:
-+ void removeNotification(Notification *notification);
-+
-+protected Q_SLOTS:
-+ void tabSwitched(int index);
-+ void extenderItemDestroyed(Plasma::ExtenderItem *object);
-+
-+Q_SIGNALS:
-+ void scrollerEmpty();
-+
-+private:
-+ Plasma::TabBar *m_notificationBar;
-+
-+ //housekeeping data structures
-+ QList<Notification *>m_notifications;
-+
-+ //Those two are kept on both ways since we are not sure the thing
-+ //contained in the hash is still valid so we couldn't obtain the
-+ //info to remove the proper key, unless both ways are stored
-+ QHash<QString, QSet<Notification *> > m_notificationsForApp;
-+ QHash<Notification *, QString> m_appForNotification;
-+
-+ QHash<Notification *, Plasma::ExtenderItem *>m_extenderItemsForNotification;
-+ QHash<Plasma::ExtenderItem *, Notification *>m_notificationForExtenderItems;
-+
-+ QString m_currentFilter;
-+};
-+
-+
-+#endif
-diff --git a/plasma/generic/applets/notifications/ui/notifications.cpp b/plasma/generic/applets/notifications/ui/notifications.cpp
-new file mode 100644
-index 0000000..eea1d72
---- /dev/null
-+++ b/plasma/generic/applets/notifications/ui/notifications.cpp
-@@ -0,0 +1,434 @@
-+/***************************************************************************
-+ * applet.cpp *
-+ * *
-+ * Copyright (C) 2008 Jason Stubbs <jasonbstubbs@gmail.com> *
-+ * Copyright (C) 2008 Sebastian Sauer *
-+ * Copyright (C) 2010 Marco Martin <notmart@gmail.com> *
-+ * *
-+ * This program is free software; you can redistribute it and/or modify *
-+ * it under the terms of the GNU General Public License as published by *
-+ * the Free Software Foundation; either version 2 of the License, or *
-+ * (at your option) any later version. *
-+ * *
-+ * This program is distributed in the hope that it will be useful, *
-+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
-+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
-+ * GNU General Public License for more details. *
-+ * *
-+ * You should have received a copy of the GNU General Public License *
-+ * along with this program; if not, write to the *
-+ * Free Software Foundation, Inc., *
-+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA . *
-+ ***************************************************************************/
-+
-+#include "notifications.h"
-+
-+#include <QtGui/QApplication>
-+#include <QtGui/QGraphicsLayout>
-+#include <QtGui/QGraphicsLinearLayout>
-+#include <QtGui/QVBoxLayout>
-+#include <QtGui/QIcon>
-+#include <QtGui/QLabel>
-+#include <QtGui/QListWidget>
-+#include <QtGui/QTreeWidget>
-+#include <QtGui/QCheckBox>
-+#include <QtGui/QPainter>
-+#include <QtGui/QX11Info>
-+#include <QtCore/QProcess>
-+
-+
-+#include <KConfigDialog>
-+#include <KComboBox>
-+#include <KWindowSystem>
-+#include <KIconLoader>
-+
-+#include <Solid/Device>
-+
-+#include <plasma/extender.h>
-+#include <plasma/extenderitem.h>
-+#include <plasma/extendergroup.h>
-+#include <plasma/framesvg.h>
-+#include <plasma/widgets/label.h>
-+#include <plasma/theme.h>
-+#include <plasma/dataenginemanager.h>
-+#include <plasma/dataengine.h>
-+#include <Plasma/TabBar>
-+#include <Plasma/Animator>
-+#include <Plasma/Animation>
-+#include <Plasma/Containment>
-+#include <Plasma/Corona>
-+#include <Plasma/Dialog>
-+#include <Plasma/WindowEffects>
-+
-+#include "config-notifications.h"
-+#ifdef HAVE_LIBXSS // Idle detection.
-+#include <X11/Xlib.h>
-+#include <X11/Xutil.h>
-+#include <X11/extensions/scrnsaver.h>
-+#include <fixx11h.h>
-+#endif // HAVE_LIBXSS
-+
-+#include "../core/notificationsmanager.h"
-+#include "../core/notification.h"
-+#include "../core/completedjobnotification.h"
-+#include "busywidget.h"
-+#include "jobwidget.h"
-+#include "jobtotalswidget.h"
-+#include "notificationgroup.h"
-+#include "notificationstack.h"
-+#include "stackdialog.h"
-+
-+
-+K_EXPORT_PLASMA_APPLET(notifications, Notifications)
-+
-+
-+Notifications::Notifications(QObject *parent, const QVariantList &arguments)
-+ : Plasma::PopupApplet(parent, arguments),
-+ m_jobSummaryWidget(0),
-+ m_autoHidePopup(true),
-+ m_notificationStack(0),
-+ m_notificationStackDialog(0),
-+ m_standaloneJobSummaryWidget(0),
-+ m_standaloneJobSummaryDialog(0),
-+ m_busyWidget(0)
-+{
-+ m_manager = new Manager(this);
-+
-+ setPopupIcon(QIcon());
-+ setPassivePopup(true);
-+ setAspectRatioMode(Plasma::IgnoreAspectRatio);
-+ setBackgroundHints(NoBackground);
-+ setHasConfigurationInterface(true);
-+ setMinimumSize(KIconLoader::SizeSmall, KIconLoader::SizeSmall);
-+}
-+
-+Notifications::~Notifications()
-+{
-+ // stop listening to the manager
-+ disconnect(m_manager, 0, this, 0);
-+ if (m_notificationStackDialog) {
-+ disconnect(m_notificationStackDialog, 0, this, 0);
-+ }
-+
-+ foreach (Notification *notification, m_manager->notifications()) {
-+ // we don't want a destroyed managed after the destruction of manager
-+ disconnect(notification, 0, this, 0);
-+ }
-+
-+ //has to be deleted before the manager because it will access it
-+ delete m_busyWidget;
-+ delete m_notificationStackDialog;
-+ delete m_standaloneJobSummaryDialog;
-+}
-+
-+void Notifications::init()
-+{
-+ extender()->setEmptyExtenderMessage(i18n("No notifications and no jobs"));
-+
-+ m_busyWidget = new BusyWidget(this, m_manager);
-+ connect(m_busyWidget, SIGNAL(clicked()), this, SLOT(togglePopup()));
-+ QGraphicsLinearLayout *lay = new QGraphicsLinearLayout(this);
-+ setContentsMargins(0, 0, 0, 0);
-+ lay->setContentsMargins(0, 0, 0, 0);
-+ lay->addItem(m_busyWidget);
-+
-+ configChanged();
-+ setStatus(Plasma::PassiveStatus);
-+}
-+
-+void Notifications::configChanged()
-+{
-+ KConfigGroup cg = config();
-+
-+ m_autoHidePopup = cg.readEntry("AutoHidePopup", true);
-+ if (m_notificationStackDialog) {
-+ m_notificationStackDialog->setAutoHide(m_autoHidePopup);
-+ }
-+
-+ if (cg.readEntry("ShowJobs", true)) {
-+ createJobGroups();
-+
-+ m_manager->registerJobProtocol();
-+ connect(m_manager, SIGNAL(jobAdded(Job*)),
-+ this, SLOT(addJob(Job*)), Qt::UniqueConnection);
-+ connect(m_manager, SIGNAL(jobRemoved(Job*)),
-+ this, SLOT(finishJob(Job*)), Qt::UniqueConnection);
-+ } else {
-+ delete extender()->group("jobGroup");
-+ m_manager->unregisterJobProtocol();
-+ disconnect(m_manager, SIGNAL(jobAdded(Job*)),
-+ this, SLOT(addJob(Job*)));
-+ disconnect(m_manager, SIGNAL(jobRemoved(Job*)),
-+ this, SLOT(finishJob(Job*)));
-+ }
-+
-+ if (cg.readEntry("ShowNotifications", true)) {
-+ m_manager->registerNotificationProtocol();
-+ connect(m_manager, SIGNAL(notificationAdded(Notification*)),
-+ this, SLOT(addNotification(Notification*)), Qt::UniqueConnection);
-+ } else {
-+ m_manager->unregisterNotificationProtocol();
-+ disconnect(m_manager, SIGNAL(notificationAdded(Notification*)),
-+ this, SLOT(addNotification(Notification*)));
-+ }
-+}
-+
-+void Notifications::syncNotificationBarNeeded()
-+{
-+ if (!m_manager) {
-+ return;
-+ }
-+
-+ if (m_manager->notifications().isEmpty()) {
-+ if (extender()->item("notifications")) {
-+ //don't let him in the config file
-+ extender()->item("notifications")->destroy();
-+ }
-+ } else if (!extender()->item("notifications")) {
-+ m_notificationGroup = new NotificationGroup(extender());
-+ }
-+}
-+
-+Manager *Notifications::manager() const
-+{
-+ return m_manager;
-+}
-+
-+void Notifications::createConfigurationInterface(KConfigDialog *parent)
-+{
-+ if (!m_notificationInterface) {
-+ KConfigGroup cg = config();
-+ m_notificationInterface = new QWidget();
-+
-+ m_notificationUi.setupUi(m_notificationInterface.data());
-+
-+ m_notificationUi.showJobs->setChecked(cg.readEntry("ShowJobs", true));
-+ m_notificationUi.showNotifications->setChecked(cg.readEntry("ShowNotifications", true));
-+
-+ m_notificationUi.autoHide->setChecked(config().readEntry("AutoHidePopup", true));
-+
-+ connect(parent, SIGNAL(applyClicked()), this, SLOT(configAccepted()));
-+ connect(parent, SIGNAL(okClicked()), this, SLOT(configAccepted()));
-+
-+ parent->addPage(m_notificationInterface.data(), i18n("Information"),
-+ "preferences-desktop-notification",
-+ i18n("Choose which information to show"));
-+
-+ connect(m_notificationUi.showNotifications, SIGNAL(stateChanged(int)), parent, SLOT(settingsModified()));
-+ connect(m_notificationUi.showJobs, SIGNAL(stateChanged(int)), parent, SLOT(settingsModified()));
-+ connect(m_notificationUi.autoHide, SIGNAL(stateChanged(int)), parent, SLOT(settingsModified()));
-+ }
-+}
-+
-+void Notifications::configAccepted()
-+{
-+ //TODO put in a single page
-+ //cg.writeEntry("AutoHidePopup", m_autoHideUi.autoHide->isChecked());
-+
-+ KConfigGroup cg = config();
-+ cg.writeEntry("ShowJobs", m_notificationUi.showJobs->isChecked());
-+ cg.writeEntry("ShowNotifications", m_notificationUi.showNotifications->isChecked());
-+ cg.writeEntry("AutoHidePopup", m_notificationUi.autoHide->isChecked());
-+
-+ emit configNeedsSaving();
-+}
-+
-+void Notifications::addNotification(Notification *notification)
-+{
-+ syncNotificationBarNeeded();
-+
-+ //At this point we are sure the pointer is valid
-+ m_notificationGroup.data()->addNotification(notification);
-+
-+
-+ if (isPopupShowing()) {
-+ return;
-+ }
-+
-+ if (!m_notificationStack) {
-+ m_notificationStack = new NotificationStack(this);
-+ if (containment() && containment()->corona()) {
-+ containment()->corona()->addOffscreenWidget(m_notificationStack);
-+ }
-+ m_notificationStackDialog = new StackDialog;
-+ m_notificationStackDialog->setNotificationStack(m_notificationStack);
-+ m_notificationStackDialog->setApplet(this);
-+ connect(m_notificationStack, SIGNAL(stackEmpty()), m_notificationStackDialog, SLOT(hide()));
-+ connect(m_notificationStack, SIGNAL(showRequested()), m_notificationStackDialog, SLOT(perhapsShow()));
-+ m_notificationStackDialog->setAutoHide(m_autoHidePopup);
-+
-+ if (m_standaloneJobSummaryDialog) {
-+ m_notificationStackDialog->setWindowToTile(m_standaloneJobSummaryDialog);
-+ }
-+ }
-+
-+
-+ m_notificationStack->addNotification(notification);
-+ m_notificationStackDialog->syncToGraphicsWidget();
-+
-+ if (containment() && containment()->corona()) {
-+ if (!m_notificationStackDialog->isVisible()) {
-+ m_notificationStack->setCurrentNotification(notification);
-+ }
-+
-+ KWindowSystem::setOnAllDesktops(m_notificationStackDialog->winId(), true);
-+ m_notificationStackDialog->perhapsShow();
-+ }
-+
-+ Plasma::Animation *pulse = Plasma::Animator::create(Plasma::Animator::PulseAnimation, m_busyWidget);
-+ pulse->setTargetWidget(m_busyWidget);
-+ pulse->start(QAbstractAnimation::DeleteWhenStopped);
-+}
-+
-+void Notifications::addJob(Job *job)
-+{
-+ Plasma::ExtenderGroup *group = extender()->group("jobGroup");
-+
-+ Plasma::ExtenderItem *extenderItem = new Plasma::ExtenderItem(extender());
-+ extenderItem->setTransient(true);
-+ extenderItem->config().writeEntry("type", "job");
-+ extenderItem->setWidget(new JobWidget(job, extenderItem));
-+
-+ extenderItem->setGroup(group);
-+
-+ if (group) {
-+ group->setCollapsed(group->items().count() < 2);
-+ }
-+
-+ if (isPopupShowing()) {
-+ return;
-+ }
-+
-+ //show the tiny standalone overview
-+ if (!m_standaloneJobSummaryWidget) {
-+ m_standaloneJobSummaryDialog = new Plasma::Dialog();
-+ KWindowSystem::setType(m_standaloneJobSummaryDialog->winId(), NET::Dock);
-+ if (m_notificationStackDialog) {
-+ m_notificationStackDialog->setWindowToTile(m_standaloneJobSummaryDialog);
-+ }
-+
-+ m_standaloneJobSummaryWidget = new JobTotalsWidget(m_manager->jobTotals(), this);
-+ if (containment() && containment()->corona()) {
-+ containment()->corona()->addOffscreenWidget(m_standaloneJobSummaryWidget);
-+ }
-+ m_standaloneJobSummaryDialog->setGraphicsWidget(m_standaloneJobSummaryWidget);
-+ //FIXME:sizing hack and layout issues..
-+ m_standaloneJobSummaryWidget->resize(m_standaloneJobSummaryWidget->size().width(), 32);
-+ m_standaloneJobSummaryWidget->setMaximumHeight(32);
-+ m_standaloneJobSummaryWidget->setMinimumHeight(32);
-+ m_standaloneJobSummaryWidget->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
-+ }
-+
-+ m_standaloneJobSummaryDialog->syncToGraphicsWidget();
-+ KWindowSystem::setState(m_standaloneJobSummaryDialog->winId(), NET::KeepBelow);
-+
-+ if (containment() && containment()->corona()) {
-+ m_standaloneJobSummaryDialog->move(containment()->corona()->popupPosition(this, m_standaloneJobSummaryDialog->size()));
-+ m_standaloneJobSummaryDialog->show();
-+ Plasma::WindowEffects::slideWindow(m_standaloneJobSummaryDialog, location());
-+
-+ KWindowSystem::setOnAllDesktops(m_standaloneJobSummaryDialog->winId(), true);
-+ KWindowSystem::clearState(m_standaloneJobSummaryDialog->winId(), NET::KeepAbove|NET::StaysOnTop);
-+
-+ KWindowSystem::setState(m_standaloneJobSummaryDialog->winId(), NET::SkipTaskbar|NET::SkipPager);
-+ KWindowSystem::raiseWindow(m_standaloneJobSummaryDialog->winId());
-+ KWindowSystem::setOnAllDesktops(m_standaloneJobSummaryDialog->winId(), true);
-+ }
-+}
-+
-+void Notifications::initExtenderItem(Plasma::ExtenderItem *extenderItem)
-+{
-+ if (extenderItem->name() == "jobGroup") {
-+ m_jobSummaryWidget = new JobTotalsWidget(m_manager->jobTotals(), extenderItem);
-+ extenderItem->setWidget(m_jobSummaryWidget);
-+ Plasma::ExtenderGroup *group = qobject_cast<Plasma::ExtenderGroup*>(extenderItem);
-+ if (group) {
-+ extenderItem->setCollapsed(!group->isGroupCollapsed());
-+ }
-+ return;
-+ }
-+
-+ if (extenderItem->config().readEntry("type", QString()) == "job") {
-+ extenderItem->setWidget(new JobWidget(0, extenderItem));
-+ } else {
-+ //unknown type, this should never happen
-+ extenderItem->destroy();
-+ }
-+
-+}
-+
-+void Notifications::popupEvent(bool show)
-+{
-+ if (m_busyWidget) {
-+ m_busyWidget->suppressToolTips(show);
-+ }
-+
-+ //decide about showing the tiny progressbar or not
-+ if (m_standaloneJobSummaryDialog) {
-+ if (show || !m_manager->jobs().isEmpty()) {
-+ if (!show) {
-+ KWindowSystem::raiseWindow(m_standaloneJobSummaryDialog->winId());
-+ KWindowSystem::setState(m_standaloneJobSummaryDialog->winId(), NET::SkipTaskbar|NET::SkipPager);
-+ KWindowSystem::setState(m_standaloneJobSummaryDialog->winId(), NET::KeepBelow);
-+ } else {
-+ m_standaloneJobSummaryDialog->hide();
-+ }
-+ }
-+ }
-+
-+ if (m_notificationStackDialog && show) {
-+ m_notificationStackDialog->hide();
-+ }
-+
-+ Plasma::ExtenderGroup * jobGroup = extender()->group("jobGroup");
-+ if (!jobGroup) {
-+ return;
-+ }
-+
-+ foreach (Plasma::ExtenderItem *item, jobGroup->items()) {
-+ JobWidget *job = dynamic_cast<JobWidget *>(item->widget());
-+ if (job) {
-+ job->poppedUp(show);
-+ }
-+ }
-+}
-+
-+void Notifications::finishJob(Job *job)
-+{
-+ //finished all jobs? hide the mini progressbar
-+ if (m_standaloneJobSummaryDialog && m_manager->jobs().isEmpty()) {
-+ m_standaloneJobSummaryDialog->hide();
-+ }
-+
-+ //create a fake notification
-+ CompletedJobNotification *notification = new CompletedJobNotification(this);
-+ notification->setJob(job);
-+ m_manager->addNotification(notification);
-+
-+ Plasma::ExtenderGroup *group = extender()->group("jobGroup");
-+ if (group) {
-+ // < 3 because the second still hasn't been removed from the extendergroup
-+ group->setCollapsed(!group->isGroupCollapsed() && group->items().count() < 3);
-+ }
-+}
-+
-+void Notifications::open(const QString &url)
-+{
-+ //kDebug() << "open " << url;
-+ QProcess::startDetached("kde-open", QStringList() << url);
-+}
-+
-+void Notifications::createJobGroups()
-+{
-+ if (!extender()->hasItem("jobGroup")) {
-+ Plasma::ExtenderGroup *extenderGroup = new Plasma::ExtenderGroup(extender());
-+ extenderGroup->setName("jobGroup");
-+ initExtenderItem(extenderGroup);
-+ extenderGroup->setAutoHide(true);
-+ } else if (extender()->group("jobGroup")) {
-+ extender()->group("jobGroup")->setAutoHide(true);
-+ }
-+}
-+
-+
-+#include "notifications.moc"
-diff --git a/plasma/generic/applets/notifications/ui/notifications.h b/plasma/generic/applets/notifications/ui/notifications.h
-new file mode 100644
-index 0000000..827b28e
---- /dev/null
-+++ b/plasma/generic/applets/notifications/ui/notifications.h
-@@ -0,0 +1,101 @@
-+/***************************************************************************
-+ * applet.h *
-+ * *
-+ * Copyright (C) 2008 Jason Stubbs <jasonbstubbs@gmail.com> *
-+ * Copyright (C) 2010 Marco Martin <notmart@gmail.com> *
-+ * *
-+ * This program is free software; you can redistribute it and/or modify *
-+ * it under the terms of the GNU General Public License as published by *
-+ * the Free Software Foundation; either version 2 of the License, or *
-+ * (at your option) any later version. *
-+ * *
-+ * This program is distributed in the hope that it will be useful, *
-+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
-+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
-+ * GNU General Public License for more details. *
-+ * *
-+ * You should have received a copy of the GNU General Public License *
-+ * along with this program; if not, write to the *
-+ * Free Software Foundation, Inc., *
-+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA . *
-+ ***************************************************************************/
-+
-+#ifndef NOTIFICATIONS_H
-+#define NOTIFICATIONS_H
-+
-+#include <plasma/popupapplet.h>
-+
-+#include "ui_notificationsconfig.h"
-+
-+
-+namespace Plasma
-+{
-+class ExtenderItem;
-+class TabBar;
-+class Dialog;
-+}
-+
-+class NotificationWidget;
-+class StackDialog;
-+
-+
-+class Job;
-+class JobTotalsWidget;
-+class Manager;
-+class Notification;
-+class NotificationGroup;
-+class NotificationStack;
-+class BusyWidget;
-+
-+class Notifications : public Plasma::PopupApplet
-+{
-+ Q_OBJECT
-+
-+public:
-+ explicit Notifications(QObject *parent, const QVariantList &arguments = QVariantList());
-+ ~Notifications();
-+
-+ void init();
-+ Manager *manager() const;
-+
-+protected:
-+ void createConfigurationInterface(KConfigDialog *parent);
-+ void initExtenderItem(Plasma::ExtenderItem *extenderItem);
-+ void configChanged();
-+
-+ void popupEvent(bool show);
-+
-+private slots:
-+ void configAccepted();
-+ void addNotification(Notification *notification);
-+ void addJob(Job *job);
-+ void finishJob(Job *job);
-+ void open(const QString &url);
-+ void syncNotificationBarNeeded();
-+
-+private:
-+ void createJobGroups();
-+
-+ Manager *m_manager;
-+ static int s_managerUsage;
-+
-+ QWeakPointer<QWidget> m_notificationInterface;
-+ QDateTime m_lastActivity;
-+
-+ JobTotalsWidget *m_jobSummaryWidget;
-+ bool m_autoHidePopup;
-+
-+ QWeakPointer<NotificationGroup> m_notificationGroup;
-+ NotificationStack *m_notificationStack;
-+ StackDialog *m_notificationStackDialog;
-+ JobTotalsWidget *m_standaloneJobSummaryWidget;
-+ Plasma::Dialog *m_standaloneJobSummaryDialog;
-+
-+ BusyWidget *m_busyWidget;
-+
-+ Ui::NotificationsConfig m_notificationUi;
-+};
-+
-+
-+
-+#endif
-diff --git a/plasma/generic/applets/notifications/ui/notificationsconfig.ui b/plasma/generic/applets/notifications/ui/notificationsconfig.ui
-new file mode 100644
-index 0000000..aaa6a82
---- /dev/null
-+++ b/plasma/generic/applets/notifications/ui/notificationsconfig.ui
-@@ -0,0 +1,114 @@
-+<?xml version="1.0" encoding="UTF-8"?>
-+<ui version="4.0">
-+ <class>NotificationsConfig</class>
-+ <widget class="QWidget" name="ProtocolsConfig">
-+ <property name="geometry">
-+ <rect>
-+ <x>0</x>
-+ <y>0</y>
-+ <width>213</width>
-+ <height>236</height>
-+ </rect>
-+ </property>
-+ <layout class="QFormLayout" name="formLayout">
-+ <property name="fieldGrowthPolicy">
-+ <enum>QFormLayout::ExpandingFieldsGrow</enum>
-+ </property>
-+ <item row="0" column="0" colspan="2">
-+ <widget class="QLabel" name="label_2">
-+ <property name="font">
-+ <font>
-+ <weight>75</weight>
-+ <bold>true</bold>
-+ </font>
-+ </property>
-+ <property name="text">
-+ <string>Pop Up Notices</string>
-+ </property>
-+ </widget>
-+ </item>
-+ <item row="2" column="0">
-+ <widget class="QLabel" name="label_4">
-+ <property name="text">
-+ <string>Application notifications</string>
-+ </property>
-+ <property name="buddy">
-+ <cstring>showNotifications</cstring>
-+ </property>
-+ </widget>
-+ </item>
-+ <item row="2" column="1">
-+ <widget class="QCheckBox" name="showNotifications">
-+ <property name="text">
-+ <string/>
-+ </property>
-+ </widget>
-+ </item>
-+ <item row="4" column="0">
-+ <widget class="QLabel" name="label_5">
-+ <property name="text">
-+ <string>File transfers and other jobs</string>
-+ </property>
-+ <property name="buddy">
-+ <cstring>showJobs</cstring>
-+ </property>
-+ </widget>
-+ </item>
-+ <item row="4" column="1">
-+ <widget class="QCheckBox" name="showJobs">
-+ <property name="text">
-+ <string/>
-+ </property>
-+ </widget>
-+ </item>
-+ <item row="8" column="0">
-+ <spacer name="verticalSpacer">
-+ <property name="orientation">
-+ <enum>Qt::Vertical</enum>
-+ </property>
-+ <property name="sizeType">
-+ <enum>QSizePolicy::Fixed</enum>
-+ </property>
-+ <property name="sizeHint" stdset="0">
-+ <size>
-+ <width>20</width>
-+ <height>6</height>
-+ </size>
-+ </property>
-+ </spacer>
-+ </item>
-+ <item row="9" column="0" colspan="2">
-+ <widget class="QLabel" name="label_3">
-+ <property name="font">
-+ <font>
-+ <weight>75</weight>
-+ <bold>true</bold>
-+ </font>
-+ </property>
-+ <property name="text">
-+ <string>Popup</string>
-+ </property>
-+ </widget>
-+ </item>
-+ <item row="10" column="1">
-+ <widget class="QCheckBox" name="autoHide">
-+ <property name="text">
-+ <string/>
-+ </property>
-+ </widget>
-+ </item>
-+ <item row="10" column="0">
-+ <widget class="QLabel" name="label_10">
-+ <property name="text">
-+ <string>Automatically hide</string>
-+ </property>
-+ <property name="buddy">
-+ <cstring>autoHide</cstring>
-+ </property>
-+ </widget>
-+ </item>
-+ </layout>
-+ </widget>
-+ <resources/>
-+ <connections/>
-+</ui>
-diff --git a/plasma/generic/applets/notifications/ui/notificationstack.cpp b/plasma/generic/applets/notifications/ui/notificationstack.cpp
-new file mode 100644
-index 0000000..f0fc52e
---- /dev/null
-+++ b/plasma/generic/applets/notifications/ui/notificationstack.cpp
-@@ -0,0 +1,233 @@
-+/***************************************************************************
-+ * notificationstack.cpp *
-+ * Copyright (C) 2010 Marco Martin <notmart@gmail.com> *
-+ * *
-+ * This program is free software; you can redistribute it and/or modify *
-+ * it under the terms of the GNU General Public License as published by *
-+ * the Free Software Foundation; either version 2 of the License, or *
-+ * (at your option) any later version. *
-+ * *
-+ * This program is distributed in the hope that it will be useful, *
-+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
-+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
-+ * GNU General Public License for more details. *
-+ * *
-+ * You should have received a copy of the GNU General Public License *
-+ * along with this program; if not, write to the *
-+ * Free Software Foundation, Inc., *
-+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA . *
-+ ***************************************************************************/
-+
-+#include "notificationstack.h"
-+#include "../core/notification.h"
-+#include "notificationwidget.h"
-+
-+#include <QGraphicsLinearLayout>
-+#include <QTimer>
-+
-+#include <KDebug>
-+#include <KGlobalSettings>
-+
-+#include <Plasma/FrameSvg>
-+#include <Plasma/Dialog>
-+
-+NotificationStack::NotificationStack(QGraphicsItem *parent)
-+ : QGraphicsWidget(parent),
-+ m_size(4),
-+ m_underMouse(false)
-+{
-+ m_mainLayout = new QGraphicsLinearLayout(Qt::Vertical, this);
-+ m_canDismissTimer = new QTimer(this);
-+ m_canDismissTimer->setSingleShot(true);
-+
-+ m_delayedRemoveTimer = new QTimer(this);
-+ m_delayedRemoveTimer->setSingleShot(true);
-+ connect(m_delayedRemoveTimer, SIGNAL(timeout()), this, SLOT(popNotification()));
-+
-+ setAcceptsHoverEvents(true);
-+}
-+
-+NotificationStack::~NotificationStack()
-+{
-+}
-+
-+void NotificationStack::addNotification(Notification *notification)
-+{
-+ m_canDismissTimer->start(1000);
-+ connect(notification, SIGNAL(notificationDestroyed(Notification*)), this, SLOT(removeNotification(Notification*)), Qt::UniqueConnection);
-+ connect(notification, SIGNAL(expired(Notification*)), this, SLOT(delayedRemoveNotification(Notification*)), Qt::UniqueConnection);
-+ connect(notification, SIGNAL(changed(Notification*)), this, SLOT(notificationChanged(Notification*)), Qt::UniqueConnection);
-+
-+ NotificationWidget *notificationWidget = new NotificationWidget(notification, this);
-+ notificationWidget->installEventFilter(this);
-+ notificationWidget->setAcceptsHoverEvents(this);
-+ connect(notificationWidget, SIGNAL(actionTriggered(Notification*)), this, SLOT(removeNotification(Notification*)));
-+
-+ m_notificationWidgets[notification] = notificationWidget;
-+ m_notifications.append(notification);
-+
-+ if (m_notifications.size() > 1) {
-+ notificationWidget->setCollapsed(true, false);
-+ } else {
-+ m_currentNotificationWidget = notificationWidget;
-+ }
-+
-+ if (m_notifications.size() > m_size) {
-+ bool found = false;
-+
-+ //try to kill the oldest notification of the same app
-+ foreach (Notification *notif, m_notifications) {
-+ if (notif->applicationName() == notification->applicationName()) {
-+ m_notificationWidgets[notif]->deleteLater();
-+ m_notificationWidgets.remove(notif);
-+ m_notifications.removeAll(notif);
-+ found = true;
-+ break;
-+ }
-+ }
-+ //or kill the oldest one
-+ if (!found) {
-+ Notification *notif = m_notifications.first();
-+ m_notificationWidgets[notif]->deleteLater();
-+ m_notificationWidgets.remove(notif);
-+ m_notifications.pop_front();
-+ }
-+ }
-+
-+ m_mainLayout->insertItem(0, notificationWidget);
-+ m_mainLayout->activate();
-+ updateGeometry();
-+ resize(size().width(), effectiveSizeHint(Qt::MinimumSize).height());
-+ emit updateRequested();
-+}
-+
-+void NotificationStack::notificationChanged(Notification *notification)
-+{
-+ //if it was gone away put in on the stack again
-+ if (!m_notificationWidgets.contains(notification)) {
-+ addNotification(notification);
-+ }
-+ emit showRequested();
-+}
-+
-+void NotificationStack::removeNotification(Notification *notification)
-+{
-+ NotificationWidget *nw = m_notificationWidgets.value(notification);
-+ if (nw) {
-+ nw->deleteLater();
-+ }
-+ m_mainLayout->removeItem(nw);
-+ m_notificationWidgets.remove(notification);
-+ m_notifications.removeAll(notification);
-+
-+ if (m_notifications.count() > 0) {
-+ setCurrentNotification(m_notifications.first());
-+ }
-+
-+ if (m_notifications.count() == 0) {
-+ emit stackEmpty();
-+ }
-+
-+ updateGeometry();
-+ resize(size().width(), sizeHint(Qt::MinimumSize, QSizeF()).height());
-+ emit updateRequested();
-+}
-+
-+void NotificationStack::delayedRemoveNotification(Notification *notification)
-+{
-+ m_notificationsToRemove.append(notification);
-+ if (!m_underMouse) {
-+ m_delayedRemoveTimer->start(1000);
-+ }
-+}
-+
-+void NotificationStack::setCurrentNotification(Notification *notification)
-+{
-+ if (m_notificationWidgets.contains(notification)) {
-+ if (m_currentNotificationWidget) {
-+ m_currentNotificationWidget.data()->setCollapsed(true);
-+ }
-+ m_currentNotificationWidget = m_notificationWidgets.value(notification);
-+ m_currentNotificationWidget.data()->setCollapsed(false);
-+ }
-+}
-+
-+void NotificationStack::hoverEnterEvent(QGraphicsSceneHoverEvent *event)
-+{
-+ Q_UNUSED(event)
-+
-+ m_underMouse = true;
-+ m_delayedRemoveTimer->stop();
-+}
-+
-+void NotificationStack::hoverLeaveEvent(QGraphicsSceneHoverEvent *event)
-+{
-+ Q_UNUSED(event)
-+
-+ m_underMouse = false;
-+ m_delayedRemoveTimer->start(1000);
-+}
-+
-+void NotificationStack::mousePressEvent(QGraphicsSceneMouseEvent *event)
-+{
-+ event->accept();
-+}
-+
-+void NotificationStack::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
-+{
-+ Q_UNUSED(event)
-+
-+ if (!m_canDismissTimer->isActive() &&
-+ QPointF(event->buttonDownScenePos(event->button()) - event->scenePos()).manhattanLength() < KGlobalSettings::dndEventDelay()) {
-+ emit hideRequested();
-+ }
-+}
-+
-+NotificationWidget *NotificationStack::currentNotificationWidget() const
-+{
-+ if (m_currentNotificationWidget) {
-+ return m_currentNotificationWidget.data();
-+ } else {
-+ return 0;
-+ }
-+}
-+
-+bool NotificationStack::eventFilter(QObject *watched, QEvent *event)
-+{
-+ NotificationWidget *nw = qobject_cast<NotificationWidget *>(watched);
-+
-+ if (!nw) {
-+ return false;
-+ }
-+
-+ if (event->type() == QEvent::GraphicsSceneHoverEnter) {
-+ if (m_currentNotificationWidget && m_currentNotificationWidget.data() == nw) {
-+ return false;
-+ } else if (m_currentNotificationWidget) {
-+ m_currentNotificationWidget.data()->setCollapsed(true);
-+ }
-+ nw->setCollapsed(false);
-+ m_currentNotificationWidget = nw;
-+ m_canDismissTimer->start(1000);
-+ } else if (event->type() == QEvent::GraphicsSceneMove) {
-+ emit updateRequested();
-+ }
-+
-+
-+ return false;
-+}
-+
-+void NotificationStack::popNotification()
-+{
-+ if (m_notificationsToRemove.isEmpty()) {
-+ return;
-+ }
-+
-+ Notification *notif = m_notificationsToRemove.first();
-+ removeNotification(notif);
-+ m_notificationsToRemove.pop_front();
-+ m_delayedRemoveTimer->start(1000);
-+}
-+
-+
-+#include "notificationstack.moc"
-diff --git a/plasma/generic/applets/notifications/ui/notificationstack.h b/plasma/generic/applets/notifications/ui/notificationstack.h
-new file mode 100644
-index 0000000..783e5ae
---- /dev/null
-+++ b/plasma/generic/applets/notifications/ui/notificationstack.h
-@@ -0,0 +1,81 @@
-+/***************************************************************************
-+ * notificationscroller.h *
-+ * Copyright (C) 2010 Marco Martin <notmart@gmail.com> *
-+ * *
-+ * This program is free software; you can redistribute it and/or modify *
-+ * it under the terms of the GNU General Public License as published by *
-+ * the Free Software Foundation; either version 2 of the License, or *
-+ * (at your option) any later version. *
-+ * *
-+ * This program is distributed in the hope that it will be useful, *
-+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
-+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
-+ * GNU General Public License for more details. *
-+ * *
-+ * You should have received a copy of the GNU General Public License *
-+ * along with this program; if not, write to the *
-+ * Free Software Foundation, Inc., *
-+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA . *
-+ ***************************************************************************/
-+
-+#ifndef NOTIFICATIONSTACK_H
-+#define NOTIFICATIONSTACK_H
-+
-+#include <QGraphicsWidget>
-+
-+class QGraphicsLinearLayout;
-+class QTimer;
-+
-+class NotificationWidget;
-+
-+class Notification;
-+
-+class NotificationStack : public QGraphicsWidget
-+{
-+ Q_OBJECT
-+
-+public:
-+ NotificationStack(QGraphicsItem *parent = 0);
-+ ~NotificationStack();
-+
-+ void addNotification(Notification *notification);
-+
-+ //TODO:accessor
-+ void setCurrentNotification(Notification *notification);
-+
-+ NotificationWidget *currentNotificationWidget() const;
-+
-+protected:
-+ void hoverEnterEvent(QGraphicsSceneHoverEvent *event);
-+ void hoverLeaveEvent(QGraphicsSceneHoverEvent *event);
-+ void mousePressEvent(QGraphicsSceneMouseEvent *event);
-+ void mouseReleaseEvent(QGraphicsSceneMouseEvent *event);
-+ bool eventFilter(QObject *watched, QEvent *event);
-+
-+public Q_SLOTS:
-+ void removeNotification(Notification *notification);
-+ void delayedRemoveNotification(Notification *notification);
-+
-+private Q_SLOTS:
-+ void popNotification();
-+ void notificationChanged(Notification *notification);
-+
-+Q_SIGNALS:
-+ void stackEmpty();
-+ void updateRequested();
-+ void hideRequested();
-+ void showRequested();
-+
-+private:
-+ QList<Notification *> m_notifications;
-+ QList<Notification *> m_notificationsToRemove;
-+ QHash<Notification *, NotificationWidget *> m_notificationWidgets;
-+ QGraphicsLinearLayout *m_mainLayout;
-+ int m_size;
-+ bool m_underMouse;
-+ QWeakPointer<NotificationWidget> m_currentNotificationWidget;
-+ QTimer *m_delayedRemoveTimer;
-+ QTimer *m_canDismissTimer;
-+};
-+
-+#endif
-diff --git a/plasma/generic/applets/notifications/ui/notificationwidget.cpp b/plasma/generic/applets/notifications/ui/notificationwidget.cpp
-new file mode 100644
-index 0000000..02ed38f
---- /dev/null
-+++ b/plasma/generic/applets/notifications/ui/notificationwidget.cpp
-@@ -0,0 +1,486 @@
-+/***************************************************************************
-+ * notificationwidget.cpp *
-+ * *
-+ * Copyright (C) 2008 Dmitry Suzdalev <dimsuz@gmail.com> *
-+ * Copyright (C) 2008 Rob Scheepmaker <r.scheepmaker@student.utwente.nl> *
-+ * Copyright (C) 2008 Jason Stubbs <jasonbstubbs@gmail.com> *
-+ * Copyright (C) 2010 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 of the *
-+ * License, or (at your option) any later version. *
-+ * *
-+ * This program is distributed in the hope that it will be useful, *
-+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
-+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
-+ * GNU Library General Public License for more details. *
-+ * *
-+ * You should have received a copy of the GNU Library General Public *
-+ * License along with this library; if not, write to the *
-+ * Free Software Foundation, Inc., *
-+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA . *
-+ ***************************************************************************/
-+
-+#include "notificationwidget.h"
-+
-+#include <QSignalMapper>
-+
-+#include <QtGui/QGraphicsLinearLayout>
-+#include <QtGui/QGraphicsGridLayout>
-+#include <QtGui/QTextDocument>
-+#include <QtGui/QFontMetrics>
-+#include <QtGui/QGraphicsSceneResizeEvent>
-+#include <QtGui/QPainter>
-+#include <QAction>
-+#include <QLabel>
-+#include <QPropertyAnimation>
-+#include <QParallelAnimationGroup>
-+
-+#include <KColorScheme>
-+#include <KPushButton>
-+#include <KTextBrowser>
-+
-+#include <Plasma/Animation>
-+#include <Plasma/Animator>
-+#include <Plasma/Frame>
-+#include <Plasma/IconWidget>
-+#include <Plasma/Label>
-+#include <Plasma/PushButton>
-+#include <Plasma/TextBrowser>
-+#include <Plasma/Theme>
-+
-+class NotificationWidgetPrivate
-+{
-+public:
-+ NotificationWidgetPrivate(NotificationWidget *q)
-+ : q(q),
-+ destroyOnClose(true),
-+ autoDelete(false),
-+ collapsed(false),
-+ icon(0),
-+ actionsWidget(0),
-+ signalMapper(new QSignalMapper(q))
-+ {
-+ }
-+
-+ void setTextFields(const QString &applicationName, const QString &summary, const QString &message);
-+ void completeDetach();
-+ void updateActions();
-+ void updateNotification();
-+ void buttonClicked();
-+ void hideFinished();
-+ QRectF bigIconRect() const;
-+
-+ NotificationWidget *q;
-+
-+ QWeakPointer<Notification> notification;
-+ bool destroyOnClose;
-+ bool autoDelete;
-+ bool collapsed;
-+
-+ QString message;
-+ Plasma::TextBrowser *messageLabel;
-+ Plasma::Label *title;
-+ Plasma::IconWidget *closeButton;
-+ Plasma::IconWidget *icon;
-+ QGraphicsLinearLayout *titleLayout;
-+ QGraphicsLinearLayout *mainLayout;
-+ QGraphicsGridLayout *bodyLayout;
-+ QGraphicsWidget *body;
-+ QGraphicsWidget *iconPlaceSmall;
-+ QGraphicsWidget *iconPlaceBig;
-+ QGraphicsWidget *actionsWidget;
-+ QHash<QString, QString> actions;
-+ QStringList actionOrder;
-+ QPropertyAnimation *hideAnimation;
-+ QPropertyAnimation *iconAnimation;
-+ QParallelAnimationGroup *animationGroup;
-+
-+ QSignalMapper *signalMapper;
-+};
-+
-+NotificationWidget::NotificationWidget(Notification *notification, QGraphicsWidget *parent)
-+ : QGraphicsWidget(parent),
-+ d(new NotificationWidgetPrivate(this))
-+{
-+ setFlag(QGraphicsItem::ItemHasNoContents, true);
-+ setMinimumWidth(300);
-+
-+ setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Preferred);
-+
-+
-+ d->iconPlaceSmall = new QGraphicsWidget(this);
-+ d->iconPlaceSmall->setMinimumSize(KIconLoader::SizeSmall, KIconLoader::SizeSmall);
-+ d->iconPlaceSmall->setMaximumSize(KIconLoader::SizeSmall, KIconLoader::SizeSmall);
-+ d->icon = new Plasma::IconWidget(this);
-+ d->icon->setAcceptHoverEvents(false);
-+ d->icon->setAcceptedMouseButtons(Qt::NoButton);
-+
-+ d->title = new Plasma::Label(this);
-+ d->title->setWordWrap(false);
-+ d->title->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
-+ d->title->setAlignment(Qt::AlignCenter);
-+
-+ d->closeButton = new Plasma::IconWidget(this);
-+ d->closeButton->setSvg("widgets/configuration-icons", "close");
-+ d->closeButton->setMaximumSize(d->closeButton->sizeFromIconSize(KIconLoader::SizeSmall));
-+ d->closeButton->setMinimumSize(d->closeButton->maximumSize());
-+ connect(d->closeButton, SIGNAL(clicked()), notification, SLOT(deleteLater()));
-+
-+ d->titleLayout = new QGraphicsLinearLayout(Qt::Horizontal);
-+ d->titleLayout->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
-+ d->titleLayout->addItem(d->iconPlaceSmall);
-+ d->titleLayout->addItem(d->title);
-+ d->titleLayout->addItem(d->closeButton);
-+
-+
-+
-+ d->body = new QGraphicsWidget(this);
-+ d->body->setContentsMargins(0,0,0,0);
-+ d->body->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
-+ d->bodyLayout = new QGraphicsGridLayout(d->body);
-+ d->bodyLayout->setSpacing(0);
-+ d->bodyLayout->setContentsMargins(0,0,0,0);
-+
-+ d->messageLabel = new Plasma::TextBrowser(d->body);
-+ d->messageLabel->setPreferredWidth(0);
-+ d->messageLabel->nativeWidget()->setHorizontalScrollBarPolicy( Qt::ScrollBarAlwaysOff );
-+ d->messageLabel->nativeWidget()->setVerticalScrollBarPolicy( Qt::ScrollBarAlwaysOff );
-+ d->messageLabel->nativeWidget()->setAlignment(Qt::AlignLeft|Qt::AlignVCenter);
-+ d->messageLabel->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed);
-+ connect(d->messageLabel->nativeWidget(), SIGNAL(urlClick(QString)),
-+ notification, SLOT(linkActivated(QString)));
-+
-+ d->iconPlaceBig = new QGraphicsWidget(this);
-+ d->iconPlaceBig->setMaximumHeight(KIconLoader::SizeLarge);
-+ d->iconPlaceBig->setMinimumHeight(KIconLoader::SizeLarge);
-+ d->iconPlaceBig->setMaximumWidth(KIconLoader::SizeLarge);
-+ d->iconPlaceBig->setMinimumWidth(KIconLoader::SizeLarge);
-+ d->iconPlaceBig->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Expanding);
-+ d->bodyLayout->addItem(d->iconPlaceBig, 0, 0, Qt::AlignCenter);
-+ d->bodyLayout->addItem(d->messageLabel, 0, 1, Qt::AlignCenter);
-+
-+ d->mainLayout = new QGraphicsLinearLayout(Qt::Vertical, this);
-+ d->mainLayout->setSpacing(0);
-+ d->mainLayout->addItem(d->titleLayout);
-+ d->mainLayout->addItem(d->body);
-+
-+ d->notification = notification;
-+
-+ connect(d->signalMapper, SIGNAL(mapped(QString)),
-+ notification, SLOT(triggerAction(QString)));
-+ connect(notification, SIGNAL(changed()),
-+ this, SLOT(updateNotification()));
-+ connect(notification, SIGNAL(destroyed()),
-+ this, SLOT(deleteLater()));
-+
-+ d->hideAnimation = new QPropertyAnimation(this, "bodyHeight", this);
-+ d->hideAnimation->setDuration(250);
-+ connect(d->hideAnimation, SIGNAL(finished()), this, SLOT(hideFinished()));
-+
-+ d->iconAnimation = new QPropertyAnimation(d->icon, "geometry", d->icon);
-+ d->iconAnimation->setDuration(250);
-+
-+ d->animationGroup = new QParallelAnimationGroup(this);
-+ d->animationGroup->addAnimation(d->hideAnimation);
-+ d->animationGroup->addAnimation(d->iconAnimation);
-+
-+ d->updateNotification();
-+
-+ d->mainLayout->activate();
-+ updateGeometry();
-+}
-+
-+NotificationWidget::~NotificationWidget()
-+{
-+ delete d;
-+}
-+
-+void NotificationWidget::setCollapsed(bool collapse, bool animate)
-+{
-+ if (collapse == d->collapsed) {
-+ return;
-+ }
-+
-+ setTitleBarVisible(true);
-+
-+ //use this weird way to make easy to animate
-+ if (animate) {
-+ setMinimumHeight(-1);
-+ if (collapse) {
-+ d->hideAnimation->setStartValue(d->body->size().height());
-+ d->hideAnimation->setEndValue(0);
-+
-+ d->iconAnimation->setStartValue(d->icon->geometry());
-+ d->iconAnimation->setEndValue(d->iconPlaceSmall->geometry());
-+ d->animationGroup->start();
-+ } else {
-+ d->body->setVisible(true);
-+ d->hideAnimation->setStartValue(d->body->size().height());
-+ d->body->setMaximumHeight(-1);
-+ d->hideAnimation->setEndValue(d->body->effectiveSizeHint(Qt::PreferredSize).height());
-+
-+ d->iconAnimation->setStartValue(d->icon->geometry());
-+ d->iconAnimation->setEndValue(d->bigIconRect());
-+ d->animationGroup->start();
-+ }
-+ } else {
-+ if (collapse) {
-+ //setMaximumHeight(d->titleLayout->geometry().bottom());
-+ d->body->setMinimumHeight(0);
-+ d->body->setMaximumHeight(0);
-+ d->body->hide();
-+ setMinimumHeight(-1);
-+ d->icon->setGeometry(d->iconPlaceSmall->geometry());
-+ } else {
-+ d->body->show();
-+ d->body->setMaximumHeight(-1);
-+ d->body->setMinimumHeight(-1);
-+ d->body->setMaximumHeight(d->body->effectiveSizeHint(Qt::PreferredSize).height());
-+ d->body->setMinimumHeight(d->body->maximumHeight());
-+ updateGeometry();
-+ d->mainLayout->invalidate();
-+ setMinimumHeight(sizeHint(Qt::PreferredSize, QSizeF()).height());
-+
-+ d->icon->setGeometry(d->bigIconRect());
-+ }
-+ }
-+
-+ if (collapse) {
-+ d->messageLabel->nativeWidget()->setTextInteractionFlags(Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse|Qt::TextSelectableByKeyboard);
-+ } else {
-+ d->messageLabel->nativeWidget()->setTextInteractionFlags(Qt::LinksAccessibleByMouse);
-+ }
-+
-+ d->body->setFlag(QGraphicsItem::ItemClipsChildrenToShape, true);
-+
-+ d->collapsed = collapse;
-+}
-+
-+Notification *NotificationWidget::notification() const
-+{
-+ return d->notification.data();
-+}
-+
-+qreal NotificationWidget::bodyHeight() const
-+{
-+ return d->body->maximumHeight();
-+}
-+
-+void NotificationWidget::setBodyHeight(const qreal height)
-+{
-+ d->body->setMaximumHeight(height);
-+ d->body->setMinimumHeight(height);
-+ updateGeometry();
-+ d->mainLayout->invalidate();
-+ setMinimumHeight(sizeHint(Qt::PreferredSize, QSizeF()).height());
-+}
-+
-+bool NotificationWidget::isCollapsed() const
-+{
-+ return d->collapsed;
-+}
-+
-+void NotificationWidget::setTitleBarVisible(bool visible)
-+{
-+ if (visible) {
-+ d->iconPlaceSmall->show();
-+ d->title->show();
-+ d->closeButton->show();
-+ d->titleLayout->setMaximumHeight(QWIDGETSIZE_MAX);
-+ } else {
-+ d->iconPlaceSmall->hide();
-+ d->title->hide();
-+ d->closeButton->hide();
-+ d->titleLayout->setMaximumHeight(0);
-+ }
-+}
-+
-+bool NotificationWidget::isTitleBarVisible() const
-+{
-+ return d->title->isVisible();
-+}
-+
-+void NotificationWidget::resizeEvent(QGraphicsSceneResizeEvent *event)
-+{
-+ QGraphicsWidget::resizeEvent(event);
-+ if (d->icon && !d->collapsed && d->animationGroup->state() != QAbstractAnimation::Running) {
-+ d->icon->setGeometry(d->bigIconRect());
-+ }
-+}
-+
-+QRectF NotificationWidgetPrivate::bigIconRect() const
-+{
-+ return q->mapFromScene(iconPlaceBig->mapToScene(iconPlaceBig->boundingRect())).boundingRect();
-+}
-+
-+void NotificationWidgetPrivate::setTextFields(const QString &applicationName,
-+ const QString &summary, const QString &message)
-+{
-+ if (!summary.isEmpty()) {
-+ title->setText(summary);
-+ } else {
-+ title->setText(i18n("Notification from %1", applicationName));
-+ }
-+
-+
-+ QString processed = message.trimmed();
-+
-+ /*if there is a < that is not closed as a tag, replace it with an entity*/
-+ processed = processed.replace(QRegExp("<(?![^<]*>)"), "<");
-+ processed.replace('\n', "<br>");
-+
-+ QFontMetricsF fm(messageLabel->font());
-+ qreal maxLine = messageLabel->rect().width();
-+
-+ QString parsed;
-+
-+ QString::const_iterator i = processed.begin();
-+ bool inTag = false;
-+ QString word;
-+ QString sentence;
-+
-+ while (i != processed.end()) {
-+ QChar c = *i;
-+
-+ if (c == '<') {
-+ inTag = true;
-+ sentence.append(word);
-+ parsed.append(fm.elidedText(sentence, Qt::ElideRight, maxLine*4.6));
-+ sentence = QString();
-+ word = QString();
-+ word.append(c);
-+ } else if (c == '>') {
-+ word.append(c);
-+ if (!sentence.isEmpty()) {
-+ parsed.append(fm.elidedText(sentence, Qt::ElideRight, maxLine*4.6));
-+ sentence.clear();
-+ }
-+ inTag = false;
-+ parsed.append(word);
-+ word = QString();
-+ } else if (c == ' ') {
-+ word.append(c);
-+ if (inTag) {
-+ parsed.append(word);
-+ } else {
-+ sentence.append(word);
-+ }
-+ word = QString();
-+ } else {
-+ word.append(c);
-+ }
-+
-+ ++i;
-+ }
-+
-+ sentence.append(word);
-+ parsed.append(fm.elidedText(sentence, Qt::ElideRight, maxLine*4.6));
-+
-+ messageLabel->setText(QLatin1String("<html>") + parsed + QLatin1String("</html>"));
-+
-+ if (!collapsed) {
-+ icon->setGeometry(bigIconRect());
-+ }
-+}
-+
-+void NotificationWidgetPrivate::completeDetach()
-+{
-+ actions.clear();
-+ actionOrder.clear();
-+
-+ delete actionsWidget;
-+ actionsWidget = 0;
-+}
-+
-+void NotificationWidgetPrivate::updateActions()
-+{
-+ if (actions.isEmpty() || actionsWidget) {
-+ return;
-+ }
-+
-+ actionsWidget = new QGraphicsWidget(body);
-+ QGraphicsLinearLayout *layout = new QGraphicsLinearLayout(actionsWidget);
-+ layout->setOrientation(Qt::Vertical);
-+ layout->setContentsMargins(0, 0, 0, 0);
-+ layout->addStretch();
-+ actionsWidget->setContentsMargins(0, 0, 0, 0);
-+
-+ foreach (const QString &actionId, actionOrder) {
-+ Plasma::PushButton *button = new Plasma::PushButton(actionsWidget);
-+ QString &action = actions[actionId];
-+ button->setText(action);
-+ button->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum);
-+ //TODO: we need smaller buttons but I don't like this method of accomplishing it.
-+ button->setPreferredHeight(button->minimumHeight() - 6);
-+
-+ q->connect(button, SIGNAL(clicked()), signalMapper, SLOT(map()));
-+ q->connect(button, SIGNAL(clicked()), q, SLOT(buttonClicked()));
-+ signalMapper->setMapping(button, actionId);
-+
-+ layout->addItem(button);
-+ }
-+ layout->addStretch();
-+ layout->activate();
-+
-+ if (actionsWidget->size().width() > q->size().width() * 0.4) {
-+ layout->setOrientation(Qt::Horizontal);
-+ bodyLayout->addItem(actionsWidget, 1, 0, 1, 2, Qt::AlignCenter);
-+ } else {
-+ bodyLayout->addItem(actionsWidget, 0, 2, Qt::AlignCenter);
-+ }
-+}
-+
-+void NotificationWidgetPrivate::buttonClicked()
-+{
-+ //a decsion has already been taken
-+ if (actionsWidget) {
-+ notification.data()->deleteLater();
-+ }
-+ emit q->actionTriggered(notification.data());
-+}
-+
-+void NotificationWidgetPrivate::updateNotification()
-+{
-+ if (!notification) {
-+ return;
-+ }
-+
-+
-+ //set text fields and icon
-+ setTextFields(notification.data()->applicationName(), notification.data()->summary(), notification.data()->message());
-+ icon->setIcon(notification.data()->applicationIcon());
-+ messageLabel->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
-+
-+ //set the actions provided
-+ actions = notification.data()->actions();
-+ actionOrder = notification.data()->actionOrder();
-+ updateActions();
-+
-+ if (!notification.data()->image().isNull()) {
-+
-+ icon->setIcon(QPixmap::fromImage(notification.data()->image()));
-+
-+ QSize imageSize = notification.data()->image().size();
-+
-+ if (imageSize.width() > KIconLoader::SizeLarge || imageSize.height() > KIconLoader::SizeLarge) {
-+ imageSize.scale(KIconLoader::SizeLarge, KIconLoader::SizeLarge, Qt::KeepAspectRatio);
-+ }
-+
-+ icon->setMaximumIconSize(QSizeF(qMin((int)KIconLoader::SizeLarge, imageSize.width()),
-+ qMin((int)KIconLoader::SizeLarge, imageSize.height())));
-+ }
-+
-+
-+ //FIXME: this sounds wrong
-+ q->setPreferredHeight(mainLayout->effectiveSizeHint(Qt::MinimumSize).height());
-+}
-+
-+void NotificationWidgetPrivate::hideFinished()
-+{
-+ body->setVisible(!collapsed);
-+ body->setFlag(QGraphicsItem::ItemClipsChildrenToShape, false);
-+}
-+
-+#include "notificationwidget.moc"
-diff --git a/plasma/generic/applets/notifications/ui/notificationwidget.h b/plasma/generic/applets/notifications/ui/notificationwidget.h
-new file mode 100644
-index 0000000..7c1a436
---- /dev/null
-+++ b/plasma/generic/applets/notifications/ui/notificationwidget.h
-@@ -0,0 +1,72 @@
-+/***************************************************************************
-+ * notificationwidget.h *
-+ * *
-+ * Copyright (C) 2008 Dmitry Suzdalev <dimsuz@gmail.com> *
-+ * Copyright (C) 2008 Rob Scheepmaker <r.scheepmaker@student.utwente.nl> *
-+ * Copyright (C) 2008 Jason Stubbs <jasonbstubbs@gmail.com> *
-+ * Copyright (C) 2010 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 of the *
-+ * License, or (at your option) any later version. *
-+ * *
-+ * This program is distributed in the hope that it will be useful, *
-+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
-+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
-+ * GNU Library General Public License for more details. *
-+ * *
-+ * You should have received a copy of the GNU Library General Public *
-+ * License along with this library; if not, write to the *
-+ * Free Software Foundation, Inc., *
-+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA . *
-+ ***************************************************************************/
-+
-+#ifndef NOTIFICATIONWIDGET_H
-+#define NOTIFICATIONWIDGET_H
-+
-+#include <Plasma/Frame>
-+
-+#include "../core/notification.h"
-+
-+class NotificationWidgetPrivate;
-+
-+/**
-+ * A graphics item, representing notification message.
-+ */
-+class NotificationWidget : public QGraphicsWidget
-+{
-+ Q_OBJECT
-+ Q_PROPERTY(qreal bodyHeight READ bodyHeight WRITE setBodyHeight)
-+
-+public:
-+ NotificationWidget(Notification *notification, QGraphicsWidget *parent);
-+ ~NotificationWidget();
-+
-+ void setCollapsed(bool collapse, bool animate = true);
-+ bool isCollapsed() const;
-+
-+ void setTitleBarVisible(bool visible);
-+ bool isTitleBarVisible() const;
-+
-+ qreal bodyHeight() const;
-+ void setBodyHeight(const qreal height);
-+
-+ Notification *notification() const;
-+
-+protected:
-+ void resizeEvent(QGraphicsSceneResizeEvent *event);
-+
-+Q_SIGNALS:
-+ void actionTriggered(Notification *);
-+
-+private:
-+ friend class NotificationWidgetPrivate;
-+ NotificationWidgetPrivate* const d;
-+
-+ Q_PRIVATE_SLOT(d, void updateNotification())
-+ Q_PRIVATE_SLOT(d, void buttonClicked())
-+ Q_PRIVATE_SLOT(d, void hideFinished())
-+};
-+
-+#endif // NOTIFICATIONWIDGET_H
-diff --git a/plasma/generic/applets/notifications/ui/stackdialog.cpp b/plasma/generic/applets/notifications/ui/stackdialog.cpp
-new file mode 100644
-index 0000000..9be364e
---- /dev/null
-+++ b/plasma/generic/applets/notifications/ui/stackdialog.cpp
-@@ -0,0 +1,446 @@
-+/***************************************************************************
-+ * stacdialog.h *
-+ * Copyright (C) 2010 Marco Martin <notmart@gmail.com> *
-+ * *
-+ * This program is free software; you can redistribute it and/or modify *
-+ * it under the terms of the GNU General Public License as published by *
-+ * the Free Software Foundation; either version 2 of the License, or *
-+ * (at your option) any later version. *
-+ * *
-+ * This program is distributed in the hope that it will be useful, *
-+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
-+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
-+ * GNU General Public License for more details. *
-+ * *
-+ * You should have received a copy of the GNU General Public License *
-+ * along with this program; if not, write to the *
-+ * Free Software Foundation, Inc., *
-+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA . *
-+ ***************************************************************************/
-+
-+#include "stackdialog.h"
-+#include "notificationstack.h"
-+#include "notificationwidget.h"
-+
-+#include <QApplication>
-+#include <QDesktopWidget>
-+#include <QGraphicsLayout>
-+#include <QLayout>
-+#include <QPropertyAnimation>
-+#include <QTimer>
-+
-+#include <KWindowSystem>
-+
-+#include <Plasma/Applet>
-+#include <Plasma/Containment>
-+#include <Plasma/Corona>
-+#include <Plasma/FrameSvg>
-+#include <Plasma/WindowEffects>
-+
-+static const uint hideTimeout = 15 * 1000;
-+
-+StackDialog::StackDialog(QWidget *parent, Qt::WindowFlags f)
-+ : Plasma::Dialog(parent, f),
-+ m_applet(0),
-+ m_windowToTile(0),
-+ m_notificationStack(0),
-+ m_view(0),
-+ m_drawLeft(true),
-+ m_drawRight(true),
-+ m_autoHide(true),
-+ m_hasCustomPosition(false)
-+{
-+ m_background = new Plasma::FrameSvg(this);
-+ m_background->setImagePath("widgets/extender-background");
-+ setWindowFlags(Qt::FramelessWindowHint | Qt::WindowStaysOnTopHint);
-+ KWindowSystem::setType(winId(), NET::Dock);
-+
-+ m_showTimer = new QTimer(this);
-+ m_showTimer->setSingleShot(true);
-+ m_showTimer->setInterval(0);
-+ connect(m_showTimer, SIGNAL(timeout()), this, SLOT(show()));
-+
-+ m_hideTimer = new QTimer(this);
-+ m_hideTimer->setSingleShot(true);
-+ connect(m_hideTimer, SIGNAL(timeout()), this, SLOT(hideRequested()));
-+}
-+
-+StackDialog::~StackDialog()
-+{
-+}
-+
-+void StackDialog::setNotificationStack(NotificationStack *stack)
-+{
-+ setGraphicsWidget(stack);
-+
-+ if (!m_view && layout()) {
-+ m_view = qobject_cast<QGraphicsView *>(layout()->itemAt(0)->widget());
-+ if (m_view) {
-+ m_view->installEventFilter(this);
-+ }
-+ }
-+
-+ if (m_notificationStack) {
-+ disconnect(m_notificationStack, 0, this, 0);
-+ }
-+
-+ m_notificationStack = stack;
-+
-+ connect(m_notificationStack, SIGNAL(updateRequested()), this, SLOT(update()));
-+ connect(m_notificationStack, SIGNAL(hideRequested()), this, SLOT(hideRequested()));
-+}
-+
-+NotificationStack *StackDialog::notificartionStack() const
-+{
-+ return m_notificationStack;
-+}
-+
-+void StackDialog::setApplet(Plasma::Applet *applet)
-+{
-+ m_applet = applet;
-+ adjustPosition(QPoint(-1, -1));
-+}
-+
-+Plasma::Applet *StackDialog::applet() const
-+{
-+ return m_applet;
-+}
-+
-+void StackDialog::setWindowToTile(QWidget *widget)
-+{
-+ if (m_windowToTile) {
-+ m_windowToTile->removeEventFilter(this);
-+ delete m_windowToTileAnimation;
-+ }
-+
-+ m_windowToTile = widget;
-+ m_windowToTile->installEventFilter(this);
-+ m_windowToTileAnimation = new QPropertyAnimation(m_windowToTile, "pos", this);
-+ m_windowToTileAnimation->setDuration(250);
-+ m_windowToTileAnimation->setEasingCurve(QEasingCurve::InOutQuad);
-+}
-+
-+QWidget *StackDialog::windowToTile() const
-+{
-+ return m_windowToTile;
-+}
-+
-+void StackDialog::setAutoHide(const bool autoHide)
-+{
-+ m_autoHide = autoHide;
-+}
-+
-+bool StackDialog::autoHide() const
-+{
-+ return m_autoHide;
-+}
-+
-+void StackDialog::adjustWindowToTilePos()
-+{
-+ if (m_applet && m_windowToTile && m_hasCustomPosition) {
-+ m_windowToTileAnimation->setStartValue(m_windowToTile->pos());
-+
-+ if (isVisible()) {
-+ const QRect realScreenRect = qApp->desktop()->screenGeometry(m_applet->containment()->screen());
-+ if (m_applet->location() == Plasma::TopEdge || (pos().y() - realScreenRect.top()) < m_windowToTile->size().height()) {
-+ m_windowToTileAnimation->setEndValue(QPoint(m_windowToTile->pos().x(), geometry().bottom()));
-+ } else {
-+ m_windowToTileAnimation->setEndValue(QPoint(m_windowToTile->pos().x(), pos().y() - m_windowToTile->size().height()));
-+ }
-+ } else if (m_applet && m_applet->containment() && m_applet->containment()->corona()) {
-+ m_windowToTileAnimation->setEndValue(QPoint(m_applet->containment()->corona()->popupPosition(m_applet, m_windowToTile->size())));
-+ }
-+ m_windowToTileAnimation->start();
-+ }
-+}
-+
-+void StackDialog::paintEvent(QPaintEvent *e)
-+{
-+ Plasma::Dialog::paintEvent(e);
-+
-+ QPainter painter(this);
-+ if (m_notificationStack) {
-+ //FIXME: assumption++
-+ QGraphicsLayout *mainLayout = static_cast<QGraphicsLayout *>(m_notificationStack->layout());
-+
-+ if (!m_notificationStack->currentNotificationWidget() || mainLayout->count() < 2) {
-+ return;
-+ }
-+
-+ for (int i = 0; i < mainLayout->count(); ++i) {
-+ //assumption++ all items are NotificationWidget
-+ NotificationWidget *nw = static_cast<NotificationWidget *>(mainLayout->itemAt(i));
-+
-+ //first element
-+ if (i == 0 && m_notificationStack->currentNotificationWidget() != nw) {
-+ continue;
-+ } else if (i == 0) {
-+ m_background->setEnabledBorders((Plasma::FrameSvg::EnabledBorders)(Plasma::FrameSvg::AllBorders&~Plasma::FrameSvg::TopBorder));
-+ //last element
-+ } else if (i == mainLayout->count()-1 && m_notificationStack->currentNotificationWidget() != nw) {
-+ continue;
-+ } else if (i == mainLayout->count()-1) {
-+ m_background->setEnabledBorders((Plasma::FrameSvg::EnabledBorders)Plasma::FrameSvg::AllBorders&~Plasma::FrameSvg::BottomBorder);
-+ //element under the active one
-+ } else if (m_notificationStack->currentNotificationWidget()->pos().y() < nw->pos().y()) {
-+ m_background->setEnabledBorders((Plasma::FrameSvg::EnabledBorders)Plasma::FrameSvg::AllBorders&~Plasma::FrameSvg::TopBorder);
-+ //element over the active one
-+ } else if (m_notificationStack->currentNotificationWidget()->pos().y() > nw->pos().y()) {
-+ m_background->setEnabledBorders((Plasma::FrameSvg::EnabledBorders)Plasma::FrameSvg::AllBorders&~Plasma::FrameSvg::BottomBorder);
-+ //active element
-+ } else {
-+ m_background->setEnabledBorders(Plasma::FrameSvg::AllBorders);
-+ }
-+
-+ qreal left, top, right, bottom;
-+ m_background->getMargins(left, top, right, bottom);
-+ m_background->resizeFrame(QSizeF(size().width(), nw->size().height() + top + bottom));
-+
-+ Plasma::FrameSvg::EnabledBorders borders = m_background->enabledBorders();
-+
-+ if (!m_drawLeft) {
-+ borders &= ~Plasma::FrameSvg::LeftBorder;
-+ }
-+
-+ if (!m_drawRight) {
-+ borders &= ~Plasma::FrameSvg::RightBorder;
-+ }
-+
-+ m_background->setEnabledBorders(borders);
-+
-+ const int topMargin = contentsRect().top();
-+ m_background->paintFrame(&painter, QPointF(0, nw->pos().y() - top + topMargin));
-+ }
-+ }
-+}
-+
-+void StackDialog::perhapsShow()
-+{
-+ if (m_applet && m_applet->view()) {
-+ // we use a timer here because when the stack is going to be shown, the applet is likely
-+ // to also be in movement (e.g. from hidden away to now shown to the user) and that will
-+ // likely result in an odd geometry for the applet when popupPosition is called on this
-+ // dialog. so instead we wait a bit after the show request to allow the applet to find
-+ // it's proper place
-+ m_showTimer->start();
-+ }
-+}
-+
-+void StackDialog::showEvent(QShowEvent *event)
-+{
-+ Q_UNUSED(event)
-+
-+ adjustPosition(adjustedSavedPos());
-+ adjustWindowToTilePos();
-+
-+ if (m_autoHide) {
-+ m_hideTimer->start(hideTimeout);
-+ }
-+
-+ Plasma::Dialog::showEvent(event);
-+}
-+
-+void StackDialog::hideRequested()
-+{
-+ // we have this method because hide() may not always cause a hideEvent, e.g. when
-+ // the StackDialog has not been shown yet .. however, we always want to ensure the
-+ // show timer is stopped. we also stop it in the hideEvent for completeness and to
-+ // catch any direct calls to hide() that may happen
-+ m_showTimer->stop();
-+ hide();
-+}
-+
-+void StackDialog::hideEvent(QHideEvent *event)
-+{
-+ Q_UNUSED(event)
-+
-+ m_showTimer->stop();
-+ m_hideTimer->stop();
-+
-+ adjustWindowToTilePos();
-+
-+ m_notificationStack->adjustSize();
-+ Plasma::Dialog::hideEvent(event);
-+}
-+
-+void StackDialog::resizeEvent(QResizeEvent *event)
-+{
-+ Q_UNUSED(event)
-+ adjustWindowToTilePos();
-+ Plasma::Dialog::resizeEvent(event);
-+ if (m_hasCustomPosition) {
-+ adjustPosition(pos());
-+ } else if (m_applet && m_applet->containment() && m_applet->containment()->corona()) {
-+ move(m_applet->containment()->corona()->popupPosition(m_applet, size()));
-+ }
-+}
-+
-+void StackDialog::moveEvent(QMoveEvent *event)
-+{
-+ Q_UNUSED(event)
-+
-+ adjustWindowToTilePos();
-+ Plasma::Dialog::moveEvent(event);
-+}
-+
-+void StackDialog::enterEvent(QEvent *event)
-+{
-+ Q_UNUSED(event)
-+
-+ m_hideTimer->stop();
-+}
-+
-+void StackDialog::leaveEvent(QEvent *event)
-+{
-+ Q_UNUSED(event)
-+
-+ if (m_autoHide) {
-+ m_hideTimer->start(hideTimeout);
-+ }
-+ Plasma::Dialog::leaveEvent(event);
-+}
-+
-+void StackDialog::adjustPosition(const QPoint &pos)
-+{
-+ if (!m_applet) {
-+ return;
-+ }
-+
-+ if (pos == QPoint(-1, -1)) {
-+ const QPoint popupPosition = m_applet->containment()->corona()->popupPosition(m_applet, size());
-+ move(popupPosition);
-+ Plasma::WindowEffects::slideWindow(this, m_applet->location());
-+ m_hasCustomPosition = false;
-+ } else {
-+ QPoint customPosition = pos;
-+
-+ if (m_applet->containment() &&
-+ m_applet->containment()->corona() &&
-+ m_notificationStack) {
-+ QRect screenRect = m_applet->containment()->corona()->availableScreenRegion(m_applet->containment()->screen()).boundingRect();
-+
-+ customPosition.rx() = qMax(customPosition.x(), screenRect.left());
-+ customPosition.ry() = qMax(customPosition.y(), screenRect.top());
-+ customPosition.rx() = qMin(customPosition.x() + size().width(), screenRect.right()) - size().width();
-+ customPosition.ry() = qMin(customPosition.y() + size().height(), screenRect.bottom()) - size().height();
-+
-+ bool closerToBottom = (customPosition.ry() > (screenRect.height() / 2));
-+ if (!m_lastSize.isNull() && closerToBottom
-+ && (m_lastSize.height() > size().height())) {
-+ customPosition.ry() += m_lastSize.height() - size().height();
-+ }
-+ m_lastSize = size();
-+ }
-+
-+ move(customPosition);
-+ Plasma::WindowEffects::slideWindow(this, Plasma::Desktop);
-+ m_hasCustomPosition = true;
-+ }
-+}
-+
-+void StackDialog::savePosition(const QPoint& pos)
-+{
-+ if (!m_applet || !m_applet->containment()) {
-+ return;
-+ }
-+
-+ QByteArray horizSide, vertSide;
-+ QPoint pixelsToSave;
-+ const QRect realScreenRect = qApp->desktop()->screenGeometry(m_applet->containment()->screen());
-+
-+ int screenRelativeX = pos.x() - realScreenRect.x();
-+ int diffWithRight = realScreenRect.width() - (screenRelativeX + size().width());
-+ if (screenRelativeX < diffWithRight) {
-+ horizSide = "l";
-+ pixelsToSave.rx() = screenRelativeX;
-+ } else {
-+ horizSide = "r";
-+ pixelsToSave.rx() = diffWithRight;
-+ }
-+
-+ int screenRelativeY = pos.y() - realScreenRect.y();
-+ int diffWithBottom = realScreenRect.height() - (screenRelativeY + size().height());
-+ if (screenRelativeY < diffWithBottom) {
-+ vertSide = "t";
-+ pixelsToSave.ry() = screenRelativeY;
-+ } else {
-+ vertSide = "b";
-+ pixelsToSave.ry() = diffWithBottom;
-+ }
-+
-+ kDebug() << "Affinity-v" << vertSide;
-+ kDebug() << "Affinity-h" << horizSide;
-+ kDebug() << "Y: " << pixelsToSave.ry();
-+ kDebug() << "X: " << pixelsToSave.rx();
-+
-+ m_applet->config().writeEntry("customPosition", pixelsToSave);
-+ m_applet->config().writeEntry("customPositionAffinityHoriz", horizSide);
-+ m_applet->config().writeEntry("customPositionAffinityVert", vertSide);
-+}
-+
-+QPoint StackDialog::adjustedSavedPos() const
-+{
-+ if (!m_applet) {
-+ return QPoint(-1, -1);
-+ }
-+
-+ QPoint pos = m_applet->config().readEntry("customPosition", QPoint(-1, -1));
-+
-+ if (pos != QPoint(-1, -1)) {
-+ const QRect realScreenRect = qApp->desktop()->screenGeometry(m_applet->containment()->screen());
-+ QByteArray horizSide = m_applet->config().readEntry("customPositionAffinityHoriz").toLatin1();
-+ QByteArray vertSide = m_applet->config().readEntry("customPositionAffinityVert").toLatin1();
-+
-+ if (horizSide == "l") {
-+ pos.rx() += realScreenRect.x();
-+ } else {
-+ pos.rx() = realScreenRect.x() + (realScreenRect.width() - pos.rx() - size().width());
-+ }
-+
-+ if (vertSide == "t") {
-+ pos.ry() += realScreenRect.y();
-+ } else {
-+ pos.ry() = realScreenRect.y() + (realScreenRect.height() - pos.ry() - size().height());
-+ }
-+ }
-+
-+ return pos;
-+}
-+
-+bool StackDialog::event(QEvent *event)
-+{
-+ bool ret = Dialog::event(event);
-+
-+ if (event->type() == QEvent::ContentsRectChange) {
-+ int left, top, right, bottom;
-+ getContentsMargins(&left, &top, &right, &bottom);
-+
-+ m_drawLeft = (left != 0);
-+ m_drawRight = (right != 0);
-+ update();
-+ }
-+
-+ return ret;
-+}
-+
-+bool StackDialog::eventFilter(QObject *watched, QEvent *event)
-+{
-+ if (m_windowToTile && watched == m_windowToTile &&
-+ event->type() == QEvent::Show && isVisible()) {
-+ adjustWindowToTilePos();
-+ } else if (watched == m_notificationStack && event->type() == QEvent::GraphicsSceneMousePress) {
-+ QGraphicsSceneMouseEvent *me = static_cast<QGraphicsSceneMouseEvent *>(event);
-+ m_dragPos = me->pos().toPoint();
-+ } else if (watched == m_notificationStack && event->type() == QEvent::GraphicsSceneMouseMove) {
-+ QGraphicsSceneMouseEvent *me = static_cast<QGraphicsSceneMouseEvent *>(event);
-+ adjustPosition(me->screenPos() - m_dragPos);
-+ } else if (watched == m_notificationStack && event->type() == QEvent::GraphicsSceneMouseRelease) {
-+ QGraphicsSceneMouseEvent *me = static_cast<QGraphicsSceneMouseEvent *>(event);
-+ adjustPosition(me->screenPos() - m_dragPos);
-+ savePosition(me->screenPos() - m_dragPos);
-+ }
-+
-+ return Plasma::Dialog::eventFilter(watched, event);
-+}
-+
-+#include "stackdialog.moc"
-diff --git a/plasma/generic/applets/notifications/ui/stackdialog.h b/plasma/generic/applets/notifications/ui/stackdialog.h
-new file mode 100644
-index 0000000..4360c4a
---- /dev/null
-+++ b/plasma/generic/applets/notifications/ui/stackdialog.h
-@@ -0,0 +1,99 @@
-+/***************************************************************************
-+ * stacdialog.h *
-+ * Copyright (C) 2010 Marco Martin <notmart@gmail.com> *
-+ * *
-+ * This program is free software; you can redistribute it and/or modify *
-+ * it under the terms of the GNU General Public License as published by *
-+ * the Free Software Foundation; either version 2 of the License, or *
-+ * (at your option) any later version. *
-+ * *
-+ * This program is distributed in the hope that it will be useful, *
-+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
-+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
-+ * GNU General Public License for more details. *
-+ * *
-+ * You should have received a copy of the GNU General Public License *
-+ * along with this program; if not, write to the *
-+ * Free Software Foundation, Inc., *
-+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA . *
-+ ***************************************************************************/
-+
-+#ifndef STACKDIALOG_H
-+#define STACKDIALOG_H
-+
-+#include <Plasma/Dialog>
-+
-+#include <QPoint>
-+
-+class QPropertyAnimation;
-+class QTimer;
-+
-+namespace Plasma
-+{
-+ class Applet;
-+ class FrameSvg;
-+}
-+
-+class NotificationStack;
-+
-+
-+class StackDialog : public Plasma::Dialog
-+{
-+ Q_OBJECT
-+public:
-+ StackDialog(QWidget *parent = 0, Qt::WindowFlags f = Qt::Window);
-+ ~StackDialog();
-+
-+ void setNotificationStack(NotificationStack *stack);
-+ NotificationStack *notificartionStack() const;
-+
-+ void setApplet(Plasma::Applet *applet);
-+ Plasma::Applet *applet() const;
-+
-+ void setWindowToTile(QWidget *widget);
-+ QWidget *windowToTile() const;
-+
-+ void setAutoHide(const bool autoHide);
-+ bool autoHide() const;
-+
-+public Q_SLOTS:
-+ void perhapsShow();
-+
-+protected:
-+ void adjustWindowToTilePos();
-+
-+ void paintEvent(QPaintEvent *e);
-+ void showEvent(QShowEvent *event);
-+ void hideEvent(QHideEvent *event);
-+ void enterEvent(QEvent *event);
-+ void leaveEvent(QEvent *event);
-+ void resizeEvent(QResizeEvent *event);
-+ void moveEvent(QMoveEvent *event);
-+ bool event(QEvent *event);
-+ bool eventFilter(QObject *watched, QEvent *event);
-+ void adjustPosition(const QPoint &pos = QPoint(-1, -1));
-+ void savePosition(const QPoint &pos);
-+ QPoint adjustedSavedPos() const;
-+
-+private Q_SLOTS:
-+ void hideRequested();
-+
-+private:
-+ Plasma::Applet *m_applet;
-+ QWidget *m_windowToTile;
-+ QPropertyAnimation *m_windowToTileAnimation;
-+ QPoint m_dragPos;
-+ QSize m_lastSize;
-+
-+ Plasma::FrameSvg *m_background;
-+ NotificationStack *m_notificationStack;
-+ QTimer *m_showTimer;
-+ QTimer *m_hideTimer;
-+ QGraphicsView *m_view;
-+ bool m_drawLeft;
-+ bool m_drawRight;
-+ bool m_autoHide;
-+ bool m_hasCustomPosition;
-+};
-+
-+#endif
diff --git a/kde-base/plasma-workspace/files/plasma-workspace-4.10.1-noplasmalock.patch b/kde-base/plasma-workspace/files/plasma-workspace-4.10.1-noplasmalock.patch
deleted file mode 100644
index 2380d20..0000000
--- a/kde-base/plasma-workspace/files/plasma-workspace-4.10.1-noplasmalock.patch
+++ /dev/null
@@ -1,11 +0,0 @@
-diff --git a/plasma/screensaver/CMakeLists.txt b/plasma/screensaver/CMakeLists.txt
-index 514c856..9dcb5f4 100644
---- a/plasma/screensaver/CMakeLists.txt
-+++ b/plasma/screensaver/CMakeLists.txt
-@@ -1,6 +1,3 @@
- add_subdirectory(containments)
--if(NOT WIN32)
--add_subdirectory(shell)
--endif(NOT WIN32)
-
-
diff --git a/kde-base/plasma-workspace/metadata.xml b/kde-base/plasma-workspace/metadata.xml
deleted file mode 100644
index 3b3fc4f..0000000
--- a/kde-base/plasma-workspace/metadata.xml
+++ /dev/null
@@ -1,9 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!DOCTYPE pkgmetadata SYSTEM "http://www.gentoo.org/dtd/metadata.dtd">
-<pkgmetadata>
-<herd>kde</herd>
-<use>
- <flag name="json">Enable JSON support via <pkg>dev-libs/qjson</pkg></flag>
- <flag name="qalculate">Enable Qalculate runner using <pkg>sci-libs/libqalculate</pkg></flag>
-</use>
-</pkgmetadata>
diff --git a/kde-base/plasma-workspace/plasma-workspace-4.11.2-r2.ebuild b/kde-base/plasma-workspace/plasma-workspace-4.11.2-r2.ebuild
deleted file mode 100644
index 4ed968e..0000000
--- a/kde-base/plasma-workspace/plasma-workspace-4.11.2-r2.ebuild
+++ /dev/null
@@ -1,125 +0,0 @@
-# Copyright 1999-2013 Gentoo Foundation
-# Distributed under the terms of the GNU General Public License v2
-# $Header: $
-
-EAPI=5
-
-DECLARATIVE_REQUIRED="always"
-KDE_HANDBOOK="optional"
-KMNAME="kde-workspace"
-KMMODULE="plasma"
-PYTHON_COMPAT=( python2_{6,7} )
-OPENGL_REQUIRED="always"
-inherit python-single-r1 kde4-meta
-
-DESCRIPTION="Plasma: KDE desktop framework"
-KEYWORDS="~amd64 ~arm ~ppc ~ppc64 ~x86 ~amd64-linux ~x86-linux"
-IUSE="debug gps json python qalculate semantic-desktop"
-
-REQUIRED_USE="python? ( ${PYTHON_REQUIRED_USE} )"
-
-COMMONDEPEND="
- dev-libs/libdbusmenu-qt
- >=dev-qt/qtcore-4.8.4-r3:4
- !kde-misc/ktouchpadenabler
- $(add_kdebase_dep kactivities)
- $(add_kdebase_dep kdelibs 'semantic-desktop?')
- $(add_kdebase_dep kdepimlibs)
- $(add_kdebase_dep kephal)
- $(add_kdebase_dep ksysguard)
- $(add_kdebase_dep libkworkspace)
- $(add_kdebase_dep libplasmaclock)
- $(add_kdebase_dep libplasmagenericshell)
- $(add_kdebase_dep libtaskmanager)
- x11-libs/libX11
- x11-libs/libXcomposite
- x11-libs/libXdamage
- x11-libs/libXext
- x11-libs/libXfixes
- x11-libs/libXi
- x11-libs/libXrender
- gps? ( >=sci-geosciences/gpsd-2.37 )
- json? ( dev-libs/qjson )
- python? (
- ${PYTHON_DEPS}
- >=dev-python/PyQt4-4.4.0[X,${PYTHON_USEDEP}]
- $(add_kdebase_dep pykde4 "${PYTHON_USEDEP}")
- )
- qalculate? ( sci-libs/libqalculate )
- semantic-desktop? (
- dev-libs/soprano
- $(add_kdebase_dep nepomuk-core)
- )
-"
-DEPEND="${COMMONDEPEND}
- dev-libs/boost
- x11-proto/compositeproto
- x11-proto/damageproto
- x11-proto/fixesproto
- x11-proto/renderproto
-"
-RDEPEND="${COMMONDEPEND}
- $(add_kdebase_dep plasma-runtime)
-"
-
-KMEXTRA="
- appmenu/
- ktouchpadenabler/
- statusnotifierwatcher/
-"
-KMEXTRACTONLY="
- kcheckpass/
- krunner/dbus/org.freedesktop.ScreenSaver.xml
- krunner/dbus/org.kde.krunner.App.xml
- ksmserver/org.kde.KSMServerInterface.xml
- ksmserver/screenlocker/
- libs/kephal/
- libs/kworkspace/
- libs/taskmanager/
- libs/plasmagenericshell/
- libs/ksysguard/
- libs/kdm/kgreeterplugin.h
- ksysguard/
-"
-
-KMLOADLIBS="libkworkspace libplasmaclock libplasmagenericshell libtaskmanager"
-
-PATCHES=( "${FILESDIR}/${PN}-4.10.1-noplasmalock.patch" "${FILESDIR}/oldnotify.patch" )
-
-pkg_setup() {
- if use python ; then
- python-single-r1_pkg_setup
- fi
- kde4-meta_pkg_setup
-}
-
-src_unpack() {
- if use handbook; then
- KMEXTRA+=" doc/plasma-desktop"
- fi
-
- kde4-meta_src_unpack
-}
-
-src_configure() {
- mycmakeargs=(
- $(cmake-utils_use_with gps libgps)
- $(cmake-utils_use_with json QJSON)
- $(cmake-utils_use_with python PythonLibrary)
- $(cmake-utils_use_with qalculate)
- $(cmake-utils_use_with semantic-desktop Akonadi)
- $(cmake-utils_use_with semantic-desktop NepomukCore)
- $(cmake-utils_use_with semantic-desktop Soprano)
- -DWITH_Xmms=OFF
- )
-
- kde4-meta_src_configure
-}
-
-src_install() {
- kde4-meta_src_install
-
- if use python; then
- python_optimize "${ED}"
- fi
-}
^ permalink raw reply related [flat|nested] 2+ messages in thread
end of thread, other threads:[~2014-04-29 22:47 UTC | newest]
Thread overview: 2+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2014-04-29 22:47 [gentoo-commits] dev/dilfridge:master commit in: kde-base/plasma-workspace/files/, kde-base/plasma-workspace/ Andreas Hüttel
-- strict thread matches above, loose matches on Subject: below --
2013-10-09 12:02 Andreas Hüttel
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox