/* Copyright (c) 2007 Paolo Capriotti Copyright (C) 2007 Ivan Cukic Copyright (c) 2008 by Petri Damsten 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. */ #include "backgrounddialog.h" #include "mouseplugins.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "kworkspace/screenpreviewwidget.h" #include "ui_BackgroundDialog.h" // From kcategorizeditemsviewdelegate by Ivan Cukic #define EMBLEM_ICON_SIZE 16 #define UNIVERSAL_PADDING 6 #define FADE_LENGTH 32 #define MAIN_ICON_SIZE 48 class AppletDelegate : public QAbstractItemDelegate { public: enum { DescriptionRole = Qt::UserRole + 1, PluginNameRole, ModeRole }; AppletDelegate(QObject * parent = 0); virtual void paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const; virtual QSize sizeHint(const QStyleOptionViewItem& option, const QModelIndex& index) const; int calcItemHeight(const QStyleOptionViewItem& option) const; }; AppletDelegate::AppletDelegate(QObject* parent) : QAbstractItemDelegate(parent) { } void AppletDelegate::paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const { QStyleOptionViewItemV4 opt(option); QStyle *style = opt.widget ? opt.widget->style() : QApplication::style(); style->drawPrimitive(QStyle::PE_PanelItemViewItem, &opt, painter, opt.widget); const int left = option.rect.left(); const int top = option.rect.top(); const int width = option.rect.width(); const int height = calcItemHeight(option); bool leftToRight = (painter->layoutDirection() == Qt::LeftToRight); QIcon::Mode iconMode = QIcon::Normal; const QColor foregroundColor = (option.state.testFlag(QStyle::State_Selected)) ? option.palette.color(QPalette::HighlightedText) : option.palette.color(QPalette::Text); // Borrowed from Dolphin for consistency and beauty. // For the color of the additional info the inactive text color // is not used as this might lead to unreadable text for some color schemes. Instead // the text color is slightly mixed with the background color. const QColor textColor = option.palette.text().color(); const QColor baseColor = option.palette.base().color(); const int p1 = 70; const int p2 = 100 - p1; const QColor detailsColor = QColor((textColor.red() * p1 + baseColor.red() * p2) / 100, (textColor.green() * p1 + baseColor.green() * p2) / 100, (textColor.blue() * p1 + baseColor.blue() * p2) / 100); QPixmap pixmap(width, height); pixmap.fill(Qt::transparent); QPainter p(&pixmap); p.translate(-option.rect.topLeft()); QLinearGradient gradient; QString title = index.model()->data(index, Qt::DisplayRole).toString(); QString description = index.model()->data(index, AppletDelegate::DescriptionRole).toString(); // Painting // Text int textInner = 2 * UNIVERSAL_PADDING + MAIN_ICON_SIZE; p.setPen(foregroundColor); p.drawText(left + (leftToRight ? textInner : 0), top, width - textInner, height / 2, Qt::AlignBottom | Qt::AlignLeft, title); p.setPen(detailsColor); p.drawText(left + (leftToRight ? textInner : 0), top + height / 2, width - textInner, height / 2, Qt::AlignTop | Qt::AlignLeft, description); // Main icon const QIcon& icon = qVariantValue(index.model()->data(index, Qt::DecorationRole)); icon.paint(&p, leftToRight ? left + UNIVERSAL_PADDING : left + width - UNIVERSAL_PADDING - MAIN_ICON_SIZE, top + UNIVERSAL_PADDING, MAIN_ICON_SIZE, MAIN_ICON_SIZE, Qt::AlignCenter, iconMode); // Gradient part of the background - fading of the text at the end if (leftToRight) { gradient = QLinearGradient(left + width - UNIVERSAL_PADDING - FADE_LENGTH, 0, left + width - UNIVERSAL_PADDING, 0); gradient.setColorAt(0, Qt::white); gradient.setColorAt(1, Qt::transparent); } else { gradient = QLinearGradient(left + UNIVERSAL_PADDING, 0, left + UNIVERSAL_PADDING + FADE_LENGTH, 0); gradient.setColorAt(0, Qt::transparent); gradient.setColorAt(1, Qt::white); } QRect paintRect = option.rect; p.setCompositionMode(QPainter::CompositionMode_DestinationIn); p.fillRect(paintRect, gradient); if (leftToRight) { gradient.setStart(left + width - FADE_LENGTH, 0); gradient.setFinalStop(left + width, 0); } else { gradient.setStart(left + UNIVERSAL_PADDING, 0); gradient.setFinalStop(left + UNIVERSAL_PADDING + FADE_LENGTH, 0); } paintRect.setHeight(UNIVERSAL_PADDING + MAIN_ICON_SIZE / 2); p.fillRect(paintRect, gradient); p.end(); painter->drawPixmap(option.rect.topLeft(), pixmap); } int AppletDelegate::calcItemHeight(const QStyleOptionViewItem& option) const { // Painting main column int textHeight = QFontInfo(option.font).pixelSize() * 2; //kDebug() << textHeight << qMax(textHeight, MAIN_ICON_SIZE) + 2 * UNIVERSAL_PADDING; return qMax(textHeight, MAIN_ICON_SIZE) + 2 * UNIVERSAL_PADDING; } QSize AppletDelegate::sizeHint(const QStyleOptionViewItem& option, const QModelIndex& index) const { Q_UNUSED(index) return QSize(200, calcItemHeight(option)); } void WallpaperWidget::settingsChanged(bool isModified) { emit modified(isModified); } class BackgroundDialogPrivate { public: BackgroundDialogPrivate(BackgroundDialog* dialog, Plasma::Containment* c, Plasma::View* v) : q(dialog), containmentModel(0), wallpaperModel(0), wallpaper(0), view(v), containment(c), preview(0), modified(false) { } ~BackgroundDialogPrivate() { } BackgroundDialog *q; Ui::BackgroundDialog backgroundDialogUi; QStandardItemModel* containmentModel; QStandardItemModel* wallpaperModel; Plasma::Wallpaper* wallpaper; Plasma::View* view; QWeakPointer containment; ScreenPreviewWidget* preview; KPageWidgetItem *activityItem; KPageWidgetItem *appearanceItem; KPageWidgetItem *mouseItem; bool modified; void createConfigurationInterfaceForPackage() { const QString uiFile = containment.data()->package()->filePath("mainconfigui"); if (uiFile.isEmpty()) { kWarning() << "No ui file found for containment"; return; } Plasma::ConfigLoader *configScheme = containment.data()->configScheme(); if (!configScheme) { kWarning() << "No configuration scheme found for containment"; return; } QFile f(uiFile); QUiLoader loader; QWidget *widget = loader.load(&f); if (widget) { q->addPage(widget, configScheme, i18n("Settings"), containment.data()->icon(), i18n("%1 Settings", containment.data()->name())); } else { kWarning() << "Failed to load widget from" << uiFile; } } }; BackgroundDialog::BackgroundDialog(const QSize& res, Plasma::Containment *c, Plasma::View* view, QWidget* parent, const QString &id, KConfigSkeleton *s) : KConfigDialog(parent, id, s), d(new BackgroundDialogPrivate(this, c, view)) { setWindowIcon(KIcon("preferences-desktop-wallpaper")); setCaption(i18n("Desktop Settings")); setButtons(Ok | Cancel | Apply); QWidget *main= new QWidget(this); d->backgroundDialogUi.setupUi(main); d->appearanceItem = addPage(main, i18n("View"), "preferences-desktop-wallpaper"); qreal previewRatio = (qreal)res.width() / (qreal)res.height(); QSize monitorSize(200, int(200 * previewRatio)); d->backgroundDialogUi.monitor->setFixedSize(200, 200); d->backgroundDialogUi.monitor->setText(QString()); d->backgroundDialogUi.monitor->setWhatsThis(i18n( "This picture of a monitor contains a preview of " "what the current settings will look like on your desktop.")); d->preview = new ScreenPreviewWidget(d->backgroundDialogUi.monitor); d->preview->setRatio(previewRatio); d->preview->resize(200, 200); connect(this, SIGNAL(finished(int)), this, SLOT(cleanup())); connect(this, SIGNAL(okClicked()), this, SLOT(saveConfig())); connect(this, SIGNAL(applyClicked()), this, SLOT(saveConfig())); if (d->containment) { connect(d->containment.data(), SIGNAL(destroyed()), this, SLOT(close())); connect(d->containment.data(), SIGNAL(immutabilityChanged(Plasma::ImmutabilityType)), this, SLOT(containmentImmutabilityChanged(Plasma::ImmutabilityType))); } d->containmentModel = new QStandardItemModel(this); d->backgroundDialogUi.containmentComboBox->setModel(d->containmentModel); d->backgroundDialogUi.containmentComboBox->setItemDelegate(new AppletDelegate()); MousePlugins *m = new MousePlugins(d->containment.data(), this); connect(m, SIGNAL(modified(bool)), this, SLOT(settingsModified(bool))); d->mouseItem = addPage(m, i18n("Mouse Actions"), "input-mouse"); if (d->containment && d->containment.data()->hasConfigurationInterface()) { if (d->containment.data()->package()) { d->createConfigurationInterfaceForPackage(); } else { d->containment.data()->createConfigurationInterface(this); } connect(this, SIGNAL(applyClicked()), d->containment.data(), SLOT(configDialogFinished())); connect(this, SIGNAL(okClicked()), d->containment.data(), SLOT(configDialogFinished())); } d->wallpaperModel = new QStandardItemModel(this); d->backgroundDialogUi.wallpaperMode->setModel(d->wallpaperModel); d->backgroundDialogUi.wallpaperMode->setItemDelegate(new AppletDelegate()); QSize dialogSize = QSize(650, 720).expandedTo(sizeHint()); if (d->containment) { const int screen = d->containment.data()->screen(); dialogSize = dialogSize.boundedTo(qApp->desktop()->availableGeometry(screen).size()); } setInitialSize(dialogSize); KConfigGroup cg(KGlobal::config(), "BackgroundConfigDialog"); restoreDialogSize(cg); reloadConfig(); connect(d->backgroundDialogUi.containmentComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(settingsModified())); connect(d->backgroundDialogUi.wallpaperMode, SIGNAL(currentIndexChanged(int)), this, SLOT(settingsModified())); settingsModified(false); } BackgroundDialog::~BackgroundDialog() { KConfigGroup cg(KGlobal::config(), "BackgroundConfigDialog"); saveDialogSize(cg); cleanup(); delete d; } void BackgroundDialog::cleanup() { delete d->wallpaper; d->wallpaper = 0; } void BackgroundDialog::containmentImmutabilityChanged(Plasma::ImmutabilityType type) { bool showWidgetsUnlocking = false; if (d->containment && d->containment.data()->corona()) { QAction *unlockAction = d->containment.data()->corona()->action("lock widgets"); if (unlockAction) { connect(d->backgroundDialogUi.unlockButton, SIGNAL(clicked()), unlockAction, SLOT(trigger()), Qt::UniqueConnection); showWidgetsUnlocking = (type == Plasma::UserImmutable); kDebug() << showWidgetsUnlocking << type; } } d->backgroundDialogUi.widgetLocking->setVisible(showWidgetsUnlocking); const bool enabled = type == Plasma::Mutable; d->backgroundDialogUi.containmentLabel->setEnabled(enabled); d->backgroundDialogUi.containmentComboBox->setEnabled(enabled); } void BackgroundDialog::reloadConfig() { disconnect(d->backgroundDialogUi.wallpaperMode, SIGNAL(currentIndexChanged(int)), this, SLOT(changeBackgroundMode(int))); int containmentIndex = 0; int wallpaperIndex = 0; // Containment KPluginInfo::List plugins = Plasma::Containment::listContainmentsOfType("desktop"); d->containmentModel->clear(); int i = 0; foreach (const KPluginInfo& info, plugins) { if (info.property("NoDisplay").toBool()) { continue; } QStandardItem* item = new QStandardItem(KIcon(info.icon()), info.name()); item->setData(info.comment(), AppletDelegate::DescriptionRole); item->setData(info.pluginName(), AppletDelegate::PluginNameRole); d->containmentModel->appendRow(item); if (d->containment && info.pluginName() == d->containment.data()->pluginName()) { containmentIndex = i; } ++i; } d->backgroundDialogUi.containmentComboBox->setCurrentIndex(containmentIndex); if (d->containment) { containmentImmutabilityChanged(d->containment.data()->immutability()); } else { d->backgroundDialogUi.containmentLabel->setEnabled(false); d->backgroundDialogUi.containmentComboBox->setEnabled(false); } // Wallpaper bool doWallpaper = d->containment && d->containment.data()->drawWallpaper(); d->backgroundDialogUi.wallpaperGroup->setVisible(doWallpaper); d->preview->setVisible(doWallpaper); //kDebug() << "do wallpapers?!" << doWallpaper; if (doWallpaper) { // Load wallpaper plugins QString currentPlugin; QString currentMode; Plasma::Wallpaper *currentWallpaper = d->containment ? d->containment.data()->wallpaper() : 0; if (currentWallpaper) { currentPlugin = currentWallpaper->pluginName(); currentMode = currentWallpaper->renderingMode().name(); KConfigGroup cg = wallpaperConfig(currentPlugin); currentWallpaper->save(cg); } plugins = Plasma::Wallpaper::listWallpaperInfo(); QMap sortedPlugins; foreach (const KPluginInfo &info, plugins) { sortedPlugins.insert(info.name(), info); } d->wallpaperModel->clear(); int i = 0; foreach (const KPluginInfo& info, sortedPlugins) { //kDebug() << "doing wallpaper" << info.pluginName() << currentPlugin; bool matches = info.pluginName() == currentPlugin; const QList& modes = info.service()->actions(); if (modes.count() > 0) { if (matches) { wallpaperIndex = i; //kDebug() << "matches at" << wallpaperIndex; } foreach (const KServiceAction& mode, modes) { QStandardItem *item = new QStandardItem(KIcon(mode.icon()), mode.text()); KConfig config(KGlobal::dirs()->locate("services", info.entryPath()), KConfig::SimpleConfig); KConfigGroup cg(&config, "Desktop Action " + mode.name()); item->setData(cg.readEntry("Comment", QString()), AppletDelegate::DescriptionRole); item->setData(info.pluginName(), AppletDelegate::PluginNameRole); item->setData(mode.name(), AppletDelegate::ModeRole); d->wallpaperModel->appendRow(item); //kDebug() << matches << mode.name() << currentMode; if (matches && mode.name() == currentMode) { wallpaperIndex = i; //kDebug() << "matches at" << wallpaperIndex; } ++i; } } else { QStandardItem *item = new QStandardItem(KIcon(info.icon()), info.name()); item->setData(info.comment(), AppletDelegate::DescriptionRole); item->setData(info.pluginName(), AppletDelegate::PluginNameRole); d->wallpaperModel->appendRow(item); if (matches) { wallpaperIndex = i; //kDebug() << "matches at" << wallpaperIndex; } ++i; } } //kDebug() << "match is said to be" << wallpaperIndex << "out of" << d->backgroundDialogUi.wallpaperMode->count(); d->backgroundDialogUi.wallpaperMode->setCurrentIndex(wallpaperIndex); changeBackgroundMode(wallpaperIndex); } connect(d->backgroundDialogUi.wallpaperMode, SIGNAL(currentIndexChanged(int)), this, SLOT(changeBackgroundMode(int))); settingsModified(false); } void BackgroundDialog::changeBackgroundMode(int index) { kDebug(); QWidget* w = 0; const QString plugin = d->backgroundDialogUi.wallpaperMode->itemData(index, AppletDelegate::PluginNameRole).toString(); const QString mode = d->backgroundDialogUi.wallpaperMode->itemData(index, AppletDelegate::ModeRole).toString(); if (d->backgroundDialogUi.wallpaperGroup->layout()->count() > 1) { QLayoutItem *item = d->backgroundDialogUi.wallpaperGroup->layout()->takeAt(1); QWidget *widget = item->widget(); delete item; delete widget; } if (d->wallpaper && d->wallpaper->pluginName() != plugin) { delete d->wallpaper; d->wallpaper = 0; } if (!d->wallpaper) { d->wallpaper = Plasma::Wallpaper::load(plugin); } if (d->wallpaper) { if (mode == "SingleImage") { d->wallpaper->setPreviewing(true); d->preview->setPreview(d->wallpaper); } d->wallpaper->setRenderingMode(mode); KConfigGroup cfg = wallpaperConfig(plugin); //kDebug() << "making a" << plugin << "in mode" << mode; if (d->containment) { d->wallpaper->setTargetSizeHint(d->containment.data()->size()); } d->wallpaper->restore(cfg); WallpaperWidget *wallpaperWidget = new WallpaperWidget(d->backgroundDialogUi.wallpaperGroup); w = d->wallpaper->createConfigurationInterface(wallpaperWidget); connect(wallpaperWidget, SIGNAL(modified(bool)), this, SLOT(settingsModified(bool))); const bool needsPreviewDuringConfiguration = d->wallpaper->needsPreviewDuringConfiguration(); d->backgroundDialogUi.monitorWidget->setVisible(needsPreviewDuringConfiguration); d->preview->setVisible(needsPreviewDuringConfiguration); } if (!w) { w = new QWidget(d->backgroundDialogUi.wallpaperGroup); } else if (w->layout()) { QGridLayout *gridLayout = dynamic_cast(w->layout()); if (gridLayout) { gridLayout->setColumnMinimumWidth(0, d->backgroundDialogUi.wallpaperTypeLabel->geometry().right()); gridLayout->setColumnStretch(0, 0); gridLayout->setColumnStretch(1, 10); gridLayout->setContentsMargins(0, 0, 0, 0); } } d->backgroundDialogUi.wallpaperGroup->layout()->addWidget(w); settingsModified(true); } KConfigGroup BackgroundDialog::wallpaperConfig(const QString &plugin) { //FIXME: we have details about the structure of the containment config duplicated here! KConfigGroup cfg = d->containment ? d->containment.data()->config() : KConfigGroup(KGlobal::config(), "Wallpaper"); cfg = KConfigGroup(&cfg, "Wallpaper"); return KConfigGroup(&cfg, plugin); } void BackgroundDialog::setLayoutChangeable(bool changeable) { d->backgroundDialogUi.containmentLabel->setVisible(changeable); d->backgroundDialogUi.containmentComboBox->setVisible(changeable); } bool BackgroundDialog::isLayoutChangeable() const { return d->backgroundDialogUi.containmentComboBox->isVisible(); } void BackgroundDialog::saveConfig() { if (!isButtonEnabled(Apply)) { return; } const int wallpaperIndex = d->backgroundDialogUi.wallpaperMode->currentIndex(); const QString wallpaperPlugin = d->backgroundDialogUi.wallpaperMode->itemData(wallpaperIndex, AppletDelegate::PluginNameRole).toString(); const QString wallpaperMode = d->backgroundDialogUi.wallpaperMode->itemData(wallpaperIndex, AppletDelegate::ModeRole).toString(); const QString containmentPlugin = d->backgroundDialogUi.containmentComboBox->itemData(d->backgroundDialogUi.containmentComboBox->currentIndex(), AppletDelegate::PluginNameRole).toString(); // Containment if (isLayoutChangeable()) { if (!d->containment || d->containment.data()->pluginName() != containmentPlugin) { if (d->containment) { disconnect(d->containment.data(), SIGNAL(destroyed()), this, SLOT(close())); disconnect(this, 0, d->containment.data(), 0); } Plasma::Containment *containment = d->view->swapContainment(d->containment.data(), containmentPlugin); if (containment != d->containment.data()) { d->containment = containment; emit containmentPluginChanged(d->containment.data()); //remove all pages but our own KPageWidgetModel *m = qobject_cast(pageWidget()->model()); if (m) { int rows = m->rowCount(); QList itemsToRemove; for (int i = 0; i < rows; ++i) { QModelIndex idx = m->index(i, 0); if (!idx.isValid()) { continue; } KPageWidgetItem *item = m->item(idx); if (item && item != d->appearanceItem && item != d->mouseItem && item != d->activityItem) { itemsToRemove.append(item); } } foreach (KPageWidgetItem *item, itemsToRemove) { removePage(item); } } //add the new containment's config if (d->containment.data()->hasConfigurationInterface()) { if (d->containment.data()->package()) { d->createConfigurationInterfaceForPackage(); } else { d->containment.data()->createConfigurationInterface(this); } connect(this, SIGNAL(applyClicked()), d->containment.data(), SLOT(configDialogFinished())); connect(this, SIGNAL(okClicked()), d->containment.data(), SLOT(configDialogFinished())); } connect(d->containment.data(), SIGNAL(destroyed()), this, SLOT(close())); } } // Wallpaper Plasma::Wallpaper *currentWallpaper = d->containment.data()->wallpaper(); if (currentWallpaper) { KConfigGroup cfg = wallpaperConfig(currentWallpaper->pluginName()); currentWallpaper->save(cfg); } } if (d->wallpaper) { KConfigGroup cfg = wallpaperConfig(d->wallpaper->pluginName()); d->wallpaper->save(cfg); } if (d->containment) { d->containment.data()->setWallpaper(wallpaperPlugin, wallpaperMode); } settingsModified(false); } void BackgroundDialog::settingsModified(bool modified) { d->modified = modified; updateButtons(); } bool BackgroundDialog::hasChanged() { return d->modified; }