File indexing completed on 2024-04-21 16:17:10

0001 /*
0002 *  Copyright 2017-2018 Michail Vourlakos <mvourlakos@gmail.com>
0003 *
0004 *  This file is part of Latte-Dock
0005 *
0006 *  Latte-Dock is free software; you can redistribute it and/or
0007 *  modify it under the terms of the GNU General Public License as
0008 *  published by the Free Software Foundation; either version 2 of
0009 *  the License, or (at your option) any later version.
0010 *
0011 *  Latte-Dock is distributed in the hope that it will be useful,
0012 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
0013 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
0014 *  GNU General Public License for more details.
0015 *
0016 *  You should have received a copy of the GNU General Public License
0017 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
0018 */
0019 
0020 #include "activitiesdelegate.h"
0021 
0022 // local
0023 #include "persistentmenu.h"
0024 #include "../settingsdialog.h"
0025 #include "../tools/settingstools.h"
0026 
0027 // Qt
0028 #include <QApplication>
0029 #include <QDebug>
0030 #include <QWidget>
0031 #include <QMenu>
0032 #include <QModelIndex>
0033 #include <QPainter>
0034 #include <QPushButton>
0035 #include <QString>
0036 #include <QTextDocument>
0037 
0038 // KDE
0039 #include <KActivities/Info>
0040 
0041 ActivitiesDelegate::ActivitiesDelegate(QObject *parent)
0042     : QItemDelegate(parent)
0043 {
0044     auto *settingsDialog = qobject_cast<Latte::SettingsDialog *>(parent);
0045 
0046     if (settingsDialog) {
0047         m_settingsDialog = settingsDialog;
0048     }
0049 }
0050 
0051 QWidget *ActivitiesDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const
0052 {
0053     QPushButton *button = new QPushButton(parent);
0054     PersistentMenu *menu = new PersistentMenu(button);
0055     button->setMenu(menu);
0056     menu->setMinimumWidth(option.rect.width());
0057 
0058     QStringList assignedActivities = index.model()->data(index, Qt::UserRole).toStringList();
0059     QStringList availableActivities = m_settingsDialog->availableActivities();
0060     QStringList activities = m_settingsDialog->activities();
0061 
0062     QStringList shownActivities;
0063 
0064     for (const auto &activity : activities) {
0065         if (assignedActivities.contains(activity) || availableActivities.contains(activity)) {
0066             shownActivities.append(activity);
0067         }
0068     }
0069 
0070     for (unsigned int i = 0; i < shownActivities.count(); ++i) {
0071         KActivities::Info info(shownActivities[i]);
0072 
0073         if (info.state() != KActivities::Info::Invalid) {
0074             QAction *action = new QAction(info.name());
0075             action->setData(shownActivities[i]);
0076             action->setIcon(QIcon::fromTheme(info.icon()));
0077             action->setCheckable(true);
0078             action->setChecked(assignedActivities.contains(shownActivities[i]));
0079 
0080             if ((info.state() == KActivities::Info::Running) || (info.state() == KActivities::Info::Starting)) {
0081                 QFont font = action->font();
0082                 font.setBold(true);
0083                 action->setFont(font);
0084             }
0085 
0086             menu->addAction(action);
0087 
0088             connect(action, &QAction::toggled, this, [this, button, action]() {
0089                 updateButton(button);
0090 
0091                 if (action->isChecked()) {
0092                     m_settingsDialog->addActivityInCurrent(action->data().toString());
0093                 } else {
0094                     m_settingsDialog->removeActivityFromCurrent(action->data().toString());
0095                 }
0096             });
0097         }
0098     }
0099 
0100     return button;
0101 }
0102 
0103 void ActivitiesDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const
0104 {
0105     updateButton(editor);
0106 }
0107 
0108 void ActivitiesDelegate::setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const
0109 {
0110     QPushButton *button = static_cast<QPushButton *>(editor);
0111 
0112     QStringList assignedActivities;
0113     foreach (QAction *action, button->menu()->actions()) {
0114         if (action->isChecked()) {
0115             assignedActivities << action->data().toString();
0116         }
0117     }
0118 
0119     model->setData(index, assignedActivities, Qt::UserRole);
0120 }
0121 
0122 void ActivitiesDelegate::updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &index) const
0123 {
0124     editor->setGeometry(option.rect);
0125 }
0126 
0127 void ActivitiesDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
0128 {
0129     QStyleOptionViewItem myOptions = option;
0130     //! Remove the focus dotted lines
0131     myOptions.state = (myOptions.state & ~QStyle::State_HasFocus);
0132 
0133     if (myOptions.state & QStyle::State_Enabled) {
0134         painter->save();
0135 
0136         QStringList assignedActivities = index.model()->data(index, Qt::UserRole).toStringList();
0137 
0138         if (assignedActivities.count() > 0) {
0139             myOptions.text = joinedActivities(assignedActivities);
0140 
0141             QTextDocument doc;
0142             QString css;
0143             QString activitiesText = myOptions.text;
0144 
0145             QPalette::ColorRole applyColor = Latte::isSelected(option) ? QPalette::HighlightedText : QPalette::Text;
0146             QBrush nBrush = option.palette.brush(Latte::colorGroup(option), applyColor);
0147 
0148             css = QString("body { color : %1; }").arg(nBrush.color().name());
0149 
0150             doc.setDefaultStyleSheet(css);
0151             doc.setHtml("<body>" + myOptions.text + "</body>");
0152 
0153             myOptions.text = "";
0154             myOptions.widget->style()->drawControl(QStyle::CE_ItemViewItem, &myOptions, painter);
0155 
0156             //we need an offset to be in the same vertical center of TextEdit
0157             int offsetY = ((myOptions.rect.height() - doc.size().height()) / 2);
0158 
0159             if ((qApp->layoutDirection() == Qt::RightToLeft) && !activitiesText.isEmpty()) {
0160                 int textWidth = doc.size().width();
0161 
0162                 painter->translate(qMax(myOptions.rect.left(), myOptions.rect.right() - textWidth), myOptions.rect.top() + offsetY + 1);
0163             } else {
0164                 painter->translate(myOptions.rect.left(), myOptions.rect.top() + offsetY + 1);
0165             }
0166 
0167             QRect clip(0, 0, myOptions.rect.width(), myOptions.rect.height());
0168             doc.drawContents(painter, clip);
0169         } else {
0170             QApplication::style()->drawControl(QStyle::CE_ItemViewItem, &myOptions, painter);
0171         }
0172 
0173         painter->restore();
0174     } else {
0175         // Disabled
0176         bool isSelected{Latte::isSelected(option)};
0177         QPalette::ColorRole backColorRole = isSelected ? QPalette::Highlight : QPalette::Base;
0178         QPalette::ColorRole textColorRole = isSelected ? QPalette::HighlightedText : QPalette::Text;
0179 
0180         // background
0181         painter->fillRect(option.rect, option.palette.brush(Latte::colorGroup(option), backColorRole));
0182 
0183         // text
0184         QPen pen(Qt::DashDotDotLine);
0185         QColor textColor = option.palette.brush(Latte::colorGroup(option), textColorRole).color();
0186 
0187         pen.setWidth(2); pen.setColor(textColor);
0188         int y = option.rect.y()+option.rect.height()/2;
0189 
0190         int space = option.rect.height() / 2;
0191 
0192         painter->setPen(pen);
0193 
0194         if (qApp->layoutDirection() == Qt::LeftToRight) {
0195             painter->drawLine(option.rect.x(), y,
0196                               option.rect.x()+option.rect.width() - space, y);
0197 
0198             int xm = option.rect.x() + option.rect.width() - space;
0199             int thick = option.rect.height() / 2;
0200             int ym = option.rect.y() + ((option.rect.height() - thick) / 2);
0201 
0202             pen.setStyle(Qt::SolidLine);
0203             painter->setPen(pen);
0204             painter->setBrush(textColor);
0205 
0206             //! draw ending cirlce
0207             painter->drawEllipse(QPoint(xm, ym + thick/2), thick/4, thick/4);
0208         } else {
0209             painter->drawLine(option.rect.x() + space, y,
0210                               option.rect.x() + option.rect.width(), y);
0211 
0212             int xm = option.rect.x() + space;
0213             int thick = option.rect.height() / 2;
0214             int ym = option.rect.y() + ((option.rect.height() - thick) / 2);
0215 
0216             pen.setStyle(Qt::SolidLine);
0217             painter->setPen(pen);
0218             painter->setBrush(textColor);
0219 
0220             //! draw ending cirlce
0221             painter->drawEllipse(QPoint(xm, ym + thick/2), thick/4, thick/4);
0222         }
0223     }
0224 }
0225 
0226 QString ActivitiesDelegate::joinedActivities(const QStringList &activities, bool boldForActive) const
0227 {
0228     QString finalText;
0229 
0230     int i = 0;
0231 
0232     for (const auto &activityId : activities) {
0233         KActivities::Info info(activityId);
0234 
0235         if (info.state() != KActivities::Info::Invalid) {
0236             if (i > 0) {
0237                 finalText += ", ";
0238             }
0239             i++;
0240 
0241             bool isActive{false};
0242 
0243             if (boldForActive && (info.state() == KActivities::Info::Running) || (info.state() == KActivities::Info::Starting)) {
0244                 isActive = true;
0245             }
0246 
0247             finalText += isActive ? "<b>" + info.name() + "</b>" : info.name();
0248         }
0249     }
0250 
0251     return finalText;
0252 }
0253 
0254 void ActivitiesDelegate::updateButton(QWidget *editor) const
0255 {
0256     if (!editor) {
0257         return;
0258     }
0259     QPushButton *button = static_cast<QPushButton *>(editor);
0260     QStringList assignedActivities;
0261 
0262     foreach (QAction *action, button->menu()->actions()) {
0263         if (action->isChecked()) {
0264             assignedActivities << action->data().toString();
0265         }
0266     }
0267 
0268     button->setText(joinedActivities(assignedActivities,false));
0269 }
0270