00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020 #include "devicenotifier.h"
00021 #include "notifierview.h"
00022
00023 #include <QPainter>
00024 #include <QColor>
00025 #include <QTreeView>
00026 #include <QApplication>
00027 #include <QStandardItemModel>
00028 #include <QGraphicsLinearLayout>
00029 #include <QtDBus/QDBusInterface>
00030 #include <QtDBus/QDBusReply>
00031
00032 #include <KConfigDialog>
00033 #include <KMessageBox>
00034 #include <KRun>
00035 #include <KStandardDirs>
00036 #include <KDesktopFile>
00037 #include <kdesktopfileactions.h>
00038 #include <KGlobalSettings>
00039 #include <KColorScheme>
00040
00041 #include <plasma/svg.h>
00042 #include <plasma/containment.h>
00043 #include <plasma/dialog.h>
00044 #include <plasma/animator.h>
00045 #include <plasma/delegate.h>
00046
00047
00048 #include <plasma/widgets/icon.h>
00049 #include <plasma/theme.h>
00050
00051 #include <solid/device.h>
00052 #include <solid/opticaldisc.h>
00053 #include <solid/storageaccess.h>
00054 #include <solid/opticaldrive.h>
00055
00056 using namespace Plasma;
00057 using namespace Notifier;
00058
00059 DeviceNotifier::DeviceNotifier(QObject *parent, const QVariantList &args)
00060 : Plasma::Applet(parent, args),
00061 m_solidEngine(0),
00062 m_hotplugModel(0),
00063 m_widget(0),
00064 m_icon(0),
00065 m_label(0),
00066 m_proxy(0),
00067 m_displayTime(0),
00068 m_numberItems(0),
00069 m_itemsValidity(0),
00070 m_timer(0)
00071 {
00072 setHasConfigurationInterface(true);
00073 int iconSize = IconSize(KIconLoader::Desktop);
00074 resize(iconSize, iconSize);
00075 }
00076
00077 void DeviceNotifier::init()
00078 {
00079 KConfigGroup cg = config();
00080 m_timer = new QTimer();
00081 m_displayTime = cg.readEntry("TimeDisplayed", 8);
00082 m_numberItems = cg.readEntry("NumberItems", 4);
00083 m_itemsValidity = cg.readEntry("ItemsValidity", 5);
00084
00085
00086 m_layout = new QGraphicsLinearLayout(this);
00087 m_layout->setContentsMargins(0, 0, 0, 0);
00088 m_layout->setSpacing(0);
00089 setLayout(m_layout);
00090
00091 m_solidEngine = dataEngine("hotplug");
00092 m_solidDeviceEngine = dataEngine("soliddevice");
00093 m_widget = new Dialog();
00094 m_widget->setFocusPolicy(Qt::NoFocus);
00095 m_widget->setWindowFlags(Qt::Popup);
00096
00097 QVBoxLayout *l_layout = new QVBoxLayout(m_widget);
00098 l_layout->setSpacing(0);
00099 l_layout->setMargin(0);
00100
00101 m_hotplugModel = new QStandardItemModel(this);
00102
00103 m_label = new QLabel(m_widget);
00104 updateColors();
00105 QLabel *icon = new QLabel(m_widget);
00106 icon->setPixmap(KIcon("emblem-mounted").pixmap(KIconLoader::SizeMedium, KIconLoader::SizeMedium));
00107
00108 QHBoxLayout *l_layout2 = new QHBoxLayout(m_widget);
00109 l_layout2->setSpacing(0);
00110 l_layout2->setMargin(0);
00111
00112 l_layout2->addWidget(icon);
00113 l_layout2->addWidget(m_label);
00114
00115 m_notifierView= new NotifierView(m_widget);
00116 m_notifierView->setModel(m_hotplugModel);
00117 Plasma::Delegate *delegate = new Delegate(this);
00118
00119 delegate->setRoleMapping(Plasma::Delegate::SubTitleRole, ActionRole);
00120 delegate->setRoleMapping(Plasma::Delegate::ColumnTypeRole, ScopeRole);
00121 delegate->setRoleMapping(Plasma::Delegate::SubTitleMandatoryRole, SubTitleMandatoryRole);
00122 m_notifierView->setItemDelegate(delegate);
00123
00124 l_layout->addLayout(l_layout2);
00125 l_layout->addWidget(m_notifierView);
00126 m_widget->setLayout(l_layout);
00127
00128 m_widget->adjustSize();
00129
00130
00131 isNotificationEnabled = false;
00132 foreach (const QString &source, m_solidEngine->sources()) {
00133 onSourceAdded(source);
00134 }
00135 isNotificationEnabled = true;
00136
00137
00138 connect(m_solidEngine, SIGNAL(sourceAdded(const QString&)),
00139 this, SLOT(onSourceAdded(const QString&)));
00140 connect(m_solidEngine, SIGNAL(sourceRemoved(const QString&)),
00141 this, SLOT(onSourceRemoved(const QString&)));
00142
00143 connect(m_notifierView, SIGNAL(clicked(const QModelIndex&)), this, SLOT(slotOnItemClicked(const QModelIndex&)));
00144 connect(m_timer, SIGNAL(timeout()), this, SLOT(onTimerExpired()));
00145 connect(Plasma::Theme::defaultTheme(), SIGNAL(themeChanged()), this, SLOT(updateColors()));
00146 }
00147
00148
00149 void DeviceNotifier::initSysTray()
00150 {
00151 if (m_icon) {
00152 return;
00153 }
00154
00155
00156 QList<Solid::Device> list = Solid::Device::allDevices();
00157
00158 if (list.size() > 0) {
00159 Solid::Device device=list[0];
00160
00161 while (device.parent().isValid()) {
00162 device = device.parent();
00163 }
00164 m_icon = new Plasma::Icon(KIcon(device.icon()), QString(), this);
00165 } else {
00166
00167 m_icon = new Plasma::Icon(KIcon("computer"), QString(), this);
00168 }
00169 connect(m_icon, SIGNAL(clicked()), this, SLOT(onClickNotifier()));
00170
00171 setAspectRatioMode(Plasma::ConstrainedSquare);
00172
00173 m_layout->addItem(m_icon);
00174 }
00175
00176 DeviceNotifier::~DeviceNotifier()
00177 {
00178 delete m_widget;
00179 delete m_hotplugModel;
00180 delete m_timer;
00181 }
00182
00183 void DeviceNotifier::constraintsEvent(Plasma::Constraints constraints)
00184 {
00185
00186 setBackgroundHints(NoBackground);
00187 bool isSizeConstrained = formFactor() != Plasma::Planar && formFactor() != Plasma::MediaCenter;
00188
00189 if (constraints & FormFactorConstraint) {
00190 if (isSizeConstrained) {
00191 if (m_proxy) {
00192 m_proxy->setWidget(0);
00193 m_layout->removeItem(m_proxy);
00194 delete m_proxy;
00195 m_proxy = 0;
00196 }
00197
00198 initSysTray();
00199 } else {
00200 delete m_icon;
00201 m_icon = 0;
00202
00203 m_proxy = new QGraphicsProxyWidget(this);
00204 m_proxy->setWidget(m_widget);
00205 m_proxy->show();
00206 m_layout->addItem(m_proxy);
00207 }
00208 }
00209
00210 }
00211
00212 void DeviceNotifier::paintInterface(QPainter *p, const QStyleOptionGraphicsItem *option, const QRect &rect)
00213 {
00214 Applet::paintInterface(p,option,rect);
00215 }
00216
00217 void DeviceNotifier::dataUpdated(const QString &source, Plasma::DataEngine::Data data)
00218 {
00219 if (data.size() > 0) {
00220
00221 if (!data["predicateFiles"].isNull()) {
00222 int nb_actions = 0;
00223 QString last_action_label;
00224 foreach (const QString &desktop, data["predicateFiles"].toStringList()) {
00225 QString filePath = KStandardDirs::locate("data", "solid/actions/" + desktop);
00226 QList<KServiceAction> services = KDesktopFileActions::userDefinedServices(filePath, true);
00227 nb_actions += services.size();
00228 if (services.size() > 0) {
00229 last_action_label = QString(services[0].text());
00230 }
00231 }
00232
00233 QModelIndex index = indexForUdi(source);
00234
00235 if (index.isValid()) {
00236 m_hotplugModel->setData(index, data["predicateFiles"], PredicateFilesRole);
00237 m_hotplugModel->setData(index, data["text"], Qt::DisplayRole);
00238
00239
00240 m_hotplugModel->setData(index, data["icon"], IconNameRole);
00241
00242 m_hotplugModel->setData(index, KIcon(data["icon"].toString()), Qt::DecorationRole);
00243
00244 if (nb_actions > 1) {
00245 QString s = i18np("1 action for this device",
00246 "%1 actions for this device",
00247 nb_actions);
00248 m_hotplugModel->setData(index, s, ActionRole);
00249 } else {
00250 m_hotplugModel->setData(index,last_action_label, ActionRole);
00251 }
00252 if (m_icon && isNotificationEnabled) {
00253 m_widget->move(popupPosition(m_widget->sizeHint()));
00254 m_widget->show();
00255 m_timer->start(m_displayTime*1000);
00256 }
00257 }
00258
00259 } else {
00260 kDebug() << "DeviceNotifier::solidDeviceEngine updated" << source;
00261 QModelIndex index = indexForUdi(source);
00262 if (index.isValid()) {
00263 QModelIndex actionIndex = m_hotplugModel->index(index.row(), 1, QModelIndex());
00264
00265 if (data["Device Types"].toStringList().contains("Storage Access")) {
00266 if (data["Accessible"].toBool() == true) {
00267 m_hotplugModel->setData(actionIndex, KIcon("media-eject"), Qt::DecorationRole);
00268
00269
00270 QStringList overlays;
00271 overlays << "emblem-mounted";
00272 m_hotplugModel->setData(index, KIcon(index.data(IconNameRole).toString(), NULL, overlays), Qt::DecorationRole);
00273
00274 } else if (data["Device Types"].toStringList().contains("OpticalDisc")) {
00275 m_hotplugModel->setData(actionIndex, KIcon("media-eject"), Qt::DecorationRole);
00276
00277 m_hotplugModel->setData(index, KIcon(index.data(IconNameRole).toString()), Qt::DecorationRole);
00278 } else {
00279 m_hotplugModel->setData(actionIndex, KIcon(), Qt::DecorationRole);
00280
00281
00282 m_hotplugModel->setData(index, KIcon(index.data(IconNameRole).toString()), Qt::DecorationRole);
00283 }
00284 }
00285 }
00286
00287 }
00288 }
00289 }
00290
00291 void DeviceNotifier::onSourceAdded(const QString &name)
00292 {
00293 kDebug() << "DeviceNotifier:: source added" << name;
00294 if (m_hotplugModel->rowCount() == m_numberItems && m_numberItems != 0) {
00295 QModelIndex index = m_hotplugModel->index(m_hotplugModel->rowCount() - 1, 0);
00296 QString itemUdi = m_hotplugModel->data(index, SolidUdiRole).toString();
00297
00298
00299 m_solidDeviceEngine->disconnectSource(itemUdi, this);
00300 m_solidEngine->disconnectSource(itemUdi, this);
00301 m_hotplugModel->removeRow(m_hotplugModel->rowCount() - 1);
00302 }
00303 QStandardItem *item = new QStandardItem();
00304 item->setData(name, SolidUdiRole);
00305 item->setData(Plasma::Delegate::MainColumn, ScopeRole);
00306 item->setData(false, SubTitleMandatoryRole);
00307
00308 m_hotplugModel->insertRow(0, item);
00309 m_solidEngine->connectSource(name, this);
00310
00311 m_solidDeviceEngine->connectSource(name, this);
00312
00313
00314 QStandardItem *actionItem = new QStandardItem();
00315 actionItem->setData(name, SolidUdiRole);
00316 actionItem->setData(Plasma::Delegate::SecondaryActionColumn, ScopeRole);
00317
00318 m_hotplugModel->setItem(0, 1, actionItem);
00319
00320 }
00321
00322 void DeviceNotifier::onSourceRemoved(const QString &name)
00323 {
00324 m_solidEngine->disconnectSource(name, this);
00325 m_solidDeviceEngine->disconnectSource(name, this);
00326
00327 QModelIndex index = indexForUdi(name);
00328 if (index.isValid()) {
00329 m_hotplugModel->removeRow(index.row());
00330 }
00331
00332 if (m_icon && m_hotplugModel->rowCount() == 0) {
00333 m_widget->hide();
00334 }
00335 }
00336
00337 QModelIndex DeviceNotifier::indexForUdi(const QString &udi) const
00338 {
00339 int rowCount = m_hotplugModel->rowCount();
00340 for (int i=0; i < rowCount; ++i) {
00341 QModelIndex index = m_hotplugModel->index(i, 0);
00342 QString itemUdi = m_hotplugModel->data(index, SolidUdiRole).toString();
00343 if (itemUdi == udi) {
00344 return index;
00345 }
00346 }
00347
00348 kDebug() << "We should not be here!";
00349 return QModelIndex();
00350 }
00351
00352 void DeviceNotifier::onClickNotifier()
00353 {
00354 if (m_widget->isVisible()) {
00355 m_widget->hide();
00356 } else {
00357 m_widget->move(popupPosition(m_widget->sizeHint()));
00358 m_widget->show();
00359 }
00360 }
00361
00362 void DeviceNotifier::createConfigurationInterface(KConfigDialog *parent)
00363 {
00364 QWidget *widget = new QWidget;
00365 ui.setupUi(widget);
00366 parent->setButtons(KDialog::Ok | KDialog::Cancel | KDialog::Apply);
00367 connect(parent, SIGNAL(applyClicked()), this, SLOT(configAccepted()));
00368 connect(parent, SIGNAL(okClicked()), this, SLOT(configAccepted()));
00369 parent->addPage(widget, parent->windowTitle(), icon());
00370
00371 ui.spinTime->setValue(m_displayTime);
00372 ui.spinItems->setValue(m_numberItems);
00373 ui.spinTimeItems->setValue(m_itemsValidity);
00374 }
00375
00376 void DeviceNotifier::configAccepted()
00377 {
00378 kDebug() << "DeviceNotifier:: Config Accepted with params" << ui.spinTime->value() \
00379 << "," << ui.spinItems->value() \
00380 << "," << ui.spinTimeItems->value();
00381 m_displayTime = ui.spinTime->value();
00382 m_numberItems = ui.spinItems->value();
00383 m_itemsValidity = ui.spinTimeItems->value();
00384 KConfigGroup cg = config();
00385 cg.writeEntry("TimeDisplayed", m_displayTime);
00386 cg.writeEntry("NumberItems", m_numberItems);
00387 cg.writeEntry("ItemsValidity", m_itemsValidity);
00388 emit configNeedsSaving();
00389 }
00390
00391 void DeviceNotifier::slotOnItemClicked(const QModelIndex &index)
00392 {
00393 kDebug() << index;
00394 if (m_icon) {
00395 m_timer->stop();
00396 }
00397
00398 QString udi = QString(m_hotplugModel->data(index, SolidUdiRole).toString());
00399
00400
00401 if (index.data(ScopeRole).toInt() == Plasma::Delegate::SecondaryActionColumn) {
00402 Solid::Device device(udi);
00403
00404 if (device.is<Solid::OpticalDisc>()) {
00405 Solid::OpticalDrive *drive = device.parent().as<Solid::OpticalDrive>();
00406
00407 connect(drive, SIGNAL(ejectDone(Solid::ErrorType, QVariant, const QString &)),
00408 this, SLOT(storageEjectDone(Solid::ErrorType, QVariant)));
00409 drive->eject();
00410 } else if (device.is<Solid::StorageVolume>()) {
00411 Solid::StorageAccess *access = device.as<Solid::StorageAccess>();
00412
00413 if (access->isAccessible()) {
00414 connect(access, SIGNAL(teardownDone(Solid::ErrorType, QVariant, const QString &)),
00415 this, SLOT(storageTeardownDone(Solid::ErrorType, QVariant)));
00416 access->teardown();
00417 }
00418 }
00419
00420 } else {
00421 if (m_icon) {
00422 m_widget->hide();
00423 }
00424 QStringList desktop_files = m_hotplugModel->data(index, PredicateFilesRole).toStringList();
00425
00426 kDebug() << "DeviceNotifier:: call Solid Ui Server with params :" << udi \
00427 << "," << desktop_files;
00428 QDBusInterface soliduiserver("org.kde.kded", "/modules/soliduiserver", "org.kde.SolidUiServer");
00429 QDBusReply<void> reply = soliduiserver.call("showActionsDialog", udi, desktop_files);
00430 }
00431 }
00432
00433 void DeviceNotifier::onTimerExpired()
00434 {
00435 if (m_icon) {
00436 m_timer->stop();
00437 m_widget->hide();
00438 }
00439 }
00440
00441 void DeviceNotifier::storageTeardownDone(Solid::ErrorType error, QVariant errorData)
00442 {
00443 if (error && errorData.isValid()) {
00444 KMessageBox::error(0, i18n("Cannot unmount the device.\nOne or more files on this device are open within an application."), QString());
00445 } else if (m_icon) {
00446 m_icon->setIcon(KIcon("dialog-ok"));
00447 QTimer::singleShot(2000, this, SLOT(resetIcon()));
00448 update();
00449 }
00450
00451
00452 disconnect(sender(), SIGNAL(teardownDone(Solid::ErrorType, QVariant, const QString &)),
00453 this, SLOT(storageTeardownDone(Solid::ErrorType, QVariant)));
00454 }
00455
00456 void DeviceNotifier::storageEjectDone(Solid::ErrorType error, QVariant errorData)
00457 {
00458 if (error && errorData.isValid()) {
00459 KMessageBox::error(0, i18n("Cannot eject the disc.\nOne or more files on this disc are open within an application."), QString());
00460 } else if (m_icon) {
00461 m_icon->setIcon(KIcon("dialog-ok"));
00462 QTimer::singleShot(2000, this, SLOT(resetIcon()));
00463 update();
00464 }
00465
00466
00467 disconnect(sender(), SIGNAL(ejectDone(Solid::ErrorType, QVariant, const QString &)),
00468 this, SLOT(storageEjectDone(Solid::ErrorType, QVariant)));
00469 }
00470
00471 void DeviceNotifier::resetIcon()
00472 {
00473 if (m_icon) {
00474
00475 QList<Solid::Device> list = Solid::Device::allDevices();
00476
00477 if (list.size() > 0) {
00478 Solid::Device device=list[0];
00479
00480 while (device.parent().isValid()) {
00481 device = device.parent();
00482 }
00483 m_icon->setIcon(KIcon(device.icon()));
00484 } else {
00485
00486 m_icon->setIcon(KIcon("computer"));
00487 }
00488 update();
00489 }
00490 }
00491
00492 void DeviceNotifier::updateColors()
00493 {
00494 KColorScheme colorTheme = KColorScheme(QPalette::Active, KColorScheme::View, Plasma::Theme::defaultTheme()->colorScheme());
00495 m_label->setText(i18n("<font color=\"%1\">Devices recently plugged in:</font>",
00496 colorTheme.foreground(KColorScheme::NormalText).color().name()));
00497 }
00498
00499 #include "devicenotifier.moc"