1 | /* |
2 | Copyright (C) 2010 by Jacopo De Simoi <wilderkde@gmail.com> |
3 | |
4 | This program is free software; you can redistribute it and/or modify |
5 | it under the terms of the GNU General Public License as published by |
6 | the Free Software Foundation; either version 2, or (at your option) |
7 | any later version. |
8 | |
9 | This program is distributed in the hope that it will be useful, |
10 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
12 | GNU General Public License for more details. |
13 | |
14 | You should have received a copy of the GNU General Public License |
15 | along with this program; if not, write to the Free Software |
16 | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
17 | |
18 | */ |
19 | |
20 | #include "ksolidnotify.h" |
21 | #include "knotify.h" |
22 | |
23 | //solid specific includes |
24 | #include <Solid/DeviceNotifier> |
25 | #include <Solid/Device> |
26 | #include <Solid/DeviceInterface> |
27 | #include <Solid/StorageDrive> |
28 | #include <Solid/StorageVolume> |
29 | #include <Solid/StorageAccess> |
30 | #include <Solid/OpticalDrive> |
31 | #include <Solid/OpticalDisc> |
32 | #include <Solid/PortableMediaPlayer> |
33 | #include <Solid/Predicate> |
34 | |
35 | #include <QDBusConnection> |
36 | #include <QDBusConnectionInterface> |
37 | #include <QDBusServiceWatcher> |
38 | |
39 | #include <KLocale> |
40 | |
41 | static const char dbusDeviceNotificationsName[] = "org.kde.DeviceNotifications" ; |
42 | static const char dbusDeviceNotificationsPath[] = "/org/kde/DeviceNotifications" ; |
43 | |
44 | |
45 | KSolidNotify::KSolidNotify(KNotify* parent): |
46 | QObject(parent), |
47 | m_kNotify(parent), |
48 | m_dbusServiceExists(false) |
49 | { |
50 | Solid::Predicate p(Solid::DeviceInterface::StorageAccess); |
51 | p |= Solid::Predicate(Solid::DeviceInterface::OpticalDrive); |
52 | p |= Solid::Predicate(Solid::DeviceInterface::PortableMediaPlayer); |
53 | QList<Solid::Device> devices = Solid::Device::listFromQuery(p); |
54 | foreach (const Solid::Device &dev, devices) |
55 | { |
56 | m_devices.insert(dev.udi(), dev); |
57 | connectSignals(&m_devices[dev.udi()]); |
58 | } |
59 | |
60 | connect(Solid::DeviceNotifier::instance(), SIGNAL(deviceAdded(const QString &)), |
61 | this, SLOT(onDeviceAdded(const QString &))); |
62 | connect(Solid::DeviceNotifier::instance(), SIGNAL(deviceRemoved(const QString &)), |
63 | this, SLOT(onDeviceRemoved(const QString &))); |
64 | |
65 | // check if service already exists on plugin instantiation |
66 | QDBusConnectionInterface* interface = QDBusConnection::sessionBus().interface(); |
67 | m_dbusServiceExists = interface && interface->isServiceRegistered(dbusDeviceNotificationsName); |
68 | |
69 | if( m_dbusServiceExists ) |
70 | slotServiceOwnerChanged(dbusDeviceNotificationsName, QString(), "_" ); //connect signals |
71 | |
72 | // to catch register/unregister events from service in runtime |
73 | QDBusServiceWatcher *watcher = new QDBusServiceWatcher(this); |
74 | watcher->setConnection(QDBusConnection::sessionBus()); |
75 | watcher->setWatchMode(QDBusServiceWatcher::WatchForOwnerChange); |
76 | watcher->addWatchedService(dbusDeviceNotificationsName); |
77 | connect(watcher, SIGNAL(serviceOwnerChanged(const QString&, const QString&, const QString&)), |
78 | SLOT(slotServiceOwnerChanged(const QString&, const QString&, const QString&))); |
79 | } |
80 | |
81 | void KSolidNotify::onDeviceAdded(const QString &udi) |
82 | { |
83 | Solid::Device device(udi); |
84 | m_devices.insert(udi, device); |
85 | connectSignals(&m_devices[udi]); |
86 | } |
87 | |
88 | void KSolidNotify::onDeviceRemoved(const QString &udi) |
89 | { |
90 | if (m_devices[udi].is<Solid::StorageVolume>()) |
91 | { |
92 | Solid::StorageAccess *access = m_devices[udi].as<Solid::StorageAccess>(); |
93 | if (access) |
94 | disconnect(access, 0, this, 0); |
95 | } |
96 | m_devices.remove(udi); |
97 | } |
98 | |
99 | bool KSolidNotify::isSafelyRemovable(const QString &udi) |
100 | { |
101 | Solid::Device parent = m_devices[udi].parent(); |
102 | if (parent.is<Solid::StorageDrive>()) |
103 | { |
104 | Solid::StorageDrive *drive = parent.as<Solid::StorageDrive>(); |
105 | return (!drive->isInUse() && (drive->isHotpluggable() || drive->isRemovable())); |
106 | } |
107 | Solid::StorageAccess* access = m_devices[udi].as<Solid::StorageAccess>(); |
108 | if (access) { |
109 | return !m_devices[udi].as<Solid::StorageAccess>()->isAccessible(); |
110 | } else { |
111 | // If this check fails, the device has been already physically |
112 | // ejected, so no need to say that it is safe to remove it |
113 | return false; |
114 | } |
115 | } |
116 | |
117 | void KSolidNotify::connectSignals(Solid::Device* device) |
118 | { |
119 | Solid::StorageAccess *access = device->as<Solid::StorageAccess>(); |
120 | if (access) |
121 | { |
122 | connect(access, SIGNAL(teardownDone(Solid::ErrorType, QVariant, const QString &)), |
123 | this, SLOT(storageTeardownDone(Solid::ErrorType, QVariant , const QString &))); |
124 | connect(access, SIGNAL(setupDone(Solid::ErrorType, QVariant, const QString &)), |
125 | this, SLOT(storageSetupDone(Solid::ErrorType, QVariant , const QString &))); |
126 | } |
127 | if (device->is<Solid::OpticalDisc>()) |
128 | { |
129 | Solid::OpticalDrive *drive = device->parent().as<Solid::OpticalDrive>(); |
130 | connect(drive, SIGNAL(ejectDone(Solid::ErrorType, QVariant, const QString &)), |
131 | this, SLOT(storageEjectDone(Solid::ErrorType, QVariant , const QString &))); |
132 | } |
133 | } |
134 | |
135 | void KSolidNotify::notifySolidEvent(QString event, Solid::ErrorType error, QVariant errorData, const QString & udi, const QString & errorMessage) |
136 | { |
137 | ContextList context; |
138 | if (m_dbusServiceExists) |
139 | { |
140 | KNotifyConfig mountConfig("hardwarenotifications" , ContextList(), event); |
141 | if (mountConfig.readEntry("Action" ).split('|').contains("Popup" )) |
142 | { |
143 | QDBusMessage m = QDBusMessage::createMethodCall( dbusDeviceNotificationsName, dbusDeviceNotificationsPath, dbusDeviceNotificationsName, "notify" ); |
144 | m << error << errorMessage << errorData.toString().simplified() << udi; |
145 | QDBusConnection::sessionBus().call(m); |
146 | } |
147 | context << QPair<QString, QString>("devnotifier" , "present" ); |
148 | } |
149 | |
150 | m_kNotify->event(event, "hardwarenotifications" , context, i18n("Devices notification" ), errorMessage, KNotifyImage(), QStringList(), -1); |
151 | |
152 | } |
153 | |
154 | void KSolidNotify::storageSetupDone(Solid::ErrorType error, QVariant errorData, const QString &udi) |
155 | { |
156 | if (error) |
157 | { |
158 | Solid::Device device(udi); |
159 | QString errorMessage = i18n("Could not mount the following device: %1" , device.description()); |
160 | notifySolidEvent("mounterror" , error, errorData, udi, errorMessage); |
161 | } |
162 | } |
163 | |
164 | void KSolidNotify::storageTeardownDone(Solid::ErrorType error, QVariant errorData, const QString &udi) |
165 | { |
166 | if (error) |
167 | { |
168 | Solid::Device device(udi); |
169 | QString errorMessage = i18n("Could not unmount the following device: %1\nOne or more files on this device are open within an application " , device.description()); |
170 | notifySolidEvent("mounterror" , error, errorData, udi, errorMessage); |
171 | } else if (isSafelyRemovable(udi)) |
172 | { |
173 | Solid::Device device(udi); |
174 | notifySolidEvent("safetoremove" , error, errorData, udi, i18nc("The term \"remove\" here means \"physically disconnect the device from the computer\", whereas \"safely\" means \"without risk of data loss\"" , "The following device can now be safely removed: %1" , device.description())); |
175 | } |
176 | } |
177 | |
178 | void KSolidNotify::storageEjectDone(Solid::ErrorType error, QVariant errorData, const QString &udi) |
179 | { |
180 | if (error) |
181 | { |
182 | QString discUdi; |
183 | foreach (Solid::Device device, m_devices) { |
184 | if (device.parentUdi() == udi) { |
185 | discUdi = device.udi(); |
186 | } |
187 | } |
188 | |
189 | if (discUdi.isNull()) { |
190 | //This should not happen, bail out |
191 | return; |
192 | } |
193 | |
194 | Solid::Device discDevice(discUdi); |
195 | QString errorMessage = i18n("Could not eject the following device: %1\nOne or more files on this device are open within an application " , discDevice.description()); |
196 | notifySolidEvent("mounterror" , error, errorData, udi, errorMessage); |
197 | } else if (isSafelyRemovable(udi)) |
198 | { |
199 | Solid::Device device(udi); |
200 | notifySolidEvent("safetoremove" , error, errorData, udi, i18n("The following device can now be safely removed: %1" , device.description())); |
201 | } |
202 | } |
203 | |
204 | void KSolidNotify::slotServiceOwnerChanged( const QString & serviceName, const QString & oldOwner, const QString & newOwner ) |
205 | { |
206 | Q_UNUSED(serviceName); |
207 | if (newOwner.isEmpty()) |
208 | m_dbusServiceExists = false; |
209 | else if (oldOwner.isEmpty()) |
210 | m_dbusServiceExists = true; |
211 | } |
212 | |
213 | |
214 | #include "ksolidnotify.moc" |
215 | |