From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from lists.gentoo.org (pigeon.gentoo.org [208.92.234.80]) by finch.gentoo.org (Postfix) with ESMTP id 780F61381F3 for ; Wed, 9 Oct 2013 12:02:18 +0000 (UTC) Received: from pigeon.gentoo.org (localhost [127.0.0.1]) by pigeon.gentoo.org (Postfix) with SMTP id 2BF9DE09C5; Wed, 9 Oct 2013 12:02:17 +0000 (UTC) Received: from smtp.gentoo.org (smtp.gentoo.org [140.211.166.183]) (using TLSv1 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by pigeon.gentoo.org (Postfix) with ESMTPS id 4583AE09C5 for ; Wed, 9 Oct 2013 12:02:16 +0000 (UTC) Received: from hornbill.gentoo.org (hornbill.gentoo.org [94.100.119.163]) (using TLSv1 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by smtp.gentoo.org (Postfix) with ESMTPS id 0EE0533EE3C for ; Wed, 9 Oct 2013 12:02:15 +0000 (UTC) Received: from localhost.localdomain (localhost [127.0.0.1]) by hornbill.gentoo.org (Postfix) with ESMTP id AAB2EE5460 for ; Wed, 9 Oct 2013 12:02:13 +0000 (UTC) From: "Andreas Hüttel" To: gentoo-commits@lists.gentoo.org Content-Transfer-Encoding: 8bit Content-type: text/plain; charset=UTF-8 Reply-To: gentoo-dev@lists.gentoo.org, "Andreas Hüttel" Message-ID: <1381320108.bc6ae00719b0b577cc0814f167a55211fec65003.dilfridge@gentoo> Subject: [gentoo-commits] dev/dilfridge:master commit in: kde-base/plasma-workspace/files/, kde-base/plasma-workspace/ X-VCS-Repository: dev/dilfridge X-VCS-Files: kde-base/plasma-workspace/Manifest kde-base/plasma-workspace/files/oldnotify.patch kde-base/plasma-workspace/files/plasma-workspace-4.10.1-noplasmalock.patch kde-base/plasma-workspace/metadata.xml kde-base/plasma-workspace/plasma-workspace-4.11.2-r2.ebuild X-VCS-Directories: kde-base/plasma-workspace/files/ kde-base/plasma-workspace/ X-VCS-Committer: dilfridge X-VCS-Committer-Name: Andreas Hüttel X-VCS-Revision: bc6ae00719b0b577cc0814f167a55211fec65003 X-VCS-Branch: master Date: Wed, 9 Oct 2013 12:02:13 +0000 (UTC) Precedence: bulk List-Post: List-Help: List-Unsubscribe: List-Subscribe: List-Id: Gentoo Linux mail X-BeenThere: gentoo-commits@lists.gentoo.org X-Archives-Salt: 576b2d94-f658-4de9-8190-ee5417028189 X-Archives-Hash: 4de44d07cb636e9983e51c0383cca87f commit: bc6ae00719b0b577cc0814f167a55211fec65003 Author: Andreas K. Huettel gentoo org> AuthorDate: Wed Oct 9 12:00:44 2013 +0000 Commit: Andreas Hüttel gentoo 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 @@ +- +- +- +- +- +- +- +- -1,-1 +- +- +- +- true +- +- +- +- true +- +- +- +- +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 +- * +- * 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 +- * +- * 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 * +- * Copyright 2011 Marco Martin * +- * * +- * 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 +- * +- * 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 * +- * Copyright 2011 Marco Martin * +- * * +- * 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 +- * +- * 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 +- * +- * 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 @@ +- +- +- feedsConfig +- +- +- +- 0 +- 0 +- 337 +- 181 +- +- +- +- +- +- +- &Application notifications: +- +- +- kcfg_ShowNotifications +- +- +- +- +- +- +- +- +- +- +- +- +- +- &File transfers and jobs: +- +- +- kcfg_ShowJobs +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- 75 +- true +- +- +- +- Choose which information to show +- +- +- +- +- +- +- kcfg_ShowNotifications +- kcfg_ShowJobs +- +- +- +- +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 * +- * Copyright 2011 Marco Martin * +- * * +- * 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) + "
" + 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 +- * +- * 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 * ++ * * ++ * 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 ++ ++#include ++#include ++#include ++ ++ ++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 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 * ++ * * ++ * 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 * ++ * * ++ * 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 ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++ ++ ++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 totalAmounts; ++ QMap processedAmounts; ++ ++ QList > 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("%2").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 Job::totalAmounts() const ++{ ++ return d->totalAmounts; ++} ++ ++void Job::setTotalAmounts(QMap amounts) ++{ ++ d->totalAmounts = amounts; ++ scheduleChangedSignal(); ++} ++ ++QMap Job::processedAmounts() const ++{ ++ return d->processedAmounts; ++} ++ ++void Job::setProcessedAmounts(QMap 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 > Job::labels() const ++{ ++ return d->labels; ++} ++ ++void Job::setLabels(QList > 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 * ++ * * ++ * 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 ++#include ++ ++#include ++ ++ ++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 totalAmounts() const; ++ ++ QMap processedAmounts() const; ++ ++ /** ++ * @return a list of pairs containing label names/values in the order they should be displayed. ++ */ ++ QList > 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 amount); ++ void setProcessedAmounts(QMap amount); ++ void setState(State state); ++ void setSuspendable(bool suspendable); ++ void setKillable(bool killable); ++ void setPercentage(uint percentage); ++ void setLabels(QList > 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 * ++ * * ++ * 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 ++#include ++ ++#include ++ ++ ++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 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 Notification::actions() const ++{ ++ return d->actions; ++} ++ ++ ++void Notification::setActions(const QHash &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 * ++ * * ++ * 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 ++#include ++#include ++ ++#include ++ ++ ++ ++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 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 &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 * ++ * Copyright (C) 2010 Marco Martin * ++ * * ++ * 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 ++ ++#include ++ ++#include ++ ++#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 tasks; ++ QList notifications; ++ QList 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 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 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 * ++ * Copyright (C) 2010 Marco Martin * ++ * * ++ * 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 ++ ++#include ++ ++#include ++#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 notifications() const; ++ ++ /** ++ * clear all notifications ++ */ ++ void clearNotifications(); ++ ++ /** ++ * @return a list of all known Job instances ++ **/ ++ QList 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 * ++ * * ++ * 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 * ++ * * ++ * 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 ++ ++ ++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 +- * +- * 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 +- * +- * 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 * ++ * * ++ * 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 ++ ++ ++ ++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 * ++ * * ++ * 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 * ++ * * ++ * 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 ++ ++#include ++#include ++#include ++ ++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 > labels; ++ while (data.contains(QString("label%1").arg(i))) { ++ QPair 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 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 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 * ++ * * ++ * 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 ++ ++ ++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 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 * ++ * * ++ * 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 ++ ++ ++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 * ++ * * ++ * 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 * ++ * * ++ * 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 ++#include ++#include ++ ++#include ++#include ++#include ++ ++ ++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(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 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 * ++ * Copyright (C) 2008 Dmitry Suzdalev * ++ * * ++ * 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 ++ ++#include ++ ++ ++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 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 * ++ * Copyright (C) 2010 Marco Martin * ++ * * ++ * 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 ++ ++#include ++#include ++#include ++#include // QWIDGETSIZE_MAX ++#include ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++ ++#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(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(); ++ painter->drawPixmap(iconRect, pix, pix.rect()); ++ } else if (m_fadeOutAnimation->state() == QAbstractAnimation::Running) { ++ QPixmap pix = m_fadeOutAnimation->property("currentPixmap").value(); ++ 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(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 += "
"; ++ } ++ } ++ ++ if (pausedJobs > 0) { ++ tooltipContent += i18np("%1 suspended job", "%1 suspended jobs", pausedJobs); ++ if (completedJobs > 0 || !m_manager->notifications().isEmpty()) { ++ tooltipContent += "
"; ++ } ++ } ++ ++ if (completedJobs > 0) { ++ tooltipContent += i18np("%1 completed job", "%1 completed jobs", completedJobs); ++ if (!m_manager->notifications().isEmpty()) { ++ tooltipContent += "
"; ++ } ++ } ++ ++ 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 * ++ * Copyright (C) 2010 Marco Martin * ++ * * ++ * 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 ++ ++#include ++ ++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 * ++ * * ++ * 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 ++#include ++ ++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(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 ++ ++#include ++#include ++#include ++#include ++ ++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 * ++ * * ++ * 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 ++#include ++#include ++#include ++ ++#include ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++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 processed = m_job.data()->processedAmounts(); ++ QMap 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 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(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 ++#include ++ ++#include ++#include ++#include ++#include ++ ++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; ++ QWeakPointerm_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 * ++ * * ++ * 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 ++#include ++ ++#include ++#include ++#include ++ ++#include ++#include ++ ++ ++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 * ++ * * ++ * 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 ++#include ++ ++ ++#include ++ ++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 ++ QListm_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 > m_notificationsForApp; ++ QHash m_appForNotification; ++ ++ QHashm_extenderItemsForNotification; ++ QHashm_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 * ++ * Copyright (C) 2008 Sebastian Sauer * ++ * Copyright (C) 2010 Marco Martin * ++ * * ++ * 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 ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++ ++#include ++#include ++#include ++#include ++ ++#include ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "config-notifications.h" ++#ifdef HAVE_LIBXSS // Idle detection. ++#include ++#include ++#include ++#include ++#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(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(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 * ++ * Copyright (C) 2010 Marco Martin * ++ * * ++ * 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 ++ ++#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 m_notificationInterface; ++ QDateTime m_lastActivity; ++ ++ JobTotalsWidget *m_jobSummaryWidget; ++ bool m_autoHidePopup; ++ ++ QWeakPointer 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 @@ ++ ++ ++ NotificationsConfig ++ ++ ++ ++ 0 ++ 0 ++ 213 ++ 236 ++ ++ ++ ++ ++ QFormLayout::ExpandingFieldsGrow ++ ++ ++ ++ ++ ++ 75 ++ true ++ ++ ++ ++ Pop Up Notices ++ ++ ++ ++ ++ ++ ++ Application notifications ++ ++ ++ showNotifications ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ File transfers and other jobs ++ ++ ++ showJobs ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ Qt::Vertical ++ ++ ++ QSizePolicy::Fixed ++ ++ ++ ++ 20 ++ 6 ++ ++ ++ ++ ++ ++ ++ ++ ++ 75 ++ true ++ ++ ++ ++ Popup ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ Automatically hide ++ ++ ++ autoHide ++ ++ ++ ++ ++ ++ ++ ++ +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 * ++ * * ++ * 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 ++#include ++ ++#include ++#include ++ ++#include ++#include ++ ++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(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 * ++ * * ++ * 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 ++ ++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 m_notifications; ++ QList m_notificationsToRemove; ++ QHash m_notificationWidgets; ++ QGraphicsLinearLayout *m_mainLayout; ++ int m_size; ++ bool m_underMouse; ++ QWeakPointer 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 * ++ * Copyright (C) 2008 Rob Scheepmaker * ++ * Copyright (C) 2008 Jason Stubbs * ++ * Copyright (C) 2010 Marco Martin * ++ * * ++ * 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 ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++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; ++ 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 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', "
"); ++ ++ 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("") + parsed + QLatin1String("")); ++ ++ 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 * ++ * Copyright (C) 2008 Rob Scheepmaker * ++ * Copyright (C) 2008 Jason Stubbs * ++ * Copyright (C) 2010 Marco Martin * ++ * * ++ * 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 ++ ++#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 * ++ * * ++ * 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 ++#include ++#include ++#include ++#include ++#include ++ ++#include ++ ++#include ++#include ++#include ++#include ++#include ++ ++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(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(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(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(event); ++ m_dragPos = me->pos().toPoint(); ++ } else if (watched == m_notificationStack && event->type() == QEvent::GraphicsSceneMouseMove) { ++ QGraphicsSceneMouseEvent *me = static_cast(event); ++ adjustPosition(me->screenPos() - m_dragPos); ++ } else if (watched == m_notificationStack && event->type() == QEvent::GraphicsSceneMouseRelease) { ++ QGraphicsSceneMouseEvent *me = static_cast(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 * ++ * * ++ * 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 ++ ++#include ++ ++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 @@ + + + +kde + + Enable JSON support via dev-libs/qjson + Enable Qalculate runner using sci-libs/libqalculate + + 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 +}